1 /*
2  * Copyright (C) 1999-2001, 2003 by Darren Reed.
3  *
4  * See the IPFILTER.LICENCE file for details on licencing.
5  *
6  * Copyright 2005 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, 0, FR_INQUE|FR_OUTQUE|FR_INACTIVE);
143 	(void) frflush(IPL_LOGIPF, 0, 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, 4, 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 #ifdef USE_INET6
442 	case	SIOCIPFL6 :
443 		if (!(mode & FWRITE))
444 			error = EPERM;
445 		else {
446 			error = COPYIN((caddr_t)data, (caddr_t)&tmp,
447 				       sizeof(tmp));
448 			if (!error) {
449 				tmp = frflush(unit, 6, tmp);
450 				error = COPYOUT((caddr_t)&tmp, (caddr_t)data,
451 					       sizeof(tmp));
452 				if (error != 0)
453 					error = EFAULT;
454 			} else
455 				error = EFAULT;
456 		}
457 		break;
458 #endif
459 	case SIOCSTLCK :
460 		error = COPYIN((caddr_t)data, (caddr_t)&tmp, sizeof(tmp));
461 		if (error == 0) {
462 			fr_state_lock = tmp;
463 			fr_nat_lock = tmp;
464 			fr_frag_lock = tmp;
465 			fr_auth_lock = tmp;
466 		} else
467 			error = EFAULT;
468 	break;
469 #ifdef	IPFILTER_LOG
470 	case	SIOCIPFFB :
471 		if (!(mode & FWRITE))
472 			error = EPERM;
473 		else {
474 			tmp = ipflog_clear(unit);
475 			error = COPYOUT((caddr_t)&tmp, (caddr_t)data,
476 				       sizeof(tmp));
477 			if (error)
478 				error = EFAULT;
479 		}
480 		break;
481 #endif /* IPFILTER_LOG */
482 	case SIOCFRSYN :
483 		if (!(mode & FWRITE))
484 			error = EPERM;
485 		else {
486 			RWLOCK_EXIT(&ipf_global);
487 			WRITE_ENTER(&ipf_global);
488 			error = ipfsync();
489 		}
490 		break;
491 	case SIOCGFRST :
492 		error = fr_outobj((void *)data, fr_fragstats(),
493 				  IPFOBJ_FRAGSTAT);
494 		break;
495 	case FIONREAD :
496 #ifdef	IPFILTER_LOG
497 		tmp = (int)iplused[IPL_LOGIPF];
498 
499 		error = COPYOUT((caddr_t)&tmp, (caddr_t)data, sizeof(tmp));
500 		if (error != 0)
501 			error = EFAULT;
502 #endif
503 		break;
504 	default :
505 		cmn_err(CE_NOTE, "Unknown: cmd 0x%x data %p",
506 			cmd, (void *)data);
507 		error = EINVAL;
508 		break;
509 	}
510 	RWLOCK_EXIT(&ipf_global);
511 	return error;
512 }
513 
514 #ifndef IRE_ILL_CN
515 ill_t	*get_unit(name, v)
516 char	*name;
517 int	v;
518 {
519 	size_t len = strlen(name) + 1;	/* includes \0 */
520 	ill_t *il;
521 #if SOLARIS2 >= 10
522 	ill_walk_context_t ctx;
523 #endif
524 	int sap;
525 
526 	if (v == 4)
527 		sap = 0x0800;
528 	else if (v == 6)
529 		sap = 0x86dd;
530 	else
531 		return NULL;
532 #if SOLARIS2 >= 10
533 	for (il = ILL_START_WALK_ALL(&ctx); il; il = ill_next(&ctx, il))
534 #else
535 	for (il = ill_g_head; il; il = il->ill_next)
536 #endif
537 		if ((len == il->ill_name_length) && (il->ill_sap == sap) &&
538 		    !strncmp(il->ill_name, name, len))
539 			return il;
540 	return NULL;
541 }
542 #else
543 s_ill_t	*get_unit(name, v)
544 char	*name;
545 int	v;
546 {
547 	s_ill_t *il;
548 
549 	int sap;
550 
551 	if (v == 4)
552 		sap = 0x0800;
553 	else if (v == 6)
554 		sap = 0x86dd;
555 	else
556 		return NULL;
557 
558 	mutex_enter(&s_ill_g_head_lock);
559 	for (il = s_ill_g_head; il; il = il->ill_next)
560 		if ((il->ill_sap == sap) &&
561 		    !strncmp(il->ill_name, name, LIFNAMSIZ))
562 			break;
563 	mutex_exit(&s_ill_g_head_lock);
564 	return il;
565 }
566 #endif /* IRE_ILL_CN */
567 
568 
569 /*
570  * routines below for saving IP headers to buffer
571  */
572 /*ARGSUSED*/
573 int iplopen(devp, flags, otype, cred)
574 dev_t *devp;
575 int flags, otype;
576 cred_t *cred;
577 {
578 	minor_t min = getminor(*devp);
579 
580 #ifdef	IPFDEBUG
581 	cmn_err(CE_CONT, "iplopen(%x,%x,%x,%x)\n", devp, flags, otype, cred);
582 #endif
583 	if (!(otype & OTYP_CHR))
584 		return ENXIO;
585 
586 	min = (IPL_LOGMAX < min) ? ENXIO : 0;
587 	return min;
588 }
589 
590 
591 /*ARGSUSED*/
592 int iplclose(dev, flags, otype, cred)
593 dev_t dev;
594 int flags, otype;
595 cred_t *cred;
596 {
597 	minor_t	min = getminor(dev);
598 
599 #ifdef	IPFDEBUG
600 	cmn_err(CE_CONT, "iplclose(%x,%x,%x,%x)\n", dev, flags, otype, cred);
601 #endif
602 
603 	min = (IPL_LOGMAX < min) ? ENXIO : 0;
604 	return min;
605 }
606 
607 #ifdef	IPFILTER_LOG
608 /*
609  * iplread/ipllog
610  * both of these must operate with at least splnet() lest they be
611  * called during packet processing and cause an inconsistancy to appear in
612  * the filter lists.
613  */
614 /*ARGSUSED*/
615 int iplread(dev, uio, cp)
616 dev_t dev;
617 register struct uio *uio;
618 cred_t *cp;
619 {
620 # ifdef	IPFDEBUG
621 	cmn_err(CE_CONT, "iplread(%x,%x,%x)\n", dev, uio, cp);
622 # endif
623 # ifdef	IPFILTER_SYNC
624 	if (getminor(dev) == IPL_LOGSYNC)
625 		return ipfsync_read(uio);
626 # endif
627 
628 	return ipflog_read(getminor(dev), uio);
629 }
630 #endif /* IPFILTER_LOG */
631 
632 
633 #ifdef	IPFILTER_SYNC
634 /*
635  * iplread/ipllog
636  * both of these must operate with at least splnet() lest they be
637  * called during packet processing and cause an inconsistancy to appear in
638  * the filter lists.
639  */
640 int iplwrite(dev, uio, cp)
641 dev_t dev;
642 register struct uio *uio;
643 cred_t *cp;
644 {
645 #ifdef	IPFDEBUG
646 	cmn_err(CE_CONT, "iplwrite(%x,%x,%x)\n", dev, uio, cp);
647 #endif
648 	if (getminor(dev) != IPL_LOGSYNC)
649 		return ENXIO;
650 	return ipfsync_write(uio);
651 }
652 #endif /* IPFILTER_SYNC */
653 
654 
655 /*
656  * fr_send_reset - this could conceivably be a call to tcp_respond(), but that
657  * requires a large amount of setting up and isn't any more efficient.
658  */
659 int fr_send_reset(fin)
660 fr_info_t *fin;
661 {
662 	tcphdr_t *tcp, *tcp2;
663 	int tlen, hlen;
664 	mblk_t *m;
665 #ifdef	USE_INET6
666 	ip6_t *ip6;
667 #endif
668 	ip_t *ip;
669 
670 	tcp = fin->fin_dp;
671 	if (tcp->th_flags & TH_RST)
672 		return -1;
673 
674 #ifndef	IPFILTER_CKSUM
675 	if (fr_checkl4sum(fin) == -1)
676 		return -1;
677 #endif
678 
679 	tlen = (tcp->th_flags & (TH_SYN|TH_FIN)) ? 1 : 0;
680 #ifdef	USE_INET6
681 	if (fin->fin_v == 6)
682 		hlen = sizeof(ip6_t);
683 	else
684 #endif
685 		hlen = sizeof(ip_t);
686 	hlen += sizeof(*tcp2);
687 	if ((m = (mblk_t *)allocb(hlen + 64, BPRI_HI)) == NULL)
688 		return -1;
689 
690 	m->b_rptr += 64;
691 	MTYPE(m) = M_DATA;
692 	m->b_wptr = m->b_rptr + hlen;
693 	bzero((char *)m->b_rptr, hlen);
694 	tcp2 = (struct tcphdr *)(m->b_rptr + hlen - sizeof(*tcp2));
695 	tcp2->th_dport = tcp->th_sport;
696 	tcp2->th_sport = tcp->th_dport;
697 	if (tcp->th_flags & TH_ACK) {
698 		tcp2->th_seq = tcp->th_ack;
699 		tcp2->th_flags = TH_RST;
700 	} else {
701 		tcp2->th_ack = ntohl(tcp->th_seq);
702 		tcp2->th_ack += tlen;
703 		tcp2->th_ack = htonl(tcp2->th_ack);
704 		tcp2->th_flags = TH_RST|TH_ACK;
705 	}
706 	tcp2->th_off = sizeof(struct tcphdr) >> 2;
707 
708 	/*
709 	 * This is to get around a bug in the Solaris 2.4/2.5 TCP checksum
710 	 * computation that is done by their put routine.
711 	 */
712 #ifdef	USE_INET6
713 	if (fin->fin_v == 6) {
714 		ip6 = (ip6_t *)m->b_rptr;
715 		ip6->ip6_flow = ((ip6_t *)fin->fin_ip)->ip6_flow;
716 		ip6->ip6_src = fin->fin_dst6;
717 		ip6->ip6_dst = fin->fin_src6;
718 		ip6->ip6_plen = htons(sizeof(*tcp));
719 		ip6->ip6_nxt = IPPROTO_TCP;
720 		tcp2->th_sum = fr_cksum(m, (ip_t *)ip6, IPPROTO_TCP, tcp2);
721 	} else
722 #endif
723 	{
724 		ip = (ip_t *)m->b_rptr;
725 		ip->ip_src.s_addr = fin->fin_daddr;
726 		ip->ip_dst.s_addr = fin->fin_saddr;
727 		ip->ip_id = fr_nextipid(fin);
728 		ip->ip_hl = sizeof(*ip) >> 2;
729 		ip->ip_p = IPPROTO_TCP;
730 		ip->ip_len = sizeof(*ip) + sizeof(*tcp);
731 		ip->ip_tos = fin->fin_ip->ip_tos;
732 		tcp2->th_sum = fr_cksum(m, ip, IPPROTO_TCP, tcp2);
733 	}
734 	return fr_send_ip(fin, m);
735 }
736 
737 
738 /*
739  * Function:	fr_send_ip
740  * Returns:	 0: success
741  *		-1: failed
742  * Parameters:
743  *	fin: packet information
744  *	m: the message block where ip head starts
745  *
746  * Send a new packet through the IP stack.
747  *
748  * For IPv4 packets, ip_len must be in host byte order, and ip_v,
749  * ip_ttl, ip_off, and ip_sum are ignored (filled in by this
750  * function).
751  *
752  * For IPv6 packets, ip6_flow, ip6_vfc, and ip6_hlim are filled
753  * in by this function.
754  *
755  * All other portions of the packet must be in on-the-wire format.
756  */
757 static int fr_send_ip(fin, m)
758 fr_info_t *fin;
759 mblk_t *m;
760 {
761 	int i;
762 
763 #ifdef	USE_INET6
764 	if (fin->fin_v == 6) {
765 		ip6_t *ip6;
766 
767 		ip6 = (ip6_t *)m->b_rptr;
768 		ip6->ip6_vfc = 0x60;
769 		ip6->ip6_hlim = 127;
770 	} else
771 #endif
772 	{
773 		ip_t *ip;
774 
775 		ip = (ip_t *)m->b_rptr;
776 		ip->ip_v = IPVERSION;
777 
778 #if SOLARIS2 >= 10
779 		ip->ip_ttl = 255;
780 
781 		ip->ip_off = IP_DF;
782 #else
783 		ip->ip_ttl = (u_char)(*ip_ttl_ptr);
784 		ip->ip_off = *ip_mtudisc ? IP_DF : 0;
785 #endif
786 
787 		ip->ip_sum = ipf_cksum((u_short *)ip, sizeof(*ip));
788 
789 	}
790 	i = fr_fastroute(m, &m, fin, NULL);
791 	return i;
792 }
793 
794 
795 int fr_send_icmp_err(type, fin, dst)
796 int type;
797 fr_info_t *fin;
798 int dst;
799 {
800 	struct in_addr dst4;
801 	struct icmp *icmp;
802 	int hlen, code;
803 	qif_t *qif;
804 	u_short sz;
805 #ifdef	USE_INET6
806 	mblk_t *mb;
807 #endif
808 	mblk_t *m;
809 #ifdef	icmp_nextmtu
810 #ifndef IRE_ILL_CN
811 	ill_t *il;
812 #else
813 	s_ill_t *il;
814 #endif	/* IRE_ILL_CN */
815 #endif
816 #ifdef	USE_INET6
817 	ip6_t *ip6;
818 #endif
819 	ip_t *ip;
820 
821 #ifdef	icmp_nextmtu
822 	/* lint fodder */
823 	il = NULL;
824 	il = il;
825 #endif
826 
827 	if ((type < 0) || (type > ICMP_MAXTYPE))
828 		return -1;
829 
830 	code = fin->fin_icode;
831 #ifdef USE_INET6
832 	if ((code < 0) || (code > sizeof(icmptoicmp6unreach)/sizeof(int)))
833 		return -1;
834 #endif
835 
836 #ifndef	IPFILTER_CKSUM
837 	if (fr_checkl4sum(fin) == -1)
838 		return -1;
839 #endif
840 
841 	qif = fin->fin_qif;
842 
843 #ifdef	USE_INET6
844 	mb = fin->fin_qfm;
845 
846 	if (fin->fin_v == 6) {
847 		sz = sizeof(ip6_t);
848 		sz += MIN(mb->b_wptr - mb->b_rptr, 512);
849 		hlen = sizeof(ip6_t);
850 		type = icmptoicmp6types[type];
851 		if (type == ICMP6_DST_UNREACH)
852 			code = icmptoicmp6unreach[code];
853 	} else
854 #endif
855 	{
856 		if ((fin->fin_p == IPPROTO_ICMP) &&
857 		    !(fin->fin_flx & FI_SHORT))
858 			switch (ntohs(fin->fin_data[0]) >> 8)
859 			{
860 			case ICMP_ECHO :
861 			case ICMP_TSTAMP :
862 			case ICMP_IREQ :
863 			case ICMP_MASKREQ :
864 				break;
865 			default :
866 				return 0;
867 			}
868 
869 		sz = sizeof(ip_t) * 2;
870 		sz += 8;		/* 64 bits of data */
871 		hlen = sizeof(ip_t);
872 	}
873 
874 	sz += offsetof(struct icmp, icmp_ip);
875 	if ((m = (mblk_t *)allocb((size_t)sz + 64, BPRI_HI)) == NULL)
876 		return -1;
877 	MTYPE(m) = M_DATA;
878 	m->b_rptr += 64;
879 	m->b_wptr = m->b_rptr + sz;
880 	bzero((char *)m->b_rptr, (size_t)sz);
881 	icmp = (struct icmp *)(m->b_rptr + hlen);
882 	icmp->icmp_type = type & 0xff;
883 	icmp->icmp_code = code & 0xff;
884 #ifndef IRE_ILL_CN
885 #ifdef	icmp_nextmtu
886 	if (type == ICMP_UNREACH && ((il = qif->qf_ill) != NULL) &&
887 	    fin->fin_icode == ICMP_UNREACH_NEEDFRAG)
888 		icmp->icmp_nextmtu = htons(il->ill_max_frag);
889 #endif
890 #endif	/* IRE_ILL_CN */
891 
892 #ifdef	USE_INET6
893 	if (fin->fin_v == 6) {
894 		struct in6_addr dst6;
895 		int csz;
896 
897 		if (dst == 0) {
898 			if (fr_ifpaddr(6, FRI_NORMAL, qif->qf_ill,
899 				       (struct in_addr *)&dst6, NULL) == -1) {
900 				FREE_MB_T(m);
901 				return -1;
902 			}
903 		} else
904 			dst6 = fin->fin_dst6;
905 
906 		csz = sz;
907 		sz -= sizeof(ip6_t);
908 		ip6 = (ip6_t *)m->b_rptr;
909 		ip6->ip6_flow = ((ip6_t *)fin->fin_ip)->ip6_flow;
910 		ip6->ip6_plen = htons((u_short)sz);
911 		ip6->ip6_nxt = IPPROTO_ICMPV6;
912 		ip6->ip6_src = dst6;
913 		ip6->ip6_dst = fin->fin_src6;
914 		sz -= offsetof(struct icmp, icmp_ip);
915 		bcopy((char *)mb->b_rptr, (char *)&icmp->icmp_ip, sz);
916 		icmp->icmp_cksum = csz - sizeof(ip6_t);
917 	} else
918 #endif
919 	{
920 		ip = (ip_t *)m->b_rptr;
921 		ip->ip_hl = sizeof(*ip) >> 2;
922 		ip->ip_p = IPPROTO_ICMP;
923 		ip->ip_id = fin->fin_ip->ip_id;
924 		ip->ip_tos = fin->fin_ip->ip_tos;
925 		ip->ip_len = (u_short)sz;
926 		if (dst == 0) {
927 			if (fr_ifpaddr(4, FRI_NORMAL, qif->qf_ill,
928 				       &dst4, NULL) == -1) {
929 				FREE_MB_T(m);
930 				return -1;
931 			}
932 		} else
933 			dst4 = fin->fin_dst;
934 		ip->ip_src = dst4;
935 		ip->ip_dst = fin->fin_src;
936 		bcopy((char *)fin->fin_ip, (char *)&icmp->icmp_ip,
937 		      sizeof(*fin->fin_ip));
938 		bcopy((char *)fin->fin_ip + fin->fin_hlen,
939 		      (char *)&icmp->icmp_ip + sizeof(*fin->fin_ip), 8);
940 		icmp->icmp_ip.ip_len = htons(icmp->icmp_ip.ip_len);
941 		icmp->icmp_cksum = ipf_cksum((u_short *)icmp,
942 					     sz - sizeof(ip_t));
943 	}
944 
945 	/*
946 	 * Need to exit out of these so we don't recursively call rw_enter
947 	 * from fr_qout.
948 	 */
949 	return fr_send_ip(fin, m);
950 }
951 
952 #ifdef IRE_ILL_CN
953 #include <sys/time.h>
954 #include <sys/varargs.h>
955 
956 #ifndef _KERNEL
957 #include <stdio.h>
958 #endif
959 
960 #define	NULLADDR_RATE_LIMIT 10	/* 10 seconds */
961 
962 
963 /*
964  * Print out warning message at rate-limited speed.
965  */
966 static void rate_limit_message(int rate, const char *message, ...)
967 {
968 	static time_t last_time = 0;
969 	time_t now;
970 	va_list args;
971 	char msg_buf[256];
972 	int  need_printed = 0;
973 
974 	now = ddi_get_time();
975 
976 	/* make sure, no multiple entries */
977 	ASSERT(MUTEX_NOT_HELD(&(ipf_rw.ipf_lk)));
978 	MUTEX_ENTER(&ipf_rw);
979 	if (now - last_time >= rate) {
980 		need_printed = 1;
981 		last_time = now;
982 	}
983 	MUTEX_EXIT(&ipf_rw);
984 
985 	if (need_printed) {
986 		va_start(args, message);
987 		(void)vsnprintf(msg_buf, 255, message, args);
988 		va_end(args);
989 #ifdef _KERNEL
990 		cmn_err(CE_WARN, msg_buf);
991 #else
992 		fprintf(std_err, msg_buf);
993 #endif
994 	}
995 }
996 #endif
997 
998 /*
999  * return the first IP Address associated with an interface
1000  */
1001 /*ARGSUSED*/
1002 int fr_ifpaddr(v, atype, ifptr, inp, inpmask)
1003 int v, atype;
1004 void *ifptr;
1005 struct in_addr *inp, *inpmask;
1006 {
1007 #ifdef	USE_INET6
1008 	struct sockaddr_in6 sin6, mask6;
1009 #endif
1010 	struct sockaddr_in sin, mask;
1011 
1012 #ifndef IRE_ILL_CN
1013 	ill_t *ill = ifptr;
1014 	ipif_t *ipif;
1015 #else
1016 	s_ill_t *ill = ifptr;
1017 #endif /* IRE_ILL_CN */
1018 
1019 	if ((ifptr == NULL) || (ifptr == (void *)-1))
1020 		return -1;
1021 
1022 #ifdef	USE_INET6
1023 	if (v == 6) {
1024 #ifndef IRE_ILL_CN
1025 		in6_addr_t *inp6;
1026 
1027 		/*
1028 		 * First is always link local.
1029 		 */
1030 		for (ipif = ill->ill_ipif; ipif; ipif = ipif->ipif_next) {
1031 			inp6 = &ipif->ipif_v6lcl_addr;
1032 			if (!IN6_IS_ADDR_LINKLOCAL(inp6) &&
1033 			    !IN6_IS_ADDR_LOOPBACK(inp6))
1034 				break;
1035 		}
1036 		if (ipif == NULL)
1037 			return -1;
1038 
1039 		mask6.sin6_addr = ipif->ipif_v6net_mask;
1040 		if (atype == FRI_BROADCAST)
1041 			sin6.sin6_addr = ipif->ipif_v6brd_addr;
1042 		else if (atype == FRI_PEERADDR)
1043 			sin6.sin6_addr = ipif->ipif_v6pp_dst_addr;
1044 		else
1045 			sin6.sin6_addr = *inp6;
1046 #else /* IRE_ILL_CN */
1047 		if (IN6_IS_ADDR_UNSPECIFIED(&ill->netmask.in6.sin6_addr) ||
1048 		    IN6_IS_ADDR_UNSPECIFIED(&ill->localaddr.in6.sin6_addr)) {
1049 			rate_limit_message(NULLADDR_RATE_LIMIT,
1050 			   "Check pfild is running: IP#/netmask is 0 on %s.\n",
1051 			   ill->ill_name);
1052 			return -1;
1053 		}
1054 		mask6 = ill->netmask.in6;
1055 		if (atype == FRI_BROADCAST)
1056 			sin6 = ill->broadaddr.in6;
1057 		else if (atype == FRI_PEERADDR)
1058 			sin6 = ill->dstaddr.in6;
1059 		else
1060 			sin6 = ill->localaddr.in6;
1061 #endif /* IRE_ILL_CN */
1062 		return fr_ifpfillv6addr(atype, &sin6, &mask6, inp, inpmask);
1063 	}
1064 #endif
1065 #ifndef IRE_ILL_CN
1066 	ipif = ill->ill_ipif;
1067 
1068 	mask.sin_addr.s_addr = ipif->ipif_net_mask;
1069 	if (atype == FRI_BROADCAST)
1070 #if SOLARIS2 < 7
1071 		sin.sin_addr.s_addr = ipif->ipif_broadcast_addr;
1072 #else
1073 		sin.sin_addr.s_addr = ipif->ipif_brd_addr;
1074 #endif
1075 	else if (atype == FRI_PEERADDR)
1076 		sin.sin_addr.s_addr = ipif->ipif_pp_dst_addr;
1077 	else
1078 #if SOLARIS2 < 7
1079 		sin.sin_addr.s_addr = ipif->ipif_local_addr;
1080 #else
1081 		sin.sin_addr.s_addr = ipif->ipif_lcl_addr;
1082 #endif
1083 
1084 #else
1085 	if (ill->netmask.in.sin_addr.s_addr == 0 ||
1086 		ill->localaddr.in.sin_addr.s_addr == 0) {
1087 		rate_limit_message(NULLADDR_RATE_LIMIT,
1088 			"Check pfild is running: IP#/netmask is 0 on %s.\n",
1089 			ill->ill_name);
1090 		return -1;
1091 	}
1092 	mask = ill->netmask.in;
1093 	if (atype == FRI_BROADCAST)
1094 		sin = ill->broadaddr.in;
1095 	else if (atype == FRI_PEERADDR)
1096 		sin = ill->dstaddr.in;
1097 	else
1098 		sin = ill->localaddr.in;
1099 #endif /* IRE_ILL_CN */
1100 	return fr_ifpfillv4addr(atype, &sin, &mask, inp, inpmask);
1101 }
1102 
1103 
1104 
1105 #ifdef IRE_ILL_CN
1106 /* ARGSUSED */
1107 #endif
1108 void fr_resolvdest(fdp, v)
1109 frdest_t *fdp;
1110 int v;
1111 {
1112 #ifndef IRE_ILL_CN
1113 	ipif_t *ipif;
1114 	ill_t *ill;
1115 	ire_t *ire;
1116 
1117 	ire = NULL;
1118 
1119 	if (*fdp->fd_ifname) {
1120 		ill = get_unit(fdp->fd_ifname, v);
1121 		if (ill == NULL)
1122 			ire = (ire_t *)-1;
1123 		else if (((ipif = ill->ill_ipif) != NULL) && (v == 4)) {
1124 #if SOLARIS2 > 5
1125 			ire = ire_ctable_lookup(ipif->ipif_local_addr, 0,
1126 						IRE_LOCAL, NULL, NULL,
1127 						MATCH_IRE_TYPE);
1128 #else
1129 			ire = ire_lookup_myaddr(ipif->ipif_local_addr);
1130 #endif
1131 			if (ire == NULL)
1132 				ire = (ire_t *)-1;
1133 		}
1134 #ifdef	USE_INET6
1135 		else if (((ipif = ill->ill_ipif) != NULL) && (v == 6)) {
1136 			ire = ire_ctable_lookup_v6(&ipif->ipif_v6lcl_addr, 0,
1137 						   IRE_LOCAL, NULL, NULL,
1138 						   MATCH_IRE_TYPE);
1139 			if (ire == NULL)
1140 				ire = (ire_t *)-1;
1141 		}
1142 #endif
1143 	}
1144 	fdp->fd_ifp = (struct ifnet *)ire;
1145 #else
1146 #endif /*IRE_ILL_CN */
1147 }
1148 
1149 
1150 u_32_t fr_newisn(fin)
1151 fr_info_t *fin;
1152 {
1153 	static int iss_seq_off = 0;
1154 	u_char hash[16];
1155 	u_32_t newiss;
1156 	MD5_CTX ctx;
1157 
1158 	/*
1159 	 * Compute the base value of the ISS.  It is a hash
1160 	 * of (saddr, sport, daddr, dport, secret).
1161 	 */
1162 	MD5Init(&ctx);
1163 
1164 	MD5Update(&ctx, (u_char *) &fin->fin_fi.fi_src,
1165 		  sizeof(fin->fin_fi.fi_src));
1166 	MD5Update(&ctx, (u_char *) &fin->fin_fi.fi_dst,
1167 		  sizeof(fin->fin_fi.fi_dst));
1168 	MD5Update(&ctx, (u_char *) &fin->fin_dat, sizeof(fin->fin_dat));
1169 
1170 	MD5Update(&ctx, ipf_iss_secret, sizeof(ipf_iss_secret));
1171 
1172 	MD5Final(hash, &ctx);
1173 
1174 	bcopy(hash, &newiss, sizeof(newiss));
1175 
1176 	/*
1177 	 * Now increment our "timer", and add it in to
1178 	 * the computed value.
1179 	 *
1180 	 * XXX Use `addin'?
1181 	 * XXX TCP_ISSINCR too large to use?
1182 	 */
1183 	iss_seq_off += 0x00010000;
1184 	newiss += iss_seq_off;
1185 	return newiss;
1186 }
1187 
1188 
1189 /* ------------------------------------------------------------------------ */
1190 /* Function:    fr_nextipid                                                 */
1191 /* Returns:     int - 0 == success, -1 == error (packet should be droppped) */
1192 /* Parameters:  fin(I) - pointer to packet information                      */
1193 /*                                                                          */
1194 /* Returns the next IPv4 ID to use for this packet.                         */
1195 /* ------------------------------------------------------------------------ */
1196 INLINE u_short fr_nextipid(fin)
1197 fr_info_t *fin;
1198 {
1199 	static u_short ipid = 0;
1200 	ipstate_t *is;
1201 	nat_t *nat;
1202 	u_short id;
1203 
1204 	MUTEX_ENTER(&ipf_rw);
1205 	if (fin->fin_state != NULL) {
1206 		is = fin->fin_state;
1207 		id = (u_short)(is->is_pkts[(fin->fin_rev << 1) + 1] & 0xffff);
1208 	} else if (fin->fin_nat != NULL) {
1209 		nat = fin->fin_nat;
1210 		id = (u_short)(nat->nat_pkts[fin->fin_out] & 0xffff);
1211 	} else
1212 		id = ipid++;
1213 	MUTEX_EXIT(&ipf_rw);
1214 
1215 	return id;
1216 }
1217 
1218 
1219 #ifndef IPFILTER_CKSUM
1220 /* ARGSUSED */
1221 #endif
1222 INLINE void fr_checkv4sum(fin)
1223 fr_info_t *fin;
1224 {
1225 #ifdef IPFILTER_CKSUM
1226 	if (fr_checkl4sum(fin) == -1)
1227 		fin->fin_flx |= FI_BAD;
1228 #endif
1229 }
1230 
1231 
1232 #ifdef USE_INET6
1233 /* ARGSUSED */
1234 INLINE void fr_checkv6sum(fin)
1235 fr_info_t *fin;
1236 {
1237 # ifdef IPFILTER_CKSUM
1238 	if (fr_checkl4sum(fin) == -1)
1239 		fin->fin_flx |= FI_BAD;
1240 # endif
1241 }
1242 #endif /* USE_INET6 */
1243