18023SPhil.Kirk@Sun.COM /* 28023SPhil.Kirk@Sun.COM * CDDL HEADER START 38023SPhil.Kirk@Sun.COM * 48023SPhil.Kirk@Sun.COM * The contents of this file are subject to the terms of the 58023SPhil.Kirk@Sun.COM * Common Development and Distribution License (the "License"). 68023SPhil.Kirk@Sun.COM * You may not use this file except in compliance with the License. 78023SPhil.Kirk@Sun.COM * 88023SPhil.Kirk@Sun.COM * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 98023SPhil.Kirk@Sun.COM * or http://www.opensolaris.org/os/licensing. 108023SPhil.Kirk@Sun.COM * See the License for the specific language governing permissions 118023SPhil.Kirk@Sun.COM * and limitations under the License. 128023SPhil.Kirk@Sun.COM * 138023SPhil.Kirk@Sun.COM * When distributing Covered Code, include this CDDL HEADER in each 148023SPhil.Kirk@Sun.COM * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 158023SPhil.Kirk@Sun.COM * If applicable, add the following below this CDDL HEADER, with the 168023SPhil.Kirk@Sun.COM * fields enclosed by brackets "[]" replaced with your own identifying 178023SPhil.Kirk@Sun.COM * information: Portions Copyright [yyyy] [name of copyright owner] 188023SPhil.Kirk@Sun.COM * 198023SPhil.Kirk@Sun.COM * CDDL HEADER END 208023SPhil.Kirk@Sun.COM */ 218023SPhil.Kirk@Sun.COM 228023SPhil.Kirk@Sun.COM /* 23*8485SPeter.Memishian@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 248023SPhil.Kirk@Sun.COM * Use is subject to license terms. 258023SPhil.Kirk@Sun.COM */ 268023SPhil.Kirk@Sun.COM 278023SPhil.Kirk@Sun.COM /* 288023SPhil.Kirk@Sun.COM * The ipnet device defined here provides access to packets at the IP layer. To 298023SPhil.Kirk@Sun.COM * provide access to packets at this layer it registers a callback function in 308023SPhil.Kirk@Sun.COM * the ip module and when there are open instances of the device ip will pass 318023SPhil.Kirk@Sun.COM * packets into the device. Packets from ip are passed on the input, output and 328023SPhil.Kirk@Sun.COM * loopback paths. Internally the module returns to ip as soon as possible by 338023SPhil.Kirk@Sun.COM * deferring processing using a taskq. 348023SPhil.Kirk@Sun.COM * 358023SPhil.Kirk@Sun.COM * Management of the devices in /dev/ipnet/ is handled by the devname 368023SPhil.Kirk@Sun.COM * filesystem and use of the neti interfaces. This module registers for NIC 378023SPhil.Kirk@Sun.COM * events using the neti framework so that when IP interfaces are bought up, 388023SPhil.Kirk@Sun.COM * taken down etc. the ipnet module is notified and its view of the interfaces 398023SPhil.Kirk@Sun.COM * configured on the system adjusted. On attach, the module gets an initial 408023SPhil.Kirk@Sun.COM * view of the system again using the neti framework but as it has already 418023SPhil.Kirk@Sun.COM * registered for IP interface events, it is still up-to-date with any changes. 428023SPhil.Kirk@Sun.COM */ 438023SPhil.Kirk@Sun.COM 448023SPhil.Kirk@Sun.COM #include <sys/types.h> 458023SPhil.Kirk@Sun.COM #include <sys/conf.h> 468023SPhil.Kirk@Sun.COM #include <sys/cred.h> 478023SPhil.Kirk@Sun.COM #include <sys/stat.h> 488023SPhil.Kirk@Sun.COM #include <sys/ddi.h> 498023SPhil.Kirk@Sun.COM #include <sys/sunddi.h> 508023SPhil.Kirk@Sun.COM #include <sys/modctl.h> 518023SPhil.Kirk@Sun.COM #include <sys/dlpi.h> 528023SPhil.Kirk@Sun.COM #include <sys/strsun.h> 538023SPhil.Kirk@Sun.COM #include <sys/id_space.h> 548023SPhil.Kirk@Sun.COM #include <sys/kmem.h> 558023SPhil.Kirk@Sun.COM #include <sys/mkdev.h> 568023SPhil.Kirk@Sun.COM #include <sys/neti.h> 578023SPhil.Kirk@Sun.COM #include <net/if.h> 588023SPhil.Kirk@Sun.COM #include <sys/errno.h> 598023SPhil.Kirk@Sun.COM #include <sys/list.h> 608023SPhil.Kirk@Sun.COM #include <sys/ksynch.h> 618023SPhil.Kirk@Sun.COM #include <sys/hook_event.h> 628023SPhil.Kirk@Sun.COM #include <sys/stropts.h> 638023SPhil.Kirk@Sun.COM #include <sys/sysmacros.h> 648023SPhil.Kirk@Sun.COM #include <inet/ip.h> 658023SPhil.Kirk@Sun.COM #include <inet/ip_multi.h> 668023SPhil.Kirk@Sun.COM #include <inet/ip6.h> 678023SPhil.Kirk@Sun.COM #include <inet/ipnet.h> 688023SPhil.Kirk@Sun.COM 698023SPhil.Kirk@Sun.COM static struct module_info ipnet_minfo = { 708023SPhil.Kirk@Sun.COM 1, /* mi_idnum */ 718023SPhil.Kirk@Sun.COM "ipnet", /* mi_idname */ 728023SPhil.Kirk@Sun.COM 0, /* mi_minpsz */ 738023SPhil.Kirk@Sun.COM INFPSZ, /* mi_maxpsz */ 748023SPhil.Kirk@Sun.COM 2048, /* mi_hiwat */ 758023SPhil.Kirk@Sun.COM 0 /* mi_lowat */ 768023SPhil.Kirk@Sun.COM }; 778023SPhil.Kirk@Sun.COM 788023SPhil.Kirk@Sun.COM /* 798023SPhil.Kirk@Sun.COM * List to hold static view of ipnetif_t's on the system. This is needed to 808023SPhil.Kirk@Sun.COM * avoid holding the lock protecting the avl tree of ipnetif's over the 818023SPhil.Kirk@Sun.COM * callback into the dev filesystem. 828023SPhil.Kirk@Sun.COM */ 838023SPhil.Kirk@Sun.COM typedef struct ipnetif_cbdata { 848023SPhil.Kirk@Sun.COM char ic_ifname[LIFNAMSIZ]; 858023SPhil.Kirk@Sun.COM dev_t ic_dev; 868023SPhil.Kirk@Sun.COM list_node_t ic_next; 878023SPhil.Kirk@Sun.COM } ipnetif_cbdata_t; 888023SPhil.Kirk@Sun.COM 898023SPhil.Kirk@Sun.COM /* 908023SPhil.Kirk@Sun.COM * Convenience enumerated type for ipnet_accept(). It describes the 918023SPhil.Kirk@Sun.COM * properties of a given ipnet_addrp_t relative to a single ipnet_t 928023SPhil.Kirk@Sun.COM * client stream. The values represent whether the address is ... 938023SPhil.Kirk@Sun.COM */ 948023SPhil.Kirk@Sun.COM typedef enum { 958023SPhil.Kirk@Sun.COM IPNETADDR_MYADDR, /* an address on my ipnetif_t. */ 968023SPhil.Kirk@Sun.COM IPNETADDR_MBCAST, /* a multicast or broadcast address. */ 978023SPhil.Kirk@Sun.COM IPNETADDR_UNKNOWN /* none of the above. */ 988023SPhil.Kirk@Sun.COM } ipnet_addrtype_t; 998023SPhil.Kirk@Sun.COM 1008023SPhil.Kirk@Sun.COM /* Argument used for the ipnet_nicevent_taskq callback. */ 1018023SPhil.Kirk@Sun.COM typedef struct ipnet_nicevent_s { 1028023SPhil.Kirk@Sun.COM nic_event_t ipne_event; 1038023SPhil.Kirk@Sun.COM net_handle_t ipne_protocol; 1048023SPhil.Kirk@Sun.COM netstackid_t ipne_stackid; 1058023SPhil.Kirk@Sun.COM uint64_t ipne_ifindex; 1068023SPhil.Kirk@Sun.COM uint64_t ipne_lifindex; 1078023SPhil.Kirk@Sun.COM char ipne_ifname[LIFNAMSIZ]; 1088023SPhil.Kirk@Sun.COM } ipnet_nicevent_t; 1098023SPhil.Kirk@Sun.COM 1108023SPhil.Kirk@Sun.COM static dev_info_t *ipnet_dip; 1118023SPhil.Kirk@Sun.COM static major_t ipnet_major; 1128023SPhil.Kirk@Sun.COM static ddi_taskq_t *ipnet_taskq; /* taskq for packets */ 1138023SPhil.Kirk@Sun.COM static ddi_taskq_t *ipnet_nicevent_taskq; /* taskq for NIC events */ 1148023SPhil.Kirk@Sun.COM static id_space_t *ipnet_minor_space; 1158023SPhil.Kirk@Sun.COM static const int IPNET_MINOR_LO = 1; /* minor number for /dev/lo0 */ 1168023SPhil.Kirk@Sun.COM static const int IPNET_MINOR_MIN = 2; /* start of dynamic minors */ 1178023SPhil.Kirk@Sun.COM static dl_info_ack_t ipnet_infoack = IPNET_INFO_ACK_INIT; 1188023SPhil.Kirk@Sun.COM static ipnet_acceptfn_t ipnet_accept, ipnet_loaccept; 1198023SPhil.Kirk@Sun.COM 1208023SPhil.Kirk@Sun.COM static void ipnet_input(mblk_t *); 1218023SPhil.Kirk@Sun.COM static int ipnet_wput(queue_t *, mblk_t *); 1228023SPhil.Kirk@Sun.COM static int ipnet_rsrv(queue_t *); 1238023SPhil.Kirk@Sun.COM static int ipnet_open(queue_t *, dev_t *, int, int, cred_t *); 1248023SPhil.Kirk@Sun.COM static int ipnet_close(queue_t *); 1258023SPhil.Kirk@Sun.COM static void ipnet_ioctl(queue_t *, mblk_t *); 1268023SPhil.Kirk@Sun.COM static void ipnet_iocdata(queue_t *, mblk_t *); 1278023SPhil.Kirk@Sun.COM static void ipnet_wputnondata(queue_t *, mblk_t *); 1288023SPhil.Kirk@Sun.COM static int ipnet_attach(dev_info_t *, ddi_attach_cmd_t); 1298023SPhil.Kirk@Sun.COM static int ipnet_detach(dev_info_t *, ddi_detach_cmd_t); 1308023SPhil.Kirk@Sun.COM static int ipnet_devinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 1318023SPhil.Kirk@Sun.COM static void ipnet_inforeq(queue_t *q, mblk_t *mp); 1328023SPhil.Kirk@Sun.COM static void ipnet_bindreq(queue_t *q, mblk_t *mp); 1338023SPhil.Kirk@Sun.COM static void ipnet_unbindreq(queue_t *q, mblk_t *mp); 1348023SPhil.Kirk@Sun.COM static void ipnet_dlpromisconreq(queue_t *q, mblk_t *mp); 1358023SPhil.Kirk@Sun.COM static void ipnet_dlpromiscoffreq(queue_t *q, mblk_t *mp); 1368023SPhil.Kirk@Sun.COM static int ipnet_join_allmulti(ipnetif_t *, ipnet_stack_t *); 1378023SPhil.Kirk@Sun.COM static void ipnet_leave_allmulti(ipnetif_t *, ipnet_stack_t *); 1388023SPhil.Kirk@Sun.COM static int ipnet_nicevent_cb(hook_event_token_t, hook_data_t, void *); 1398023SPhil.Kirk@Sun.COM static void ipnet_nicevent_task(void *); 1408023SPhil.Kirk@Sun.COM static ipnetif_t *ipnet_create_if(const char *, uint64_t, ipnet_stack_t *); 1418023SPhil.Kirk@Sun.COM static void ipnet_remove_if(ipnetif_t *, ipnet_stack_t *); 1428023SPhil.Kirk@Sun.COM static ipnetif_addr_t *ipnet_match_lif(ipnetif_t *, lif_if_t, boolean_t); 1438023SPhil.Kirk@Sun.COM static ipnetif_t *ipnet_if_getby_index(uint64_t, ipnet_stack_t *); 1448023SPhil.Kirk@Sun.COM static ipnetif_t *ipnet_if_getby_dev(dev_t, ipnet_stack_t *); 1458023SPhil.Kirk@Sun.COM static boolean_t ipnet_if_in_zone(ipnetif_t *, zoneid_t, ipnet_stack_t *); 1468023SPhil.Kirk@Sun.COM static void ipnet_if_zonecheck(ipnetif_t *, ipnet_stack_t *); 1478023SPhil.Kirk@Sun.COM static int ipnet_populate_if(net_handle_t, ipnet_stack_t *, boolean_t); 1488023SPhil.Kirk@Sun.COM static int ipnet_if_compare_name(const void *, const void *); 1498023SPhil.Kirk@Sun.COM static int ipnet_if_compare_index(const void *, const void *); 1508023SPhil.Kirk@Sun.COM static void ipnet_add_ifaddr(uint64_t, ipnetif_t *, net_handle_t); 1518023SPhil.Kirk@Sun.COM static void ipnet_delete_ifaddr(ipnetif_addr_t *, ipnetif_t *, boolean_t); 1528023SPhil.Kirk@Sun.COM static void ipnetif_refhold(ipnetif_t *); 1538023SPhil.Kirk@Sun.COM static void ipnetif_refrele(ipnetif_t *); 1548023SPhil.Kirk@Sun.COM static void ipnet_walkers_inc(ipnet_stack_t *); 1558023SPhil.Kirk@Sun.COM static void ipnet_walkers_dec(ipnet_stack_t *); 1568023SPhil.Kirk@Sun.COM static void ipnet_register_netihook(ipnet_stack_t *); 1578023SPhil.Kirk@Sun.COM static void *ipnet_stack_init(netstackid_t, netstack_t *); 1588023SPhil.Kirk@Sun.COM static void ipnet_stack_fini(netstackid_t, void *); 1598023SPhil.Kirk@Sun.COM 1608023SPhil.Kirk@Sun.COM static struct qinit ipnet_rinit = { 1618023SPhil.Kirk@Sun.COM NULL, /* qi_putp */ 1628023SPhil.Kirk@Sun.COM ipnet_rsrv, /* qi_srvp */ 1638023SPhil.Kirk@Sun.COM ipnet_open, /* qi_qopen */ 1648023SPhil.Kirk@Sun.COM ipnet_close, /* qi_qclose */ 1658023SPhil.Kirk@Sun.COM NULL, /* qi_qadmin */ 1668023SPhil.Kirk@Sun.COM &ipnet_minfo, /* qi_minfo */ 1678023SPhil.Kirk@Sun.COM }; 1688023SPhil.Kirk@Sun.COM 1698023SPhil.Kirk@Sun.COM static struct qinit ipnet_winit = { 1708023SPhil.Kirk@Sun.COM ipnet_wput, /* qi_putp */ 1718023SPhil.Kirk@Sun.COM NULL, /* qi_srvp */ 1728023SPhil.Kirk@Sun.COM NULL, /* qi_qopen */ 1738023SPhil.Kirk@Sun.COM NULL, /* qi_qclose */ 1748023SPhil.Kirk@Sun.COM NULL, /* qi_qadmin */ 1758023SPhil.Kirk@Sun.COM &ipnet_minfo, /* qi_minfo */ 1768023SPhil.Kirk@Sun.COM }; 1778023SPhil.Kirk@Sun.COM 1788023SPhil.Kirk@Sun.COM static struct streamtab ipnet_info = { 1798023SPhil.Kirk@Sun.COM &ipnet_rinit, &ipnet_winit 1808023SPhil.Kirk@Sun.COM }; 1818023SPhil.Kirk@Sun.COM 1828023SPhil.Kirk@Sun.COM DDI_DEFINE_STREAM_OPS(ipnet_ops, nulldev, nulldev, ipnet_attach, 1838023SPhil.Kirk@Sun.COM ipnet_detach, nodev, ipnet_devinfo, D_MP | D_MTPERMOD, &ipnet_info, 1848023SPhil.Kirk@Sun.COM ddi_quiesce_not_supported); 1858023SPhil.Kirk@Sun.COM 1868023SPhil.Kirk@Sun.COM static struct modldrv modldrv = { 1878023SPhil.Kirk@Sun.COM &mod_driverops, 1888023SPhil.Kirk@Sun.COM "STREAMS ipnet driver", 1898023SPhil.Kirk@Sun.COM &ipnet_ops 1908023SPhil.Kirk@Sun.COM }; 1918023SPhil.Kirk@Sun.COM 1928023SPhil.Kirk@Sun.COM static struct modlinkage modlinkage = { 1938023SPhil.Kirk@Sun.COM MODREV_1, &modldrv, NULL 1948023SPhil.Kirk@Sun.COM }; 1958023SPhil.Kirk@Sun.COM 1968023SPhil.Kirk@Sun.COM /* 1978023SPhil.Kirk@Sun.COM * Walk the list of physical interfaces on the machine, for each 1988023SPhil.Kirk@Sun.COM * interface create a new ipnetif_t and add any addresses to it. We 1998023SPhil.Kirk@Sun.COM * need to do the walk twice, once for IPv4 and once for IPv6. 2008023SPhil.Kirk@Sun.COM * 2018023SPhil.Kirk@Sun.COM * The interfaces are destroyed as part of ipnet_stack_fini() for each 2028023SPhil.Kirk@Sun.COM * stack. Note that we cannot do this initialization in 2038023SPhil.Kirk@Sun.COM * ipnet_stack_init(), since ipnet_stack_init() cannot fail. 2048023SPhil.Kirk@Sun.COM */ 2058023SPhil.Kirk@Sun.COM static int 2068023SPhil.Kirk@Sun.COM ipnet_if_init(void) 2078023SPhil.Kirk@Sun.COM { 2088023SPhil.Kirk@Sun.COM netstack_handle_t nh; 2098023SPhil.Kirk@Sun.COM netstack_t *ns; 2108023SPhil.Kirk@Sun.COM ipnet_stack_t *ips; 2118023SPhil.Kirk@Sun.COM int ret = 0; 2128023SPhil.Kirk@Sun.COM 2138023SPhil.Kirk@Sun.COM netstack_next_init(&nh); 2148023SPhil.Kirk@Sun.COM while ((ns = netstack_next(&nh)) != NULL) { 2158023SPhil.Kirk@Sun.COM ips = ns->netstack_ipnet; 2168356SSebastien.Roy@Sun.COM if ((ret = ipnet_populate_if(ips->ips_ndv4, ips, B_FALSE)) == 0) 2178356SSebastien.Roy@Sun.COM ret = ipnet_populate_if(ips->ips_ndv6, ips, B_TRUE); 2188356SSebastien.Roy@Sun.COM netstack_rele(ns); 2198356SSebastien.Roy@Sun.COM if (ret != 0) 2208023SPhil.Kirk@Sun.COM break; 2218023SPhil.Kirk@Sun.COM } 2228023SPhil.Kirk@Sun.COM netstack_next_fini(&nh); 2238023SPhil.Kirk@Sun.COM return (ret); 2248023SPhil.Kirk@Sun.COM } 2258023SPhil.Kirk@Sun.COM 2268023SPhil.Kirk@Sun.COM /* 2278023SPhil.Kirk@Sun.COM * Standard module entry points. 2288023SPhil.Kirk@Sun.COM */ 2298023SPhil.Kirk@Sun.COM int 2308023SPhil.Kirk@Sun.COM _init(void) 2318023SPhil.Kirk@Sun.COM { 232*8485SPeter.Memishian@Sun.COM int ret; 233*8485SPeter.Memishian@Sun.COM boolean_t netstack_registered = B_FALSE; 2348023SPhil.Kirk@Sun.COM 2358023SPhil.Kirk@Sun.COM if ((ipnet_major = ddi_name_to_major("ipnet")) == (major_t)-1) 2368023SPhil.Kirk@Sun.COM return (ENODEV); 2378023SPhil.Kirk@Sun.COM ipnet_minor_space = id_space_create("ipnet_minor_space", 2388023SPhil.Kirk@Sun.COM IPNET_MINOR_MIN, MAXMIN32); 239*8485SPeter.Memishian@Sun.COM 2408023SPhil.Kirk@Sun.COM /* 2418023SPhil.Kirk@Sun.COM * We call ddi_taskq_create() with nthread == 1 to ensure in-order 242*8485SPeter.Memishian@Sun.COM * delivery of packets to clients. Note that we need to create the 243*8485SPeter.Memishian@Sun.COM * taskqs before calling netstack_register() since ipnet_stack_init() 244*8485SPeter.Memishian@Sun.COM * registers callbacks that use 'em. 2458023SPhil.Kirk@Sun.COM */ 2468023SPhil.Kirk@Sun.COM ipnet_taskq = ddi_taskq_create(NULL, "ipnet", 1, TASKQ_DEFAULTPRI, 0); 2478023SPhil.Kirk@Sun.COM ipnet_nicevent_taskq = ddi_taskq_create(NULL, "ipnet_nic_event_queue", 2488023SPhil.Kirk@Sun.COM 1, TASKQ_DEFAULTPRI, 0); 2498023SPhil.Kirk@Sun.COM if (ipnet_taskq == NULL || ipnet_nicevent_taskq == NULL) { 2508023SPhil.Kirk@Sun.COM ret = ENOMEM; 2518023SPhil.Kirk@Sun.COM goto done; 2528023SPhil.Kirk@Sun.COM } 253*8485SPeter.Memishian@Sun.COM 254*8485SPeter.Memishian@Sun.COM netstack_register(NS_IPNET, ipnet_stack_init, NULL, ipnet_stack_fini); 255*8485SPeter.Memishian@Sun.COM netstack_registered = B_TRUE; 256*8485SPeter.Memishian@Sun.COM 2578023SPhil.Kirk@Sun.COM if ((ret = ipnet_if_init()) == 0) 2588023SPhil.Kirk@Sun.COM ret = mod_install(&modlinkage); 2598023SPhil.Kirk@Sun.COM done: 2608023SPhil.Kirk@Sun.COM if (ret != 0) { 2618023SPhil.Kirk@Sun.COM if (ipnet_taskq != NULL) 2628023SPhil.Kirk@Sun.COM ddi_taskq_destroy(ipnet_taskq); 2638023SPhil.Kirk@Sun.COM if (ipnet_nicevent_taskq != NULL) 2648023SPhil.Kirk@Sun.COM ddi_taskq_destroy(ipnet_nicevent_taskq); 265*8485SPeter.Memishian@Sun.COM if (netstack_registered) 266*8485SPeter.Memishian@Sun.COM netstack_unregister(NS_IPNET); 2678023SPhil.Kirk@Sun.COM id_space_destroy(ipnet_minor_space); 2688023SPhil.Kirk@Sun.COM } 2698023SPhil.Kirk@Sun.COM return (ret); 2708023SPhil.Kirk@Sun.COM } 2718023SPhil.Kirk@Sun.COM 2728023SPhil.Kirk@Sun.COM int 2738023SPhil.Kirk@Sun.COM _fini(void) 2748023SPhil.Kirk@Sun.COM { 2758023SPhil.Kirk@Sun.COM int err; 2768023SPhil.Kirk@Sun.COM 2778023SPhil.Kirk@Sun.COM if ((err = mod_remove(&modlinkage)) != 0) 2788023SPhil.Kirk@Sun.COM return (err); 279*8485SPeter.Memishian@Sun.COM 280*8485SPeter.Memishian@Sun.COM netstack_unregister(NS_IPNET); 2818023SPhil.Kirk@Sun.COM ddi_taskq_destroy(ipnet_nicevent_taskq); 2828023SPhil.Kirk@Sun.COM ddi_taskq_destroy(ipnet_taskq); 2838023SPhil.Kirk@Sun.COM id_space_destroy(ipnet_minor_space); 2848023SPhil.Kirk@Sun.COM return (0); 2858023SPhil.Kirk@Sun.COM } 2868023SPhil.Kirk@Sun.COM 2878023SPhil.Kirk@Sun.COM int 2888023SPhil.Kirk@Sun.COM _info(struct modinfo *modinfop) 2898023SPhil.Kirk@Sun.COM { 2908023SPhil.Kirk@Sun.COM return (mod_info(&modlinkage, modinfop)); 2918023SPhil.Kirk@Sun.COM } 2928023SPhil.Kirk@Sun.COM 2938023SPhil.Kirk@Sun.COM static void 2948023SPhil.Kirk@Sun.COM ipnet_register_netihook(ipnet_stack_t *ips) 2958023SPhil.Kirk@Sun.COM { 2968023SPhil.Kirk@Sun.COM int ret; 2978356SSebastien.Roy@Sun.COM zoneid_t zoneid; 2988356SSebastien.Roy@Sun.COM netid_t netid; 2998023SPhil.Kirk@Sun.COM 3008023SPhil.Kirk@Sun.COM HOOK_INIT(ips->ips_nicevents, ipnet_nicevent_cb, "ipnet_nicevents", 3018023SPhil.Kirk@Sun.COM ips); 3028023SPhil.Kirk@Sun.COM 3038023SPhil.Kirk@Sun.COM /* 3048356SSebastien.Roy@Sun.COM * It is possible for an exclusive stack to be in the process of 3058356SSebastien.Roy@Sun.COM * shutting down here, and the netid and protocol lookups could fail 3068356SSebastien.Roy@Sun.COM * in that case. 3078023SPhil.Kirk@Sun.COM */ 3088356SSebastien.Roy@Sun.COM zoneid = netstackid_to_zoneid(ips->ips_netstack->netstack_stackid); 3098356SSebastien.Roy@Sun.COM if ((netid = net_zoneidtonetid(zoneid)) == -1) 3108356SSebastien.Roy@Sun.COM return; 3118023SPhil.Kirk@Sun.COM 3128356SSebastien.Roy@Sun.COM if ((ips->ips_ndv4 = net_protocol_lookup(netid, NHF_INET)) != NULL) { 3138356SSebastien.Roy@Sun.COM if ((ret = net_hook_register(ips->ips_ndv4, NH_NIC_EVENTS, 3148356SSebastien.Roy@Sun.COM ips->ips_nicevents)) != 0) { 3158356SSebastien.Roy@Sun.COM VERIFY(net_protocol_release(ips->ips_ndv4) == 0); 3168356SSebastien.Roy@Sun.COM ips->ips_ndv4 = NULL; 3178356SSebastien.Roy@Sun.COM cmn_err(CE_WARN, "unable to register IPv4 netinfo hooks" 3188356SSebastien.Roy@Sun.COM " in zone %d: %d", zoneid, ret); 3198356SSebastien.Roy@Sun.COM } 3208023SPhil.Kirk@Sun.COM } 3218356SSebastien.Roy@Sun.COM if ((ips->ips_ndv6 = net_protocol_lookup(netid, NHF_INET6)) != NULL) { 3228356SSebastien.Roy@Sun.COM if ((ret = net_hook_register(ips->ips_ndv6, NH_NIC_EVENTS, 3238356SSebastien.Roy@Sun.COM ips->ips_nicevents)) != 0) { 3248356SSebastien.Roy@Sun.COM VERIFY(net_protocol_release(ips->ips_ndv6) == 0); 3258356SSebastien.Roy@Sun.COM ips->ips_ndv6 = NULL; 3268356SSebastien.Roy@Sun.COM cmn_err(CE_WARN, "unable to register IPv6 netinfo hooks" 3278356SSebastien.Roy@Sun.COM " in zone %d: %d", zoneid, ret); 3288356SSebastien.Roy@Sun.COM } 3298023SPhil.Kirk@Sun.COM } 3308023SPhil.Kirk@Sun.COM } 3318023SPhil.Kirk@Sun.COM 3328023SPhil.Kirk@Sun.COM /* 3338023SPhil.Kirk@Sun.COM * This function is called on attach to build an initial view of the 3348023SPhil.Kirk@Sun.COM * interfaces on the system. It will be called once for IPv4 and once 3358023SPhil.Kirk@Sun.COM * for IPv6, although there is only one ipnet interface for both IPv4 3368023SPhil.Kirk@Sun.COM * and IPv6 there are separate address lists. 3378023SPhil.Kirk@Sun.COM */ 3388023SPhil.Kirk@Sun.COM static int 3398023SPhil.Kirk@Sun.COM ipnet_populate_if(net_handle_t nd, ipnet_stack_t *ips, boolean_t isv6) 3408023SPhil.Kirk@Sun.COM { 3418023SPhil.Kirk@Sun.COM phy_if_t phyif; 3428023SPhil.Kirk@Sun.COM lif_if_t lif; 3438023SPhil.Kirk@Sun.COM ipnetif_t *ipnetif; 3448023SPhil.Kirk@Sun.COM char name[LIFNAMSIZ]; 3458023SPhil.Kirk@Sun.COM boolean_t new_if = B_FALSE; 3468023SPhil.Kirk@Sun.COM uint64_t ifflags; 3478023SPhil.Kirk@Sun.COM int ret = 0; 3488023SPhil.Kirk@Sun.COM 3498023SPhil.Kirk@Sun.COM /* 3508356SSebastien.Roy@Sun.COM * If ipnet_register_netihook() was unable to initialize this 3518356SSebastien.Roy@Sun.COM * stack's net_handle_t, then we cannot populate any interface 3528356SSebastien.Roy@Sun.COM * information. This usually happens when we attempted to 3538356SSebastien.Roy@Sun.COM * grab a net_handle_t as a stack was shutting down. We don't 3548356SSebastien.Roy@Sun.COM * want to fail the entire _init() operation because of a 3558356SSebastien.Roy@Sun.COM * stack shutdown (other stacks will continue to work just 3568356SSebastien.Roy@Sun.COM * fine), so we silently return success here. 3578356SSebastien.Roy@Sun.COM */ 3588356SSebastien.Roy@Sun.COM if (nd == NULL) 3598356SSebastien.Roy@Sun.COM return (0); 3608356SSebastien.Roy@Sun.COM 3618356SSebastien.Roy@Sun.COM /* 3628023SPhil.Kirk@Sun.COM * Make sure we're not processing NIC events during the 3638023SPhil.Kirk@Sun.COM * population of our interfaces and address lists. 3648023SPhil.Kirk@Sun.COM */ 3658023SPhil.Kirk@Sun.COM mutex_enter(&ips->ips_event_lock); 3668023SPhil.Kirk@Sun.COM 3678023SPhil.Kirk@Sun.COM for (phyif = net_phygetnext(nd, 0); phyif != 0; 3688023SPhil.Kirk@Sun.COM phyif = net_phygetnext(nd, phyif)) { 3698023SPhil.Kirk@Sun.COM if (net_getifname(nd, phyif, name, LIFNAMSIZ) != 0) 3708023SPhil.Kirk@Sun.COM continue; 3718023SPhil.Kirk@Sun.COM if ((ipnetif = ipnet_if_getby_index(phyif, ips)) == NULL) { 3728023SPhil.Kirk@Sun.COM ipnetif = ipnet_create_if(name, phyif, ips); 3738023SPhil.Kirk@Sun.COM if (ipnetif == NULL) { 3748023SPhil.Kirk@Sun.COM ret = ENOMEM; 3758023SPhil.Kirk@Sun.COM goto done; 3768023SPhil.Kirk@Sun.COM } 3778023SPhil.Kirk@Sun.COM new_if = B_TRUE; 3788023SPhil.Kirk@Sun.COM } 3798023SPhil.Kirk@Sun.COM ipnetif->if_flags |= 3808023SPhil.Kirk@Sun.COM isv6 ? IPNETIF_IPV6PLUMBED : IPNETIF_IPV4PLUMBED; 3818023SPhil.Kirk@Sun.COM 3828023SPhil.Kirk@Sun.COM for (lif = net_lifgetnext(nd, phyif, 0); lif != 0; 3838023SPhil.Kirk@Sun.COM lif = net_lifgetnext(nd, phyif, lif)) { 3848023SPhil.Kirk@Sun.COM /* 3858023SPhil.Kirk@Sun.COM * Skip addresses that aren't up. We'll add 3868023SPhil.Kirk@Sun.COM * them when we receive an NE_LIF_UP event. 3878023SPhil.Kirk@Sun.COM */ 3888023SPhil.Kirk@Sun.COM if (net_getlifflags(nd, phyif, lif, &ifflags) != 0 || 3898023SPhil.Kirk@Sun.COM !(ifflags & IFF_UP)) 3908023SPhil.Kirk@Sun.COM continue; 3918023SPhil.Kirk@Sun.COM /* Don't add it if we already have it. */ 3928023SPhil.Kirk@Sun.COM if (ipnet_match_lif(ipnetif, lif, isv6) != NULL) 3938023SPhil.Kirk@Sun.COM continue; 3948023SPhil.Kirk@Sun.COM ipnet_add_ifaddr(lif, ipnetif, nd); 3958023SPhil.Kirk@Sun.COM } 3968023SPhil.Kirk@Sun.COM if (!new_if) 3978023SPhil.Kirk@Sun.COM ipnetif_refrele(ipnetif); 3988023SPhil.Kirk@Sun.COM } 3998023SPhil.Kirk@Sun.COM 4008023SPhil.Kirk@Sun.COM done: 4018023SPhil.Kirk@Sun.COM mutex_exit(&ips->ips_event_lock); 4028023SPhil.Kirk@Sun.COM return (ret); 4038023SPhil.Kirk@Sun.COM } 4048023SPhil.Kirk@Sun.COM 4058023SPhil.Kirk@Sun.COM static int 4068023SPhil.Kirk@Sun.COM ipnet_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 4078023SPhil.Kirk@Sun.COM { 4088023SPhil.Kirk@Sun.COM if (cmd != DDI_ATTACH) 4098023SPhil.Kirk@Sun.COM return (DDI_FAILURE); 4108023SPhil.Kirk@Sun.COM 4118023SPhil.Kirk@Sun.COM if (ddi_create_minor_node(dip, "lo0", S_IFCHR, IPNET_MINOR_LO, 4128023SPhil.Kirk@Sun.COM DDI_PSEUDO, 0) == DDI_FAILURE) 4138023SPhil.Kirk@Sun.COM return (DDI_FAILURE); 4148023SPhil.Kirk@Sun.COM 4158023SPhil.Kirk@Sun.COM ipnet_dip = dip; 4168023SPhil.Kirk@Sun.COM return (DDI_SUCCESS); 4178023SPhil.Kirk@Sun.COM } 4188023SPhil.Kirk@Sun.COM 4198023SPhil.Kirk@Sun.COM static int 4208023SPhil.Kirk@Sun.COM ipnet_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 4218023SPhil.Kirk@Sun.COM { 4228023SPhil.Kirk@Sun.COM if (cmd != DDI_DETACH) 4238023SPhil.Kirk@Sun.COM return (DDI_FAILURE); 4248023SPhil.Kirk@Sun.COM 4258023SPhil.Kirk@Sun.COM ASSERT(dip == ipnet_dip); 4268023SPhil.Kirk@Sun.COM ddi_remove_minor_node(ipnet_dip, NULL); 4278023SPhil.Kirk@Sun.COM ipnet_dip = NULL; 4288023SPhil.Kirk@Sun.COM return (DDI_SUCCESS); 4298023SPhil.Kirk@Sun.COM } 4308023SPhil.Kirk@Sun.COM 4318023SPhil.Kirk@Sun.COM /* ARGSUSED */ 4328023SPhil.Kirk@Sun.COM static int 4338023SPhil.Kirk@Sun.COM ipnet_devinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 4348023SPhil.Kirk@Sun.COM { 4358023SPhil.Kirk@Sun.COM int error = DDI_FAILURE; 4368023SPhil.Kirk@Sun.COM 4378023SPhil.Kirk@Sun.COM switch (infocmd) { 4388023SPhil.Kirk@Sun.COM case DDI_INFO_DEVT2INSTANCE: 4398023SPhil.Kirk@Sun.COM *result = (void *)0; 4408023SPhil.Kirk@Sun.COM error = DDI_SUCCESS; 4418023SPhil.Kirk@Sun.COM break; 4428023SPhil.Kirk@Sun.COM case DDI_INFO_DEVT2DEVINFO: 4438023SPhil.Kirk@Sun.COM if (ipnet_dip != NULL) { 4448023SPhil.Kirk@Sun.COM *result = ipnet_dip; 4458023SPhil.Kirk@Sun.COM error = DDI_SUCCESS; 4468023SPhil.Kirk@Sun.COM } 4478023SPhil.Kirk@Sun.COM break; 4488023SPhil.Kirk@Sun.COM } 4498023SPhil.Kirk@Sun.COM return (error); 4508023SPhil.Kirk@Sun.COM } 4518023SPhil.Kirk@Sun.COM 4528023SPhil.Kirk@Sun.COM /* ARGSUSED */ 4538023SPhil.Kirk@Sun.COM static int 4548023SPhil.Kirk@Sun.COM ipnet_open(queue_t *rq, dev_t *dev, int oflag, int sflag, cred_t *crp) 4558023SPhil.Kirk@Sun.COM { 4568023SPhil.Kirk@Sun.COM ipnet_t *ipnet; 4578023SPhil.Kirk@Sun.COM netstack_t *ns = NULL; 4588023SPhil.Kirk@Sun.COM ipnet_stack_t *ips; 4598023SPhil.Kirk@Sun.COM int err = 0; 4608023SPhil.Kirk@Sun.COM zoneid_t zoneid = crgetzoneid(crp); 4618023SPhil.Kirk@Sun.COM 4628023SPhil.Kirk@Sun.COM /* 4638023SPhil.Kirk@Sun.COM * If the system is labeled, only the global zone is allowed to open 4648023SPhil.Kirk@Sun.COM * IP observability nodes. 4658023SPhil.Kirk@Sun.COM */ 4668023SPhil.Kirk@Sun.COM if (is_system_labeled() && zoneid != GLOBAL_ZONEID) 4678023SPhil.Kirk@Sun.COM return (EACCES); 4688023SPhil.Kirk@Sun.COM 4698023SPhil.Kirk@Sun.COM /* We don't support open as a module */ 4708023SPhil.Kirk@Sun.COM if (sflag & MODOPEN) 4718023SPhil.Kirk@Sun.COM return (ENOTSUP); 4728023SPhil.Kirk@Sun.COM 4738023SPhil.Kirk@Sun.COM /* This driver is self-cloning, we don't support re-open. */ 4748023SPhil.Kirk@Sun.COM if (rq->q_ptr != NULL) 4758023SPhil.Kirk@Sun.COM return (EBUSY); 4768023SPhil.Kirk@Sun.COM 4778023SPhil.Kirk@Sun.COM if ((ipnet = kmem_zalloc(sizeof (*ipnet), KM_NOSLEEP)) == NULL) 4788023SPhil.Kirk@Sun.COM return (ENOMEM); 4798023SPhil.Kirk@Sun.COM 4808023SPhil.Kirk@Sun.COM VERIFY((ns = netstack_find_by_cred(crp)) != NULL); 4818023SPhil.Kirk@Sun.COM ips = ns->netstack_ipnet; 4828023SPhil.Kirk@Sun.COM 4838023SPhil.Kirk@Sun.COM rq->q_ptr = WR(rq)->q_ptr = ipnet; 4848023SPhil.Kirk@Sun.COM ipnet->ipnet_rq = rq; 4858023SPhil.Kirk@Sun.COM ipnet->ipnet_minor = (minor_t)id_alloc(ipnet_minor_space); 4868023SPhil.Kirk@Sun.COM ipnet->ipnet_zoneid = zoneid; 4878023SPhil.Kirk@Sun.COM ipnet->ipnet_dlstate = DL_UNBOUND; 4888023SPhil.Kirk@Sun.COM ipnet->ipnet_sap = 0; 4898023SPhil.Kirk@Sun.COM ipnet->ipnet_ns = ns; 4908023SPhil.Kirk@Sun.COM 4918023SPhil.Kirk@Sun.COM /* 4928023SPhil.Kirk@Sun.COM * We need to hold ips_event_lock here as any NE_LIF_DOWN events need 4938023SPhil.Kirk@Sun.COM * to be processed after ipnet_if is set and the ipnet_t has been 4948023SPhil.Kirk@Sun.COM * inserted in the ips_str_list. 4958023SPhil.Kirk@Sun.COM */ 4968023SPhil.Kirk@Sun.COM mutex_enter(&ips->ips_event_lock); 4978023SPhil.Kirk@Sun.COM if (getminor(*dev) == IPNET_MINOR_LO) { 4988023SPhil.Kirk@Sun.COM ipnet->ipnet_flags |= IPNET_LOMODE; 4998023SPhil.Kirk@Sun.COM ipnet->ipnet_acceptfn = ipnet_loaccept; 5008023SPhil.Kirk@Sun.COM } else { 5018023SPhil.Kirk@Sun.COM ipnet->ipnet_acceptfn = ipnet_accept; 5028023SPhil.Kirk@Sun.COM ipnet->ipnet_if = ipnet_if_getby_dev(*dev, ips); 5038023SPhil.Kirk@Sun.COM if (ipnet->ipnet_if == NULL || 5048023SPhil.Kirk@Sun.COM !ipnet_if_in_zone(ipnet->ipnet_if, zoneid, ips)) { 5058023SPhil.Kirk@Sun.COM err = ENODEV; 5068023SPhil.Kirk@Sun.COM goto done; 5078023SPhil.Kirk@Sun.COM } 5088023SPhil.Kirk@Sun.COM } 5098023SPhil.Kirk@Sun.COM 5108023SPhil.Kirk@Sun.COM mutex_enter(&ips->ips_walkers_lock); 5118023SPhil.Kirk@Sun.COM while (ips->ips_walkers_cnt != 0) 5128023SPhil.Kirk@Sun.COM cv_wait(&ips->ips_walkers_cv, &ips->ips_walkers_lock); 5138023SPhil.Kirk@Sun.COM list_insert_head(&ips->ips_str_list, ipnet); 5148023SPhil.Kirk@Sun.COM *dev = makedevice(getmajor(*dev), ipnet->ipnet_minor); 5158023SPhil.Kirk@Sun.COM qprocson(rq); 5168023SPhil.Kirk@Sun.COM 5178023SPhil.Kirk@Sun.COM /* 5188023SPhil.Kirk@Sun.COM * Only register our callback if we're the first open client; we call 5198023SPhil.Kirk@Sun.COM * unregister in close() for the last open client. 5208023SPhil.Kirk@Sun.COM */ 5218023SPhil.Kirk@Sun.COM if (list_head(&ips->ips_str_list) == list_tail(&ips->ips_str_list)) 5228023SPhil.Kirk@Sun.COM ipobs_register_hook(ns, ipnet_input); 5238023SPhil.Kirk@Sun.COM mutex_exit(&ips->ips_walkers_lock); 5248023SPhil.Kirk@Sun.COM 5258023SPhil.Kirk@Sun.COM done: 5268023SPhil.Kirk@Sun.COM mutex_exit(&ips->ips_event_lock); 5278023SPhil.Kirk@Sun.COM if (err != 0) { 5288023SPhil.Kirk@Sun.COM netstack_rele(ns); 5298023SPhil.Kirk@Sun.COM id_free(ipnet_minor_space, ipnet->ipnet_minor); 5308023SPhil.Kirk@Sun.COM if (ipnet->ipnet_if != NULL) 5318023SPhil.Kirk@Sun.COM ipnetif_refrele(ipnet->ipnet_if); 5328023SPhil.Kirk@Sun.COM kmem_free(ipnet, sizeof (*ipnet)); 5338023SPhil.Kirk@Sun.COM } 5348023SPhil.Kirk@Sun.COM return (err); 5358023SPhil.Kirk@Sun.COM } 5368023SPhil.Kirk@Sun.COM 5378023SPhil.Kirk@Sun.COM static int 5388023SPhil.Kirk@Sun.COM ipnet_close(queue_t *rq) 5398023SPhil.Kirk@Sun.COM { 5408023SPhil.Kirk@Sun.COM ipnet_t *ipnet = rq->q_ptr; 5418023SPhil.Kirk@Sun.COM ipnet_stack_t *ips = ipnet->ipnet_ns->netstack_ipnet; 5428023SPhil.Kirk@Sun.COM 5438023SPhil.Kirk@Sun.COM if (ipnet->ipnet_flags & IPNET_PROMISC_PHYS) 5448023SPhil.Kirk@Sun.COM ipnet_leave_allmulti(ipnet->ipnet_if, ips); 5458023SPhil.Kirk@Sun.COM if (ipnet->ipnet_flags & IPNET_PROMISC_MULTI) 5468023SPhil.Kirk@Sun.COM ipnet_leave_allmulti(ipnet->ipnet_if, ips); 5478023SPhil.Kirk@Sun.COM 5488023SPhil.Kirk@Sun.COM mutex_enter(&ips->ips_walkers_lock); 5498023SPhil.Kirk@Sun.COM while (ips->ips_walkers_cnt != 0) 5508023SPhil.Kirk@Sun.COM cv_wait(&ips->ips_walkers_cv, &ips->ips_walkers_lock); 5518023SPhil.Kirk@Sun.COM 5528023SPhil.Kirk@Sun.COM qprocsoff(rq); 5538023SPhil.Kirk@Sun.COM 5548023SPhil.Kirk@Sun.COM list_remove(&ips->ips_str_list, ipnet); 5558023SPhil.Kirk@Sun.COM if (ipnet->ipnet_if != NULL) 5568023SPhil.Kirk@Sun.COM ipnetif_refrele(ipnet->ipnet_if); 5578023SPhil.Kirk@Sun.COM id_free(ipnet_minor_space, ipnet->ipnet_minor); 5588023SPhil.Kirk@Sun.COM kmem_free(ipnet, sizeof (*ipnet)); 5598023SPhil.Kirk@Sun.COM 5608023SPhil.Kirk@Sun.COM if (list_is_empty(&ips->ips_str_list)) 5618023SPhil.Kirk@Sun.COM ipobs_unregister_hook(ips->ips_netstack, ipnet_input); 5628023SPhil.Kirk@Sun.COM 5638023SPhil.Kirk@Sun.COM mutex_exit(&ips->ips_walkers_lock); 5648023SPhil.Kirk@Sun.COM netstack_rele(ips->ips_netstack); 5658023SPhil.Kirk@Sun.COM return (0); 5668023SPhil.Kirk@Sun.COM } 5678023SPhil.Kirk@Sun.COM 5688023SPhil.Kirk@Sun.COM static int 5698023SPhil.Kirk@Sun.COM ipnet_wput(queue_t *q, mblk_t *mp) 5708023SPhil.Kirk@Sun.COM { 5718023SPhil.Kirk@Sun.COM switch (mp->b_datap->db_type) { 5728023SPhil.Kirk@Sun.COM case M_FLUSH: 5738023SPhil.Kirk@Sun.COM if (*mp->b_rptr & FLUSHW) { 5748023SPhil.Kirk@Sun.COM flushq(q, FLUSHDATA); 5758023SPhil.Kirk@Sun.COM *mp->b_rptr &= ~FLUSHW; 5768023SPhil.Kirk@Sun.COM } 5778023SPhil.Kirk@Sun.COM if (*mp->b_rptr & FLUSHR) 5788023SPhil.Kirk@Sun.COM qreply(q, mp); 5798023SPhil.Kirk@Sun.COM else 5808023SPhil.Kirk@Sun.COM freemsg(mp); 5818023SPhil.Kirk@Sun.COM break; 5828023SPhil.Kirk@Sun.COM case M_PROTO: 5838023SPhil.Kirk@Sun.COM case M_PCPROTO: 5848023SPhil.Kirk@Sun.COM ipnet_wputnondata(q, mp); 5858023SPhil.Kirk@Sun.COM break; 5868023SPhil.Kirk@Sun.COM case M_IOCTL: 5878023SPhil.Kirk@Sun.COM ipnet_ioctl(q, mp); 5888023SPhil.Kirk@Sun.COM break; 5898023SPhil.Kirk@Sun.COM case M_IOCDATA: 5908023SPhil.Kirk@Sun.COM ipnet_iocdata(q, mp); 5918023SPhil.Kirk@Sun.COM break; 5928023SPhil.Kirk@Sun.COM default: 5938023SPhil.Kirk@Sun.COM freemsg(mp); 5948023SPhil.Kirk@Sun.COM break; 5958023SPhil.Kirk@Sun.COM } 5968023SPhil.Kirk@Sun.COM return (0); 5978023SPhil.Kirk@Sun.COM } 5988023SPhil.Kirk@Sun.COM 5998023SPhil.Kirk@Sun.COM static int 6008023SPhil.Kirk@Sun.COM ipnet_rsrv(queue_t *q) 6018023SPhil.Kirk@Sun.COM { 6028023SPhil.Kirk@Sun.COM mblk_t *mp; 6038023SPhil.Kirk@Sun.COM 6048023SPhil.Kirk@Sun.COM while ((mp = getq(q)) != NULL) { 6058023SPhil.Kirk@Sun.COM ASSERT(DB_TYPE(mp) == M_DATA); 6068023SPhil.Kirk@Sun.COM if (canputnext(q)) { 6078023SPhil.Kirk@Sun.COM putnext(q, mp); 6088023SPhil.Kirk@Sun.COM } else { 6098023SPhil.Kirk@Sun.COM (void) putbq(q, mp); 6108023SPhil.Kirk@Sun.COM break; 6118023SPhil.Kirk@Sun.COM } 6128023SPhil.Kirk@Sun.COM } 6138023SPhil.Kirk@Sun.COM return (0); 6148023SPhil.Kirk@Sun.COM } 6158023SPhil.Kirk@Sun.COM 6168023SPhil.Kirk@Sun.COM static void 6178023SPhil.Kirk@Sun.COM ipnet_ioctl(queue_t *q, mblk_t *mp) 6188023SPhil.Kirk@Sun.COM { 6198023SPhil.Kirk@Sun.COM struct iocblk *iocp = (struct iocblk *)mp->b_rptr; 6208023SPhil.Kirk@Sun.COM 6218023SPhil.Kirk@Sun.COM switch (iocp->ioc_cmd) { 6228023SPhil.Kirk@Sun.COM case DLIOCRAW: 6238023SPhil.Kirk@Sun.COM miocack(q, mp, 0, 0); 6248023SPhil.Kirk@Sun.COM break; 6258023SPhil.Kirk@Sun.COM case DLIOCIPNETINFO: 6268023SPhil.Kirk@Sun.COM if (iocp->ioc_count == TRANSPARENT) { 6278023SPhil.Kirk@Sun.COM mcopyin(mp, NULL, sizeof (uint_t), NULL); 6288023SPhil.Kirk@Sun.COM qreply(q, mp); 6298023SPhil.Kirk@Sun.COM break; 6308023SPhil.Kirk@Sun.COM } 6318023SPhil.Kirk@Sun.COM /* Fallthrough, we don't support I_STR with DLIOCIPNETINFO. */ 6328023SPhil.Kirk@Sun.COM default: 6338023SPhil.Kirk@Sun.COM miocnak(q, mp, 0, EINVAL); 6348023SPhil.Kirk@Sun.COM break; 6358023SPhil.Kirk@Sun.COM } 6368023SPhil.Kirk@Sun.COM } 6378023SPhil.Kirk@Sun.COM 6388023SPhil.Kirk@Sun.COM static void 6398023SPhil.Kirk@Sun.COM ipnet_iocdata(queue_t *q, mblk_t *mp) 6408023SPhil.Kirk@Sun.COM { 6418023SPhil.Kirk@Sun.COM struct iocblk *iocp = (struct iocblk *)mp->b_rptr; 6428023SPhil.Kirk@Sun.COM ipnet_t *ipnet = q->q_ptr; 6438023SPhil.Kirk@Sun.COM 6448023SPhil.Kirk@Sun.COM switch (iocp->ioc_cmd) { 6458023SPhil.Kirk@Sun.COM case DLIOCIPNETINFO: 6468023SPhil.Kirk@Sun.COM if (*(int *)mp->b_cont->b_rptr == 1) 6478023SPhil.Kirk@Sun.COM ipnet->ipnet_flags |= IPNET_INFO; 6488023SPhil.Kirk@Sun.COM else if (*(int *)mp->b_cont->b_rptr == 0) 6498023SPhil.Kirk@Sun.COM ipnet->ipnet_flags &= ~IPNET_INFO; 6508023SPhil.Kirk@Sun.COM else 6518023SPhil.Kirk@Sun.COM goto iocnak; 6528023SPhil.Kirk@Sun.COM miocack(q, mp, 0, DL_IPNETINFO_VERSION); 6538023SPhil.Kirk@Sun.COM break; 6548023SPhil.Kirk@Sun.COM default: 6558023SPhil.Kirk@Sun.COM iocnak: 6568023SPhil.Kirk@Sun.COM miocnak(q, mp, 0, EINVAL); 6578023SPhil.Kirk@Sun.COM break; 6588023SPhil.Kirk@Sun.COM } 6598023SPhil.Kirk@Sun.COM } 6608023SPhil.Kirk@Sun.COM 6618023SPhil.Kirk@Sun.COM static void 6628023SPhil.Kirk@Sun.COM ipnet_wputnondata(queue_t *q, mblk_t *mp) 6638023SPhil.Kirk@Sun.COM { 6648023SPhil.Kirk@Sun.COM union DL_primitives *dlp = (union DL_primitives *)mp->b_rptr; 6658023SPhil.Kirk@Sun.COM t_uscalar_t prim = dlp->dl_primitive; 6668023SPhil.Kirk@Sun.COM 6678023SPhil.Kirk@Sun.COM switch (prim) { 6688023SPhil.Kirk@Sun.COM case DL_INFO_REQ: 6698023SPhil.Kirk@Sun.COM ipnet_inforeq(q, mp); 6708023SPhil.Kirk@Sun.COM break; 6718023SPhil.Kirk@Sun.COM case DL_UNBIND_REQ: 6728023SPhil.Kirk@Sun.COM ipnet_unbindreq(q, mp); 6738023SPhil.Kirk@Sun.COM break; 6748023SPhil.Kirk@Sun.COM case DL_BIND_REQ: 6758023SPhil.Kirk@Sun.COM ipnet_bindreq(q, mp); 6768023SPhil.Kirk@Sun.COM break; 6778023SPhil.Kirk@Sun.COM case DL_PROMISCON_REQ: 6788023SPhil.Kirk@Sun.COM ipnet_dlpromisconreq(q, mp); 6798023SPhil.Kirk@Sun.COM break; 6808023SPhil.Kirk@Sun.COM case DL_PROMISCOFF_REQ: 6818023SPhil.Kirk@Sun.COM ipnet_dlpromiscoffreq(q, mp); 6828023SPhil.Kirk@Sun.COM break; 6838023SPhil.Kirk@Sun.COM case DL_UNITDATA_REQ: 6848023SPhil.Kirk@Sun.COM case DL_DETACH_REQ: 6858023SPhil.Kirk@Sun.COM case DL_PHYS_ADDR_REQ: 6868023SPhil.Kirk@Sun.COM case DL_SET_PHYS_ADDR_REQ: 6878023SPhil.Kirk@Sun.COM case DL_ENABMULTI_REQ: 6888023SPhil.Kirk@Sun.COM case DL_DISABMULTI_REQ: 6898023SPhil.Kirk@Sun.COM case DL_ATTACH_REQ: 6908023SPhil.Kirk@Sun.COM dlerrorack(q, mp, prim, DL_UNSUPPORTED, 0); 6918023SPhil.Kirk@Sun.COM break; 6928023SPhil.Kirk@Sun.COM default: 6938023SPhil.Kirk@Sun.COM dlerrorack(q, mp, prim, DL_BADPRIM, 0); 6948023SPhil.Kirk@Sun.COM break; 6958023SPhil.Kirk@Sun.COM } 6968023SPhil.Kirk@Sun.COM } 6978023SPhil.Kirk@Sun.COM 6988023SPhil.Kirk@Sun.COM static void 6998023SPhil.Kirk@Sun.COM ipnet_inforeq(queue_t *q, mblk_t *mp) 7008023SPhil.Kirk@Sun.COM { 7018023SPhil.Kirk@Sun.COM dl_info_ack_t *dlip; 7028023SPhil.Kirk@Sun.COM size_t size = sizeof (dl_info_ack_t) + sizeof (ushort_t); 7038023SPhil.Kirk@Sun.COM 7048023SPhil.Kirk@Sun.COM if (MBLKL(mp) < DL_INFO_REQ_SIZE) { 7058023SPhil.Kirk@Sun.COM dlerrorack(q, mp, DL_INFO_REQ, DL_BADPRIM, 0); 7068023SPhil.Kirk@Sun.COM return; 7078023SPhil.Kirk@Sun.COM } 7088023SPhil.Kirk@Sun.COM 7098023SPhil.Kirk@Sun.COM if ((mp = mexchange(q, mp, size, M_PCPROTO, DL_INFO_ACK)) == NULL) 7108023SPhil.Kirk@Sun.COM return; 7118023SPhil.Kirk@Sun.COM 7128023SPhil.Kirk@Sun.COM dlip = (dl_info_ack_t *)mp->b_rptr; 7138023SPhil.Kirk@Sun.COM *dlip = ipnet_infoack; 7148023SPhil.Kirk@Sun.COM qreply(q, mp); 7158023SPhil.Kirk@Sun.COM } 7168023SPhil.Kirk@Sun.COM 7178023SPhil.Kirk@Sun.COM static void 7188023SPhil.Kirk@Sun.COM ipnet_bindreq(queue_t *q, mblk_t *mp) 7198023SPhil.Kirk@Sun.COM { 7208023SPhil.Kirk@Sun.COM union DL_primitives *dlp = (union DL_primitives *)mp->b_rptr; 7218023SPhil.Kirk@Sun.COM int32_t sap; 7228023SPhil.Kirk@Sun.COM ipnet_t *ipnet = q->q_ptr; 7238023SPhil.Kirk@Sun.COM 7248023SPhil.Kirk@Sun.COM if (MBLKL(mp) < DL_BIND_REQ_SIZE) { 7258023SPhil.Kirk@Sun.COM dlerrorack(q, mp, DL_BIND_REQ, DL_BADPRIM, 0); 7268023SPhil.Kirk@Sun.COM return; 7278023SPhil.Kirk@Sun.COM } 7288023SPhil.Kirk@Sun.COM 7298023SPhil.Kirk@Sun.COM sap = dlp->bind_req.dl_sap; 7308023SPhil.Kirk@Sun.COM if (sap != IPV4_VERSION && sap != IPV6_VERSION && sap != 0) { 7318023SPhil.Kirk@Sun.COM dlerrorack(q, mp, DL_BIND_REQ, DL_BADSAP, 0); 7328023SPhil.Kirk@Sun.COM } else { 7338023SPhil.Kirk@Sun.COM ipnet->ipnet_sap = sap; 7348023SPhil.Kirk@Sun.COM ipnet->ipnet_dlstate = DL_IDLE; 7358023SPhil.Kirk@Sun.COM dlbindack(q, mp, sap, 0, 0, 0, 0); 7368023SPhil.Kirk@Sun.COM } 7378023SPhil.Kirk@Sun.COM } 7388023SPhil.Kirk@Sun.COM 7398023SPhil.Kirk@Sun.COM static void 7408023SPhil.Kirk@Sun.COM ipnet_unbindreq(queue_t *q, mblk_t *mp) 7418023SPhil.Kirk@Sun.COM { 7428023SPhil.Kirk@Sun.COM ipnet_t *ipnet = q->q_ptr; 7438023SPhil.Kirk@Sun.COM 7448023SPhil.Kirk@Sun.COM if (MBLKL(mp) < DL_UNBIND_REQ_SIZE) { 7458023SPhil.Kirk@Sun.COM dlerrorack(q, mp, DL_UNBIND_REQ, DL_BADPRIM, 0); 7468023SPhil.Kirk@Sun.COM return; 7478023SPhil.Kirk@Sun.COM } 7488023SPhil.Kirk@Sun.COM 7498023SPhil.Kirk@Sun.COM if (ipnet->ipnet_dlstate != DL_IDLE) { 7508023SPhil.Kirk@Sun.COM dlerrorack(q, mp, DL_UNBIND_REQ, DL_OUTSTATE, 0); 7518023SPhil.Kirk@Sun.COM } else { 7528023SPhil.Kirk@Sun.COM ipnet->ipnet_dlstate = DL_UNBOUND; 7538023SPhil.Kirk@Sun.COM ipnet->ipnet_sap = 0; 7548023SPhil.Kirk@Sun.COM dlokack(q, mp, DL_UNBIND_REQ); 7558023SPhil.Kirk@Sun.COM } 7568023SPhil.Kirk@Sun.COM } 7578023SPhil.Kirk@Sun.COM 7588023SPhil.Kirk@Sun.COM static void 7598023SPhil.Kirk@Sun.COM ipnet_dlpromisconreq(queue_t *q, mblk_t *mp) 7608023SPhil.Kirk@Sun.COM { 7618023SPhil.Kirk@Sun.COM ipnet_t *ipnet = q->q_ptr; 7628023SPhil.Kirk@Sun.COM t_uscalar_t level; 7638023SPhil.Kirk@Sun.COM int err; 7648023SPhil.Kirk@Sun.COM 7658023SPhil.Kirk@Sun.COM if (MBLKL(mp) < DL_PROMISCON_REQ_SIZE) { 7668023SPhil.Kirk@Sun.COM dlerrorack(q, mp, DL_PROMISCON_REQ, DL_BADPRIM, 0); 7678023SPhil.Kirk@Sun.COM return; 7688023SPhil.Kirk@Sun.COM } 7698023SPhil.Kirk@Sun.COM 7708023SPhil.Kirk@Sun.COM if (ipnet->ipnet_flags & IPNET_LOMODE) { 7718023SPhil.Kirk@Sun.COM dlokack(q, mp, DL_PROMISCON_REQ); 7728023SPhil.Kirk@Sun.COM return; 7738023SPhil.Kirk@Sun.COM } 7748023SPhil.Kirk@Sun.COM 7758023SPhil.Kirk@Sun.COM level = ((dl_promiscon_req_t *)mp->b_rptr)->dl_level; 7768023SPhil.Kirk@Sun.COM if (level == DL_PROMISC_PHYS || level == DL_PROMISC_MULTI) { 7778023SPhil.Kirk@Sun.COM if ((err = ipnet_join_allmulti(ipnet->ipnet_if, 7788023SPhil.Kirk@Sun.COM ipnet->ipnet_ns->netstack_ipnet)) != 0) { 7798023SPhil.Kirk@Sun.COM dlerrorack(q, mp, DL_PROMISCON_REQ, DL_SYSERR, err); 7808023SPhil.Kirk@Sun.COM return; 7818023SPhil.Kirk@Sun.COM } 7828023SPhil.Kirk@Sun.COM } 7838023SPhil.Kirk@Sun.COM 7848023SPhil.Kirk@Sun.COM switch (level) { 7858023SPhil.Kirk@Sun.COM case DL_PROMISC_PHYS: 7868023SPhil.Kirk@Sun.COM ipnet->ipnet_flags |= IPNET_PROMISC_PHYS; 7878023SPhil.Kirk@Sun.COM break; 7888023SPhil.Kirk@Sun.COM case DL_PROMISC_SAP: 7898023SPhil.Kirk@Sun.COM ipnet->ipnet_flags |= IPNET_PROMISC_SAP; 7908023SPhil.Kirk@Sun.COM break; 7918023SPhil.Kirk@Sun.COM case DL_PROMISC_MULTI: 7928023SPhil.Kirk@Sun.COM ipnet->ipnet_flags |= IPNET_PROMISC_MULTI; 7938023SPhil.Kirk@Sun.COM break; 7948023SPhil.Kirk@Sun.COM default: 7958023SPhil.Kirk@Sun.COM dlerrorack(q, mp, DL_PROMISCON_REQ, DL_BADPRIM, 0); 7968023SPhil.Kirk@Sun.COM return; 7978023SPhil.Kirk@Sun.COM } 7988023SPhil.Kirk@Sun.COM 7998023SPhil.Kirk@Sun.COM dlokack(q, mp, DL_PROMISCON_REQ); 8008023SPhil.Kirk@Sun.COM } 8018023SPhil.Kirk@Sun.COM 8028023SPhil.Kirk@Sun.COM static void 8038023SPhil.Kirk@Sun.COM ipnet_dlpromiscoffreq(queue_t *q, mblk_t *mp) 8048023SPhil.Kirk@Sun.COM { 8058023SPhil.Kirk@Sun.COM ipnet_t *ipnet = q->q_ptr; 8068023SPhil.Kirk@Sun.COM t_uscalar_t level; 8078023SPhil.Kirk@Sun.COM uint16_t orig_ipnet_flags = ipnet->ipnet_flags; 8088023SPhil.Kirk@Sun.COM 8098023SPhil.Kirk@Sun.COM if (MBLKL(mp) < DL_PROMISCOFF_REQ_SIZE) { 8108023SPhil.Kirk@Sun.COM dlerrorack(q, mp, DL_PROMISCOFF_REQ, DL_BADPRIM, 0); 8118023SPhil.Kirk@Sun.COM return; 8128023SPhil.Kirk@Sun.COM } 8138023SPhil.Kirk@Sun.COM 8148023SPhil.Kirk@Sun.COM if (ipnet->ipnet_flags & IPNET_LOMODE) { 8158023SPhil.Kirk@Sun.COM dlokack(q, mp, DL_PROMISCOFF_REQ); 8168023SPhil.Kirk@Sun.COM return; 8178023SPhil.Kirk@Sun.COM } 8188023SPhil.Kirk@Sun.COM 8198023SPhil.Kirk@Sun.COM level = ((dl_promiscon_req_t *)mp->b_rptr)->dl_level; 8208023SPhil.Kirk@Sun.COM switch (level) { 8218023SPhil.Kirk@Sun.COM case DL_PROMISC_PHYS: 8228023SPhil.Kirk@Sun.COM if (ipnet->ipnet_flags & IPNET_PROMISC_PHYS) 8238023SPhil.Kirk@Sun.COM ipnet->ipnet_flags &= ~IPNET_PROMISC_PHYS; 8248023SPhil.Kirk@Sun.COM break; 8258023SPhil.Kirk@Sun.COM case DL_PROMISC_SAP: 8268023SPhil.Kirk@Sun.COM if (ipnet->ipnet_flags & IPNET_PROMISC_SAP) 8278023SPhil.Kirk@Sun.COM ipnet->ipnet_flags &= ~IPNET_PROMISC_SAP; 8288023SPhil.Kirk@Sun.COM break; 8298023SPhil.Kirk@Sun.COM case DL_PROMISC_MULTI: 8308023SPhil.Kirk@Sun.COM if (ipnet->ipnet_flags & IPNET_PROMISC_MULTI) 8318023SPhil.Kirk@Sun.COM ipnet->ipnet_flags &= ~IPNET_PROMISC_MULTI; 8328023SPhil.Kirk@Sun.COM break; 8338023SPhil.Kirk@Sun.COM default: 8348023SPhil.Kirk@Sun.COM dlerrorack(q, mp, DL_PROMISCOFF_REQ, DL_BADPRIM, 0); 8358023SPhil.Kirk@Sun.COM return; 8368023SPhil.Kirk@Sun.COM } 8378023SPhil.Kirk@Sun.COM 8388023SPhil.Kirk@Sun.COM if (orig_ipnet_flags == ipnet->ipnet_flags) { 8398023SPhil.Kirk@Sun.COM dlerrorack(q, mp, DL_PROMISCOFF_REQ, DL_NOTENAB, 0); 8408023SPhil.Kirk@Sun.COM return; 8418023SPhil.Kirk@Sun.COM } 8428023SPhil.Kirk@Sun.COM 8438023SPhil.Kirk@Sun.COM if (level == DL_PROMISC_PHYS || level == DL_PROMISC_MULTI) { 8448023SPhil.Kirk@Sun.COM ipnet_leave_allmulti(ipnet->ipnet_if, 8458023SPhil.Kirk@Sun.COM ipnet->ipnet_ns->netstack_ipnet); 8468023SPhil.Kirk@Sun.COM } 8478023SPhil.Kirk@Sun.COM 8488023SPhil.Kirk@Sun.COM dlokack(q, mp, DL_PROMISCOFF_REQ); 8498023SPhil.Kirk@Sun.COM } 8508023SPhil.Kirk@Sun.COM 8518023SPhil.Kirk@Sun.COM static int 8528023SPhil.Kirk@Sun.COM ipnet_join_allmulti(ipnetif_t *ipnetif, ipnet_stack_t *ips) 8538023SPhil.Kirk@Sun.COM { 8548023SPhil.Kirk@Sun.COM int err = 0; 8558023SPhil.Kirk@Sun.COM ip_stack_t *ipst = ips->ips_netstack->netstack_ip; 8568023SPhil.Kirk@Sun.COM uint64_t index = ipnetif->if_index; 8578023SPhil.Kirk@Sun.COM 8588023SPhil.Kirk@Sun.COM mutex_enter(&ips->ips_event_lock); 8598023SPhil.Kirk@Sun.COM if (ipnetif->if_multicnt == 0) { 8608023SPhil.Kirk@Sun.COM ASSERT((ipnetif->if_flags & 8618023SPhil.Kirk@Sun.COM (IPNETIF_IPV4ALLMULTI | IPNETIF_IPV6ALLMULTI)) == 0); 8628023SPhil.Kirk@Sun.COM if (ipnetif->if_flags & IPNETIF_IPV4PLUMBED) { 8638023SPhil.Kirk@Sun.COM err = ip_join_allmulti(index, B_FALSE, ipst); 8648023SPhil.Kirk@Sun.COM if (err != 0) 8658023SPhil.Kirk@Sun.COM goto done; 8668023SPhil.Kirk@Sun.COM ipnetif->if_flags |= IPNETIF_IPV4ALLMULTI; 8678023SPhil.Kirk@Sun.COM } 8688023SPhil.Kirk@Sun.COM if (ipnetif->if_flags & IPNETIF_IPV6PLUMBED) { 8698023SPhil.Kirk@Sun.COM err = ip_join_allmulti(index, B_TRUE, ipst); 8708023SPhil.Kirk@Sun.COM if (err != 0 && 8718023SPhil.Kirk@Sun.COM (ipnetif->if_flags & IPNETIF_IPV4ALLMULTI)) { 8728023SPhil.Kirk@Sun.COM (void) ip_leave_allmulti(index, B_FALSE, ipst); 8738023SPhil.Kirk@Sun.COM ipnetif->if_flags &= ~IPNETIF_IPV4ALLMULTI; 8748023SPhil.Kirk@Sun.COM goto done; 8758023SPhil.Kirk@Sun.COM } 8768023SPhil.Kirk@Sun.COM ipnetif->if_flags |= IPNETIF_IPV6ALLMULTI; 8778023SPhil.Kirk@Sun.COM } 8788023SPhil.Kirk@Sun.COM } 8798023SPhil.Kirk@Sun.COM ipnetif->if_multicnt++; 8808023SPhil.Kirk@Sun.COM 8818023SPhil.Kirk@Sun.COM done: 8828023SPhil.Kirk@Sun.COM mutex_exit(&ips->ips_event_lock); 8838023SPhil.Kirk@Sun.COM return (err); 8848023SPhil.Kirk@Sun.COM } 8858023SPhil.Kirk@Sun.COM 8868023SPhil.Kirk@Sun.COM static void 8878023SPhil.Kirk@Sun.COM ipnet_leave_allmulti(ipnetif_t *ipnetif, ipnet_stack_t *ips) 8888023SPhil.Kirk@Sun.COM { 8898023SPhil.Kirk@Sun.COM int err; 8908023SPhil.Kirk@Sun.COM ip_stack_t *ipst = ips->ips_netstack->netstack_ip; 8918023SPhil.Kirk@Sun.COM uint64_t index = ipnetif->if_index; 8928023SPhil.Kirk@Sun.COM 8938023SPhil.Kirk@Sun.COM mutex_enter(&ips->ips_event_lock); 8948023SPhil.Kirk@Sun.COM ASSERT(ipnetif->if_multicnt != 0); 8958023SPhil.Kirk@Sun.COM if (--ipnetif->if_multicnt == 0) { 8968023SPhil.Kirk@Sun.COM if (ipnetif->if_flags & IPNETIF_IPV4ALLMULTI) { 8978023SPhil.Kirk@Sun.COM err = ip_leave_allmulti(index, B_FALSE, ipst); 8988023SPhil.Kirk@Sun.COM ASSERT(err == 0 || err == ENODEV); 8998023SPhil.Kirk@Sun.COM ipnetif->if_flags &= ~IPNETIF_IPV4ALLMULTI; 9008023SPhil.Kirk@Sun.COM } 9018023SPhil.Kirk@Sun.COM if (ipnetif->if_flags & IPNETIF_IPV6ALLMULTI) { 9028023SPhil.Kirk@Sun.COM err = ip_leave_allmulti(index, B_TRUE, ipst); 9038023SPhil.Kirk@Sun.COM ASSERT(err == 0 || err == ENODEV); 9048023SPhil.Kirk@Sun.COM ipnetif->if_flags &= ~IPNETIF_IPV6ALLMULTI; 9058023SPhil.Kirk@Sun.COM } 9068023SPhil.Kirk@Sun.COM } 9078023SPhil.Kirk@Sun.COM mutex_exit(&ips->ips_event_lock); 9088023SPhil.Kirk@Sun.COM } 9098023SPhil.Kirk@Sun.COM 9108023SPhil.Kirk@Sun.COM static mblk_t * 9118023SPhil.Kirk@Sun.COM ipnet_addheader(ipobs_hook_data_t *ihd, mblk_t *mp) 9128023SPhil.Kirk@Sun.COM { 9138023SPhil.Kirk@Sun.COM mblk_t *dlhdr; 9148023SPhil.Kirk@Sun.COM dl_ipnetinfo_t *dl; 9158023SPhil.Kirk@Sun.COM 9168023SPhil.Kirk@Sun.COM if ((dlhdr = allocb(sizeof (dl_ipnetinfo_t), BPRI_HI)) == NULL) { 9178023SPhil.Kirk@Sun.COM freemsg(mp); 9188023SPhil.Kirk@Sun.COM return (NULL); 9198023SPhil.Kirk@Sun.COM } 9208023SPhil.Kirk@Sun.COM dl = (dl_ipnetinfo_t *)dlhdr->b_rptr; 9218023SPhil.Kirk@Sun.COM dl->dli_version = DL_IPNETINFO_VERSION; 9228023SPhil.Kirk@Sun.COM dl->dli_len = htons(sizeof (*dl)); 9238023SPhil.Kirk@Sun.COM dl->dli_ipver = ihd->ihd_ipver; 9248023SPhil.Kirk@Sun.COM dl->dli_srczone = BE_64((uint64_t)ihd->ihd_zsrc); 9258023SPhil.Kirk@Sun.COM dl->dli_dstzone = BE_64((uint64_t)ihd->ihd_zdst); 9268023SPhil.Kirk@Sun.COM dlhdr->b_wptr += sizeof (*dl); 9278023SPhil.Kirk@Sun.COM dlhdr->b_cont = mp; 9288023SPhil.Kirk@Sun.COM 9298023SPhil.Kirk@Sun.COM return (dlhdr); 9308023SPhil.Kirk@Sun.COM } 9318023SPhil.Kirk@Sun.COM 9328023SPhil.Kirk@Sun.COM static ipnet_addrtype_t 9338023SPhil.Kirk@Sun.COM ipnet_get_addrtype(ipnet_t *ipnet, ipnet_addrp_t *addr) 9348023SPhil.Kirk@Sun.COM { 9358023SPhil.Kirk@Sun.COM list_t *list; 9368023SPhil.Kirk@Sun.COM ipnetif_t *ipnetif = ipnet->ipnet_if; 9378023SPhil.Kirk@Sun.COM ipnetif_addr_t *ifaddr; 9388023SPhil.Kirk@Sun.COM ipnet_addrtype_t addrtype = IPNETADDR_UNKNOWN; 9398023SPhil.Kirk@Sun.COM 9408023SPhil.Kirk@Sun.COM /* First check if the address is multicast or limited broadcast. */ 9418023SPhil.Kirk@Sun.COM switch (addr->iap_family) { 9428023SPhil.Kirk@Sun.COM case AF_INET: 9438023SPhil.Kirk@Sun.COM if (CLASSD(*(addr->iap_addr4)) || 9448023SPhil.Kirk@Sun.COM *(addr->iap_addr4) == INADDR_BROADCAST) 9458023SPhil.Kirk@Sun.COM return (IPNETADDR_MBCAST); 9468023SPhil.Kirk@Sun.COM break; 9478023SPhil.Kirk@Sun.COM case AF_INET6: 9488023SPhil.Kirk@Sun.COM if (IN6_IS_ADDR_MULTICAST(addr->iap_addr6)) 9498023SPhil.Kirk@Sun.COM return (IPNETADDR_MBCAST); 9508023SPhil.Kirk@Sun.COM break; 9518023SPhil.Kirk@Sun.COM } 9528023SPhil.Kirk@Sun.COM 9538023SPhil.Kirk@Sun.COM /* 9548023SPhil.Kirk@Sun.COM * Walk the address list to see if the address belongs to our 9558023SPhil.Kirk@Sun.COM * interface or is one of our subnet broadcast addresses. 9568023SPhil.Kirk@Sun.COM */ 9578023SPhil.Kirk@Sun.COM mutex_enter(&ipnetif->if_addr_lock); 9588023SPhil.Kirk@Sun.COM list = (addr->iap_family == AF_INET) ? 9598023SPhil.Kirk@Sun.COM &ipnetif->if_ip4addr_list : &ipnetif->if_ip6addr_list; 9608023SPhil.Kirk@Sun.COM for (ifaddr = list_head(list); 9618023SPhil.Kirk@Sun.COM ifaddr != NULL && addrtype == IPNETADDR_UNKNOWN; 9628023SPhil.Kirk@Sun.COM ifaddr = list_next(list, ifaddr)) { 9638023SPhil.Kirk@Sun.COM /* 9648023SPhil.Kirk@Sun.COM * If we're not in the global zone, then only look at 9658023SPhil.Kirk@Sun.COM * addresses in our zone. 9668023SPhil.Kirk@Sun.COM */ 9678023SPhil.Kirk@Sun.COM if (ipnet->ipnet_zoneid != GLOBAL_ZONEID && 9688023SPhil.Kirk@Sun.COM ipnet->ipnet_zoneid != ifaddr->ifa_zone) 9698023SPhil.Kirk@Sun.COM continue; 9708023SPhil.Kirk@Sun.COM switch (addr->iap_family) { 9718023SPhil.Kirk@Sun.COM case AF_INET: 9728023SPhil.Kirk@Sun.COM if (ifaddr->ifa_ip4addr != INADDR_ANY && 9738023SPhil.Kirk@Sun.COM *(addr->iap_addr4) == ifaddr->ifa_ip4addr) 9748023SPhil.Kirk@Sun.COM addrtype = IPNETADDR_MYADDR; 9758023SPhil.Kirk@Sun.COM else if (ifaddr->ifa_brdaddr != INADDR_ANY && 9768023SPhil.Kirk@Sun.COM *(addr->iap_addr4) == ifaddr->ifa_brdaddr) 9778023SPhil.Kirk@Sun.COM addrtype = IPNETADDR_MBCAST; 9788023SPhil.Kirk@Sun.COM break; 9798023SPhil.Kirk@Sun.COM case AF_INET6: 9808023SPhil.Kirk@Sun.COM if (IN6_ARE_ADDR_EQUAL(addr->iap_addr6, 9818023SPhil.Kirk@Sun.COM &ifaddr->ifa_ip6addr)) 9828023SPhil.Kirk@Sun.COM addrtype = IPNETADDR_MYADDR; 9838023SPhil.Kirk@Sun.COM break; 9848023SPhil.Kirk@Sun.COM } 9858023SPhil.Kirk@Sun.COM } 9868023SPhil.Kirk@Sun.COM mutex_exit(&ipnetif->if_addr_lock); 9878023SPhil.Kirk@Sun.COM 9888023SPhil.Kirk@Sun.COM return (addrtype); 9898023SPhil.Kirk@Sun.COM } 9908023SPhil.Kirk@Sun.COM 9918023SPhil.Kirk@Sun.COM /* 9928023SPhil.Kirk@Sun.COM * Verify if the packet contained in ihd should be passed up to the 9938023SPhil.Kirk@Sun.COM * ipnet client stream. 9948023SPhil.Kirk@Sun.COM */ 9958023SPhil.Kirk@Sun.COM static boolean_t 9968023SPhil.Kirk@Sun.COM ipnet_accept(ipnet_t *ipnet, ipobs_hook_data_t *ihd, ipnet_addrp_t *src, 9978023SPhil.Kirk@Sun.COM ipnet_addrp_t *dst) 9988023SPhil.Kirk@Sun.COM { 999*8485SPeter.Memishian@Sun.COM boolean_t obsif; 10008023SPhil.Kirk@Sun.COM uint64_t ifindex = ipnet->ipnet_if->if_index; 10018023SPhil.Kirk@Sun.COM ipnet_addrtype_t srctype, dsttype; 10028023SPhil.Kirk@Sun.COM 10038023SPhil.Kirk@Sun.COM srctype = ipnet_get_addrtype(ipnet, src); 10048023SPhil.Kirk@Sun.COM dsttype = ipnet_get_addrtype(ipnet, dst); 10058023SPhil.Kirk@Sun.COM 10068023SPhil.Kirk@Sun.COM /* 1007*8485SPeter.Memishian@Sun.COM * If the packet's ifindex matches ours, or the packet's group ifindex 1008*8485SPeter.Memishian@Sun.COM * matches ours, it's on the interface we're observing. (Thus, 1009*8485SPeter.Memishian@Sun.COM * observing on the group ifindex matches all ifindexes in the group.) 1010*8485SPeter.Memishian@Sun.COM */ 1011*8485SPeter.Memishian@Sun.COM obsif = (ihd->ihd_ifindex == ifindex || ihd->ihd_grifindex == ifindex); 1012*8485SPeter.Memishian@Sun.COM 1013*8485SPeter.Memishian@Sun.COM /* 10148023SPhil.Kirk@Sun.COM * Do not allow an ipnet stream to see packets that are not from or to 10158023SPhil.Kirk@Sun.COM * its zone. The exception is when zones are using the shared stack 10168023SPhil.Kirk@Sun.COM * model. In this case, streams in the global zone have visibility 10178023SPhil.Kirk@Sun.COM * into other shared-stack zones, and broadcast and multicast traffic 10188023SPhil.Kirk@Sun.COM * is visible by all zones in the stack. 10198023SPhil.Kirk@Sun.COM */ 10208023SPhil.Kirk@Sun.COM if (ipnet->ipnet_zoneid != GLOBAL_ZONEID && 10218023SPhil.Kirk@Sun.COM dsttype != IPNETADDR_MBCAST) { 10228023SPhil.Kirk@Sun.COM if (ipnet->ipnet_zoneid != ihd->ihd_zsrc && 10238023SPhil.Kirk@Sun.COM ipnet->ipnet_zoneid != ihd->ihd_zdst) 10248023SPhil.Kirk@Sun.COM return (B_FALSE); 10258023SPhil.Kirk@Sun.COM } 10268023SPhil.Kirk@Sun.COM 10278023SPhil.Kirk@Sun.COM /* 10288023SPhil.Kirk@Sun.COM * If DL_PROMISC_SAP isn't enabled, then the bound SAP must match the 10298023SPhil.Kirk@Sun.COM * packet's IP version. 10308023SPhil.Kirk@Sun.COM */ 10318023SPhil.Kirk@Sun.COM if (!(ipnet->ipnet_flags & IPNET_PROMISC_SAP) && 10328023SPhil.Kirk@Sun.COM ipnet->ipnet_sap != ihd->ihd_ipver) 10338023SPhil.Kirk@Sun.COM return (B_FALSE); 10348023SPhil.Kirk@Sun.COM 10358023SPhil.Kirk@Sun.COM /* If the destination address is ours, then accept the packet. */ 10368023SPhil.Kirk@Sun.COM if (dsttype == IPNETADDR_MYADDR) 10378023SPhil.Kirk@Sun.COM return (B_TRUE); 10388023SPhil.Kirk@Sun.COM 10398023SPhil.Kirk@Sun.COM /* 10408023SPhil.Kirk@Sun.COM * If DL_PROMISC_PHYS is enabled, then we can see all packets that are 10418023SPhil.Kirk@Sun.COM * sent or received on the interface we're observing, or packets that 10428023SPhil.Kirk@Sun.COM * have our source address (this allows us to see packets we send). 10438023SPhil.Kirk@Sun.COM */ 10448023SPhil.Kirk@Sun.COM if (ipnet->ipnet_flags & IPNET_PROMISC_PHYS) { 1045*8485SPeter.Memishian@Sun.COM if (srctype == IPNETADDR_MYADDR || obsif) 10468023SPhil.Kirk@Sun.COM return (B_TRUE); 10478023SPhil.Kirk@Sun.COM } 10488023SPhil.Kirk@Sun.COM 10498023SPhil.Kirk@Sun.COM /* 10508023SPhil.Kirk@Sun.COM * We accept multicast and broadcast packets transmitted or received 10518023SPhil.Kirk@Sun.COM * on the interface we're observing. 10528023SPhil.Kirk@Sun.COM */ 1053*8485SPeter.Memishian@Sun.COM if (dsttype == IPNETADDR_MBCAST && obsif) 10548023SPhil.Kirk@Sun.COM return (B_TRUE); 10558023SPhil.Kirk@Sun.COM 10568023SPhil.Kirk@Sun.COM return (B_FALSE); 10578023SPhil.Kirk@Sun.COM } 10588023SPhil.Kirk@Sun.COM 10598023SPhil.Kirk@Sun.COM /* 10608023SPhil.Kirk@Sun.COM * Verify if the packet contained in ihd should be passed up to the ipnet 10618023SPhil.Kirk@Sun.COM * client stream that's in IPNET_LOMODE. 10628023SPhil.Kirk@Sun.COM */ 10638023SPhil.Kirk@Sun.COM /* ARGSUSED */ 10648023SPhil.Kirk@Sun.COM static boolean_t 10658023SPhil.Kirk@Sun.COM ipnet_loaccept(ipnet_t *ipnet, ipobs_hook_data_t *ihd, ipnet_addrp_t *src, 10668023SPhil.Kirk@Sun.COM ipnet_addrp_t *dst) 10678023SPhil.Kirk@Sun.COM { 10688023SPhil.Kirk@Sun.COM if (ihd->ihd_htype != IPOBS_HOOK_LOCAL) 10698023SPhil.Kirk@Sun.COM return (B_FALSE); 10708023SPhil.Kirk@Sun.COM 10718023SPhil.Kirk@Sun.COM /* 10728023SPhil.Kirk@Sun.COM * An ipnet stream must not see packets that are not from/to its zone. 10738023SPhil.Kirk@Sun.COM */ 10748023SPhil.Kirk@Sun.COM if (ipnet->ipnet_zoneid != GLOBAL_ZONEID) { 10758023SPhil.Kirk@Sun.COM if (ipnet->ipnet_zoneid != ihd->ihd_zsrc && 10768023SPhil.Kirk@Sun.COM ipnet->ipnet_zoneid != ihd->ihd_zdst) 10778023SPhil.Kirk@Sun.COM return (B_FALSE); 10788023SPhil.Kirk@Sun.COM } 10798023SPhil.Kirk@Sun.COM 10808023SPhil.Kirk@Sun.COM return (ipnet->ipnet_sap == 0 || ipnet->ipnet_sap == ihd->ihd_ipver); 10818023SPhil.Kirk@Sun.COM } 10828023SPhil.Kirk@Sun.COM 10838023SPhil.Kirk@Sun.COM static void 10848023SPhil.Kirk@Sun.COM ipnet_dispatch(void *arg) 10858023SPhil.Kirk@Sun.COM { 10868023SPhil.Kirk@Sun.COM mblk_t *mp = arg; 10878023SPhil.Kirk@Sun.COM ipobs_hook_data_t *ihd = (ipobs_hook_data_t *)mp->b_rptr; 10888023SPhil.Kirk@Sun.COM ipnet_t *ipnet; 10898023SPhil.Kirk@Sun.COM mblk_t *netmp; 10908023SPhil.Kirk@Sun.COM list_t *list; 10918023SPhil.Kirk@Sun.COM ipnet_stack_t *ips = ihd->ihd_stack->netstack_ipnet; 10928023SPhil.Kirk@Sun.COM ipnet_addrp_t src, dst; 10938023SPhil.Kirk@Sun.COM 10948023SPhil.Kirk@Sun.COM if (ihd->ihd_ipver == IPV4_VERSION) { 10958023SPhil.Kirk@Sun.COM src.iap_family = dst.iap_family = AF_INET; 10968023SPhil.Kirk@Sun.COM src.iap_addr4 = &((ipha_t *)(ihd->ihd_mp->b_rptr))->ipha_src; 10978023SPhil.Kirk@Sun.COM dst.iap_addr4 = &((ipha_t *)(ihd->ihd_mp->b_rptr))->ipha_dst; 10988023SPhil.Kirk@Sun.COM } else { 10998023SPhil.Kirk@Sun.COM src.iap_family = dst.iap_family = AF_INET6; 11008023SPhil.Kirk@Sun.COM src.iap_addr6 = &((ip6_t *)(ihd->ihd_mp->b_rptr))->ip6_src; 11018023SPhil.Kirk@Sun.COM dst.iap_addr6 = &((ip6_t *)(ihd->ihd_mp->b_rptr))->ip6_dst; 11028023SPhil.Kirk@Sun.COM } 11038023SPhil.Kirk@Sun.COM 11048023SPhil.Kirk@Sun.COM ipnet_walkers_inc(ips); 11058023SPhil.Kirk@Sun.COM 11068023SPhil.Kirk@Sun.COM list = &ips->ips_str_list; 11078023SPhil.Kirk@Sun.COM for (ipnet = list_head(list); ipnet != NULL; 11088023SPhil.Kirk@Sun.COM ipnet = list_next(list, ipnet)) { 11098023SPhil.Kirk@Sun.COM if (!(*ipnet->ipnet_acceptfn)(ipnet, ihd, &src, &dst)) 11108023SPhil.Kirk@Sun.COM continue; 11118023SPhil.Kirk@Sun.COM 11128023SPhil.Kirk@Sun.COM if (list_next(list, ipnet) == NULL) { 11138023SPhil.Kirk@Sun.COM netmp = ihd->ihd_mp; 11148023SPhil.Kirk@Sun.COM ihd->ihd_mp = NULL; 11158023SPhil.Kirk@Sun.COM } else { 11168023SPhil.Kirk@Sun.COM if ((netmp = dupmsg(ihd->ihd_mp)) == NULL && 11178023SPhil.Kirk@Sun.COM (netmp = copymsg(ihd->ihd_mp)) == NULL) { 11188023SPhil.Kirk@Sun.COM atomic_inc_64(&ips->ips_drops); 11198023SPhil.Kirk@Sun.COM continue; 11208023SPhil.Kirk@Sun.COM } 11218023SPhil.Kirk@Sun.COM } 11228023SPhil.Kirk@Sun.COM 11238023SPhil.Kirk@Sun.COM if (ipnet->ipnet_flags & IPNET_INFO) { 11248023SPhil.Kirk@Sun.COM if ((netmp = ipnet_addheader(ihd, netmp)) == NULL) { 11258023SPhil.Kirk@Sun.COM atomic_inc_64(&ips->ips_drops); 11268023SPhil.Kirk@Sun.COM continue; 11278023SPhil.Kirk@Sun.COM } 11288023SPhil.Kirk@Sun.COM } 11298023SPhil.Kirk@Sun.COM 11308023SPhil.Kirk@Sun.COM if (ipnet->ipnet_rq->q_first == NULL && 11318023SPhil.Kirk@Sun.COM canputnext(ipnet->ipnet_rq)) { 11328023SPhil.Kirk@Sun.COM putnext(ipnet->ipnet_rq, netmp); 11338023SPhil.Kirk@Sun.COM } else if (canput(ipnet->ipnet_rq)) { 11348023SPhil.Kirk@Sun.COM (void) putq(ipnet->ipnet_rq, netmp); 11358023SPhil.Kirk@Sun.COM } else { 11368023SPhil.Kirk@Sun.COM freemsg(netmp); 11378023SPhil.Kirk@Sun.COM atomic_inc_64(&ips->ips_drops); 11388023SPhil.Kirk@Sun.COM } 11398023SPhil.Kirk@Sun.COM } 11408023SPhil.Kirk@Sun.COM 11418023SPhil.Kirk@Sun.COM ipnet_walkers_dec(ips); 11428023SPhil.Kirk@Sun.COM 11438023SPhil.Kirk@Sun.COM freemsg(ihd->ihd_mp); 11448023SPhil.Kirk@Sun.COM freemsg(mp); 11458023SPhil.Kirk@Sun.COM } 11468023SPhil.Kirk@Sun.COM 11478023SPhil.Kirk@Sun.COM static void 11488023SPhil.Kirk@Sun.COM ipnet_input(mblk_t *mp) 11498023SPhil.Kirk@Sun.COM { 11508023SPhil.Kirk@Sun.COM ipobs_hook_data_t *ihd = (ipobs_hook_data_t *)mp->b_rptr; 11518023SPhil.Kirk@Sun.COM 11528023SPhil.Kirk@Sun.COM if (ddi_taskq_dispatch(ipnet_taskq, ipnet_dispatch, mp, DDI_NOSLEEP) != 11538023SPhil.Kirk@Sun.COM DDI_SUCCESS) { 11548023SPhil.Kirk@Sun.COM atomic_inc_64(&ihd->ihd_stack->netstack_ipnet->ips_drops); 11558023SPhil.Kirk@Sun.COM freemsg(ihd->ihd_mp); 11568023SPhil.Kirk@Sun.COM freemsg(mp); 11578023SPhil.Kirk@Sun.COM } 11588023SPhil.Kirk@Sun.COM } 11598023SPhil.Kirk@Sun.COM 11608023SPhil.Kirk@Sun.COM /* 11618023SPhil.Kirk@Sun.COM * Create a new ipnetif_t and new minor node for it. If creation is 11628023SPhil.Kirk@Sun.COM * successful the new ipnetif_t is inserted into an avl_tree 11638023SPhil.Kirk@Sun.COM * containing ipnetif's for this stack instance. 11648023SPhil.Kirk@Sun.COM */ 11658023SPhil.Kirk@Sun.COM static ipnetif_t * 11668023SPhil.Kirk@Sun.COM ipnet_create_if(const char *name, uint64_t index, ipnet_stack_t *ips) 11678023SPhil.Kirk@Sun.COM { 11688023SPhil.Kirk@Sun.COM ipnetif_t *ipnetif; 11698023SPhil.Kirk@Sun.COM avl_index_t where = 0; 11708023SPhil.Kirk@Sun.COM minor_t ifminor; 11718023SPhil.Kirk@Sun.COM 11728023SPhil.Kirk@Sun.COM /* 11738023SPhil.Kirk@Sun.COM * Because ipnet_create_if() can be called from a NIC event 11748023SPhil.Kirk@Sun.COM * callback, it should not block. 11758023SPhil.Kirk@Sun.COM */ 11768023SPhil.Kirk@Sun.COM ifminor = (minor_t)id_alloc_nosleep(ipnet_minor_space); 11778023SPhil.Kirk@Sun.COM if (ifminor == (minor_t)-1) 11788023SPhil.Kirk@Sun.COM return (NULL); 11798023SPhil.Kirk@Sun.COM if ((ipnetif = kmem_zalloc(sizeof (*ipnetif), KM_NOSLEEP)) == NULL) { 11808023SPhil.Kirk@Sun.COM id_free(ipnet_minor_space, ifminor); 11818023SPhil.Kirk@Sun.COM return (NULL); 11828023SPhil.Kirk@Sun.COM } 11838023SPhil.Kirk@Sun.COM 11848023SPhil.Kirk@Sun.COM (void) strlcpy(ipnetif->if_name, name, LIFNAMSIZ); 11858023SPhil.Kirk@Sun.COM ipnetif->if_index = index; 11868023SPhil.Kirk@Sun.COM 11878023SPhil.Kirk@Sun.COM mutex_init(&ipnetif->if_addr_lock, NULL, MUTEX_DEFAULT, 0); 11888023SPhil.Kirk@Sun.COM list_create(&ipnetif->if_ip4addr_list, sizeof (ipnetif_addr_t), 11898023SPhil.Kirk@Sun.COM offsetof(ipnetif_addr_t, ifa_link)); 11908023SPhil.Kirk@Sun.COM list_create(&ipnetif->if_ip6addr_list, sizeof (ipnetif_addr_t), 11918023SPhil.Kirk@Sun.COM offsetof(ipnetif_addr_t, ifa_link)); 11928023SPhil.Kirk@Sun.COM ipnetif->if_dev = makedevice(ipnet_major, ifminor); 11938023SPhil.Kirk@Sun.COM mutex_init(&ipnetif->if_reflock, NULL, MUTEX_DEFAULT, 0); 11948023SPhil.Kirk@Sun.COM ipnetif->if_refcnt = 1; 11958023SPhil.Kirk@Sun.COM 11968023SPhil.Kirk@Sun.COM mutex_enter(&ips->ips_avl_lock); 11978023SPhil.Kirk@Sun.COM VERIFY(avl_find(&ips->ips_avl_by_index, &index, &where) == NULL); 11988023SPhil.Kirk@Sun.COM avl_insert(&ips->ips_avl_by_index, ipnetif, where); 11998023SPhil.Kirk@Sun.COM VERIFY(avl_find(&ips->ips_avl_by_name, (void *)name, &where) == NULL); 12008023SPhil.Kirk@Sun.COM avl_insert(&ips->ips_avl_by_name, ipnetif, where); 12018023SPhil.Kirk@Sun.COM mutex_exit(&ips->ips_avl_lock); 12028023SPhil.Kirk@Sun.COM 12038023SPhil.Kirk@Sun.COM return (ipnetif); 12048023SPhil.Kirk@Sun.COM } 12058023SPhil.Kirk@Sun.COM 12068023SPhil.Kirk@Sun.COM static void 12078023SPhil.Kirk@Sun.COM ipnet_remove_if(ipnetif_t *ipnetif, ipnet_stack_t *ips) 12088023SPhil.Kirk@Sun.COM { 12098023SPhil.Kirk@Sun.COM ipnet_t *ipnet; 12108023SPhil.Kirk@Sun.COM 12118023SPhil.Kirk@Sun.COM ipnet_walkers_inc(ips); 12128023SPhil.Kirk@Sun.COM /* Send a SIGHUP to all open streams associated with this ipnetif. */ 12138023SPhil.Kirk@Sun.COM for (ipnet = list_head(&ips->ips_str_list); ipnet != NULL; 12148023SPhil.Kirk@Sun.COM ipnet = list_next(&ips->ips_str_list, ipnet)) { 12158023SPhil.Kirk@Sun.COM if (ipnet->ipnet_if == ipnetif) 12168023SPhil.Kirk@Sun.COM (void) putnextctl(ipnet->ipnet_rq, M_HANGUP); 12178023SPhil.Kirk@Sun.COM } 12188023SPhil.Kirk@Sun.COM ipnet_walkers_dec(ips); 12198023SPhil.Kirk@Sun.COM mutex_enter(&ips->ips_avl_lock); 12208023SPhil.Kirk@Sun.COM avl_remove(&ips->ips_avl_by_index, ipnetif); 12218023SPhil.Kirk@Sun.COM avl_remove(&ips->ips_avl_by_name, ipnetif); 12228023SPhil.Kirk@Sun.COM mutex_exit(&ips->ips_avl_lock); 12238023SPhil.Kirk@Sun.COM /* Release the reference we implicitly held in ipnet_create_if(). */ 12248023SPhil.Kirk@Sun.COM ipnetif_refrele(ipnetif); 12258023SPhil.Kirk@Sun.COM } 12268023SPhil.Kirk@Sun.COM 12278023SPhil.Kirk@Sun.COM static void 12288023SPhil.Kirk@Sun.COM ipnet_purge_addrlist(list_t *addrlist) 12298023SPhil.Kirk@Sun.COM { 12308023SPhil.Kirk@Sun.COM ipnetif_addr_t *ifa; 12318023SPhil.Kirk@Sun.COM 12328023SPhil.Kirk@Sun.COM while ((ifa = list_head(addrlist)) != NULL) { 12338023SPhil.Kirk@Sun.COM list_remove(addrlist, ifa); 12348023SPhil.Kirk@Sun.COM kmem_free(ifa, sizeof (*ifa)); 12358023SPhil.Kirk@Sun.COM } 12368023SPhil.Kirk@Sun.COM } 12378023SPhil.Kirk@Sun.COM 12388023SPhil.Kirk@Sun.COM static void 12398023SPhil.Kirk@Sun.COM ipnet_free_if(ipnetif_t *ipnetif) 12408023SPhil.Kirk@Sun.COM { 12418023SPhil.Kirk@Sun.COM ASSERT(ipnetif->if_refcnt == 0); 12428023SPhil.Kirk@Sun.COM 12438023SPhil.Kirk@Sun.COM /* Remove IPv4/v6 address lists from the ipnetif */ 12448023SPhil.Kirk@Sun.COM ipnet_purge_addrlist(&ipnetif->if_ip4addr_list); 12458023SPhil.Kirk@Sun.COM list_destroy(&ipnetif->if_ip4addr_list); 12468023SPhil.Kirk@Sun.COM ipnet_purge_addrlist(&ipnetif->if_ip6addr_list); 12478023SPhil.Kirk@Sun.COM list_destroy(&ipnetif->if_ip6addr_list); 12488023SPhil.Kirk@Sun.COM mutex_destroy(&ipnetif->if_addr_lock); 12498023SPhil.Kirk@Sun.COM mutex_destroy(&ipnetif->if_reflock); 12508023SPhil.Kirk@Sun.COM id_free(ipnet_minor_space, getminor(ipnetif->if_dev)); 12518023SPhil.Kirk@Sun.COM kmem_free(ipnetif, sizeof (*ipnetif)); 12528023SPhil.Kirk@Sun.COM } 12538023SPhil.Kirk@Sun.COM 12548023SPhil.Kirk@Sun.COM /* 12558023SPhil.Kirk@Sun.COM * Create an ipnetif_addr_t with the given logical interface id (lif) 12568023SPhil.Kirk@Sun.COM * and add it to the supplied ipnetif. The lif is the netinfo 12578023SPhil.Kirk@Sun.COM * representation of logical interface id, and we use this id to match 12588023SPhil.Kirk@Sun.COM * incoming netinfo events against our lists of addresses. 12598023SPhil.Kirk@Sun.COM */ 12608023SPhil.Kirk@Sun.COM static void 12618023SPhil.Kirk@Sun.COM ipnet_add_ifaddr(uint64_t lif, ipnetif_t *ipnetif, net_handle_t nd) 12628023SPhil.Kirk@Sun.COM { 12638023SPhil.Kirk@Sun.COM ipnetif_addr_t *ifaddr; 12648023SPhil.Kirk@Sun.COM zoneid_t zoneid; 12658023SPhil.Kirk@Sun.COM struct sockaddr_in bcast; 12668023SPhil.Kirk@Sun.COM struct sockaddr_storage addr; 12678023SPhil.Kirk@Sun.COM net_ifaddr_t type = NA_ADDRESS; 12688023SPhil.Kirk@Sun.COM uint64_t phyif = ipnetif->if_index; 12698023SPhil.Kirk@Sun.COM 12708023SPhil.Kirk@Sun.COM if (net_getlifaddr(nd, phyif, lif, 1, &type, &addr) != 0 || 12718023SPhil.Kirk@Sun.COM net_getlifzone(nd, phyif, lif, &zoneid) != 0) 12728023SPhil.Kirk@Sun.COM return; 12738023SPhil.Kirk@Sun.COM if ((ifaddr = kmem_alloc(sizeof (*ifaddr), KM_NOSLEEP)) == NULL) 12748023SPhil.Kirk@Sun.COM return; 12758023SPhil.Kirk@Sun.COM 12768023SPhil.Kirk@Sun.COM ifaddr->ifa_zone = zoneid; 12778023SPhil.Kirk@Sun.COM ifaddr->ifa_id = lif; 12788023SPhil.Kirk@Sun.COM 12798023SPhil.Kirk@Sun.COM switch (addr.ss_family) { 12808023SPhil.Kirk@Sun.COM case AF_INET: 12818023SPhil.Kirk@Sun.COM ifaddr->ifa_ip4addr = 12828023SPhil.Kirk@Sun.COM ((struct sockaddr_in *)&addr)->sin_addr.s_addr; 12838023SPhil.Kirk@Sun.COM /* 12848023SPhil.Kirk@Sun.COM * Try and get the broadcast address. Note that it's okay for 12858023SPhil.Kirk@Sun.COM * an interface to not have a broadcast address, so we don't 12868023SPhil.Kirk@Sun.COM * fail the entire operation if net_getlifaddr() fails here. 12878023SPhil.Kirk@Sun.COM */ 12888023SPhil.Kirk@Sun.COM type = NA_BROADCAST; 12898023SPhil.Kirk@Sun.COM if (net_getlifaddr(nd, phyif, lif, 1, &type, &bcast) == 0) 12908023SPhil.Kirk@Sun.COM ifaddr->ifa_brdaddr = bcast.sin_addr.s_addr; 12918023SPhil.Kirk@Sun.COM break; 12928023SPhil.Kirk@Sun.COM case AF_INET6: 12938023SPhil.Kirk@Sun.COM ifaddr->ifa_ip6addr = ((struct sockaddr_in6 *)&addr)->sin6_addr; 12948023SPhil.Kirk@Sun.COM break; 12958023SPhil.Kirk@Sun.COM } 12968023SPhil.Kirk@Sun.COM 12978023SPhil.Kirk@Sun.COM mutex_enter(&ipnetif->if_addr_lock); 12988023SPhil.Kirk@Sun.COM list_insert_tail(addr.ss_family == AF_INET ? 12998023SPhil.Kirk@Sun.COM &ipnetif->if_ip4addr_list : &ipnetif->if_ip6addr_list, ifaddr); 13008023SPhil.Kirk@Sun.COM mutex_exit(&ipnetif->if_addr_lock); 13018023SPhil.Kirk@Sun.COM } 13028023SPhil.Kirk@Sun.COM 13038023SPhil.Kirk@Sun.COM static void 13048023SPhil.Kirk@Sun.COM ipnet_delete_ifaddr(ipnetif_addr_t *ifaddr, ipnetif_t *ipnetif, boolean_t isv6) 13058023SPhil.Kirk@Sun.COM { 13068023SPhil.Kirk@Sun.COM mutex_enter(&ipnetif->if_addr_lock); 13078023SPhil.Kirk@Sun.COM list_remove(isv6 ? 13088023SPhil.Kirk@Sun.COM &ipnetif->if_ip6addr_list : &ipnetif->if_ip4addr_list, ifaddr); 13098023SPhil.Kirk@Sun.COM mutex_exit(&ipnetif->if_addr_lock); 13108023SPhil.Kirk@Sun.COM kmem_free(ifaddr, sizeof (*ifaddr)); 13118023SPhil.Kirk@Sun.COM } 13128023SPhil.Kirk@Sun.COM 13138023SPhil.Kirk@Sun.COM static void 13148023SPhil.Kirk@Sun.COM ipnet_plumb_ev(uint64_t ifindex, const char *ifname, ipnet_stack_t *ips, 13158023SPhil.Kirk@Sun.COM boolean_t isv6) 13168023SPhil.Kirk@Sun.COM { 13178023SPhil.Kirk@Sun.COM ipnetif_t *ipnetif; 13188023SPhil.Kirk@Sun.COM boolean_t refrele_needed = B_TRUE; 13198023SPhil.Kirk@Sun.COM 13208023SPhil.Kirk@Sun.COM if ((ipnetif = ipnet_if_getby_index(ifindex, ips)) == NULL) { 13218023SPhil.Kirk@Sun.COM ipnetif = ipnet_create_if(ifname, ifindex, ips); 13228023SPhil.Kirk@Sun.COM refrele_needed = B_FALSE; 13238023SPhil.Kirk@Sun.COM } 13248023SPhil.Kirk@Sun.COM if (ipnetif != NULL) { 13258023SPhil.Kirk@Sun.COM ipnetif->if_flags |= 13268023SPhil.Kirk@Sun.COM isv6 ? IPNETIF_IPV6PLUMBED : IPNETIF_IPV4PLUMBED; 13278023SPhil.Kirk@Sun.COM } 13288023SPhil.Kirk@Sun.COM 13298023SPhil.Kirk@Sun.COM if (ipnetif->if_multicnt != 0) { 13308023SPhil.Kirk@Sun.COM if (ip_join_allmulti(ifindex, isv6, 13318023SPhil.Kirk@Sun.COM ips->ips_netstack->netstack_ip) == 0) { 13328023SPhil.Kirk@Sun.COM ipnetif->if_flags |= 13338023SPhil.Kirk@Sun.COM isv6 ? IPNETIF_IPV6ALLMULTI : IPNETIF_IPV4ALLMULTI; 13348023SPhil.Kirk@Sun.COM } 13358023SPhil.Kirk@Sun.COM } 13368023SPhil.Kirk@Sun.COM 13378023SPhil.Kirk@Sun.COM if (refrele_needed) 13388023SPhil.Kirk@Sun.COM ipnetif_refrele(ipnetif); 13398023SPhil.Kirk@Sun.COM } 13408023SPhil.Kirk@Sun.COM 13418023SPhil.Kirk@Sun.COM static void 13428023SPhil.Kirk@Sun.COM ipnet_unplumb_ev(uint64_t ifindex, ipnet_stack_t *ips, boolean_t isv6) 13438023SPhil.Kirk@Sun.COM { 13448023SPhil.Kirk@Sun.COM ipnetif_t *ipnetif; 13458023SPhil.Kirk@Sun.COM 13468023SPhil.Kirk@Sun.COM if ((ipnetif = ipnet_if_getby_index(ifindex, ips)) == NULL) 13478023SPhil.Kirk@Sun.COM return; 13488023SPhil.Kirk@Sun.COM 13498023SPhil.Kirk@Sun.COM mutex_enter(&ipnetif->if_addr_lock); 13508023SPhil.Kirk@Sun.COM ipnet_purge_addrlist(isv6 ? 13518023SPhil.Kirk@Sun.COM &ipnetif->if_ip6addr_list : &ipnetif->if_ip4addr_list); 13528023SPhil.Kirk@Sun.COM mutex_exit(&ipnetif->if_addr_lock); 13538023SPhil.Kirk@Sun.COM 13548023SPhil.Kirk@Sun.COM /* 13558023SPhil.Kirk@Sun.COM * Note that we have one ipnetif for both IPv4 and IPv6, but we receive 13568023SPhil.Kirk@Sun.COM * separate NE_UNPLUMB events for IPv4 and IPv6. We remove the ipnetif 13578023SPhil.Kirk@Sun.COM * if both IPv4 and IPv6 interfaces have been unplumbed. 13588023SPhil.Kirk@Sun.COM */ 13598023SPhil.Kirk@Sun.COM ipnetif->if_flags &= isv6 ? ~IPNETIF_IPV6PLUMBED : ~IPNETIF_IPV4PLUMBED; 13608023SPhil.Kirk@Sun.COM if (!(ipnetif->if_flags & (IPNETIF_IPV4PLUMBED | IPNETIF_IPV6PLUMBED))) 13618023SPhil.Kirk@Sun.COM ipnet_remove_if(ipnetif, ips); 13628023SPhil.Kirk@Sun.COM ipnetif_refrele(ipnetif); 13638023SPhil.Kirk@Sun.COM } 13648023SPhil.Kirk@Sun.COM 13658023SPhil.Kirk@Sun.COM static void 13668023SPhil.Kirk@Sun.COM ipnet_lifup_ev(uint64_t ifindex, uint64_t lifindex, net_handle_t nd, 13678023SPhil.Kirk@Sun.COM ipnet_stack_t *ips, boolean_t isv6) 13688023SPhil.Kirk@Sun.COM { 13698023SPhil.Kirk@Sun.COM ipnetif_t *ipnetif; 13708023SPhil.Kirk@Sun.COM ipnetif_addr_t *ifaddr; 13718023SPhil.Kirk@Sun.COM 13728023SPhil.Kirk@Sun.COM if ((ipnetif = ipnet_if_getby_index(ifindex, ips)) == NULL) 13738023SPhil.Kirk@Sun.COM return; 13748023SPhil.Kirk@Sun.COM if ((ifaddr = ipnet_match_lif(ipnetif, lifindex, isv6)) != NULL) { 13758023SPhil.Kirk@Sun.COM /* 13768023SPhil.Kirk@Sun.COM * We must have missed a NE_LIF_DOWN event. Delete this 13778023SPhil.Kirk@Sun.COM * ifaddr and re-create it. 13788023SPhil.Kirk@Sun.COM */ 13798023SPhil.Kirk@Sun.COM ipnet_delete_ifaddr(ifaddr, ipnetif, isv6); 13808023SPhil.Kirk@Sun.COM } 13818023SPhil.Kirk@Sun.COM 13828023SPhil.Kirk@Sun.COM ipnet_add_ifaddr(lifindex, ipnetif, nd); 13838023SPhil.Kirk@Sun.COM ipnetif_refrele(ipnetif); 13848023SPhil.Kirk@Sun.COM } 13858023SPhil.Kirk@Sun.COM 13868023SPhil.Kirk@Sun.COM static void 13878023SPhil.Kirk@Sun.COM ipnet_lifdown_ev(uint64_t ifindex, uint64_t lifindex, ipnet_stack_t *ips, 13888023SPhil.Kirk@Sun.COM boolean_t isv6) 13898023SPhil.Kirk@Sun.COM { 13908023SPhil.Kirk@Sun.COM ipnetif_t *ipnetif; 13918023SPhil.Kirk@Sun.COM ipnetif_addr_t *ifaddr; 13928023SPhil.Kirk@Sun.COM 13938023SPhil.Kirk@Sun.COM if ((ipnetif = ipnet_if_getby_index(ifindex, ips)) == NULL) 13948023SPhil.Kirk@Sun.COM return; 13958023SPhil.Kirk@Sun.COM if ((ifaddr = ipnet_match_lif(ipnetif, lifindex, isv6)) != NULL) 13968023SPhil.Kirk@Sun.COM ipnet_delete_ifaddr(ifaddr, ipnetif, isv6); 13978023SPhil.Kirk@Sun.COM ipnetif_refrele(ipnetif); 13988023SPhil.Kirk@Sun.COM /* 13998023SPhil.Kirk@Sun.COM * Make sure that open streams on this ipnetif are still allowed to 14008023SPhil.Kirk@Sun.COM * have it open. 14018023SPhil.Kirk@Sun.COM */ 14028023SPhil.Kirk@Sun.COM ipnet_if_zonecheck(ipnetif, ips); 14038023SPhil.Kirk@Sun.COM } 14048023SPhil.Kirk@Sun.COM 14058023SPhil.Kirk@Sun.COM /* 14068023SPhil.Kirk@Sun.COM * This callback from the NIC event framework dispatches a taskq as the event 14078023SPhil.Kirk@Sun.COM * handlers may block. 14088023SPhil.Kirk@Sun.COM */ 14098023SPhil.Kirk@Sun.COM /* ARGSUSED */ 14108023SPhil.Kirk@Sun.COM static int 14118023SPhil.Kirk@Sun.COM ipnet_nicevent_cb(hook_event_token_t token, hook_data_t info, void *arg) 14128023SPhil.Kirk@Sun.COM { 14138023SPhil.Kirk@Sun.COM ipnet_stack_t *ips = arg; 14148023SPhil.Kirk@Sun.COM hook_nic_event_t *hn = (hook_nic_event_t *)info; 14158023SPhil.Kirk@Sun.COM ipnet_nicevent_t *ipne; 14168023SPhil.Kirk@Sun.COM 14178023SPhil.Kirk@Sun.COM if ((ipne = kmem_alloc(sizeof (ipnet_nicevent_t), KM_NOSLEEP)) == NULL) 14188023SPhil.Kirk@Sun.COM return (0); 14198023SPhil.Kirk@Sun.COM ipne->ipne_event = hn->hne_event; 14208023SPhil.Kirk@Sun.COM ipne->ipne_protocol = hn->hne_protocol; 14218023SPhil.Kirk@Sun.COM ipne->ipne_stackid = ips->ips_netstack->netstack_stackid; 14228023SPhil.Kirk@Sun.COM ipne->ipne_ifindex = hn->hne_nic; 14238023SPhil.Kirk@Sun.COM ipne->ipne_lifindex = hn->hne_lif; 14248023SPhil.Kirk@Sun.COM if (hn->hne_datalen != 0) { 14258023SPhil.Kirk@Sun.COM (void) strlcpy(ipne->ipne_ifname, hn->hne_data, 14268023SPhil.Kirk@Sun.COM sizeof (ipne->ipne_ifname)); 14278023SPhil.Kirk@Sun.COM } 14288023SPhil.Kirk@Sun.COM (void) ddi_taskq_dispatch(ipnet_nicevent_taskq, ipnet_nicevent_task, 14298023SPhil.Kirk@Sun.COM ipne, DDI_NOSLEEP); 14308023SPhil.Kirk@Sun.COM return (0); 14318023SPhil.Kirk@Sun.COM } 14328023SPhil.Kirk@Sun.COM 14338023SPhil.Kirk@Sun.COM static void 14348023SPhil.Kirk@Sun.COM ipnet_nicevent_task(void *arg) 14358023SPhil.Kirk@Sun.COM { 14368023SPhil.Kirk@Sun.COM ipnet_nicevent_t *ipne = arg; 14378023SPhil.Kirk@Sun.COM netstack_t *ns; 14388023SPhil.Kirk@Sun.COM ipnet_stack_t *ips; 14398023SPhil.Kirk@Sun.COM boolean_t isv6; 14408023SPhil.Kirk@Sun.COM 14418023SPhil.Kirk@Sun.COM if ((ns = netstack_find_by_stackid(ipne->ipne_stackid)) == NULL) 14428023SPhil.Kirk@Sun.COM goto done; 14438023SPhil.Kirk@Sun.COM ips = ns->netstack_ipnet; 14448023SPhil.Kirk@Sun.COM isv6 = (ipne->ipne_protocol == ips->ips_ndv6); 14458023SPhil.Kirk@Sun.COM 14468023SPhil.Kirk@Sun.COM mutex_enter(&ips->ips_event_lock); 14478023SPhil.Kirk@Sun.COM switch (ipne->ipne_event) { 14488023SPhil.Kirk@Sun.COM case NE_PLUMB: 14498023SPhil.Kirk@Sun.COM ipnet_plumb_ev(ipne->ipne_ifindex, ipne->ipne_ifname, ips, 14508023SPhil.Kirk@Sun.COM isv6); 14518023SPhil.Kirk@Sun.COM break; 14528023SPhil.Kirk@Sun.COM case NE_UNPLUMB: 14538023SPhil.Kirk@Sun.COM ipnet_unplumb_ev(ipne->ipne_ifindex, ips, isv6); 14548023SPhil.Kirk@Sun.COM break; 14558023SPhil.Kirk@Sun.COM case NE_LIF_UP: 14568023SPhil.Kirk@Sun.COM ipnet_lifup_ev(ipne->ipne_ifindex, ipne->ipne_lifindex, 14578023SPhil.Kirk@Sun.COM ipne->ipne_protocol, ips, isv6); 14588023SPhil.Kirk@Sun.COM break; 14598023SPhil.Kirk@Sun.COM case NE_LIF_DOWN: 14608023SPhil.Kirk@Sun.COM ipnet_lifdown_ev(ipne->ipne_ifindex, ipne->ipne_lifindex, ips, 14618023SPhil.Kirk@Sun.COM isv6); 14628023SPhil.Kirk@Sun.COM break; 14638023SPhil.Kirk@Sun.COM default: 14648023SPhil.Kirk@Sun.COM break; 14658023SPhil.Kirk@Sun.COM } 14668023SPhil.Kirk@Sun.COM mutex_exit(&ips->ips_event_lock); 14678023SPhil.Kirk@Sun.COM done: 14688023SPhil.Kirk@Sun.COM if (ns != NULL) 14698023SPhil.Kirk@Sun.COM netstack_rele(ns); 14708023SPhil.Kirk@Sun.COM kmem_free(ipne, sizeof (ipnet_nicevent_t)); 14718023SPhil.Kirk@Sun.COM } 14728023SPhil.Kirk@Sun.COM 14738023SPhil.Kirk@Sun.COM dev_t 14748023SPhil.Kirk@Sun.COM ipnet_if_getdev(char *name, zoneid_t zoneid) 14758023SPhil.Kirk@Sun.COM { 14768023SPhil.Kirk@Sun.COM netstack_t *ns; 14778023SPhil.Kirk@Sun.COM ipnet_stack_t *ips; 14788023SPhil.Kirk@Sun.COM ipnetif_t *ipnetif; 14798023SPhil.Kirk@Sun.COM dev_t dev = (dev_t)-1; 14808023SPhil.Kirk@Sun.COM 14818023SPhil.Kirk@Sun.COM if (is_system_labeled() && zoneid != GLOBAL_ZONEID) 14828023SPhil.Kirk@Sun.COM return (dev); 14838023SPhil.Kirk@Sun.COM if ((ns = netstack_find_by_zoneid(zoneid)) == NULL) 14848023SPhil.Kirk@Sun.COM return (dev); 14858023SPhil.Kirk@Sun.COM 14868023SPhil.Kirk@Sun.COM ips = ns->netstack_ipnet; 14878023SPhil.Kirk@Sun.COM mutex_enter(&ips->ips_avl_lock); 14888023SPhil.Kirk@Sun.COM if ((ipnetif = avl_find(&ips->ips_avl_by_name, name, NULL)) != NULL) { 14898023SPhil.Kirk@Sun.COM if (ipnet_if_in_zone(ipnetif, zoneid, ips)) 14908023SPhil.Kirk@Sun.COM dev = ipnetif->if_dev; 14918023SPhil.Kirk@Sun.COM } 14928023SPhil.Kirk@Sun.COM mutex_exit(&ips->ips_avl_lock); 14938023SPhil.Kirk@Sun.COM netstack_rele(ns); 14948023SPhil.Kirk@Sun.COM 14958023SPhil.Kirk@Sun.COM return (dev); 14968023SPhil.Kirk@Sun.COM } 14978023SPhil.Kirk@Sun.COM 14988023SPhil.Kirk@Sun.COM static ipnetif_t * 14998023SPhil.Kirk@Sun.COM ipnet_if_getby_index(uint64_t id, ipnet_stack_t *ips) 15008023SPhil.Kirk@Sun.COM { 15018023SPhil.Kirk@Sun.COM ipnetif_t *ipnetif; 15028023SPhil.Kirk@Sun.COM 15038023SPhil.Kirk@Sun.COM mutex_enter(&ips->ips_avl_lock); 15048023SPhil.Kirk@Sun.COM if ((ipnetif = avl_find(&ips->ips_avl_by_index, &id, NULL)) != NULL) 15058023SPhil.Kirk@Sun.COM ipnetif_refhold(ipnetif); 15068023SPhil.Kirk@Sun.COM mutex_exit(&ips->ips_avl_lock); 15078023SPhil.Kirk@Sun.COM return (ipnetif); 15088023SPhil.Kirk@Sun.COM } 15098023SPhil.Kirk@Sun.COM 15108023SPhil.Kirk@Sun.COM static ipnetif_t * 15118023SPhil.Kirk@Sun.COM ipnet_if_getby_dev(dev_t dev, ipnet_stack_t *ips) 15128023SPhil.Kirk@Sun.COM { 15138023SPhil.Kirk@Sun.COM ipnetif_t *ipnetif; 15148023SPhil.Kirk@Sun.COM avl_tree_t *tree; 15158023SPhil.Kirk@Sun.COM 15168023SPhil.Kirk@Sun.COM mutex_enter(&ips->ips_avl_lock); 15178023SPhil.Kirk@Sun.COM tree = &ips->ips_avl_by_index; 15188023SPhil.Kirk@Sun.COM for (ipnetif = avl_first(tree); ipnetif != NULL; 15198023SPhil.Kirk@Sun.COM ipnetif = avl_walk(tree, ipnetif, AVL_AFTER)) { 15208023SPhil.Kirk@Sun.COM if (ipnetif->if_dev == dev) { 15218023SPhil.Kirk@Sun.COM ipnetif_refhold(ipnetif); 15228023SPhil.Kirk@Sun.COM break; 15238023SPhil.Kirk@Sun.COM } 15248023SPhil.Kirk@Sun.COM } 15258023SPhil.Kirk@Sun.COM mutex_exit(&ips->ips_avl_lock); 15268023SPhil.Kirk@Sun.COM return (ipnetif); 15278023SPhil.Kirk@Sun.COM } 15288023SPhil.Kirk@Sun.COM 15298023SPhil.Kirk@Sun.COM static ipnetif_addr_t * 15308023SPhil.Kirk@Sun.COM ipnet_match_lif(ipnetif_t *ipnetif, lif_if_t lid, boolean_t isv6) 15318023SPhil.Kirk@Sun.COM { 15328023SPhil.Kirk@Sun.COM ipnetif_addr_t *ifaddr; 15338023SPhil.Kirk@Sun.COM list_t *list; 15348023SPhil.Kirk@Sun.COM 15358023SPhil.Kirk@Sun.COM mutex_enter(&ipnetif->if_addr_lock); 15368023SPhil.Kirk@Sun.COM list = isv6 ? &ipnetif->if_ip6addr_list : &ipnetif->if_ip4addr_list; 15378023SPhil.Kirk@Sun.COM for (ifaddr = list_head(list); ifaddr != NULL; 15388023SPhil.Kirk@Sun.COM ifaddr = list_next(list, ifaddr)) { 15398023SPhil.Kirk@Sun.COM if (lid == ifaddr->ifa_id) 15408023SPhil.Kirk@Sun.COM break; 15418023SPhil.Kirk@Sun.COM } 15428023SPhil.Kirk@Sun.COM mutex_exit(&ipnetif->if_addr_lock); 15438023SPhil.Kirk@Sun.COM return (ifaddr); 15448023SPhil.Kirk@Sun.COM } 15458023SPhil.Kirk@Sun.COM 15468023SPhil.Kirk@Sun.COM /* ARGSUSED */ 15478023SPhil.Kirk@Sun.COM static void * 15488023SPhil.Kirk@Sun.COM ipnet_stack_init(netstackid_t stackid, netstack_t *ns) 15498023SPhil.Kirk@Sun.COM { 15508023SPhil.Kirk@Sun.COM ipnet_stack_t *ips; 15518023SPhil.Kirk@Sun.COM 15528023SPhil.Kirk@Sun.COM ips = kmem_zalloc(sizeof (*ips), KM_SLEEP); 15538023SPhil.Kirk@Sun.COM ips->ips_netstack = ns; 15548023SPhil.Kirk@Sun.COM mutex_init(&ips->ips_avl_lock, NULL, MUTEX_DEFAULT, 0); 15558023SPhil.Kirk@Sun.COM avl_create(&ips->ips_avl_by_index, ipnet_if_compare_index, 15568023SPhil.Kirk@Sun.COM sizeof (ipnetif_t), offsetof(ipnetif_t, if_avl_by_index)); 15578023SPhil.Kirk@Sun.COM avl_create(&ips->ips_avl_by_name, ipnet_if_compare_name, 15588023SPhil.Kirk@Sun.COM sizeof (ipnetif_t), offsetof(ipnetif_t, if_avl_by_name)); 15598023SPhil.Kirk@Sun.COM mutex_init(&ips->ips_walkers_lock, NULL, MUTEX_DEFAULT, NULL); 15608023SPhil.Kirk@Sun.COM cv_init(&ips->ips_walkers_cv, NULL, CV_DRIVER, NULL); 15618023SPhil.Kirk@Sun.COM list_create(&ips->ips_str_list, sizeof (ipnet_t), 15628023SPhil.Kirk@Sun.COM offsetof(ipnet_t, ipnet_next)); 15638023SPhil.Kirk@Sun.COM ipnet_register_netihook(ips); 15648023SPhil.Kirk@Sun.COM return (ips); 15658023SPhil.Kirk@Sun.COM } 15668023SPhil.Kirk@Sun.COM 15678023SPhil.Kirk@Sun.COM /* ARGSUSED */ 15688023SPhil.Kirk@Sun.COM static void 15698023SPhil.Kirk@Sun.COM ipnet_stack_fini(netstackid_t stackid, void *arg) 15708023SPhil.Kirk@Sun.COM { 15718023SPhil.Kirk@Sun.COM ipnet_stack_t *ips = arg; 15728023SPhil.Kirk@Sun.COM ipnetif_t *ipnetif, *nipnetif; 15738023SPhil.Kirk@Sun.COM 15748023SPhil.Kirk@Sun.COM if (ips->ips_ndv4 != NULL) { 15758023SPhil.Kirk@Sun.COM VERIFY(net_hook_unregister(ips->ips_ndv4, NH_NIC_EVENTS, 15768023SPhil.Kirk@Sun.COM ips->ips_nicevents) == 0); 15778023SPhil.Kirk@Sun.COM VERIFY(net_protocol_release(ips->ips_ndv4) == 0); 15788023SPhil.Kirk@Sun.COM } 15798023SPhil.Kirk@Sun.COM if (ips->ips_ndv6 != NULL) { 15808023SPhil.Kirk@Sun.COM VERIFY(net_hook_unregister(ips->ips_ndv6, NH_NIC_EVENTS, 15818023SPhil.Kirk@Sun.COM ips->ips_nicevents) == 0); 15828023SPhil.Kirk@Sun.COM VERIFY(net_protocol_release(ips->ips_ndv6) == 0); 15838023SPhil.Kirk@Sun.COM } 15848023SPhil.Kirk@Sun.COM hook_free(ips->ips_nicevents); 15858023SPhil.Kirk@Sun.COM 15868023SPhil.Kirk@Sun.COM for (ipnetif = avl_first(&ips->ips_avl_by_index); ipnetif != NULL; 15878023SPhil.Kirk@Sun.COM ipnetif = nipnetif) { 15888023SPhil.Kirk@Sun.COM nipnetif = AVL_NEXT(&ips->ips_avl_by_index, ipnetif); 15898023SPhil.Kirk@Sun.COM ipnet_remove_if(ipnetif, ips); 15908023SPhil.Kirk@Sun.COM } 15918023SPhil.Kirk@Sun.COM avl_destroy(&ips->ips_avl_by_index); 15928023SPhil.Kirk@Sun.COM avl_destroy(&ips->ips_avl_by_name); 15938023SPhil.Kirk@Sun.COM mutex_destroy(&ips->ips_avl_lock); 15948023SPhil.Kirk@Sun.COM mutex_destroy(&ips->ips_walkers_lock); 15958023SPhil.Kirk@Sun.COM cv_destroy(&ips->ips_walkers_cv); 15968023SPhil.Kirk@Sun.COM list_destroy(&ips->ips_str_list); 15978023SPhil.Kirk@Sun.COM kmem_free(ips, sizeof (*ips)); 15988023SPhil.Kirk@Sun.COM } 15998023SPhil.Kirk@Sun.COM 16008023SPhil.Kirk@Sun.COM /* Do any of the addresses in addrlist belong the supplied zoneid? */ 16018023SPhil.Kirk@Sun.COM static boolean_t 16028023SPhil.Kirk@Sun.COM ipnet_addrs_in_zone(list_t *addrlist, zoneid_t zoneid) 16038023SPhil.Kirk@Sun.COM { 16048023SPhil.Kirk@Sun.COM ipnetif_addr_t *ifa; 16058023SPhil.Kirk@Sun.COM 16068023SPhil.Kirk@Sun.COM for (ifa = list_head(addrlist); ifa != NULL; 16078023SPhil.Kirk@Sun.COM ifa = list_next(addrlist, ifa)) { 16088023SPhil.Kirk@Sun.COM if (ifa->ifa_zone == zoneid) 16098023SPhil.Kirk@Sun.COM return (B_TRUE); 16108023SPhil.Kirk@Sun.COM } 16118023SPhil.Kirk@Sun.COM return (B_FALSE); 16128023SPhil.Kirk@Sun.COM } 16138023SPhil.Kirk@Sun.COM 16148023SPhil.Kirk@Sun.COM /* Should the supplied ipnetif be visible from the supplied zoneid? */ 16158023SPhil.Kirk@Sun.COM static boolean_t 16168023SPhil.Kirk@Sun.COM ipnet_if_in_zone(ipnetif_t *ipnetif, zoneid_t zoneid, ipnet_stack_t *ips) 16178023SPhil.Kirk@Sun.COM { 16188023SPhil.Kirk@Sun.COM int ret; 16198023SPhil.Kirk@Sun.COM 16208023SPhil.Kirk@Sun.COM /* 16218023SPhil.Kirk@Sun.COM * The global zone has visibility into all interfaces in the global 16228023SPhil.Kirk@Sun.COM * stack, and exclusive stack zones have visibility into all 16238023SPhil.Kirk@Sun.COM * interfaces in their stack. 16248023SPhil.Kirk@Sun.COM */ 16258023SPhil.Kirk@Sun.COM if (zoneid == GLOBAL_ZONEID || 16268023SPhil.Kirk@Sun.COM ips->ips_netstack->netstack_stackid != GLOBAL_NETSTACKID) 16278023SPhil.Kirk@Sun.COM return (B_TRUE); 16288023SPhil.Kirk@Sun.COM 16298023SPhil.Kirk@Sun.COM /* 16308023SPhil.Kirk@Sun.COM * Shared-stack zones only have visibility for interfaces that have 16318023SPhil.Kirk@Sun.COM * addresses in their zone. 16328023SPhil.Kirk@Sun.COM */ 16338023SPhil.Kirk@Sun.COM mutex_enter(&ipnetif->if_addr_lock); 16348023SPhil.Kirk@Sun.COM ret = ipnet_addrs_in_zone(&ipnetif->if_ip4addr_list, zoneid) || 16358023SPhil.Kirk@Sun.COM ipnet_addrs_in_zone(&ipnetif->if_ip6addr_list, zoneid); 16368023SPhil.Kirk@Sun.COM mutex_exit(&ipnetif->if_addr_lock); 16378023SPhil.Kirk@Sun.COM return (ret); 16388023SPhil.Kirk@Sun.COM } 16398023SPhil.Kirk@Sun.COM 16408023SPhil.Kirk@Sun.COM /* 16418023SPhil.Kirk@Sun.COM * Verify that any ipnet_t that has a reference to the supplied ipnetif should 16428023SPhil.Kirk@Sun.COM * still be allowed to have it open. A given ipnet_t may no longer be allowed 16438023SPhil.Kirk@Sun.COM * to have an ipnetif open if there are no longer any addresses that belong to 16448023SPhil.Kirk@Sun.COM * the ipnetif in the ipnet_t's non-global shared-stack zoneid. If that's the 16458023SPhil.Kirk@Sun.COM * case, send the ipnet_t an M_HANGUP. 16468023SPhil.Kirk@Sun.COM */ 16478023SPhil.Kirk@Sun.COM static void 16488023SPhil.Kirk@Sun.COM ipnet_if_zonecheck(ipnetif_t *ipnetif, ipnet_stack_t *ips) 16498023SPhil.Kirk@Sun.COM { 16508023SPhil.Kirk@Sun.COM list_t *strlist = &ips->ips_str_list; 16518023SPhil.Kirk@Sun.COM ipnet_t *ipnet; 16528023SPhil.Kirk@Sun.COM 16538023SPhil.Kirk@Sun.COM ipnet_walkers_inc(ips); 16548023SPhil.Kirk@Sun.COM for (ipnet = list_head(strlist); ipnet != NULL; 16558023SPhil.Kirk@Sun.COM ipnet = list_next(strlist, ipnet)) { 16568023SPhil.Kirk@Sun.COM if (ipnet->ipnet_if != ipnetif) 16578023SPhil.Kirk@Sun.COM continue; 16588023SPhil.Kirk@Sun.COM if (!ipnet_if_in_zone(ipnetif, ipnet->ipnet_zoneid, ips)) 16598023SPhil.Kirk@Sun.COM (void) putnextctl(ipnet->ipnet_rq, M_HANGUP); 16608023SPhil.Kirk@Sun.COM } 16618023SPhil.Kirk@Sun.COM ipnet_walkers_dec(ips); 16628023SPhil.Kirk@Sun.COM } 16638023SPhil.Kirk@Sun.COM 16648023SPhil.Kirk@Sun.COM void 16658023SPhil.Kirk@Sun.COM ipnet_walk_if(ipnet_walkfunc_t *cb, void *arg, zoneid_t zoneid) 16668023SPhil.Kirk@Sun.COM { 16678023SPhil.Kirk@Sun.COM ipnetif_t *ipnetif; 16688023SPhil.Kirk@Sun.COM list_t cbdata; 16698023SPhil.Kirk@Sun.COM ipnetif_cbdata_t *cbnode; 16708023SPhil.Kirk@Sun.COM netstack_t *ns; 16718023SPhil.Kirk@Sun.COM ipnet_stack_t *ips; 16728023SPhil.Kirk@Sun.COM 16738023SPhil.Kirk@Sun.COM /* 16748023SPhil.Kirk@Sun.COM * On labeled systems, non-global zones shouldn't see anything 16758023SPhil.Kirk@Sun.COM * in /dev/ipnet. 16768023SPhil.Kirk@Sun.COM */ 16778023SPhil.Kirk@Sun.COM if (is_system_labeled() && zoneid != GLOBAL_ZONEID) 16788023SPhil.Kirk@Sun.COM return; 16798023SPhil.Kirk@Sun.COM 16808023SPhil.Kirk@Sun.COM if ((ns = netstack_find_by_zoneid(zoneid)) == NULL) 16818023SPhil.Kirk@Sun.COM return; 16828023SPhil.Kirk@Sun.COM 16838023SPhil.Kirk@Sun.COM ips = ns->netstack_ipnet; 16848023SPhil.Kirk@Sun.COM list_create(&cbdata, sizeof (ipnetif_cbdata_t), 16858023SPhil.Kirk@Sun.COM offsetof(ipnetif_cbdata_t, ic_next)); 16868023SPhil.Kirk@Sun.COM 16878023SPhil.Kirk@Sun.COM mutex_enter(&ips->ips_avl_lock); 16888023SPhil.Kirk@Sun.COM for (ipnetif = avl_first(&ips->ips_avl_by_index); ipnetif != NULL; 16898023SPhil.Kirk@Sun.COM ipnetif = avl_walk(&ips->ips_avl_by_index, ipnetif, AVL_AFTER)) { 16908023SPhil.Kirk@Sun.COM if (!ipnet_if_in_zone(ipnetif, zoneid, ips)) 16918023SPhil.Kirk@Sun.COM continue; 16928023SPhil.Kirk@Sun.COM cbnode = kmem_zalloc(sizeof (ipnetif_cbdata_t), KM_SLEEP); 16938023SPhil.Kirk@Sun.COM (void) strlcpy(cbnode->ic_ifname, ipnetif->if_name, LIFNAMSIZ); 16948023SPhil.Kirk@Sun.COM cbnode->ic_dev = ipnetif->if_dev; 16958023SPhil.Kirk@Sun.COM list_insert_head(&cbdata, cbnode); 16968023SPhil.Kirk@Sun.COM } 16978023SPhil.Kirk@Sun.COM mutex_exit(&ips->ips_avl_lock); 16988023SPhil.Kirk@Sun.COM 16998023SPhil.Kirk@Sun.COM while ((cbnode = list_head(&cbdata)) != NULL) { 17008023SPhil.Kirk@Sun.COM cb(cbnode->ic_ifname, arg, cbnode->ic_dev); 17018023SPhil.Kirk@Sun.COM list_remove(&cbdata, cbnode); 17028023SPhil.Kirk@Sun.COM kmem_free(cbnode, sizeof (ipnetif_cbdata_t)); 17038023SPhil.Kirk@Sun.COM } 17048023SPhil.Kirk@Sun.COM list_destroy(&cbdata); 17058023SPhil.Kirk@Sun.COM netstack_rele(ns); 17068023SPhil.Kirk@Sun.COM } 17078023SPhil.Kirk@Sun.COM 17088023SPhil.Kirk@Sun.COM static int 17098023SPhil.Kirk@Sun.COM ipnet_if_compare_index(const void *index_ptr, const void *ipnetifp) 17108023SPhil.Kirk@Sun.COM { 17118023SPhil.Kirk@Sun.COM int64_t index1 = *((int64_t *)index_ptr); 17128023SPhil.Kirk@Sun.COM int64_t index2 = (int64_t)((ipnetif_t *)ipnetifp)->if_index; 17138023SPhil.Kirk@Sun.COM 17148023SPhil.Kirk@Sun.COM return (SIGNOF(index2 - index1)); 17158023SPhil.Kirk@Sun.COM } 17168023SPhil.Kirk@Sun.COM 17178023SPhil.Kirk@Sun.COM static int 17188023SPhil.Kirk@Sun.COM ipnet_if_compare_name(const void *name_ptr, const void *ipnetifp) 17198023SPhil.Kirk@Sun.COM { 17208023SPhil.Kirk@Sun.COM int res; 17218023SPhil.Kirk@Sun.COM 17228023SPhil.Kirk@Sun.COM res = strcmp(((ipnetif_t *)ipnetifp)->if_name, name_ptr); 17238023SPhil.Kirk@Sun.COM return (SIGNOF(res)); 17248023SPhil.Kirk@Sun.COM } 17258023SPhil.Kirk@Sun.COM 17268023SPhil.Kirk@Sun.COM static void 17278023SPhil.Kirk@Sun.COM ipnetif_refhold(ipnetif_t *ipnetif) 17288023SPhil.Kirk@Sun.COM { 17298023SPhil.Kirk@Sun.COM mutex_enter(&ipnetif->if_reflock); 17308023SPhil.Kirk@Sun.COM ipnetif->if_refcnt++; 17318023SPhil.Kirk@Sun.COM mutex_exit(&ipnetif->if_reflock); 17328023SPhil.Kirk@Sun.COM } 17338023SPhil.Kirk@Sun.COM 17348023SPhil.Kirk@Sun.COM static void 17358023SPhil.Kirk@Sun.COM ipnetif_refrele(ipnetif_t *ipnetif) 17368023SPhil.Kirk@Sun.COM { 17378023SPhil.Kirk@Sun.COM mutex_enter(&ipnetif->if_reflock); 17388023SPhil.Kirk@Sun.COM ASSERT(ipnetif->if_refcnt != 0); 17398023SPhil.Kirk@Sun.COM if (--ipnetif->if_refcnt == 0) 17408023SPhil.Kirk@Sun.COM ipnet_free_if(ipnetif); 17418023SPhil.Kirk@Sun.COM else 17428023SPhil.Kirk@Sun.COM mutex_exit(&ipnetif->if_reflock); 17438023SPhil.Kirk@Sun.COM } 17448023SPhil.Kirk@Sun.COM 17458023SPhil.Kirk@Sun.COM static void 17468023SPhil.Kirk@Sun.COM ipnet_walkers_inc(ipnet_stack_t *ips) 17478023SPhil.Kirk@Sun.COM { 17488023SPhil.Kirk@Sun.COM mutex_enter(&ips->ips_walkers_lock); 17498023SPhil.Kirk@Sun.COM ips->ips_walkers_cnt++; 17508023SPhil.Kirk@Sun.COM mutex_exit(&ips->ips_walkers_lock); 17518023SPhil.Kirk@Sun.COM } 17528023SPhil.Kirk@Sun.COM 17538023SPhil.Kirk@Sun.COM static void 17548023SPhil.Kirk@Sun.COM ipnet_walkers_dec(ipnet_stack_t *ips) 17558023SPhil.Kirk@Sun.COM { 17568023SPhil.Kirk@Sun.COM mutex_enter(&ips->ips_walkers_lock); 17578023SPhil.Kirk@Sun.COM ASSERT(ips->ips_walkers_cnt != 0); 17588023SPhil.Kirk@Sun.COM if (--ips->ips_walkers_cnt == 0) 17598023SPhil.Kirk@Sun.COM cv_broadcast(&ips->ips_walkers_cv); 17608023SPhil.Kirk@Sun.COM mutex_exit(&ips->ips_walkers_lock); 17618023SPhil.Kirk@Sun.COM } 1762