2 * (C)opyright 1993-1996 by Darren Reed.
4 * Redistribution and use in source and binary forms are permitted
5 * provided that this notice is preserved and due credit is given
6 * to the original author and the contributors.
8 #if !defined(lint) && defined(LIBC_SCCS)
9 static char sccsid[] = "@(#)fil.c 1.36 6/5/96 (C) 1993-1996 Darren Reed";
10 static char rcsid[] = "$Id: fil.c,v 2.0.2.7 1997/04/02 12:23:15 darrenr Exp $";
13 #include <sys/errno.h>
14 #include <sys/types.h>
15 #include <sys/param.h>
18 #include <sys/ioctl.h>
19 #if defined(_KERNEL) || defined(KERNEL)
20 # include <sys/systm.h>
26 #if !defined(__SVR4) && !defined(__svr4__)
27 # include <sys/mbuf.h>
29 # include <sys/byteorder.h>
30 # include <sys/dditypes.h>
31 # include <sys/stream.h>
33 #include <sys/protosw.h>
34 #include <sys/socket.h>
39 #include <net/route.h>
40 #include <netinet/in.h>
41 #include <netinet/in_systm.h>
42 #include <netinet/ip.h>
43 #include <netinet/ip_var.h>
44 #include <netinet/tcp.h>
45 #include <netinet/udp.h>
46 #include <netinet/tcpip.h>
47 #include <netinet/ip_icmp.h>
48 #include "ip_compat.h"
54 #define MIN(a,b) (((a)<(b))?(a):(b))
62 # define FR_IFVERBOSE(ex,second,verb_pr) if (ex) { verbose verb_pr; \
64 # define FR_IFDEBUG(ex,second,verb_pr) if (ex) { debug verb_pr; \
66 # define FR_VERBOSE(verb_pr) verbose verb_pr
67 # define FR_DEBUG(verb_pr) debug verb_pr
68 # define FR_SCANLIST(p, ip, fi, m) fr_scanlist(p, ip, fi, m)
69 # define SEND_RESET(ip, qif, q, if) send_reset(ip, if)
70 # define IPLLOG(a, c, d, e) ipllog()
72 # define ICMP_ERROR(b, ip, t, c, if, src) icmp_error(ip)
75 # define ICMP_ERROR(b, ip, t, c, if, src) icmp_error(b, ip, if)
78 #else /* #ifndef _KERNEL */
79 # define FR_IFVERBOSE(ex,second,verb_pr) ;
80 # define FR_IFDEBUG(ex,second,verb_pr) ;
81 # define FR_VERBOSE(verb_pr)
82 # define FR_DEBUG(verb_pr)
83 # define FR_SCANLIST(p, ip, fi, m) fr_scanlist(p, ip, fi, m)
84 # define IPLLOG(a, c, d, e) ipllog(a, IPL_LOGIPF, c, d, e)
86 extern kmutex_t ipf_mutex;
87 # define SEND_RESET(ip, qif, q, if) send_reset(ip, qif, q)
88 # define ICMP_ERROR(b, ip, t, c, if, src) \
89 icmp_error(b, ip, t, c, if, src)
91 # define FR_SCANLIST(p, ip, fi, m) fr_scanlist(p, ip, fi, m)
92 # define SEND_RESET(ip, qif, q, if) send_reset((struct tcpiphdr *)ip)
94 # define ICMP_ERROR(b, ip, t, c, if, src) \
95 icmp_error(mtod(b, ip_t *), t, c, if, src)
97 # define ICMP_ERROR(b, ip, t, c, if, src) \
98 icmp_error(b, t, c, (src).s_addr, if)
104 #define IPF_LOGGING 0
106 #ifdef IPF_DEFAULT_PASS
107 #define IPF_NOMATCH (IPF_DEFAULT_PASS|FR_NOMATCH)
109 #define IPF_NOMATCH (FR_PASS|FR_NOMATCH)
112 struct filterstats frstats[2] = {{0,0,0,0,0},{0,0,0,0,0}};
113 struct frentry *ipfilter[2][2] = { { NULL, NULL }, { NULL, NULL } },
114 *ipacct[2][2] = { { NULL, NULL }, { NULL, NULL } };
115 int fr_flags = IPF_LOGGING, fr_active = 0;
117 fr_info_t frcache[2];
119 static void fr_makefrip __P((int, ip_t *, fr_info_t *));
120 static int fr_tcpudpchk __P((frentry_t *, fr_info_t *));
121 static int fr_scanlist __P((int, ip_t *, fr_info_t *, void *));
125 * bit values for identifying presence of individual IP options
127 struct optlist ipopts[20] = {
128 { IPOPT_NOP, 0x000001 },
129 { IPOPT_RR, 0x000002 },
130 { IPOPT_ZSU, 0x000004 },
131 { IPOPT_MTUP, 0x000008 },
132 { IPOPT_MTUR, 0x000010 },
133 { IPOPT_ENCODE, 0x000020 },
134 { IPOPT_TS, 0x000040 },
135 { IPOPT_TR, 0x000080 },
136 { IPOPT_SECURITY, 0x000100 },
137 { IPOPT_LSRR, 0x000200 },
138 { IPOPT_E_SEC, 0x000400 },
139 { IPOPT_CIPSO, 0x000800 },
140 { IPOPT_SATID, 0x001000 },
141 { IPOPT_SSRR, 0x002000 },
142 { IPOPT_ADDEXT, 0x004000 },
143 { IPOPT_VISA, 0x008000 },
144 { IPOPT_IMITD, 0x010000 },
145 { IPOPT_EIP, 0x020000 },
146 { IPOPT_FINN, 0x040000 },
151 * bit values for identifying presence of individual IP security options
153 struct optlist secopt[8] = {
154 { IPSO_CLASS_RES4, 0x01 },
155 { IPSO_CLASS_TOPS, 0x02 },
156 { IPSO_CLASS_SECR, 0x04 },
157 { IPSO_CLASS_RES3, 0x08 },
158 { IPSO_CLASS_CONF, 0x10 },
159 { IPSO_CLASS_UNCL, 0x20 },
160 { IPSO_CLASS_RES2, 0x40 },
161 { IPSO_CLASS_RES1, 0x80 }
166 * compact the IP header into a structure which contains just the info.
167 * which is useful for comparing IP headers with.
169 static void fr_makefrip(hlen, ip, fin)
176 fr_ip_t *fi = &fin->fin_fi;
177 u_short optmsk = 0, secmsk = 0, auth = 0;
183 fin->fin_data[0] = 0;
184 fin->fin_data[1] = 0;
187 fin->fin_icode = ipl_unreach;
190 fi->fi_tos = ip->ip_tos;
191 fin->fin_hlen = hlen;
192 fin->fin_dlen = ip->ip_len - hlen;
193 tcp = (tcphdr_t *)((char *)ip + hlen);
194 fin->fin_dp = (void *)tcp;
195 (*(((u_short *)fi) + 1)) = (*(((u_short *)ip) + 4));
196 (*(((u_long *)fi) + 1)) = (*(((u_long *)ip) + 3));
197 (*(((u_long *)fi) + 2)) = (*(((u_long *)ip) + 4));
199 fi->fi_fl = (hlen > sizeof(struct ip)) ? FI_OPTIONS : 0;
200 off = (ip->ip_off & 0x1fff) << 3;
201 if (ip->ip_off & 0x3fff)
202 fi->fi_fl |= FI_FRAG;
206 if ((!IPMINLEN(ip, icmp) && !off) ||
207 (off && off < sizeof(struct icmp)))
208 fi->fi_fl |= FI_SHORT;
209 if (fin->fin_dlen > 1)
210 fin->fin_data[0] = *(u_short *)tcp;
213 fi->fi_fl |= FI_TCPUDP;
214 if ((!IPMINLEN(ip, tcphdr) && !off) ||
215 (off && off < sizeof(struct tcphdr)))
216 fi->fi_fl |= FI_SHORT;
217 if (!(fi->fi_fl & FI_SHORT) && !off)
218 fin->fin_tcpf = tcp->th_flags;
221 fi->fi_fl |= FI_TCPUDP;
222 if ((!IPMINLEN(ip, udphdr) && !off) ||
223 (off && off < sizeof(struct udphdr)))
224 fi->fi_fl |= FI_SHORT;
226 if (!off && (fin->fin_dlen > 3)) {
227 fin->fin_data[0] = ntohs(tcp->th_sport);
228 fin->fin_data[1] = ntohs(tcp->th_dport);
236 for (s = (u_char *)(ip + 1), hlen -= sizeof(*ip); hlen; ) {
239 ol = (opt == IPOPT_NOP) ? 1 : (int)*(s+1);
240 if (opt > 1 && (ol < 2 || ol > hlen))
242 for (i = 9, mv = 4; mv >= 0; ) {
244 if (opt == (u_char)op->ol_val) {
245 optmsk |= op->ol_bit;
246 if (opt == IPOPT_SECURITY) {
251 sec = *(s + 2); /* classification */
252 for (j = 3, m = 2; m >= 0; ) {
254 if (sec == sp->ol_val) {
255 secmsk |= sp->ol_bit;
261 if (sec < sp->ol_val)
269 if (opt < op->ol_val)
277 if (auth && !(auth & 0x0100))
279 fi->fi_optmsk = optmsk;
280 fi->fi_secmsk = secmsk;
286 * check an IP packet for TCP/UDP characteristics such as ports and flags.
288 static int fr_tcpudpchk(fr, fin)
292 register u_short po, tup;
294 register int err = 1;
297 * Both ports should *always* be in the first fragment.
298 * So far, I cannot find any cases where they can not be.
300 * compare destination ports
302 if ((i = (int)fr->fr_dcmp)) {
304 tup = fin->fin_data[1];
306 * Do opposite test to that required and
307 * continue if that succeeds.
309 if (!--i && tup != po) /* EQUAL */
311 else if (!--i && tup == po) /* NOTEQUAL */
313 else if (!--i && tup >= po) /* LESSTHAN */
315 else if (!--i && tup <= po) /* GREATERTHAN */
317 else if (!--i && tup > po) /* LT or EQ */
319 else if (!--i && tup < po) /* GT or EQ */
321 else if (!--i && /* Out of range */
322 (tup >= po && tup <= fr->fr_dtop))
324 else if (!--i && /* In range */
325 (tup <= po || tup >= fr->fr_dtop))
329 * compare source ports
331 if (err && (i = (int)fr->fr_scmp)) {
333 tup = fin->fin_data[0];
334 if (!--i && tup != po)
336 else if (!--i && tup == po)
338 else if (!--i && tup >= po)
340 else if (!--i && tup <= po)
342 else if (!--i && tup > po)
344 else if (!--i && tup < po)
346 else if (!--i && /* Out of range */
347 (tup >= po && tup <= fr->fr_stop))
349 else if (!--i && /* In range */
350 (tup <= po || tup >= fr->fr_stop))
355 * If we don't have all the TCP/UDP header, then how can we
356 * expect to do any sort of match on it ? If we were looking for
357 * TCP flags, then NO match. If not, then match (which should
358 * satisfy the "short" class too).
360 if (err && (fin->fin_fi.fi_p == IPPROTO_TCP)) {
361 if (fin->fin_fi.fi_fl & FI_SHORT)
362 return !(fr->fr_tcpf | fr->fr_tcpfm);
364 * Match the flags ? If not, abort this match.
367 fr->fr_tcpf != (fin->fin_tcpf & fr->fr_tcpfm)) {
368 FR_DEBUG(("f. %#x & %#x != %#x\n", fin->fin_tcpf,
369 fr->fr_tcpfm, fr->fr_tcpf));
377 * Check the input/output list of rules for a match and result.
378 * Could be per interface, but this gets real nasty when you don't have
381 static int fr_scanlist(pass, ip, fin, m)
384 register fr_info_t *fin;
387 register struct frentry *fr;
388 register fr_ip_t *fi = &fin->fin_fi;
389 int rulen, portcmp = 0, off;
394 off = ip->ip_off & 0x1fff;
395 pass |= (fi->fi_fl << 20);
397 if ((fi->fi_fl & FI_TCPUDP) && (fin->fin_dlen > 3) && !off)
400 for (rulen = 0; fr; fr = fr->fr_next, rulen++) {
402 * In all checks below, a null (zero) value in the
403 * filter struture is taken to mean a wildcard.
405 * check that we are working for the right interface
408 if (fr->fr_ifa && fr->fr_ifa != fin->fin_ifp)
411 if (opts & (OPT_VERBOSE|OPT_DEBUG))
413 FR_VERBOSE(("%c", (pass & FR_PASS) ? 'p' : 'b'));
414 if (fr->fr_ifa && fr->fr_ifa != fin->fin_ifp)
419 register u_long *ld, *lm, *lip;
423 lm = (u_long *)&fr->fr_mip;
424 ld = (u_long *)&fr->fr_ip;
425 i = ((lip[0] & lm[0]) != ld[0]);
426 FR_IFDEBUG(i,continue,("0. %#08x & %#08x != %#08x\n",
427 lip[0], lm[0], ld[0]));
428 i |= ((lip[1] & lm[1]) != ld[1]);
429 FR_IFDEBUG(i,continue,("1. %#08x & %#08x != %#08x\n",
430 lip[1], lm[1], ld[1]));
431 i |= ((lip[2] & lm[2]) != ld[2]);
432 FR_IFDEBUG(i,continue,("2. %#08x & %#08x != %#08x\n",
433 lip[2], lm[2], ld[2]));
434 i |= ((lip[3] & lm[3]) != ld[3]);
435 FR_IFDEBUG(i,continue,("3. %#08x & %#08x != %#08x\n",
436 lip[3], lm[3], ld[3]));
437 i |= ((lip[4] & lm[4]) != ld[4]);
438 FR_IFDEBUG(i,continue,("4. %#08x & %#08x != %#08x\n",
439 lip[4], lm[4], ld[4]));
445 * If a fragment, then only the first has what we're looking
448 if (fi->fi_fl & FI_TCPUDP) {
450 if (!fr_tcpudpchk(fr, fin))
452 } else if (fr->fr_dcmp || fr->fr_scmp || fr->fr_tcpf ||
455 } else if (fi->fi_p == IPPROTO_ICMP) {
456 if (!off && (fin->fin_dlen > 1)) {
457 if ((fin->fin_data[0] & fr->fr_icmpm) !=
459 FR_DEBUG(("i. %#x & %#x != %#x\n",
461 fr->fr_icmpm, fr->fr_icmp));
464 } else if (fr->fr_icmpm || fr->fr_icmp)
469 * Just log this packet...
472 if ((pass & FR_CALLNOW) && fr->fr_func)
473 pass = (*fr->fr_func)(pass, ip, fin);
475 if ((pass & FR_LOGMASK) == FR_LOG) {
476 if (!IPLLOG(fr->fr_flags, ip, fin, m))
477 frstats[fin->fin_out].fr_skip++;
478 frstats[fin->fin_out].fr_pkl++;
480 #endif /* IPFILTER_LOG */
481 FR_DEBUG(("pass %#x\n", pass));
483 if (pass & FR_ACCOUNT)
484 fr->fr_bytes += (U_QUAD_T)ip->ip_len;
486 fin->fin_icode = fr->fr_icode;
487 fin->fin_rule = rulen;
497 * frcheck - filter check
498 * check using source and destination addresses/pors in a packet whether
499 * or not to pass it on or not.
501 int fr_check(ip, hlen, ifp, out
522 * The above really sucks, but short of writing a diff
524 fr_info_t frinfo, *fc;
525 register fr_info_t *fin = &frinfo;
526 frentry_t *fr = NULL;
529 char *mc = mp, *m = mp;
533 # if !defined(__SVR4) && !defined(__svr4__)
534 register struct mbuf *m = *mp;
535 struct mbuf *mc = NULL;
537 if ((ip->ip_p == IPPROTO_TCP || ip->ip_p == IPPROTO_UDP ||
538 ip->ip_p == IPPROTO_ICMP)) {
539 register int up = MIN(hlen + 8, ip->ip_len);
542 if ((*mp = m_pullup(m, up)) == 0) {
543 frstats[out].fr_pull[1]++;
546 frstats[out].fr_pull[0]++;
548 ip = mtod(m, struct ip *);
554 mblk_t *mc = NULL, *m = qif->qf_m;
557 fr_makefrip(hlen, ip, fin);
561 MUTEX_ENTER(&ipf_mutex);
563 changed = ip_natin(ip, hlen, fin);
564 if ((fin->fin_fr = ipacct[0][fr_active]) &&
565 (FR_SCANLIST(FR_NOMATCH, ip, fin, m) & FR_ACCOUNT))
566 frstats[0].fr_acct++;
569 if ((pass = ipfr_knownfrag(ip, fin))) {
570 if ((pass & FR_KEEPSTATE)) {
571 if (fr_addstate(ip, fin, pass) == -1)
572 frstats[out].fr_bads++;
574 frstats[out].fr_ads++;
576 } else if ((pass = fr_checkstate(ip, fin))) {
577 if ((pass & FR_KEEPFRAG)) {
578 if (fin->fin_fi.fi_fl & FI_FRAG) {
579 if (ipfr_newfrag(ip, fin, pass) == -1)
580 frstats[out].fr_bnfr++;
582 frstats[out].fr_nfr++;
584 frstats[out].fr_cfr++;
588 if (fc->fin_fr && !bcmp((char *)fin, (char *)fc, FI_CSIZE)) {
590 * copy cached data so we can unlock the mutex
593 bcopy((char *)fc, (char *)fin, sizeof(*fin));
594 frstats[out].fr_chit++;
595 pass = fin->fin_fr->fr_flags;
598 if ((fin->fin_fr = ipfilter[out][fr_active]))
599 pass = FR_SCANLIST(IPF_NOMATCH, ip, fin, m);
600 bcopy((char *)fin, (char *)fc, FI_CSIZE);
601 if (pass & FR_NOMATCH)
602 frstats[out].fr_nom++;
606 if ((pass & FR_KEEPFRAG)) {
607 if (fin->fin_fi.fi_fl & FI_FRAG) {
608 if (ipfr_newfrag(ip, fin, pass) == -1)
609 frstats[out].fr_bnfr++;
611 frstats[out].fr_nfr++;
613 frstats[out].fr_cfr++;
615 if (pass & FR_KEEPSTATE) {
616 if (fr_addstate(ip, fin, pass) == -1)
617 frstats[out].fr_bads++;
619 frstats[out].fr_ads++;
623 if (fr && fr->fr_func && !(pass & FR_CALLNOW))
624 pass = (*fr->fr_func)(pass, ip, fin);
627 if ((fin->fin_fr = ipacct[1][fr_active]) &&
628 (FR_SCANLIST(FR_NOMATCH, ip, fin, m) & FR_ACCOUNT))
629 frstats[1].fr_acct++;
631 changed = ip_natout(ip, hlen, fin);
634 MUTEX_EXIT(&ipf_mutex);
637 if ((fr_flags & FF_LOGGING) || (pass & FR_LOGMASK)) {
638 if ((fr_flags & FF_LOGNOMATCH) && (pass & FR_NOMATCH)) {
639 pass |= FF_LOGNOMATCH;
640 frstats[out].fr_npkl++;
642 } else if (((pass & FR_LOGMASK) == FR_LOGP) ||
643 ((pass & FR_PASS) && (fr_flags & FF_LOGPASS))) {
644 if ((pass & FR_LOGMASK) != FR_LOGP)
646 frstats[out].fr_ppkl++;
648 } else if (((pass & FR_LOGMASK) == FR_LOGB) ||
649 ((pass & FR_BLOCK) && (fr_flags & FF_LOGBLOCK))) {
650 if ((pass & FR_LOGMASK) != FR_LOGB)
652 frstats[out].fr_bpkl++;
654 if (!IPLLOG(pass, ip, fin, m)) {
655 frstats[out].fr_skip++;
656 if ((pass & (FR_PASS|FR_LOGORBLOCK)) ==
657 (FR_PASS|FR_LOGORBLOCK))
658 pass ^= FR_PASS|FR_BLOCK;
662 #endif /* IPFILTER_LOG */
665 frstats[out].fr_pass++;
666 else if (pass & FR_BLOCK) {
667 frstats[out].fr_block++;
669 * Should we return an ICMP packet to indicate error
670 * status passing through the packet filter ?
671 * WARNING: ICMP error packets AND TCP RST packets should
672 * ONLY be sent in repsonse to incoming packets. Sending them
673 * in response to outbound packets can result in a panic on
674 * some operating systems.
678 if (pass & FR_RETICMP) {
680 ICMP_ERROR(q, ip, ICMP_UNREACH, fin->fin_icode,
683 ICMP_ERROR(m, ip, ICMP_UNREACH, fin->fin_icode,
685 m = *mp = NULL; /* freed by icmp_error() */
689 } else if ((pass & FR_RETRST) &&
690 !(fin->fin_fi.fi_fl & FI_SHORT)) {
691 if (SEND_RESET(ip, qif, q, ifp) == 0)
695 if (pass & FR_RETICMP) {
696 verbose("- ICMP unreachable sent\n");
698 } else if ((pass & FR_RETRST) &&
699 !(fin->fin_fi.fi_fl & FI_SHORT)) {
700 verbose("- TCP RST sent\n");
709 mc = m_copy(m, 0, M_COPYALL);
711 frdest_t *fdp = &fr->fr_tif;
713 if ((pass & FR_FASTROUTE) ||
714 (fdp->fd_ifp && fdp->fd_ifp != (struct ifnet *)-1)) {
715 ipfr_fastroute(m, fin, fdp);
719 ipfr_fastroute(mc, fin, &fr->fr_dif);
721 if (!(pass & FR_PASS) && m)
723 return (pass & FR_PASS) ? 0 : -1;
728 frdest_t *fdp = &fr->fr_tif;
730 if ((pass & FR_FASTROUTE) ||
731 (fdp->fd_ifp && fdp->fd_ifp != (struct ifnet *)-1)) {
732 ipfr_fastroute(qif, ip, m, mp, fin, fdp);
736 ipfr_fastroute(qif, ip, mc, mp, fin, &fr->fr_dif);
738 return (pass & FR_PASS) ? changed : -1;
741 if (pass & FR_NOMATCH)
751 int fr_copytolog(dev, buf, len)
756 register char *bufp = iplbuf[dev], *tp = iplt[dev], *hp = iplh[dev];
757 register int clen, tail;
759 tail = (hp >= tp) ? (bufp + IPLLOGSIZE - hp) : (tp - hp);
760 clen = MIN(tail, len);
761 bcopy(buf, hp, clen);
766 if (hp == bufp + IPLLOGSIZE) {
771 clen = MIN(tail, len);
772 bcopy(buf, hp, clen);