Home | History | Annotate | Download | only in ipf
      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 
     10 #include <sys/systm.h>
     11 #include <sys/types.h>
     12 #include <sys/param.h>
     13 #include <sys/errno.h>
     14 #include <sys/uio.h>
     15 #include <sys/buf.h>
     16 #include <sys/modctl.h>
     17 #include <sys/open.h>
     18 #include <sys/kmem.h>
     19 #include <sys/conf.h>
     20 #include <sys/cmn_err.h>
     21 #include <sys/stat.h>
     22 #include <sys/cred.h>
     23 #include <sys/dditypes.h>
     24 #include <sys/poll.h>
     25 #include <sys/autoconf.h>
     26 #include <sys/byteorder.h>
     27 #include <sys/socket.h>
     28 #include <sys/dlpi.h>
     29 #include <sys/stropts.h>
     30 #include <sys/kstat.h>
     31 #include <sys/sockio.h>
     32 #include <sys/neti.h>
     33 #include <sys/hook.h>
     34 #include <net/if.h>
     35 #if SOLARIS2 >= 6
     36 #include <net/if_types.h>
     37 #endif
     38 #include <net/af.h>
     39 #include <net/route.h>
     40 #include <netinet/in.h>
     41 #include <netinet/in_systm.h>
     42 #include <netinet/if_ether.h>
     43 #include <netinet/ip.h>
     44 #include <netinet/ip_var.h>
     45 #include <netinet/tcp.h>
     46 #include <netinet/udp.h>
     47 #include <netinet/tcpip.h>
     48 #include <netinet/ip_icmp.h>
     49 #include <sys/ddi.h>
     50 #include <sys/sunddi.h>
     51 #include "netinet/ip_compat.h"
     52 #include "netinet/ipl.h"
     53 #include "netinet/ip_fil.h"
     54 #include "netinet/ip_nat.h"
     55 #include "netinet/ip_frag.h"
     56 #include "netinet/ip_auth.h"
     57 #include "netinet/ip_state.h"
     58 #include "netinet/ipf_stack.h"
     59 
     60 extern	int	iplwrite __P((dev_t, struct uio *, cred_t *));
     61 
     62 static	int	ipf_getinfo __P((dev_info_t *, ddi_info_cmd_t,
     63 		    void *, void **));
     64 #if SOLARIS2 < 10
     65 static	int	ipf_identify __P((dev_info_t *));
     66 #endif
     67 static	int	ipf_attach __P((dev_info_t *, ddi_attach_cmd_t));
     68 static	int	ipf_detach __P((dev_info_t *, ddi_detach_cmd_t));
     69 static	void	*ipf_stack_create __P((const netid_t));
     70 static	void	ipf_stack_destroy __P((const netid_t, void *));
     71 static	void	ipf_stack_shutdown __P((const netid_t, void *));
     72 static	int	ipf_property_g_update __P((dev_info_t *));
     73 static	char	*ipf_devfiles[] = { IPL_NAME, IPNAT_NAME, IPSTATE_NAME,
     74 				    IPAUTH_NAME, IPSYNC_NAME, IPSCAN_NAME,
     75 				    IPLOOKUP_NAME, NULL };
     76 
     77 
     78 static struct cb_ops ipf_cb_ops = {
     79 	iplopen,
     80 	iplclose,
     81 	nodev,		/* strategy */
     82 	nodev,		/* print */
     83 	nodev,		/* dump */
     84 	iplread,
     85 	iplwrite,	/* write */
     86 	iplioctl,	/* ioctl */
     87 	nodev,		/* devmap */
     88 	nodev,		/* mmap */
     89 	nodev,		/* segmap */
     90 	nochpoll,	/* poll */
     91 	ddi_prop_op,
     92 	NULL,
     93 	D_MTSAFE,
     94 #if SOLARIS2 > 4
     95 	CB_REV,
     96 	nodev,		/* aread */
     97 	nodev,		/* awrite */
     98 #endif
     99 };
    100 
    101 static struct dev_ops ipf_ops = {
    102 	DEVO_REV,
    103 	0,
    104 	ipf_getinfo,
    105 #if SOLARIS2 >= 10
    106 	nulldev,
    107 #else
    108 	ipf_identify,
    109 #endif
    110 	nulldev,
    111 	ipf_attach,
    112 	ipf_detach,
    113 	nodev,		/* reset */
    114 	&ipf_cb_ops,
    115 	(struct bus_ops *)0,
    116 	NULL,
    117 	ddi_quiesce_not_needed,		/* quiesce */
    118 };
    119 
    120 
    121 static net_instance_t *ipfncb = NULL;
    122 static ipf_stack_t *ipf_stacks = NULL;
    123 static kmutex_t ipf_stack_lock;
    124 extern struct mod_ops mod_driverops;
    125 static struct modldrv iplmod = {
    126 	&mod_driverops, IPL_VERSION, &ipf_ops };
    127 static struct modlinkage modlink1 = { MODREV_1, &iplmod, NULL };
    128 
    129 #if SOLARIS2 >= 6
    130 static	size_t	hdrsizes[57][2] = {
    131 	{ 0, 0 },
    132 	{ IFT_OTHER, 0 },
    133 	{ IFT_1822, 0 },
    134 	{ IFT_HDH1822, 0 },
    135 	{ IFT_X25DDN, 0 },
    136 	{ IFT_X25, 0 },
    137 	{ IFT_ETHER, 14 },
    138 	{ IFT_ISO88023, 0 },
    139 	{ IFT_ISO88024, 0 },
    140 	{ IFT_ISO88025, 0 },
    141 	{ IFT_ISO88026, 0 },
    142 	{ IFT_STARLAN, 0 },
    143 	{ IFT_P10, 0 },
    144 	{ IFT_P80, 0 },
    145 	{ IFT_HY, 0 },
    146 	{ IFT_FDDI, 24 },
    147 	{ IFT_LAPB, 0 },
    148 	{ IFT_SDLC, 0 },
    149 	{ IFT_T1, 0 },
    150 	{ IFT_CEPT, 0 },
    151 	{ IFT_ISDNBASIC, 0 },
    152 	{ IFT_ISDNPRIMARY, 0 },
    153 	{ IFT_PTPSERIAL, 0 },
    154 	{ IFT_PPP, 0 },
    155 	{ IFT_LOOP, 0 },
    156 	{ IFT_EON, 0 },
    157 	{ IFT_XETHER, 0 },
    158 	{ IFT_NSIP, 0 },
    159 	{ IFT_SLIP, 0 },
    160 	{ IFT_ULTRA, 0 },
    161 	{ IFT_DS3, 0 },
    162 	{ IFT_SIP, 0 },
    163 	{ IFT_FRELAY, 0 },
    164 	{ IFT_RS232, 0 },
    165 	{ IFT_PARA, 0 },
    166 	{ IFT_ARCNET, 0 },
    167 	{ IFT_ARCNETPLUS, 0 },
    168 	{ IFT_ATM, 0 },
    169 	{ IFT_MIOX25, 0 },
    170 	{ IFT_SONET, 0 },
    171 	{ IFT_X25PLE, 0 },
    172 	{ IFT_ISO88022LLC, 0 },
    173 	{ IFT_LOCALTALK, 0 },
    174 	{ IFT_SMDSDXI, 0 },
    175 	{ IFT_FRELAYDCE, 0 },
    176 	{ IFT_V35, 0 },
    177 	{ IFT_HSSI, 0 },
    178 	{ IFT_HIPPI, 0 },
    179 	{ IFT_MODEM, 0 },
    180 	{ IFT_AAL5, 0 },
    181 	{ IFT_SONETPATH, 0 },
    182 	{ IFT_SONETVT, 0 },
    183 	{ IFT_SMDSICIP, 0 },
    184 	{ IFT_PROPVIRTUAL, 0 },
    185 	{ IFT_PROPMUX, 0 },
    186 };
    187 #endif /* SOLARIS2 >= 6 */
    188 
    189 dev_info_t *ipf_dev_info = NULL;
    190 
    191 static const filter_kstats_t ipf_kstat_tmp = {
    192 	{ "pass",			KSTAT_DATA_ULONG },
    193 	{ "block",			KSTAT_DATA_ULONG },
    194 	{ "nomatch",			KSTAT_DATA_ULONG },
    195 	{ "short",			KSTAT_DATA_ULONG },
    196 	{ "pass, logged",		KSTAT_DATA_ULONG },
    197 	{ "block, logged",		KSTAT_DATA_ULONG },
    198 	{ "nomatch, logged",		KSTAT_DATA_ULONG },
    199 	{ "logged",			KSTAT_DATA_ULONG },
    200 	{ "skip",			KSTAT_DATA_ULONG },
    201 	{ "return sent",		KSTAT_DATA_ULONG },
    202 	{ "acct",			KSTAT_DATA_ULONG },
    203 	{ "bad frag state alloc",	KSTAT_DATA_ULONG },
    204 	{ "new frag state kept",	KSTAT_DATA_ULONG },
    205 	{ "new frag state compl. pkt",	KSTAT_DATA_ULONG },
    206 	{ "bad pkt state alloc",	KSTAT_DATA_ULONG },
    207 	{ "new pkt kept state",		KSTAT_DATA_ULONG },
    208 	{ "cachehit",			KSTAT_DATA_ULONG },
    209 	{ "tcp cksum bad",		KSTAT_DATA_ULONG },
    210 	{{ "pullup ok",			KSTAT_DATA_ULONG },
    211 	{ "pullup nok",			KSTAT_DATA_ULONG }},
    212 	{ "src != route",		KSTAT_DATA_ULONG },
    213 	{ "ttl invalid",		KSTAT_DATA_ULONG },
    214 	{ "bad ip pkt",			KSTAT_DATA_ULONG },
    215 	{ "ipv6 pkt",			KSTAT_DATA_ULONG },
    216 	{ "dropped:pps ceiling",	KSTAT_DATA_ULONG },
    217 	{ "ip upd. fail",		KSTAT_DATA_ULONG }
    218 };
    219 
    220 
    221 static int	ipf_kstat_update(kstat_t *ksp, int rwflag);
    222 
    223 static void
    224 ipf_kstat_init(ipf_stack_t *ifs)
    225 {
    226 	ifs->ifs_kstatp[0] = net_kstat_create(ifs->ifs_netid, "ipf", 0,
    227 	    "inbound", "net", KSTAT_TYPE_NAMED,
    228 	    sizeof (filter_kstats_t) / sizeof (kstat_named_t), 0);
    229 	if (ifs->ifs_kstatp[0] != NULL) {
    230 		bcopy(&ipf_kstat_tmp, ifs->ifs_kstatp[0]->ks_data,
    231 		    sizeof (filter_kstats_t));
    232 		ifs->ifs_kstatp[0]->ks_update = ipf_kstat_update;
    233 		ifs->ifs_kstatp[0]->ks_private = &ifs->ifs_frstats[0];
    234 		kstat_install(ifs->ifs_kstatp[0]);
    235 	}
    236 
    237 	ifs->ifs_kstatp[1] = net_kstat_create(ifs->ifs_netid, "ipf", 0,
    238 	    "outbound", "net", KSTAT_TYPE_NAMED,
    239 	    sizeof (filter_kstats_t) / sizeof (kstat_named_t), 0);
    240 	if (ifs->ifs_kstatp[1] != NULL) {
    241 		bcopy(&ipf_kstat_tmp, ifs->ifs_kstatp[1]->ks_data,
    242 		    sizeof (filter_kstats_t));
    243 		ifs->ifs_kstatp[1]->ks_update = ipf_kstat_update;
    244 		ifs->ifs_kstatp[1]->ks_private = &ifs->ifs_frstats[1];
    245 		kstat_install(ifs->ifs_kstatp[1]);
    246 	}
    247 
    248 #ifdef	IPFDEBUG
    249 	cmn_err(CE_NOTE, "IP Filter: ipf_kstat_init(%p) installed %p, %p",
    250 	    ifs, ifs->ifs_kstatp[0], ifs->ifs_kstatp[1]);
    251 #endif
    252 }
    253 
    254 
    255 static void
    256 ipf_kstat_fini(ipf_stack_t *ifs)
    257 {
    258 	int i;
    259 
    260 	for (i = 0; i < 2; i++) {
    261 		if (ifs->ifs_kstatp[i] != NULL) {
    262 			net_kstat_delete(ifs->ifs_netid, ifs->ifs_kstatp[i]);
    263 			ifs->ifs_kstatp[i] = NULL;
    264 		}
    265 	}
    266 }
    267 
    268 
    269 static int
    270 ipf_kstat_update(kstat_t *ksp, int rwflag)
    271 {
    272 	filter_kstats_t	*fkp;
    273 	filterstats_t	*fsp;
    274 
    275 	if (ksp == NULL || ksp->ks_data == NULL)
    276 		return (EIO);
    277 
    278 	if (rwflag == KSTAT_WRITE)
    279 		return (EACCES);
    280 
    281 	fkp = ksp->ks_data;
    282 	fsp = ksp->ks_private;
    283 
    284 	fkp->fks_pass.value.ul		= fsp->fr_pass;
    285 	fkp->fks_block.value.ul		= fsp->fr_block;
    286 	fkp->fks_nom.value.ul		= fsp->fr_nom;
    287 	fkp->fks_short.value.ul		= fsp->fr_short;
    288 	fkp->fks_ppkl.value.ul		= fsp->fr_ppkl;
    289 	fkp->fks_bpkl.value.ul		= fsp->fr_bpkl;
    290 	fkp->fks_npkl.value.ul		= fsp->fr_npkl;
    291 	fkp->fks_pkl.value.ul		= fsp->fr_pkl;
    292 	fkp->fks_skip.value.ul		= fsp->fr_skip;
    293 	fkp->fks_ret.value.ul		= fsp->fr_ret;
    294 	fkp->fks_acct.value.ul		= fsp->fr_acct;
    295 	fkp->fks_bnfr.value.ul		= fsp->fr_bnfr;
    296 	fkp->fks_nfr.value.ul		= fsp->fr_nfr;
    297 	fkp->fks_cfr.value.ul		= fsp->fr_cfr;
    298 	fkp->fks_bads.value.ul		= fsp->fr_bads;
    299 	fkp->fks_ads.value.ul		= fsp->fr_ads;
    300 	fkp->fks_chit.value.ul		= fsp->fr_chit;
    301 	fkp->fks_tcpbad.value.ul 	= fsp->fr_tcpbad;
    302 	fkp->fks_pull[0].value.ul 	= fsp->fr_pull[0];
    303 	fkp->fks_pull[1].value.ul 	= fsp->fr_pull[1];
    304 	fkp->fks_badsrc.value.ul 	= fsp->fr_badsrc;
    305 	fkp->fks_badttl.value.ul 	= fsp->fr_badttl;
    306 	fkp->fks_bad.value.ul		= fsp->fr_bad;
    307 	fkp->fks_ipv6.value.ul		= fsp->fr_ipv6;
    308 	fkp->fks_ppshit.value.ul 	= fsp->fr_ppshit;
    309 	fkp->fks_ipud.value.ul		= fsp->fr_ipud;
    310 
    311 	return (0);
    312 }
    313 
    314 int
    315 _init()
    316 {
    317 	int ipfinst;
    318 
    319 	ipfinst = mod_install(&modlink1);
    320 #ifdef	IPFDEBUG
    321 	cmn_err(CE_NOTE, "IP Filter: _init() = %d", ipfinst);
    322 #endif
    323 	mutex_init(&ipf_stack_lock, NULL, MUTEX_DRIVER, NULL);
    324 	return (ipfinst);
    325 }
    326 
    327 
    328 int
    329 _fini(void)
    330 {
    331 	int ipfinst;
    332 
    333 	ipfinst = mod_remove(&modlink1);
    334 #ifdef	IPFDEBUG
    335 	cmn_err(CE_NOTE, "IP Filter: _fini() = %d", ipfinst);
    336 #endif
    337 	return (ipfinst);
    338 }
    339 
    340 
    341 int
    342 _info(modinfop)
    343 struct modinfo *modinfop;
    344 {
    345 	int ipfinst;
    346 
    347 	ipfinst = mod_info(&modlink1, modinfop);
    348 #ifdef	IPFDEBUG
    349 	cmn_err(CE_NOTE, "IP Filter: _info(%p) = %d", modinfop, ipfinst);
    350 #endif
    351 	return (ipfinst);
    352 }
    353 
    354 
    355 #if SOLARIS2 < 10
    356 static int ipf_identify(dip)
    357 dev_info_t *dip;
    358 {
    359 #ifdef	IPFDEBUG
    360 	cmn_err(CE_NOTE, "IP Filter: ipf_identify(%p)", dip);
    361 #endif
    362 	if (strcmp(ddi_get_name(dip), "ipf") == 0)
    363 		return (DDI_IDENTIFIED);
    364 	return (DDI_NOT_IDENTIFIED);
    365 }
    366 #endif
    367 
    368 /*
    369  * Initialize things for IPF for each stack instance
    370  */
    371 static void *
    372 ipf_stack_create(const netid_t id)
    373 {
    374 	ipf_stack_t	*ifs;
    375 
    376 #ifdef IPFDEBUG
    377 	cmn_err(CE_NOTE, "IP Filter:stack_create id=%d", id);
    378 #endif
    379 
    380 	ifs = (ipf_stack_t *)kmem_alloc(sizeof (*ifs), KM_SLEEP);
    381 	bzero(ifs, sizeof (*ifs));
    382 
    383 	ifs->ifs_hook4_physical_in	= B_FALSE;
    384 	ifs->ifs_hook4_physical_out	= B_FALSE;
    385 	ifs->ifs_hook4_nic_events	= B_FALSE;
    386 	ifs->ifs_hook4_loopback_in	= B_FALSE;
    387 	ifs->ifs_hook4_loopback_out	= B_FALSE;
    388 	ifs->ifs_hook6_physical_in	= B_FALSE;
    389 	ifs->ifs_hook6_physical_out	= B_FALSE;
    390 	ifs->ifs_hook6_nic_events	= B_FALSE;
    391 	ifs->ifs_hook6_loopback_in	= B_FALSE;
    392 	ifs->ifs_hook6_loopback_out	= B_FALSE;
    393 
    394 	/*
    395 	 * Initialize mutex's
    396 	 */
    397 	RWLOCK_INIT(&ifs->ifs_ipf_global, "ipf filter load/unload mutex");
    398 	RWLOCK_INIT(&ifs->ifs_ipf_mutex, "ipf filter rwlock");
    399 	RWLOCK_INIT(&ifs->ifs_ipf_frcache, "ipf cache rwlock");
    400 	ifs->ifs_netid = id;
    401 	ifs->ifs_zone = net_getzoneidbynetid(id);
    402 	ipf_kstat_init(ifs);
    403 
    404 #ifdef IPFDEBUG
    405 	cmn_err(CE_CONT, "IP Filter:stack_create zone=%d", ifs->ifs_zone);
    406 #endif
    407 
    408 	/*
    409 	 * Lock people out while we set things up.
    410 	 */
    411 	WRITE_ENTER(&ifs->ifs_ipf_global);
    412 	ipftuneable_alloc(ifs);
    413 	RWLOCK_EXIT(&ifs->ifs_ipf_global);
    414 
    415 	/* Limit to global stack */
    416 	if (ifs->ifs_zone == GLOBAL_ZONEID)
    417 		cmn_err(CE_CONT, "!%s, running.\n", ipfilter_version);
    418 
    419 	mutex_enter(&ipf_stack_lock);
    420 	if (ipf_stacks != NULL)
    421 		ipf_stacks->ifs_pnext = &ifs->ifs_next;
    422 	ifs->ifs_next = ipf_stacks;
    423 	ifs->ifs_pnext = &ipf_stacks;
    424 	ipf_stacks = ifs;
    425 	mutex_exit(&ipf_stack_lock);
    426 
    427 	return (ifs);
    428 }
    429 
    430 
    431 /*
    432  * This function should only ever be used to find the pointer to the
    433  * ipfilter stack structure for the zone that is currently being
    434  * executed... so if you're running in the context of zone 1, you
    435  * should not attempt to find the ipf_stack_t for zone 0 or 2 or
    436  * anything else but 1.  In that way, the returned pointer is safe
    437  * as it will only be nuked when the instance is destroyed as part
    438  * of the final shutdown of a zone.
    439  */
    440 ipf_stack_t *
    441 ipf_find_stack(const zoneid_t zone)
    442 {
    443 	ipf_stack_t *ifs;
    444 
    445 	mutex_enter(&ipf_stack_lock);
    446 	for (ifs = ipf_stacks; ifs != NULL; ifs = ifs->ifs_next) {
    447 		if (ifs->ifs_zone == zone)
    448 			break;
    449 	}
    450 	mutex_exit(&ipf_stack_lock);
    451 	return (ifs);
    452 }
    453 
    454 
    455 static int ipf_detach_check_zone(ipf_stack_t *ifs)
    456 {
    457 	/*
    458 	 * Make sure we're the only one's modifying things.  With
    459 	 * this lock others should just fall out of the loop.
    460 	 */
    461 	READ_ENTER(&ifs->ifs_ipf_global);
    462 	if (ifs->ifs_fr_running == 1) {
    463 		RWLOCK_EXIT(&ifs->ifs_ipf_global);
    464 		return (-1);
    465 	}
    466 
    467 	/*
    468 	 * Make sure there is no active filter rule.
    469 	 */
    470 	if (ifs->ifs_ipfilter[0][ifs->ifs_fr_active] ||
    471 	    ifs->ifs_ipfilter[1][ifs->ifs_fr_active] ||
    472 	    ifs->ifs_ipfilter6[0][ifs->ifs_fr_active] ||
    473 	    ifs->ifs_ipfilter6[1][ifs->ifs_fr_active]) {
    474 		RWLOCK_EXIT(&ifs->ifs_ipf_global);
    475 		return (-1);
    476 	}
    477 
    478 	RWLOCK_EXIT(&ifs->ifs_ipf_global);
    479 
    480 	return (0);
    481 }
    482 
    483 
    484 static int ipf_detach_check_all()
    485 {
    486 	ipf_stack_t *ifs;
    487 
    488 	mutex_enter(&ipf_stack_lock);
    489 	for (ifs = ipf_stacks; ifs != NULL; ifs = ifs->ifs_next)
    490 		if (ipf_detach_check_zone(ifs) != 0)
    491 			break;
    492 	mutex_exit(&ipf_stack_lock);
    493 	return ((ifs == NULL) ? 0 : -1);
    494 }
    495 
    496 
    497 /*
    498  * Destroy things for ipf for one stack.
    499  */
    500 /* ARGSUSED */
    501 static void
    502 ipf_stack_shutdown(const netid_t id, void *arg)
    503 {
    504 	ipf_stack_t *ifs = (ipf_stack_t *)arg;
    505 
    506 	ipf_kstat_fini(ifs);
    507 }
    508 
    509 
    510 /*
    511  * Destroy things for ipf for one stack.
    512  */
    513 /* ARGSUSED */
    514 static void
    515 ipf_stack_destroy(const netid_t id, void *arg)
    516 {
    517 	ipf_stack_t *ifs = (ipf_stack_t *)arg;
    518 	timeout_id_t tid;
    519 
    520 #ifdef IPFDEBUG
    521 	(void) printf("ipf_stack_destroy(%p)\n", (void *)ifs);
    522 #endif
    523 
    524 	/*
    525 	 * Make sure we're the only one's modifying things.  With
    526 	 * this lock others should just fall out of the loop.
    527 	 */
    528 	WRITE_ENTER(&ifs->ifs_ipf_global);
    529 	if (ifs->ifs_fr_running == -2) {
    530 		RWLOCK_EXIT(&ifs->ifs_ipf_global);
    531 		return;
    532 	}
    533 	ifs->ifs_fr_running = -2;
    534 	tid = ifs->ifs_fr_timer_id;
    535 	ifs->ifs_fr_timer_id = NULL;
    536 	RWLOCK_EXIT(&ifs->ifs_ipf_global);
    537 
    538 	mutex_enter(&ipf_stack_lock);
    539 	if (ifs->ifs_next != NULL)
    540 		ifs->ifs_next->ifs_pnext = ifs->ifs_pnext;
    541 	*ifs->ifs_pnext = ifs->ifs_next;
    542 	mutex_exit(&ipf_stack_lock);
    543 
    544 	if (tid != NULL)
    545 		(void) untimeout(tid);
    546 
    547 	WRITE_ENTER(&ifs->ifs_ipf_global);
    548 	if (ipldetach(ifs) != 0) {
    549 		printf("ipf_stack_destroy: ipldetach failed\n");
    550 	}
    551 
    552 	ipftuneable_free(ifs);
    553 
    554 	RWLOCK_EXIT(&ifs->ifs_ipf_global);
    555 	RW_DESTROY(&ifs->ifs_ipf_mutex);
    556 	RW_DESTROY(&ifs->ifs_ipf_frcache);
    557 	RW_DESTROY(&ifs->ifs_ipf_global);
    558 
    559 	KFREE(ifs);
    560 }
    561 
    562 
    563 static int ipf_attach(dip, cmd)
    564 dev_info_t *dip;
    565 ddi_attach_cmd_t cmd;
    566 {
    567 	char *s;
    568 	int i;
    569 	int instance;
    570 
    571 #ifdef	IPFDEBUG
    572 	cmn_err(CE_NOTE, "IP Filter: ipf_attach(%p,%x)", dip, cmd);
    573 #endif
    574 
    575 	switch (cmd)
    576 	{
    577 	case DDI_ATTACH:
    578 		instance = ddi_get_instance(dip);
    579 		/* Only one instance of ipf (instance 0) can be attached. */
    580 		if (instance > 0)
    581 			return (DDI_FAILURE);
    582 
    583 #ifdef	IPFDEBUG
    584 		cmn_err(CE_CONT, "IP Filter: attach ipf instance %d", instance);
    585 #endif
    586 
    587 		(void) ipf_property_g_update(dip);
    588 
    589 		for (i = 0; ((s = ipf_devfiles[i]) != NULL); i++) {
    590 			s = strrchr(s, '/');
    591 			if (s == NULL)
    592 				continue;
    593 			s++;
    594 			if (ddi_create_minor_node(dip, s, S_IFCHR, i,
    595 			    DDI_PSEUDO, 0) ==
    596 			    DDI_FAILURE) {
    597 				ddi_remove_minor_node(dip, NULL);
    598 				goto attach_failed;
    599 			}
    600 		}
    601 
    602 		ipf_dev_info = dip;
    603 
    604 		ipfncb = net_instance_alloc(NETINFO_VERSION);
    605 		ipfncb->nin_name = "ipf";
    606 		ipfncb->nin_create = ipf_stack_create;
    607 		ipfncb->nin_destroy = ipf_stack_destroy;
    608 		ipfncb->nin_shutdown = ipf_stack_shutdown;
    609 		i = net_instance_register(ipfncb);
    610 
    611 #ifdef IPFDEBUG
    612 		cmn_err(CE_CONT, "IP Filter:stack_create callback_reg=%d", i);
    613 #endif
    614 
    615 		return (DDI_SUCCESS);
    616 		/* NOTREACHED */
    617 	default:
    618 		break;
    619 	}
    620 
    621 attach_failed:
    622 	ddi_prop_remove_all(dip);
    623 	return (DDI_FAILURE);
    624 }
    625 
    626 
    627 static int ipf_detach(dip, cmd)
    628 dev_info_t *dip;
    629 ddi_detach_cmd_t cmd;
    630 {
    631 	int i;
    632 
    633 #ifdef	IPFDEBUG
    634 	cmn_err(CE_NOTE, "IP Filter: ipf_detach(%p,%x)", dip, cmd);
    635 #endif
    636 	switch (cmd) {
    637 	case DDI_DETACH:
    638 		if (ipf_detach_check_all() != 0)
    639 			return (DDI_FAILURE);
    640 
    641 		/*
    642 		 * Undo what we did in ipf_attach, freeing resources
    643 		 * and removing things we installed.  The system
    644 		 * framework guarantees we are not active with this devinfo
    645 		 * node in any other entry points at this time.
    646 		 */
    647 		ddi_prop_remove_all(dip);
    648 		i = ddi_get_instance(dip);
    649 		ddi_remove_minor_node(dip, NULL);
    650 		if (i > 0) {
    651 			cmn_err(CE_CONT, "IP Filter: still attached (%d)\n", i);
    652 			return (DDI_FAILURE);
    653 		}
    654 
    655 		(void) net_instance_unregister(ipfncb);
    656 		net_instance_free(ipfncb);
    657 
    658 		return (DDI_SUCCESS);
    659 		/* NOTREACHED */
    660 	default:
    661 		break;
    662 	}
    663 	cmn_err(CE_NOTE, "IP Filter: failed to detach\n");
    664 	return (DDI_FAILURE);
    665 }
    666 
    667 
    668 /*ARGSUSED*/
    669 static int ipf_getinfo(dip, infocmd, arg, result)
    670 dev_info_t *dip;
    671 ddi_info_cmd_t infocmd;
    672 void *arg, **result;
    673 {
    674 	int error;
    675 
    676 	error = DDI_FAILURE;
    677 #ifdef	IPFDEBUG
    678 	cmn_err(CE_NOTE, "IP Filter: ipf_getinfo(%p,%x,%p)", dip, infocmd, arg);
    679 #endif
    680 	switch (infocmd) {
    681 	case DDI_INFO_DEVT2DEVINFO:
    682 		*result = ipf_dev_info;
    683 		error = DDI_SUCCESS;
    684 		break;
    685 	case DDI_INFO_DEVT2INSTANCE:
    686 		*result = (void *)0;
    687 		error = DDI_SUCCESS;
    688 		break;
    689 	default:
    690 		break;
    691 	}
    692 	return (error);
    693 }
    694 
    695 
    696 /*
    697  * Fetch configuration file values that have been entered into the ipf.conf
    698  * driver file.
    699  */
    700 static int ipf_property_g_update(dip)
    701 dev_info_t *dip;
    702 {
    703 #ifdef DDI_NO_AUTODETACH
    704 	if (ddi_prop_update_int(DDI_DEV_T_NONE, dip,
    705 				DDI_NO_AUTODETACH, 1) != DDI_PROP_SUCCESS) {
    706 		cmn_err(CE_WARN, "!updating DDI_NO_AUTODETACH failed");
    707 		return (DDI_FAILURE);
    708 	}
    709 #else
    710 	if (ddi_prop_update_int(DDI_DEV_T_NONE, dip,
    711 				"ddi-no-autodetach", 1) != DDI_PROP_SUCCESS) {
    712 		cmn_err(CE_WARN, "!updating ddi-no-autodetach failed");
    713 		return (DDI_FAILURE);
    714 	}
    715 #endif
    716 
    717 	return (DDI_SUCCESS);
    718 }
    719 
    720 int
    721 ipf_property_update(dip, ifs)
    722 dev_info_t *dip;
    723 ipf_stack_t *ifs;
    724 {
    725 	ipftuneable_t *ipft;
    726 	char *name;
    727 	uint_t one;
    728 	int *i32p;
    729 	int err, rv = 0;
    730 
    731 	for (ipft = ifs->ifs_ipf_tuneables;
    732 		(name = ipft->ipft_name) != NULL; ipft++) {
    733 		one = 1;
    734 		i32p = NULL;
    735 		err = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip,
    736 						0, name, &i32p, &one);
    737 		if (err == DDI_PROP_NOT_FOUND)
    738 			continue;
    739 #ifdef	IPFDEBUG
    740 		cmn_err(CE_CONT, "IP Filter: lookup_int(%s) = %d\n",
    741 			name, err);
    742 #endif
    743 		if (err != DDI_PROP_SUCCESS) {
    744 			rv = err;
    745 			continue;
    746 		}
    747 
    748 		if (*i32p >= ipft->ipft_min &&
    749 		    *i32p <= ipft->ipft_max) {
    750 			if (ipft->ipft_sz == sizeof (uint32_t)) {
    751 				*ipft->ipft_pint = *i32p;
    752 			} else if (ipft->ipft_sz == sizeof (uint64_t)) {
    753 				*ipft->ipft_plong = *i32p;
    754 			}
    755 		}
    756 
    757 		ddi_prop_free(i32p);
    758 	}
    759 
    760 	return (rv);
    761 }
    762