1b127ac41SPhilip Kirk /* 2b127ac41SPhilip Kirk * CDDL HEADER START 3b127ac41SPhilip Kirk * 4b127ac41SPhilip Kirk * The contents of this file are subject to the terms of the 5b127ac41SPhilip Kirk * Common Development and Distribution License (the "License"). 6b127ac41SPhilip Kirk * You may not use this file except in compliance with the License. 7b127ac41SPhilip Kirk * 8b127ac41SPhilip Kirk * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9b127ac41SPhilip Kirk * or http://www.opensolaris.org/os/licensing. 10b127ac41SPhilip Kirk * See the License for the specific language governing permissions 11b127ac41SPhilip Kirk * and limitations under the License. 12b127ac41SPhilip Kirk * 13b127ac41SPhilip Kirk * When distributing Covered Code, include this CDDL HEADER in each 14b127ac41SPhilip Kirk * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15b127ac41SPhilip Kirk * If applicable, add the following below this CDDL HEADER, with the 16b127ac41SPhilip Kirk * fields enclosed by brackets "[]" replaced with your own identifying 17b127ac41SPhilip Kirk * information: Portions Copyright [yyyy] [name of copyright owner] 18b127ac41SPhilip Kirk * 19b127ac41SPhilip Kirk * CDDL HEADER END 20b127ac41SPhilip Kirk */ 21b127ac41SPhilip Kirk 22b127ac41SPhilip Kirk /* 23e11c3f44Smeem * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24b127ac41SPhilip Kirk * Use is subject to license terms. 25b127ac41SPhilip Kirk */ 26b127ac41SPhilip Kirk 27b127ac41SPhilip Kirk /* 28b127ac41SPhilip Kirk * The ipnet device defined here provides access to packets at the IP layer. To 29b127ac41SPhilip Kirk * provide access to packets at this layer it registers a callback function in 30b127ac41SPhilip Kirk * the ip module and when there are open instances of the device ip will pass 31b127ac41SPhilip Kirk * packets into the device. Packets from ip are passed on the input, output and 32b127ac41SPhilip Kirk * loopback paths. Internally the module returns to ip as soon as possible by 33b127ac41SPhilip Kirk * deferring processing using a taskq. 34b127ac41SPhilip Kirk * 35b127ac41SPhilip Kirk * Management of the devices in /dev/ipnet/ is handled by the devname 36b127ac41SPhilip Kirk * filesystem and use of the neti interfaces. This module registers for NIC 37b127ac41SPhilip Kirk * events using the neti framework so that when IP interfaces are bought up, 38b127ac41SPhilip Kirk * taken down etc. the ipnet module is notified and its view of the interfaces 39b127ac41SPhilip Kirk * configured on the system adjusted. On attach, the module gets an initial 40b127ac41SPhilip Kirk * view of the system again using the neti framework but as it has already 41b127ac41SPhilip Kirk * registered for IP interface events, it is still up-to-date with any changes. 42b127ac41SPhilip Kirk */ 43b127ac41SPhilip Kirk 44b127ac41SPhilip Kirk #include <sys/types.h> 45b127ac41SPhilip Kirk #include <sys/conf.h> 46b127ac41SPhilip Kirk #include <sys/cred.h> 47b127ac41SPhilip Kirk #include <sys/stat.h> 48b127ac41SPhilip Kirk #include <sys/ddi.h> 49b127ac41SPhilip Kirk #include <sys/sunddi.h> 50b127ac41SPhilip Kirk #include <sys/modctl.h> 51b127ac41SPhilip Kirk #include <sys/dlpi.h> 52b127ac41SPhilip Kirk #include <sys/strsun.h> 53b127ac41SPhilip Kirk #include <sys/id_space.h> 54b127ac41SPhilip Kirk #include <sys/kmem.h> 55b127ac41SPhilip Kirk #include <sys/mkdev.h> 56b127ac41SPhilip Kirk #include <sys/neti.h> 57b127ac41SPhilip Kirk #include <net/if.h> 58b127ac41SPhilip Kirk #include <sys/errno.h> 59b127ac41SPhilip Kirk #include <sys/list.h> 60b127ac41SPhilip Kirk #include <sys/ksynch.h> 61b127ac41SPhilip Kirk #include <sys/hook_event.h> 620a0e9771SDarren Reed #include <sys/sdt.h> 63b127ac41SPhilip Kirk #include <sys/stropts.h> 64b127ac41SPhilip Kirk #include <sys/sysmacros.h> 65b127ac41SPhilip Kirk #include <inet/ip.h> 660a0e9771SDarren Reed #include <inet/ip_if.h> 67b127ac41SPhilip Kirk #include <inet/ip_multi.h> 68b127ac41SPhilip Kirk #include <inet/ip6.h> 69b127ac41SPhilip Kirk #include <inet/ipnet.h> 700a0e9771SDarren Reed #include <net/bpf.h> 710a0e9771SDarren Reed #include <net/bpfdesc.h> 720a0e9771SDarren Reed #include <net/dlt.h> 73b127ac41SPhilip Kirk 74b127ac41SPhilip Kirk static struct module_info ipnet_minfo = { 75b127ac41SPhilip Kirk 1, /* mi_idnum */ 76b127ac41SPhilip Kirk "ipnet", /* mi_idname */ 77b127ac41SPhilip Kirk 0, /* mi_minpsz */ 78b127ac41SPhilip Kirk INFPSZ, /* mi_maxpsz */ 79b127ac41SPhilip Kirk 2048, /* mi_hiwat */ 80b127ac41SPhilip Kirk 0 /* mi_lowat */ 81b127ac41SPhilip Kirk }; 82b127ac41SPhilip Kirk 83b127ac41SPhilip Kirk /* 84b127ac41SPhilip Kirk * List to hold static view of ipnetif_t's on the system. This is needed to 85b127ac41SPhilip Kirk * avoid holding the lock protecting the avl tree of ipnetif's over the 86b127ac41SPhilip Kirk * callback into the dev filesystem. 87b127ac41SPhilip Kirk */ 88b127ac41SPhilip Kirk typedef struct ipnetif_cbdata { 89b127ac41SPhilip Kirk char ic_ifname[LIFNAMSIZ]; 90b127ac41SPhilip Kirk dev_t ic_dev; 91b127ac41SPhilip Kirk list_node_t ic_next; 92b127ac41SPhilip Kirk } ipnetif_cbdata_t; 93b127ac41SPhilip Kirk 94b127ac41SPhilip Kirk /* 95b127ac41SPhilip Kirk * Convenience enumerated type for ipnet_accept(). It describes the 96b127ac41SPhilip Kirk * properties of a given ipnet_addrp_t relative to a single ipnet_t 97b127ac41SPhilip Kirk * client stream. The values represent whether the address is ... 98b127ac41SPhilip Kirk */ 99b127ac41SPhilip Kirk typedef enum { 100b127ac41SPhilip Kirk IPNETADDR_MYADDR, /* an address on my ipnetif_t. */ 101b127ac41SPhilip Kirk IPNETADDR_MBCAST, /* a multicast or broadcast address. */ 102b127ac41SPhilip Kirk IPNETADDR_UNKNOWN /* none of the above. */ 103b127ac41SPhilip Kirk } ipnet_addrtype_t; 104b127ac41SPhilip Kirk 105b127ac41SPhilip Kirk /* Argument used for the ipnet_nicevent_taskq callback. */ 106b127ac41SPhilip Kirk typedef struct ipnet_nicevent_s { 107b127ac41SPhilip Kirk nic_event_t ipne_event; 108b127ac41SPhilip Kirk net_handle_t ipne_protocol; 109b127ac41SPhilip Kirk netstackid_t ipne_stackid; 110b127ac41SPhilip Kirk uint64_t ipne_ifindex; 111b127ac41SPhilip Kirk uint64_t ipne_lifindex; 112b127ac41SPhilip Kirk char ipne_ifname[LIFNAMSIZ]; 113b127ac41SPhilip Kirk } ipnet_nicevent_t; 114b127ac41SPhilip Kirk 115b127ac41SPhilip Kirk static dev_info_t *ipnet_dip; 116b127ac41SPhilip Kirk static major_t ipnet_major; 117b127ac41SPhilip Kirk static ddi_taskq_t *ipnet_taskq; /* taskq for packets */ 118b127ac41SPhilip Kirk static ddi_taskq_t *ipnet_nicevent_taskq; /* taskq for NIC events */ 119b127ac41SPhilip Kirk static id_space_t *ipnet_minor_space; 120b127ac41SPhilip Kirk static const int IPNET_MINOR_LO = 1; /* minor number for /dev/lo0 */ 121b127ac41SPhilip Kirk static const int IPNET_MINOR_MIN = 2; /* start of dynamic minors */ 122b127ac41SPhilip Kirk static dl_info_ack_t ipnet_infoack = IPNET_INFO_ACK_INIT; 123b127ac41SPhilip Kirk static ipnet_acceptfn_t ipnet_accept, ipnet_loaccept; 1240a0e9771SDarren Reed static bpf_itap_fn_t ipnet_itap; 125b127ac41SPhilip Kirk 126b127ac41SPhilip Kirk static void ipnet_input(mblk_t *); 127b127ac41SPhilip Kirk static int ipnet_wput(queue_t *, mblk_t *); 128b127ac41SPhilip Kirk static int ipnet_rsrv(queue_t *); 129b127ac41SPhilip Kirk static int ipnet_open(queue_t *, dev_t *, int, int, cred_t *); 130b127ac41SPhilip Kirk static int ipnet_close(queue_t *); 131b127ac41SPhilip Kirk static void ipnet_ioctl(queue_t *, mblk_t *); 132b127ac41SPhilip Kirk static void ipnet_iocdata(queue_t *, mblk_t *); 133b127ac41SPhilip Kirk static void ipnet_wputnondata(queue_t *, mblk_t *); 134b127ac41SPhilip Kirk static int ipnet_attach(dev_info_t *, ddi_attach_cmd_t); 135b127ac41SPhilip Kirk static int ipnet_detach(dev_info_t *, ddi_detach_cmd_t); 136b127ac41SPhilip Kirk static int ipnet_devinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 137b127ac41SPhilip Kirk static void ipnet_inforeq(queue_t *q, mblk_t *mp); 138b127ac41SPhilip Kirk static void ipnet_bindreq(queue_t *q, mblk_t *mp); 139b127ac41SPhilip Kirk static void ipnet_unbindreq(queue_t *q, mblk_t *mp); 140b127ac41SPhilip Kirk static void ipnet_dlpromisconreq(queue_t *q, mblk_t *mp); 141b127ac41SPhilip Kirk static void ipnet_dlpromiscoffreq(queue_t *q, mblk_t *mp); 142b127ac41SPhilip Kirk static int ipnet_join_allmulti(ipnetif_t *, ipnet_stack_t *); 143b127ac41SPhilip Kirk static void ipnet_leave_allmulti(ipnetif_t *, ipnet_stack_t *); 144b127ac41SPhilip Kirk static int ipnet_nicevent_cb(hook_event_token_t, hook_data_t, void *); 145b127ac41SPhilip Kirk static void ipnet_nicevent_task(void *); 1460a0e9771SDarren Reed static ipnetif_t *ipnetif_create(const char *, uint64_t, ipnet_stack_t *, 1470a0e9771SDarren Reed uint64_t); 1480a0e9771SDarren Reed static void ipnetif_remove(ipnetif_t *, ipnet_stack_t *); 149b127ac41SPhilip Kirk static ipnetif_addr_t *ipnet_match_lif(ipnetif_t *, lif_if_t, boolean_t); 1500a0e9771SDarren Reed static ipnetif_t *ipnetif_getby_index(uint64_t, ipnet_stack_t *); 1510a0e9771SDarren Reed static ipnetif_t *ipnetif_getby_dev(dev_t, ipnet_stack_t *); 1520a0e9771SDarren Reed static boolean_t ipnetif_in_zone(ipnetif_t *, zoneid_t, ipnet_stack_t *); 1530a0e9771SDarren Reed static void ipnetif_zonecheck(ipnetif_t *, ipnet_stack_t *); 154b127ac41SPhilip Kirk static int ipnet_populate_if(net_handle_t, ipnet_stack_t *, boolean_t); 1550a0e9771SDarren Reed static int ipnetif_compare_name(const void *, const void *); 1560a0e9771SDarren Reed static int ipnetif_compare_name_zone(const void *, const void *); 1570a0e9771SDarren Reed static int ipnetif_compare_index(const void *, const void *); 158b127ac41SPhilip Kirk static void ipnet_add_ifaddr(uint64_t, ipnetif_t *, net_handle_t); 159b127ac41SPhilip Kirk static void ipnet_delete_ifaddr(ipnetif_addr_t *, ipnetif_t *, boolean_t); 160b127ac41SPhilip Kirk static void ipnetif_refhold(ipnetif_t *); 161b127ac41SPhilip Kirk static void ipnetif_refrele(ipnetif_t *); 162b127ac41SPhilip Kirk static void ipnet_walkers_inc(ipnet_stack_t *); 163b127ac41SPhilip Kirk static void ipnet_walkers_dec(ipnet_stack_t *); 164b127ac41SPhilip Kirk static void ipnet_register_netihook(ipnet_stack_t *); 165b127ac41SPhilip Kirk static void *ipnet_stack_init(netstackid_t, netstack_t *); 166b127ac41SPhilip Kirk static void ipnet_stack_fini(netstackid_t, void *); 1670a0e9771SDarren Reed static void ipnet_dispatch(void *); 1680a0e9771SDarren Reed static int ipobs_bounce_func(hook_event_token_t, hook_data_t, void *); 1690a0e9771SDarren Reed static int ipnet_bpf_bounce(hook_event_token_t, hook_data_t, void *); 1700a0e9771SDarren Reed static ipnetif_t *ipnetif_clone_create(ipnetif_t *, zoneid_t); 1710a0e9771SDarren Reed static void ipnetif_clone_release(ipnetif_t *); 172b127ac41SPhilip Kirk 173b127ac41SPhilip Kirk static struct qinit ipnet_rinit = { 174b127ac41SPhilip Kirk NULL, /* qi_putp */ 175b127ac41SPhilip Kirk ipnet_rsrv, /* qi_srvp */ 176b127ac41SPhilip Kirk ipnet_open, /* qi_qopen */ 177b127ac41SPhilip Kirk ipnet_close, /* qi_qclose */ 178b127ac41SPhilip Kirk NULL, /* qi_qadmin */ 179b127ac41SPhilip Kirk &ipnet_minfo, /* qi_minfo */ 180b127ac41SPhilip Kirk }; 181b127ac41SPhilip Kirk 182b127ac41SPhilip Kirk static struct qinit ipnet_winit = { 183b127ac41SPhilip Kirk ipnet_wput, /* qi_putp */ 184b127ac41SPhilip Kirk NULL, /* qi_srvp */ 185b127ac41SPhilip Kirk NULL, /* qi_qopen */ 186b127ac41SPhilip Kirk NULL, /* qi_qclose */ 187b127ac41SPhilip Kirk NULL, /* qi_qadmin */ 188b127ac41SPhilip Kirk &ipnet_minfo, /* qi_minfo */ 189b127ac41SPhilip Kirk }; 190b127ac41SPhilip Kirk 191b127ac41SPhilip Kirk static struct streamtab ipnet_info = { 192b127ac41SPhilip Kirk &ipnet_rinit, &ipnet_winit 193b127ac41SPhilip Kirk }; 194b127ac41SPhilip Kirk 195b127ac41SPhilip Kirk DDI_DEFINE_STREAM_OPS(ipnet_ops, nulldev, nulldev, ipnet_attach, 196b127ac41SPhilip Kirk ipnet_detach, nodev, ipnet_devinfo, D_MP | D_MTPERMOD, &ipnet_info, 197b127ac41SPhilip Kirk ddi_quiesce_not_supported); 198b127ac41SPhilip Kirk 199b127ac41SPhilip Kirk static struct modldrv modldrv = { 200b127ac41SPhilip Kirk &mod_driverops, 201b127ac41SPhilip Kirk "STREAMS ipnet driver", 202b127ac41SPhilip Kirk &ipnet_ops 203b127ac41SPhilip Kirk }; 204b127ac41SPhilip Kirk 205b127ac41SPhilip Kirk static struct modlinkage modlinkage = { 206b127ac41SPhilip Kirk MODREV_1, &modldrv, NULL 207b127ac41SPhilip Kirk }; 208b127ac41SPhilip Kirk 209b127ac41SPhilip Kirk /* 2100a0e9771SDarren Reed * This structure contains the template data (names and type) that is 2110a0e9771SDarren Reed * copied, in bulk, into the new kstats structure created by net_kstat_create. 2120a0e9771SDarren Reed * No actual statistical information is stored in this instance of the 2130a0e9771SDarren Reed * ipnet_kstats_t structure. 2140a0e9771SDarren Reed */ 2150a0e9771SDarren Reed static ipnet_kstats_t stats_template = { 2160a0e9771SDarren Reed { "duplicationFail", KSTAT_DATA_UINT64 }, 2170a0e9771SDarren Reed { "dispatchOk", KSTAT_DATA_UINT64 }, 2180a0e9771SDarren Reed { "dispatchFail", KSTAT_DATA_UINT64 }, 2190a0e9771SDarren Reed { "dispatchHeaderDrop", KSTAT_DATA_UINT64 }, 2200a0e9771SDarren Reed { "dispatchDupDrop", KSTAT_DATA_UINT64 }, 221*9963d0d2SArne Jansen { "dispatchPutDrop", KSTAT_DATA_UINT64 }, 2220a0e9771SDarren Reed { "dispatchDeliver", KSTAT_DATA_UINT64 }, 2230a0e9771SDarren Reed { "acceptOk", KSTAT_DATA_UINT64 }, 2240a0e9771SDarren Reed { "acceptFail", KSTAT_DATA_UINT64 } 2250a0e9771SDarren Reed }; 2260a0e9771SDarren Reed 2270a0e9771SDarren Reed /* 228b127ac41SPhilip Kirk * Walk the list of physical interfaces on the machine, for each 229b127ac41SPhilip Kirk * interface create a new ipnetif_t and add any addresses to it. We 230b127ac41SPhilip Kirk * need to do the walk twice, once for IPv4 and once for IPv6. 231b127ac41SPhilip Kirk * 232b127ac41SPhilip Kirk * The interfaces are destroyed as part of ipnet_stack_fini() for each 233b127ac41SPhilip Kirk * stack. Note that we cannot do this initialization in 234b127ac41SPhilip Kirk * ipnet_stack_init(), since ipnet_stack_init() cannot fail. 235b127ac41SPhilip Kirk */ 236b127ac41SPhilip Kirk static int 2370a0e9771SDarren Reed ipnetif_init(void) 238b127ac41SPhilip Kirk { 239b127ac41SPhilip Kirk netstack_handle_t nh; 240b127ac41SPhilip Kirk netstack_t *ns; 241b127ac41SPhilip Kirk ipnet_stack_t *ips; 242b127ac41SPhilip Kirk int ret = 0; 243b127ac41SPhilip Kirk 244b127ac41SPhilip Kirk netstack_next_init(&nh); 245b127ac41SPhilip Kirk while ((ns = netstack_next(&nh)) != NULL) { 246b127ac41SPhilip Kirk ips = ns->netstack_ipnet; 247133be305SSebastien Roy if ((ret = ipnet_populate_if(ips->ips_ndv4, ips, B_FALSE)) == 0) 248133be305SSebastien Roy ret = ipnet_populate_if(ips->ips_ndv6, ips, B_TRUE); 249133be305SSebastien Roy netstack_rele(ns); 250133be305SSebastien Roy if (ret != 0) 251b127ac41SPhilip Kirk break; 252b127ac41SPhilip Kirk } 253b127ac41SPhilip Kirk netstack_next_fini(&nh); 254b127ac41SPhilip Kirk return (ret); 255b127ac41SPhilip Kirk } 256b127ac41SPhilip Kirk 257b127ac41SPhilip Kirk /* 258b127ac41SPhilip Kirk * Standard module entry points. 259b127ac41SPhilip Kirk */ 260b127ac41SPhilip Kirk int 261b127ac41SPhilip Kirk _init(void) 262b127ac41SPhilip Kirk { 263b127ac41SPhilip Kirk int ret; 264e11c3f44Smeem boolean_t netstack_registered = B_FALSE; 265b127ac41SPhilip Kirk 266b127ac41SPhilip Kirk if ((ipnet_major = ddi_name_to_major("ipnet")) == (major_t)-1) 267b127ac41SPhilip Kirk return (ENODEV); 268b127ac41SPhilip Kirk ipnet_minor_space = id_space_create("ipnet_minor_space", 269b127ac41SPhilip Kirk IPNET_MINOR_MIN, MAXMIN32); 270e11c3f44Smeem 271b127ac41SPhilip Kirk /* 272b127ac41SPhilip Kirk * We call ddi_taskq_create() with nthread == 1 to ensure in-order 273e11c3f44Smeem * delivery of packets to clients. Note that we need to create the 274e11c3f44Smeem * taskqs before calling netstack_register() since ipnet_stack_init() 275e11c3f44Smeem * registers callbacks that use 'em. 276b127ac41SPhilip Kirk */ 277b127ac41SPhilip Kirk ipnet_taskq = ddi_taskq_create(NULL, "ipnet", 1, TASKQ_DEFAULTPRI, 0); 278b127ac41SPhilip Kirk ipnet_nicevent_taskq = ddi_taskq_create(NULL, "ipnet_nic_event_queue", 279b127ac41SPhilip Kirk 1, TASKQ_DEFAULTPRI, 0); 280b127ac41SPhilip Kirk if (ipnet_taskq == NULL || ipnet_nicevent_taskq == NULL) { 281b127ac41SPhilip Kirk ret = ENOMEM; 282b127ac41SPhilip Kirk goto done; 283b127ac41SPhilip Kirk } 284e11c3f44Smeem 285e11c3f44Smeem netstack_register(NS_IPNET, ipnet_stack_init, NULL, ipnet_stack_fini); 286e11c3f44Smeem netstack_registered = B_TRUE; 287e11c3f44Smeem 2880a0e9771SDarren Reed if ((ret = ipnetif_init()) == 0) 289b127ac41SPhilip Kirk ret = mod_install(&modlinkage); 290b127ac41SPhilip Kirk done: 291b127ac41SPhilip Kirk if (ret != 0) { 292b127ac41SPhilip Kirk if (ipnet_taskq != NULL) 293b127ac41SPhilip Kirk ddi_taskq_destroy(ipnet_taskq); 294b127ac41SPhilip Kirk if (ipnet_nicevent_taskq != NULL) 295b127ac41SPhilip Kirk ddi_taskq_destroy(ipnet_nicevent_taskq); 296e11c3f44Smeem if (netstack_registered) 297b127ac41SPhilip Kirk netstack_unregister(NS_IPNET); 298b127ac41SPhilip Kirk id_space_destroy(ipnet_minor_space); 299b127ac41SPhilip Kirk } 300b127ac41SPhilip Kirk return (ret); 301b127ac41SPhilip Kirk } 302b127ac41SPhilip Kirk 303b127ac41SPhilip Kirk int 304b127ac41SPhilip Kirk _fini(void) 305b127ac41SPhilip Kirk { 306b127ac41SPhilip Kirk int err; 307b127ac41SPhilip Kirk 308b127ac41SPhilip Kirk if ((err = mod_remove(&modlinkage)) != 0) 309b127ac41SPhilip Kirk return (err); 310e11c3f44Smeem 311e11c3f44Smeem netstack_unregister(NS_IPNET); 312b127ac41SPhilip Kirk ddi_taskq_destroy(ipnet_nicevent_taskq); 313b127ac41SPhilip Kirk ddi_taskq_destroy(ipnet_taskq); 314b127ac41SPhilip Kirk id_space_destroy(ipnet_minor_space); 315b127ac41SPhilip Kirk return (0); 316b127ac41SPhilip Kirk } 317b127ac41SPhilip Kirk 318b127ac41SPhilip Kirk int 319b127ac41SPhilip Kirk _info(struct modinfo *modinfop) 320b127ac41SPhilip Kirk { 321b127ac41SPhilip Kirk return (mod_info(&modlinkage, modinfop)); 322b127ac41SPhilip Kirk } 323b127ac41SPhilip Kirk 324b127ac41SPhilip Kirk static void 325b127ac41SPhilip Kirk ipnet_register_netihook(ipnet_stack_t *ips) 326b127ac41SPhilip Kirk { 327b127ac41SPhilip Kirk int ret; 328133be305SSebastien Roy zoneid_t zoneid; 329133be305SSebastien Roy netid_t netid; 330b127ac41SPhilip Kirk 331b127ac41SPhilip Kirk HOOK_INIT(ips->ips_nicevents, ipnet_nicevent_cb, "ipnet_nicevents", 332b127ac41SPhilip Kirk ips); 333b127ac41SPhilip Kirk 334b127ac41SPhilip Kirk /* 335133be305SSebastien Roy * It is possible for an exclusive stack to be in the process of 336133be305SSebastien Roy * shutting down here, and the netid and protocol lookups could fail 337133be305SSebastien Roy * in that case. 338b127ac41SPhilip Kirk */ 339133be305SSebastien Roy zoneid = netstackid_to_zoneid(ips->ips_netstack->netstack_stackid); 340133be305SSebastien Roy if ((netid = net_zoneidtonetid(zoneid)) == -1) 341133be305SSebastien Roy return; 342b127ac41SPhilip Kirk 343133be305SSebastien Roy if ((ips->ips_ndv4 = net_protocol_lookup(netid, NHF_INET)) != NULL) { 344133be305SSebastien Roy if ((ret = net_hook_register(ips->ips_ndv4, NH_NIC_EVENTS, 345133be305SSebastien Roy ips->ips_nicevents)) != 0) { 346133be305SSebastien Roy VERIFY(net_protocol_release(ips->ips_ndv4) == 0); 347133be305SSebastien Roy ips->ips_ndv4 = NULL; 348133be305SSebastien Roy cmn_err(CE_WARN, "unable to register IPv4 netinfo hooks" 349133be305SSebastien Roy " in zone %d: %d", zoneid, ret); 350b127ac41SPhilip Kirk } 351133be305SSebastien Roy } 352133be305SSebastien Roy if ((ips->ips_ndv6 = net_protocol_lookup(netid, NHF_INET6)) != NULL) { 353133be305SSebastien Roy if ((ret = net_hook_register(ips->ips_ndv6, NH_NIC_EVENTS, 354133be305SSebastien Roy ips->ips_nicevents)) != 0) { 355133be305SSebastien Roy VERIFY(net_protocol_release(ips->ips_ndv6) == 0); 356133be305SSebastien Roy ips->ips_ndv6 = NULL; 357133be305SSebastien Roy cmn_err(CE_WARN, "unable to register IPv6 netinfo hooks" 358133be305SSebastien Roy " in zone %d: %d", zoneid, ret); 359133be305SSebastien Roy } 360b127ac41SPhilip Kirk } 3610a0e9771SDarren Reed 3620a0e9771SDarren Reed /* 3630a0e9771SDarren Reed * Create a local set of kstats for each zone. 3640a0e9771SDarren Reed */ 3650a0e9771SDarren Reed ips->ips_kstatp = net_kstat_create(netid, "ipnet", 0, "ipnet_stats", 3660a0e9771SDarren Reed "misc", KSTAT_TYPE_NAMED, 3670a0e9771SDarren Reed sizeof (ipnet_kstats_t) / sizeof (kstat_named_t), 0); 3680a0e9771SDarren Reed if (ips->ips_kstatp != NULL) { 3690a0e9771SDarren Reed bcopy(&stats_template, &ips->ips_stats, 3700a0e9771SDarren Reed sizeof (ips->ips_stats)); 3710a0e9771SDarren Reed ips->ips_kstatp->ks_data = &ips->ips_stats; 3720a0e9771SDarren Reed ips->ips_kstatp->ks_private = 3730a0e9771SDarren Reed (void *)(uintptr_t)ips->ips_netstack->netstack_stackid; 3740a0e9771SDarren Reed kstat_install(ips->ips_kstatp); 3750a0e9771SDarren Reed } else { 3760a0e9771SDarren Reed cmn_err(CE_WARN, "net_kstat_create(%s,%s,%s) failed", 3770a0e9771SDarren Reed "ipnet", "ipnet_stats", "misc"); 3780a0e9771SDarren Reed } 379b127ac41SPhilip Kirk } 380b127ac41SPhilip Kirk 381b127ac41SPhilip Kirk /* 382b127ac41SPhilip Kirk * This function is called on attach to build an initial view of the 383b127ac41SPhilip Kirk * interfaces on the system. It will be called once for IPv4 and once 384b127ac41SPhilip Kirk * for IPv6, although there is only one ipnet interface for both IPv4 385b127ac41SPhilip Kirk * and IPv6 there are separate address lists. 386b127ac41SPhilip Kirk */ 387b127ac41SPhilip Kirk static int 388b127ac41SPhilip Kirk ipnet_populate_if(net_handle_t nd, ipnet_stack_t *ips, boolean_t isv6) 389b127ac41SPhilip Kirk { 390b127ac41SPhilip Kirk phy_if_t phyif; 391b127ac41SPhilip Kirk lif_if_t lif; 392b127ac41SPhilip Kirk ipnetif_t *ipnetif; 393b127ac41SPhilip Kirk char name[LIFNAMSIZ]; 394b127ac41SPhilip Kirk boolean_t new_if = B_FALSE; 395b127ac41SPhilip Kirk uint64_t ifflags; 396b127ac41SPhilip Kirk int ret = 0; 397b127ac41SPhilip Kirk 398b127ac41SPhilip Kirk /* 399133be305SSebastien Roy * If ipnet_register_netihook() was unable to initialize this 400133be305SSebastien Roy * stack's net_handle_t, then we cannot populate any interface 401133be305SSebastien Roy * information. This usually happens when we attempted to 402133be305SSebastien Roy * grab a net_handle_t as a stack was shutting down. We don't 403133be305SSebastien Roy * want to fail the entire _init() operation because of a 404133be305SSebastien Roy * stack shutdown (other stacks will continue to work just 405133be305SSebastien Roy * fine), so we silently return success here. 406133be305SSebastien Roy */ 407133be305SSebastien Roy if (nd == NULL) 408133be305SSebastien Roy return (0); 409133be305SSebastien Roy 410133be305SSebastien Roy /* 411b127ac41SPhilip Kirk * Make sure we're not processing NIC events during the 412b127ac41SPhilip Kirk * population of our interfaces and address lists. 413b127ac41SPhilip Kirk */ 414b127ac41SPhilip Kirk mutex_enter(&ips->ips_event_lock); 415b127ac41SPhilip Kirk 416b127ac41SPhilip Kirk for (phyif = net_phygetnext(nd, 0); phyif != 0; 417b127ac41SPhilip Kirk phyif = net_phygetnext(nd, phyif)) { 418b127ac41SPhilip Kirk if (net_getifname(nd, phyif, name, LIFNAMSIZ) != 0) 419b127ac41SPhilip Kirk continue; 4200a0e9771SDarren Reed ifflags = 0; 4210a0e9771SDarren Reed (void) net_getlifflags(nd, phyif, 0, &ifflags); 4220a0e9771SDarren Reed if ((ipnetif = ipnetif_getby_index(phyif, ips)) == NULL) { 4230a0e9771SDarren Reed ipnetif = ipnetif_create(name, phyif, ips, ifflags); 424b127ac41SPhilip Kirk if (ipnetif == NULL) { 425b127ac41SPhilip Kirk ret = ENOMEM; 426b127ac41SPhilip Kirk goto done; 427b127ac41SPhilip Kirk } 428b127ac41SPhilip Kirk new_if = B_TRUE; 429b127ac41SPhilip Kirk } 430b127ac41SPhilip Kirk ipnetif->if_flags |= 431b127ac41SPhilip Kirk isv6 ? IPNETIF_IPV6PLUMBED : IPNETIF_IPV4PLUMBED; 432b127ac41SPhilip Kirk 433b127ac41SPhilip Kirk for (lif = net_lifgetnext(nd, phyif, 0); lif != 0; 434b127ac41SPhilip Kirk lif = net_lifgetnext(nd, phyif, lif)) { 435b127ac41SPhilip Kirk /* 436b127ac41SPhilip Kirk * Skip addresses that aren't up. We'll add 437b127ac41SPhilip Kirk * them when we receive an NE_LIF_UP event. 438b127ac41SPhilip Kirk */ 439b127ac41SPhilip Kirk if (net_getlifflags(nd, phyif, lif, &ifflags) != 0 || 440b127ac41SPhilip Kirk !(ifflags & IFF_UP)) 441b127ac41SPhilip Kirk continue; 442b127ac41SPhilip Kirk /* Don't add it if we already have it. */ 443b127ac41SPhilip Kirk if (ipnet_match_lif(ipnetif, lif, isv6) != NULL) 444b127ac41SPhilip Kirk continue; 445b127ac41SPhilip Kirk ipnet_add_ifaddr(lif, ipnetif, nd); 446b127ac41SPhilip Kirk } 447b127ac41SPhilip Kirk if (!new_if) 448b127ac41SPhilip Kirk ipnetif_refrele(ipnetif); 449b127ac41SPhilip Kirk } 450b127ac41SPhilip Kirk 451b127ac41SPhilip Kirk done: 452b127ac41SPhilip Kirk mutex_exit(&ips->ips_event_lock); 453b127ac41SPhilip Kirk return (ret); 454b127ac41SPhilip Kirk } 455b127ac41SPhilip Kirk 456b127ac41SPhilip Kirk static int 457b127ac41SPhilip Kirk ipnet_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 458b127ac41SPhilip Kirk { 459b127ac41SPhilip Kirk if (cmd != DDI_ATTACH) 460b127ac41SPhilip Kirk return (DDI_FAILURE); 461b127ac41SPhilip Kirk 462b127ac41SPhilip Kirk if (ddi_create_minor_node(dip, "lo0", S_IFCHR, IPNET_MINOR_LO, 463b127ac41SPhilip Kirk DDI_PSEUDO, 0) == DDI_FAILURE) 464b127ac41SPhilip Kirk return (DDI_FAILURE); 465b127ac41SPhilip Kirk 466b127ac41SPhilip Kirk ipnet_dip = dip; 467b127ac41SPhilip Kirk return (DDI_SUCCESS); 468b127ac41SPhilip Kirk } 469b127ac41SPhilip Kirk 470b127ac41SPhilip Kirk static int 471b127ac41SPhilip Kirk ipnet_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 472b127ac41SPhilip Kirk { 473b127ac41SPhilip Kirk if (cmd != DDI_DETACH) 474b127ac41SPhilip Kirk return (DDI_FAILURE); 475b127ac41SPhilip Kirk 476b127ac41SPhilip Kirk ASSERT(dip == ipnet_dip); 477b127ac41SPhilip Kirk ddi_remove_minor_node(ipnet_dip, NULL); 478b127ac41SPhilip Kirk ipnet_dip = NULL; 479b127ac41SPhilip Kirk return (DDI_SUCCESS); 480b127ac41SPhilip Kirk } 481b127ac41SPhilip Kirk 482b127ac41SPhilip Kirk /* ARGSUSED */ 483b127ac41SPhilip Kirk static int 484b127ac41SPhilip Kirk ipnet_devinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 485b127ac41SPhilip Kirk { 486b127ac41SPhilip Kirk int error = DDI_FAILURE; 487b127ac41SPhilip Kirk 488b127ac41SPhilip Kirk switch (infocmd) { 489b127ac41SPhilip Kirk case DDI_INFO_DEVT2INSTANCE: 490b127ac41SPhilip Kirk *result = (void *)0; 491b127ac41SPhilip Kirk error = DDI_SUCCESS; 492b127ac41SPhilip Kirk break; 493b127ac41SPhilip Kirk case DDI_INFO_DEVT2DEVINFO: 494b127ac41SPhilip Kirk if (ipnet_dip != NULL) { 495b127ac41SPhilip Kirk *result = ipnet_dip; 496b127ac41SPhilip Kirk error = DDI_SUCCESS; 497b127ac41SPhilip Kirk } 498b127ac41SPhilip Kirk break; 499b127ac41SPhilip Kirk } 500b127ac41SPhilip Kirk return (error); 501b127ac41SPhilip Kirk } 502b127ac41SPhilip Kirk 503b127ac41SPhilip Kirk /* ARGSUSED */ 504b127ac41SPhilip Kirk static int 505b127ac41SPhilip Kirk ipnet_open(queue_t *rq, dev_t *dev, int oflag, int sflag, cred_t *crp) 506b127ac41SPhilip Kirk { 507b127ac41SPhilip Kirk ipnet_t *ipnet; 508b127ac41SPhilip Kirk netstack_t *ns = NULL; 509b127ac41SPhilip Kirk ipnet_stack_t *ips; 510b127ac41SPhilip Kirk int err = 0; 511b127ac41SPhilip Kirk zoneid_t zoneid = crgetzoneid(crp); 512b127ac41SPhilip Kirk 513b127ac41SPhilip Kirk /* 514b127ac41SPhilip Kirk * If the system is labeled, only the global zone is allowed to open 515b127ac41SPhilip Kirk * IP observability nodes. 516b127ac41SPhilip Kirk */ 517b127ac41SPhilip Kirk if (is_system_labeled() && zoneid != GLOBAL_ZONEID) 518b127ac41SPhilip Kirk return (EACCES); 519b127ac41SPhilip Kirk 520b127ac41SPhilip Kirk /* We don't support open as a module */ 521b127ac41SPhilip Kirk if (sflag & MODOPEN) 522b127ac41SPhilip Kirk return (ENOTSUP); 523b127ac41SPhilip Kirk 524b127ac41SPhilip Kirk /* This driver is self-cloning, we don't support re-open. */ 525b127ac41SPhilip Kirk if (rq->q_ptr != NULL) 526b127ac41SPhilip Kirk return (EBUSY); 527b127ac41SPhilip Kirk 528b127ac41SPhilip Kirk if ((ipnet = kmem_zalloc(sizeof (*ipnet), KM_NOSLEEP)) == NULL) 529b127ac41SPhilip Kirk return (ENOMEM); 530b127ac41SPhilip Kirk 531b127ac41SPhilip Kirk VERIFY((ns = netstack_find_by_cred(crp)) != NULL); 532b127ac41SPhilip Kirk ips = ns->netstack_ipnet; 533b127ac41SPhilip Kirk 534b127ac41SPhilip Kirk rq->q_ptr = WR(rq)->q_ptr = ipnet; 535b127ac41SPhilip Kirk ipnet->ipnet_rq = rq; 536b127ac41SPhilip Kirk ipnet->ipnet_minor = (minor_t)id_alloc(ipnet_minor_space); 537b127ac41SPhilip Kirk ipnet->ipnet_zoneid = zoneid; 538b127ac41SPhilip Kirk ipnet->ipnet_dlstate = DL_UNBOUND; 539b127ac41SPhilip Kirk ipnet->ipnet_ns = ns; 540b127ac41SPhilip Kirk 541b127ac41SPhilip Kirk /* 542b127ac41SPhilip Kirk * We need to hold ips_event_lock here as any NE_LIF_DOWN events need 543b127ac41SPhilip Kirk * to be processed after ipnet_if is set and the ipnet_t has been 544b127ac41SPhilip Kirk * inserted in the ips_str_list. 545b127ac41SPhilip Kirk */ 546b127ac41SPhilip Kirk mutex_enter(&ips->ips_event_lock); 547b127ac41SPhilip Kirk if (getminor(*dev) == IPNET_MINOR_LO) { 548b127ac41SPhilip Kirk ipnet->ipnet_flags |= IPNET_LOMODE; 549b127ac41SPhilip Kirk ipnet->ipnet_acceptfn = ipnet_loaccept; 550b127ac41SPhilip Kirk } else { 551b127ac41SPhilip Kirk ipnet->ipnet_acceptfn = ipnet_accept; 5520a0e9771SDarren Reed ipnet->ipnet_if = ipnetif_getby_dev(*dev, ips); 553b127ac41SPhilip Kirk if (ipnet->ipnet_if == NULL || 5540a0e9771SDarren Reed !ipnetif_in_zone(ipnet->ipnet_if, zoneid, ips)) { 555b127ac41SPhilip Kirk err = ENODEV; 556b127ac41SPhilip Kirk goto done; 557b127ac41SPhilip Kirk } 558b127ac41SPhilip Kirk } 559b127ac41SPhilip Kirk 560b127ac41SPhilip Kirk mutex_enter(&ips->ips_walkers_lock); 561b127ac41SPhilip Kirk while (ips->ips_walkers_cnt != 0) 562b127ac41SPhilip Kirk cv_wait(&ips->ips_walkers_cv, &ips->ips_walkers_lock); 563b127ac41SPhilip Kirk list_insert_head(&ips->ips_str_list, ipnet); 564b127ac41SPhilip Kirk *dev = makedevice(getmajor(*dev), ipnet->ipnet_minor); 565b127ac41SPhilip Kirk qprocson(rq); 566b127ac41SPhilip Kirk 567b127ac41SPhilip Kirk /* 568b127ac41SPhilip Kirk * Only register our callback if we're the first open client; we call 569b127ac41SPhilip Kirk * unregister in close() for the last open client. 570b127ac41SPhilip Kirk */ 571b127ac41SPhilip Kirk if (list_head(&ips->ips_str_list) == list_tail(&ips->ips_str_list)) 5720a0e9771SDarren Reed ips->ips_hook = ipobs_register_hook(ns, ipnet_input); 573b127ac41SPhilip Kirk mutex_exit(&ips->ips_walkers_lock); 574b127ac41SPhilip Kirk 575b127ac41SPhilip Kirk done: 576b127ac41SPhilip Kirk mutex_exit(&ips->ips_event_lock); 577b127ac41SPhilip Kirk if (err != 0) { 578b127ac41SPhilip Kirk netstack_rele(ns); 579b127ac41SPhilip Kirk id_free(ipnet_minor_space, ipnet->ipnet_minor); 580b127ac41SPhilip Kirk if (ipnet->ipnet_if != NULL) 581b127ac41SPhilip Kirk ipnetif_refrele(ipnet->ipnet_if); 582b127ac41SPhilip Kirk kmem_free(ipnet, sizeof (*ipnet)); 583b127ac41SPhilip Kirk } 584b127ac41SPhilip Kirk return (err); 585b127ac41SPhilip Kirk } 586b127ac41SPhilip Kirk 587b127ac41SPhilip Kirk static int 588b127ac41SPhilip Kirk ipnet_close(queue_t *rq) 589b127ac41SPhilip Kirk { 590b127ac41SPhilip Kirk ipnet_t *ipnet = rq->q_ptr; 591b127ac41SPhilip Kirk ipnet_stack_t *ips = ipnet->ipnet_ns->netstack_ipnet; 592b127ac41SPhilip Kirk 593b127ac41SPhilip Kirk if (ipnet->ipnet_flags & IPNET_PROMISC_PHYS) 594b127ac41SPhilip Kirk ipnet_leave_allmulti(ipnet->ipnet_if, ips); 595b127ac41SPhilip Kirk if (ipnet->ipnet_flags & IPNET_PROMISC_MULTI) 596b127ac41SPhilip Kirk ipnet_leave_allmulti(ipnet->ipnet_if, ips); 597b127ac41SPhilip Kirk 598b127ac41SPhilip Kirk mutex_enter(&ips->ips_walkers_lock); 599b127ac41SPhilip Kirk while (ips->ips_walkers_cnt != 0) 600b127ac41SPhilip Kirk cv_wait(&ips->ips_walkers_cv, &ips->ips_walkers_lock); 601b127ac41SPhilip Kirk 602b127ac41SPhilip Kirk qprocsoff(rq); 603b127ac41SPhilip Kirk 604b127ac41SPhilip Kirk list_remove(&ips->ips_str_list, ipnet); 605b127ac41SPhilip Kirk if (ipnet->ipnet_if != NULL) 606b127ac41SPhilip Kirk ipnetif_refrele(ipnet->ipnet_if); 607b127ac41SPhilip Kirk id_free(ipnet_minor_space, ipnet->ipnet_minor); 608b127ac41SPhilip Kirk 6090a0e9771SDarren Reed if (list_is_empty(&ips->ips_str_list)) { 6100a0e9771SDarren Reed ipobs_unregister_hook(ips->ips_netstack, ips->ips_hook); 6110a0e9771SDarren Reed ips->ips_hook = NULL; 6120a0e9771SDarren Reed } 6130a0e9771SDarren Reed 6140a0e9771SDarren Reed kmem_free(ipnet, sizeof (*ipnet)); 615b127ac41SPhilip Kirk 616b127ac41SPhilip Kirk mutex_exit(&ips->ips_walkers_lock); 617b127ac41SPhilip Kirk netstack_rele(ips->ips_netstack); 618b127ac41SPhilip Kirk return (0); 619b127ac41SPhilip Kirk } 620b127ac41SPhilip Kirk 621b127ac41SPhilip Kirk static int 622b127ac41SPhilip Kirk ipnet_wput(queue_t *q, mblk_t *mp) 623b127ac41SPhilip Kirk { 624b127ac41SPhilip Kirk switch (mp->b_datap->db_type) { 625b127ac41SPhilip Kirk case M_FLUSH: 626b127ac41SPhilip Kirk if (*mp->b_rptr & FLUSHW) { 627b127ac41SPhilip Kirk flushq(q, FLUSHDATA); 628b127ac41SPhilip Kirk *mp->b_rptr &= ~FLUSHW; 629b127ac41SPhilip Kirk } 630b127ac41SPhilip Kirk if (*mp->b_rptr & FLUSHR) 631b127ac41SPhilip Kirk qreply(q, mp); 632b127ac41SPhilip Kirk else 633b127ac41SPhilip Kirk freemsg(mp); 634b127ac41SPhilip Kirk break; 635b127ac41SPhilip Kirk case M_PROTO: 636b127ac41SPhilip Kirk case M_PCPROTO: 637b127ac41SPhilip Kirk ipnet_wputnondata(q, mp); 638b127ac41SPhilip Kirk break; 639b127ac41SPhilip Kirk case M_IOCTL: 640b127ac41SPhilip Kirk ipnet_ioctl(q, mp); 641b127ac41SPhilip Kirk break; 642b127ac41SPhilip Kirk case M_IOCDATA: 643b127ac41SPhilip Kirk ipnet_iocdata(q, mp); 644b127ac41SPhilip Kirk break; 645b127ac41SPhilip Kirk default: 646b127ac41SPhilip Kirk freemsg(mp); 647b127ac41SPhilip Kirk break; 648b127ac41SPhilip Kirk } 649b127ac41SPhilip Kirk return (0); 650b127ac41SPhilip Kirk } 651b127ac41SPhilip Kirk 652b127ac41SPhilip Kirk static int 653b127ac41SPhilip Kirk ipnet_rsrv(queue_t *q) 654b127ac41SPhilip Kirk { 655b127ac41SPhilip Kirk mblk_t *mp; 656b127ac41SPhilip Kirk 657b127ac41SPhilip Kirk while ((mp = getq(q)) != NULL) { 658b127ac41SPhilip Kirk ASSERT(DB_TYPE(mp) == M_DATA); 659b127ac41SPhilip Kirk if (canputnext(q)) { 660b127ac41SPhilip Kirk putnext(q, mp); 661b127ac41SPhilip Kirk } else { 662b127ac41SPhilip Kirk (void) putbq(q, mp); 663b127ac41SPhilip Kirk break; 664b127ac41SPhilip Kirk } 665b127ac41SPhilip Kirk } 666b127ac41SPhilip Kirk return (0); 667b127ac41SPhilip Kirk } 668b127ac41SPhilip Kirk 669b127ac41SPhilip Kirk static void 670b127ac41SPhilip Kirk ipnet_ioctl(queue_t *q, mblk_t *mp) 671b127ac41SPhilip Kirk { 672b127ac41SPhilip Kirk struct iocblk *iocp = (struct iocblk *)mp->b_rptr; 673b127ac41SPhilip Kirk 674b127ac41SPhilip Kirk switch (iocp->ioc_cmd) { 675b127ac41SPhilip Kirk case DLIOCRAW: 676b127ac41SPhilip Kirk miocack(q, mp, 0, 0); 677b127ac41SPhilip Kirk break; 678b127ac41SPhilip Kirk case DLIOCIPNETINFO: 679b127ac41SPhilip Kirk if (iocp->ioc_count == TRANSPARENT) { 680b127ac41SPhilip Kirk mcopyin(mp, NULL, sizeof (uint_t), NULL); 681b127ac41SPhilip Kirk qreply(q, mp); 682b127ac41SPhilip Kirk break; 683b127ac41SPhilip Kirk } 684b127ac41SPhilip Kirk /* Fallthrough, we don't support I_STR with DLIOCIPNETINFO. */ 685b127ac41SPhilip Kirk default: 686b127ac41SPhilip Kirk miocnak(q, mp, 0, EINVAL); 687b127ac41SPhilip Kirk break; 688b127ac41SPhilip Kirk } 689b127ac41SPhilip Kirk } 690b127ac41SPhilip Kirk 691b127ac41SPhilip Kirk static void 692b127ac41SPhilip Kirk ipnet_iocdata(queue_t *q, mblk_t *mp) 693b127ac41SPhilip Kirk { 694b127ac41SPhilip Kirk struct iocblk *iocp = (struct iocblk *)mp->b_rptr; 695b127ac41SPhilip Kirk ipnet_t *ipnet = q->q_ptr; 696b127ac41SPhilip Kirk 697b127ac41SPhilip Kirk switch (iocp->ioc_cmd) { 698b127ac41SPhilip Kirk case DLIOCIPNETINFO: 699b127ac41SPhilip Kirk if (*(int *)mp->b_cont->b_rptr == 1) 700b127ac41SPhilip Kirk ipnet->ipnet_flags |= IPNET_INFO; 701b127ac41SPhilip Kirk else if (*(int *)mp->b_cont->b_rptr == 0) 702b127ac41SPhilip Kirk ipnet->ipnet_flags &= ~IPNET_INFO; 703b127ac41SPhilip Kirk else 704b127ac41SPhilip Kirk goto iocnak; 705b127ac41SPhilip Kirk miocack(q, mp, 0, DL_IPNETINFO_VERSION); 706b127ac41SPhilip Kirk break; 707b127ac41SPhilip Kirk default: 708b127ac41SPhilip Kirk iocnak: 709b127ac41SPhilip Kirk miocnak(q, mp, 0, EINVAL); 710b127ac41SPhilip Kirk break; 711b127ac41SPhilip Kirk } 712b127ac41SPhilip Kirk } 713b127ac41SPhilip Kirk 714b127ac41SPhilip Kirk static void 715b127ac41SPhilip Kirk ipnet_wputnondata(queue_t *q, mblk_t *mp) 716b127ac41SPhilip Kirk { 717b127ac41SPhilip Kirk union DL_primitives *dlp = (union DL_primitives *)mp->b_rptr; 718b127ac41SPhilip Kirk t_uscalar_t prim = dlp->dl_primitive; 719b127ac41SPhilip Kirk 720b127ac41SPhilip Kirk switch (prim) { 721b127ac41SPhilip Kirk case DL_INFO_REQ: 722b127ac41SPhilip Kirk ipnet_inforeq(q, mp); 723b127ac41SPhilip Kirk break; 724b127ac41SPhilip Kirk case DL_UNBIND_REQ: 725b127ac41SPhilip Kirk ipnet_unbindreq(q, mp); 726b127ac41SPhilip Kirk break; 727b127ac41SPhilip Kirk case DL_BIND_REQ: 728b127ac41SPhilip Kirk ipnet_bindreq(q, mp); 729b127ac41SPhilip Kirk break; 730b127ac41SPhilip Kirk case DL_PROMISCON_REQ: 731b127ac41SPhilip Kirk ipnet_dlpromisconreq(q, mp); 732b127ac41SPhilip Kirk break; 733b127ac41SPhilip Kirk case DL_PROMISCOFF_REQ: 734b127ac41SPhilip Kirk ipnet_dlpromiscoffreq(q, mp); 735b127ac41SPhilip Kirk break; 736b127ac41SPhilip Kirk case DL_UNITDATA_REQ: 737b127ac41SPhilip Kirk case DL_DETACH_REQ: 738b127ac41SPhilip Kirk case DL_PHYS_ADDR_REQ: 739b127ac41SPhilip Kirk case DL_SET_PHYS_ADDR_REQ: 740b127ac41SPhilip Kirk case DL_ENABMULTI_REQ: 741b127ac41SPhilip Kirk case DL_DISABMULTI_REQ: 742b127ac41SPhilip Kirk case DL_ATTACH_REQ: 743b127ac41SPhilip Kirk dlerrorack(q, mp, prim, DL_UNSUPPORTED, 0); 744b127ac41SPhilip Kirk break; 745b127ac41SPhilip Kirk default: 746b127ac41SPhilip Kirk dlerrorack(q, mp, prim, DL_BADPRIM, 0); 747b127ac41SPhilip Kirk break; 748b127ac41SPhilip Kirk } 749b127ac41SPhilip Kirk } 750b127ac41SPhilip Kirk 751b127ac41SPhilip Kirk static void 752b127ac41SPhilip Kirk ipnet_inforeq(queue_t *q, mblk_t *mp) 753b127ac41SPhilip Kirk { 754b127ac41SPhilip Kirk dl_info_ack_t *dlip; 755b127ac41SPhilip Kirk size_t size = sizeof (dl_info_ack_t) + sizeof (ushort_t); 756b127ac41SPhilip Kirk 757b127ac41SPhilip Kirk if (MBLKL(mp) < DL_INFO_REQ_SIZE) { 758b127ac41SPhilip Kirk dlerrorack(q, mp, DL_INFO_REQ, DL_BADPRIM, 0); 759b127ac41SPhilip Kirk return; 760b127ac41SPhilip Kirk } 761b127ac41SPhilip Kirk 762b127ac41SPhilip Kirk if ((mp = mexchange(q, mp, size, M_PCPROTO, DL_INFO_ACK)) == NULL) 763b127ac41SPhilip Kirk return; 764b127ac41SPhilip Kirk 765b127ac41SPhilip Kirk dlip = (dl_info_ack_t *)mp->b_rptr; 766b127ac41SPhilip Kirk *dlip = ipnet_infoack; 767b127ac41SPhilip Kirk qreply(q, mp); 768b127ac41SPhilip Kirk } 769b127ac41SPhilip Kirk 770b127ac41SPhilip Kirk static void 771b127ac41SPhilip Kirk ipnet_bindreq(queue_t *q, mblk_t *mp) 772b127ac41SPhilip Kirk { 773b127ac41SPhilip Kirk union DL_primitives *dlp = (union DL_primitives *)mp->b_rptr; 774b127ac41SPhilip Kirk ipnet_t *ipnet = q->q_ptr; 775b127ac41SPhilip Kirk 776b127ac41SPhilip Kirk if (MBLKL(mp) < DL_BIND_REQ_SIZE) { 777b127ac41SPhilip Kirk dlerrorack(q, mp, DL_BIND_REQ, DL_BADPRIM, 0); 778b127ac41SPhilip Kirk return; 779b127ac41SPhilip Kirk } 780b127ac41SPhilip Kirk 7810a0e9771SDarren Reed switch (dlp->bind_req.dl_sap) { 7820a0e9771SDarren Reed case 0 : 7830a0e9771SDarren Reed ipnet->ipnet_family = AF_UNSPEC; 7840a0e9771SDarren Reed break; 7850a0e9771SDarren Reed case IPV4_VERSION : 7860a0e9771SDarren Reed ipnet->ipnet_family = AF_INET; 7870a0e9771SDarren Reed break; 7880a0e9771SDarren Reed case IPV6_VERSION : 7890a0e9771SDarren Reed ipnet->ipnet_family = AF_INET6; 7900a0e9771SDarren Reed break; 7910a0e9771SDarren Reed default : 792b127ac41SPhilip Kirk dlerrorack(q, mp, DL_BIND_REQ, DL_BADSAP, 0); 7930a0e9771SDarren Reed return; 7940a0e9771SDarren Reed /*NOTREACHED*/ 795b127ac41SPhilip Kirk } 7960a0e9771SDarren Reed 7970a0e9771SDarren Reed ipnet->ipnet_dlstate = DL_IDLE; 7980a0e9771SDarren Reed dlbindack(q, mp, dlp->bind_req.dl_sap, 0, 0, 0, 0); 799b127ac41SPhilip Kirk } 800b127ac41SPhilip Kirk 801b127ac41SPhilip Kirk static void 802b127ac41SPhilip Kirk ipnet_unbindreq(queue_t *q, mblk_t *mp) 803b127ac41SPhilip Kirk { 804b127ac41SPhilip Kirk ipnet_t *ipnet = q->q_ptr; 805b127ac41SPhilip Kirk 806b127ac41SPhilip Kirk if (MBLKL(mp) < DL_UNBIND_REQ_SIZE) { 807b127ac41SPhilip Kirk dlerrorack(q, mp, DL_UNBIND_REQ, DL_BADPRIM, 0); 808b127ac41SPhilip Kirk return; 809b127ac41SPhilip Kirk } 810b127ac41SPhilip Kirk 811b127ac41SPhilip Kirk if (ipnet->ipnet_dlstate != DL_IDLE) { 812b127ac41SPhilip Kirk dlerrorack(q, mp, DL_UNBIND_REQ, DL_OUTSTATE, 0); 813b127ac41SPhilip Kirk } else { 814b127ac41SPhilip Kirk ipnet->ipnet_dlstate = DL_UNBOUND; 8150a0e9771SDarren Reed ipnet->ipnet_family = AF_UNSPEC; 816b127ac41SPhilip Kirk dlokack(q, mp, DL_UNBIND_REQ); 817b127ac41SPhilip Kirk } 818b127ac41SPhilip Kirk } 819b127ac41SPhilip Kirk 820b127ac41SPhilip Kirk static void 821b127ac41SPhilip Kirk ipnet_dlpromisconreq(queue_t *q, mblk_t *mp) 822b127ac41SPhilip Kirk { 823b127ac41SPhilip Kirk ipnet_t *ipnet = q->q_ptr; 824b127ac41SPhilip Kirk t_uscalar_t level; 825b127ac41SPhilip Kirk int err; 826b127ac41SPhilip Kirk 827b127ac41SPhilip Kirk if (MBLKL(mp) < DL_PROMISCON_REQ_SIZE) { 828b127ac41SPhilip Kirk dlerrorack(q, mp, DL_PROMISCON_REQ, DL_BADPRIM, 0); 829b127ac41SPhilip Kirk return; 830b127ac41SPhilip Kirk } 831b127ac41SPhilip Kirk 832b127ac41SPhilip Kirk if (ipnet->ipnet_flags & IPNET_LOMODE) { 833b127ac41SPhilip Kirk dlokack(q, mp, DL_PROMISCON_REQ); 834b127ac41SPhilip Kirk return; 835b127ac41SPhilip Kirk } 836b127ac41SPhilip Kirk 837b127ac41SPhilip Kirk level = ((dl_promiscon_req_t *)mp->b_rptr)->dl_level; 838b127ac41SPhilip Kirk if (level == DL_PROMISC_PHYS || level == DL_PROMISC_MULTI) { 839b127ac41SPhilip Kirk if ((err = ipnet_join_allmulti(ipnet->ipnet_if, 840b127ac41SPhilip Kirk ipnet->ipnet_ns->netstack_ipnet)) != 0) { 841b127ac41SPhilip Kirk dlerrorack(q, mp, DL_PROMISCON_REQ, DL_SYSERR, err); 842b127ac41SPhilip Kirk return; 843b127ac41SPhilip Kirk } 844b127ac41SPhilip Kirk } 845b127ac41SPhilip Kirk 846b127ac41SPhilip Kirk switch (level) { 847b127ac41SPhilip Kirk case DL_PROMISC_PHYS: 848b127ac41SPhilip Kirk ipnet->ipnet_flags |= IPNET_PROMISC_PHYS; 849b127ac41SPhilip Kirk break; 850b127ac41SPhilip Kirk case DL_PROMISC_SAP: 851b127ac41SPhilip Kirk ipnet->ipnet_flags |= IPNET_PROMISC_SAP; 852b127ac41SPhilip Kirk break; 853b127ac41SPhilip Kirk case DL_PROMISC_MULTI: 854b127ac41SPhilip Kirk ipnet->ipnet_flags |= IPNET_PROMISC_MULTI; 855b127ac41SPhilip Kirk break; 856b127ac41SPhilip Kirk default: 857b127ac41SPhilip Kirk dlerrorack(q, mp, DL_PROMISCON_REQ, DL_BADPRIM, 0); 858b127ac41SPhilip Kirk return; 859b127ac41SPhilip Kirk } 860b127ac41SPhilip Kirk 861b127ac41SPhilip Kirk dlokack(q, mp, DL_PROMISCON_REQ); 862b127ac41SPhilip Kirk } 863b127ac41SPhilip Kirk 864b127ac41SPhilip Kirk static void 865b127ac41SPhilip Kirk ipnet_dlpromiscoffreq(queue_t *q, mblk_t *mp) 866b127ac41SPhilip Kirk { 867b127ac41SPhilip Kirk ipnet_t *ipnet = q->q_ptr; 868b127ac41SPhilip Kirk t_uscalar_t level; 869b127ac41SPhilip Kirk uint16_t orig_ipnet_flags = ipnet->ipnet_flags; 870b127ac41SPhilip Kirk 871b127ac41SPhilip Kirk if (MBLKL(mp) < DL_PROMISCOFF_REQ_SIZE) { 872b127ac41SPhilip Kirk dlerrorack(q, mp, DL_PROMISCOFF_REQ, DL_BADPRIM, 0); 873b127ac41SPhilip Kirk return; 874b127ac41SPhilip Kirk } 875b127ac41SPhilip Kirk 876b127ac41SPhilip Kirk if (ipnet->ipnet_flags & IPNET_LOMODE) { 877b127ac41SPhilip Kirk dlokack(q, mp, DL_PROMISCOFF_REQ); 878b127ac41SPhilip Kirk return; 879b127ac41SPhilip Kirk } 880b127ac41SPhilip Kirk 881b127ac41SPhilip Kirk level = ((dl_promiscon_req_t *)mp->b_rptr)->dl_level; 882b127ac41SPhilip Kirk switch (level) { 883b127ac41SPhilip Kirk case DL_PROMISC_PHYS: 884b127ac41SPhilip Kirk if (ipnet->ipnet_flags & IPNET_PROMISC_PHYS) 885b127ac41SPhilip Kirk ipnet->ipnet_flags &= ~IPNET_PROMISC_PHYS; 886b127ac41SPhilip Kirk break; 887b127ac41SPhilip Kirk case DL_PROMISC_SAP: 888b127ac41SPhilip Kirk if (ipnet->ipnet_flags & IPNET_PROMISC_SAP) 889b127ac41SPhilip Kirk ipnet->ipnet_flags &= ~IPNET_PROMISC_SAP; 890b127ac41SPhilip Kirk break; 891b127ac41SPhilip Kirk case DL_PROMISC_MULTI: 892b127ac41SPhilip Kirk if (ipnet->ipnet_flags & IPNET_PROMISC_MULTI) 893b127ac41SPhilip Kirk ipnet->ipnet_flags &= ~IPNET_PROMISC_MULTI; 894b127ac41SPhilip Kirk break; 895b127ac41SPhilip Kirk default: 896b127ac41SPhilip Kirk dlerrorack(q, mp, DL_PROMISCOFF_REQ, DL_BADPRIM, 0); 897b127ac41SPhilip Kirk return; 898b127ac41SPhilip Kirk } 899b127ac41SPhilip Kirk 900b127ac41SPhilip Kirk if (orig_ipnet_flags == ipnet->ipnet_flags) { 901b127ac41SPhilip Kirk dlerrorack(q, mp, DL_PROMISCOFF_REQ, DL_NOTENAB, 0); 902b127ac41SPhilip Kirk return; 903b127ac41SPhilip Kirk } 904b127ac41SPhilip Kirk 905b127ac41SPhilip Kirk if (level == DL_PROMISC_PHYS || level == DL_PROMISC_MULTI) { 906b127ac41SPhilip Kirk ipnet_leave_allmulti(ipnet->ipnet_if, 907b127ac41SPhilip Kirk ipnet->ipnet_ns->netstack_ipnet); 908b127ac41SPhilip Kirk } 909b127ac41SPhilip Kirk 910b127ac41SPhilip Kirk dlokack(q, mp, DL_PROMISCOFF_REQ); 911b127ac41SPhilip Kirk } 912b127ac41SPhilip Kirk 913b127ac41SPhilip Kirk static int 914b127ac41SPhilip Kirk ipnet_join_allmulti(ipnetif_t *ipnetif, ipnet_stack_t *ips) 915b127ac41SPhilip Kirk { 916b127ac41SPhilip Kirk int err = 0; 917b127ac41SPhilip Kirk ip_stack_t *ipst = ips->ips_netstack->netstack_ip; 918b127ac41SPhilip Kirk uint64_t index = ipnetif->if_index; 919b127ac41SPhilip Kirk 920b127ac41SPhilip Kirk mutex_enter(&ips->ips_event_lock); 921b127ac41SPhilip Kirk if (ipnetif->if_multicnt == 0) { 922b127ac41SPhilip Kirk ASSERT((ipnetif->if_flags & 923b127ac41SPhilip Kirk (IPNETIF_IPV4ALLMULTI | IPNETIF_IPV6ALLMULTI)) == 0); 924b127ac41SPhilip Kirk if (ipnetif->if_flags & IPNETIF_IPV4PLUMBED) { 925b127ac41SPhilip Kirk err = ip_join_allmulti(index, B_FALSE, ipst); 926b127ac41SPhilip Kirk if (err != 0) 927b127ac41SPhilip Kirk goto done; 928b127ac41SPhilip Kirk ipnetif->if_flags |= IPNETIF_IPV4ALLMULTI; 929b127ac41SPhilip Kirk } 930b127ac41SPhilip Kirk if (ipnetif->if_flags & IPNETIF_IPV6PLUMBED) { 931b127ac41SPhilip Kirk err = ip_join_allmulti(index, B_TRUE, ipst); 932b127ac41SPhilip Kirk if (err != 0 && 933b127ac41SPhilip Kirk (ipnetif->if_flags & IPNETIF_IPV4ALLMULTI)) { 934b127ac41SPhilip Kirk (void) ip_leave_allmulti(index, B_FALSE, ipst); 935b127ac41SPhilip Kirk ipnetif->if_flags &= ~IPNETIF_IPV4ALLMULTI; 936b127ac41SPhilip Kirk goto done; 937b127ac41SPhilip Kirk } 938b127ac41SPhilip Kirk ipnetif->if_flags |= IPNETIF_IPV6ALLMULTI; 939b127ac41SPhilip Kirk } 940b127ac41SPhilip Kirk } 941b127ac41SPhilip Kirk ipnetif->if_multicnt++; 942b127ac41SPhilip Kirk 943b127ac41SPhilip Kirk done: 944b127ac41SPhilip Kirk mutex_exit(&ips->ips_event_lock); 945b127ac41SPhilip Kirk return (err); 946b127ac41SPhilip Kirk } 947b127ac41SPhilip Kirk 948b127ac41SPhilip Kirk static void 949b127ac41SPhilip Kirk ipnet_leave_allmulti(ipnetif_t *ipnetif, ipnet_stack_t *ips) 950b127ac41SPhilip Kirk { 951b127ac41SPhilip Kirk int err; 952b127ac41SPhilip Kirk ip_stack_t *ipst = ips->ips_netstack->netstack_ip; 953b127ac41SPhilip Kirk uint64_t index = ipnetif->if_index; 954b127ac41SPhilip Kirk 955b127ac41SPhilip Kirk mutex_enter(&ips->ips_event_lock); 956b127ac41SPhilip Kirk ASSERT(ipnetif->if_multicnt != 0); 957b127ac41SPhilip Kirk if (--ipnetif->if_multicnt == 0) { 958b127ac41SPhilip Kirk if (ipnetif->if_flags & IPNETIF_IPV4ALLMULTI) { 959b127ac41SPhilip Kirk err = ip_leave_allmulti(index, B_FALSE, ipst); 960b127ac41SPhilip Kirk ASSERT(err == 0 || err == ENODEV); 961b127ac41SPhilip Kirk ipnetif->if_flags &= ~IPNETIF_IPV4ALLMULTI; 962b127ac41SPhilip Kirk } 963b127ac41SPhilip Kirk if (ipnetif->if_flags & IPNETIF_IPV6ALLMULTI) { 964b127ac41SPhilip Kirk err = ip_leave_allmulti(index, B_TRUE, ipst); 965b127ac41SPhilip Kirk ASSERT(err == 0 || err == ENODEV); 966b127ac41SPhilip Kirk ipnetif->if_flags &= ~IPNETIF_IPV6ALLMULTI; 967b127ac41SPhilip Kirk } 968b127ac41SPhilip Kirk } 969b127ac41SPhilip Kirk mutex_exit(&ips->ips_event_lock); 970b127ac41SPhilip Kirk } 971b127ac41SPhilip Kirk 9720a0e9771SDarren Reed /* 9730a0e9771SDarren Reed * Allocate a new mblk_t and put a dl_ipnetinfo_t in it. 9740a0e9771SDarren Reed * The structure it copies the header information from, 9750a0e9771SDarren Reed * hook_pkt_observe_t, is constructed using network byte 9760a0e9771SDarren Reed * order in ipobs_hook(), so there is no conversion here. 9770a0e9771SDarren Reed */ 978b127ac41SPhilip Kirk static mblk_t * 9790a0e9771SDarren Reed ipnet_addheader(hook_pkt_observe_t *hdr, mblk_t *mp) 980b127ac41SPhilip Kirk { 981b127ac41SPhilip Kirk mblk_t *dlhdr; 982b127ac41SPhilip Kirk dl_ipnetinfo_t *dl; 983b127ac41SPhilip Kirk 984b127ac41SPhilip Kirk if ((dlhdr = allocb(sizeof (dl_ipnetinfo_t), BPRI_HI)) == NULL) { 985b127ac41SPhilip Kirk freemsg(mp); 986b127ac41SPhilip Kirk return (NULL); 987b127ac41SPhilip Kirk } 988b127ac41SPhilip Kirk dl = (dl_ipnetinfo_t *)dlhdr->b_rptr; 989b127ac41SPhilip Kirk dl->dli_version = DL_IPNETINFO_VERSION; 9900a0e9771SDarren Reed dl->dli_family = hdr->hpo_family; 9910a0e9771SDarren Reed dl->dli_htype = hdr->hpo_htype; 9920a0e9771SDarren Reed dl->dli_pktlen = hdr->hpo_pktlen; 9930a0e9771SDarren Reed dl->dli_ifindex = hdr->hpo_ifindex; 9940a0e9771SDarren Reed dl->dli_grifindex = hdr->hpo_grifindex; 9950a0e9771SDarren Reed dl->dli_zsrc = hdr->hpo_zsrc; 9960a0e9771SDarren Reed dl->dli_zdst = hdr->hpo_zdst; 997b127ac41SPhilip Kirk dlhdr->b_wptr += sizeof (*dl); 998b127ac41SPhilip Kirk dlhdr->b_cont = mp; 999b127ac41SPhilip Kirk 1000b127ac41SPhilip Kirk return (dlhdr); 1001b127ac41SPhilip Kirk } 1002b127ac41SPhilip Kirk 1003b127ac41SPhilip Kirk static ipnet_addrtype_t 1004b127ac41SPhilip Kirk ipnet_get_addrtype(ipnet_t *ipnet, ipnet_addrp_t *addr) 1005b127ac41SPhilip Kirk { 1006b127ac41SPhilip Kirk list_t *list; 1007b127ac41SPhilip Kirk ipnetif_t *ipnetif = ipnet->ipnet_if; 1008b127ac41SPhilip Kirk ipnetif_addr_t *ifaddr; 1009b127ac41SPhilip Kirk ipnet_addrtype_t addrtype = IPNETADDR_UNKNOWN; 1010b127ac41SPhilip Kirk 1011b127ac41SPhilip Kirk /* First check if the address is multicast or limited broadcast. */ 1012b127ac41SPhilip Kirk switch (addr->iap_family) { 1013b127ac41SPhilip Kirk case AF_INET: 1014b127ac41SPhilip Kirk if (CLASSD(*(addr->iap_addr4)) || 1015b127ac41SPhilip Kirk *(addr->iap_addr4) == INADDR_BROADCAST) 1016b127ac41SPhilip Kirk return (IPNETADDR_MBCAST); 1017b127ac41SPhilip Kirk break; 1018b127ac41SPhilip Kirk case AF_INET6: 1019b127ac41SPhilip Kirk if (IN6_IS_ADDR_MULTICAST(addr->iap_addr6)) 1020b127ac41SPhilip Kirk return (IPNETADDR_MBCAST); 1021b127ac41SPhilip Kirk break; 1022b127ac41SPhilip Kirk } 1023b127ac41SPhilip Kirk 1024b127ac41SPhilip Kirk /* 1025b127ac41SPhilip Kirk * Walk the address list to see if the address belongs to our 1026b127ac41SPhilip Kirk * interface or is one of our subnet broadcast addresses. 1027b127ac41SPhilip Kirk */ 1028b127ac41SPhilip Kirk mutex_enter(&ipnetif->if_addr_lock); 1029b127ac41SPhilip Kirk list = (addr->iap_family == AF_INET) ? 1030b127ac41SPhilip Kirk &ipnetif->if_ip4addr_list : &ipnetif->if_ip6addr_list; 1031b127ac41SPhilip Kirk for (ifaddr = list_head(list); 1032b127ac41SPhilip Kirk ifaddr != NULL && addrtype == IPNETADDR_UNKNOWN; 1033b127ac41SPhilip Kirk ifaddr = list_next(list, ifaddr)) { 1034b127ac41SPhilip Kirk /* 1035b127ac41SPhilip Kirk * If we're not in the global zone, then only look at 1036b127ac41SPhilip Kirk * addresses in our zone. 1037b127ac41SPhilip Kirk */ 1038b127ac41SPhilip Kirk if (ipnet->ipnet_zoneid != GLOBAL_ZONEID && 1039b127ac41SPhilip Kirk ipnet->ipnet_zoneid != ifaddr->ifa_zone) 1040b127ac41SPhilip Kirk continue; 1041b127ac41SPhilip Kirk switch (addr->iap_family) { 1042b127ac41SPhilip Kirk case AF_INET: 1043b127ac41SPhilip Kirk if (ifaddr->ifa_ip4addr != INADDR_ANY && 1044b127ac41SPhilip Kirk *(addr->iap_addr4) == ifaddr->ifa_ip4addr) 1045b127ac41SPhilip Kirk addrtype = IPNETADDR_MYADDR; 1046b127ac41SPhilip Kirk else if (ifaddr->ifa_brdaddr != INADDR_ANY && 1047b127ac41SPhilip Kirk *(addr->iap_addr4) == ifaddr->ifa_brdaddr) 1048b127ac41SPhilip Kirk addrtype = IPNETADDR_MBCAST; 1049b127ac41SPhilip Kirk break; 1050b127ac41SPhilip Kirk case AF_INET6: 1051b127ac41SPhilip Kirk if (IN6_ARE_ADDR_EQUAL(addr->iap_addr6, 1052b127ac41SPhilip Kirk &ifaddr->ifa_ip6addr)) 1053b127ac41SPhilip Kirk addrtype = IPNETADDR_MYADDR; 1054b127ac41SPhilip Kirk break; 1055b127ac41SPhilip Kirk } 1056b127ac41SPhilip Kirk } 1057b127ac41SPhilip Kirk mutex_exit(&ipnetif->if_addr_lock); 1058b127ac41SPhilip Kirk 1059b127ac41SPhilip Kirk return (addrtype); 1060b127ac41SPhilip Kirk } 1061b127ac41SPhilip Kirk 1062b127ac41SPhilip Kirk /* 10630a0e9771SDarren Reed * Verify if the packet contained in hdr should be passed up to the 1064b127ac41SPhilip Kirk * ipnet client stream. 1065b127ac41SPhilip Kirk */ 1066b127ac41SPhilip Kirk static boolean_t 10670a0e9771SDarren Reed ipnet_accept(ipnet_t *ipnet, hook_pkt_observe_t *hdr, ipnet_addrp_t *src, 1068b127ac41SPhilip Kirk ipnet_addrp_t *dst) 1069b127ac41SPhilip Kirk { 1070e11c3f44Smeem boolean_t obsif; 1071b127ac41SPhilip Kirk uint64_t ifindex = ipnet->ipnet_if->if_index; 10720a0e9771SDarren Reed ipnet_addrtype_t srctype; 10730a0e9771SDarren Reed ipnet_addrtype_t dsttype; 1074b127ac41SPhilip Kirk 1075b127ac41SPhilip Kirk srctype = ipnet_get_addrtype(ipnet, src); 1076b127ac41SPhilip Kirk dsttype = ipnet_get_addrtype(ipnet, dst); 1077b127ac41SPhilip Kirk 1078b127ac41SPhilip Kirk /* 1079e11c3f44Smeem * If the packet's ifindex matches ours, or the packet's group ifindex 1080e11c3f44Smeem * matches ours, it's on the interface we're observing. (Thus, 1081e11c3f44Smeem * observing on the group ifindex matches all ifindexes in the group.) 1082e11c3f44Smeem */ 10830a0e9771SDarren Reed obsif = (ntohl(hdr->hpo_ifindex) == ifindex || 10840a0e9771SDarren Reed ntohl(hdr->hpo_grifindex) == ifindex); 10850a0e9771SDarren Reed 10860a0e9771SDarren Reed DTRACE_PROBE5(ipnet_accept__addr, 10870a0e9771SDarren Reed ipnet_addrtype_t, srctype, ipnet_addrp_t *, src, 10880a0e9771SDarren Reed ipnet_addrtype_t, dsttype, ipnet_addrp_t *, dst, 10890a0e9771SDarren Reed boolean_t, obsif); 1090e11c3f44Smeem 1091e11c3f44Smeem /* 1092b127ac41SPhilip Kirk * Do not allow an ipnet stream to see packets that are not from or to 1093b127ac41SPhilip Kirk * its zone. The exception is when zones are using the shared stack 1094b127ac41SPhilip Kirk * model. In this case, streams in the global zone have visibility 1095b127ac41SPhilip Kirk * into other shared-stack zones, and broadcast and multicast traffic 1096b127ac41SPhilip Kirk * is visible by all zones in the stack. 1097b127ac41SPhilip Kirk */ 1098b127ac41SPhilip Kirk if (ipnet->ipnet_zoneid != GLOBAL_ZONEID && 1099b127ac41SPhilip Kirk dsttype != IPNETADDR_MBCAST) { 11000a0e9771SDarren Reed if (ipnet->ipnet_zoneid != ntohl(hdr->hpo_zsrc) && 11010a0e9771SDarren Reed ipnet->ipnet_zoneid != ntohl(hdr->hpo_zdst)) 1102b127ac41SPhilip Kirk return (B_FALSE); 1103b127ac41SPhilip Kirk } 1104b127ac41SPhilip Kirk 1105b127ac41SPhilip Kirk /* 1106b127ac41SPhilip Kirk * If DL_PROMISC_SAP isn't enabled, then the bound SAP must match the 1107b127ac41SPhilip Kirk * packet's IP version. 1108b127ac41SPhilip Kirk */ 1109b127ac41SPhilip Kirk if (!(ipnet->ipnet_flags & IPNET_PROMISC_SAP) && 11100a0e9771SDarren Reed ipnet->ipnet_family != hdr->hpo_family) 1111b127ac41SPhilip Kirk return (B_FALSE); 1112b127ac41SPhilip Kirk 1113b127ac41SPhilip Kirk /* If the destination address is ours, then accept the packet. */ 1114b127ac41SPhilip Kirk if (dsttype == IPNETADDR_MYADDR) 1115b127ac41SPhilip Kirk return (B_TRUE); 1116b127ac41SPhilip Kirk 1117b127ac41SPhilip Kirk /* 1118b127ac41SPhilip Kirk * If DL_PROMISC_PHYS is enabled, then we can see all packets that are 1119b127ac41SPhilip Kirk * sent or received on the interface we're observing, or packets that 1120b127ac41SPhilip Kirk * have our source address (this allows us to see packets we send). 1121b127ac41SPhilip Kirk */ 1122b127ac41SPhilip Kirk if (ipnet->ipnet_flags & IPNET_PROMISC_PHYS) { 1123e11c3f44Smeem if (srctype == IPNETADDR_MYADDR || obsif) 1124b127ac41SPhilip Kirk return (B_TRUE); 1125b127ac41SPhilip Kirk } 1126b127ac41SPhilip Kirk 1127b127ac41SPhilip Kirk /* 1128b127ac41SPhilip Kirk * We accept multicast and broadcast packets transmitted or received 1129b127ac41SPhilip Kirk * on the interface we're observing. 1130b127ac41SPhilip Kirk */ 1131e11c3f44Smeem if (dsttype == IPNETADDR_MBCAST && obsif) 1132b127ac41SPhilip Kirk return (B_TRUE); 1133b127ac41SPhilip Kirk 1134b127ac41SPhilip Kirk return (B_FALSE); 1135b127ac41SPhilip Kirk } 1136b127ac41SPhilip Kirk 1137b127ac41SPhilip Kirk /* 11380a0e9771SDarren Reed * Verify if the packet contained in hdr should be passed up to the ipnet 1139b127ac41SPhilip Kirk * client stream that's in IPNET_LOMODE. 1140b127ac41SPhilip Kirk */ 1141b127ac41SPhilip Kirk /* ARGSUSED */ 1142b127ac41SPhilip Kirk static boolean_t 11430a0e9771SDarren Reed ipnet_loaccept(ipnet_t *ipnet, hook_pkt_observe_t *hdr, ipnet_addrp_t *src, 1144b127ac41SPhilip Kirk ipnet_addrp_t *dst) 1145b127ac41SPhilip Kirk { 1146fc5884fcSDarren Reed if (hdr->hpo_htype != htons(IPOBS_HOOK_LOCAL)) { 11470a0e9771SDarren Reed /* 11480a0e9771SDarren Reed * ipnet_if is only NULL for IPNET_MINOR_LO devices. 11490a0e9771SDarren Reed */ 11500a0e9771SDarren Reed if (ipnet->ipnet_if == NULL) 1151b127ac41SPhilip Kirk return (B_FALSE); 11520a0e9771SDarren Reed } 1153b127ac41SPhilip Kirk 1154b127ac41SPhilip Kirk /* 1155b127ac41SPhilip Kirk * An ipnet stream must not see packets that are not from/to its zone. 1156b127ac41SPhilip Kirk */ 1157b127ac41SPhilip Kirk if (ipnet->ipnet_zoneid != GLOBAL_ZONEID) { 11580a0e9771SDarren Reed if (ipnet->ipnet_zoneid != ntohl(hdr->hpo_zsrc) && 11590a0e9771SDarren Reed ipnet->ipnet_zoneid != ntohl(hdr->hpo_zdst)) 1160b127ac41SPhilip Kirk return (B_FALSE); 1161b127ac41SPhilip Kirk } 1162b127ac41SPhilip Kirk 11630a0e9771SDarren Reed return (ipnet->ipnet_family == AF_UNSPEC || 11640a0e9771SDarren Reed ipnet->ipnet_family == hdr->hpo_family); 1165b127ac41SPhilip Kirk } 1166b127ac41SPhilip Kirk 1167b127ac41SPhilip Kirk static void 1168b127ac41SPhilip Kirk ipnet_dispatch(void *arg) 1169b127ac41SPhilip Kirk { 1170b127ac41SPhilip Kirk mblk_t *mp = arg; 11710a0e9771SDarren Reed hook_pkt_observe_t *hdr = (hook_pkt_observe_t *)mp->b_rptr; 1172b127ac41SPhilip Kirk ipnet_t *ipnet; 1173b127ac41SPhilip Kirk mblk_t *netmp; 1174b127ac41SPhilip Kirk list_t *list; 11750a0e9771SDarren Reed ipnet_stack_t *ips; 11760a0e9771SDarren Reed ipnet_addrp_t src; 11770a0e9771SDarren Reed ipnet_addrp_t dst; 1178b127ac41SPhilip Kirk 11790a0e9771SDarren Reed ips = ((netstack_t *)hdr->hpo_ctx)->netstack_ipnet; 11800a0e9771SDarren Reed 11810a0e9771SDarren Reed netmp = hdr->hpo_pkt->b_cont; 11820a0e9771SDarren Reed src.iap_family = hdr->hpo_family; 11830a0e9771SDarren Reed dst.iap_family = hdr->hpo_family; 11840a0e9771SDarren Reed 11850a0e9771SDarren Reed if (hdr->hpo_family == AF_INET) { 11860a0e9771SDarren Reed src.iap_addr4 = &((ipha_t *)(netmp->b_rptr))->ipha_src; 11870a0e9771SDarren Reed dst.iap_addr4 = &((ipha_t *)(netmp->b_rptr))->ipha_dst; 1188b127ac41SPhilip Kirk } else { 11890a0e9771SDarren Reed src.iap_addr6 = &((ip6_t *)(netmp->b_rptr))->ip6_src; 11900a0e9771SDarren Reed dst.iap_addr6 = &((ip6_t *)(netmp->b_rptr))->ip6_dst; 1191b127ac41SPhilip Kirk } 1192b127ac41SPhilip Kirk 1193b127ac41SPhilip Kirk ipnet_walkers_inc(ips); 1194b127ac41SPhilip Kirk 1195b127ac41SPhilip Kirk list = &ips->ips_str_list; 1196b127ac41SPhilip Kirk for (ipnet = list_head(list); ipnet != NULL; 1197b127ac41SPhilip Kirk ipnet = list_next(list, ipnet)) { 11980a0e9771SDarren Reed if (!(*ipnet->ipnet_acceptfn)(ipnet, hdr, &src, &dst)) { 11990a0e9771SDarren Reed IPSK_BUMP(ips, ik_acceptFail); 1200b127ac41SPhilip Kirk continue; 12010a0e9771SDarren Reed } 12020a0e9771SDarren Reed IPSK_BUMP(ips, ik_acceptOk); 1203b127ac41SPhilip Kirk 1204b127ac41SPhilip Kirk if (list_next(list, ipnet) == NULL) { 12050a0e9771SDarren Reed netmp = hdr->hpo_pkt->b_cont; 12060a0e9771SDarren Reed hdr->hpo_pkt->b_cont = NULL; 1207b127ac41SPhilip Kirk } else { 12080a0e9771SDarren Reed if ((netmp = dupmsg(hdr->hpo_pkt->b_cont)) == NULL && 12090a0e9771SDarren Reed (netmp = copymsg(hdr->hpo_pkt->b_cont)) == NULL) { 12100a0e9771SDarren Reed IPSK_BUMP(ips, ik_duplicationFail); 1211b127ac41SPhilip Kirk continue; 1212b127ac41SPhilip Kirk } 1213b127ac41SPhilip Kirk } 1214b127ac41SPhilip Kirk 1215b127ac41SPhilip Kirk if (ipnet->ipnet_flags & IPNET_INFO) { 12160a0e9771SDarren Reed if ((netmp = ipnet_addheader(hdr, netmp)) == NULL) { 12170a0e9771SDarren Reed IPSK_BUMP(ips, ik_dispatchHeaderDrop); 1218b127ac41SPhilip Kirk continue; 1219b127ac41SPhilip Kirk } 1220b127ac41SPhilip Kirk } 1221b127ac41SPhilip Kirk 1222b127ac41SPhilip Kirk if (ipnet->ipnet_rq->q_first == NULL && 1223b127ac41SPhilip Kirk canputnext(ipnet->ipnet_rq)) { 1224b127ac41SPhilip Kirk putnext(ipnet->ipnet_rq, netmp); 12250a0e9771SDarren Reed IPSK_BUMP(ips, ik_dispatchDeliver); 1226b127ac41SPhilip Kirk } else if (canput(ipnet->ipnet_rq)) { 1227b127ac41SPhilip Kirk (void) putq(ipnet->ipnet_rq, netmp); 12280a0e9771SDarren Reed IPSK_BUMP(ips, ik_dispatchDeliver); 1229b127ac41SPhilip Kirk } else { 1230b127ac41SPhilip Kirk freemsg(netmp); 12310a0e9771SDarren Reed IPSK_BUMP(ips, ik_dispatchPutDrop); 1232b127ac41SPhilip Kirk } 1233b127ac41SPhilip Kirk } 1234b127ac41SPhilip Kirk 1235b127ac41SPhilip Kirk ipnet_walkers_dec(ips); 1236b127ac41SPhilip Kirk 1237b127ac41SPhilip Kirk freemsg(mp); 1238b127ac41SPhilip Kirk } 1239b127ac41SPhilip Kirk 1240b127ac41SPhilip Kirk static void 1241b127ac41SPhilip Kirk ipnet_input(mblk_t *mp) 1242b127ac41SPhilip Kirk { 12430a0e9771SDarren Reed hook_pkt_observe_t *hdr = (hook_pkt_observe_t *)mp->b_rptr; 12440a0e9771SDarren Reed ipnet_stack_t *ips; 12450a0e9771SDarren Reed 12460a0e9771SDarren Reed ips = ((netstack_t *)hdr->hpo_ctx)->netstack_ipnet; 1247b127ac41SPhilip Kirk 1248b127ac41SPhilip Kirk if (ddi_taskq_dispatch(ipnet_taskq, ipnet_dispatch, mp, DDI_NOSLEEP) != 1249b127ac41SPhilip Kirk DDI_SUCCESS) { 12500a0e9771SDarren Reed IPSK_BUMP(ips, ik_dispatchFail); 1251b127ac41SPhilip Kirk freemsg(mp); 12520a0e9771SDarren Reed } else { 12530a0e9771SDarren Reed IPSK_BUMP(ips, ik_dispatchOk); 1254b127ac41SPhilip Kirk } 1255b127ac41SPhilip Kirk } 1256b127ac41SPhilip Kirk 12570a0e9771SDarren Reed static ipnetif_t * 12580a0e9771SDarren Reed ipnet_alloc_if(ipnet_stack_t *ips) 12590a0e9771SDarren Reed { 12600a0e9771SDarren Reed ipnetif_t *ipnetif; 12610a0e9771SDarren Reed 12620a0e9771SDarren Reed if ((ipnetif = kmem_zalloc(sizeof (*ipnetif), KM_NOSLEEP)) == NULL) 12630a0e9771SDarren Reed return (NULL); 12640a0e9771SDarren Reed 12650a0e9771SDarren Reed mutex_init(&ipnetif->if_addr_lock, NULL, MUTEX_DEFAULT, 0); 12660a0e9771SDarren Reed list_create(&ipnetif->if_ip4addr_list, sizeof (ipnetif_addr_t), 12670a0e9771SDarren Reed offsetof(ipnetif_addr_t, ifa_link)); 12680a0e9771SDarren Reed list_create(&ipnetif->if_ip6addr_list, sizeof (ipnetif_addr_t), 12690a0e9771SDarren Reed offsetof(ipnetif_addr_t, ifa_link)); 12700a0e9771SDarren Reed mutex_init(&ipnetif->if_reflock, NULL, MUTEX_DEFAULT, 0); 12710a0e9771SDarren Reed 12720a0e9771SDarren Reed ipnetif->if_stackp = ips; 12730a0e9771SDarren Reed 12740a0e9771SDarren Reed return (ipnetif); 12750a0e9771SDarren Reed } 12760a0e9771SDarren Reed 1277b127ac41SPhilip Kirk /* 1278b127ac41SPhilip Kirk * Create a new ipnetif_t and new minor node for it. If creation is 1279b127ac41SPhilip Kirk * successful the new ipnetif_t is inserted into an avl_tree 1280b127ac41SPhilip Kirk * containing ipnetif's for this stack instance. 1281b127ac41SPhilip Kirk */ 1282b127ac41SPhilip Kirk static ipnetif_t * 12830a0e9771SDarren Reed ipnetif_create(const char *name, uint64_t index, ipnet_stack_t *ips, 12840a0e9771SDarren Reed uint64_t ifflags) 1285b127ac41SPhilip Kirk { 1286b127ac41SPhilip Kirk ipnetif_t *ipnetif; 1287b127ac41SPhilip Kirk avl_index_t where = 0; 1288b127ac41SPhilip Kirk minor_t ifminor; 1289b127ac41SPhilip Kirk 1290b127ac41SPhilip Kirk /* 12910a0e9771SDarren Reed * Because ipnetif_create() can be called from a NIC event 1292b127ac41SPhilip Kirk * callback, it should not block. 1293b127ac41SPhilip Kirk */ 1294b127ac41SPhilip Kirk ifminor = (minor_t)id_alloc_nosleep(ipnet_minor_space); 1295b127ac41SPhilip Kirk if (ifminor == (minor_t)-1) 1296b127ac41SPhilip Kirk return (NULL); 12970a0e9771SDarren Reed if ((ipnetif = ipnet_alloc_if(ips)) == NULL) { 1298b127ac41SPhilip Kirk id_free(ipnet_minor_space, ifminor); 1299b127ac41SPhilip Kirk return (NULL); 1300b127ac41SPhilip Kirk } 1301b127ac41SPhilip Kirk 1302b127ac41SPhilip Kirk (void) strlcpy(ipnetif->if_name, name, LIFNAMSIZ); 13030a0e9771SDarren Reed ipnetif->if_index = (uint_t)index; 13040a0e9771SDarren Reed ipnetif->if_zoneid = netstack_get_zoneid(ips->ips_netstack); 1305b127ac41SPhilip Kirk ipnetif->if_dev = makedevice(ipnet_major, ifminor); 13060a0e9771SDarren Reed 1307b127ac41SPhilip Kirk ipnetif->if_refcnt = 1; 13080a0e9771SDarren Reed if ((ifflags & IFF_LOOPBACK) != 0) 13090a0e9771SDarren Reed ipnetif->if_flags = IPNETIF_LOOPBACK; 1310b127ac41SPhilip Kirk 1311b127ac41SPhilip Kirk mutex_enter(&ips->ips_avl_lock); 1312b127ac41SPhilip Kirk VERIFY(avl_find(&ips->ips_avl_by_index, &index, &where) == NULL); 1313b127ac41SPhilip Kirk avl_insert(&ips->ips_avl_by_index, ipnetif, where); 1314b127ac41SPhilip Kirk VERIFY(avl_find(&ips->ips_avl_by_name, (void *)name, &where) == NULL); 1315b127ac41SPhilip Kirk avl_insert(&ips->ips_avl_by_name, ipnetif, where); 1316b127ac41SPhilip Kirk mutex_exit(&ips->ips_avl_lock); 1317b127ac41SPhilip Kirk 1318b127ac41SPhilip Kirk return (ipnetif); 1319b127ac41SPhilip Kirk } 1320b127ac41SPhilip Kirk 1321b127ac41SPhilip Kirk static void 13220a0e9771SDarren Reed ipnetif_remove(ipnetif_t *ipnetif, ipnet_stack_t *ips) 1323b127ac41SPhilip Kirk { 1324b127ac41SPhilip Kirk ipnet_t *ipnet; 1325b127ac41SPhilip Kirk 1326b127ac41SPhilip Kirk ipnet_walkers_inc(ips); 1327b127ac41SPhilip Kirk /* Send a SIGHUP to all open streams associated with this ipnetif. */ 1328b127ac41SPhilip Kirk for (ipnet = list_head(&ips->ips_str_list); ipnet != NULL; 1329b127ac41SPhilip Kirk ipnet = list_next(&ips->ips_str_list, ipnet)) { 1330b127ac41SPhilip Kirk if (ipnet->ipnet_if == ipnetif) 1331b127ac41SPhilip Kirk (void) putnextctl(ipnet->ipnet_rq, M_HANGUP); 1332b127ac41SPhilip Kirk } 1333b127ac41SPhilip Kirk ipnet_walkers_dec(ips); 1334b127ac41SPhilip Kirk mutex_enter(&ips->ips_avl_lock); 1335b127ac41SPhilip Kirk avl_remove(&ips->ips_avl_by_index, ipnetif); 1336b127ac41SPhilip Kirk avl_remove(&ips->ips_avl_by_name, ipnetif); 1337b127ac41SPhilip Kirk mutex_exit(&ips->ips_avl_lock); 13380a0e9771SDarren Reed /* 13390a0e9771SDarren Reed * Release the reference we implicitly held in ipnetif_create(). 13400a0e9771SDarren Reed */ 1341b127ac41SPhilip Kirk ipnetif_refrele(ipnetif); 1342b127ac41SPhilip Kirk } 1343b127ac41SPhilip Kirk 1344b127ac41SPhilip Kirk static void 1345b127ac41SPhilip Kirk ipnet_purge_addrlist(list_t *addrlist) 1346b127ac41SPhilip Kirk { 1347b127ac41SPhilip Kirk ipnetif_addr_t *ifa; 1348b127ac41SPhilip Kirk 1349b127ac41SPhilip Kirk while ((ifa = list_head(addrlist)) != NULL) { 1350b127ac41SPhilip Kirk list_remove(addrlist, ifa); 13510a0e9771SDarren Reed if (ifa->ifa_shared != NULL) 13520a0e9771SDarren Reed ipnetif_clone_release(ifa->ifa_shared); 1353b127ac41SPhilip Kirk kmem_free(ifa, sizeof (*ifa)); 1354b127ac41SPhilip Kirk } 1355b127ac41SPhilip Kirk } 1356b127ac41SPhilip Kirk 1357b127ac41SPhilip Kirk static void 13580a0e9771SDarren Reed ipnetif_free(ipnetif_t *ipnetif) 1359b127ac41SPhilip Kirk { 1360b127ac41SPhilip Kirk ASSERT(ipnetif->if_refcnt == 0); 13610a0e9771SDarren Reed ASSERT(ipnetif->if_sharecnt == 0); 1362b127ac41SPhilip Kirk 1363b127ac41SPhilip Kirk /* Remove IPv4/v6 address lists from the ipnetif */ 1364b127ac41SPhilip Kirk ipnet_purge_addrlist(&ipnetif->if_ip4addr_list); 1365b127ac41SPhilip Kirk list_destroy(&ipnetif->if_ip4addr_list); 1366b127ac41SPhilip Kirk ipnet_purge_addrlist(&ipnetif->if_ip6addr_list); 1367b127ac41SPhilip Kirk list_destroy(&ipnetif->if_ip6addr_list); 1368b127ac41SPhilip Kirk mutex_destroy(&ipnetif->if_addr_lock); 1369b127ac41SPhilip Kirk mutex_destroy(&ipnetif->if_reflock); 13700a0e9771SDarren Reed if (ipnetif->if_dev != 0) 1371b127ac41SPhilip Kirk id_free(ipnet_minor_space, getminor(ipnetif->if_dev)); 1372b127ac41SPhilip Kirk kmem_free(ipnetif, sizeof (*ipnetif)); 1373b127ac41SPhilip Kirk } 1374b127ac41SPhilip Kirk 1375b127ac41SPhilip Kirk /* 1376b127ac41SPhilip Kirk * Create an ipnetif_addr_t with the given logical interface id (lif) 1377b127ac41SPhilip Kirk * and add it to the supplied ipnetif. The lif is the netinfo 1378b127ac41SPhilip Kirk * representation of logical interface id, and we use this id to match 1379b127ac41SPhilip Kirk * incoming netinfo events against our lists of addresses. 1380b127ac41SPhilip Kirk */ 1381b127ac41SPhilip Kirk static void 1382b127ac41SPhilip Kirk ipnet_add_ifaddr(uint64_t lif, ipnetif_t *ipnetif, net_handle_t nd) 1383b127ac41SPhilip Kirk { 1384b127ac41SPhilip Kirk ipnetif_addr_t *ifaddr; 1385b127ac41SPhilip Kirk zoneid_t zoneid; 1386b127ac41SPhilip Kirk struct sockaddr_in bcast; 1387b127ac41SPhilip Kirk struct sockaddr_storage addr; 1388b127ac41SPhilip Kirk net_ifaddr_t type = NA_ADDRESS; 1389b127ac41SPhilip Kirk uint64_t phyif = ipnetif->if_index; 1390b127ac41SPhilip Kirk 1391b127ac41SPhilip Kirk if (net_getlifaddr(nd, phyif, lif, 1, &type, &addr) != 0 || 1392b127ac41SPhilip Kirk net_getlifzone(nd, phyif, lif, &zoneid) != 0) 1393b127ac41SPhilip Kirk return; 13940a0e9771SDarren Reed 1395b127ac41SPhilip Kirk if ((ifaddr = kmem_alloc(sizeof (*ifaddr), KM_NOSLEEP)) == NULL) 1396b127ac41SPhilip Kirk return; 1397b127ac41SPhilip Kirk ifaddr->ifa_zone = zoneid; 1398b127ac41SPhilip Kirk ifaddr->ifa_id = lif; 13990a0e9771SDarren Reed ifaddr->ifa_shared = NULL; 1400b127ac41SPhilip Kirk 1401b127ac41SPhilip Kirk switch (addr.ss_family) { 1402b127ac41SPhilip Kirk case AF_INET: 1403b127ac41SPhilip Kirk ifaddr->ifa_ip4addr = 1404b127ac41SPhilip Kirk ((struct sockaddr_in *)&addr)->sin_addr.s_addr; 1405b127ac41SPhilip Kirk /* 1406b127ac41SPhilip Kirk * Try and get the broadcast address. Note that it's okay for 1407b127ac41SPhilip Kirk * an interface to not have a broadcast address, so we don't 1408b127ac41SPhilip Kirk * fail the entire operation if net_getlifaddr() fails here. 1409b127ac41SPhilip Kirk */ 1410b127ac41SPhilip Kirk type = NA_BROADCAST; 1411b127ac41SPhilip Kirk if (net_getlifaddr(nd, phyif, lif, 1, &type, &bcast) == 0) 1412b127ac41SPhilip Kirk ifaddr->ifa_brdaddr = bcast.sin_addr.s_addr; 1413b127ac41SPhilip Kirk break; 1414b127ac41SPhilip Kirk case AF_INET6: 1415b127ac41SPhilip Kirk ifaddr->ifa_ip6addr = ((struct sockaddr_in6 *)&addr)->sin6_addr; 1416b127ac41SPhilip Kirk break; 1417b127ac41SPhilip Kirk } 1418b127ac41SPhilip Kirk 14197b57f05aSDarren Reed /* 14207b57f05aSDarren Reed * The zoneid stored in ipnetif_t needs to correspond to the actual 14217b57f05aSDarren Reed * zone the address is being used in. This facilitates finding the 14227b57f05aSDarren Reed * correct netstack_t pointer, amongst other things, later. 14237b57f05aSDarren Reed */ 14247b57f05aSDarren Reed if (zoneid == ALL_ZONES) 14257b57f05aSDarren Reed zoneid = GLOBAL_ZONEID; 14267b57f05aSDarren Reed 1427b127ac41SPhilip Kirk mutex_enter(&ipnetif->if_addr_lock); 14280a0e9771SDarren Reed if (zoneid != ipnetif->if_zoneid) { 14290a0e9771SDarren Reed ipnetif_t *ifp2; 14300a0e9771SDarren Reed 14310a0e9771SDarren Reed ifp2 = ipnetif_clone_create(ipnetif, zoneid); 14320a0e9771SDarren Reed ifaddr->ifa_shared = ifp2; 14330a0e9771SDarren Reed } 1434b127ac41SPhilip Kirk list_insert_tail(addr.ss_family == AF_INET ? 1435b127ac41SPhilip Kirk &ipnetif->if_ip4addr_list : &ipnetif->if_ip6addr_list, ifaddr); 1436b127ac41SPhilip Kirk mutex_exit(&ipnetif->if_addr_lock); 1437b127ac41SPhilip Kirk } 1438b127ac41SPhilip Kirk 1439b127ac41SPhilip Kirk static void 1440b127ac41SPhilip Kirk ipnet_delete_ifaddr(ipnetif_addr_t *ifaddr, ipnetif_t *ipnetif, boolean_t isv6) 1441b127ac41SPhilip Kirk { 1442b127ac41SPhilip Kirk mutex_enter(&ipnetif->if_addr_lock); 14430a0e9771SDarren Reed if (ifaddr->ifa_shared != NULL) 14440a0e9771SDarren Reed ipnetif_clone_release(ifaddr->ifa_shared); 14450a0e9771SDarren Reed 1446b127ac41SPhilip Kirk list_remove(isv6 ? 1447b127ac41SPhilip Kirk &ipnetif->if_ip6addr_list : &ipnetif->if_ip4addr_list, ifaddr); 1448b127ac41SPhilip Kirk mutex_exit(&ipnetif->if_addr_lock); 1449b127ac41SPhilip Kirk kmem_free(ifaddr, sizeof (*ifaddr)); 1450b127ac41SPhilip Kirk } 1451b127ac41SPhilip Kirk 1452b127ac41SPhilip Kirk static void 14530a0e9771SDarren Reed ipnet_plumb_ev(ipnet_nicevent_t *ipne, ipnet_stack_t *ips, boolean_t isv6) 1454b127ac41SPhilip Kirk { 1455b127ac41SPhilip Kirk ipnetif_t *ipnetif; 1456b127ac41SPhilip Kirk boolean_t refrele_needed = B_TRUE; 14570a0e9771SDarren Reed uint64_t ifflags; 14580a0e9771SDarren Reed uint64_t ifindex; 14590a0e9771SDarren Reed char *ifname; 1460b127ac41SPhilip Kirk 14610a0e9771SDarren Reed ifflags = 0; 14620a0e9771SDarren Reed ifname = ipne->ipne_ifname; 14630a0e9771SDarren Reed ifindex = ipne->ipne_ifindex; 14640a0e9771SDarren Reed 14650a0e9771SDarren Reed (void) net_getlifflags(ipne->ipne_protocol, ifindex, 0, &ifflags); 14660a0e9771SDarren Reed 14670a0e9771SDarren Reed if ((ipnetif = ipnetif_getby_index(ifindex, ips)) == NULL) { 14680a0e9771SDarren Reed ipnetif = ipnetif_create(ifname, ifindex, ips, ifflags); 1469b127ac41SPhilip Kirk refrele_needed = B_FALSE; 1470b127ac41SPhilip Kirk } 1471b127ac41SPhilip Kirk if (ipnetif != NULL) { 1472b127ac41SPhilip Kirk ipnetif->if_flags |= 1473b127ac41SPhilip Kirk isv6 ? IPNETIF_IPV6PLUMBED : IPNETIF_IPV4PLUMBED; 1474b127ac41SPhilip Kirk } 1475b127ac41SPhilip Kirk 1476b127ac41SPhilip Kirk if (ipnetif->if_multicnt != 0) { 1477b127ac41SPhilip Kirk if (ip_join_allmulti(ifindex, isv6, 1478b127ac41SPhilip Kirk ips->ips_netstack->netstack_ip) == 0) { 1479b127ac41SPhilip Kirk ipnetif->if_flags |= 1480b127ac41SPhilip Kirk isv6 ? IPNETIF_IPV6ALLMULTI : IPNETIF_IPV4ALLMULTI; 1481b127ac41SPhilip Kirk } 1482b127ac41SPhilip Kirk } 1483b127ac41SPhilip Kirk 1484b127ac41SPhilip Kirk if (refrele_needed) 1485b127ac41SPhilip Kirk ipnetif_refrele(ipnetif); 1486b127ac41SPhilip Kirk } 1487b127ac41SPhilip Kirk 1488b127ac41SPhilip Kirk static void 1489b127ac41SPhilip Kirk ipnet_unplumb_ev(uint64_t ifindex, ipnet_stack_t *ips, boolean_t isv6) 1490b127ac41SPhilip Kirk { 1491b127ac41SPhilip Kirk ipnetif_t *ipnetif; 1492b127ac41SPhilip Kirk 14930a0e9771SDarren Reed if ((ipnetif = ipnetif_getby_index(ifindex, ips)) == NULL) 1494b127ac41SPhilip Kirk return; 1495b127ac41SPhilip Kirk 1496b127ac41SPhilip Kirk mutex_enter(&ipnetif->if_addr_lock); 1497b127ac41SPhilip Kirk ipnet_purge_addrlist(isv6 ? 1498b127ac41SPhilip Kirk &ipnetif->if_ip6addr_list : &ipnetif->if_ip4addr_list); 1499b127ac41SPhilip Kirk mutex_exit(&ipnetif->if_addr_lock); 1500b127ac41SPhilip Kirk 1501b127ac41SPhilip Kirk /* 1502b127ac41SPhilip Kirk * Note that we have one ipnetif for both IPv4 and IPv6, but we receive 1503b127ac41SPhilip Kirk * separate NE_UNPLUMB events for IPv4 and IPv6. We remove the ipnetif 1504b127ac41SPhilip Kirk * if both IPv4 and IPv6 interfaces have been unplumbed. 1505b127ac41SPhilip Kirk */ 1506b127ac41SPhilip Kirk ipnetif->if_flags &= isv6 ? ~IPNETIF_IPV6PLUMBED : ~IPNETIF_IPV4PLUMBED; 1507b127ac41SPhilip Kirk if (!(ipnetif->if_flags & (IPNETIF_IPV4PLUMBED | IPNETIF_IPV6PLUMBED))) 15080a0e9771SDarren Reed ipnetif_remove(ipnetif, ips); 1509b127ac41SPhilip Kirk ipnetif_refrele(ipnetif); 1510b127ac41SPhilip Kirk } 1511b127ac41SPhilip Kirk 1512b127ac41SPhilip Kirk static void 1513b127ac41SPhilip Kirk ipnet_lifup_ev(uint64_t ifindex, uint64_t lifindex, net_handle_t nd, 1514b127ac41SPhilip Kirk ipnet_stack_t *ips, boolean_t isv6) 1515b127ac41SPhilip Kirk { 1516b127ac41SPhilip Kirk ipnetif_t *ipnetif; 1517b127ac41SPhilip Kirk ipnetif_addr_t *ifaddr; 1518b127ac41SPhilip Kirk 15190a0e9771SDarren Reed if ((ipnetif = ipnetif_getby_index(ifindex, ips)) == NULL) 1520b127ac41SPhilip Kirk return; 1521b127ac41SPhilip Kirk if ((ifaddr = ipnet_match_lif(ipnetif, lifindex, isv6)) != NULL) { 1522b127ac41SPhilip Kirk /* 1523b127ac41SPhilip Kirk * We must have missed a NE_LIF_DOWN event. Delete this 1524b127ac41SPhilip Kirk * ifaddr and re-create it. 1525b127ac41SPhilip Kirk */ 1526b127ac41SPhilip Kirk ipnet_delete_ifaddr(ifaddr, ipnetif, isv6); 1527b127ac41SPhilip Kirk } 1528b127ac41SPhilip Kirk 1529b127ac41SPhilip Kirk ipnet_add_ifaddr(lifindex, ipnetif, nd); 1530b127ac41SPhilip Kirk ipnetif_refrele(ipnetif); 1531b127ac41SPhilip Kirk } 1532b127ac41SPhilip Kirk 1533b127ac41SPhilip Kirk static void 1534b127ac41SPhilip Kirk ipnet_lifdown_ev(uint64_t ifindex, uint64_t lifindex, ipnet_stack_t *ips, 1535b127ac41SPhilip Kirk boolean_t isv6) 1536b127ac41SPhilip Kirk { 1537b127ac41SPhilip Kirk ipnetif_t *ipnetif; 1538b127ac41SPhilip Kirk ipnetif_addr_t *ifaddr; 1539b127ac41SPhilip Kirk 15400a0e9771SDarren Reed if ((ipnetif = ipnetif_getby_index(ifindex, ips)) == NULL) 1541b127ac41SPhilip Kirk return; 1542b127ac41SPhilip Kirk if ((ifaddr = ipnet_match_lif(ipnetif, lifindex, isv6)) != NULL) 1543b127ac41SPhilip Kirk ipnet_delete_ifaddr(ifaddr, ipnetif, isv6); 1544b127ac41SPhilip Kirk ipnetif_refrele(ipnetif); 1545b127ac41SPhilip Kirk /* 1546b127ac41SPhilip Kirk * Make sure that open streams on this ipnetif are still allowed to 1547b127ac41SPhilip Kirk * have it open. 1548b127ac41SPhilip Kirk */ 15490a0e9771SDarren Reed ipnetif_zonecheck(ipnetif, ips); 1550b127ac41SPhilip Kirk } 1551b127ac41SPhilip Kirk 1552b127ac41SPhilip Kirk /* 1553b127ac41SPhilip Kirk * This callback from the NIC event framework dispatches a taskq as the event 1554b127ac41SPhilip Kirk * handlers may block. 1555b127ac41SPhilip Kirk */ 1556b127ac41SPhilip Kirk /* ARGSUSED */ 1557b127ac41SPhilip Kirk static int 1558b127ac41SPhilip Kirk ipnet_nicevent_cb(hook_event_token_t token, hook_data_t info, void *arg) 1559b127ac41SPhilip Kirk { 1560b127ac41SPhilip Kirk ipnet_stack_t *ips = arg; 1561b127ac41SPhilip Kirk hook_nic_event_t *hn = (hook_nic_event_t *)info; 1562b127ac41SPhilip Kirk ipnet_nicevent_t *ipne; 1563b127ac41SPhilip Kirk 1564b127ac41SPhilip Kirk if ((ipne = kmem_alloc(sizeof (ipnet_nicevent_t), KM_NOSLEEP)) == NULL) 1565b127ac41SPhilip Kirk return (0); 1566b127ac41SPhilip Kirk ipne->ipne_event = hn->hne_event; 1567b127ac41SPhilip Kirk ipne->ipne_protocol = hn->hne_protocol; 1568b127ac41SPhilip Kirk ipne->ipne_stackid = ips->ips_netstack->netstack_stackid; 1569b127ac41SPhilip Kirk ipne->ipne_ifindex = hn->hne_nic; 1570b127ac41SPhilip Kirk ipne->ipne_lifindex = hn->hne_lif; 1571b127ac41SPhilip Kirk if (hn->hne_datalen != 0) { 1572b127ac41SPhilip Kirk (void) strlcpy(ipne->ipne_ifname, hn->hne_data, 1573b127ac41SPhilip Kirk sizeof (ipne->ipne_ifname)); 1574b127ac41SPhilip Kirk } 1575b127ac41SPhilip Kirk (void) ddi_taskq_dispatch(ipnet_nicevent_taskq, ipnet_nicevent_task, 1576b127ac41SPhilip Kirk ipne, DDI_NOSLEEP); 1577b127ac41SPhilip Kirk return (0); 1578b127ac41SPhilip Kirk } 1579b127ac41SPhilip Kirk 1580b127ac41SPhilip Kirk static void 1581b127ac41SPhilip Kirk ipnet_nicevent_task(void *arg) 1582b127ac41SPhilip Kirk { 1583b127ac41SPhilip Kirk ipnet_nicevent_t *ipne = arg; 1584b127ac41SPhilip Kirk netstack_t *ns; 1585b127ac41SPhilip Kirk ipnet_stack_t *ips; 1586b127ac41SPhilip Kirk boolean_t isv6; 1587b127ac41SPhilip Kirk 1588b127ac41SPhilip Kirk if ((ns = netstack_find_by_stackid(ipne->ipne_stackid)) == NULL) 1589b127ac41SPhilip Kirk goto done; 1590b127ac41SPhilip Kirk ips = ns->netstack_ipnet; 1591b127ac41SPhilip Kirk isv6 = (ipne->ipne_protocol == ips->ips_ndv6); 1592b127ac41SPhilip Kirk 1593b127ac41SPhilip Kirk mutex_enter(&ips->ips_event_lock); 1594b127ac41SPhilip Kirk switch (ipne->ipne_event) { 1595b127ac41SPhilip Kirk case NE_PLUMB: 15960a0e9771SDarren Reed ipnet_plumb_ev(ipne, ips, isv6); 1597b127ac41SPhilip Kirk break; 1598b127ac41SPhilip Kirk case NE_UNPLUMB: 1599b127ac41SPhilip Kirk ipnet_unplumb_ev(ipne->ipne_ifindex, ips, isv6); 1600b127ac41SPhilip Kirk break; 1601b127ac41SPhilip Kirk case NE_LIF_UP: 1602b127ac41SPhilip Kirk ipnet_lifup_ev(ipne->ipne_ifindex, ipne->ipne_lifindex, 1603b127ac41SPhilip Kirk ipne->ipne_protocol, ips, isv6); 1604b127ac41SPhilip Kirk break; 1605b127ac41SPhilip Kirk case NE_LIF_DOWN: 1606b127ac41SPhilip Kirk ipnet_lifdown_ev(ipne->ipne_ifindex, ipne->ipne_lifindex, ips, 1607b127ac41SPhilip Kirk isv6); 1608b127ac41SPhilip Kirk break; 1609b127ac41SPhilip Kirk default: 1610b127ac41SPhilip Kirk break; 1611b127ac41SPhilip Kirk } 1612b127ac41SPhilip Kirk mutex_exit(&ips->ips_event_lock); 1613b127ac41SPhilip Kirk done: 1614b127ac41SPhilip Kirk if (ns != NULL) 1615b127ac41SPhilip Kirk netstack_rele(ns); 1616b127ac41SPhilip Kirk kmem_free(ipne, sizeof (ipnet_nicevent_t)); 1617b127ac41SPhilip Kirk } 1618b127ac41SPhilip Kirk 1619b127ac41SPhilip Kirk dev_t 1620b127ac41SPhilip Kirk ipnet_if_getdev(char *name, zoneid_t zoneid) 1621b127ac41SPhilip Kirk { 1622b127ac41SPhilip Kirk netstack_t *ns; 1623b127ac41SPhilip Kirk ipnet_stack_t *ips; 1624b127ac41SPhilip Kirk ipnetif_t *ipnetif; 1625b127ac41SPhilip Kirk dev_t dev = (dev_t)-1; 1626b127ac41SPhilip Kirk 1627b127ac41SPhilip Kirk if (is_system_labeled() && zoneid != GLOBAL_ZONEID) 1628b127ac41SPhilip Kirk return (dev); 1629b127ac41SPhilip Kirk if ((ns = netstack_find_by_zoneid(zoneid)) == NULL) 1630b127ac41SPhilip Kirk return (dev); 1631b127ac41SPhilip Kirk 1632b127ac41SPhilip Kirk ips = ns->netstack_ipnet; 1633b127ac41SPhilip Kirk mutex_enter(&ips->ips_avl_lock); 1634b127ac41SPhilip Kirk if ((ipnetif = avl_find(&ips->ips_avl_by_name, name, NULL)) != NULL) { 16350a0e9771SDarren Reed if (ipnetif_in_zone(ipnetif, zoneid, ips)) 1636b127ac41SPhilip Kirk dev = ipnetif->if_dev; 1637b127ac41SPhilip Kirk } 1638b127ac41SPhilip Kirk mutex_exit(&ips->ips_avl_lock); 1639b127ac41SPhilip Kirk netstack_rele(ns); 1640b127ac41SPhilip Kirk 1641b127ac41SPhilip Kirk return (dev); 1642b127ac41SPhilip Kirk } 1643b127ac41SPhilip Kirk 1644b127ac41SPhilip Kirk static ipnetif_t * 16450a0e9771SDarren Reed ipnetif_getby_index(uint64_t id, ipnet_stack_t *ips) 1646b127ac41SPhilip Kirk { 1647b127ac41SPhilip Kirk ipnetif_t *ipnetif; 1648b127ac41SPhilip Kirk 1649b127ac41SPhilip Kirk mutex_enter(&ips->ips_avl_lock); 1650b127ac41SPhilip Kirk if ((ipnetif = avl_find(&ips->ips_avl_by_index, &id, NULL)) != NULL) 1651b127ac41SPhilip Kirk ipnetif_refhold(ipnetif); 1652b127ac41SPhilip Kirk mutex_exit(&ips->ips_avl_lock); 1653b127ac41SPhilip Kirk return (ipnetif); 1654b127ac41SPhilip Kirk } 1655b127ac41SPhilip Kirk 1656b127ac41SPhilip Kirk static ipnetif_t * 16570a0e9771SDarren Reed ipnetif_getby_dev(dev_t dev, ipnet_stack_t *ips) 1658b127ac41SPhilip Kirk { 1659b127ac41SPhilip Kirk ipnetif_t *ipnetif; 1660b127ac41SPhilip Kirk avl_tree_t *tree; 1661b127ac41SPhilip Kirk 1662b127ac41SPhilip Kirk mutex_enter(&ips->ips_avl_lock); 1663b127ac41SPhilip Kirk tree = &ips->ips_avl_by_index; 1664b127ac41SPhilip Kirk for (ipnetif = avl_first(tree); ipnetif != NULL; 1665b127ac41SPhilip Kirk ipnetif = avl_walk(tree, ipnetif, AVL_AFTER)) { 1666b127ac41SPhilip Kirk if (ipnetif->if_dev == dev) { 1667b127ac41SPhilip Kirk ipnetif_refhold(ipnetif); 1668b127ac41SPhilip Kirk break; 1669b127ac41SPhilip Kirk } 1670b127ac41SPhilip Kirk } 1671b127ac41SPhilip Kirk mutex_exit(&ips->ips_avl_lock); 1672b127ac41SPhilip Kirk return (ipnetif); 1673b127ac41SPhilip Kirk } 1674b127ac41SPhilip Kirk 1675b127ac41SPhilip Kirk static ipnetif_addr_t * 1676b127ac41SPhilip Kirk ipnet_match_lif(ipnetif_t *ipnetif, lif_if_t lid, boolean_t isv6) 1677b127ac41SPhilip Kirk { 1678b127ac41SPhilip Kirk ipnetif_addr_t *ifaddr; 1679b127ac41SPhilip Kirk list_t *list; 1680b127ac41SPhilip Kirk 1681b127ac41SPhilip Kirk mutex_enter(&ipnetif->if_addr_lock); 1682b127ac41SPhilip Kirk list = isv6 ? &ipnetif->if_ip6addr_list : &ipnetif->if_ip4addr_list; 1683b127ac41SPhilip Kirk for (ifaddr = list_head(list); ifaddr != NULL; 1684b127ac41SPhilip Kirk ifaddr = list_next(list, ifaddr)) { 1685b127ac41SPhilip Kirk if (lid == ifaddr->ifa_id) 1686b127ac41SPhilip Kirk break; 1687b127ac41SPhilip Kirk } 1688b127ac41SPhilip Kirk mutex_exit(&ipnetif->if_addr_lock); 1689b127ac41SPhilip Kirk return (ifaddr); 1690b127ac41SPhilip Kirk } 1691b127ac41SPhilip Kirk 1692b127ac41SPhilip Kirk /* ARGSUSED */ 1693b127ac41SPhilip Kirk static void * 1694b127ac41SPhilip Kirk ipnet_stack_init(netstackid_t stackid, netstack_t *ns) 1695b127ac41SPhilip Kirk { 1696b127ac41SPhilip Kirk ipnet_stack_t *ips; 1697b127ac41SPhilip Kirk 1698b127ac41SPhilip Kirk ips = kmem_zalloc(sizeof (*ips), KM_SLEEP); 1699b127ac41SPhilip Kirk ips->ips_netstack = ns; 1700b127ac41SPhilip Kirk mutex_init(&ips->ips_avl_lock, NULL, MUTEX_DEFAULT, 0); 17010a0e9771SDarren Reed avl_create(&ips->ips_avl_by_index, ipnetif_compare_index, 1702b127ac41SPhilip Kirk sizeof (ipnetif_t), offsetof(ipnetif_t, if_avl_by_index)); 17030a0e9771SDarren Reed avl_create(&ips->ips_avl_by_name, ipnetif_compare_name, 1704b127ac41SPhilip Kirk sizeof (ipnetif_t), offsetof(ipnetif_t, if_avl_by_name)); 17050a0e9771SDarren Reed avl_create(&ips->ips_avl_by_shared, ipnetif_compare_name_zone, 17060a0e9771SDarren Reed sizeof (ipnetif_t), offsetof(ipnetif_t, if_avl_by_shared)); 1707b127ac41SPhilip Kirk mutex_init(&ips->ips_walkers_lock, NULL, MUTEX_DEFAULT, NULL); 1708b127ac41SPhilip Kirk cv_init(&ips->ips_walkers_cv, NULL, CV_DRIVER, NULL); 1709b127ac41SPhilip Kirk list_create(&ips->ips_str_list, sizeof (ipnet_t), 1710b127ac41SPhilip Kirk offsetof(ipnet_t, ipnet_next)); 1711b127ac41SPhilip Kirk ipnet_register_netihook(ips); 1712b127ac41SPhilip Kirk return (ips); 1713b127ac41SPhilip Kirk } 1714b127ac41SPhilip Kirk 1715b127ac41SPhilip Kirk /* ARGSUSED */ 1716b127ac41SPhilip Kirk static void 1717b127ac41SPhilip Kirk ipnet_stack_fini(netstackid_t stackid, void *arg) 1718b127ac41SPhilip Kirk { 1719b127ac41SPhilip Kirk ipnet_stack_t *ips = arg; 1720b127ac41SPhilip Kirk ipnetif_t *ipnetif, *nipnetif; 1721b127ac41SPhilip Kirk 17220a0e9771SDarren Reed if (ips->ips_kstatp != NULL) { 17230a0e9771SDarren Reed zoneid_t zoneid; 17240a0e9771SDarren Reed 17250a0e9771SDarren Reed zoneid = netstackid_to_zoneid(stackid); 17260a0e9771SDarren Reed net_kstat_delete(net_zoneidtonetid(zoneid), ips->ips_kstatp); 17270a0e9771SDarren Reed } 1728b127ac41SPhilip Kirk if (ips->ips_ndv4 != NULL) { 1729b127ac41SPhilip Kirk VERIFY(net_hook_unregister(ips->ips_ndv4, NH_NIC_EVENTS, 1730b127ac41SPhilip Kirk ips->ips_nicevents) == 0); 1731b127ac41SPhilip Kirk VERIFY(net_protocol_release(ips->ips_ndv4) == 0); 1732b127ac41SPhilip Kirk } 1733b127ac41SPhilip Kirk if (ips->ips_ndv6 != NULL) { 1734b127ac41SPhilip Kirk VERIFY(net_hook_unregister(ips->ips_ndv6, NH_NIC_EVENTS, 1735b127ac41SPhilip Kirk ips->ips_nicevents) == 0); 1736b127ac41SPhilip Kirk VERIFY(net_protocol_release(ips->ips_ndv6) == 0); 1737b127ac41SPhilip Kirk } 1738b127ac41SPhilip Kirk hook_free(ips->ips_nicevents); 1739b127ac41SPhilip Kirk 1740b127ac41SPhilip Kirk for (ipnetif = avl_first(&ips->ips_avl_by_index); ipnetif != NULL; 1741b127ac41SPhilip Kirk ipnetif = nipnetif) { 1742b127ac41SPhilip Kirk nipnetif = AVL_NEXT(&ips->ips_avl_by_index, ipnetif); 17430a0e9771SDarren Reed ipnetif_remove(ipnetif, ips); 1744b127ac41SPhilip Kirk } 17450a0e9771SDarren Reed avl_destroy(&ips->ips_avl_by_shared); 1746b127ac41SPhilip Kirk avl_destroy(&ips->ips_avl_by_index); 1747b127ac41SPhilip Kirk avl_destroy(&ips->ips_avl_by_name); 1748b127ac41SPhilip Kirk mutex_destroy(&ips->ips_avl_lock); 1749b127ac41SPhilip Kirk mutex_destroy(&ips->ips_walkers_lock); 1750b127ac41SPhilip Kirk cv_destroy(&ips->ips_walkers_cv); 1751b127ac41SPhilip Kirk list_destroy(&ips->ips_str_list); 1752b127ac41SPhilip Kirk kmem_free(ips, sizeof (*ips)); 1753b127ac41SPhilip Kirk } 1754b127ac41SPhilip Kirk 1755b127ac41SPhilip Kirk /* Do any of the addresses in addrlist belong the supplied zoneid? */ 1756b127ac41SPhilip Kirk static boolean_t 1757b127ac41SPhilip Kirk ipnet_addrs_in_zone(list_t *addrlist, zoneid_t zoneid) 1758b127ac41SPhilip Kirk { 1759b127ac41SPhilip Kirk ipnetif_addr_t *ifa; 1760b127ac41SPhilip Kirk 1761b127ac41SPhilip Kirk for (ifa = list_head(addrlist); ifa != NULL; 1762b127ac41SPhilip Kirk ifa = list_next(addrlist, ifa)) { 1763b127ac41SPhilip Kirk if (ifa->ifa_zone == zoneid) 1764b127ac41SPhilip Kirk return (B_TRUE); 1765b127ac41SPhilip Kirk } 1766b127ac41SPhilip Kirk return (B_FALSE); 1767b127ac41SPhilip Kirk } 1768b127ac41SPhilip Kirk 1769b127ac41SPhilip Kirk /* Should the supplied ipnetif be visible from the supplied zoneid? */ 1770b127ac41SPhilip Kirk static boolean_t 17710a0e9771SDarren Reed ipnetif_in_zone(ipnetif_t *ipnetif, zoneid_t zoneid, ipnet_stack_t *ips) 1772b127ac41SPhilip Kirk { 1773b127ac41SPhilip Kirk int ret; 1774b127ac41SPhilip Kirk 1775b127ac41SPhilip Kirk /* 1776b127ac41SPhilip Kirk * The global zone has visibility into all interfaces in the global 1777b127ac41SPhilip Kirk * stack, and exclusive stack zones have visibility into all 1778b127ac41SPhilip Kirk * interfaces in their stack. 1779b127ac41SPhilip Kirk */ 1780b127ac41SPhilip Kirk if (zoneid == GLOBAL_ZONEID || 1781b127ac41SPhilip Kirk ips->ips_netstack->netstack_stackid != GLOBAL_NETSTACKID) 1782b127ac41SPhilip Kirk return (B_TRUE); 1783b127ac41SPhilip Kirk 1784b127ac41SPhilip Kirk /* 1785b127ac41SPhilip Kirk * Shared-stack zones only have visibility for interfaces that have 1786b127ac41SPhilip Kirk * addresses in their zone. 1787b127ac41SPhilip Kirk */ 1788b127ac41SPhilip Kirk mutex_enter(&ipnetif->if_addr_lock); 1789b127ac41SPhilip Kirk ret = ipnet_addrs_in_zone(&ipnetif->if_ip4addr_list, zoneid) || 1790b127ac41SPhilip Kirk ipnet_addrs_in_zone(&ipnetif->if_ip6addr_list, zoneid); 1791b127ac41SPhilip Kirk mutex_exit(&ipnetif->if_addr_lock); 1792b127ac41SPhilip Kirk return (ret); 1793b127ac41SPhilip Kirk } 1794b127ac41SPhilip Kirk 1795b127ac41SPhilip Kirk /* 1796b127ac41SPhilip Kirk * Verify that any ipnet_t that has a reference to the supplied ipnetif should 1797b127ac41SPhilip Kirk * still be allowed to have it open. A given ipnet_t may no longer be allowed 1798b127ac41SPhilip Kirk * to have an ipnetif open if there are no longer any addresses that belong to 1799b127ac41SPhilip Kirk * the ipnetif in the ipnet_t's non-global shared-stack zoneid. If that's the 1800b127ac41SPhilip Kirk * case, send the ipnet_t an M_HANGUP. 1801b127ac41SPhilip Kirk */ 1802b127ac41SPhilip Kirk static void 18030a0e9771SDarren Reed ipnetif_zonecheck(ipnetif_t *ipnetif, ipnet_stack_t *ips) 1804b127ac41SPhilip Kirk { 1805b127ac41SPhilip Kirk list_t *strlist = &ips->ips_str_list; 1806b127ac41SPhilip Kirk ipnet_t *ipnet; 1807b127ac41SPhilip Kirk 1808b127ac41SPhilip Kirk ipnet_walkers_inc(ips); 1809b127ac41SPhilip Kirk for (ipnet = list_head(strlist); ipnet != NULL; 1810b127ac41SPhilip Kirk ipnet = list_next(strlist, ipnet)) { 1811b127ac41SPhilip Kirk if (ipnet->ipnet_if != ipnetif) 1812b127ac41SPhilip Kirk continue; 18130a0e9771SDarren Reed if (!ipnetif_in_zone(ipnetif, ipnet->ipnet_zoneid, ips)) 1814b127ac41SPhilip Kirk (void) putnextctl(ipnet->ipnet_rq, M_HANGUP); 1815b127ac41SPhilip Kirk } 1816b127ac41SPhilip Kirk ipnet_walkers_dec(ips); 1817b127ac41SPhilip Kirk } 1818b127ac41SPhilip Kirk 1819b127ac41SPhilip Kirk void 1820b127ac41SPhilip Kirk ipnet_walk_if(ipnet_walkfunc_t *cb, void *arg, zoneid_t zoneid) 1821b127ac41SPhilip Kirk { 1822b127ac41SPhilip Kirk ipnetif_t *ipnetif; 1823b127ac41SPhilip Kirk list_t cbdata; 1824b127ac41SPhilip Kirk ipnetif_cbdata_t *cbnode; 1825b127ac41SPhilip Kirk netstack_t *ns; 1826b127ac41SPhilip Kirk ipnet_stack_t *ips; 1827b127ac41SPhilip Kirk 1828b127ac41SPhilip Kirk /* 1829b127ac41SPhilip Kirk * On labeled systems, non-global zones shouldn't see anything 1830b127ac41SPhilip Kirk * in /dev/ipnet. 1831b127ac41SPhilip Kirk */ 1832b127ac41SPhilip Kirk if (is_system_labeled() && zoneid != GLOBAL_ZONEID) 1833b127ac41SPhilip Kirk return; 1834b127ac41SPhilip Kirk 1835b127ac41SPhilip Kirk if ((ns = netstack_find_by_zoneid(zoneid)) == NULL) 1836b127ac41SPhilip Kirk return; 1837b127ac41SPhilip Kirk 1838b127ac41SPhilip Kirk ips = ns->netstack_ipnet; 1839b127ac41SPhilip Kirk list_create(&cbdata, sizeof (ipnetif_cbdata_t), 1840b127ac41SPhilip Kirk offsetof(ipnetif_cbdata_t, ic_next)); 1841b127ac41SPhilip Kirk 1842b127ac41SPhilip Kirk mutex_enter(&ips->ips_avl_lock); 1843b127ac41SPhilip Kirk for (ipnetif = avl_first(&ips->ips_avl_by_index); ipnetif != NULL; 1844b127ac41SPhilip Kirk ipnetif = avl_walk(&ips->ips_avl_by_index, ipnetif, AVL_AFTER)) { 18450a0e9771SDarren Reed if (!ipnetif_in_zone(ipnetif, zoneid, ips)) 1846b127ac41SPhilip Kirk continue; 1847b127ac41SPhilip Kirk cbnode = kmem_zalloc(sizeof (ipnetif_cbdata_t), KM_SLEEP); 1848b127ac41SPhilip Kirk (void) strlcpy(cbnode->ic_ifname, ipnetif->if_name, LIFNAMSIZ); 1849b127ac41SPhilip Kirk cbnode->ic_dev = ipnetif->if_dev; 1850b127ac41SPhilip Kirk list_insert_head(&cbdata, cbnode); 1851b127ac41SPhilip Kirk } 1852b127ac41SPhilip Kirk mutex_exit(&ips->ips_avl_lock); 1853b127ac41SPhilip Kirk 1854b127ac41SPhilip Kirk while ((cbnode = list_head(&cbdata)) != NULL) { 1855b127ac41SPhilip Kirk cb(cbnode->ic_ifname, arg, cbnode->ic_dev); 1856b127ac41SPhilip Kirk list_remove(&cbdata, cbnode); 1857b127ac41SPhilip Kirk kmem_free(cbnode, sizeof (ipnetif_cbdata_t)); 1858b127ac41SPhilip Kirk } 1859b127ac41SPhilip Kirk list_destroy(&cbdata); 1860b127ac41SPhilip Kirk netstack_rele(ns); 1861b127ac41SPhilip Kirk } 1862b127ac41SPhilip Kirk 1863b127ac41SPhilip Kirk static int 18640a0e9771SDarren Reed ipnetif_compare_index(const void *index_ptr, const void *ipnetifp) 1865b127ac41SPhilip Kirk { 1866b127ac41SPhilip Kirk int64_t index1 = *((int64_t *)index_ptr); 1867b127ac41SPhilip Kirk int64_t index2 = (int64_t)((ipnetif_t *)ipnetifp)->if_index; 1868b127ac41SPhilip Kirk 1869b127ac41SPhilip Kirk return (SIGNOF(index2 - index1)); 1870b127ac41SPhilip Kirk } 1871b127ac41SPhilip Kirk 1872b127ac41SPhilip Kirk static int 18730a0e9771SDarren Reed ipnetif_compare_name(const void *name_ptr, const void *ipnetifp) 1874b127ac41SPhilip Kirk { 1875b127ac41SPhilip Kirk int res; 1876b127ac41SPhilip Kirk 1877b127ac41SPhilip Kirk res = strcmp(((ipnetif_t *)ipnetifp)->if_name, name_ptr); 1878b127ac41SPhilip Kirk return (SIGNOF(res)); 1879b127ac41SPhilip Kirk } 1880b127ac41SPhilip Kirk 18810a0e9771SDarren Reed static int 18820a0e9771SDarren Reed ipnetif_compare_name_zone(const void *key_ptr, const void *ipnetifp) 18830a0e9771SDarren Reed { 18840a0e9771SDarren Reed const uintptr_t *ptr = key_ptr; 18850a0e9771SDarren Reed const ipnetif_t *ifp; 18860a0e9771SDarren Reed int res; 18870a0e9771SDarren Reed 18880a0e9771SDarren Reed ifp = ipnetifp; 18890a0e9771SDarren Reed res = ifp->if_zoneid - ptr[0]; 18900a0e9771SDarren Reed if (res != 0) 18910a0e9771SDarren Reed return (SIGNOF(res)); 18920a0e9771SDarren Reed res = strcmp(ifp->if_name, (char *)ptr[1]); 18930a0e9771SDarren Reed return (SIGNOF(res)); 18940a0e9771SDarren Reed } 18950a0e9771SDarren Reed 1896b127ac41SPhilip Kirk static void 1897b127ac41SPhilip Kirk ipnetif_refhold(ipnetif_t *ipnetif) 1898b127ac41SPhilip Kirk { 1899b127ac41SPhilip Kirk mutex_enter(&ipnetif->if_reflock); 1900b127ac41SPhilip Kirk ipnetif->if_refcnt++; 1901b127ac41SPhilip Kirk mutex_exit(&ipnetif->if_reflock); 1902b127ac41SPhilip Kirk } 1903b127ac41SPhilip Kirk 1904b127ac41SPhilip Kirk static void 1905b127ac41SPhilip Kirk ipnetif_refrele(ipnetif_t *ipnetif) 1906b127ac41SPhilip Kirk { 1907b127ac41SPhilip Kirk mutex_enter(&ipnetif->if_reflock); 19080a0e9771SDarren Reed ASSERT(ipnetif->if_refcnt > 0); 1909b127ac41SPhilip Kirk if (--ipnetif->if_refcnt == 0) 19100a0e9771SDarren Reed ipnetif_free(ipnetif); 1911b127ac41SPhilip Kirk else 1912b127ac41SPhilip Kirk mutex_exit(&ipnetif->if_reflock); 1913b127ac41SPhilip Kirk } 1914b127ac41SPhilip Kirk 1915b127ac41SPhilip Kirk static void 1916b127ac41SPhilip Kirk ipnet_walkers_inc(ipnet_stack_t *ips) 1917b127ac41SPhilip Kirk { 1918b127ac41SPhilip Kirk mutex_enter(&ips->ips_walkers_lock); 1919b127ac41SPhilip Kirk ips->ips_walkers_cnt++; 1920b127ac41SPhilip Kirk mutex_exit(&ips->ips_walkers_lock); 1921b127ac41SPhilip Kirk } 1922b127ac41SPhilip Kirk 1923b127ac41SPhilip Kirk static void 1924b127ac41SPhilip Kirk ipnet_walkers_dec(ipnet_stack_t *ips) 1925b127ac41SPhilip Kirk { 1926b127ac41SPhilip Kirk mutex_enter(&ips->ips_walkers_lock); 1927b127ac41SPhilip Kirk ASSERT(ips->ips_walkers_cnt != 0); 1928b127ac41SPhilip Kirk if (--ips->ips_walkers_cnt == 0) 1929b127ac41SPhilip Kirk cv_broadcast(&ips->ips_walkers_cv); 1930b127ac41SPhilip Kirk mutex_exit(&ips->ips_walkers_lock); 1931b127ac41SPhilip Kirk } 19320a0e9771SDarren Reed 19330a0e9771SDarren Reed /*ARGSUSED*/ 19340a0e9771SDarren Reed static int 19350a0e9771SDarren Reed ipobs_bounce_func(hook_event_token_t token, hook_data_t info, void *arg) 19360a0e9771SDarren Reed { 19370a0e9771SDarren Reed hook_pkt_observe_t *hdr; 19380a0e9771SDarren Reed pfv_t func = (pfv_t)arg; 19390a0e9771SDarren Reed mblk_t *mp; 19400a0e9771SDarren Reed 19410a0e9771SDarren Reed hdr = (hook_pkt_observe_t *)info; 1942dbed73cbSSangeeta Misra /* 1943dbed73cbSSangeeta Misra * Code in ip_input() expects that it is the only one accessing the 1944dbed73cbSSangeeta Misra * packet. 1945dbed73cbSSangeeta Misra */ 19460a0e9771SDarren Reed mp = copymsg(hdr->hpo_pkt); 19470a0e9771SDarren Reed if (mp == NULL) { 19480a0e9771SDarren Reed netstack_t *ns = hdr->hpo_ctx; 19490a0e9771SDarren Reed ipnet_stack_t *ips = ns->netstack_ipnet; 19500a0e9771SDarren Reed 19510a0e9771SDarren Reed IPSK_BUMP(ips, ik_dispatchDupDrop); 19520a0e9771SDarren Reed return (0); 19530a0e9771SDarren Reed } 19540a0e9771SDarren Reed 19550a0e9771SDarren Reed hdr = (hook_pkt_observe_t *)mp->b_rptr; 19560a0e9771SDarren Reed hdr->hpo_pkt = mp; 19570a0e9771SDarren Reed 19580a0e9771SDarren Reed func(mp); 19590a0e9771SDarren Reed 19600a0e9771SDarren Reed return (0); 19610a0e9771SDarren Reed } 19620a0e9771SDarren Reed 19630a0e9771SDarren Reed hook_t * 19640a0e9771SDarren Reed ipobs_register_hook(netstack_t *ns, pfv_t func) 19650a0e9771SDarren Reed { 19660a0e9771SDarren Reed ip_stack_t *ipst = ns->netstack_ip; 19670a0e9771SDarren Reed char name[32]; 19680a0e9771SDarren Reed hook_t *hook; 19690a0e9771SDarren Reed 19700a0e9771SDarren Reed HOOK_INIT(hook, ipobs_bounce_func, "", (void *)func); 19710a0e9771SDarren Reed VERIFY(hook != NULL); 19720a0e9771SDarren Reed 19730a0e9771SDarren Reed /* 19740a0e9771SDarren Reed * To register multiple hooks with he same callback function, 19750a0e9771SDarren Reed * a unique name is needed. 19760a0e9771SDarren Reed */ 1977391710f2SDarren Reed (void) snprintf(name, sizeof (name), "ipobserve_%p", (void *)hook); 19780a0e9771SDarren Reed hook->h_name = strdup(name); 19790a0e9771SDarren Reed 19800a0e9771SDarren Reed (void) net_hook_register(ipst->ips_ip4_observe_pr, NH_OBSERVE, hook); 19810a0e9771SDarren Reed (void) net_hook_register(ipst->ips_ip6_observe_pr, NH_OBSERVE, hook); 19820a0e9771SDarren Reed 19830a0e9771SDarren Reed return (hook); 19840a0e9771SDarren Reed } 19850a0e9771SDarren Reed 19860a0e9771SDarren Reed void 19870a0e9771SDarren Reed ipobs_unregister_hook(netstack_t *ns, hook_t *hook) 19880a0e9771SDarren Reed { 19890a0e9771SDarren Reed ip_stack_t *ipst = ns->netstack_ip; 19900a0e9771SDarren Reed 19910a0e9771SDarren Reed (void) net_hook_unregister(ipst->ips_ip4_observe_pr, NH_OBSERVE, hook); 19920a0e9771SDarren Reed 19930a0e9771SDarren Reed (void) net_hook_unregister(ipst->ips_ip6_observe_pr, NH_OBSERVE, hook); 19940a0e9771SDarren Reed 19950a0e9771SDarren Reed strfree(hook->h_name); 19960a0e9771SDarren Reed 19970a0e9771SDarren Reed hook_free(hook); 19980a0e9771SDarren Reed } 19990a0e9771SDarren Reed 20000a0e9771SDarren Reed /* ******************************************************************** */ 20010a0e9771SDarren Reed /* BPF Functions below */ 20020a0e9771SDarren Reed /* ******************************************************************** */ 20030a0e9771SDarren Reed 20040a0e9771SDarren Reed /* 20050a0e9771SDarren Reed * Convenience function to make mapping a zoneid to an ipnet_stack_t easy. 20060a0e9771SDarren Reed */ 2007b7ea883bSDarren Reed ipnet_stack_t * 20080a0e9771SDarren Reed ipnet_find_by_zoneid(zoneid_t zoneid) 20090a0e9771SDarren Reed { 20100a0e9771SDarren Reed netstack_t *ns; 20110a0e9771SDarren Reed 20120a0e9771SDarren Reed VERIFY((ns = netstack_find_by_zoneid(zoneid)) != NULL); 20130a0e9771SDarren Reed return (ns->netstack_ipnet); 20140a0e9771SDarren Reed } 20150a0e9771SDarren Reed 20160a0e9771SDarren Reed /* 2017b7ea883bSDarren Reed * Functions, such as the above ipnet_find_by_zoneid(), will return a 2018b7ea883bSDarren Reed * pointer to ipnet_stack_t by calling a netstack lookup function. 2019b7ea883bSDarren Reed * The netstack_find_*() functions return a pointer after doing a "hold" 2020b7ea883bSDarren Reed * on the data structure and thereby require a "release" when the caller 2021b7ea883bSDarren Reed * is finished with it. We need to mirror that API here and thus a caller 2022b7ea883bSDarren Reed * of ipnet_find_by_zoneid() is required to call ipnet_rele(). 20230a0e9771SDarren Reed */ 20240a0e9771SDarren Reed void 2025b7ea883bSDarren Reed ipnet_rele(ipnet_stack_t *ips) 20260a0e9771SDarren Reed { 2027b7ea883bSDarren Reed netstack_rele(ips->ips_netstack); 2028b7ea883bSDarren Reed } 20290a0e9771SDarren Reed 2030b7ea883bSDarren Reed /* 2031b7ea883bSDarren Reed */ 2032b7ea883bSDarren Reed void 2033b7ea883bSDarren Reed ipnet_set_itap(bpf_itap_fn_t tapfunc) 2034b7ea883bSDarren Reed { 20350a0e9771SDarren Reed ipnet_itap = tapfunc; 20360a0e9771SDarren Reed } 20370a0e9771SDarren Reed 20380a0e9771SDarren Reed /* 20390a0e9771SDarren Reed * The list of interfaces available via ipnet is private for each zone, 20400a0e9771SDarren Reed * so the AVL tree of each zone must be searched for a given name, even 20410a0e9771SDarren Reed * if all names are unique. 20420a0e9771SDarren Reed */ 20430a0e9771SDarren Reed int 20440a0e9771SDarren Reed ipnet_open_byname(const char *name, ipnetif_t **ptr, zoneid_t zoneid) 20450a0e9771SDarren Reed { 20460a0e9771SDarren Reed ipnet_stack_t *ips; 20470a0e9771SDarren Reed ipnetif_t *ipnetif; 20480a0e9771SDarren Reed 20490a0e9771SDarren Reed ASSERT(ptr != NULL); 20500a0e9771SDarren Reed VERIFY((ips = ipnet_find_by_zoneid(zoneid)) != NULL); 20510a0e9771SDarren Reed 20520a0e9771SDarren Reed mutex_enter(&ips->ips_avl_lock); 2053b7ea883bSDarren Reed 2054b7ea883bSDarren Reed /* 2055b7ea883bSDarren Reed * Shared instance zone? 2056b7ea883bSDarren Reed */ 2057b7ea883bSDarren Reed if (netstackid_to_zoneid(zoneid_to_netstackid(zoneid)) != zoneid) { 2058b7ea883bSDarren Reed uintptr_t key[2] = { zoneid, (uintptr_t)name }; 2059b7ea883bSDarren Reed 2060b7ea883bSDarren Reed ipnetif = avl_find(&ips->ips_avl_by_shared, (void *)key, NULL); 2061b7ea883bSDarren Reed } else { 2062b7ea883bSDarren Reed ipnetif = avl_find(&ips->ips_avl_by_name, (void *)name, NULL); 20630a0e9771SDarren Reed } 2064b7ea883bSDarren Reed if (ipnetif != NULL) 2065b7ea883bSDarren Reed ipnetif_refhold(ipnetif); 20660a0e9771SDarren Reed mutex_exit(&ips->ips_avl_lock); 20670a0e9771SDarren Reed 20680a0e9771SDarren Reed *ptr = ipnetif; 2069b7ea883bSDarren Reed ipnet_rele(ips); 20700a0e9771SDarren Reed 20710a0e9771SDarren Reed if (ipnetif == NULL) 20720a0e9771SDarren Reed return (ESRCH); 20730a0e9771SDarren Reed return (0); 20740a0e9771SDarren Reed } 20750a0e9771SDarren Reed 20760a0e9771SDarren Reed void 20770a0e9771SDarren Reed ipnet_close_byhandle(ipnetif_t *ifp) 20780a0e9771SDarren Reed { 20790a0e9771SDarren Reed ASSERT(ifp != NULL); 20800a0e9771SDarren Reed ipnetif_refrele(ifp); 20810a0e9771SDarren Reed } 20820a0e9771SDarren Reed 20830a0e9771SDarren Reed const char * 20840a0e9771SDarren Reed ipnet_name(ipnetif_t *ifp) 20850a0e9771SDarren Reed { 20860a0e9771SDarren Reed ASSERT(ifp != NULL); 20870a0e9771SDarren Reed return (ifp->if_name); 20880a0e9771SDarren Reed } 20890a0e9771SDarren Reed 20900a0e9771SDarren Reed /* 20910a0e9771SDarren Reed * To find the linkid for a given name, it is necessary to know which zone 20920a0e9771SDarren Reed * the interface name belongs to and to search the avl tree for that zone 20930a0e9771SDarren Reed * as there is no master list of all interfaces and which zone they belong 20940a0e9771SDarren Reed * to. It is assumed that the caller of this function is somehow already 20950a0e9771SDarren Reed * working with the ipnet interfaces and hence the ips_event_lock is held. 20960a0e9771SDarren Reed * When BPF calls into this function, it is doing so because of an event 20970a0e9771SDarren Reed * in ipnet, and thus ipnet holds the ips_event_lock. Thus the datalink id 20980a0e9771SDarren Reed * value returned has meaning without the need for grabbing a hold on the 20990a0e9771SDarren Reed * owning structure. 21000a0e9771SDarren Reed */ 21010a0e9771SDarren Reed int 21020a0e9771SDarren Reed ipnet_get_linkid_byname(const char *name, uint_t *idp, zoneid_t zoneid) 21030a0e9771SDarren Reed { 21040a0e9771SDarren Reed ipnet_stack_t *ips; 21050a0e9771SDarren Reed ipnetif_t *ifp; 21060a0e9771SDarren Reed 21070a0e9771SDarren Reed VERIFY((ips = ipnet_find_by_zoneid(zoneid)) != NULL); 21080a0e9771SDarren Reed ASSERT(mutex_owned(&ips->ips_event_lock)); 21090a0e9771SDarren Reed 21100a0e9771SDarren Reed mutex_enter(&ips->ips_avl_lock); 21110a0e9771SDarren Reed ifp = avl_find(&ips->ips_avl_by_name, (void *)name, NULL); 21120a0e9771SDarren Reed if (ifp != NULL) 21130a0e9771SDarren Reed *idp = (uint_t)ifp->if_index; 21140a0e9771SDarren Reed 21150a0e9771SDarren Reed /* 21160a0e9771SDarren Reed * Shared instance zone? 21170a0e9771SDarren Reed */ 21180a0e9771SDarren Reed if (netstackid_to_zoneid(zoneid_to_netstackid(zoneid)) != zoneid) { 21190a0e9771SDarren Reed uintptr_t key[2] = { zoneid, (uintptr_t)name }; 21200a0e9771SDarren Reed 21210a0e9771SDarren Reed ifp = avl_find(&ips->ips_avl_by_shared, (void *)key, NULL); 21220a0e9771SDarren Reed if (ifp != NULL) 21230a0e9771SDarren Reed *idp = (uint_t)ifp->if_index; 21240a0e9771SDarren Reed } 21250a0e9771SDarren Reed 21260a0e9771SDarren Reed mutex_exit(&ips->ips_avl_lock); 2127b7ea883bSDarren Reed ipnet_rele(ips); 21280a0e9771SDarren Reed 21290a0e9771SDarren Reed if (ifp == NULL) 21300a0e9771SDarren Reed return (ESRCH); 21310a0e9771SDarren Reed return (0); 21320a0e9771SDarren Reed } 21330a0e9771SDarren Reed 21340a0e9771SDarren Reed /* 21350a0e9771SDarren Reed * Strictly speaking, there is no such thing as a "client" in ipnet, like 21360a0e9771SDarren Reed * there is in mac. BPF only needs to have this because it is required as 21370a0e9771SDarren Reed * part of interfacing correctly with mac. The reuse of the original 21380a0e9771SDarren Reed * ipnetif_t as a client poses no danger, so long as it is done with its 21390a0e9771SDarren Reed * own ref-count'd hold that is given up on close. 21400a0e9771SDarren Reed */ 21410a0e9771SDarren Reed int 21420a0e9771SDarren Reed ipnet_client_open(ipnetif_t *ptr, ipnetif_t **result) 21430a0e9771SDarren Reed { 21440a0e9771SDarren Reed ASSERT(ptr != NULL); 21450a0e9771SDarren Reed ASSERT(result != NULL); 21460a0e9771SDarren Reed ipnetif_refhold(ptr); 21470a0e9771SDarren Reed *result = ptr; 21480a0e9771SDarren Reed 21490a0e9771SDarren Reed return (0); 21500a0e9771SDarren Reed } 21510a0e9771SDarren Reed 21520a0e9771SDarren Reed void 21530a0e9771SDarren Reed ipnet_client_close(ipnetif_t *ptr) 21540a0e9771SDarren Reed { 21550a0e9771SDarren Reed ASSERT(ptr != NULL); 21560a0e9771SDarren Reed ipnetif_refrele(ptr); 21570a0e9771SDarren Reed } 21580a0e9771SDarren Reed 21590a0e9771SDarren Reed /* 21600a0e9771SDarren Reed * This is called from BPF when it needs to start receiving packets 21610a0e9771SDarren Reed * from ipnet. 21620a0e9771SDarren Reed * 21630a0e9771SDarren Reed * The use of the ipnet_t structure here is somewhat lightweight when 21640a0e9771SDarren Reed * compared to how it is used elsewhere but it already has all of the 21650a0e9771SDarren Reed * right fields in it, so reuse here doesn't seem out of order. Its 21660a0e9771SDarren Reed * primary purpose here is to provide the means to store pointers for 21670a0e9771SDarren Reed * use when ipnet_promisc_remove() needs to be called. 21680a0e9771SDarren Reed * 21690a0e9771SDarren Reed * This should never be called for the IPNET_MINOR_LO device as it is 21700a0e9771SDarren Reed * never created via ipnetif_create. 21710a0e9771SDarren Reed */ 21720a0e9771SDarren Reed /*ARGSUSED*/ 21730a0e9771SDarren Reed int 21740a0e9771SDarren Reed ipnet_promisc_add(void *handle, uint_t how, void *data, uintptr_t *mhandle, 21750a0e9771SDarren Reed int flags) 21760a0e9771SDarren Reed { 21770a0e9771SDarren Reed ip_stack_t *ipst; 21780a0e9771SDarren Reed netstack_t *ns; 21790a0e9771SDarren Reed ipnetif_t *ifp; 21800a0e9771SDarren Reed ipnet_t *ipnet; 21810a0e9771SDarren Reed char name[32]; 21820a0e9771SDarren Reed int error; 21830a0e9771SDarren Reed 21840a0e9771SDarren Reed ifp = (ipnetif_t *)handle; 21850a0e9771SDarren Reed ns = netstack_find_by_zoneid(ifp->if_zoneid); 21860a0e9771SDarren Reed 21870a0e9771SDarren Reed if ((how == DL_PROMISC_PHYS) || (how == DL_PROMISC_MULTI)) { 21880a0e9771SDarren Reed error = ipnet_join_allmulti(ifp, ns->netstack_ipnet); 21890a0e9771SDarren Reed if (error != 0) 21900a0e9771SDarren Reed return (error); 21910a0e9771SDarren Reed } else { 21920a0e9771SDarren Reed return (EINVAL); 21930a0e9771SDarren Reed } 21940a0e9771SDarren Reed 21950a0e9771SDarren Reed ipnet = kmem_zalloc(sizeof (*ipnet), KM_SLEEP); 21960a0e9771SDarren Reed ipnet->ipnet_if = ifp; 21970a0e9771SDarren Reed ipnet->ipnet_ns = ns; 21980a0e9771SDarren Reed ipnet->ipnet_flags = flags; 21990a0e9771SDarren Reed 22000a0e9771SDarren Reed if ((ifp->if_flags & IPNETIF_LOOPBACK) != 0) { 22010a0e9771SDarren Reed ipnet->ipnet_acceptfn = ipnet_loaccept; 22020a0e9771SDarren Reed } else { 22030a0e9771SDarren Reed ipnet->ipnet_acceptfn = ipnet_accept; 22040a0e9771SDarren Reed } 22050a0e9771SDarren Reed 22060a0e9771SDarren Reed /* 22070a0e9771SDarren Reed * To register multiple hooks with the same callback function, 22080a0e9771SDarren Reed * a unique name is needed. 22090a0e9771SDarren Reed */ 22100a0e9771SDarren Reed HOOK_INIT(ipnet->ipnet_hook, ipnet_bpf_bounce, "", ipnet); 22110a0e9771SDarren Reed (void) snprintf(name, sizeof (name), "ipnet_promisc_%p", 2212391710f2SDarren Reed (void *)ipnet->ipnet_hook); 22130a0e9771SDarren Reed ipnet->ipnet_hook->h_name = strdup(name); 22140a0e9771SDarren Reed ipnet->ipnet_data = data; 22150a0e9771SDarren Reed ipnet->ipnet_zoneid = ifp->if_zoneid; 22160a0e9771SDarren Reed 22170a0e9771SDarren Reed ipst = ns->netstack_ip; 22180a0e9771SDarren Reed 22190a0e9771SDarren Reed error = net_hook_register(ipst->ips_ip4_observe_pr, NH_OBSERVE, 22200a0e9771SDarren Reed ipnet->ipnet_hook); 22210a0e9771SDarren Reed if (error != 0) 22220a0e9771SDarren Reed goto regfail; 22230a0e9771SDarren Reed 22240a0e9771SDarren Reed error = net_hook_register(ipst->ips_ip6_observe_pr, NH_OBSERVE, 22250a0e9771SDarren Reed ipnet->ipnet_hook); 22260a0e9771SDarren Reed if (error != 0) { 22270a0e9771SDarren Reed (void) net_hook_unregister(ipst->ips_ip4_observe_pr, 22280a0e9771SDarren Reed NH_OBSERVE, ipnet->ipnet_hook); 22290a0e9771SDarren Reed goto regfail; 22300a0e9771SDarren Reed } 22310a0e9771SDarren Reed 22320a0e9771SDarren Reed *mhandle = (uintptr_t)ipnet; 22330a0e9771SDarren Reed 22340a0e9771SDarren Reed return (0); 22350a0e9771SDarren Reed 22360a0e9771SDarren Reed regfail: 22370a0e9771SDarren Reed cmn_err(CE_WARN, "net_hook_register failed: %d", error); 22380a0e9771SDarren Reed strfree(ipnet->ipnet_hook->h_name); 22390a0e9771SDarren Reed hook_free(ipnet->ipnet_hook); 224026ee720cSArne Jansen ipnet_leave_allmulti(ifp, ns->netstack_ipnet); 2241b7ea883bSDarren Reed netstack_rele(ns); 22420a0e9771SDarren Reed return (error); 22430a0e9771SDarren Reed } 22440a0e9771SDarren Reed 22450a0e9771SDarren Reed void 22460a0e9771SDarren Reed ipnet_promisc_remove(void *data) 22470a0e9771SDarren Reed { 22480a0e9771SDarren Reed ip_stack_t *ipst; 22490a0e9771SDarren Reed ipnet_t *ipnet; 22500a0e9771SDarren Reed hook_t *hook; 22510a0e9771SDarren Reed 22520a0e9771SDarren Reed ipnet = data; 22530a0e9771SDarren Reed ipst = ipnet->ipnet_ns->netstack_ip; 22540a0e9771SDarren Reed hook = ipnet->ipnet_hook; 22550a0e9771SDarren Reed 22560a0e9771SDarren Reed VERIFY(net_hook_unregister(ipst->ips_ip4_observe_pr, NH_OBSERVE, 22570a0e9771SDarren Reed hook) == 0); 22580a0e9771SDarren Reed 22590a0e9771SDarren Reed VERIFY(net_hook_unregister(ipst->ips_ip6_observe_pr, NH_OBSERVE, 22600a0e9771SDarren Reed hook) == 0); 22610a0e9771SDarren Reed 22620a0e9771SDarren Reed strfree(hook->h_name); 22630a0e9771SDarren Reed 22640a0e9771SDarren Reed hook_free(hook); 22650a0e9771SDarren Reed 226626ee720cSArne Jansen ipnet_leave_allmulti(ipnet->ipnet_if, ipnet->ipnet_ns->netstack_ipnet); 226726ee720cSArne Jansen 226826ee720cSArne Jansen netstack_rele(ipnet->ipnet_ns); 226926ee720cSArne Jansen 22700a0e9771SDarren Reed kmem_free(ipnet, sizeof (*ipnet)); 22710a0e9771SDarren Reed } 22720a0e9771SDarren Reed 22730a0e9771SDarren Reed /* 22740a0e9771SDarren Reed * arg here comes from the ipnet_t allocated in ipnet_promisc_add. 22750a0e9771SDarren Reed * An important field from that structure is "ipnet_data" that 22760a0e9771SDarren Reed * contains the "data" pointer passed into ipnet_promisc_add: it needs 22770a0e9771SDarren Reed * to be passed back to bpf when we call into ipnet_itap. 22780a0e9771SDarren Reed * 22790a0e9771SDarren Reed * ipnet_itap is set by ipnet_set_bpfattach, which in turn is called 22800a0e9771SDarren Reed * from BPF. 22810a0e9771SDarren Reed */ 22820a0e9771SDarren Reed /*ARGSUSED*/ 22830a0e9771SDarren Reed static int 22840a0e9771SDarren Reed ipnet_bpf_bounce(hook_event_token_t token, hook_data_t info, void *arg) 22850a0e9771SDarren Reed { 22860a0e9771SDarren Reed hook_pkt_observe_t *hdr; 22870a0e9771SDarren Reed ipnet_addrp_t src; 22880a0e9771SDarren Reed ipnet_addrp_t dst; 22890a0e9771SDarren Reed ipnet_stack_t *ips; 22900a0e9771SDarren Reed ipnet_t *ipnet; 22910a0e9771SDarren Reed mblk_t *netmp; 22920a0e9771SDarren Reed mblk_t *mp; 22930a0e9771SDarren Reed 22940a0e9771SDarren Reed hdr = (hook_pkt_observe_t *)info; 22950a0e9771SDarren Reed mp = hdr->hpo_pkt; 22960a0e9771SDarren Reed ipnet = (ipnet_t *)arg; 22970a0e9771SDarren Reed ips = ((netstack_t *)hdr->hpo_ctx)->netstack_ipnet; 22980a0e9771SDarren Reed 22990a0e9771SDarren Reed netmp = hdr->hpo_pkt->b_cont; 23000a0e9771SDarren Reed src.iap_family = hdr->hpo_family; 23010a0e9771SDarren Reed dst.iap_family = hdr->hpo_family; 23020a0e9771SDarren Reed 23030a0e9771SDarren Reed if (hdr->hpo_family == AF_INET) { 23040a0e9771SDarren Reed src.iap_addr4 = &((ipha_t *)(netmp->b_rptr))->ipha_src; 23050a0e9771SDarren Reed dst.iap_addr4 = &((ipha_t *)(netmp->b_rptr))->ipha_dst; 23060a0e9771SDarren Reed } else { 23070a0e9771SDarren Reed src.iap_addr6 = &((ip6_t *)(netmp->b_rptr))->ip6_src; 23080a0e9771SDarren Reed dst.iap_addr6 = &((ip6_t *)(netmp->b_rptr))->ip6_dst; 23090a0e9771SDarren Reed } 23100a0e9771SDarren Reed 23110a0e9771SDarren Reed if (!(*ipnet->ipnet_acceptfn)(ipnet, hdr, &src, &dst)) { 23120a0e9771SDarren Reed IPSK_BUMP(ips, ik_acceptFail); 23130a0e9771SDarren Reed return (0); 23140a0e9771SDarren Reed } 23150a0e9771SDarren Reed IPSK_BUMP(ips, ik_acceptOk); 23160a0e9771SDarren Reed 23170a0e9771SDarren Reed ipnet_itap(ipnet->ipnet_data, mp, 2318fc5884fcSDarren Reed hdr->hpo_htype == htons(IPOBS_HOOK_OUTBOUND), 2319b7ea883bSDarren Reed ntohl(hdr->hpo_pktlen) + MBLKL(mp)); 23200a0e9771SDarren Reed 23210a0e9771SDarren Reed return (0); 23220a0e9771SDarren Reed } 23230a0e9771SDarren Reed 23240a0e9771SDarren Reed /* 23250a0e9771SDarren Reed * clone'd ipnetif_t's are created when a shared IP instance zone comes 23260a0e9771SDarren Reed * to life and configures an IP address. The model that BPF uses is that 23270a0e9771SDarren Reed * each interface must have a unique pointer and each interface must be 23280a0e9771SDarren Reed * representative of what it can capture. They are limited to one DLT 23290a0e9771SDarren Reed * per interface and one zone per interface. Thus every interface that 23300a0e9771SDarren Reed * can be seen in a zone must be announced via an attach to bpf. For 23310a0e9771SDarren Reed * shared instance zones, this means the ipnet driver needs to detect 23320a0e9771SDarren Reed * when an address is added to an interface in a zone for the first 23330a0e9771SDarren Reed * time (and also when the last address is removed.) 23340a0e9771SDarren Reed */ 23350a0e9771SDarren Reed static ipnetif_t * 23360a0e9771SDarren Reed ipnetif_clone_create(ipnetif_t *ifp, zoneid_t zoneid) 23370a0e9771SDarren Reed { 23380a0e9771SDarren Reed uintptr_t key[2] = { zoneid, (uintptr_t)ifp->if_name }; 23390a0e9771SDarren Reed ipnet_stack_t *ips = ifp->if_stackp; 23400a0e9771SDarren Reed avl_index_t where = 0; 23410a0e9771SDarren Reed ipnetif_t *newif; 23420a0e9771SDarren Reed 23430a0e9771SDarren Reed mutex_enter(&ips->ips_avl_lock); 23440a0e9771SDarren Reed newif = avl_find(&ips->ips_avl_by_shared, (void *)key, &where); 23450a0e9771SDarren Reed if (newif != NULL) { 23460a0e9771SDarren Reed ipnetif_refhold(newif); 23470a0e9771SDarren Reed newif->if_sharecnt++; 23480a0e9771SDarren Reed mutex_exit(&ips->ips_avl_lock); 23490a0e9771SDarren Reed return (newif); 23500a0e9771SDarren Reed } 23510a0e9771SDarren Reed 23520a0e9771SDarren Reed newif = ipnet_alloc_if(ips); 23530a0e9771SDarren Reed if (newif == NULL) { 23540a0e9771SDarren Reed mutex_exit(&ips->ips_avl_lock); 23550a0e9771SDarren Reed return (NULL); 23560a0e9771SDarren Reed } 23570a0e9771SDarren Reed 23580a0e9771SDarren Reed newif->if_refcnt = 1; 23590a0e9771SDarren Reed newif->if_sharecnt = 1; 23600a0e9771SDarren Reed newif->if_zoneid = zoneid; 23610a0e9771SDarren Reed (void) strlcpy(newif->if_name, ifp->if_name, LIFNAMSIZ); 23620a0e9771SDarren Reed newif->if_flags = ifp->if_flags & IPNETIF_LOOPBACK; 23630a0e9771SDarren Reed newif->if_index = ifp->if_index; 23640a0e9771SDarren Reed 23650a0e9771SDarren Reed avl_insert(&ips->ips_avl_by_shared, newif, where); 23660a0e9771SDarren Reed mutex_exit(&ips->ips_avl_lock); 23670a0e9771SDarren Reed 23680a0e9771SDarren Reed return (newif); 23690a0e9771SDarren Reed } 23700a0e9771SDarren Reed 23710a0e9771SDarren Reed static void 23720a0e9771SDarren Reed ipnetif_clone_release(ipnetif_t *ipnetif) 23730a0e9771SDarren Reed { 23740a0e9771SDarren Reed boolean_t dofree = B_FALSE; 23750a0e9771SDarren Reed boolean_t doremove = B_FALSE; 23760a0e9771SDarren Reed ipnet_stack_t *ips = ipnetif->if_stackp; 23770a0e9771SDarren Reed 23780a0e9771SDarren Reed mutex_enter(&ipnetif->if_reflock); 23790a0e9771SDarren Reed ASSERT(ipnetif->if_refcnt > 0); 23800a0e9771SDarren Reed if (--ipnetif->if_refcnt == 0) 23810a0e9771SDarren Reed dofree = B_TRUE; 23820a0e9771SDarren Reed ASSERT(ipnetif->if_sharecnt > 0); 23830a0e9771SDarren Reed if (--ipnetif->if_sharecnt == 0) 23840a0e9771SDarren Reed doremove = B_TRUE; 23850a0e9771SDarren Reed mutex_exit(&ipnetif->if_reflock); 23860a0e9771SDarren Reed if (doremove) { 23870a0e9771SDarren Reed mutex_enter(&ips->ips_avl_lock); 23880a0e9771SDarren Reed avl_remove(&ips->ips_avl_by_shared, ipnetif); 23890a0e9771SDarren Reed mutex_exit(&ips->ips_avl_lock); 23900a0e9771SDarren Reed } 23910a0e9771SDarren Reed if (dofree) { 23920a0e9771SDarren Reed ASSERT(ipnetif->if_sharecnt == 0); 23930a0e9771SDarren Reed ipnetif_free(ipnetif); 23940a0e9771SDarren Reed } 23950a0e9771SDarren Reed } 2396