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