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
ipnetif_init(void)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
_init(void)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
_fini(void)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
_info(struct modinfo * modinfop)319b127ac41SPhilip Kirk _info(struct modinfo *modinfop)
320b127ac41SPhilip Kirk {
321b127ac41SPhilip Kirk return (mod_info(&modlinkage, modinfop));
322b127ac41SPhilip Kirk }
323b127ac41SPhilip Kirk
324b127ac41SPhilip Kirk static void
ipnet_register_netihook(ipnet_stack_t * ips)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
ipnet_populate_if(net_handle_t nd,ipnet_stack_t * ips,boolean_t isv6)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
ipnet_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)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
ipnet_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)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
ipnet_devinfo(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)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
ipnet_open(queue_t * rq,dev_t * dev,int oflag,int sflag,cred_t * crp)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
ipnet_close(queue_t * rq)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
ipnet_wput(queue_t * q,mblk_t * mp)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
ipnet_rsrv(queue_t * q)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
ipnet_ioctl(queue_t * q,mblk_t * mp)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
ipnet_iocdata(queue_t * q,mblk_t * mp)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
ipnet_wputnondata(queue_t * q,mblk_t * mp)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
ipnet_inforeq(queue_t * q,mblk_t * mp)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
ipnet_bindreq(queue_t * q,mblk_t * mp)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
ipnet_unbindreq(queue_t * q,mblk_t * mp)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
ipnet_dlpromisconreq(queue_t * q,mblk_t * mp)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
ipnet_dlpromiscoffreq(queue_t * q,mblk_t * mp)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
ipnet_join_allmulti(ipnetif_t * ipnetif,ipnet_stack_t * ips)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
ipnet_leave_allmulti(ipnetif_t * ipnetif,ipnet_stack_t * ips)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 *
ipnet_addheader(hook_pkt_observe_t * hdr,mblk_t * mp)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
ipnet_get_addrtype(ipnet_t * ipnet,ipnet_addrp_t * addr)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
ipnet_accept(ipnet_t * ipnet,hook_pkt_observe_t * hdr,ipnet_addrp_t * src,ipnet_addrp_t * dst)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
ipnet_loaccept(ipnet_t * ipnet,hook_pkt_observe_t * hdr,ipnet_addrp_t * src,ipnet_addrp_t * dst)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
ipnet_dispatch(void * arg)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
ipnet_input(mblk_t * mp)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 *
ipnet_alloc_if(ipnet_stack_t * ips)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 *
ipnetif_create(const char * name,uint64_t index,ipnet_stack_t * ips,uint64_t ifflags)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
ipnetif_remove(ipnetif_t * ipnetif,ipnet_stack_t * ips)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
ipnet_purge_addrlist(list_t * addrlist)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
ipnetif_free(ipnetif_t * ipnetif)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
ipnet_add_ifaddr(uint64_t lif,ipnetif_t * ipnetif,net_handle_t nd)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
ipnet_delete_ifaddr(ipnetif_addr_t * ifaddr,ipnetif_t * ipnetif,boolean_t isv6)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
ipnet_plumb_ev(ipnet_nicevent_t * ipne,ipnet_stack_t * ips,boolean_t isv6)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
ipnet_unplumb_ev(uint64_t ifindex,ipnet_stack_t * ips,boolean_t isv6)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
ipnet_lifup_ev(uint64_t ifindex,uint64_t lifindex,net_handle_t nd,ipnet_stack_t * ips,boolean_t isv6)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
ipnet_lifdown_ev(uint64_t ifindex,uint64_t lifindex,ipnet_stack_t * ips,boolean_t isv6)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
ipnet_nicevent_cb(hook_event_token_t token,hook_data_t info,void * arg)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
ipnet_nicevent_task(void * arg)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
ipnet_if_getdev(char * name,zoneid_t zoneid)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 *
ipnetif_getby_index(uint64_t id,ipnet_stack_t * ips)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 *
ipnetif_getby_dev(dev_t dev,ipnet_stack_t * ips)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 *
ipnet_match_lif(ipnetif_t * ipnetif,lif_if_t lid,boolean_t isv6)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 *
ipnet_stack_init(netstackid_t stackid,netstack_t * ns)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
ipnet_stack_fini(netstackid_t stackid,void * arg)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
ipnet_addrs_in_zone(list_t * addrlist,zoneid_t zoneid)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
ipnetif_in_zone(ipnetif_t * ipnetif,zoneid_t zoneid,ipnet_stack_t * ips)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
ipnetif_zonecheck(ipnetif_t * ipnetif,ipnet_stack_t * ips)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
ipnet_walk_if(ipnet_walkfunc_t * cb,void * arg,zoneid_t zoneid)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
ipnetif_compare_index(const void * index_ptr,const void * ipnetifp)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
ipnetif_compare_name(const void * name_ptr,const void * ipnetifp)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
ipnetif_compare_name_zone(const void * key_ptr,const void * ipnetifp)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
ipnetif_refhold(ipnetif_t * ipnetif)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
ipnetif_refrele(ipnetif_t * ipnetif)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
ipnet_walkers_inc(ipnet_stack_t * ips)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
ipnet_walkers_dec(ipnet_stack_t * ips)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
ipobs_bounce_func(hook_event_token_t token,hook_data_t info,void * arg)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 *
ipobs_register_hook(netstack_t * ns,pfv_t func)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
ipobs_unregister_hook(netstack_t * ns,hook_t * hook)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 *
ipnet_find_by_zoneid(zoneid_t zoneid)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
ipnet_rele(ipnet_stack_t * ips)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
ipnet_set_itap(bpf_itap_fn_t tapfunc)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
ipnet_open_byname(const char * name,ipnetif_t ** ptr,zoneid_t zoneid)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
ipnet_close_byhandle(ipnetif_t * ifp)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 *
ipnet_name(ipnetif_t * ifp)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
ipnet_get_linkid_byname(const char * name,uint_t * idp,zoneid_t zoneid)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
ipnet_client_open(ipnetif_t * ptr,ipnetif_t ** result)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
ipnet_client_close(ipnetif_t * ptr)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
ipnet_promisc_add(void * handle,uint_t how,void * data,uintptr_t * mhandle,int flags)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
ipnet_promisc_remove(void * data)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
ipnet_bpf_bounce(hook_event_token_t token,hook_data_t info,void * arg)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 *
ipnetif_clone_create(ipnetif_t * ifp,zoneid_t zoneid)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
ipnetif_clone_release(ipnetif_t * ipnetif)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