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