xref: /illumos-gate/usr/src/uts/common/inet/ipf/solaris.c (revision 2aeafac3612e19716bf8164f89c3c9196342979c)
1 /*
2  * Copyright (C) 1993-2001, 2003 by Darren Reed.
3  *
4  * See the IPFILTER.LICENCE file for details on licencing.
5  *
6  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
7  * Use is subject to license terms.
8  *
9  * Copyright (c) 2014, Joyent, Inc.  All rights reserved.
10  */
11 
12 /*
13  * ipfilter kernel module mutexes and locking:
14  *
15  * Enabling ipfilter creates a per-netstack ipf_stack_t object that is
16  * stored in the ipf_stacks list, which is protected by ipf_stack_lock.
17  * ipf_stack_t objects are accessed in three contexts:
18  *
19  * 1) administering that filter (eg: ioctls handled with iplioctl())
20  * 2) reading log data (eg: iplread() / iplwrite())
21  * 3) filtering packets (eg: ipf_hook4_* and ipf_hook6_* pfhooks
22  *    functions)
23  *
24  * Each ipf_stack_t has a RW lock, ifs_ipf_global, protecting access to the
25  * whole structure. The structure also has locks protecting the various
26  * data structures used for filtering. The following guidelines should be
27  * followed for ipf_stack_t locks:
28  *
29  * - ipf_stack_lock must be held when accessing the ipf_stacks list
30  * - ipf_stack_lock should be held before acquiring ifs_ipf_global for
31  *   a stack (the exception to this is ipf_stack_destroy(), which removes
32  *   the ipf_stack_t from the list, then drops ipf_stack_lock before
33  *   acquiring ifs_ipf_global)
34  * - ifs_ipf_global must be held when accessing an ipf_stack_t in that list:
35  *   - The write lock is held only during stack creation / destruction
36  *   - The read lock should be held for all other accesses
37  * - To alter the filtering data in the administrative context, one must:
38  *   - acquire the read lock for ifs_ipf_global
39  *   - then acquire the write lock for the data in question
40  * - In the filtering path, the read lock needs to be held for each type of
41  *   filtering data used
42  * - ifs_ipf_global does not need to be held in the filtering path:
43  *   - The filtering hooks don't need to modify the stack itself
44  *   - The ipf_stack_t will not be destroyed until the hooks are unregistered.
45  *     This requires a write lock on the hook, ensuring that no active hooks
46  *     (eg: the filtering path) are running, and that the hooks won't be run
47  *     afterward.
48  *
49  * Note that there is a deadlock possible when calling net_hook_register()
50  * or net_hook_unregister() with ifs_ipf_global held: see the comments in
51  * iplattach() and ipldetach() for details.
52  */
53 
54 #include <sys/systm.h>
55 #include <sys/types.h>
56 #include <sys/param.h>
57 #include <sys/errno.h>
58 #include <sys/uio.h>
59 #include <sys/buf.h>
60 #include <sys/modctl.h>
61 #include <sys/open.h>
62 #include <sys/kmem.h>
63 #include <sys/conf.h>
64 #include <sys/cmn_err.h>
65 #include <sys/stat.h>
66 #include <sys/cred.h>
67 #include <sys/dditypes.h>
68 #include <sys/poll.h>
69 #include <sys/autoconf.h>
70 #include <sys/byteorder.h>
71 #include <sys/socket.h>
72 #include <sys/dlpi.h>
73 #include <sys/stropts.h>
74 #include <sys/kstat.h>
75 #include <sys/sockio.h>
76 #include <sys/neti.h>
77 #include <sys/hook.h>
78 #include <net/if.h>
79 #if SOLARIS2 >= 6
80 #include <net/if_types.h>
81 #endif
82 #include <net/af.h>
83 #include <net/route.h>
84 #include <netinet/in.h>
85 #include <netinet/in_systm.h>
86 #include <netinet/if_ether.h>
87 #include <netinet/ip.h>
88 #include <netinet/ip_var.h>
89 #include <netinet/tcp.h>
90 #include <netinet/udp.h>
91 #include <netinet/tcpip.h>
92 #include <netinet/ip_icmp.h>
93 #include <sys/ddi.h>
94 #include <sys/sunddi.h>
95 #include "netinet/ip_compat.h"
96 #include "netinet/ipl.h"
97 #include "netinet/ip_fil.h"
98 #include "netinet/ip_nat.h"
99 #include "netinet/ip_frag.h"
100 #include "netinet/ip_auth.h"
101 #include "netinet/ip_state.h"
102 #include "netinet/ipf_stack.h"
103 
104 extern	int	iplwrite __P((dev_t, struct uio *, cred_t *));
105 
106 static	int	ipf_getinfo __P((dev_info_t *, ddi_info_cmd_t,
107 		    void *, void **));
108 #if SOLARIS2 < 10
109 static	int	ipf_identify __P((dev_info_t *));
110 #endif
111 static	int	ipf_attach __P((dev_info_t *, ddi_attach_cmd_t));
112 static	int	ipf_detach __P((dev_info_t *, ddi_detach_cmd_t));
113 static	void	*ipf_stack_create __P((const netid_t));
114 static	void	ipf_stack_destroy __P((const netid_t, void *));
115 static	void	ipf_stack_shutdown __P((const netid_t, void *));
116 static	int	ipf_property_g_update __P((dev_info_t *));
117 static	char	*ipf_devfiles[] = { IPL_NAME, IPNAT_NAME, IPSTATE_NAME,
118 				    IPAUTH_NAME, IPSYNC_NAME, IPSCAN_NAME,
119 				    IPLOOKUP_NAME, NULL };
120 extern void 	*ipf_state;	/* DDI state */
121 extern vmem_t	*ipf_minor;	/* minor number arena */
122 
123 static struct cb_ops ipf_cb_ops = {
124 	iplopen,
125 	iplclose,
126 	nodev,		/* strategy */
127 	nodev,		/* print */
128 	nodev,		/* dump */
129 	iplread,
130 	iplwrite,	/* write */
131 	iplioctl,	/* ioctl */
132 	nodev,		/* devmap */
133 	nodev,		/* mmap */
134 	nodev,		/* segmap */
135 	nochpoll,	/* poll */
136 	ddi_prop_op,
137 	NULL,
138 	D_MTSAFE,
139 #if SOLARIS2 > 4
140 	CB_REV,
141 	nodev,		/* aread */
142 	nodev,		/* awrite */
143 #endif
144 };
145 
146 static struct dev_ops ipf_ops = {
147 	DEVO_REV,
148 	0,
149 	ipf_getinfo,
150 #if SOLARIS2 >= 10
151 	nulldev,
152 #else
153 	ipf_identify,
154 #endif
155 	nulldev,
156 	ipf_attach,
157 	ipf_detach,
158 	nodev,		/* reset */
159 	&ipf_cb_ops,
160 	(struct bus_ops *)0,
161 	NULL,
162 	ddi_quiesce_not_needed,		/* quiesce */
163 };
164 
165 
166 static net_instance_t *ipfncb = NULL;
167 static ipf_stack_t *ipf_stacks = NULL;
168 static kmutex_t ipf_stack_lock;
169 extern struct mod_ops mod_driverops;
170 static struct modldrv iplmod = {
171 	&mod_driverops, IPL_VERSION, &ipf_ops };
172 static struct modlinkage modlink1 = { MODREV_1, &iplmod, NULL };
173 
174 #if SOLARIS2 >= 6
175 static	size_t	hdrsizes[57][2] = {
176 	{ 0, 0 },
177 	{ IFT_OTHER, 0 },
178 	{ IFT_1822, 0 },
179 	{ IFT_HDH1822, 0 },
180 	{ IFT_X25DDN, 0 },
181 	{ IFT_X25, 0 },
182 	{ IFT_ETHER, 14 },
183 	{ IFT_ISO88023, 0 },
184 	{ IFT_ISO88024, 0 },
185 	{ IFT_ISO88025, 0 },
186 	{ IFT_ISO88026, 0 },
187 	{ IFT_STARLAN, 0 },
188 	{ IFT_P10, 0 },
189 	{ IFT_P80, 0 },
190 	{ IFT_HY, 0 },
191 	{ IFT_FDDI, 24 },
192 	{ IFT_LAPB, 0 },
193 	{ IFT_SDLC, 0 },
194 	{ IFT_T1, 0 },
195 	{ IFT_CEPT, 0 },
196 	{ IFT_ISDNBASIC, 0 },
197 	{ IFT_ISDNPRIMARY, 0 },
198 	{ IFT_PTPSERIAL, 0 },
199 	{ IFT_PPP, 0 },
200 	{ IFT_LOOP, 0 },
201 	{ IFT_EON, 0 },
202 	{ IFT_XETHER, 0 },
203 	{ IFT_NSIP, 0 },
204 	{ IFT_SLIP, 0 },
205 	{ IFT_ULTRA, 0 },
206 	{ IFT_DS3, 0 },
207 	{ IFT_SIP, 0 },
208 	{ IFT_FRELAY, 0 },
209 	{ IFT_RS232, 0 },
210 	{ IFT_PARA, 0 },
211 	{ IFT_ARCNET, 0 },
212 	{ IFT_ARCNETPLUS, 0 },
213 	{ IFT_ATM, 0 },
214 	{ IFT_MIOX25, 0 },
215 	{ IFT_SONET, 0 },
216 	{ IFT_X25PLE, 0 },
217 	{ IFT_ISO88022LLC, 0 },
218 	{ IFT_LOCALTALK, 0 },
219 	{ IFT_SMDSDXI, 0 },
220 	{ IFT_FRELAYDCE, 0 },
221 	{ IFT_V35, 0 },
222 	{ IFT_HSSI, 0 },
223 	{ IFT_HIPPI, 0 },
224 	{ IFT_MODEM, 0 },
225 	{ IFT_AAL5, 0 },
226 	{ IFT_SONETPATH, 0 },
227 	{ IFT_SONETVT, 0 },
228 	{ IFT_SMDSICIP, 0 },
229 	{ IFT_PROPVIRTUAL, 0 },
230 	{ IFT_PROPMUX, 0 },
231 };
232 #endif /* SOLARIS2 >= 6 */
233 
234 dev_info_t *ipf_dev_info = NULL;
235 
236 static const filter_kstats_t ipf_kstat_tmp = {
237 	{ "pass",			KSTAT_DATA_ULONG },
238 	{ "block",			KSTAT_DATA_ULONG },
239 	{ "nomatch",			KSTAT_DATA_ULONG },
240 	{ "short",			KSTAT_DATA_ULONG },
241 	{ "pass, logged",		KSTAT_DATA_ULONG },
242 	{ "block, logged",		KSTAT_DATA_ULONG },
243 	{ "nomatch, logged",		KSTAT_DATA_ULONG },
244 	{ "logged",			KSTAT_DATA_ULONG },
245 	{ "skip",			KSTAT_DATA_ULONG },
246 	{ "return sent",		KSTAT_DATA_ULONG },
247 	{ "acct",			KSTAT_DATA_ULONG },
248 	{ "bad frag state alloc",	KSTAT_DATA_ULONG },
249 	{ "new frag state kept",	KSTAT_DATA_ULONG },
250 	{ "new frag state compl. pkt",	KSTAT_DATA_ULONG },
251 	{ "bad pkt state alloc",	KSTAT_DATA_ULONG },
252 	{ "new pkt kept state",		KSTAT_DATA_ULONG },
253 	{ "cachehit",			KSTAT_DATA_ULONG },
254 	{ "tcp cksum bad",		KSTAT_DATA_ULONG },
255 	{{ "pullup ok",			KSTAT_DATA_ULONG },
256 	{ "pullup nok",			KSTAT_DATA_ULONG }},
257 	{ "src != route",		KSTAT_DATA_ULONG },
258 	{ "ttl invalid",		KSTAT_DATA_ULONG },
259 	{ "bad ip pkt",			KSTAT_DATA_ULONG },
260 	{ "ipv6 pkt",			KSTAT_DATA_ULONG },
261 	{ "dropped:pps ceiling",	KSTAT_DATA_ULONG },
262 	{ "ip upd. fail",		KSTAT_DATA_ULONG }
263 };
264 
265 
266 static int	ipf_kstat_update(kstat_t *ksp, int rwflag);
267 
268 static void
269 ipf_kstat_init(ipf_stack_t *ifs, boolean_t from_gz)
270 {
271 	ifs->ifs_kstatp[0] = net_kstat_create(ifs->ifs_netid,
272 	    (from_gz ? "ipf_gz" : "ipf"),
273 	    0, "inbound", "net", KSTAT_TYPE_NAMED,
274 	    sizeof (filter_kstats_t) / sizeof (kstat_named_t), 0);
275 	if (ifs->ifs_kstatp[0] != NULL) {
276 		bcopy(&ipf_kstat_tmp, ifs->ifs_kstatp[0]->ks_data,
277 		    sizeof (filter_kstats_t));
278 		ifs->ifs_kstatp[0]->ks_update = ipf_kstat_update;
279 		ifs->ifs_kstatp[0]->ks_private = &ifs->ifs_frstats[0];
280 		kstat_install(ifs->ifs_kstatp[0]);
281 	}
282 
283 	ifs->ifs_kstatp[1] = net_kstat_create(ifs->ifs_netid,
284 	    (from_gz ? "ipf_gz" : "ipf"),
285 	    0, "outbound", "net", KSTAT_TYPE_NAMED,
286 	    sizeof (filter_kstats_t) / sizeof (kstat_named_t), 0);
287 	if (ifs->ifs_kstatp[1] != NULL) {
288 		bcopy(&ipf_kstat_tmp, ifs->ifs_kstatp[1]->ks_data,
289 		    sizeof (filter_kstats_t));
290 		ifs->ifs_kstatp[1]->ks_update = ipf_kstat_update;
291 		ifs->ifs_kstatp[1]->ks_private = &ifs->ifs_frstats[1];
292 		kstat_install(ifs->ifs_kstatp[1]);
293 	}
294 
295 #ifdef	IPFDEBUG
296 	cmn_err(CE_NOTE, "IP Filter: ipf_kstat_init(%p) installed %p, %p",
297 	    ifs, ifs->ifs_kstatp[0], ifs->ifs_kstatp[1]);
298 #endif
299 }
300 
301 
302 static void
303 ipf_kstat_fini(ipf_stack_t *ifs)
304 {
305 	int i;
306 
307 	for (i = 0; i < 2; i++) {
308 		if (ifs->ifs_kstatp[i] != NULL) {
309 			net_kstat_delete(ifs->ifs_netid, ifs->ifs_kstatp[i]);
310 			ifs->ifs_kstatp[i] = NULL;
311 		}
312 	}
313 }
314 
315 
316 static int
317 ipf_kstat_update(kstat_t *ksp, int rwflag)
318 {
319 	filter_kstats_t	*fkp;
320 	filterstats_t	*fsp;
321 
322 	if (ksp == NULL || ksp->ks_data == NULL)
323 		return (EIO);
324 
325 	if (rwflag == KSTAT_WRITE)
326 		return (EACCES);
327 
328 	fkp = ksp->ks_data;
329 	fsp = ksp->ks_private;
330 
331 	fkp->fks_pass.value.ul		= fsp->fr_pass;
332 	fkp->fks_block.value.ul		= fsp->fr_block;
333 	fkp->fks_nom.value.ul		= fsp->fr_nom;
334 	fkp->fks_short.value.ul		= fsp->fr_short;
335 	fkp->fks_ppkl.value.ul		= fsp->fr_ppkl;
336 	fkp->fks_bpkl.value.ul		= fsp->fr_bpkl;
337 	fkp->fks_npkl.value.ul		= fsp->fr_npkl;
338 	fkp->fks_pkl.value.ul		= fsp->fr_pkl;
339 	fkp->fks_skip.value.ul		= fsp->fr_skip;
340 	fkp->fks_ret.value.ul		= fsp->fr_ret;
341 	fkp->fks_acct.value.ul		= fsp->fr_acct;
342 	fkp->fks_bnfr.value.ul		= fsp->fr_bnfr;
343 	fkp->fks_nfr.value.ul		= fsp->fr_nfr;
344 	fkp->fks_cfr.value.ul		= fsp->fr_cfr;
345 	fkp->fks_bads.value.ul		= fsp->fr_bads;
346 	fkp->fks_ads.value.ul		= fsp->fr_ads;
347 	fkp->fks_chit.value.ul		= fsp->fr_chit;
348 	fkp->fks_tcpbad.value.ul 	= fsp->fr_tcpbad;
349 	fkp->fks_pull[0].value.ul 	= fsp->fr_pull[0];
350 	fkp->fks_pull[1].value.ul 	= fsp->fr_pull[1];
351 	fkp->fks_badsrc.value.ul 	= fsp->fr_badsrc;
352 	fkp->fks_badttl.value.ul 	= fsp->fr_badttl;
353 	fkp->fks_bad.value.ul		= fsp->fr_bad;
354 	fkp->fks_ipv6.value.ul		= fsp->fr_ipv6;
355 	fkp->fks_ppshit.value.ul 	= fsp->fr_ppshit;
356 	fkp->fks_ipud.value.ul		= fsp->fr_ipud;
357 
358 	return (0);
359 }
360 
361 int
362 _init()
363 {
364 	int ipfinst;
365 
366 	ipfinst = mod_install(&modlink1);
367 #ifdef	IPFDEBUG
368 	cmn_err(CE_NOTE, "IP Filter: _init() = %d", ipfinst);
369 #endif
370 	mutex_init(&ipf_stack_lock, NULL, MUTEX_DRIVER, NULL);
371 	return (ipfinst);
372 }
373 
374 
375 int
376 _fini(void)
377 {
378 	int ipfinst;
379 
380 	ipfinst = mod_remove(&modlink1);
381 #ifdef	IPFDEBUG
382 	cmn_err(CE_NOTE, "IP Filter: _fini() = %d", ipfinst);
383 #endif
384 	return (ipfinst);
385 }
386 
387 
388 int
389 _info(modinfop)
390 struct modinfo *modinfop;
391 {
392 	int ipfinst;
393 
394 	ipfinst = mod_info(&modlink1, modinfop);
395 #ifdef	IPFDEBUG
396 	cmn_err(CE_NOTE, "IP Filter: _info(%p) = %d", modinfop, ipfinst);
397 #endif
398 	return (ipfinst);
399 }
400 
401 
402 #if SOLARIS2 < 10
403 static int ipf_identify(dip)
404 dev_info_t *dip;
405 {
406 #ifdef	IPFDEBUG
407 	cmn_err(CE_NOTE, "IP Filter: ipf_identify(%p)", dip);
408 #endif
409 	if (strcmp(ddi_get_name(dip), "ipf") == 0)
410 		return (DDI_IDENTIFIED);
411 	return (DDI_NOT_IDENTIFIED);
412 }
413 #endif
414 
415 /*
416  * Initialize things for IPF for each stack instance
417  */
418 static void *
419 ipf_stack_create_one(const netid_t id, const zoneid_t zid, boolean_t from_gz,
420     ipf_stack_t *ifs_gz)
421 {
422 	ipf_stack_t	*ifs;
423 
424 #ifdef IPFDEBUG
425 	cmn_err(CE_NOTE, "IP Filter:stack_create_one id=%d global=%d", id,
426 	    global);
427 #endif
428 
429 	ifs = (ipf_stack_t *)kmem_alloc(sizeof (*ifs), KM_SLEEP);
430 	bzero(ifs, sizeof (*ifs));
431 
432 	ifs->ifs_hook4_physical_in	= B_FALSE;
433 	ifs->ifs_hook4_physical_out	= B_FALSE;
434 	ifs->ifs_hook4_nic_events	= B_FALSE;
435 	ifs->ifs_hook4_loopback_in	= B_FALSE;
436 	ifs->ifs_hook4_loopback_out	= B_FALSE;
437 	ifs->ifs_hook6_physical_in	= B_FALSE;
438 	ifs->ifs_hook6_physical_out	= B_FALSE;
439 	ifs->ifs_hook6_nic_events	= B_FALSE;
440 	ifs->ifs_hook6_loopback_in	= B_FALSE;
441 	ifs->ifs_hook6_loopback_out	= B_FALSE;
442 
443 	/*
444 	 * Initialize mutex's
445 	 */
446 	RWLOCK_INIT(&ifs->ifs_ipf_global, "ipf filter load/unload mutex");
447 	RWLOCK_INIT(&ifs->ifs_ipf_mutex, "ipf filter rwlock");
448 	RWLOCK_INIT(&ifs->ifs_ipf_frcache, "ipf cache rwlock");
449 	ifs->ifs_netid = id;
450 	ifs->ifs_zone = zid;
451 	ifs->ifs_gz_controlled = from_gz;
452 	ifs->ifs_gz_cont_ifs = ifs_gz;
453 
454 	ipf_kstat_init(ifs, from_gz);
455 
456 #ifdef IPFDEBUG
457 	cmn_err(CE_CONT, "IP Filter:stack_create zone=%d", ifs->ifs_zone);
458 #endif
459 
460 	/*
461 	 * Lock people out while we set things up.
462 	 */
463 	WRITE_ENTER(&ifs->ifs_ipf_global);
464 	ipftuneable_alloc(ifs);
465 	RWLOCK_EXIT(&ifs->ifs_ipf_global);
466 
467 	/* Limit to global stack */
468 	if (ifs->ifs_zone == GLOBAL_ZONEID)
469 		cmn_err(CE_CONT, "!%s, running.\n", ipfilter_version);
470 
471 	mutex_enter(&ipf_stack_lock);
472 	if (ipf_stacks != NULL)
473 		ipf_stacks->ifs_pnext = &ifs->ifs_next;
474 	ifs->ifs_next = ipf_stacks;
475 	ifs->ifs_pnext = &ipf_stacks;
476 	ipf_stacks = ifs;
477 	mutex_exit(&ipf_stack_lock);
478 
479 	return (ifs);
480 }
481 
482 static void *
483 ipf_stack_create(const netid_t id)
484 {
485 	ipf_stack_t	*ifs = NULL;
486 	zoneid_t	zid = net_getzoneidbynetid(id);
487 
488 	/*
489 	 * Create two ipfilter stacks for a zone - the first can only be
490 	 * controlled from the global zone, and the second is owned by
491 	 * the zone itself. There is no need to create a GZ-controlled
492 	 * stack for the global zone, since we're already in the global
493 	 * zone. See the "GZ-controlled and per-zone stacks" comment block in
494 	 * ip_fil_solaris.c for details.
495 	 */
496 	if (zid != GLOBAL_ZONEID)
497 		ifs = ipf_stack_create_one(id, zid, B_TRUE, NULL);
498 
499 	return (ipf_stack_create_one(id, zid, B_FALSE, ifs));
500 }
501 
502 /*
503  * Find an ipfilter stack for the given zone. Return the GZ-controlled or
504  * per-zone stack if set by an earlier SIOCIPFZONESET ioctl call. See the
505  * "GZ-controlled and per-zone stacks" comment block in ip_fil_solaris.c for
506  * details.
507  *
508  * This function returns with the ipf_stack_t's ifs_ipf_global
509  * read lock held (if the stack is found). See the "ipfilter kernel module
510  * mutexes and locking" comment block at the top of this file.
511  */
512 ipf_stack_t *
513 ipf_find_stack(const zoneid_t orig_zone, ipf_devstate_t *isp)
514 {
515 	ipf_stack_t *ifs;
516 	boolean_t gz_stack;
517 	zoneid_t zone;
518 
519 	/*
520 	 * If we're in the GZ, determine if we're acting on a zone's stack,
521 	 * and whether or not that stack is the GZ-controlled or in-zone
522 	 * one.  See the "GZ and per-zone stacks" note at the top of this
523 	 * file.
524 	 */
525 	if (orig_zone == GLOBAL_ZONEID &&
526 	    (isp->ipfs_zoneid != IPFS_ZONE_UNSET)) {
527 		/* Global zone, and we've set the zoneid for this fd already */
528 
529 		if (orig_zone == isp->ipfs_zoneid) {
530 			/* There's only a per-zone stack for the GZ */
531 			gz_stack = B_FALSE;
532 		} else {
533 			gz_stack = isp->ipfs_gz;
534 		}
535 
536 		zone = isp->ipfs_zoneid;
537 	} else {
538 		/*
539 		 * Non-global zone or GZ without having set a zoneid: act on
540 		 * the per-zone stack of the zone that this ioctl originated
541 		 * from.
542 		 */
543 		gz_stack = B_FALSE;
544 		zone = orig_zone;
545 	}
546 
547 	mutex_enter(&ipf_stack_lock);
548 	for (ifs = ipf_stacks; ifs != NULL; ifs = ifs->ifs_next) {
549 		if (ifs->ifs_zone == zone && ifs->ifs_gz_controlled == gz_stack)
550 			break;
551 	}
552 
553 	if (ifs != NULL) {
554 		READ_ENTER(&ifs->ifs_ipf_global);
555 	}
556 	mutex_exit(&ipf_stack_lock);
557 	return (ifs);
558 }
559 
560 static int ipf_detach_check_zone(ipf_stack_t *ifs)
561 {
562 	/*
563 	 * Make sure we're the only one's modifying things.  With
564 	 * this lock others should just fall out of the loop.
565 	 */
566 	READ_ENTER(&ifs->ifs_ipf_global);
567 	if (ifs->ifs_fr_running == 1) {
568 		RWLOCK_EXIT(&ifs->ifs_ipf_global);
569 		return (-1);
570 	}
571 
572 	/*
573 	 * Make sure there is no active filter rule.
574 	 */
575 	if (ifs->ifs_ipfilter[0][ifs->ifs_fr_active] ||
576 	    ifs->ifs_ipfilter[1][ifs->ifs_fr_active] ||
577 	    ifs->ifs_ipfilter6[0][ifs->ifs_fr_active] ||
578 	    ifs->ifs_ipfilter6[1][ifs->ifs_fr_active]) {
579 		RWLOCK_EXIT(&ifs->ifs_ipf_global);
580 		return (-1);
581 	}
582 
583 	RWLOCK_EXIT(&ifs->ifs_ipf_global);
584 
585 	return (0);
586 }
587 
588 
589 static int ipf_detach_check_all()
590 {
591 	ipf_stack_t *ifs;
592 
593 	mutex_enter(&ipf_stack_lock);
594 	for (ifs = ipf_stacks; ifs != NULL; ifs = ifs->ifs_next)
595 		if (ipf_detach_check_zone(ifs) != 0)
596 			break;
597 	mutex_exit(&ipf_stack_lock);
598 	return ((ifs == NULL) ? 0 : -1);
599 }
600 
601 
602 /*
603  * Remove ipf kstats for both the per-zone ipf stack and the
604  * GZ-controlled stack for the same zone, if it exists.
605  */
606 /* ARGSUSED */
607 static void
608 ipf_stack_shutdown(const netid_t id, void *arg)
609 {
610 	ipf_stack_t *ifs = (ipf_stack_t *)arg;
611 
612 	/*
613 	 * The GZ-controlled stack
614 	 */
615 	if (ifs->ifs_gz_cont_ifs != NULL)
616 		ipf_kstat_fini(ifs->ifs_gz_cont_ifs);
617 
618 	/*
619 	 * The per-zone stack
620 	 */
621 	ipf_kstat_fini(ifs);
622 }
623 
624 
625 /*
626  * Destroy things for ipf for one stack.
627  */
628 /* ARGSUSED */
629 static void
630 ipf_stack_destroy_one(const netid_t id, ipf_stack_t *ifs)
631 {
632 	timeout_id_t tid;
633 
634 #ifdef IPFDEBUG
635 	(void) printf("ipf_stack_destroy_one(%p)\n", (void *)ifs);
636 #endif
637 
638 	/*
639 	 * Make sure we're the only one's modifying things.  With
640 	 * this lock others should just fall out of the loop.
641 	 */
642 	WRITE_ENTER(&ifs->ifs_ipf_global);
643 	if (ifs->ifs_fr_running == -2) {
644 		RWLOCK_EXIT(&ifs->ifs_ipf_global);
645 		return;
646 	}
647 	ifs->ifs_fr_running = -2;
648 	tid = ifs->ifs_fr_timer_id;
649 	ifs->ifs_fr_timer_id = NULL;
650 	RWLOCK_EXIT(&ifs->ifs_ipf_global);
651 
652 	mutex_enter(&ipf_stack_lock);
653 	if (ifs->ifs_next != NULL)
654 		ifs->ifs_next->ifs_pnext = ifs->ifs_pnext;
655 	*ifs->ifs_pnext = ifs->ifs_next;
656 	mutex_exit(&ipf_stack_lock);
657 
658 	if (tid != NULL)
659 		(void) untimeout(tid);
660 
661 	WRITE_ENTER(&ifs->ifs_ipf_global);
662 	if (ipldetach(ifs) != 0) {
663 		printf("ipf_stack_destroy_one: ipldetach failed\n");
664 	}
665 
666 	ipftuneable_free(ifs);
667 
668 	RWLOCK_EXIT(&ifs->ifs_ipf_global);
669 	RW_DESTROY(&ifs->ifs_ipf_mutex);
670 	RW_DESTROY(&ifs->ifs_ipf_frcache);
671 	RW_DESTROY(&ifs->ifs_ipf_global);
672 
673 	KFREE(ifs);
674 }
675 
676 
677 /*
678  * Destroy things for ipf for both the per-zone ipf stack and the
679  * GZ-controlled stack for the same zone, if it exists. See the "GZ-controlled
680  * and per-zone stacks" comment block in ip_fil_solaris.c for details.
681  */
682 /* ARGSUSED */
683 static void
684 ipf_stack_destroy(const netid_t id, void *arg)
685 {
686 	ipf_stack_t *ifs = (ipf_stack_t *)arg;
687 
688 	/*
689 	 * The GZ-controlled stack
690 	 */
691 	if (ifs->ifs_gz_cont_ifs != NULL)
692 		ipf_stack_destroy_one(id, ifs->ifs_gz_cont_ifs);
693 
694 	/*
695 	 * The per-zone stack
696 	 */
697 	ipf_stack_destroy_one(id, ifs);
698 }
699 
700 
701 static int ipf_attach(dip, cmd)
702 dev_info_t *dip;
703 ddi_attach_cmd_t cmd;
704 {
705 	char *s;
706 	int i;
707 	int instance;
708 
709 #ifdef	IPFDEBUG
710 	cmn_err(CE_NOTE, "IP Filter: ipf_attach(%p,%x)", dip, cmd);
711 #endif
712 
713 	switch (cmd)
714 	{
715 	case DDI_ATTACH:
716 		instance = ddi_get_instance(dip);
717 		/* Only one instance of ipf (instance 0) can be attached. */
718 		if (instance > 0)
719 			return (DDI_FAILURE);
720 
721 #ifdef	IPFDEBUG
722 		cmn_err(CE_CONT, "IP Filter: attach ipf instance %d", instance);
723 #endif
724 
725 		(void) ipf_property_g_update(dip);
726 
727 		if (ddi_soft_state_init(&ipf_state, sizeof (ipf_devstate_t), 1)
728 		    != 0) {
729 			ddi_prop_remove_all(dip);
730 			return (DDI_FAILURE);
731 		}
732 
733 		for (i = 0; ((s = ipf_devfiles[i]) != NULL); i++) {
734 			s = strrchr(s, '/');
735 			if (s == NULL)
736 				continue;
737 			s++;
738 			if (ddi_create_minor_node(dip, s, S_IFCHR, i,
739 			    DDI_PSEUDO, 0) == DDI_FAILURE)
740 				goto attach_failed;
741 		}
742 
743 		ipf_dev_info = dip;
744 
745 		ipfncb = net_instance_alloc(NETINFO_VERSION);
746 		if (ipfncb == NULL)
747 			goto attach_failed;
748 
749 		ipfncb->nin_name = "ipf";
750 		ipfncb->nin_create = ipf_stack_create;
751 		ipfncb->nin_destroy = ipf_stack_destroy;
752 		ipfncb->nin_shutdown = ipf_stack_shutdown;
753 		if (net_instance_register(ipfncb) == DDI_FAILURE) {
754 			net_instance_free(ipfncb);
755 			goto attach_failed;
756 		}
757 
758 		ipf_minor = vmem_create("ipf_minor", (void *)1, UINT32_MAX - 1,
759 		    1, NULL, NULL, NULL, 0, VM_SLEEP | VMC_IDENTIFIER);
760 
761 #ifdef IPFDEBUG
762 		cmn_err(CE_CONT, "IP Filter:stack_create callback_reg=%d", i);
763 #endif
764 
765 		return (DDI_SUCCESS);
766 		/* NOTREACHED */
767 	default:
768 		break;
769 	}
770 
771 attach_failed:
772 	ddi_remove_minor_node(dip, NULL);
773 	ddi_prop_remove_all(dip);
774 	ddi_soft_state_fini(&ipf_state);
775 	return (DDI_FAILURE);
776 }
777 
778 
779 static int ipf_detach(dip, cmd)
780 dev_info_t *dip;
781 ddi_detach_cmd_t cmd;
782 {
783 	int i;
784 
785 #ifdef	IPFDEBUG
786 	cmn_err(CE_NOTE, "IP Filter: ipf_detach(%p,%x)", dip, cmd);
787 #endif
788 	switch (cmd) {
789 	case DDI_DETACH:
790 		if (ipf_detach_check_all() != 0)
791 			return (DDI_FAILURE);
792 
793 		/*
794 		 * Undo what we did in ipf_attach, freeing resources
795 		 * and removing things we installed.  The system
796 		 * framework guarantees we are not active with this devinfo
797 		 * node in any other entry points at this time.
798 		 */
799 		ddi_prop_remove_all(dip);
800 		i = ddi_get_instance(dip);
801 		ddi_remove_minor_node(dip, NULL);
802 		if (i > 0) {
803 			cmn_err(CE_CONT, "IP Filter: still attached (%d)\n", i);
804 			return (DDI_FAILURE);
805 		}
806 
807 		vmem_destroy(ipf_minor);
808 		ddi_soft_state_fini(&ipf_state);
809 
810 		(void) net_instance_unregister(ipfncb);
811 		net_instance_free(ipfncb);
812 
813 		return (DDI_SUCCESS);
814 		/* NOTREACHED */
815 	default:
816 		break;
817 	}
818 	cmn_err(CE_NOTE, "IP Filter: failed to detach\n");
819 	return (DDI_FAILURE);
820 }
821 
822 
823 /*ARGSUSED*/
824 static int ipf_getinfo(dip, infocmd, arg, result)
825 dev_info_t *dip;
826 ddi_info_cmd_t infocmd;
827 void *arg, **result;
828 {
829 	int error;
830 
831 	error = DDI_FAILURE;
832 #ifdef	IPFDEBUG
833 	cmn_err(CE_NOTE, "IP Filter: ipf_getinfo(%p,%x,%p)", dip, infocmd, arg);
834 #endif
835 	switch (infocmd) {
836 	case DDI_INFO_DEVT2DEVINFO:
837 		*result = ipf_dev_info;
838 		error = DDI_SUCCESS;
839 		break;
840 	case DDI_INFO_DEVT2INSTANCE:
841 		*result = (void *)0;
842 		error = DDI_SUCCESS;
843 		break;
844 	default:
845 		break;
846 	}
847 	return (error);
848 }
849 
850 
851 /*
852  * Fetch configuration file values that have been entered into the ipf.conf
853  * driver file.
854  */
855 static int ipf_property_g_update(dip)
856 dev_info_t *dip;
857 {
858 #ifdef DDI_NO_AUTODETACH
859 	if (ddi_prop_update_int(DDI_DEV_T_NONE, dip,
860 				DDI_NO_AUTODETACH, 1) != DDI_PROP_SUCCESS) {
861 		cmn_err(CE_WARN, "!updating DDI_NO_AUTODETACH failed");
862 		return (DDI_FAILURE);
863 	}
864 #else
865 	if (ddi_prop_update_int(DDI_DEV_T_NONE, dip,
866 				"ddi-no-autodetach", 1) != DDI_PROP_SUCCESS) {
867 		cmn_err(CE_WARN, "!updating ddi-no-autodetach failed");
868 		return (DDI_FAILURE);
869 	}
870 #endif
871 
872 	return (DDI_SUCCESS);
873 }
874 
875 int
876 ipf_property_update(dip, ifs)
877 dev_info_t *dip;
878 ipf_stack_t *ifs;
879 {
880 	ipftuneable_t *ipft;
881 	char *name;
882 	uint_t one;
883 	int *i32p;
884 	int err, rv = 0;
885 
886 	for (ipft = ifs->ifs_ipf_tuneables;
887 		(name = ipft->ipft_name) != NULL; ipft++) {
888 		one = 1;
889 		i32p = NULL;
890 		err = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip,
891 						0, name, &i32p, &one);
892 		if (err == DDI_PROP_NOT_FOUND)
893 			continue;
894 #ifdef	IPFDEBUG
895 		cmn_err(CE_CONT, "IP Filter: lookup_int(%s) = %d\n",
896 			name, err);
897 #endif
898 		if (err != DDI_PROP_SUCCESS) {
899 			rv = err;
900 			continue;
901 		}
902 
903 		if (*i32p >= ipft->ipft_min &&
904 		    *i32p <= ipft->ipft_max) {
905 			if (ipft->ipft_sz == sizeof (uint32_t)) {
906 				*ipft->ipft_pint = *i32p;
907 			} else if (ipft->ipft_sz == sizeof (uint64_t)) {
908 				*ipft->ipft_plong = *i32p;
909 			}
910 		}
911 
912 		ddi_prop_free(i32p);
913 	}
914 
915 	return (rv);
916 }
917