xref: /illumos-gate/usr/src/uts/common/inet/ipf/solaris.c (revision 9e26e16f703d2dfcc0689de957c21efcb72473e6)
1 /*
2  * Copyright (C) 1993-2001, 2003 by Darren Reed.
3  *
4  * See the IPFILTER.LICENCE file for details on licencing.
5  *
6  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
7  * Use is subject to license terms.
8  */
9 /* #pragma ident   "@(#)solaris.c	1.12 6/5/96 (C) 1995 Darren Reed"*/
10 #pragma ident "@(#)$Id: solaris.c,v 2.73.2.6 2005/07/13 21:40:47 darrenr Exp $"
11 
12 #pragma ident	"%Z%%M%	%I%	%E% SMI"
13 
14 #include <sys/systm.h>
15 #include <sys/types.h>
16 #include <sys/param.h>
17 #include <sys/errno.h>
18 #include <sys/uio.h>
19 #include <sys/buf.h>
20 #include <sys/modctl.h>
21 #include <sys/open.h>
22 #include <sys/kmem.h>
23 #include <sys/conf.h>
24 #include <sys/cmn_err.h>
25 #include <sys/stat.h>
26 #include <sys/cred.h>
27 #include <sys/dditypes.h>
28 #include <sys/poll.h>
29 #include <sys/autoconf.h>
30 #include <sys/byteorder.h>
31 #include <sys/socket.h>
32 #include <sys/dlpi.h>
33 #include <sys/stropts.h>
34 #include <sys/kstat.h>
35 #include <sys/sockio.h>
36 #include <sys/neti.h>
37 #include <sys/hook.h>
38 #include <net/if.h>
39 #if SOLARIS2 >= 6
40 # include <net/if_types.h>
41 #endif
42 #include <net/af.h>
43 #include <net/route.h>
44 #include <netinet/in.h>
45 #include <netinet/in_systm.h>
46 #include <netinet/if_ether.h>
47 #include <netinet/ip.h>
48 #include <netinet/ip_var.h>
49 #include <netinet/tcp.h>
50 #include <netinet/udp.h>
51 #include <netinet/tcpip.h>
52 #include <netinet/ip_icmp.h>
53 #include <sys/ddi.h>
54 #include <sys/sunddi.h>
55 #include "netinet/ip_compat.h"
56 #include "netinet/ipl.h"
57 #include "netinet/ip_fil.h"
58 #include "netinet/ip_nat.h"
59 #include "netinet/ip_frag.h"
60 #include "netinet/ip_auth.h"
61 #include "netinet/ip_state.h"
62 
63 extern	struct	filterstats	frstats[];
64 extern	int	fr_running;
65 extern	int	fr_flags;
66 extern	int	iplwrite __P((dev_t, struct uio *, cred_t *));
67 
68 extern ipnat_t *nat_list;
69 
70 static	int	ipf_getinfo __P((dev_info_t *, ddi_info_cmd_t,
71 				 void *, void **));
72 #if SOLARIS2 < 10
73 static	int	ipf_identify __P((dev_info_t *));
74 #endif
75 static	int	ipf_attach __P((dev_info_t *, ddi_attach_cmd_t));
76 static	int	ipf_detach __P((dev_info_t *, ddi_detach_cmd_t));
77 static	int	ipf_property_update __P((dev_info_t *));
78 static	char	*ipf_devfiles[] = { IPL_NAME, IPNAT_NAME, IPSTATE_NAME,
79 				    IPAUTH_NAME, IPSYNC_NAME, IPSCAN_NAME,
80 				    IPLOOKUP_NAME, NULL };
81 
82 
83 #if SOLARIS2 >= 7
84 extern	timeout_id_t	fr_timer_id;
85 #else
86 extern	int		fr_timer_id;
87 #endif
88 
89 static struct cb_ops ipf_cb_ops = {
90 	iplopen,
91 	iplclose,
92 	nodev,		/* strategy */
93 	nodev,		/* print */
94 	nodev,		/* dump */
95 	iplread,
96 	iplwrite,	/* write */
97 	iplioctl,	/* ioctl */
98 	nodev,		/* devmap */
99 	nodev,		/* mmap */
100 	nodev,		/* segmap */
101 	nochpoll,	/* poll */
102 	ddi_prop_op,
103 	NULL,
104 	D_MTSAFE,
105 #if SOLARIS2 > 4
106 	CB_REV,
107 	nodev,		/* aread */
108 	nodev,		/* awrite */
109 #endif
110 };
111 
112 static struct dev_ops ipf_ops = {
113 	DEVO_REV,
114 	0,
115 	ipf_getinfo,
116 #if SOLARIS2 >= 10
117 	nulldev,
118 #else
119 	ipf_identify,
120 #endif
121 	nulldev,
122 	ipf_attach,
123 	ipf_detach,
124 	nodev,		/* reset */
125 	&ipf_cb_ops,
126 	(struct bus_ops *)0
127 };
128 
129 extern struct mod_ops mod_driverops;
130 static struct modldrv iplmod = {
131 	&mod_driverops, IPL_VERSION, &ipf_ops };
132 static struct modlinkage modlink1 = { MODREV_1, &iplmod, NULL };
133 
134 #if SOLARIS2 >= 6
135 static	size_t	hdrsizes[57][2] = {
136 	{ 0, 0 },
137 	{ IFT_OTHER, 0 },
138 	{ IFT_1822, 0 },
139 	{ IFT_HDH1822, 0 },
140 	{ IFT_X25DDN, 0 },
141 	{ IFT_X25, 0 },
142 	{ IFT_ETHER, 14 },
143 	{ IFT_ISO88023, 0 },
144 	{ IFT_ISO88024, 0 },
145 	{ IFT_ISO88025, 0 },
146 	{ IFT_ISO88026, 0 },
147 	{ IFT_STARLAN, 0 },
148 	{ IFT_P10, 0 },
149 	{ IFT_P80, 0 },
150 	{ IFT_HY, 0 },
151 	{ IFT_FDDI, 24 },
152 	{ IFT_LAPB, 0 },
153 	{ IFT_SDLC, 0 },
154 	{ IFT_T1, 0 },
155 	{ IFT_CEPT, 0 },
156 	{ IFT_ISDNBASIC, 0 },
157 	{ IFT_ISDNPRIMARY, 0 },
158 	{ IFT_PTPSERIAL, 0 },
159 	{ IFT_PPP, 0 },
160 	{ IFT_LOOP, 0 },
161 	{ IFT_EON, 0 },
162 	{ IFT_XETHER, 0 },
163 	{ IFT_NSIP, 0 },
164 	{ IFT_SLIP, 0 },
165 	{ IFT_ULTRA, 0 },
166 	{ IFT_DS3, 0 },
167 	{ IFT_SIP, 0 },
168 	{ IFT_FRELAY, 0 },
169 	{ IFT_RS232, 0 },
170 	{ IFT_PARA, 0 },
171 	{ IFT_ARCNET, 0 },
172 	{ IFT_ARCNETPLUS, 0 },
173 	{ IFT_ATM, 0 },
174 	{ IFT_MIOX25, 0 },
175 	{ IFT_SONET, 0 },
176 	{ IFT_X25PLE, 0 },
177 	{ IFT_ISO88022LLC, 0 },
178 	{ IFT_LOCALTALK, 0 },
179 	{ IFT_SMDSDXI, 0 },
180 	{ IFT_FRELAYDCE, 0 },
181 	{ IFT_V35, 0 },
182 	{ IFT_HSSI, 0 },
183 	{ IFT_HIPPI, 0 },
184 	{ IFT_MODEM, 0 },
185 	{ IFT_AAL5, 0 },
186 	{ IFT_SONETPATH, 0 },
187 	{ IFT_SONETVT, 0 },
188 	{ IFT_SMDSICIP, 0 },
189 	{ IFT_PROPVIRTUAL, 0 },
190 	{ IFT_PROPMUX, 0 },
191 };
192 #endif /* SOLARIS2 >= 6 */
193 
194 static dev_info_t *ipf_dev_info = NULL;
195 
196 static const filter_kstats_t ipf_kstat_tmp = {
197 	{ "pass",			KSTAT_DATA_ULONG },
198 	{ "block",			KSTAT_DATA_ULONG },
199 	{ "nomatch",			KSTAT_DATA_ULONG },
200 	{ "short",			KSTAT_DATA_ULONG },
201 	{ "pass, logged",		KSTAT_DATA_ULONG },
202 	{ "block, logged",		KSTAT_DATA_ULONG },
203 	{ "nomatch, logged",		KSTAT_DATA_ULONG },
204 	{ "logged",			KSTAT_DATA_ULONG },
205 	{ "skip",			KSTAT_DATA_ULONG },
206 	{ "return sent",		KSTAT_DATA_ULONG },
207 	{ "acct",			KSTAT_DATA_ULONG },
208 	{ "bad frag state alloc",	KSTAT_DATA_ULONG },
209 	{ "new frag state kept",	KSTAT_DATA_ULONG },
210 	{ "new frag state compl. pkt",	KSTAT_DATA_ULONG },
211 	{ "bad pkt state alloc",	KSTAT_DATA_ULONG },
212 	{ "new pkt kept state",		KSTAT_DATA_ULONG },
213 	{ "cachehit",			KSTAT_DATA_ULONG },
214 	{ "tcp cksum bad",		KSTAT_DATA_ULONG },
215 	{{ "pullup ok",			KSTAT_DATA_ULONG },
216 	{ "pullup nok",			KSTAT_DATA_ULONG }},
217 	{ "src != route",		KSTAT_DATA_ULONG },
218 	{ "ttl invalid",		KSTAT_DATA_ULONG },
219 	{ "bad ip pkt",			KSTAT_DATA_ULONG },
220 	{ "ipv6 pkt",			KSTAT_DATA_ULONG },
221 	{ "dropped:pps ceiling",	KSTAT_DATA_ULONG },
222 	{ "ip upd. fail",		KSTAT_DATA_ULONG }
223 };
224 
225 net_data_t ipf_ipv4;
226 net_data_t ipf_ipv6;
227 
228 kstat_t		*ipf_kstatp[2] = {NULL, NULL};
229 static int	ipf_kstat_update(kstat_t *ksp, int rwflag);
230 
231 static void
232 ipf_kstat_init(void)
233 {
234 	int 	i;
235 
236 	for (i = 0; i < 2; i++) {
237 		ipf_kstatp[i] = kstat_create("ipf", 0,
238 			(i==0)?"inbound":"outbound",
239 			"net",
240 			KSTAT_TYPE_NAMED,
241 			sizeof (filter_kstats_t) / sizeof (kstat_named_t),
242 			0);
243 		if (ipf_kstatp[i] != NULL) {
244 			bcopy(&ipf_kstat_tmp, ipf_kstatp[i]->ks_data,
245 				sizeof (filter_kstats_t));
246 			ipf_kstatp[i]->ks_update = ipf_kstat_update;
247 			ipf_kstatp[i]->ks_private = &frstats[i];
248 			kstat_install(ipf_kstatp[i]);
249 		}
250 	}
251 
252 #ifdef	IPFDEBUG
253 	cmn_err(CE_NOTE, "IP Filter: ipf_kstat_init() installed 0x%x, 0x%x",
254 		ipf_kstatp[0], ipf_kstatp[1]);
255 #endif
256 }
257 
258 static void
259 ipf_kstat_fini(void)
260 {
261 	int i;
262 	for (i = 0; i < 2; i++) {
263 		if (ipf_kstatp[i] != NULL) {
264 			kstat_delete(ipf_kstatp[i]);
265 			ipf_kstatp[i] = NULL;
266 		}
267 	}
268 }
269 
270 static int
271 ipf_kstat_update(kstat_t *ksp, int rwflag)
272 {
273 	filter_kstats_t	*fkp;
274 	filterstats_t	*fsp;
275 
276 	if (rwflag == KSTAT_WRITE)
277 		return (EACCES);
278 
279 	fkp = ksp->ks_data;
280 	fsp = ksp->ks_private;
281 
282 	fkp->fks_pass.value.ul		= fsp->fr_pass;
283 	fkp->fks_block.value.ul		= fsp->fr_block;
284 	fkp->fks_nom.value.ul		= fsp->fr_nom;
285 	fkp->fks_short.value.ul		= fsp->fr_short;
286 	fkp->fks_ppkl.value.ul		= fsp->fr_ppkl;
287 	fkp->fks_bpkl.value.ul		= fsp->fr_bpkl;
288 	fkp->fks_npkl.value.ul		= fsp->fr_npkl;
289 	fkp->fks_pkl.value.ul		= fsp->fr_pkl;
290 	fkp->fks_skip.value.ul		= fsp->fr_skip;
291 	fkp->fks_ret.value.ul		= fsp->fr_ret;
292 	fkp->fks_acct.value.ul		= fsp->fr_acct;
293 	fkp->fks_bnfr.value.ul		= fsp->fr_bnfr;
294 	fkp->fks_nfr.value.ul		= fsp->fr_nfr;
295 	fkp->fks_cfr.value.ul		= fsp->fr_cfr;
296 	fkp->fks_bads.value.ul		= fsp->fr_bads;
297 	fkp->fks_ads.value.ul		= fsp->fr_ads;
298 	fkp->fks_chit.value.ul		= fsp->fr_chit;
299 	fkp->fks_tcpbad.value.ul 	= fsp->fr_tcpbad;
300 	fkp->fks_pull[0].value.ul 	= fsp->fr_pull[0];
301 	fkp->fks_pull[1].value.ul 	= fsp->fr_pull[1];
302 	fkp->fks_badsrc.value.ul 	= fsp->fr_badsrc;
303 	fkp->fks_badttl.value.ul 	= fsp->fr_badttl;
304 	fkp->fks_bad.value.ul		= fsp->fr_bad;
305 	fkp->fks_ipv6.value.ul		= fsp->fr_ipv6;
306 	fkp->fks_ppshit.value.ul 	= fsp->fr_ppshit;
307 	fkp->fks_ipud.value.ul		= fsp->fr_ipud;
308 
309 	return (0);
310 }
311 
312 int _init()
313 {
314 	int ipfinst;
315 
316 	ipf_kstat_init();
317 
318 	ipfinst = mod_install(&modlink1);
319 
320 	if (ipfinst != 0)
321 		ipf_kstat_fini();
322 #ifdef	IPFDEBUG
323 	cmn_err(CE_NOTE, "IP Filter: _init() = %d", ipfinst);
324 #endif
325 	return ipfinst;
326 }
327 
328 
329 int _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 	if (ipfinst == 0)
338 		ipf_kstat_fini();
339 
340 	return ipfinst;
341 }
342 
343 
344 int _info(modinfop)
345 struct modinfo *modinfop;
346 {
347 	int ipfinst;
348 
349 	ipfinst = mod_info(&modlink1, modinfop);
350 #ifdef	IPFDEBUG
351 	cmn_err(CE_NOTE, "IP Filter: _info(%x) = %x", modinfop, ipfinst);
352 #endif
353 	return ipfinst;
354 }
355 
356 
357 #if SOLARIS2 < 10
358 static int ipf_identify(dip)
359 dev_info_t *dip;
360 {
361 # ifdef	IPFDEBUG
362 	cmn_err(CE_NOTE, "IP Filter: ipf_identify(%x)", dip);
363 # endif
364 	if (strcmp(ddi_get_name(dip), "ipf") == 0)
365 		return (DDI_IDENTIFIED);
366 	return (DDI_NOT_IDENTIFIED);
367 }
368 #endif
369 
370 
371 static int ipf_attach(dip, cmd)
372 dev_info_t *dip;
373 ddi_attach_cmd_t cmd;
374 {
375 	char *s;
376 	int i;
377 	int instance;
378 
379 #ifdef	IPFDEBUG
380 	cmn_err(CE_NOTE, "IP Filter: ipf_attach(%x,%x)", dip, cmd);
381 #endif
382 
383 	switch (cmd)
384 	{
385 	case DDI_ATTACH:
386 		instance = ddi_get_instance(dip);
387 		/* Only one instance of ipf (instance 0) can be attached. */
388 		if (instance > 0)
389 			return DDI_FAILURE;
390 		if (fr_running != 0)
391 			return DDI_FAILURE;
392 
393 #ifdef	IPFDEBUG
394 		cmn_err(CE_NOTE, "IP Filter: attach ipf instance %d", instance);
395 #endif
396 
397 		(void) ipf_property_update(dip);
398 
399 		for (i = 0; ((s = ipf_devfiles[i]) != NULL); i++) {
400 			s = strrchr(s, '/');
401 			if (s == NULL)
402 				continue;
403 			s++;
404 			if (ddi_create_minor_node(dip, s, S_IFCHR, i,
405 						  DDI_PSEUDO, 0) ==
406 			    DDI_FAILURE) {
407 				ddi_remove_minor_node(dip, NULL);
408 				goto attach_failed;
409 			}
410 		}
411 
412 		ipf_dev_info = dip;
413 		/*
414 		 * Initialize mutex's
415 		 */
416 		RWLOCK_INIT(&ipf_global, "ipf filter load/unload mutex");
417 		RWLOCK_INIT(&ipf_mutex, "ipf filter rwlock");
418 		RWLOCK_INIT(&ipf_frcache, "ipf cache rwlock");
419 
420 		/*
421 		 * Lock people out while we set things up.
422 		 */
423 		WRITE_ENTER(&ipf_global);
424 		if ((fr_running != 0) || (iplattach() == -1)) {
425 			RWLOCK_EXIT(&ipf_global);
426 			goto attach_failed;
427 		}
428 
429 		fr_timer_id = timeout(fr_slowtimer, NULL,
430 				      drv_usectohz(500000));
431 
432 		fr_running = 1;
433 
434 		RWLOCK_EXIT(&ipf_global);
435 
436 		cmn_err(CE_CONT, "!%s, running.\n", ipfilter_version);
437 
438 		return DDI_SUCCESS;
439 		/* NOTREACHED */
440 	default:
441 		break;
442 	}
443 
444 attach_failed:
445 #ifdef	IPFDEBUG
446 	cmn_err(CE_NOTE, "IP Filter: failed to attach\n");
447 #endif
448 	/*
449 	 * Use our own detach routine to toss
450 	 * away any stuff we allocated above.
451 	 */
452 	(void) ipf_detach(dip, DDI_DETACH);
453 	return DDI_FAILURE;
454 }
455 
456 
457 static int ipf_detach(dip, cmd)
458 dev_info_t *dip;
459 ddi_detach_cmd_t cmd;
460 {
461 	int i;
462 
463 #ifdef	IPFDEBUG
464 	cmn_err(CE_NOTE, "IP Filter: ipf_detach(%x,%x)", dip, cmd);
465 #endif
466 	switch (cmd) {
467 	case DDI_DETACH:
468 		if (fr_refcnt != 0)
469 			return DDI_FAILURE;
470 
471 		if (fr_running == -2)
472 			break;
473 		/*
474 		 * Make sure we're the only one's modifying things.  With
475 		 * this lock others should just fall out of the loop.
476 		 */
477 		WRITE_ENTER(&ipf_global);
478 		if (fr_running <= 0) {
479 			RWLOCK_EXIT(&ipf_global);
480 			return DDI_FAILURE;
481 		}
482 		/*
483 		 * Make sure there is no active filter rule.
484 		 */
485 		if (ipfilter[0][fr_active] || ipfilter[1][fr_active] ||
486 		    ipfilter6[0][fr_active] || ipfilter6[1][fr_active]) {
487 		    RWLOCK_EXIT(&ipf_global);
488 			return DDI_FAILURE;
489 		}
490 		fr_running = -2;
491 
492 		RWLOCK_EXIT(&ipf_global);
493 
494 		if (fr_timer_id != 0) {
495 			(void) untimeout(fr_timer_id);
496 			fr_timer_id = 0;
497 		}
498 
499 		/*
500 		 * Undo what we did in ipf_attach, freeing resources
501 		 * and removing things we installed.  The system
502 		 * framework guarantees we are not active with this devinfo
503 		 * node in any other entry points at this time.
504 		 */
505 		ddi_prop_remove_all(dip);
506 		i = ddi_get_instance(dip);
507 		ddi_remove_minor_node(dip, NULL);
508 		if (i > 0) {
509 			cmn_err(CE_CONT, "IP Filter: still attached (%d)\n", i);
510 			return DDI_FAILURE;
511 		}
512 
513 		WRITE_ENTER(&ipf_global);
514 		if (!ipldetach()) {
515 			RWLOCK_EXIT(&ipf_global);
516 			RW_DESTROY(&ipf_mutex);
517 			RW_DESTROY(&ipf_frcache);
518 			RW_DESTROY(&ipf_global);
519 			cmn_err(CE_CONT, "!%s detached.\n", ipfilter_version);
520 			return (DDI_SUCCESS);
521 		}
522 		RWLOCK_EXIT(&ipf_global);
523 		break;
524 	default:
525 		break;
526 	}
527 	cmn_err(CE_NOTE, "IP Filter: failed to detach\n");
528 	return DDI_FAILURE;
529 }
530 
531 
532 /*ARGSUSED*/
533 static int ipf_getinfo(dip, infocmd, arg, result)
534 dev_info_t *dip;
535 ddi_info_cmd_t infocmd;
536 void *arg, **result;
537 {
538 	int error;
539 
540 	if (fr_running <= 0)
541 		return DDI_FAILURE;
542 	error = DDI_FAILURE;
543 #ifdef	IPFDEBUG
544 	cmn_err(CE_NOTE, "IP Filter: ipf_getinfo(%x,%x,%x)", dip, infocmd, arg);
545 #endif
546 	switch (infocmd) {
547 	case DDI_INFO_DEVT2DEVINFO:
548 		*result = ipf_dev_info;
549 		error = DDI_SUCCESS;
550 		break;
551 	case DDI_INFO_DEVT2INSTANCE:
552 		*result = (void *)0;
553 		error = DDI_SUCCESS;
554 		break;
555 	default:
556 		break;
557 	}
558 	return (error);
559 }
560 
561 
562 /*
563  * Fetch configuration file values that have been entered into the ipf.conf
564  * driver file.
565  */
566 static int ipf_property_update(dip)
567 dev_info_t *dip;
568 {
569 	ipftuneable_t *ipft;
570 	int64_t *i64p;
571 	char *name;
572 	u_int one;
573 	int *i32p;
574 	int err;
575 
576 #ifdef DDI_NO_AUTODETACH
577 	if (ddi_prop_update_int(DDI_DEV_T_NONE, dip,
578 				DDI_NO_AUTODETACH, 1) != DDI_PROP_SUCCESS) {
579 		cmn_err(CE_WARN, "!updating DDI_NO_AUTODETACH failed");
580 		return DDI_FAILURE;
581 	}
582 #else
583 	if (ddi_prop_update_int(DDI_DEV_T_NONE, dip,
584 				"ddi-no-autodetach", 1) != DDI_PROP_SUCCESS) {
585 		cmn_err(CE_WARN, "!updating ddi-no-autodetach failed");
586 		return DDI_FAILURE;
587 	}
588 #endif
589 
590 	err = DDI_SUCCESS;
591 	ipft = ipf_tuneables;
592 	for (ipft = ipf_tuneables; (name = ipft->ipft_name) != NULL; ipft++) {
593 		one = 1;
594 		switch (ipft->ipft_sz)
595 		{
596 		case 4 :
597 			i32p = NULL;
598 			err = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip,
599 							0, name, &i32p, &one);
600 			if (err == DDI_PROP_NOT_FOUND)
601 				continue;
602 #ifdef	IPFDEBUG
603 			cmn_err(CE_CONT, "IP Filter: lookup_int(%s) = %d\n",
604 				name, err);
605 #endif
606 			if (err != DDI_PROP_SUCCESS)
607 				return err;
608 			if (*i32p >= ipft->ipft_min && *i32p <= ipft->ipft_max)
609 				*ipft->ipft_pint = *i32p;
610 			else
611 				err = DDI_PROP_CANNOT_DECODE;
612 			ddi_prop_free(i32p);
613 			break;
614 
615 #if SOLARIS2 > 8
616 		case 8 :
617 			i64p = NULL;
618 			err = ddi_prop_lookup_int64_array(DDI_DEV_T_ANY, dip,
619 							  0, name, &i64p, &one);
620 			if (err == DDI_PROP_NOT_FOUND)
621 				continue;
622 # ifdef	IPFDEBUG
623 			cmn_err(CE_CONT, "IP Filter: lookup_int64(%s) = %d\n",
624 				name, err);
625 # endif
626 			if (err != DDI_PROP_SUCCESS)
627 				return err;
628 			if (*i64p >= ipft->ipft_min && *i64p <= ipft->ipft_max)
629 				*ipft->ipft_pint = *i64p;
630 			else
631 				err = DDI_PROP_CANNOT_DECODE;
632 			ddi_prop_free(i64p);
633 			break;
634 #endif
635 
636 		default :
637 			break;
638 		}
639 		if (err != DDI_SUCCESS)
640 			break;
641 	}
642 
643 	return err;
644 }
645