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