1 /*
2  * Copyright (C) 1999-2001, 2003 by Darren Reed.
3  *
4  * See the IPFILTER.LICENCE file for details on licencing.
5  *
6  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
7  * Use is subject to license terms.
8  */
9 
10 #pragma ident	"%Z%%M%	%I%	%E% SMI"
11 
12 #if !defined(lint)
13 static const char rcsid[] = "@(#)$Id: ip_fil_solaris.c,v 2.36 2003/07/01 18:30:20 darrenr Exp $";
14 #endif
15 
16 #include <sys/types.h>
17 #include <sys/errno.h>
18 #include <sys/param.h>
19 #include <sys/cpuvar.h>
20 #include <sys/open.h>
21 #include <sys/ioctl.h>
22 #include <sys/filio.h>
23 #include <sys/systm.h>
24 #include <sys/cred.h>
25 #include <sys/ddi.h>
26 #include <sys/sunddi.h>
27 #include <sys/ksynch.h>
28 #include <sys/kmem.h>
29 #include <sys/mkdev.h>
30 #include <sys/protosw.h>
31 #include <sys/socket.h>
32 #include <sys/dditypes.h>
33 #include <sys/cmn_err.h>
34 #include <net/if.h>
35 #include <net/af.h>
36 #include <net/route.h>
37 #include <netinet/in.h>
38 #include <netinet/in_systm.h>
39 #include <netinet/ip.h>
40 #include <netinet/ip_var.h>
41 #include <netinet/tcp.h>
42 #include <netinet/udp.h>
43 #include <netinet/tcpip.h>
44 #include <netinet/ip_icmp.h>
45 #include "ip_compat.h"
46 #ifdef	USE_INET6
47 # include <netinet/icmp6.h>
48 #endif
49 #include "ip_fil.h"
50 #include "ip_nat.h"
51 #include "ip_frag.h"
52 #include "ip_state.h"
53 #include "ip_auth.h"
54 #include "ip_proxy.h"
55 #ifdef	IPFILTER_LOOKUP
56 #include "ip_lookup.h"
57 #endif
58 #ifdef	IPFILTER_COMPILED
59 #include "ip_rules.h"
60 #endif
61 #include <inet/ip_ire.h>
62 
63 #include <sys/md5.h>
64 
65 extern	int fr_flags, fr_active;
66 #if SOLARIS2 >= 7
67 extern	timeout_id_t	fr_timer_id;
68 #else
69 extern	int	fr_timer_id;
70 #endif
71 
72 
73 static	int	frzerostats __P((caddr_t));
74 static	int	fr_send_ip __P((fr_info_t *fin, mblk_t *m));
75 
76 ipfmutex_t	ipl_mutex, ipf_authmx, ipf_rw, ipf_stinsert;
77 ipfmutex_t	ipf_nat_new, ipf_natio, ipf_timeoutlock;
78 ipfrwlock_t	ipf_mutex, ipf_global, ipf_ipidfrag;
79 ipfrwlock_t	ipf_frag, ipf_state, ipf_nat, ipf_natfrag, ipf_auth;
80 kcondvar_t	iplwait, ipfauthwait;
81 #if SOLARIS2 < 10
82 #if SOLARIS2 >= 7
83 u_int		*ip_ttl_ptr;
84 u_int		*ip_mtudisc;
85 # if SOLARIS2 >= 8
86 int		*ip_forwarding;
87 u_int		*ip6_forwarding;
88 # else
89 u_int		*ip_forwarding;
90 # endif
91 #else
92 u_long		*ip_ttl_ptr;
93 u_long		*ip_mtudisc;
94 u_long		*ip_forwarding;
95 #endif
96 #endif
97 int		ipf_locks_done = 0;
98 
99 
100 /* ------------------------------------------------------------------------ */
101 /* Function:    ipldetach                                                   */
102 /* Returns:     int - 0 == success, else error.                             */
103 /* Parameters:  Nil                                                         */
104 /*                                                                          */
105 /* This function is responsible for undoing anything that might have been   */
106 /* done in a call to iplattach().  It must be able to clean up from a call  */
107 /* to iplattach() that did not succeed.  Why might that happen?  Someone    */
108 /* configures a table to be so large that we cannot allocate enough memory  */
109 /* for it.                                                                  */
110 /* ------------------------------------------------------------------------ */
111 int ipldetach()
112 {
113 
114 	ASSERT(rw_read_locked(&ipf_global.ipf_lk) == 0);
115 
116 	if (fr_refcnt)
117 		return EBUSY;
118 #if SOLARIS2 < 10
119 
120 	if (fr_control_forwarding & 2) {
121 		*ip_forwarding = 0;
122 #if SOLARIS2 >= 8
123 		*ip6_forwarding = 0;
124 #endif
125 	}
126 #endif
127 
128 #ifdef	IPFDEBUG
129 	cmn_err(CE_CONT, "ipldetach()\n");
130 #endif
131 
132 	fr_fragunload();
133 	fr_authunload();
134 	fr_stateunload();
135 	fr_natunload();
136 	appr_unload();
137 
138 #ifdef	IPFILTER_COMPILED
139 	ipfrule_remove();
140 #endif
141 
142 	(void) frflush(IPL_LOGIPF, FR_INQUE|FR_OUTQUE|FR_INACTIVE);
143 	(void) frflush(IPL_LOGIPF, FR_INQUE|FR_OUTQUE);
144 
145 #ifdef	IPFILTER_LOOKUP
146 	ip_lookup_unload();
147 #endif
148 
149 #ifdef	IPFILTER_LOG
150 	fr_logunload();
151 #endif
152 
153 	if (ipf_locks_done == 1) {
154 		MUTEX_DESTROY(&ipf_timeoutlock);
155 		MUTEX_DESTROY(&ipf_rw);
156 		RW_DESTROY(&ipf_ipidfrag);
157 		ipf_locks_done = 0;
158 	}
159 	return 0;
160 }
161 
162 
163 int iplattach __P((void))
164 {
165 #if SOLARIS2 < 10
166 	int i;
167 #endif
168 
169 #ifdef	IPFDEBUG
170 	cmn_err(CE_CONT, "iplattach()\n");
171 #endif
172 
173 	ASSERT(rw_read_locked(&ipf_global.ipf_lk) == 0);
174 
175 	bzero((char *)frcache, sizeof(frcache));
176 	MUTEX_INIT(&ipf_rw, "ipf rw mutex");
177 	MUTEX_INIT(&ipf_timeoutlock, "ipf timeout lock mutex");
178 	RWLOCK_INIT(&ipf_ipidfrag, "ipf IP NAT-Frag rwlock");
179 	ipf_locks_done = 1;
180 
181 #ifdef	IPFILTER_LOG
182 	if (fr_loginit() == -1)
183 		return -1;
184 #endif
185 	if (fr_natinit() == -1)
186 		return -1;
187 	if (fr_stateinit() == -1)
188 		return -1;
189 	if (fr_authinit() == -1)
190 		return -1;
191 	if (fr_fraginit() == -1)
192 		return -1;
193 	if (appr_init() == -1)
194 		return -1;
195 #ifdef	IPFILTER_SYNC
196 	ipfsync_init();
197 #endif
198 #ifdef	IPFILTER_SCAN
199 	isc_init();
200 #endif
201 #ifdef IPFILTER_LOOKUP
202 	if (ip_lookup_init() == -1)
203 		return -1;
204 #endif
205 
206 /* Do not use private interface ip_params_arr[] in Solaris 10 */
207 #if SOLARIS2 < 10
208 
209 #if SOLARIS2 >= 8
210 	ip_forwarding = &ip_g_forward;
211 #endif
212 	/*
213 	 * XXX - There is no terminator for this array, so it is not possible
214 	 * to tell if what we are looking for is missing and go off the end
215 	 * of the array.
216 	 */
217 
218 	for (i = 0; ; i++) {
219 		if (!strcmp(ip_param_arr[i].ip_param_name, "ip_def_ttl")) {
220 			ip_ttl_ptr = &ip_param_arr[i].ip_param_value;
221 		} else if (!strcmp(ip_param_arr[i].ip_param_name,
222 			    "ip_path_mtu_discovery")) {
223 			ip_mtudisc = &ip_param_arr[i].ip_param_value;
224 		}
225 #if SOLARIS2 < 8
226 		else if (!strcmp(ip_param_arr[i].ip_param_name,
227 			    "ip_forwarding")) {
228 			ip_forwarding = &ip_param_arr[i].ip_param_value;
229 		}
230 #else
231 		else if (!strcmp(ip_param_arr[i].ip_param_name,
232 			    "ip6_forwarding")) {
233 			ip6_forwarding = &ip_param_arr[i].ip_param_value;
234 		}
235 #endif
236 
237 		if (ip_mtudisc != NULL && ip_ttl_ptr != NULL &&
238 #if SOLARIS2 >= 8
239 		    ip6_forwarding != NULL &&
240 #endif
241 		    ip_forwarding != NULL)
242 			break;
243 	}
244 
245 	if (fr_control_forwarding & 1) {
246 		*ip_forwarding = 1;
247 #if SOLARIS2 >= 8
248 		*ip6_forwarding = 1;
249 #endif
250 	}
251 
252 #endif
253 
254 	return 0;
255 }
256 
257 
258 static	int	frzerostats(data)
259 caddr_t	data;
260 {
261 	friostat_t fio;
262 	int error;
263 
264 	fr_getstat(&fio);
265 	error = copyoutptr((caddr_t)&fio, data, sizeof(fio));
266 	if (error)
267 		return error;
268 
269 	bzero((char *)frstats, sizeof(*frstats) * 2);
270 
271 	return 0;
272 }
273 
274 
275 /*
276  * Filter ioctl interface.
277  */
278 /*ARGSUSED*/
279 int iplioctl(dev, cmd, data, mode, cp, rp)
280 dev_t dev;
281 int cmd;
282 #if SOLARIS2 >= 7
283 intptr_t data;
284 #else
285 int *data;
286 #endif
287 int mode;
288 cred_t *cp;
289 int *rp;
290 {
291 	int error = 0, tmp;
292 	friostat_t fio;
293 	minor_t unit;
294 	u_int enable;
295 
296 #ifdef	IPFDEBUG
297 	cmn_err(CE_CONT, "iplioctl(%x,%x,%x,%d,%x,%d)\n",
298 		dev, cmd, data, mode, cp, rp);
299 #endif
300 	unit = getminor(dev);
301 	if (IPL_LOGMAX < unit)
302 		return ENXIO;
303 
304 	if (fr_running <= 0) {
305 		if (unit != IPL_LOGIPF)
306 			return EIO;
307 		if (cmd != SIOCIPFGETNEXT && cmd != SIOCIPFGET &&
308 		    cmd != SIOCIPFSET && cmd != SIOCFRENB && cmd != SIOCGETFS)
309 			return EIO;
310 	}
311 
312 	READ_ENTER(&ipf_global);
313 
314 	error = fr_ioctlswitch(unit, (caddr_t)data, cmd, mode);
315 	if (error != -1) {
316 		RWLOCK_EXIT(&ipf_global);
317 		return error;
318 	}
319 	error = 0;
320 
321 	switch (cmd)
322 	{
323 	case SIOCFRENB :
324 		if (!(mode & FWRITE))
325 			error = EPERM;
326 		else {
327 			error = COPYIN((caddr_t)data, (caddr_t)&enable,
328 				       sizeof(enable));
329 			if (error != 0) {
330 				error = EFAULT;
331 				break;
332 			}
333 
334 			RWLOCK_EXIT(&ipf_global);
335 			WRITE_ENTER(&ipf_global);
336 			if (enable) {
337 				if (fr_running > 0)
338 					error = 0;
339 				else
340 					error = iplattach();
341 				if (error == 0)
342 					fr_running = 1;
343 				else
344 					(void) ipldetach();
345 			} else {
346 				error = ipldetach();
347 				if (error == 0)
348 					fr_running = -1;
349 			}
350 		}
351 		break;
352 	case SIOCIPFSET :
353 		if (!(mode & FWRITE)) {
354 			error = EPERM;
355 			break;
356 		}
357 		/* FALLTHRU */
358 	case SIOCIPFGETNEXT :
359 	case SIOCIPFGET :
360 		error = fr_ipftune(cmd, (char *)data);
361 		break;
362 	case SIOCSETFF :
363 		if (!(mode & FWRITE))
364 			error = EPERM;
365 		else {
366 			error = COPYIN((caddr_t)data, (caddr_t)&fr_flags,
367 			       sizeof(fr_flags));
368 			if (error != 0)
369 				error = EFAULT;
370 		}
371 		break;
372 	case SIOCGETFF :
373 		error = COPYOUT((caddr_t)&fr_flags, (caddr_t)data,
374 			       sizeof(fr_flags));
375 		if (error != 0)
376 			error = EFAULT;
377 		break;
378 	case SIOCFUNCL :
379 		error = fr_resolvefunc((void *)data);
380 		break;
381 	case SIOCINAFR :
382 	case SIOCRMAFR :
383 	case SIOCADAFR :
384 	case SIOCZRLST :
385 		if (!(mode & FWRITE))
386 			error = EPERM;
387 		else
388 			error = frrequest(unit, cmd, (caddr_t)data,
389 					  fr_active, 1);
390 		break;
391 	case SIOCINIFR :
392 	case SIOCRMIFR :
393 	case SIOCADIFR :
394 		if (!(mode & FWRITE))
395 			error = EPERM;
396 		else
397 			error = frrequest(unit, cmd, (caddr_t)data,
398 					  1 - fr_active, 1);
399 		break;
400 	case SIOCSWAPA :
401 		if (!(mode & FWRITE))
402 			error = EPERM;
403 		else {
404 			WRITE_ENTER(&ipf_mutex);
405 			bzero((char *)frcache, sizeof(frcache[0]) * 2);
406 			error = COPYOUT((caddr_t)&fr_active, (caddr_t)data,
407 				       sizeof(fr_active));
408 			if (error != 0)
409 				error = EFAULT;
410 			else
411 				fr_active = 1 - fr_active;
412 			RWLOCK_EXIT(&ipf_mutex);
413 		}
414 		break;
415 	case SIOCGETFS :
416 		fr_getstat(&fio);
417 		error = fr_outobj((void *)data, &fio, IPFOBJ_IPFSTAT);
418 		break;
419 	case SIOCFRZST :
420 		if (!(mode & FWRITE))
421 			error = EPERM;
422 		else
423 			error = frzerostats((caddr_t)data);
424 		break;
425 	case	SIOCIPFFL :
426 		if (!(mode & FWRITE))
427 			error = EPERM;
428 		else {
429 			error = COPYIN((caddr_t)data, (caddr_t)&tmp,
430 				       sizeof(tmp));
431 			if (!error) {
432 				tmp = frflush(unit, tmp);
433 				error = COPYOUT((caddr_t)&tmp, (caddr_t)data,
434 					       sizeof(tmp));
435 				if (error != 0)
436 					error = EFAULT;
437 			} else
438 				error = EFAULT;
439 		}
440 		break;
441 	case SIOCSTLCK :
442 		error = COPYIN((caddr_t)data, (caddr_t)&tmp, sizeof(tmp));
443 		if (error == 0) {
444 			fr_state_lock = tmp;
445 			fr_nat_lock = tmp;
446 			fr_frag_lock = tmp;
447 			fr_auth_lock = tmp;
448 		} else
449 			error = EFAULT;
450 	break;
451 #ifdef	IPFILTER_LOG
452 	case	SIOCIPFFB :
453 		if (!(mode & FWRITE))
454 			error = EPERM;
455 		else {
456 			tmp = ipflog_clear(unit);
457 			error = COPYOUT((caddr_t)&tmp, (caddr_t)data,
458 				       sizeof(tmp));
459 			if (error)
460 				error = EFAULT;
461 		}
462 		break;
463 #endif /* IPFILTER_LOG */
464 	case SIOCFRSYN :
465 		if (!(mode & FWRITE))
466 			error = EPERM;
467 		else {
468 			RWLOCK_EXIT(&ipf_global);
469 			WRITE_ENTER(&ipf_global);
470 			error = ipfsync();
471 		}
472 		break;
473 	case SIOCGFRST :
474 		error = fr_outobj((void *)data, fr_fragstats(),
475 				  IPFOBJ_FRAGSTAT);
476 		break;
477 	case FIONREAD :
478 #ifdef	IPFILTER_LOG
479 		tmp = (int)iplused[IPL_LOGIPF];
480 
481 		error = COPYOUT((caddr_t)&tmp, (caddr_t)data, sizeof(tmp));
482 		if (error != 0)
483 			error = EFAULT;
484 #endif
485 		break;
486 	default :
487 		cmn_err(CE_NOTE, "Unknown: cmd 0x%x data %p",
488 			cmd, (void *)data);
489 		error = EINVAL;
490 		break;
491 	}
492 	RWLOCK_EXIT(&ipf_global);
493 	return error;
494 }
495 
496 #ifndef IRE_ILL_CN
497 ill_t	*get_unit(name, v)
498 char	*name;
499 int	v;
500 {
501 	size_t len = strlen(name) + 1;	/* includes \0 */
502 	ill_t *il;
503 #if SOLARIS2 >= 10
504 	ill_walk_context_t ctx;
505 #endif
506 	int sap;
507 
508 	if (v == 4)
509 		sap = 0x0800;
510 	else if (v == 6)
511 		sap = 0x86dd;
512 	else
513 		return NULL;
514 #if SOLARIS2 >= 10
515 	for (il = ILL_START_WALK_ALL(&ctx); il; il = ill_next(&ctx, il))
516 #else
517 	for (il = ill_g_head; il; il = il->ill_next)
518 #endif
519 		if ((len == il->ill_name_length) && (il->ill_sap == sap) &&
520 		    !strncmp(il->ill_name, name, len))
521 			return il;
522 	return NULL;
523 }
524 #else
525 s_ill_t	*get_unit(name, v)
526 char	*name;
527 int	v;
528 {
529 	s_ill_t *il;
530 
531 	int sap;
532 
533 	if (v == 4)
534 		sap = 0x0800;
535 	else if (v == 6)
536 		sap = 0x86dd;
537 	else
538 		return NULL;
539 
540 	mutex_enter(&s_ill_g_head_lock);
541 	for (il = s_ill_g_head; il; il = il->ill_next)
542 		if ((il->ill_sap == sap) &&
543 		    !strncmp(il->ill_name, name, LIFNAMSIZ))
544 			break;
545 	mutex_exit(&s_ill_g_head_lock);
546 	return il;
547 }
548 #endif /* IRE_ILL_CN */
549 
550 
551 /*
552  * routines below for saving IP headers to buffer
553  */
554 /*ARGSUSED*/
555 int iplopen(devp, flags, otype, cred)
556 dev_t *devp;
557 int flags, otype;
558 cred_t *cred;
559 {
560 	minor_t min = getminor(*devp);
561 
562 #ifdef	IPFDEBUG
563 	cmn_err(CE_CONT, "iplopen(%x,%x,%x,%x)\n", devp, flags, otype, cred);
564 #endif
565 	if (!(otype & OTYP_CHR))
566 		return ENXIO;
567 
568 	min = (IPL_LOGMAX < min) ? ENXIO : 0;
569 	return min;
570 }
571 
572 
573 /*ARGSUSED*/
574 int iplclose(dev, flags, otype, cred)
575 dev_t dev;
576 int flags, otype;
577 cred_t *cred;
578 {
579 	minor_t	min = getminor(dev);
580 
581 #ifdef	IPFDEBUG
582 	cmn_err(CE_CONT, "iplclose(%x,%x,%x,%x)\n", dev, flags, otype, cred);
583 #endif
584 
585 	min = (IPL_LOGMAX < min) ? ENXIO : 0;
586 	return min;
587 }
588 
589 #ifdef	IPFILTER_LOG
590 /*
591  * iplread/ipllog
592  * both of these must operate with at least splnet() lest they be
593  * called during packet processing and cause an inconsistancy to appear in
594  * the filter lists.
595  */
596 /*ARGSUSED*/
597 int iplread(dev, uio, cp)
598 dev_t dev;
599 register struct uio *uio;
600 cred_t *cp;
601 {
602 # ifdef	IPFDEBUG
603 	cmn_err(CE_CONT, "iplread(%x,%x,%x)\n", dev, uio, cp);
604 # endif
605 # ifdef	IPFILTER_SYNC
606 	if (getminor(dev) == IPL_LOGSYNC)
607 		return ipfsync_read(uio);
608 # endif
609 
610 	return ipflog_read(getminor(dev), uio);
611 }
612 #endif /* IPFILTER_LOG */
613 
614 
615 #ifdef	IPFILTER_SYNC
616 /*
617  * iplread/ipllog
618  * both of these must operate with at least splnet() lest they be
619  * called during packet processing and cause an inconsistancy to appear in
620  * the filter lists.
621  */
622 int iplwrite(dev, uio, cp)
623 dev_t dev;
624 register struct uio *uio;
625 cred_t *cp;
626 {
627 #ifdef	IPFDEBUG
628 	cmn_err(CE_CONT, "iplwrite(%x,%x,%x)\n", dev, uio, cp);
629 #endif
630 	if (getminor(dev) != IPL_LOGSYNC)
631 		return ENXIO;
632 	return ipfsync_write(uio);
633 }
634 #endif /* IPFILTER_SYNC */
635 
636 
637 /*
638  * fr_send_reset - this could conceivably be a call to tcp_respond(), but that
639  * requires a large amount of setting up and isn't any more efficient.
640  */
641 int fr_send_reset(fin)
642 fr_info_t *fin;
643 {
644 	tcphdr_t *tcp, *tcp2;
645 	int tlen, hlen;
646 	mblk_t *m;
647 #ifdef	USE_INET6
648 	ip6_t *ip6;
649 #endif
650 	ip_t *ip;
651 
652 	tcp = fin->fin_dp;
653 	if (tcp->th_flags & TH_RST)
654 		return -1;
655 
656 #ifndef	IPFILTER_CKSUM
657 	if (fr_checkl4sum(fin) == -1)
658 		return -1;
659 #endif
660 
661 	tlen = (tcp->th_flags & (TH_SYN|TH_FIN)) ? 1 : 0;
662 #ifdef	USE_INET6
663 	if (fin->fin_v == 6)
664 		hlen = sizeof(ip6_t);
665 	else
666 #endif
667 		hlen = sizeof(ip_t);
668 	hlen += sizeof(*tcp2);
669 	if ((m = (mblk_t *)allocb(hlen + 64, BPRI_HI)) == NULL)
670 		return -1;
671 
672 	m->b_rptr += 64;
673 	MTYPE(m) = M_DATA;
674 	m->b_wptr = m->b_rptr + hlen;
675 	bzero((char *)m->b_rptr, hlen);
676 	tcp2 = (struct tcphdr *)(m->b_rptr + hlen - sizeof(*tcp2));
677 	tcp2->th_dport = tcp->th_sport;
678 	tcp2->th_sport = tcp->th_dport;
679 	if (tcp->th_flags & TH_ACK) {
680 		tcp2->th_seq = tcp->th_ack;
681 		tcp2->th_flags = TH_RST;
682 	} else {
683 		tcp2->th_ack = ntohl(tcp->th_seq);
684 		tcp2->th_ack += tlen;
685 		tcp2->th_ack = htonl(tcp2->th_ack);
686 		tcp2->th_flags = TH_RST|TH_ACK;
687 	}
688 	tcp2->th_off = sizeof(struct tcphdr) >> 2;
689 
690 	/*
691 	 * This is to get around a bug in the Solaris 2.4/2.5 TCP checksum
692 	 * computation that is done by their put routine.
693 	 */
694 #ifdef	USE_INET6
695 	if (fin->fin_v == 6) {
696 		ip6 = (ip6_t *)m->b_rptr;
697 		ip6->ip6_src = fin->fin_dst6;
698 		ip6->ip6_dst = fin->fin_src6;
699 		ip6->ip6_plen = htons(sizeof(*tcp));
700 		ip6->ip6_nxt = IPPROTO_TCP;
701 	} else
702 #endif
703 	{
704 		ip = (ip_t *)m->b_rptr;
705 		ip->ip_src.s_addr = fin->fin_daddr;
706 		ip->ip_dst.s_addr = fin->fin_saddr;
707 		ip->ip_id = fr_nextipid(fin);
708 		ip->ip_hl = sizeof(*ip) >> 2;
709 		ip->ip_p = IPPROTO_TCP;
710 		ip->ip_len = htons(sizeof(*ip) + sizeof(*tcp));
711 		ip->ip_tos = fin->fin_ip->ip_tos;
712 		tcp2->th_sum = fr_cksum(m, ip, IPPROTO_TCP, tcp2);
713 	}
714 	return fr_send_ip(fin, m);
715 }
716 
717 
718 static int fr_send_ip(fin, m)
719 fr_info_t *fin;
720 mblk_t *m;
721 {
722 	int i;
723 
724 #ifdef	USE_INET6
725 	if (fin->fin_v == 6) {
726 		ip6_t *ip6;
727 
728 		ip6 = (ip6_t *)m->b_rptr;
729 		ip6->ip6_flow = 0;
730 		ip6->ip6_vfc = 0x60;
731 		ip6->ip6_hlim = 127;
732 	} else
733 #endif
734 	{
735 		ip_t *ip;
736 
737 		ip = (ip_t *)m->b_rptr;
738 		ip->ip_v = IPVERSION;
739 
740 #if SOLARIS2 >= 10
741 		ip->ip_ttl = 255;
742 		ip->ip_off = htons(IP_DF);
743 #else
744 		ip->ip_ttl = (u_char)(*ip_ttl_ptr);
745 		ip->ip_off = htons(*ip_mtudisc ? IP_DF : 0);
746 #endif
747 
748 		ip->ip_sum = ipf_cksum((u_short *)ip, sizeof(*ip));
749 
750 	}
751 	i = fr_fastroute(m, &m, fin, NULL);
752 	return i;
753 }
754 
755 
756 int fr_send_icmp_err(type, fin, dst)
757 int type;
758 fr_info_t *fin;
759 int dst;
760 {
761 	struct in_addr dst4;
762 	struct icmp *icmp;
763 	int hlen, code;
764 	qif_t *qif;
765 	u_short sz;
766 #ifdef	USE_INET6
767 	mblk_t *mb;
768 #endif
769 	mblk_t *m;
770 #ifdef	icmp_nextmtu
771 #ifndef IRE_ILL_CN
772 	ill_t *il;
773 #else
774 	s_ill_t *il;
775 #endif	/* IRE_ILL_CN */
776 #endif
777 #ifdef	USE_INET6
778 	ip6_t *ip6;
779 #endif
780 	ip_t *ip;
781 
782 #ifdef	icmp_nextmtu
783 	/* lint fodder */
784 	il = NULL;
785 	il = il;
786 #endif
787 
788 	if ((type < 0) || (type > ICMP_MAXTYPE))
789 		return -1;
790 
791 	code = fin->fin_icode;
792 #ifdef USE_INET6
793 	if ((code < 0) || (code > sizeof(icmptoicmp6unreach)/sizeof(int)))
794 		return -1;
795 #endif
796 
797 #ifndef	IPFILTER_CKSUM
798 	if (fr_checkl4sum(fin) == -1)
799 		return -1;
800 #endif
801 
802 	qif = fin->fin_qif;
803 
804 #ifdef	USE_INET6
805 	mb = fin->fin_qfm;
806 
807 	if (fin->fin_v == 6) {
808 		sz = sizeof(ip6_t);
809 		sz += MIN(mb->b_wptr - mb->b_rptr, 512);
810 		hlen = sizeof(ip6_t);
811 		type = icmptoicmp6types[type];
812 		if (type == ICMP6_DST_UNREACH)
813 			code = icmptoicmp6unreach[code];
814 	} else
815 #endif
816 	{
817 		if ((fin->fin_p == IPPROTO_ICMP) &&
818 		    !(fin->fin_flx & FI_SHORT))
819 			switch (ntohs(fin->fin_data[0]) >> 8)
820 			{
821 			case ICMP_ECHO :
822 			case ICMP_TSTAMP :
823 			case ICMP_IREQ :
824 			case ICMP_MASKREQ :
825 				break;
826 			default :
827 				return 0;
828 			}
829 
830 		sz = sizeof(ip_t) * 2;
831 		sz += 8;		/* 64 bits of data */
832 		hlen = sizeof(ip_t);
833 	}
834 
835 	sz += offsetof(struct icmp, icmp_ip);
836 	if ((m = (mblk_t *)allocb((size_t)sz + 64, BPRI_HI)) == NULL)
837 		return -1;
838 	MTYPE(m) = M_DATA;
839 	m->b_rptr += 64;
840 	m->b_wptr = m->b_rptr + sz;
841 	bzero((char *)m->b_rptr, (size_t)sz);
842 	icmp = (struct icmp *)(m->b_rptr + hlen);
843 	icmp->icmp_type = type & 0xff;
844 	icmp->icmp_code = code & 0xff;
845 #ifndef IRE_ILL_CN
846 #ifdef	icmp_nextmtu
847 	if (type == ICMP_UNREACH && ((il = qif->qf_ill) != NULL) &&
848 	    fin->fin_icode == ICMP_UNREACH_NEEDFRAG)
849 		icmp->icmp_nextmtu = htons(il->ill_max_frag);
850 #endif
851 #endif	/* IRE_ILL_CN */
852 
853 #ifdef	USE_INET6
854 	if (fin->fin_v == 6) {
855 		struct in6_addr dst6;
856 		int csz;
857 
858 		if (dst == 0) {
859 			if (fr_ifpaddr(6, FRI_NORMAL, qif->qf_ill,
860 				       (struct in_addr *)&dst6, NULL) == -1) {
861 				FREE_MB_T(m);
862 				return -1;
863 			}
864 		} else
865 			dst6 = fin->fin_dst6;
866 
867 		csz = sz;
868 		sz -= sizeof(ip6_t);
869 		ip6 = (ip6_t *)m->b_rptr;
870 		ip6->ip6_plen = htons((u_short)sz);
871 		ip6->ip6_nxt = IPPROTO_ICMPV6;
872 		ip6->ip6_src = dst6;
873 		ip6->ip6_dst = fin->fin_src6;
874 		sz -= offsetof(struct icmp, icmp_ip);
875 		bcopy((char *)mb->b_rptr, (char *)&icmp->icmp_ip, sz);
876 		icmp->icmp_cksum = csz - sizeof(ip6_t);
877 	} else
878 #endif
879 	{
880 		ip = (ip_t *)m->b_rptr;
881 		ip->ip_hl = sizeof(*ip) >> 2;
882 		ip->ip_p = IPPROTO_ICMP;
883 		ip->ip_id = fin->fin_ip->ip_id;
884 		ip->ip_tos = fin->fin_ip->ip_tos;
885 		ip->ip_len = htons((u_short)sz);
886 		if (dst == 0) {
887 			if (fr_ifpaddr(4, FRI_NORMAL, qif->qf_ill,
888 				       &dst4, NULL) == -1) {
889 				FREE_MB_T(m);
890 				return -1;
891 			}
892 		} else
893 			dst4 = fin->fin_dst;
894 		ip->ip_src = dst4;
895 		ip->ip_dst = fin->fin_src;
896 		bcopy((char *)fin->fin_ip, (char *)&icmp->icmp_ip,
897 		      sizeof(*fin->fin_ip));
898 		bcopy((char *)fin->fin_ip + fin->fin_hlen,
899 		      (char *)&icmp->icmp_ip + sizeof(*fin->fin_ip), 8);
900 		icmp->icmp_cksum = ipf_cksum((u_short *)icmp,
901 					     sz - sizeof(ip_t));
902 	}
903 
904 	/*
905 	 * Need to exit out of these so we don't recursively call rw_enter
906 	 * from fr_qout.
907 	 */
908 	return fr_send_ip(fin, m);
909 }
910 
911 #ifdef IRE_ILL_CN
912 #include <sys/time.h>
913 #include <sys/varargs.h>
914 
915 #ifndef _KERNEL
916 #include <stdio.h>
917 #endif
918 
919 #define	NULLADDR_RATE_LIMIT 10	/* 10 seconds */
920 
921 
922 /*
923  * Print out warning message at rate-limited speed.
924  */
925 static void rate_limit_message(int rate, const char *message, ...)
926 {
927 	static time_t last_time = 0;
928 	time_t now;
929 	va_list args;
930 	char msg_buf[256];
931 	int  need_printed = 0;
932 
933 	now = ddi_get_time();
934 
935 	/* make sure, no multiple entries */
936 	ASSERT(MUTEX_NOT_HELD(&(ipf_rw.ipf_lk)));
937 	MUTEX_ENTER(&ipf_rw);
938 	if (now - last_time >= rate) {
939 		need_printed = 1;
940 		last_time = now;
941 	}
942 	MUTEX_EXIT(&ipf_rw);
943 
944 	if (need_printed) {
945 		va_start(args, message);
946 		(void)vsnprintf(msg_buf, 255, message, args);
947 		va_end(args);
948 #ifdef _KERNEL
949 		cmn_err(CE_WARN, msg_buf);
950 #else
951 		fprintf(std_err, msg_buf);
952 #endif
953 	}
954 }
955 #endif
956 
957 /*
958  * return the first IP Address associated with an interface
959  */
960 /*ARGSUSED*/
961 int fr_ifpaddr(v, atype, ifptr, inp, inpmask)
962 int v, atype;
963 void *ifptr;
964 struct in_addr *inp, *inpmask;
965 {
966 #ifdef	USE_INET6
967 	struct sockaddr_in6 sin6, mask6;
968 #endif
969 	struct sockaddr_in sin, mask;
970 
971 #ifndef IRE_ILL_CN
972 	ill_t *ill = ifptr;
973 	ipif_t *ipif;
974 #else
975 	s_ill_t *ill = ifptr;
976 #endif /* IRE_ILL_CN */
977 
978 	if ((ifptr == NULL) || (ifptr == (void *)-1))
979 		return -1;
980 
981 #ifdef	USE_INET6
982 	if (v == 6) {
983 #ifndef IRE_ILL_CN
984 		in6_addr_t *inp6;
985 
986 		/*
987 		 * First is always link local.
988 		 */
989 		for (ipif = ill->ill_ipif; ipif; ipif = ipif->ipif_next) {
990 			inp6 = &ipif->ipif_v6lcl_addr;
991 			if (!IN6_IS_ADDR_LINKLOCAL(inp6) &&
992 			    !IN6_IS_ADDR_LOOPBACK(inp6))
993 				break;
994 		}
995 		if (ipif == NULL)
996 			return -1;
997 
998 		mask6.sin6_addr = ipif->ipif_v6net_mask;
999 		if (atype == FRI_BROADCAST)
1000 			sin6.sin6_addr = ipif->ipif_v6brd_addr;
1001 		else if (atype == FRI_PEERADDR)
1002 			sin6.sin6_addr = ipif->ipif_v6pp_dst_addr;
1003 		else
1004 			sin6.sin6_addr = *inp6;
1005 #else /* IRE_ILL_CN */
1006 		if (IN6_IS_ADDR_UNSPECIFIED(ill->netmask.in6) ||
1007 			IN6_IS_ADDR_UNSPECIFIED(ill->localaddr.in6)) {
1008 			rate_limit_message(NULLADDR_RATE_LIMIT,
1009 				"Check pfild is running: "
1010 				"IP#/netmask is 0 on %s.\n"
1011 				ill->ill_name);
1012 			return -1;
1013 		}
1014 		mask6 = ill->netmask.in6;
1015 		if (atype == FRI_BROADCAST)
1016 			sin6 = ill->broadaddr.in6;
1017 		else if (atype == FRI_PEERADDR)
1018 			sin6 = ill->dstaddr.in6;
1019 		else
1020 			sin6 = ill->localaddr.in6;
1021 #endif /* IRE_ILL_CN */
1022 		return fr_ifpfillv6addr(atype, &sin6, &mask6, inp, inpmask);
1023 	}
1024 #endif
1025 #ifndef IRE_ILL_CN
1026 	ipif = ill->ill_ipif;
1027 
1028 	mask.sin_addr.s_addr = ipif->ipif_net_mask;
1029 	if (atype == FRI_BROADCAST)
1030 #if SOLARIS2 < 7
1031 		sin.sin_addr.s_addr = ipif->ipif_broadcast_addr;
1032 #else
1033 		sin.sin_addr.s_addr = ipif->ipif_brd_addr;
1034 #endif
1035 	else if (atype == FRI_PEERADDR)
1036 		sin.sin_addr.s_addr = ipif->ipif_pp_dst_addr;
1037 	else
1038 #if SOLARIS2 < 7
1039 		sin.sin_addr.s_addr = ipif->ipif_local_addr;
1040 #else
1041 		sin.sin_addr.s_addr = ipif->ipif_lcl_addr;
1042 #endif
1043 
1044 #else
1045 	if (ill->netmask.in.sin_addr.s_addr == 0 ||
1046 		ill->localaddr.in.sin_addr.s_addr == 0) {
1047 		rate_limit_message(NULLADDR_RATE_LIMIT,
1048 			"Check pfild is running: IP#/netmask is 0 on %s.\n",
1049 			ill->ill_name);
1050 		return -1;
1051 	}
1052 	mask = ill->netmask.in;
1053 	if (atype == FRI_BROADCAST)
1054 		sin = ill->broadaddr.in;
1055 	else if (atype == FRI_PEERADDR)
1056 		sin = ill->dstaddr.in;
1057 	else
1058 		sin = ill->localaddr.in;
1059 #endif /* IRE_ILL_CN */
1060 	return fr_ifpfillv4addr(atype, &sin, &mask, inp, inpmask);
1061 }
1062 
1063 
1064 
1065 #ifdef IRE_ILL_CN
1066 /* ARGSUSED */
1067 #endif
1068 void fr_resolvdest(fdp, v)
1069 frdest_t *fdp;
1070 int v;
1071 {
1072 #ifndef IRE_ILL_CN
1073 	ipif_t *ipif;
1074 	ill_t *ill;
1075 	ire_t *ire;
1076 
1077 	ire = NULL;
1078 
1079 	if (*fdp->fd_ifname) {
1080 		ill = get_unit(fdp->fd_ifname, v);
1081 		if (ill == NULL)
1082 			ire = (ire_t *)-1;
1083 		else if (((ipif = ill->ill_ipif) != NULL) && (v == 4)) {
1084 #if SOLARIS2 > 5
1085 			ire = ire_ctable_lookup(ipif->ipif_local_addr, 0,
1086 						IRE_LOCAL, NULL, NULL,
1087 						MATCH_IRE_TYPE);
1088 #else
1089 			ire = ire_lookup_myaddr(ipif->ipif_local_addr);
1090 #endif
1091 			if (ire == NULL)
1092 				ire = (ire_t *)-1;
1093 		}
1094 #ifdef	USE_INET6
1095 		else if (((ipif = ill->ill_ipif) != NULL) && (v == 6)) {
1096 			ire = ire_ctable_lookup_v6(&ipif->ipif_v6lcl_addr, 0,
1097 						   IRE_LOCAL, NULL, NULL,
1098 						   MATCH_IRE_TYPE);
1099 			if (ire == NULL)
1100 				ire = (ire_t *)-1;
1101 		}
1102 #endif
1103 	}
1104 	fdp->fd_ifp = (struct ifnet *)ire;
1105 #else
1106 #endif /*IRE_ILL_CN */
1107 }
1108 
1109 
1110 u_32_t fr_newisn(fin)
1111 fr_info_t *fin;
1112 {
1113 	static int iss_seq_off = 0;
1114 	u_char hash[16];
1115 	u_32_t newiss;
1116 	MD5_CTX ctx;
1117 
1118 	/*
1119 	 * Compute the base value of the ISS.  It is a hash
1120 	 * of (saddr, sport, daddr, dport, secret).
1121 	 */
1122 	MD5Init(&ctx);
1123 
1124 	MD5Update(&ctx, (u_char *) &fin->fin_fi.fi_src,
1125 		  sizeof(fin->fin_fi.fi_src));
1126 	MD5Update(&ctx, (u_char *) &fin->fin_fi.fi_dst,
1127 		  sizeof(fin->fin_fi.fi_dst));
1128 	MD5Update(&ctx, (u_char *) &fin->fin_dat, sizeof(fin->fin_dat));
1129 
1130 	MD5Update(&ctx, ipf_iss_secret, sizeof(ipf_iss_secret));
1131 
1132 	MD5Final(hash, &ctx);
1133 
1134 	bcopy(hash, &newiss, sizeof(newiss));
1135 
1136 	/*
1137 	 * Now increment our "timer", and add it in to
1138 	 * the computed value.
1139 	 *
1140 	 * XXX Use `addin'?
1141 	 * XXX TCP_ISSINCR too large to use?
1142 	 */
1143 	iss_seq_off += 0x00010000;
1144 	newiss += iss_seq_off;
1145 	return newiss;
1146 }
1147 
1148 
1149 /* ------------------------------------------------------------------------ */
1150 /* Function:    fr_nextipid                                                 */
1151 /* Returns:     int - 0 == success, -1 == error (packet should be droppped) */
1152 /* Parameters:  fin(I) - pointer to packet information                      */
1153 /*                                                                          */
1154 /* Returns the next IPv4 ID to use for this packet.                         */
1155 /* ------------------------------------------------------------------------ */
1156 INLINE u_short fr_nextipid(fin)
1157 fr_info_t *fin;
1158 {
1159 	static u_short ipid = 0;
1160 	ipstate_t *is;
1161 	nat_t *nat;
1162 	u_short id;
1163 
1164 	MUTEX_ENTER(&ipf_rw);
1165 	if (fin->fin_state != NULL) {
1166 		is = fin->fin_state;
1167 		id = (u_short)(is->is_pkts[(fin->fin_rev << 1) + 1] & 0xffff);
1168 	} else if (fin->fin_nat != NULL) {
1169 		nat = fin->fin_nat;
1170 		id = (u_short)(nat->nat_pkts[fin->fin_out] & 0xffff);
1171 	} else
1172 		id = ipid++;
1173 	MUTEX_EXIT(&ipf_rw);
1174 
1175 	return id;
1176 }
1177 
1178 
1179 #ifndef IPFILTER_CKSUM
1180 /* ARGSUSED */
1181 #endif
1182 INLINE void fr_checkv4sum(fin)
1183 fr_info_t *fin;
1184 {
1185 #ifdef IPFILTER_CKSUM
1186 	if (fr_checkl4sum(fin) == -1)
1187 		fin->fin_flx |= FI_BAD;
1188 #endif
1189 }
1190 
1191 
1192 #ifdef USE_INET6
1193 INLINE void fr_checkv6sum(fin)
1194 fr_info_t *fin;
1195 {
1196 # ifdef IPFILTER_CKSUM
1197 	if (fr_checkl4sum(fin) == -1)
1198 		fin->fin_flx |= FI_BAD;
1199 # endif
1200 }
1201 #endif /* USE_INET6 */
1202