diff options
author | dgregor <dgregor@cvs.openbsd.org> | 1998-01-26 04:10:47 +0000 |
---|---|---|
committer | dgregor <dgregor@cvs.openbsd.org> | 1998-01-26 04:10:47 +0000 |
commit | 3bc04ed9d84fa464f1185f97ecbf0d42a35278d5 (patch) | |
tree | 4ee1d204830b235bf592a7f663cfabdc7bf34a5a /sys/netinet/fil.c | |
parent | a95cd44675061863d776c0b405c6cffa4721f915 (diff) |
IPF 3.2.3
Diffstat (limited to 'sys/netinet/fil.c')
-rw-r--r-- | sys/netinet/fil.c | 905 |
1 files changed, 710 insertions, 195 deletions
diff --git a/sys/netinet/fil.c b/sys/netinet/fil.c index 8bbb4f70aad..ec88678a4d2 100644 --- a/sys/netinet/fil.c +++ b/sys/netinet/fil.c @@ -1,24 +1,22 @@ -/* $OpenBSD: fil.c,v 1.10 1997/06/23 19:03:47 kstailey Exp $ */ /* - * (C)opyright 1993-1996 by Darren Reed. + * Copyright (C) 1993-1997 by Darren Reed. * * Redistribution and use in source and binary forms are permitted * provided that this notice is preserved and due credit is given * to the original author and the contributors. */ -#if 0 -#if !defined(lint) && defined(LIBC_SCCS) -static char sccsid[] = "@(#)fil.c 1.36 6/5/96 (C) 1993-1996 Darren Reed"; -static char rcsid[] = "$DRId: fil.c,v 2.0.1.10 1997/04/13 22:33:07 darrenr Exp $"; -#endif +#if !defined(lint) +static const char sccsid[] = "@(#)fil.c 1.36 6/5/96 (C) 1993-1996 Darren Reed"; +static const char rcsid[] = "@(#)$Id: fil.c,v 1.11 1998/01/26 04:10:37 dgregor Exp $"; #endif #include <sys/errno.h> #include <sys/types.h> #include <sys/param.h> +#include <sys/time.h> #include <sys/file.h> #include <sys/ioctl.h> -#if defined(_KERNEL) || defined(KERNEL) +#if (defined(_KERNEL) || defined(KERNEL)) && !defined(linux) # include <sys/systm.h> #else # include <stdio.h> @@ -26,14 +24,18 @@ static char rcsid[] = "$DRId: fil.c,v 2.0.1.10 1997/04/13 22:33:07 darrenr Exp $ #endif #include <sys/uio.h> #if !defined(__SVR4) && !defined(__svr4__) -# include <sys/mbuf.h> +# ifndef linux +# include <sys/mbuf.h> +# endif #else # include <sys/byteorder.h> # include <sys/dditypes.h> # include <sys/stream.h> #endif -#include <sys/protosw.h> -#include <sys/socket.h> +#ifndef linux +# include <sys/protosw.h> +# include <sys/socket.h> +#endif #include <net/if.h> #ifdef sun # include <net/af.h> @@ -42,86 +44,103 @@ static char rcsid[] = "$DRId: fil.c,v 2.0.1.10 1997/04/13 22:33:07 darrenr Exp $ #include <netinet/in.h> #include <netinet/in_systm.h> #include <netinet/ip.h> -#include <netinet/ip_var.h> +#ifndef linux +# include <netinet/ip_var.h> +#endif #include <netinet/tcp.h> #include <netinet/udp.h> -#include <netinet/tcpip.h> #include <netinet/ip_icmp.h> #include "ip_fil_compat.h" +#include <netinet/tcpip.h> #include "ip_fil.h" +#include "ip_proxy.h" #include "ip_nat.h" #include "ip_frag.h" #include "ip_state.h" +#include "ip_auth.h" #ifndef MIN #define MIN(a,b) (((a)<(b))?(a):(b)) #endif #ifndef _KERNEL -#include "ipf.h" +# include "ipf.h" +# include "ipt.h" extern int opts; -extern void debug(), verbose(); -#define FR_IFVERBOSE(ex,second,verb_pr) if (ex) { verbose verb_pr; second; } -#define FR_IFDEBUG(ex,second,verb_pr) if (ex) { debug verb_pr; second; } -#define FR_VERBOSE(verb_pr) verbose verb_pr -#define FR_DEBUG(verb_pr) debug verb_pr -#define FR_SCANLIST(p, ip, fi, m) fr_scanlist(p, ip, fi, NULL) +# define FR_IFVERBOSE(ex,second,verb_pr) if (ex) { verbose verb_pr; \ + second; } +# define FR_IFDEBUG(ex,second,verb_pr) if (ex) { debug verb_pr; \ + second; } +# define FR_VERBOSE(verb_pr) verbose verb_pr +# define FR_DEBUG(verb_pr) debug verb_pr +# define SEND_RESET(ip, qif, if, m) send_reset(ip, if) +# define IPLLOG(a, c, d, e) ipllog() +# define FR_NEWAUTH(m, fi, ip, qif) fr_newauth((mb_t *)m, fi, ip) # if SOLARIS -# define bcmp memcmp -# endif -#else -#define FR_IFVERBOSE(ex,second,verb_pr) ; -#define FR_IFDEBUG(ex,second,verb_pr) ; -#define FR_VERBOSE(verb_pr) -#define FR_DEBUG(verb_pr) -#define FR_SCANLIST(p, ip, fi, m) fr_scanlist(p, ip, fi, m) -extern int send_reset __P((struct tcpiphdr *)); -# if SOLARIS -extern int icmp_error(), ipfr_fastroute(); -extern kmutex_t ipf_mutex, ipl_mutex; +# define ICMP_ERROR(b, ip, t, c, if, src) icmp_error(ip) # else -extern void ipfr_fastroute __P((struct mbuf *, fr_info_t *, frdest_t *)); +# define ICMP_ERROR(b, ip, t, c, if, src) icmp_error(b, ip, if) # endif -extern int ipl_unreach; -extern int ipllog __P((u_int, ip_t *, register fr_info_t *, - struct mbuf *)); -#endif - -#if SOLARIS -# define SEND_RESET(ip, if, q) send_reset(ip, qif, q) -# define ICMP_ERROR(b, ip, t, c, if, src) \ - icmp_error(b, ip, t, c, if, src) -#else -# define SEND_RESET(ip, if, q) send_reset( \ - (struct tcpiphdr *)ip) -# if BSD < 199103 +#else /* #ifndef _KERNEL */ +# define FR_IFVERBOSE(ex,second,verb_pr) ; +# define FR_IFDEBUG(ex,second,verb_pr) ; +# define FR_VERBOSE(verb_pr) +# define FR_DEBUG(verb_pr) +# define IPLLOG(a, c, d, e) ipflog(a, c, d, e) +# if SOLARIS || defined(__sgi) +extern kmutex_t ipf_mutex, ipf_auth; +# endif +# if SOLARIS +# define FR_NEWAUTH(m, fi, ip, qif) fr_newauth((mb_t *)m, fi, \ + ip, qif) +# define SEND_RESET(ip, qif, if) send_reset(ip, qif) # define ICMP_ERROR(b, ip, t, c, if, src) \ + icmp_error(ip, t, c, if, src) +# else /* SOLARIS */ +# define FR_NEWAUTH(m, fi, ip, qif) fr_newauth((mb_t *)m, fi, ip) +# ifdef linux +# define SEND_RESET(ip, qif, if) send_reset((tcpiphdr_t *)ip,\ + ifp) +# else +# define SEND_RESET(ip, qif, if) send_reset((tcpiphdr_t *)ip) +# endif +# ifdef __sgi +# define ICMP_ERROR(b, ip, t, c, if, src) \ + icmp_error(b, t, c, if, src, if) +# else +# if BSD < 199103 +# ifdef linux +# define ICMP_ERROR(b, ip, t, c, if, src) icmp_send(b,t,c,0,if) +# else +# define ICMP_ERROR(b, ip, t, c, if, src) \ icmp_error(mtod(b, ip_t *), t, c, if, src) -# else -# define ICMP_ERROR(b, ip, t, c, if, src) \ +# endif /* linux */ +# else +# define ICMP_ERROR(b, ip, t, c, if, src) \ icmp_error(b, t, c, (src).s_addr, if) -# endif -#endif +# endif /* BSD < 199103 */ +# endif /* __sgi */ +# endif /* SOLARIS || __sgi */ +#endif /* _KERNEL */ -#ifndef IPF_LOGGING -#define IPF_LOGGING 0 -#endif -#ifdef IPF_DEFAULT_PASS -#define IPF_NOMATCH (IPF_DEFAULT_PASS|FR_NOMATCH) -#else -#define IPF_NOMATCH (FR_PASS|FR_NOMATCH) -#endif struct filterstats frstats[2] = {{0,0,0,0,0},{0,0,0,0,0}}; struct frentry *ipfilter[2][2] = { { NULL, NULL }, { NULL, NULL } }, *ipacct[2][2] = { { NULL, NULL }, { NULL, NULL } }; +struct frgroup *ipfgroups[3][2]; int fr_flags = IPF_LOGGING, fr_active = 0; +#if defined(IPFILTER_DEFAULT_BLOCK) +int fr_pass = FR_NOMATCH|FR_BLOCK; +#else +int fr_pass = (IPF_DEFAULT_PASS|FR_NOMATCH); +#endif fr_info_t frcache[2]; -void fr_makefrip __P((int, ip_t *, fr_info_t *)); -int fr_tcpudpchk __P((frentry_t *, fr_info_t *)); -int fr_scanlist __P((int, ip_t *, fr_info_t *, void *m)); +static void fr_makefrip __P((int, ip_t *, fr_info_t *)); +static int fr_tcpudpchk __P((frentry_t *, fr_info_t *)); +static int frflushlist __P((int, int, int *, frentry_t *, frentry_t **)); + /* * bit values for identifying presence of individual IP options @@ -168,11 +187,10 @@ struct optlist secopt[8] = { * compact the IP header into a structure which contains just the info. * which is useful for comparing IP headers with. */ -void -fr_makefrip(hlen, ip, fin) - int hlen; - ip_t *ip; - fr_info_t *fin; +static void fr_makefrip(hlen, ip, fin) +int hlen; +ip_t *ip; +fr_info_t *fin; { struct optlist *op; tcphdr_t *tcp; @@ -186,6 +204,8 @@ fr_makefrip(hlen, ip, fin) fin->fin_data[0] = 0; fin->fin_data[1] = 0; fin->fin_rule = -1; + fin->fin_group = -1; + fin->fin_id = ip->ip_id; #ifdef _KERNEL fin->fin_icode = ipl_unreach; #endif @@ -196,10 +216,10 @@ fr_makefrip(hlen, ip, fin) tcp = (tcphdr_t *)((char *)ip + hlen); fin->fin_dp = (void *)tcp; (*(((u_short *)fi) + 1)) = (*(((u_short *)ip) + 4)); - (*(((u_int32_t *)fi) + 1)) = (*(((u_int32_t *)ip) + 3)); - (*(((u_int32_t *)fi) + 2)) = (*(((u_int32_t *)ip) + 4)); + (*(((u_32_t *)fi) + 1)) = (*(((u_32_t *)ip) + 3)); + (*(((u_32_t *)fi) + 2)) = (*(((u_32_t *)ip) + 4)); - fi->fi_fl = (hlen > sizeof(struct ip)) ? FI_OPTIONS : 0; + fi->fi_fl = (hlen > sizeof(ip_t)) ? FI_OPTIONS : 0; off = (ip->ip_off & 0x1fff) << 3; if (ip->ip_off & 0x3fff) fi->fi_fl |= FI_FRAG; @@ -288,10 +308,9 @@ getports: /* * check an IP packet for TCP/UDP characteristics such as ports and flags. */ -int -fr_tcpudpchk(fr, fin) - frentry_t *fr; - fr_info_t *fin; +static int fr_tcpudpchk(fr, fin) +frentry_t *fr; +fr_info_t *fin; { register u_short po, tup; register char i; @@ -382,27 +401,31 @@ fr_tcpudpchk(fr, fin) * Could be per interface, but this gets real nasty when you don't have * kernel sauce. */ -int -fr_scanlist(pass, ip, fin, m) - int pass; - ip_t *ip; - register fr_info_t *fin; - void *m; +int fr_scanlist(pass, ip, fin, m) +int pass; +ip_t *ip; +register fr_info_t *fin; +void *m; { register struct frentry *fr; register fr_ip_t *fi = &fin->fin_fi; - int rulen, portcmp = 0, off; + int rulen, portcmp = 0, off, skip = 0; fr = fin->fin_fr; fin->fin_fr = NULL; fin->fin_rule = 0; + fin->fin_group = 0; off = ip->ip_off & 0x1fff; - pass |= (fi->fi_fl << 20); + pass |= (fi->fi_fl << 24); if ((fi->fi_fl & FI_TCPUDP) && (fin->fin_dlen > 3) && !off) portcmp = 1; for (rulen = 0; fr; fr = fr->fr_next, rulen++) { + if (skip) { + skip--; + continue; + } /* * In all checks below, a null (zero) value in the * filter struture is taken to mean a wildcard. @@ -415,26 +438,26 @@ fr_scanlist(pass, ip, fin, m) #else if (opts & (OPT_VERBOSE|OPT_DEBUG)) printf("\n"); - FR_VERBOSE(("%c", (pass & FR_PASS) ? 'p' : 'b')); - if (fin->fin_ifp && *fr->fr_ifname && - strcasecmp((char *)fin->fin_ifp, fr->fr_ifname)) + FR_VERBOSE(("%c", (pass & FR_PASS) ? 'p' : + (pass & FR_AUTH) ? 'a' : 'b')); + if (fr->fr_ifa && fr->fr_ifa != fin->fin_ifp) continue; FR_VERBOSE((":i")); #endif { - register u_long *ld, *lm, *lip; + register u_32_t *ld, *lm, *lip; register int i; - lip = (u_long *)fi; - lm = (u_long *)&fr->fr_mip; - ld = (u_long *)&fr->fr_ip; + lip = (u_32_t *)fi; + lm = (u_32_t *)&fr->fr_mip; + ld = (u_32_t *)&fr->fr_ip; i = ((lip[0] & lm[0]) != ld[0]); FR_IFDEBUG(i,continue,("0. %#08x & %#08x != %#08x\n", lip[0], lm[0], ld[0])); - i |= ((lip[1] & lm[1]) != ld[1]); + i |= ((lip[1] & lm[1]) != ld[1]) << 21; FR_IFDEBUG(i,continue,("1. %#08x & %#08x != %#08x\n", lip[1], lm[1], ld[1])); - i |= ((lip[2] & lm[2]) != ld[2]); + i |= ((lip[2] & lm[2]) != ld[2]) << 22; FR_IFDEBUG(i,continue,("2. %#08x & %#08x != %#08x\n", lip[2], lm[2], ld[2])); i |= ((lip[3] & lm[3]) != ld[3]); @@ -443,6 +466,7 @@ fr_scanlist(pass, ip, fin, m) i |= ((lip[4] & lm[4]) != ld[4]); FR_IFDEBUG(i,continue,("4. %#08x & %#08x != %#08x\n", lip[4], lm[4], ld[4])); + i ^= (fi->fi_fl & (FR_NOTSRCIP|FR_NOTDSTIP)); if (i) continue; } @@ -474,12 +498,13 @@ fr_scanlist(pass, ip, fin, m) /* * Just log this packet... */ - pass = fr->fr_flags; + if (!(skip = fr->fr_skip)) + pass = fr->fr_flags; if ((pass & FR_CALLNOW) && fr->fr_func) pass = (*fr->fr_func)(pass, ip, fin); #ifdef IPFILTER_LOG if ((pass & FR_LOGMASK) == FR_LOG) { - if (!ipllog(fr->fr_flags, ip, fin, m)) + if (!IPLLOG(fr->fr_flags, ip, fin, m)) frstats[fin->fin_out].fr_skip++; frstats[fin->fin_out].fr_pkl++; } @@ -487,11 +512,21 @@ fr_scanlist(pass, ip, fin, m) FR_DEBUG(("pass %#x\n", pass)); fr->fr_hits++; if (pass & FR_ACCOUNT) - fr->fr_bytes += ip->ip_len; + fr->fr_bytes += (U_QUAD_T)ip->ip_len; else fin->fin_icode = fr->fr_icode; fin->fin_rule = rulen; + fin->fin_group = fr->fr_group; fin->fin_fr = fr; + if (fr->fr_grp) { + fin->fin_fr = fr->fr_grp; + pass = fr_scanlist(pass, ip, fin, m); + if (fin->fin_fr == NULL) { + fin->fin_rule = rulen; + fin->fin_group = fr->fr_group; + fin->fin_fr = fr; + } + } if (pass & FR_QUICK) break; } @@ -501,28 +536,21 @@ fr_scanlist(pass, ip, fin, m) /* * frcheck - filter check - * check using source and destination addresses/ports in a packet whether + * check using source and destination addresses/pors in a packet whether * or not to pass it on or not. */ -int -fr_check(ip, hlen, ifp, out -#ifdef _KERNEL -# if SOLARIS - , qif, q, mp) - qif_t *qif; - queue_t *q; - mblk_t **mp; -# else - , mp) - struct mbuf **mp; -# endif +int fr_check(ip, hlen, ifp, out +#if defined(_KERNEL) && SOLARIS +, qif, mp) +qif_t *qif; #else - ) +, mp) #endif - ip_t *ip; - int hlen; - struct ifnet *ifp; - int out; +mb_t **mp; +ip_t *ip; +int hlen; +void *ifp; +int out; { /* * The above really sucks, but short of writing a diff @@ -530,81 +558,148 @@ fr_check(ip, hlen, ifp, out fr_info_t frinfo, *fc; register fr_info_t *fin = &frinfo; frentry_t *fr = NULL; - int pass, changed; + int pass, changed, apass, error = EHOSTUNREACH; +#if !SOLARIS || !defined(_KERNEL) + register mb_t *m = *mp; +#endif -#if !defined(__SVR4) && !defined(__svr4__) && defined(_KERNEL) - register struct mbuf *m = *mp; - struct mbuf *mc = NULL; +#ifdef _KERNEL + mb_t *mc = NULL; +# if !defined(__SVR4) && !defined(__svr4__) +# ifdef __sgi + char hbuf[(0xf << 2) + sizeof(struct icmp) + sizeof(ip_t) + 8]; +# endif + int up; if ((ip->ip_p == IPPROTO_TCP || ip->ip_p == IPPROTO_UDP || ip->ip_p == IPPROTO_ICMP)) { - register int up = MIN(hlen + 8, ip->ip_len); + int plen = 0; + + switch(ip->ip_p) + { + case IPPROTO_TCP: + plen = sizeof(tcphdr_t); + break; + case IPPROTO_UDP: + plen = sizeof(udphdr_t); + break; + case IPPROTO_ICMP: + /* 96 - enough for complete ICMP error IP header */ + plen = sizeof(struct icmp) + sizeof(ip_t) + 8; + break; + } + up = MIN(hlen + plen, ip->ip_len); if (up > m->m_len) { +#ifdef __sgi /* Under IRIX, avoid m_pullup as it makes ping <hostname> panic */ + if ((up > sizeof(hbuf)) || (m_length(m) < up)) { + frstats[out].fr_pull[1]++; + return -1; + } + m_copydata(m, 0, up, hbuf); + frstats[out].fr_pull[0]++; + ip = (ip_t *)hbuf; +#else +# ifndef linux if ((*mp = m_pullup(m, up)) == 0) { frstats[out].fr_pull[1]++; return -1; } else { frstats[out].fr_pull[0]++; m = *mp; - ip = mtod(m, struct ip *); + ip = mtod(m, ip_t *); } - } - } +# endif #endif -#if SOLARIS && defined(_KERNEL) - mblk_t *mc = NULL, *m = qif->qf_m; + } else + up = 0; + } else + up = 0; +# endif +# if SOLARIS + mb_t *m = qif->qf_m; +# endif #endif fr_makefrip(hlen, ip, fin); fin->fin_ifp = ifp; fin->fin_out = out; + fin->fin_mp = mp; MUTEX_ENTER(&ipf_mutex); + + /* + * Check auth now. This, combined with the check below to see if apass + * is 0 is to ensure that we don't count the packet twice, which can + * otherwise occur when we reprocess it. As it is, we only count it + * after it has no auth. table matchup. This also stops NAT from + * occuring until after the packet has been auth'd. + */ + apass = fr_checkauth(ip, fin); + if (!out) { changed = ip_natin(ip, hlen, fin); - if ((fin->fin_fr = ipacct[0][fr_active]) && + if (!apass && (fin->fin_fr = ipacct[0][fr_active]) && (FR_SCANLIST(FR_NOMATCH, ip, fin, m) & FR_ACCOUNT)) frstats[0].fr_acct++; } - if ((pass = ipfr_knownfrag(ip, fin))) { - if ((pass & FR_KEEPSTATE)) { - if (fr_addstate(ip, fin, pass) == -1) - frstats[out].fr_bads++; + if (apass || (!(pass = ipfr_knownfrag(ip, fin)) && + !(pass = fr_checkstate(ip, fin)))) { + /* + * If a packet is found in the auth table, then skip checking + * the access lists for permission but we do need to consider + * the result as if it were from the ACL's. + */ + if (!apass) { + fc = frcache + out; + if (!bcmp((char *)fin, (char *)fc, FI_CSIZE)) { + /* + * copy cached data so we can unlock the mutex + * earlier. + */ + bcopy((char *)fc, (char *)fin, FI_COPYSIZE); + frstats[out].fr_chit++; + if ((fr = fin->fin_fr)) { + fr->fr_hits++; + pass = fr->fr_flags; + } else + pass = fr_pass; + } else { + pass = fr_pass; + if ((fin->fin_fr = ipfilter[out][fr_active])) + pass = FR_SCANLIST(fr_pass, ip, fin, m); + bcopy((char *)fin, (char *)fc, FI_COPYSIZE); + if (pass & FR_NOMATCH) + frstats[out].fr_nom++; + } + fr = fin->fin_fr; + } else + pass = apass; + + /* + * If we fail to add a packet to the authorization queue, + * then we drop the packet later. However, if it was added + * then pretend we've dropped it already. + */ + if ((pass & FR_AUTH)) + if (FR_NEWAUTH(m, fin, ip, qif) != 0) +#ifdef _KERNEL + m = *mp = NULL; +#else + ; +#endif + + if (pass & FR_PREAUTH) { + MUTEX_ENTER(&ipf_auth); + if ((fin->fin_fr = ipauth) && + (pass = FR_SCANLIST(0, ip, fin, m))) + fr_authstats.fas_hits++; else - frstats[out].fr_ads++; - } - } else if ((pass = fr_checkstate(ip, fin))) { - if ((pass & FR_KEEPFRAG)) { - if (fin->fin_fi.fi_fl & FI_FRAG) { - if (ipfr_newfrag(ip, fin, pass) == -1) - frstats[out].fr_bnfr++; - else - frstats[out].fr_nfr++; - } else - frstats[out].fr_cfr++; - } - } else { - fc = frcache + out; - if (fc->fin_fr && !bcmp((char *)fin, (char *)fc, FI_CSIZE)) { - /* - * copy cached data so we can unlock the mutex - * earlier. - */ - bcopy((char *)fc, (char *)fin, sizeof(*fin)); - frstats[out].fr_chit++; - pass = fin->fin_fr->fr_flags; - } else { - pass = IPF_NOMATCH; - if ((fin->fin_fr = ipfilter[out][fr_active])) - pass = FR_SCANLIST(IPF_NOMATCH, ip, fin, m); - bcopy((char *)fin, (char *)fc, FI_CSIZE); - if (pass & FR_NOMATCH) - frstats[out].fr_nom++; + fr_authstats.fas_miss++; + MUTEX_EXIT(&ipf_auth); } - fr = fin->fin_fr; - if ((pass & FR_KEEPFRAG)) { + if (pass & FR_KEEPFRAG) { if (fin->fin_fi.fi_fl & FI_FRAG) { if (ipfr_newfrag(ip, fin, pass) == -1) frstats[out].fr_bnfr++; @@ -624,7 +719,11 @@ fr_check(ip, hlen, ifp, out if (fr && fr->fr_func && !(pass & FR_CALLNOW)) pass = (*fr->fr_func)(pass, ip, fin); - if (out) { + /* + * Only count/translate packets which will be passed on, out the + * interface. + */ + if (out && (pass & FR_PASS)) { if ((fin->fin_fr = ipacct[1][fr_active]) && (FR_SCANLIST(FR_NOMATCH, ip, fin, m) & FR_ACCOUNT)) frstats[1].fr_acct++; @@ -652,7 +751,7 @@ fr_check(ip, hlen, ifp, out pass |= FF_LOGBLOCK; frstats[out].fr_bpkl++; logit: - if (!ipllog(pass, ip, fin, m)) { + if (!IPLLOG(pass, ip, fin, m)) { frstats[out].fr_skip++; if ((pass & (FR_PASS|FR_LOGORBLOCK)) == (FR_PASS|FR_LOGORBLOCK)) @@ -661,13 +760,21 @@ logit: } } #endif /* IPFILTER_LOG */ - #ifdef _KERNEL + /* + * Only allow FR_DUP to work if a rule matched - it makes no sense to + * set FR_DUP as a "default" as there are no instructions about where + * to send the packet. + */ if (fr && (pass & FR_DUP)) -# if SOLARIS +# if SOLARIS mc = dupmsg(m); # else +# ifndef linux mc = m_copy(m, 0, M_COPYALL); +# else + ; +# endif # endif #endif if (pass & FR_PASS) @@ -697,7 +804,7 @@ logit: frstats[0].fr_ret++; } else if ((pass & FR_RETRST) && !(fin->fin_fi.fi_fl & FI_SHORT)) { - if (SEND_RESET(ip, qif, q) == 0) + if (SEND_RESET(ip, qif, ifp) == 0) frstats[1].fr_ret++; } #else @@ -710,10 +817,22 @@ logit: frstats[1].fr_ret++; } #endif + } else { + if (pass & FR_RETRST) + error = ECONNRESET; } } -#ifdef _KERNEL -# if !SOLARIS + + /* + * If we didn't drop off the bottom of the list of rules (and thus + * the 'current' rule fr is not NULL), then we may have some extra + * instructions about what to do with a packet. + * Once we're finished return to our caller, freeing the packet if + * we are dropping it (* BSD ONLY *). + */ +#if defined(_KERNEL) +# if !SOLARIS +# if !defined(linux) if (fr) { frdest_t *fdp = &fr->fr_tif; @@ -727,8 +846,13 @@ logit: } if (!(pass & FR_PASS) && m) m_freem(m); - return (pass & FR_PASS) ? 0 : -1; -# else +# ifdef __sgi + else if (changed && up && m) + m_copyback(m, 0, up, hbuf); +# endif +# endif /* !linux */ + return (pass & FR_PASS) ? 0 : error; +# else /* !SOLARIS */ if (fr) { frdest_t *fdp = &fr->fr_tif; @@ -740,50 +864,441 @@ logit: if (mc) ipfr_fastroute(qif, ip, mc, mp, fin, &fr->fr_dif); } - return (pass & FR_PASS) ? changed : -1; -# endif -#else + return (pass & FR_PASS) ? changed : error; +# endif /* !SOLARIS */ +#else /* _KERNEL */ if (pass & FR_NOMATCH) return 1; if (pass & FR_PASS) return 0; + if (pass & FR_AUTH) + return -2; return -1; -#endif +#endif /* _KERNEL */ } -#ifdef IPFILTER_LOG -# if !(defined(_KERNEL)) -static void ipllog() +/* + * ipf_cksum + * addr should be 16bit aligned and len is in bytes. + * length is in bytes + */ +u_short ipf_cksum(addr, len) +register u_short *addr; +register int len; { - verbose("l"); + register u_long sum = 0; + + for (sum = 0; len > 1; len -= 2) + sum += *addr++; + + /* mop up an odd byte, if necessary */ + if (len == 1) + sum += *(u_char *)addr; + + /* + * add back carry outs from top 16 bits to low 16 bits + */ + sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ + sum += (sum >> 16); /* add carry */ + return (u_short)(~sum); } -# endif -int fr_copytolog(buf, len) -char *buf; +/* + * NB: This function assumes we've pullup'd enough for all of the IP header + * and the TCP header. We also assume that data blocks aren't allocated in + * odd sizes. + */ +u_short fr_tcpsum(m, ip, tcp, len) +mb_t *m; +ip_t *ip; +tcphdr_t *tcp; int len; { - int clen, tail; - - tail = (iplh >= iplt) ? (iplbuf + IPLLOGSIZE - iplh) : (iplt - iplh); - clen = MIN(tail, len); - bcopy(buf, iplh, clen); - len -= clen; - tail -= clen; - iplh += clen; - buf += clen; - if (iplh == iplbuf + IPLLOGSIZE) { - iplh = iplbuf; - tail = iplt - iplh; + union { + u_char c[2]; + u_short s; + } bytes; + u_long sum; + u_short *sp; +# if SOLARIS || defined(__sgi) + int add, hlen; +# endif + +# if SOLARIS + /* skip any leading M_PROTOs */ + while(m && (MTYPE(m) != M_DATA)) + m = m->b_cont; + PANIC((!m),("fr_tcpsum: no M_DATA")); +# endif + + /* + * Add up IP Header portion + */ + bytes.c[0] = 0; + bytes.c[1] = IPPROTO_TCP; + len -= (ip->ip_hl << 2); + sum = bytes.s; + sum += htons((u_short)len); + sp = (u_short *)&ip->ip_src; + sum += *sp++; + sum += *sp++; + sum += *sp++; + sum += *sp++; + if (sp != (u_short *)tcp) + sp = (u_short *)tcp; + sum += *sp++; + sum += *sp++; + sum += *sp++; + sum += *sp++; + sum += *sp++; + sum += *sp++; + sum += *sp++; + sum += *sp; + sp += 2; /* Skip over checksum */ + sum += *sp++; + +#if SOLARIS + /* + * In case we had to copy the IP & TCP header out of mblks, + * skip over the mblk bits which are the header + */ + if ((caddr_t)ip != (caddr_t)m->b_rptr) { + hlen = (caddr_t)sp - (caddr_t)ip; + while (hlen) { + add = MIN(hlen, m->b_wptr - m->b_rptr); + sp = (u_short *)((caddr_t)m->b_rptr + add); + hlen -= add; + if ((caddr_t)sp >= (caddr_t)m->b_wptr) { + m = m->b_cont; + PANIC((!m),("fr_tcpsum: not enough data")); + if (!hlen) + sp = (u_short *)m->b_rptr; + } + } + } +#endif +#ifdef __sgi + /* + * In case we had to copy the IP & TCP header out of mbufs, + * skip over the mbuf bits which are the header + */ + if ((caddr_t)ip != mtod(m, caddr_t)) { + hlen = (caddr_t)sp - (caddr_t)ip; + while (hlen) { + add = MIN(hlen, m->m_len); + sp = (u_short *)(mtod(m, caddr_t) + add); + hlen -= add; + if (add >= m->m_len) { + m = m->m_next; + PANIC((!m),("fr_tcpsum: not enough data")); + if (!hlen) + sp = mtod(m, u_short *); + } + } + } +#endif + + if (!(len -= sizeof(*tcp))) + goto nodata; + while (len > 0) { +#if SOLARIS + while ((caddr_t)sp >= (caddr_t)m->b_wptr) { + m = m->b_cont; + PANIC((!m),("fr_tcpsum: not enough data")); + sp = (u_short *)m->b_rptr; + } +#else + while (((caddr_t)sp - mtod(m, caddr_t)) >= m->m_len) + { + m = m->m_next; + PANIC((!m),("fr_tcpsum: not enough data")); + sp = mtod(m, u_short *); + } +#endif /* SOLARIS */ + if (len < 2) + break; + if((u_long)sp & 1) { + bcopy((char *)sp++, (char *)&bytes.s, sizeof(bytes.s)); + sum += bytes.s; + } else + sum += *sp++; + len -= 2; + } + if (len) { + bytes.c[1] = 0; + bytes.c[0] = *(u_char *)sp; + sum += bytes.s; + } +nodata: + sum = (sum >> 16) + (sum & 0xffff); + sum += (sum >> 16); + sum = (u_short)((~sum) & 0xffff); + return sum; +} + + +#if defined(_KERNEL) && ( ((BSD < 199306) && !SOLARIS) || defined(__sgi) ) +/* + * Copyright (c) 1982, 1986, 1988, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)uipc_mbuf.c 8.2 (Berkeley) 1/4/94 + * $Id: fil.c,v 1.11 1998/01/26 04:10:37 dgregor Exp $ + */ +/* + * Copy data from an mbuf chain starting "off" bytes from the beginning, + * continuing for "len" bytes, into the indicated buffer. + */ +void +m_copydata(m, off, len, cp) + register mb_t *m; + register int off; + register int len; + caddr_t cp; +{ + register unsigned count; + + if (off < 0 || len < 0) + panic("m_copydata"); + while (off > 0) { + if (m == 0) + panic("m_copydata"); + if (off < m->m_len) + break; + off -= m->m_len; + m = m->m_next; } - if (len && tail) { - clen = MIN(tail, len); - bcopy(buf, iplh, clen); - len -= clen; - iplh += clen; + while (len > 0) { + if (m == 0) + panic("m_copydata"); + count = MIN(m->m_len - off, len); + bcopy(mtod(m, caddr_t) + off, cp, count); + len -= count; + cp += count; + off = 0; + m = m->m_next; } - return len; } + + +# ifndef linux +/* + * Copy data from a buffer back into the indicated mbuf chain, + * starting "off" bytes from the beginning, extending the mbuf + * chain if necessary. + */ +void +m_copyback(m0, off, len, cp) + struct mbuf *m0; + register int off; + register int len; + caddr_t cp; +{ + register int mlen; + register struct mbuf *m = m0, *n; + int totlen = 0; + + if (m0 == 0) + return; + while (off > (mlen = m->m_len)) { + off -= mlen; + totlen += mlen; + if (m->m_next == 0) { + n = m_getclr(M_DONTWAIT, m->m_type); + if (n == 0) + goto out; + n->m_len = min(MLEN, len + off); + m->m_next = n; + } + m = m->m_next; + } + while (len > 0) { + mlen = min (m->m_len - off, len); + bcopy(cp, off + mtod(m, caddr_t), (unsigned)mlen); + cp += mlen; + len -= mlen; + mlen += off; + off = 0; + totlen += mlen; + if (len == 0) + break; + if (m->m_next == 0) { + n = m_get(M_DONTWAIT, m->m_type); + if (n == 0) + break; + n->m_len = min(MLEN, len); + m->m_next = n; + } + m = m->m_next; + } +out: +#if 0 + if (((m = m0)->m_flags & M_PKTHDR) && (m->m_pkthdr.len < totlen)) + m->m_pkthdr.len = totlen; #endif + return; +} +# endif /* linux */ +#endif /* (_KERNEL) && ( ((BSD < 199306) && !SOLARIS) || __sgi) */ + + +frgroup_t *fr_findgroup(num, flags, which, set, fgpp) +u_short num; +u_32_t flags; +int which, set; +frgroup_t ***fgpp; +{ + frgroup_t *fg, **fgp; + + if (which == IPL_LOGAUTH) + fgp = &ipfgroups[2][set]; + else if (flags & FR_ACCOUNT) + fgp = &ipfgroups[1][set]; + else if (flags & (FR_OUTQUE|FR_INQUE)) + fgp = &ipfgroups[0][set]; + else + return NULL; + + while ((fg = *fgp)) + if (fg->fg_num == num) + break; + else + fgp = &fg->fg_next; + if (fgpp) + *fgpp = fgp; + return fg; +} + + +frgroup_t *fr_addgroup(num, fp, which, set) +u_short num; +frentry_t *fp; +int which, set; +{ + frgroup_t *fg, **fgp; + + if ((fg = fr_findgroup(num, fp->fr_flags, which, set, &fgp))) + return fg; + + KMALLOC(fg, frgroup_t *, sizeof(*fg)); + if (fg) { + fg->fg_num = num; + fg->fg_next = *fgp; + fg->fg_head = fp; + fg->fg_start = &fp->fr_grp; + *fgp = fg; + } + return fg; +} + + +void fr_delgroup(num, flags, which, set) +u_short num; +u_32_t flags; +int which, set; +{ + frgroup_t *fg, **fgp; + + if (!(fg = fr_findgroup(num, flags, which, set, &fgp))) + return; + + *fgp = fg->fg_next; + KFREE(fg); +} + + + +/* + * recursively flush rules from the list, descending groups as they are + * encountered. if a rule is the head of a group and it has lost all its + * group members, then also delete the group reference. + */ +static int frflushlist(set, unit, nfreedp, list, listp) +int set, unit, *nfreedp; +frentry_t *list, **listp; +{ + register frentry_t *fp = list, *fpn; + register int freed = 0; + + while (fp) { + fpn = fp->fr_next; + if (fp->fr_grp) { + fp->fr_ref -= frflushlist(set, unit, nfreedp, + fp->fr_grp, &fp->fr_grp); + } + + if (fp->fr_ref == 1) { + if (fp->fr_grhead) + fr_delgroup(fp->fr_grhead, fp->fr_flags, unit, + set); + KFREE(fp); + *listp = fpn; + freed++; + } + fp = fpn; + } + *nfreedp += freed; + return freed; +} + + +void frflush(unit, result) +int unit; +int *result; +{ + int flags = *result, flushed = 0, set = fr_active; + + bzero((char *)frcache, sizeof(frcache[0]) * 2); + + if (flags & FR_INACTIVE) + set = 1 - set; + + if (unit == IPL_LOGIPF) { + if (flags & FR_OUTQUE) { + (void) frflushlist(set, unit, &flushed, + ipfilter[1][set], + &ipfilter[1][set]); + (void) frflushlist(set, unit, &flushed, + ipacct[1][set], &ipacct[1][set]); + } + if (flags & FR_INQUE) { + (void) frflushlist(set, unit, &flushed, + ipfilter[0][set], + &ipfilter[0][set]); + (void) frflushlist(set, unit, &flushed, + ipacct[0][set], &ipacct[0][set]); + } + } + + *result = flushed; +} |