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 | |
parent | a95cd44675061863d776c0b405c6cffa4721f915 (diff) |
IPF 3.2.3
Diffstat (limited to 'sys/netinet')
-rw-r--r-- | sys/netinet/fil.c | 905 | ||||
-rw-r--r-- | sys/netinet/ip_auth.c | 494 | ||||
-rw-r--r-- | sys/netinet/ip_auth.h | 66 | ||||
-rw-r--r-- | sys/netinet/ip_fil.c | 1069 | ||||
-rw-r--r-- | sys/netinet/ip_fil.h | 391 | ||||
-rw-r--r-- | sys/netinet/ip_fil_compat.h | 577 | ||||
-rw-r--r-- | sys/netinet/ip_frag.c | 325 | ||||
-rw-r--r-- | sys/netinet/ip_frag.h | 29 | ||||
-rw-r--r-- | sys/netinet/ip_ftp_pxy.c | 269 | ||||
-rw-r--r-- | sys/netinet/ip_log.c | 473 | ||||
-rw-r--r-- | sys/netinet/ip_nat.c | 889 | ||||
-rw-r--r-- | sys/netinet/ip_nat.h | 106 | ||||
-rw-r--r-- | sys/netinet/ip_proxy.c | 319 | ||||
-rw-r--r-- | sys/netinet/ip_proxy.h | 93 | ||||
-rw-r--r-- | sys/netinet/ip_state.c | 303 | ||||
-rw-r--r-- | sys/netinet/ip_state.h | 71 |
16 files changed, 5200 insertions, 1179 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; +} diff --git a/sys/netinet/ip_auth.c b/sys/netinet/ip_auth.c new file mode 100644 index 00000000000..e465651281a --- /dev/null +++ b/sys/netinet/ip_auth.c @@ -0,0 +1,494 @@ +/* + * Copyright (C) 1997 by Darren Reed & Guido van Rooij. + * + * 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 !defined(lint) +static const char rcsid[] = "@(#)$Id: ip_auth.c,v 1.1 1998/01/26 04:10:38 dgregor Exp $"; +#endif + +#if !defined(_KERNEL) && !defined(KERNEL) +# include <stdlib.h> +# include <string.h> +#endif +#include <sys/errno.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/time.h> +#include <sys/file.h> +#if defined(KERNEL) && (__FreeBSD_version >= 220000) +# include <sys/filio.h> +# include <sys/fcntl.h> +#else +# include <sys/ioctl.h> +#endif +#include <sys/uio.h> +#ifndef linux +# include <sys/protosw.h> +#endif +#include <sys/socket.h> +#if defined(_KERNEL) && !defined(linux) +# include <sys/systm.h> +#endif +#if !defined(__SVR4) && !defined(__svr4__) +# ifndef linux +# include <sys/mbuf.h> +# endif +#else +# include <sys/filio.h> +# include <sys/byteorder.h> +# include <sys/dditypes.h> +# include <sys/stream.h> +# include <sys/kmem.h> +#endif +#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(bsdi) +# include <machine/cpu.h> +#endif +#include <net/if.h> +#ifdef sun +#include <net/af.h> +#endif +#include <net/route.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#ifndef KERNEL +#define KERNEL +#define NOT_KERNEL +#endif +#ifndef linux +# include <netinet/ip_var.h> +#endif +#ifdef NOT_KERNEL +#undef KERNEL +#endif +#ifdef __sgi +# ifdef IFF_DRVRLOCK /* IRIX6 */ +#include <sys/hashing.h> +# endif +#endif +#include <netinet/tcp.h> +#if defined(__sgi) && !defined(IFF_DRVRLOCK) /* IRIX < 6 */ +extern struct ifqueue ipintrq; /* ip packet input queue */ +#else +# ifndef linux +# include <netinet/in_var.h> +# include <netinet/tcp_fsm.h> +# endif +#endif +#include <netinet/udp.h> +#include <netinet/ip_icmp.h> +#include "ip_fil_compat.h" +#include <netinet/tcpip.h> +#include "ip_fil.h" +#include "ip_auth.h" +#if !SOLARIS && !defined(linux) +# include <net/netisr.h> +#endif + + +#if (SOLARIS || defined(__sgi)) && defined(_KERNEL) +extern kmutex_t ipf_auth; +# if SOLARIS +extern kcondvar_t ipfauthwait; +# endif +#endif +#ifdef linux +static struct wait_queue *ipfauthwait = NULL; +#endif + +int fr_authsize = FR_NUMAUTH; +int fr_authused = 0; +int fr_defaultauthage = 600; +fr_authstat_t fr_authstats; +frauth_t fr_auth[FR_NUMAUTH]; +mb_t *fr_authpkts[FR_NUMAUTH]; +int fr_authstart = 0, fr_authend = 0, fr_authnext = 0; +frauthent_t *fae_list = NULL; +frentry_t *ipauth = NULL; + + +/* + * Check if a packet has authorization. If the packet is found to match an + * authorization result and that would result in a feedback loop (i.e. it + * will end up returning FR_AUTH) then return FR_BLOCK instead. + */ +int fr_checkauth(ip, fin) +ip_t *ip; +fr_info_t *fin; +{ + u_short id = ip->ip_id; + u_32_t pass; + int i; + + MUTEX_ENTER(&ipf_auth); + for (i = fr_authstart; i != fr_authend; ) { + /* + * index becomes -2 only after an SIOCAUTHW. Check this in + * case the same packet gets sent again and it hasn't yet been + * auth'd. + */ + if ((fr_auth[i].fra_index == -2) && + (id == fr_auth[i].fra_info.fin_id) && + !bcmp((char *)fin,(char *)&fr_auth[i].fra_info,FI_CSIZE)) { + /* + * Avoid feedback loop. + */ + if (!(pass = fr_auth[i].fra_pass) || (pass & FR_AUTH)) + pass = FR_BLOCK; + fr_authstats.fas_hits++; + fr_auth[i].fra_index = -1; + fr_authused--; + if (i == fr_authstart) { + while (fr_auth[i].fra_index == -1) { + i++; + if (i == FR_NUMAUTH) + i = 0; + fr_authstart = i; + if (i == fr_authend) + break; + } + if (fr_authstart == fr_authend) { + fr_authnext = 0; + fr_authstart = fr_authend = 0; + } + } + MUTEX_EXIT(&ipf_auth); + return pass; + } + i++; + if (i == FR_NUMAUTH) + i = 0; + } + fr_authstats.fas_miss++; + MUTEX_EXIT(&ipf_auth); + return 0; +} + + +/* + * Check if we have room in the auth array to hold details for another packet. + * If we do, store it and wake up any user programs which are waiting to + * hear about these events. + */ +int fr_newauth(m, fin, ip +#if defined(_KERNEL) && SOLARIS +, qif) +qif_t *qif; +#else +) +#endif +mb_t *m; +fr_info_t *fin; +ip_t *ip; +{ + int i; + + MUTEX_ENTER(&ipf_auth); + if ((fr_authstart > fr_authend) && (fr_authstart - fr_authend == -1)) { + fr_authstats.fas_nospace++; + MUTEX_EXIT(&ipf_auth); + return 0; + } + if (fr_authend - fr_authstart == FR_NUMAUTH - 1) { + fr_authstats.fas_nospace++; + MUTEX_EXIT(&ipf_auth); + return 0; + } + + fr_authstats.fas_added++; + fr_authused++; + i = fr_authend++; + if (fr_authend == FR_NUMAUTH) + fr_authend = 0; + MUTEX_EXIT(&ipf_auth); + fr_auth[i].fra_index = i; + fr_auth[i].fra_pass = 0; + fr_auth[i].fra_age = fr_defaultauthage; + bcopy((char *)fin, (char *)&fr_auth[i].fra_info, sizeof(*fin)); +#if !defined(sparc) && !defined(m68k) + /* + * No need to copyback here as we want to undo the changes, not keep + * them. + */ +# if SOLARIS && defined(_KERNEL) + if (ip == (ip_t *)m->b_rptr) +# endif + { + register u_short bo; + + bo = ip->ip_len; + ip->ip_len = htons(bo); +# if !SOLARIS /* 4.4BSD converts this ip_input.c, but I don't in solaris.c */ + bo = ip->ip_id; + ip->ip_id = htons(bo); +# endif + bo = ip->ip_off; + ip->ip_off = htons(bo); + } +#endif +#if SOLARIS && defined(_KERNEL) + m->b_rptr -= qif->qf_off; + fr_authpkts[i] = *(mblk_t **)fin->fin_mp; + fr_auth[i].fra_q = qif->qf_q; + cv_signal(&ipfauthwait); +#else + fr_authpkts[i] = m; +# if defined(linux) && defined(_KERNEL) + wake_up_interruptible(&ipfauthwait); +# else + WAKEUP(&fr_authnext); +# endif +#endif + return 1; +} + + +int fr_auth_ioctl(data, cmd, fr, frptr) +caddr_t data; +#if defined(__NetBSD__) || defined(__OpenBSD__) +u_long cmd; +#else +int cmd; +#endif +frentry_t *fr, **frptr; +{ + mb_t *m; +#if defined(_KERNEL) +# if !SOLARIS + struct ifqueue *ifq; + int s; +# endif +#endif + frauth_t auth, *au = &auth; + frauthent_t *fae, **faep; + int i, error = 0; + + switch (cmd) + { + case SIOCINIFR : + case SIOCRMIFR : + case SIOCADIFR : + error = EINVAL; + break; + case SIOCINAFR : + case SIOCRMAFR : + case SIOCADAFR : + for (faep = &fae_list; (fae = *faep); ) + if (&fae->fae_fr == fr) + break; + else + faep = &fae->fae_next; + if (cmd == SIOCRMAFR) { + if (!fae) + error = ESRCH; + else { + *faep = fae->fae_next; + *frptr = fr->fr_next; + KFREE(fae); + } + } else { + KMALLOC(fae, frauthent_t *, sizeof(*fae)); + if (fae != NULL) { + IRCOPY((char *)data, (char *)&fae->fae_fr, + sizeof(fae->fae_fr)); + if (!fae->fae_age) + fae->fae_age = fr_defaultauthage; + fae->fae_fr.fr_hits = 0; + fae->fae_fr.fr_next = *frptr; + *frptr = &fae->fae_fr; + fae->fae_next = *faep; + *faep = fae; + } else + error = ENOMEM; + } + break; + case SIOCATHST: + IWCOPY((char *)&fr_authstats, data, sizeof(fr_authstats)); + break; + case SIOCAUTHW: +fr_authioctlloop: + MUTEX_ENTER(&ipf_auth); + if ((fr_authnext != fr_authend) && fr_authpkts[fr_authnext]) { + IWCOPY((char *)&fr_auth[fr_authnext++], data, + sizeof(fr_info_t)); + if (fr_authnext == FR_NUMAUTH) + fr_authnext = 0; + MUTEX_EXIT(&ipf_auth); + return 0; + } +#ifdef _KERNEL +# if SOLARIS + if (!cv_wait_sig(&ipfauthwait, &ipf_auth)) { + mutex_exit(&ipf_auth); + return EINTR; + } +# else +# ifdef linux + interruptible_sleep_on(&ipfauthwait); + if (current->signal & ~current->blocked) + error = -EINTR; +# else + error = SLEEP(&fr_authnext, "fr_authnext"); +# endif +# endif +#endif + MUTEX_EXIT(&ipf_auth); + if (!error) + goto fr_authioctlloop; + break; + case SIOCAUTHR: + IRCOPY(data, (caddr_t)&auth, sizeof(auth)); + MUTEX_ENTER(&ipf_auth); + i = au->fra_index; + if ((i < 0) || (i > FR_NUMAUTH) || + (fr_auth[i].fra_info.fin_id != au->fra_info.fin_id)) { + MUTEX_EXIT(&ipf_auth); + return EINVAL; + } + m = fr_authpkts[i]; + fr_auth[i].fra_index = -2; + fr_auth[i].fra_pass = au->fra_pass; + fr_authpkts[i] = NULL; +#ifdef _KERNEL + MUTEX_EXIT(&ipf_auth); + SPL_NET(s); +# ifndef linux + if (m && au->fra_info.fin_out) { +# if SOLARIS + error = fr_qout(fr_auth[i].fra_q, m); +# else /* SOLARIS */ + error = ip_output(m, NULL, NULL, IP_FORWARDING, NULL); +# endif /* SOLARIS */ + if (error) + fr_authstats.fas_sendfail++; + else + fr_authstats.fas_sendok++; + } else if (m) { +# if SOLARIS + error = fr_qin(fr_auth[i].fra_q, m); +# else /* SOLARIS */ + ifq = &ipintrq; + if (IF_QFULL(ifq)) { + IF_DROP(ifq); + m_freem(m); + error = ENOBUFS; + } else { + IF_ENQUEUE(ifq, m); + schednetisr(NETISR_IP); + } +# endif /* SOLARIS */ + if (error) + fr_authstats.fas_quefail++; + else + fr_authstats.fas_queok++; + } else + error = EINVAL; +# endif +# if SOLARIS + if (error) + error = EINVAL; +# else + /* + * If we experience an error which will result in the packet + * not being processed, make sure we advance to the next one. + */ + if (error == ENOBUFS) { + fr_authused--; + fr_auth[i].fra_index = -1; + fr_auth[i].fra_pass = 0; + if (i == fr_authstart) { + while (fr_auth[i].fra_index == -1) { + i++; + if (i == FR_NUMAUTH) + i = 0; + fr_authstart = i; + if (i == fr_authend) + break; + } + if (fr_authstart == fr_authend) { + fr_authnext = 0; + fr_authstart = fr_authend = 0; + } + } + } +# endif + SPL_X(s); +#endif /* _KERNEL */ + break; + default : + error = EINVAL; + break; + } + return error; +} + + +#ifdef _KERNEL +/* + * Free all network buffer memory used to keep saved packets. + */ +void fr_authunload() +{ + register int i; + register frauthent_t *fae, **faep; + mb_t *m; + + MUTEX_ENTER(&ipf_auth); + for (i = 0; i < FR_NUMAUTH; i++) { + if ((m = fr_authpkts[i])) { + FREE_MB_T(m); + fr_authpkts[i] = NULL; + fr_auth[i].fra_index = -1; + } + } + + + for (faep = &fae_list; (fae = *faep); ) { + *faep = fae->fae_next; + KFREE(fae); + } + MUTEX_EXIT(&ipf_auth); +} + + +/* + * Slowly expire held auth records. Timeouts are set + * in expectation of this being called twice per second. + */ +void fr_authexpire() +{ + register int i; + register frauth_t *fra; + register frauthent_t *fae, **faep; + mb_t *m; +#if !SOLARIS + int s; +#endif + + SPL_NET(s); + MUTEX_ENTER(&ipf_auth); + for (i = 0, fra = fr_auth; i < FR_NUMAUTH; i++, fra++) { + if ((!--fra->fra_age) && (m = fr_authpkts[i])) { + FREE_MB_T(m); + fr_authpkts[i] = NULL; + fr_auth[i].fra_index = -1; + fr_authstats.fas_expire++; + fr_authused--; + } + } + + for (faep = &fae_list; (fae = *faep); ) { + if (!--fra->fra_age) { + *faep = fae->fae_next; + KFREE(fae); + fr_authstats.fas_expire++; + } else + faep = &fae->fae_next; + } + MUTEX_EXIT(&ipf_auth); + SPL_X(s); +} +#endif diff --git a/sys/netinet/ip_auth.h b/sys/netinet/ip_auth.h new file mode 100644 index 00000000000..f2fd714be4a --- /dev/null +++ b/sys/netinet/ip_auth.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 1997 by Darren Reed & Guido Van Rooij. + * + * 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. + * + * $Id: ip_auth.h,v 1.1 1998/01/26 04:10:38 dgregor Exp $ + * + */ +#ifndef __IP_AUTH_H__ +#define __IP_AUTH_H__ + +#define FR_NUMAUTH 32 + +typedef struct fr_authstat { + U_QUAD_T fas_hits; + U_QUAD_T fas_miss; + u_long fas_nospace; + u_long fas_added; + u_long fas_sendfail; + u_long fas_sendok; + u_long fas_queok; + u_long fas_quefail; + u_long fas_expire; +} fr_authstat_t; + +typedef struct frauth { + int fra_age; + int fra_index; + u_32_t fra_pass; + fr_info_t fra_info; +#if SOLARIS + queue_t *fra_q; +#endif +} frauth_t; + +typedef struct frauthent { + struct frentry fae_fr; + struct frauthent *fae_next; + u_long fae_age; +} frauthent_t; + + +extern frentry_t *ipauth; +extern struct fr_authstat fr_authstats; +extern int fr_defaultauthage; +extern int fr_authstart; +extern int fr_authend; +extern int fr_authsize; +extern int fr_authused; +extern int fr_checkauth __P((ip_t *, fr_info_t *)); +extern void fr_authexpire __P((void)); +extern void fr_authunload __P((void)); +extern mb_t *fr_authpkts[]; +#if defined(_KERNEL) && SOLARIS +extern int fr_newauth __P((mb_t *, fr_info_t *, ip_t *, qif_t *)); +#else +extern int fr_newauth __P((mb_t *, fr_info_t *, ip_t *)); +#endif +#if defined(__NetBSD__) || defined(__OpenBSD__) +extern int fr_auth_ioctl __P((caddr_t, u_long, frentry_t *, frentry_t **)); +#else +extern int fr_auth_ioctl __P((caddr_t, int, frentry_t *, frentry_t **)); +#endif +#endif /* __IP_AUTH_H__ */ diff --git a/sys/netinet/ip_fil.c b/sys/netinet/ip_fil.c index 6e90df072bb..f28ce0f7cac 100644 --- a/sys/netinet/ip_fil.c +++ b/sys/netinet/ip_fil.c @@ -1,34 +1,82 @@ -/* $OpenBSD: ip_fil.c,v 1.15 1997/12/03 01:25:32 kstailey Exp $ */ +/* $OpenBSD: ip_fil.c,v 1.16 1998/01/26 04:10:39 dgregor Exp $ */ /* - * (C)opyright 1993,1994,1995 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 !defined(lint) && defined(LIBC_SCCS) -static char sccsid[] = "@(#)ip_fil.c 2.41 6/5/96 (C) 1993-1995 Darren Reed"; -static char rcsid[] = "$DRId: ip_fil.c,v 2.0.1.8 1997/03/20 15:51:56 darrenr Exp $"; +#if !defined(lint) +static const char sccsid[] = "@(#)ip_fil.c 2.41 6/5/96 (C) 1993-1995 Darren Reed"; +static const char rcsid[] = "@(#)$Id: ip_fil.c,v 1.16 1998/01/26 04:10:39 dgregor Exp $"; #endif -#include <sys/param.h> +#ifndef SOLARIS +#define SOLARIS (defined(sun) && (defined(__svr4__) || defined(__SVR4))) +#endif + +#if defined(KERNEL) && !defined(_KERNEL) +# define _KERNEL +#endif +#ifdef __FreeBSD__ +# if defined(_KERNEL) && !defined(IPFILTER_LKM) +# include <sys/osreldate.h> +# else +# include <osreldate.h> +# endif +#endif +#ifndef _KERNEL +# include <stdio.h> +# include <string.h> +# include <stdlib.h> +# include <ctype.h> +#endif #include <sys/errno.h> +#include <sys/types.h> +#include <sys/param.h> #include <sys/file.h> -#include <sys/ioctl.h> -#include <sys/mbuf.h> +#if __FreeBSD_version >= 220000 && defined(_KERNEL) +# include <sys/fcntl.h> +# include <sys/filio.h> +#else +# include <sys/ioctl.h> +#endif +#include <sys/time.h> +#ifdef _KERNEL +# include <sys/systm.h> +#endif +#include <sys/uio.h> +#if !SOLARIS +# if (NetBSD > 199609) || (OpenBSD > 199603) +# include <sys/dirent.h> +# else +# include <sys/dir.h> +# endif +# include <sys/mbuf.h> +#else +# include <sys/filio.h> +#endif #include <sys/protosw.h> #include <sys/socket.h> -#include <sys/syslog.h> -#include <sys/systm.h> -#include <sys/uio.h> #include <net/if.h> #ifdef sun -#include <net/af.h> +# include <net/af.h> +#endif +#if __FreeBSD_version >= 300000 +# include <net/if_var.h> +#endif +#ifdef __sgi +#include <sys/debug.h> +# ifdef IFF_DRVRLOCK /* IRIX6 */ +#include <sys/hashing.h> +# endif #endif #include <net/route.h> #include <netinet/in.h> +#if !(defined(__sgi) && !defined(IFF_DRVRLOCK)) /* IRIX < 6 */ #include <netinet/in_var.h> +#endif #include <netinet/in_systm.h> #include <netinet/ip.h> #include <netinet/ip_var.h> @@ -36,60 +84,79 @@ static char rcsid[] = "$DRId: ip_fil.c,v 2.0.1.8 1997/03/20 15:51:56 darrenr #include <netinet/udp.h> #include <netinet/tcpip.h> #include <netinet/ip_icmp.h> - +#ifndef _KERNEL +# include <syslog.h> +#endif #include "ip_fil_compat.h" #include "ip_fil.h" -#include "ip_frag.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 +#if !SOLARIS && defined(_KERNEL) +extern int ip_optcopy __P((struct ip *, struct ip *)); +#endif + -extern fr_flags, fr_active; extern struct protosw inetsw[]; -extern int (*fr_checkp) __P((ip_t *, int, struct ifnet *, int, - struct mbuf **)); -#if BSD < 199306 -extern int ipfr_slowtimer __P((void)); -static int (*fr_saveslowtimo) __P((void)); -extern int tcp_ttl; + +#ifndef _KERNEL +# include "ipt.h" +static struct ifnet **ifneta = NULL; +static int nifs = 0; #else -extern void ipfr_slowtimer __P((void)); +# if (BSD < 199306) && !defined(__sgi) +static int (*fr_saveslowtimo) __P((void)); +# else static void (*fr_saveslowtimo) __P((void)); +# endif +# if (BSD < 199306) || defined(__sgi) +extern int tcp_ttl; +# endif #endif -static void frzerostats __P((caddr_t)); - int ipl_inited = 0; +#ifdef __OpenBSD__ int ipl_unreach = ICMP_UNREACH_FILTER_PROHIB; -int send_reset __P((struct tcpiphdr *)); +#else +int ipl_unreach = ICMP_UNREACH_FILTER; +#endif +u_long ipl_frouteok[2] = {0, 0}; -#ifdef IPFILTER_LOG -# define LOGSIZE 8192 -int ipllog __P((u_int, ip_t *, register fr_info_t *, struct mbuf *)); -char iplbuf[LOGSIZE]; -caddr_t iplh = iplbuf, iplt = iplbuf; -static int iplused = 0; -#endif /* IPFILTER_LOG */ -static int (*fr_savep) __P((ip_t *, int, struct ifnet *, int, - struct mbuf **)); -static void frflush __P((caddr_t)); -static int frrequest __P((u_long, caddr_t, int)); +static void fixskip __P((frentry_t **, frentry_t *, int)); +static void frzerostats __P((caddr_t)); +static void frsync __P((void)); +#if defined(__NetBSD__) || defined(__OpenBSD__) +static int frrequest __P((int, u_long, caddr_t, int)); +#else +static int frrequest __P((int, int, caddr_t, int)); +#endif +#ifdef _KERNEL +static int (*fr_savep) __P((ip_t *, int, void *, int, struct mbuf **)); +#else +int ipllog __P((void)); +void init_ifp __P((void)); +# ifdef __sgi +static int no_output __P((struct ifnet *, struct mbuf *, + struct sockaddr *)); +static int write_output __P((struct ifnet *, struct mbuf *, + struct sockaddr *)); +# else +static int no_output __P((struct ifnet *, struct mbuf *, + struct sockaddr *, struct rtentry *)); +static int write_output __P((struct ifnet *, struct mbuf *, + struct sockaddr *, struct rtentry *)); +# endif +#endif -#if _BSDI_VERSION >= 199501 +#if (_BSDI_VERSION >= 199510) && defined(_KERNEL) # include <sys/device.h> # include <sys/conf.h> -int iplioctl __P((dev_t, int, caddr_t, int, struct proc *)); -int iplopen __P((dev_t, int, int, struct proc *)); -int iplclose __P((dev_t, int, int, struct proc *)); -# ifdef IPFILTER_LOG -int iplread __P((dev_t, struct uio *, int)); -# else -# define iplread noread -# endif - struct cfdriver iplcd = { NULL, "ipl", NULL, NULL, DV_DULL, 0 }; @@ -100,44 +167,48 @@ struct devsw iplsw = { nostrat, nodump, nopsize, 0, nostop }; -#else /* _BSDI_VERSION >= 199501 */ +#endif /* _BSDI_VERSION >= 199510 && _KERNEL */ +#ifdef __OpenBSD__ /* called by main() at boot time */ -void iplattach __P((int)); - -# ifdef IPFILTER_LOG -# if BSD >= 199306 -int iplread __P((dev_t, struct uio *, int)); -# else -int iplread __P((dev_t, struct uio *)); -# endif -# else -# define iplread noread -# endif -int iplioctl __P((dev_t, int, caddr_t, int)); -int iplopen __P((dev_t, int)); -int iplclose __P((dev_t, int)); -#endif /* _BSDI_VERSION >= 199501 */ - -int ipl_enable __P((void)); -int ipl_disable __P((void)); - -#ifdef IPFILTER_LKM -int iplidentify __P((char *)); +void iplattach __P((int)); +#endif -int -iplidentify(s) - char *s; +#if defined(__NetBSD__) || defined(__OpenBSD__) +# include <sys/conf.h> +# if defined(NETBSD_PF) +# include <net/pfil.h> +/* + * We provide the fr_checkp name just to minimize changes later. + */ +int (*fr_checkp) __P((ip_t *ip, int hlen, void *ifp, int out, mb_t **mp)); +# endif /* NETBSD_PF */ +#endif /* __NetBSD__ */ + +#ifdef _KERNEL +# if defined(IPFILTER_LKM) && !defined(__sgi) +int iplidentify(s) +char *s; { if (strcmp(s, "ipl") == 0) return 1; return 0; } -#else -void iplinit __P((void)); -#endif /* IPFILTER_LKM */ +# endif /* IPFILTER_LKM */ + + +/* + * Try to detect the case when compiling for NetBSD with pseudo-device + */ +# if defined(__NetBSD__) && defined(PFIL_HOOKS) +void +ipfilterattach(count) +int count; +{ + iplattach(); +} +# endif -void ipfr_fastroute __P((struct mbuf *, fr_info_t *, frdest_t *)); /* * None of the machinery should be initialized until the caller explicitly @@ -152,55 +223,105 @@ int dummy; int ipl_enable() { + char *defpass; int s; +# ifdef __sgi + int error; +# endif - SPLNET(s); + SPL_NET(s); if (ipl_inited || (fr_checkp == fr_check)) { printf("IP Filter: already initialized\n"); - SPLX(s); + SPL_X(s); return EBUSY; } + +# ifdef NETBSD_PF + pfil_add_hook((void *)fr_check, PFIL_IN|PFIL_OUT); +# endif + +# ifdef __sgi + error = ipfilter_sgi_attach(); + if (error) { + SPL_X(s); + return error; + } +# endif + ipl_inited = 1; - bzero((char *)nat_table, sizeof(nat_t *) * NAT_SIZE * 2); + bzero((char *)frcache, sizeof(frcache)); + bzero((char *)nat_table, sizeof(nat_table)); fr_savep = fr_checkp; fr_checkp = fr_check; fr_saveslowtimo = inetsw[0].pr_slowtimo; inetsw[0].pr_slowtimo = ipfr_slowtimer; - SPLX(s); + +# ifdef IPFILTER_LOG + ipflog_init(); +# endif + SPL_X(s); + if (fr_pass & FR_PASS) + defpass = "pass"; + else if (fr_pass & FR_BLOCK) + defpass = "block"; + else + defpass = "no-match -> block"; + +#ifdef __OpenBSD__ /* don't print this message */ + printf("IP Filter: initialized. Default = %s all, Logging = %s\n", + defpass, +# ifdef IPFILTER_LOG + "enabled"); +# else + "disabled"); +# endif +#endif /* __OpenBSD__ */ return 0; } -int -ipl_disable() +/* + * Disable the filter by removing the hooks from the IP input/output + * stream. + */ +int ipl_disable() { int s, i = FR_INQUE|FR_OUTQUE; - SPLNET(s); + SPL_NET(s); if (!ipl_inited) { printf("IP Filter: not initialized\n"); - SPLX(s); - return EBUSY; + SPL_X(s); + return 0; } fr_checkp = fr_savep; inetsw[0].pr_slowtimo = fr_saveslowtimo; - frflush((caddr_t)&i); + frflush(IPL_LOGIPF, &i); ipl_inited = 0; +# ifdef NETBSD_PF + pfil_remove_hook((void *)fr_check, PFIL_IN|PFIL_OUT); +# endif + +# ifdef __sgi + ipfilter_sgi_detach(); +# endif + ipfr_unload(); ip_natunload(); fr_stateunload(); + fr_authunload(); - SPLX(s); + SPL_X(s); return 0; } +#endif /* _KERNEL */ -static void -frzerostats(data) - caddr_t data; +static void frzerostats(data) +caddr_t data; { struct friostat fio; @@ -215,77 +336,59 @@ frzerostats(data) fio.f_acctout[0] = ipacct[1][0]; fio.f_acctout[1] = ipacct[1][1]; fio.f_active = fr_active; + fio.f_froute[0] = ipl_frouteok[0]; + fio.f_froute[1] = ipl_frouteok[1]; IWCOPY((caddr_t)&fio, data, sizeof(fio)); bzero((char *)frstats, sizeof(*frstats) * 2); } -static void -frflush(data) - caddr_t data; -{ - struct frentry *f, **fp; - int flags = *(int *)data, flushed = 0, set = fr_active; - - bzero((char *)frcache, sizeof(frcache[0]) * 2); - - if (flags & FR_INACTIVE) - set = 1 - set; - if (flags & FR_OUTQUE) { - for (fp = &ipfilter[1][set]; (f = *fp); ) { - *fp = f->fr_next; - KFREE(f); - flushed++; - } - for (fp = &ipacct[1][set]; (f = *fp); ) { - *fp = f->fr_next; - KFREE(f); - flushed++; - } - } - if (flags & FR_INQUE) { - for (fp = &ipfilter[0][set]; (f = *fp); ) { - *fp = f->fr_next; - KFREE(f); - flushed++; - } - for (fp = &ipacct[0][set]; (f = *fp); ) { - *fp = f->fr_next; - KFREE(f); - flushed++; - } - } - *(int *)data = flushed; -} - - /* * Filter ioctl interface. */ -int -iplioctl(dev, cmd, data, mode -#if _BSDI_VERSION >= 199501 - , p) - struct proc *p; +#ifdef __sgi +int IPL_EXTERN(ioctl)(dev_t dev, int cmd, caddr_t data, int mode +# ifdef _KERNEL + , cred_t *cp, int *rp +# endif +) +#else +int IPL_EXTERN(ioctl)(dev, cmd, data, mode +#if ((_BSDI_VERSION >= 199510) || (BSD >= 199506) || (NetBSD >= 199511) || \ + (__FreeBSD_version >= 220000) || defined(__OpenBSD__)) && defined(_KERNEL) +, p) +struct proc *p; +#else +) +#endif +dev_t dev; +#if defined(__NetBSD__) || defined(__OpenBSD__) || (_BSDI_VERSION >= 199701) +u_long cmd; #else - ) +int cmd; #endif - dev_t dev; - int cmd; - caddr_t data; - int mode; +caddr_t data; +int mode; +#endif /* __sgi */ { - int error = 0, s, unit; +#if defined(_KERNEL) && !SOLARIS + int s; +#endif + int error = 0, unit = 0, tmp; - unit = minor(dev); - if (unit != 0) +#ifdef _KERNEL + unit = GET_MINOR(dev); + if ((IPL_LOGMAX < unit) || (unit < 0)) return ENXIO; +#endif + +#if defined(__OpenBSD__) && defined(_KERNEL) if (securelevel > 1) { switch (cmd) { -#ifndef IPFILTER_LKM +# ifndef IPFILTER_LKM case SIOCFRENB: -#endif +# endif case SIOCSETFF: case SIOCADAFR: case SIOCADIFR: @@ -297,9 +400,9 @@ iplioctl(dev, cmd, data, mode case SIOCSWAPA: case SIOCFRZST: case SIOCIPFFL: -#ifdef IPFILTER_LOG +# ifdef IPFILTER_LOG case SIOCIPFFB: -#endif +# endif case SIOCADNAT: case SIOCRMNAT: case SIOCFLNAT: @@ -307,15 +410,28 @@ iplioctl(dev, cmd, data, mode return EPERM; } } +#endif - SPLNET(s); + SPL_NET(s); + + if (unit == IPL_LOGNAT) { + error = nat_ioctl(data, cmd, mode); + SPL_X(s); + return error; + } + if (unit == IPL_LOGSTATE) { + error = fr_state_ioctl(data, cmd, mode); + SPL_X(s); + return error; + } switch (cmd) { case FIONREAD : #ifdef IPFILTER_LOG - *(int *)data = iplused; + IWCOPY((caddr_t)&iplused[IPL_LOGIPF], (caddr_t)data, + sizeof(iplused[IPL_LOGIPF])); #endif break; -#ifndef IPFILTER_LKM +#if !defined(IPFILTER_LKM) && defined(_KERNEL) case SIOCFRENB : { u_int enable; @@ -348,7 +464,7 @@ iplioctl(dev, cmd, data, mode if (!(mode & FWRITE)) error = EPERM; else - error = frrequest(cmd, data, fr_active); + error = frrequest(unit, cmd, data, fr_active); break; case SIOCINIFR : case SIOCRMIFR : @@ -356,7 +472,7 @@ iplioctl(dev, cmd, data, mode if (!(mode & FWRITE)) error = EPERM; else - error = frrequest(cmd, data, 1 - fr_active); + error = frrequest(unit, cmd, data, 1 - fr_active); break; case SIOCSWAPA : if (!(mode & FWRITE)) @@ -381,7 +497,10 @@ iplioctl(dev, cmd, data, mode fio.f_acctin[1] = ipacct[0][1]; fio.f_acctout[0] = ipacct[1][0]; fio.f_acctout[1] = ipacct[1][1]; + fio.f_auth = ipauth; fio.f_active = fr_active; + fio.f_froute[0] = ipl_frouteok[0]; + fio.f_froute[1] = ipl_frouteok[1]; IWCOPY((caddr_t)&fio, data, sizeof(fio)); break; } @@ -394,72 +513,139 @@ iplioctl(dev, cmd, data, mode case SIOCIPFFL : if (!(mode & FWRITE)) error = EPERM; - else - frflush(data); + else { + IRCOPY(data, (caddr_t)&tmp, sizeof(tmp)); + frflush(unit, &tmp); + IWCOPY((caddr_t)&tmp, data, sizeof(tmp)); + } break; #ifdef IPFILTER_LOG case SIOCIPFFB : if (!(mode & FWRITE)) error = EPERM; - else { - *(int *)data = iplused; - iplh = iplt = iplbuf; - iplused = 0; - } + else + *(int *)data = ipflog_clear(unit); break; #endif /* IPFILTER_LOG */ - case SIOCADNAT : - case SIOCRMNAT : - case SIOCGNATS : - case SIOCGNATL : - case SIOCFLNAT : - case SIOCCNATL : - error = nat_ioctl(data, cmd, mode); - break; case SIOCGFRST : IWCOPY((caddr_t)ipfr_fragstats(), data, sizeof(ipfrstat_t)); break; - case SIOCGIPST : - IWCOPY((caddr_t)fr_statetstats(), data, sizeof(ips_stat_t)); + case SIOCAUTHW : + case SIOCAUTHR : + if (!(mode & FWRITE)) { + error = EPERM; + break; + } + case SIOCATHST : + error = fr_auth_ioctl(data, cmd, NULL, NULL); + break; + case SIOCFRSYN : + if (!(mode & FWRITE)) + error = EPERM; + else { +#if defined(_KERNEL) && defined(__sgi) + ipfsync(); +#endif + frsync(); + } break; default : error = EINVAL; break; } - SPLX(s); + SPL_X(s); return error; } -static int -frrequest(req, data, set) - u_long req; - caddr_t data; - int set; +static void frsync() +{ +#ifdef _KERNEL + struct ifnet *ifp; + +# if defined(__OpenBSD__) || (NetBSD >= 199511) + for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_list.tqe_next) +# else + for (ifp = ifnet; ifp; ifp = ifp->if_next) +# endif + ip_natsync(ifp); +#endif +} + + +static void fixskip(listp, rp, addremove) +frentry_t **listp, *rp; +int addremove; +{ + frentry_t *fp; + int rules = 0, rn = 0; + + for (fp = *listp; fp && (fp != rp); fp = fp->fr_next, rules++) + ; + + if (!fp) + return; + + for (fp = *listp; fp && (fp != rp); fp = fp->fr_next, rn++) + if (fp->fr_skip && (rn + fp->fr_skip >= rules)) + fp->fr_skip += addremove; +} + + +static int frrequest(unit, req, data, set) +int unit; +#if defined(__NetBSD__) || defined(__OpenBSD__) +u_long req; +#else +int req; +#endif +int set; +caddr_t data; { register frentry_t *fp, *f, **fprev; register frentry_t **ftail; frentry_t frd; frdest_t *fdp; - int error = 0, in; + frgroup_t *fg = NULL; + int error = 0, in, group; fp = &frd; IRCOPY(data, (caddr_t)fp, sizeof(*fp)); + /* + * Check that the group number does exist and that if a head group + * has been specified, doesn't exist. + */ + if (fp->fr_grhead && + fr_findgroup(fp->fr_grhead, fp->fr_flags, unit, set, NULL)) + return EEXIST; + if (fp->fr_group && + !fr_findgroup(fp->fr_group, fp->fr_flags, unit, set, NULL)) + return ESRCH; + in = (fp->fr_flags & FR_INQUE) ? 0 : 1; - if (fp->fr_flags & FR_ACCOUNT) { + + if (unit == IPL_LOGAUTH) + ftail = fprev = &ipauth; + else if (fp->fr_flags & FR_ACCOUNT) ftail = fprev = &ipacct[in][set]; - } else if (fp->fr_flags & (FR_OUTQUE|FR_INQUE)) + else if (fp->fr_flags & (FR_OUTQUE|FR_INQUE)) ftail = fprev = &ipfilter[in][set]; else return ESRCH; + if ((group = fp->fr_group)) { + if (!(fg = fr_findgroup(group, fp->fr_flags, unit, set, NULL))) + return ESRCH; + ftail = fprev = fg->fg_start; + } + bzero((char *)frcache, sizeof(frcache[0]) * 2); if (*fp->fr_ifname) { fp->fr_ifa = GETUNIT(fp->fr_ifname); if (!fp->fr_ifa) - fp->fr_ifa = (struct ifnet *)-1; + fp->fr_ifa = (void *)-1; } fdp = &fp->fr_dif; @@ -515,18 +701,39 @@ frrequest(req, data, set) if (!f) error = ESRCH; else { + if (f->fr_ref > 1) + return EBUSY; + if (fg && fg->fg_head) + fg->fg_head->fr_ref--; + if (unit == IPL_LOGAUTH) + return fr_auth_ioctl(data, req, f, ftail); + if (f->fr_grhead) + fr_delgroup(f->fr_grhead, fp->fr_flags, unit, + set); + fixskip(fprev, f, -1); *ftail = f->fr_next; - (void) KFREE(f); + KFREE(f); } } else { if (f) error = EEXIST; else { - if ((f = (struct frentry *)KMALLOC(sizeof(*f)))) { + if (unit == IPL_LOGAUTH) + return fr_auth_ioctl(data, req, f, ftail); + KMALLOC(f, frentry_t *, sizeof(*f)); + if (f != NULL) { + if (fg && fg->fg_head) + fg->fg_head->fr_ref++; bcopy((char *)fp, (char *)f, sizeof(*f)); + f->fr_ref = 1; f->fr_hits = 0; f->fr_next = *ftail; *ftail = f; + if (req == SIOCINIFR || req == SIOCINAFR) + fixskip(fprev, f, 1); + f->fr_grp = NULL; + if ((group = f->fr_grhead)) + fg = fr_addgroup(group, f, unit, set); } else error = ENOMEM; } @@ -535,216 +742,130 @@ frrequest(req, data, set) } -#if !defined(linux) +#ifdef _KERNEL /* * routines below for saving IP headers to buffer */ -int -iplopen(dev, flags -#if _BSDI_VERSION >= 199501 - , devtype, p) - int devtype; - struct proc *p; +#ifdef __sgi +# ifdef _KERNEL +int IPL_EXTERN(open)(dev_t *pdev, int flags, int devtype, cred_t *cp) +# else +int IPL_EXTERN(open)(dev_t dev, int flags) +# endif #else - ) -#endif - dev_t dev; - int flags; +int IPL_EXTERN(open)(dev, flags +# if ((_BSDI_VERSION >= 199510) || (BSD >= 199506) || (NetBSD >= 199511) || \ + (__FreeBSD_version >= 220000) || defined(__OpenBSD__)) && defined(_KERNEL) +, devtype, p) +int devtype; +struct proc *p; +# else +) +# endif +dev_t dev; +int flags; +#endif /* __sgi */ { - u_int min = minor(dev); +#if defined(__sgi) && defined(_KERNEL) + u_int min = geteminor(*pdev); +#else + u_int min = GET_MINOR(dev); +#endif - if (min) + if (2 < min) min = ENXIO; + else + min = 0; return min; } -int -iplclose(dev, flags -#if _BSDI_VERSION >= 199501 - , devtype, p) - int devtype; - struct proc *p; +#ifdef __sgi +int IPL_EXTERN(close)(dev_t dev, int flags, int devtype, cred_t *cp) #else - ) -#endif - dev_t dev; - int flags; +int IPL_EXTERN(close)(dev, flags +# if ((_BSDI_VERSION >= 199510) || (BSD >= 199506) || (NetBSD >= 199511) || \ + (__FreeBSD_version >= 220000) || defined(__OpenBSD__)) && defined(_KERNEL) +, devtype, p) +int devtype; +struct proc *p; +# else +) +# endif +dev_t dev; +int flags; +#endif /* __sgi */ { - u_int min = minor(dev); + u_int min = GET_MINOR(dev); - if (min) + if (2 < min) min = ENXIO; + else + min = 0; return min; } -# ifdef IPFILTER_LOG /* * iplread/ipllog * both of these must operate with at least splnet() lest they be * called during packet processing and cause an inconsistancy to appear in * the filter lists. */ +#ifdef __sgi +int IPL_EXTERN(read)(dev_t dev, uio_t *uio, cred_t *crp) +#else # if BSD >= 199306 -int -iplread(dev, uio, ioflag) - int ioflag; +int IPL_EXTERN(read)(dev, uio, ioflag) +int ioflag; # else -int -iplread(dev, uio) +int IPL_EXTERN(read)(dev, uio) # endif - dev_t dev; - register struct uio *uio; +dev_t dev; +register struct uio *uio; +#endif /* __sgi */ { - register int ret, s; - register size_t sz, sx; - int error; - - if (!uio->uio_resid) - return 0; - while (!iplused) { - error = SLEEP(iplbuf, "ipl_sleep"); - if (error) - return error; - } - SPLNET(s); - - sx = sz = MIN(uio->uio_resid, iplused); - if (iplh < iplt) - sz = MIN(sz, LOGSIZE - (iplt - iplbuf)); - sx -= sz; - -# if BSD >= 199306 || defined(__FreeBSD__) - uio->uio_rw = UIO_READ; +# ifdef IPFILTER_LOG + return ipflog_read(GET_MINOR(dev), uio); +# else + return ENXIO; # endif - if (!(ret = UIOMOVE(iplt, sz, UIO_READ, uio))) { - iplt += sz; - iplused -= sz; - if ((iplh < iplt) && (iplt == iplbuf + LOGSIZE)) - iplt = iplbuf; - - if (sx && !(ret = UIOMOVE(iplt, sx, UIO_READ, uio))) { - iplt += sx; - iplused -= sx; - if ((iplh < iplt) && (iplt == iplbuf + LOGSIZE)) - iplt = iplbuf; - } - if (!iplused) /* minimise wrapping around the end */ - iplh = iplt = iplbuf; - } - SPLX(s); - return ret; } -# endif /* IPFILTER_LOG */ -#endif /* linux */ - - -#ifdef IPFILTER_LOG -int -ipllog(flags, ip, fin, m) - u_int flags; - ip_t *ip; - register fr_info_t *fin; - struct mbuf *m; -{ - struct ipl_ci iplci; - register int len, mlen, hlen; - struct ifnet *ifp = fin->fin_ifp; - - hlen = fin->fin_hlen; - if (ip->ip_p == IPPROTO_TCP || ip->ip_p == IPPROTO_UDP) - hlen += MIN(sizeof(tcphdr_t), fin->fin_dlen); - else if (ip->ip_p == IPPROTO_ICMP) { - struct icmp *icmp = (struct icmp *)((char *)ip + hlen); - - switch (icmp->icmp_type) { - case ICMP_UNREACH : - case ICMP_SOURCEQUENCH : - case ICMP_REDIRECT : - case ICMP_TIMXCEED : - case ICMP_PARAMPROB : - hlen += MIN(sizeof(struct icmp) + 8, fin->fin_dlen); - break; - default : - hlen += MIN(sizeof(struct icmp), fin->fin_dlen); - break; - } - } - mlen = (flags & FR_LOGBODY) ? MIN(ip->ip_len - hlen, 128) : 0; - len = hlen + sizeof(iplci) + mlen; - if (iplused + len > LOGSIZE) - return 0; - iplused += len; - -# ifdef sun - uniqtime(&iplci); -# endif -# if BSD >= 199306 || defined(__FreeBSD__) - microtime((struct timeval *)&iplci); -# endif - iplci.flags = flags; - iplci.hlen = (u_char)hlen; - iplci.plen = (u_char)mlen; - iplci.rule = fin->fin_rule; -# if (defined(NetBSD) && (NetBSD <= 1991011) && (NetBSD >= 199603)) || \ - (defined(OpenBSD) && (OpenBSD >= 199603)) - strncpy(iplci.ifname, ifp->if_xname, IFNAMSIZ); -# else - iplci.unit = (u_char)ifp->if_unit; - if ((iplci.ifname[0] = ifp->if_name[0])) - if ((iplci.ifname[1] = ifp->if_name[1])) - if ((iplci.ifname[2] = ifp->if_name[2])) - iplci.ifname[3] = ifp->if_name[3]; -# endif - /* - * Guaranteed to succeed from above - */ - (void) fr_copytolog((char *)&iplci, sizeof(iplci)); - - for (len -= sizeof(iplci); m && len > 0; m = m->m_next, len -= hlen) { - hlen = MIN(len, m->m_len); - if (fr_copytolog(mtod(m, char *), hlen)) - break; - } - - wakeup(iplbuf); - return 1; -} -#endif /* IPFILTER_LOG */ /* * send_reset - this could conceivably be a call to tcp_respond(), but that * requires a large amount of setting up and isn't any more efficient. */ -int -send_reset(ti) - struct tcpiphdr *ti; +int send_reset(ti) +struct tcpiphdr *ti; { struct tcpiphdr *tp; - struct ip *ip; struct tcphdr *tcp; struct mbuf *m; - int tlen = 0; + int tlen = 0, err; + ip_t *ip; +# if defined(__FreeBSD_version) && (__FreeBSD_version >= 220000) + struct route ro; +# endif if (ti->ti_flags & TH_RST) return -1; /* feedback loop */ -#if BSD < 199306 +# if (BSD < 199306) || defined(__sgi) m = m_get(M_DONTWAIT, MT_HEADER); -#else +# else m = m_gethdr(M_DONTWAIT, MT_HEADER); m->m_data += max_linkhdr; -#endif +# endif if (m == NULL) return -1; if (ti->ti_flags & TH_SYN) tlen = 1; m->m_len = sizeof (struct tcpiphdr); -#if BSD >= 199306 +# if BSD >= 199306 m->m_pkthdr.len = sizeof (struct tcpiphdr); m->m_pkthdr.rcvif = (struct ifnet *)0; -#endif +# endif bzero(mtod(m, char *), sizeof(struct tcpiphdr)); ip = mtod(m, struct ip *); tp = mtod(m, struct tcpiphdr *); @@ -764,39 +885,60 @@ send_reset(ti) ip->ip_tos = ((struct ip *)ti)->ip_tos; ip->ip_p = ((struct ip *)ti)->ip_p; ip->ip_len = sizeof (struct tcpiphdr); -#if BSD < 199306 +# if (BSD < 199306) || defined(__sgi) ip->ip_ttl = tcp_ttl; -#else +# else ip->ip_ttl = ip_defttl; -#endif +# endif +# if defined(__FreeBSD_version) && (__FreeBSD_version >= 220000) + bzero((char *)&ro, sizeof(ro)); + err = ip_output(m, (struct mbuf *)0, &ro, 0, 0); + if (ro.ro_rt) + RTFREE(ro.ro_rt); +# else /* * extra 0 in case of multicast */ - (void) ip_output(m, (struct mbuf *)0, 0, 0, 0); - return 0; + err = ip_output(m, (struct mbuf *)0, 0, 0, 0); +# endif + return err; } -#ifndef IPFILTER_LKM -# if BSD < 199306 +# if !defined(IPFILTER_LKM) && (__FreeBSD_version < 300000) && !defined(__sgi) +# if (BSD < 199306) +int iplinit __P((void)); + int -# else +# else +void iplinit __P((void)); + void -# endif +# endif iplinit() { /* (void) ipl_enable(); must explicitly enable with ipf -E */ ip_init(); } -#endif +# endif /* ! __NetBSD__ */ -void -ipfr_fastroute(m0, fin, fdp) - struct mbuf *m0; - fr_info_t *fin; - frdest_t *fdp; +size_t mbufchainlen(m0) +register struct mbuf *m0; +{ + register size_t len = 0; + + for (; m0; m0 = m0->m_next) + len += m0->m_len; + return len; +} + + +void ipfr_fastroute(m0, fin, fdp) +struct mbuf *m0; +fr_info_t *fin; +frdest_t *fdp; { register struct ip *ip, *mhip; register struct mbuf *m = m0; @@ -816,16 +958,19 @@ ipfr_fastroute(m0, fin, fdp) dst = (struct sockaddr_in *)&ro->ro_dst; dst->sin_family = AF_INET; dst->sin_addr = fdp->fd_ip.s_addr ? fdp->fd_ip : ip->ip_dst; -#if (BSD >= 199306) && !defined(__NetBSD__) && !defined(__bsdi__) \ - && !defined(__OpenBSD__) +# ifdef __bsdi__ + dst->sin_len = sizeof(*dst); +# endif +# if (BSD >= 199306) && !defined(__NetBSD__) && !defined(__bsdi__) && \ + !defined(__OpenBSD__) # ifdef RTF_CLONING rtalloc_ign(ro, RTF_CLONING); -# else +# else rtalloc_ign(ro, RTF_PRCLONING); -# endif -#else +# endif +# else rtalloc(ro); -#endif +# endif if (!ifp) { if (!(fin->fin_fr->fr_flags & FR_FASTROUTE)) { error = -2; @@ -855,20 +1000,19 @@ ipfr_fastroute(m0, fin, fdp) * If small enough for interface, can just send directly. */ if (ip->ip_len <= ifp->if_mtu) { -#ifndef sparc +# ifndef sparc ip->ip_id = htons(ip->ip_id); ip->ip_len = htons(ip->ip_len); ip->ip_off = htons(ip->ip_off); -#endif +# endif if (!ip->ip_sum) ip->ip_sum = in_cksum(m, hlen); -#if BSD >= 199306 +# if BSD >= 199306 error = (*ifp->if_output)(ifp, m, (struct sockaddr *)dst, ro->ro_rt); - -#else +# else error = (*ifp->if_output)(ifp, m, (struct sockaddr *)dst); -#endif +# endif goto done; } /* @@ -901,11 +1045,11 @@ ipfr_fastroute(m0, fin, fdp) error = ENOBUFS; goto bad; } -#if BSD >= 199306 +# if BSD >= 199306 m->m_data += max_linkhdr; -#else +# else m->m_off = MMAXOFF - hlen; -#endif +# endif mhip = mtod(m, struct ip *); bcopy((char *)ip, (char *)mhip, sizeof(*ip)); if (hlen > sizeof (struct ip)) { @@ -926,9 +1070,9 @@ ipfr_fastroute(m0, fin, fdp) error = ENOBUFS; /* ??? */ goto sendorfree; } -#ifndef sparc +# ifndef sparc mhip->ip_off = htons((u_short)mhip->ip_off); -#endif +# endif mhip->ip_sum = 0; mhip->ip_sum = in_cksum(m, mhlen); *mnext = m; @@ -948,18 +1092,23 @@ sendorfree: m0 = m->m_act; m->m_act = 0; if (error == 0) -#if BSD >= 199306 +# if BSD >= 199306 error = (*ifp->if_output)(ifp, m, (struct sockaddr *)dst, ro->ro_rt); -#else +# else error = (*ifp->if_output)(ifp, m, (struct sockaddr *)dst); -#endif +# endif else m_freem(m); } } done: + if (!error) + ipl_frouteok[0]++; + else + ipl_frouteok[1]++; + if (ro->ro_rt) { RTFREE(ro->ro_rt); } @@ -968,3 +1117,183 @@ bad: m_freem(m); goto done; } +#else /* #ifdef _KERNEL */ + + +#ifdef __sgi +static int no_output __P((struct ifnet *ifp, struct mbuf *m, + struct sockaddr *s)) +#else +static int no_output __P((struct ifnet *ifp, struct mbuf *m, + struct sockaddr *s, struct rtentry *rt)) +#endif +{ + return 0; +} + + +# ifdef __STDC__ +#ifdef __sgi +static int write_output __P((struct ifnet *ifp, struct mbuf *m, + struct sockaddr *s)) +#else +static int write_output __P((struct ifnet *ifp, struct mbuf *m, + struct sockaddr *s, struct rtentry *rt)) +#endif +{ +# if !(defined(NetBSD) && (NetBSD <= 1991011) && (NetBSD >= 199606)) || \ + (defined(OpenBSD) && (OpenBSD >= 199603)) + ip_t *ip = (ip_t *)m; +# endif +# else +static int write_output(ifp, ip) +struct ifnet *ifp; +ip_t *ip; +{ +# endif + FILE *fp; + char fname[32]; + +# if (defined(NetBSD) && (NetBSD <= 1991011) && (NetBSD >= 199606)) || \ + (defined(OpenBSD) && (OpenBSD >= 199603)) + sprintf(fname, "/tmp/%s", ifp->if_xname); + if ((fp = fopen(fname, "a"))) { + fclose(fp); + } +# else + sprintf(fname, "/tmp/%s%d", ifp->if_name, ifp->if_unit); + if ((fp = fopen(fname, "a"))) { + fwrite((char *)ip, ntohs(ip->ip_len), 1, fp); + fclose(fp); + } +# endif + return 0; +} + + +struct ifnet *get_unit(name) +char *name; +{ + struct ifnet *ifp, **ifa; +# if (defined(NetBSD) && (NetBSD <= 1991011) && (NetBSD >= 199606)) || \ + (defined(OpenBSD) && (OpenBSD >= 199603)) + for (ifa = ifneta; ifa && (ifp = *ifa); ifa++) { + if (!strcmp(name, ifp->if_xname)) + return ifp; + } +# else + char ifname[32], *s; + + for (ifa = ifneta; ifa && (ifp = *ifa); ifa++) { + (void) sprintf(ifname, "%s%d", ifp->if_name, ifp->if_unit); + if (!strcmp(name, ifname)) + return ifp; + } +# endif + + if (!ifneta) { + ifneta = (struct ifnet **)malloc(sizeof(ifp) * 2); + ifneta[1] = NULL; + ifneta[0] = (struct ifnet *)calloc(1, sizeof(*ifp)); + nifs = 1; + } else { + nifs++; + ifneta = (struct ifnet **)realloc(ifneta, + (nifs + 1) * sizeof(*ifa)); + ifneta[nifs] = NULL; + ifneta[nifs - 1] = (struct ifnet *)malloc(sizeof(*ifp)); + } + ifp = ifneta[nifs - 1]; + +# if (defined(NetBSD) && (NetBSD <= 1991011) && (NetBSD >= 199606)) || \ + (defined(OpenBSD) && (OpenBSD >= 199603)) + strncpy(ifp->if_xname, name, sizeof(ifp->if_xname)); +# else + for (s = name; *s && !isdigit(*s); s++) + ; + if (*s && isdigit(*s)) { + ifp->if_unit = atoi(s); + ifp->if_name = (char *)malloc(s - name + 1); + strncpy(ifp->if_name, name, s - name); + ifp->if_name[s - name] = '\0'; + } else { + ifp->if_name = strdup(name); + ifp->if_unit = -1; + } +# endif + ifp->if_output = no_output; + return ifp; +} + + + +void init_ifp() +{ + FILE *fp; + struct ifnet *ifp, **ifa; + char fname[32]; +# if (defined(NetBSD) && (NetBSD <= 1991011) && (NetBSD >= 199606)) || \ + (defined(OpenBSD) && (OpenBSD >= 199603)) + for (ifa = ifneta; ifa && (ifp = *ifa); ifa++) { + ifp->if_output = write_output; + sprintf(fname, "/tmp/%s", ifp->if_xname); + if ((fp = fopen(fname, "w"))) + fclose(fp); + } +# else + + for (ifa = ifneta; ifa && (ifp = *ifa); ifa++) { + ifp->if_output = write_output; + sprintf(fname, "/tmp/%s%d", ifp->if_name, ifp->if_unit); + if ((fp = fopen(fname, "w"))) + fclose(fp); + } +# endif +} + + +void ipfr_fastroute(ip, fin, fdp) +ip_t *ip; +fr_info_t *fin; +frdest_t *fdp; +{ + struct ifnet *ifp = fdp->fd_ifp; + + if (!ifp) + return; /* no routing table out here */ + + ip->ip_len = htons((u_short)ip->ip_len); + ip->ip_off = htons((u_short)(ip->ip_off | IP_MF)); + ip->ip_sum = 0; +#ifdef __sgi + (*ifp->if_output)(ifp, (void *)ip, NULL); +#else + (*ifp->if_output)(ifp, (void *)ip, NULL, 0); +#endif +} + + +int ipllog __P((void)) +{ + verbose("l"); + return 0; +} + + +int send_reset(ip, ifp) +ip_t *ip; +struct ifnet *ifp; +{ + verbose("- TCP RST sent\n"); + return 0; +} + + +int icmp_error(ip, ifp) +ip_t *ip; +struct ifnet *ifp; +{ + verbose("- TCP RST sent\n"); + return 0; +} +#endif /* _KERNEL */ diff --git a/sys/netinet/ip_fil.h b/sys/netinet/ip_fil.h index e9ec1254057..8bb0186cb60 100644 --- a/sys/netinet/ip_fil.h +++ b/sys/netinet/ip_fil.h @@ -1,18 +1,25 @@ -/* $OpenBSD: ip_fil.h,v 1.8 1997/02/11 22:23:16 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. * * @(#)ip_fil.h 1.35 6/5/96 - * Id: ip_fil.h,v 2.0.1.2 1997/01/10 00:28:15 darrenr Exp + * $Id: ip_fil.h,v 1.9 1998/01/26 04:10:39 dgregor Exp $ */ #ifndef __IP_FIL_H__ #define __IP_FIL_H__ +/* + * Pathnames for various IP Filter control devices. Used by LKM + * and userland, so defined here. + */ +#define IPNAT_NAME "/dev/ipnat" +#define IPSTATE_NAME "/dev/ipstate" +#define IPAUTH_NAME "/dev/ipauth" + #ifndef SOLARIS #define SOLARIS (defined(sun) && (defined(__svr4__) || defined(__SVR4))) #endif @@ -20,20 +27,12 @@ #if defined(KERNEL) && !defined(_KERNEL) #define _KERNEL #endif -#if SOLARIS -# include <sys/ioccom.h> -# include <sys/sysmacros.h> -# ifdef _KERNEL -# include <inet/common.h> -/* - * because Solaris 2 defines these in two places :-/ - */ -#undef IPOPT_EOL -#undef IPOPT_NOP -#undef IPOPT_LSRR -#undef IPOPT_RR -#undef IPOPT_SSRR -# include <inet/ip.h> + +#ifndef __P +# ifdef __STDC__ +# define __P(x) x +# else +# define __P(x) () # endif #endif @@ -54,6 +53,9 @@ #define SIOCFRSYN _IOW('r', 73, u_int) #define SIOCFRZST _IOWR('r', 74, struct friostat) #define SIOCZRLST _IOWR('r', 75, struct frentry) +#define SIOCAUTHW _IOWR('r', 76, struct fr_info) +#define SIOCAUTHR _IOWR('r', 77, struct fr_info) +#define SIOCATHST _IOWR('r', 78, struct fr_authstat) #else #define SIOCADAFR _IOW(r, 60, struct frentry) #define SIOCRMAFR _IOW(r, 61, struct frentry) @@ -71,6 +73,9 @@ #define SIOCFRSYN _IOW(r, 73, u_int) #define SIOCFRZST _IOWR(r, 74, struct friostat) #define SIOCZRLST _IOWR(r, 75, struct frentry) +#define SIOCAUTHW _IOWR(r, 76, struct fr_info) +#define SIOCAUTHR _IOWR(r, 77, struct fr_info) +#define SIOCATHST _IOWR(r, 78, struct fr_authstat) #endif #define SIOCADDFR SIOCADAFR #define SIOCDELFR SIOCRMAFR @@ -84,31 +89,42 @@ typedef struct fr_ip { u_char fi_p; struct in_addr fi_src; struct in_addr fi_dst; - u_long fi_optmsk; /* bitmask composed from IP options */ + u_32_t fi_optmsk; /* bitmask composed from IP options */ u_short fi_secmsk; /* bitmask composed from IP security options */ u_short fi_auth; } fr_ip_t; -#define FI_OPTIONS 0x01 -#define FI_TCPUDP 0x02 /* TCP/UCP implied comparison involved */ -#define FI_FRAG 0x04 -#define FI_SHORT 0x08 +#define FI_OPTIONS (FF_OPTIONS >> 24) +#define FI_TCPUDP (FF_TCPUDP >> 24) /* TCP/UCP implied comparison*/ +#define FI_FRAG (FF_FRAG >> 24) +#define FI_SHORT (FF_SHORT >> 24) typedef struct fr_info { struct fr_ip fin_fi; - void *fin_ifp; u_short fin_data[2]; u_short fin_out; + u_short fin_hlen; u_char fin_tcpf; - u_char fin_icode; + u_char fin_icode; /* From here on is packet specific */ u_short fin_rule; - u_short fin_hlen; + u_short fin_group; u_short fin_dlen; - char *fin_dp; /* start of data past IP header */ + u_short fin_id; + void *fin_ifp; struct frentry *fin_fr; + char *fin_dp; /* start of data past IP header */ + void *fin_mp; } fr_info_t; -#define FI_CSIZE (sizeof(struct fr_ip) + 11) +/* + * Size for compares on fr_info structures + */ +#define FI_CSIZE (sizeof(struct fr_ip) + sizeof(u_short) * 4 + \ + sizeof(u_char)) +/* + * Size for copying cache fr_info structure + */ +#define FI_COPYSIZE (sizeof(fr_info_t) - sizeof(void *) * 2) typedef struct frdest { void *fd_ifp; @@ -118,10 +134,17 @@ typedef struct frdest { typedef struct frentry { struct frentry *fr_next; - struct ifnet *fr_ifa; - u_long fr_hits; - u_long fr_bytes; /* this is only incremented when a packet */ - /* matches this rule and it is the last match*/ + u_short fr_group; /* group to which this rule belongs */ + u_short fr_grhead; /* group # which this rule starts */ + struct frentry *fr_grp; + int fr_ref; /* reference count - for grouping */ + void *fr_ifa; + /* + * These are only incremented when a packet matches this rule and + * it is the last match + */ + U_QUAD_T fr_hits; + U_QUAD_T fr_bytes; /* * Fields after this may not change whilst in the kernel. */ @@ -140,9 +163,9 @@ typedef struct frentry { u_short fr_sport; u_short fr_stop; /* top port for <> and >< */ u_short fr_dtop; /* top port for <> and >< */ - u_long fr_flags; /* per-rule flags && options (see below) */ - int (*fr_func) __P((int, ip_t *, fr_info_t *)); - /* call this function */ + u_32_t fr_flags; /* per-rule flags && options (see below) */ + int fr_skip; /* # of rules to skip */ + int (*fr_func) __P((int, ip_t *, fr_info_t *)); /* call this function */ char fr_icode; /* return ICMP code */ char fr_ifname[IFNAMSIZ]; struct frdest fr_tif; /* "to" interface */ @@ -164,11 +187,11 @@ typedef struct frentry { /* * fr_flags -*/ -#define FR_BLOCK 0x00001 -#define FR_PASS 0x00002 -#define FR_OUTQUE 0x00004 -#define FR_INQUE 0x00008 + */ +#define FR_BLOCK 0x00001 /* do not allow packet to pass */ +#define FR_PASS 0x00002 /* allow packet to pass */ +#define FR_OUTQUE 0x00004 /* outgoing packets */ +#define FR_INQUE 0x00008 /* ingoing packets */ #define FR_LOG 0x00010 /* Log */ #define FR_LOGB 0x00011 /* Log-fail */ #define FR_LOGP 0x00012 /* Log-pass */ @@ -176,7 +199,7 @@ typedef struct frentry { #define FR_LOGFIRST 0x00040 /* Log the first byte if state held */ #define FR_RETRST 0x00080 /* Return TCP RST packet - reset connection */ #define FR_RETICMP 0x00100 /* Return ICMP unreachable packet */ -#define FR_NOMATCH 0x00200 +#define FR_NOMATCH 0x00200 /* no match occured */ #define FR_ACCOUNT 0x00400 /* count packet bytes */ #define FR_KEEPFRAG 0x00800 /* keep fragment information */ #define FR_KEEPSTATE 0x01000 /* keep `connection' state information */ @@ -186,16 +209,28 @@ typedef struct frentry { #define FR_CALLNOW 0x10000 /* call another function (fr_func) if matches */ #define FR_DUP 0x20000 /* duplicate packet */ #define FR_LOGORBLOCK 0x40000 /* block the packet if it can't be logged */ +#define FR_NOTSRCIP 0x80000 /* not the src IP# */ +#define FR_NOTDSTIP 0x100000 /* not the dst IP# */ +#define FR_AUTH 0x200000 /* use authentication */ +#define FR_PREAUTH 0x400000 /* require preauthentication */ #define FR_LOGMASK (FR_LOG|FR_LOGP|FR_LOGB) + /* - * recognized flags for SIOCGETFF and SIOCSETFF + * These correspond to #define's for FI_* and are stored in fr_flags */ -#define FF_LOGPASS 0x100000 -#define FF_LOGBLOCK 0x200000 -#define FF_LOGNOMATCH 0x400000 +#define FF_OPTIONS 0x01000000 +#define FF_TCPUDP 0x02000000 +#define FF_FRAG 0x04000000 +#define FF_SHORT 0x08000000 +/* + * recognized flags for SIOCGETFF and SIOCSETFF, and get put in fr_flags + */ +#define FF_LOGPASS 0x10000000 +#define FF_LOGBLOCK 0x20000000 +#define FF_LOGNOMATCH 0x40000000 #define FF_LOGGING (FF_LOGPASS|FF_LOGBLOCK|FF_LOGNOMATCH) -#define FF_BLOCKNONIP 0x800000 /* Solaris2 Only */ +#define FF_BLOCKNONIP 0x80000000 /* Solaris2 Only */ #define FR_NONE 0 #define FR_EQUAL 1 @@ -224,6 +259,7 @@ typedef struct filterstats { u_long fr_bads; /* bad attempts to allocate packet state */ u_long fr_ads; /* new packet state kept */ u_long fr_chit; /* cached hit */ + u_long fr_tcpbad; /* TCP checksum check failures */ u_long fr_pull[2]; /* good and bad pullup attempts */ #if SOLARIS u_long fr_bad; /* bad IP packets to the filter */ @@ -241,61 +277,256 @@ typedef struct friostat { struct frentry *f_fout[2]; struct frentry *f_acctin[2]; struct frentry *f_acctout[2]; + struct frentry *f_auth; + u_long f_froute[2]; int f_active; } friostat_t; -typedef struct optlist { +typedef struct optlist { u_short ol_val; - int ol_bit; + int ol_bit; } optlist_t; + /* - * Log structure. Each packet header logged is prepended by one of these, - * minimize size to make most effective use of log space which should - * (ideally) be a muliple of the most common log entry size. + * Group list structure. */ -typedef struct ipl_ci { - u_long sec; - u_long usec; - u_char hlen; - u_char plen; - u_short rule; /* assume never more than 64k rules, total */ +typedef struct frgroup { + u_short fg_num; + struct frgroup *fg_next; + struct frentry *fg_head; + struct frentry **fg_start; +} frgroup_t; + + +/* + * Log structure. Each packet header logged is prepended by one of these. + * Following this in the log records read from the device will be an ipflog + * structure which is then followed by any packet data. + */ +typedef struct iplog { + u_long ipl_magic; + u_long ipl_sec; + u_long ipl_usec; + u_int ipl_len; + u_int ipl_count; + size_t ipl_dsize; + struct iplog *ipl_next; +} iplog_t; + +#define IPL_MAGIC 0x49504c4d /* 'IPLM' */ + +typedef struct ipflog { #if (defined(NetBSD) && (NetBSD <= 1991011) && (NetBSD >= 199603)) || \ - (defined(OpenBSD) && (OpenBSD >= 199603)) - u_long flags; - u_char ifname[IFNAMSIZ]; /* = 32 bytes */ + (defined(OpenBSD) && (OpenBSD >= 199603)) + u_char fl_ifname[IFNAMSIZ]; #else - u_long flags:24; - u_long unit:8; - u_char ifname[4]; /* = 20 bytes */ + u_int fl_unit; + u_char fl_ifname[4]; #endif -} ipl_ci_t; + u_char fl_plen; /* extra data after hlen */ + u_char fl_hlen; /* length of IP headers saved */ + u_short fl_rule; /* assume never more than 64k rules, total */ + u_short fl_group; + u_32_t fl_flags; +} ipflog_t; +# ifdef __OpenBSD__ +# ifndef ICMP_UNREACH_FILTER_PROHIB +# define ICMP_UNREACH_FILTER_PROHIB 13 +# endif +#else +# ifndef ICMP_UNREACH_FILTER +# define ICMP_UNREACH_FILTER 13 +# endif +#endif -#ifndef ICMP_UNREACH_FILTER_PROHIB -#define ICMP_UNREACH_FILTER_PROHIB 13 +#ifndef IPF_LOGGING +#define IPF_LOGGING 0 +#endif +#ifndef IPF_DEFAULT_PASS +#define IPF_DEFAULT_PASS FR_PASS #endif #define IPMINLEN(i, h) ((i)->ip_len >= ((i)->ip_hl * 4 + sizeof(struct h))) #define IPLLOGSIZE 8192 -#ifdef _KERNEL -extern int fr_check __P((ip_t *, int, struct ifnet *, int, - struct mbuf **)); -#else -extern int fr_check __P((ip_t *, int, struct ifnet *, int)); +/* + * Device filenames for reading log information. Use ipf on Solaris2 because + * ipl is already a name used by something else. + */ +#ifndef IPL_NAME +# if SOLARIS +# define IPL_NAME "/dev/ipf" +# else +# define IPL_NAME "/dev/ipl" +# endif #endif -extern int fr_copytolog(char *, int); -extern fr_info_t frcache[]; -extern char *iplh, *iplt; -extern char iplbuf[IPLLOGSIZE]; +#define IPL_NAT IPNAT_NAME +#define IPL_STATE IPSTATE_NAME +#define IPL_AUTH IPAUTH_NAME -#ifdef _KERNEL +#define IPL_LOGIPF 0 /* Minor device #'s for accessing logs */ +#define IPL_LOGNAT 1 +#define IPL_LOGSTATE 2 +#define IPL_LOGAUTH 3 +#define IPL_LOGMAX 3 -extern struct frentry *ipfilter[2][2], *ipacct[2][2]; -extern struct filterstats frstats[]; -# if SOLARIS -extern int ipfsync(); +#if !defined(CDEV_MAJOR) && defined (__FreeBSD_version) && \ + (__FreeBSD_version >= 220000) +# define CDEV_MAJOR 79 +#endif + +#ifndef _KERNEL +extern int fr_check __P((ip_t *, int, void *, int, mb_t **)); +extern int (*fr_checkp) __P((ip_t *, int, void *, int, mb_t **)); +extern int send_reset __P((ip_t *, struct ifnet *)); +extern int icmp_error __P((ip_t *, struct ifnet *)); +extern int ipf_log __P((void)); +extern void ipfr_fastroute __P((ip_t *, fr_info_t *, frdest_t *)); +extern struct ifnet *get_unit __P((char *)); +# define FR_SCANLIST(p, ip, fi, m) fr_scanlist(p, ip, fi, m) +# if defined(__NetBSD__) || defined(__OpenBSD__) || (_BSDI_VERSION >= 199701) +extern int iplioctl __P((dev_t, u_long, caddr_t, int)); +# else +extern int iplioctl __P((dev_t, int, caddr_t, int)); +# endif +# ifdef __OpenBSD__ +extern int iplopen __P((dev_t, int)); +extern int iplclose __P((dev_t, int)); # endif -#endif /* _KERNEL */ +extern int ipl_enable __P((void)); +extern int ipl_disable __P((void)); +#else /* #ifndef _KERNEL */ +# if defined(__NetBSD__) && defined(PFIL_HOOKS) +extern int ipfilterattach __P((int)); +# endif +# ifndef __OpenBSD__ +extern int iplattach __P((void)); +# endif +extern int ipl_enable __P((void)); +extern int ipl_disable __P((void)); +extern void ipflog_init __P((void)); +extern int ipflog_clear __P((int)); +extern int ipflog_read __P((int, struct uio *)); +extern int ipflog __P((u_int, ip_t *, fr_info_t *, mb_t *)); +extern int ipllog __P((int, u_long, void **, size_t *, int *, int)); +# if SOLARIS +extern int fr_check __P((ip_t *, int, void *, int, qif_t *, mb_t **)); +extern int (*fr_checkp) __P((ip_t *, int, void *, + int, qif_t *, mb_t **)); +extern int icmp_error __P((ip_t *, int, int, qif_t *, + struct in_addr)); +extern int iplioctl __P((dev_t, int, int, int, cred_t *, int *)); +extern int iplopen __P((dev_t *, int, int, cred_t *)); +extern int iplclose __P((dev_t, int, int, cred_t *)); +extern int ipfsync __P((void)); +extern int send_reset __P((ip_t *, qif_t *)); +extern int ipfr_fastroute __P((qif_t *, ip_t *, mblk_t *, mblk_t **, + fr_info_t *, frdest_t *)); +extern void copyin_mblk __P((mblk_t *, int, int, char *)); +extern void copyout_mblk __P((mblk_t *, int, int, char *)); +extern int fr_qin __P((queue_t *, mblk_t *)); +extern int fr_qout __P((queue_t *, mblk_t *)); +# ifdef IPFILTER_LOG +extern int iplread __P((dev_t, struct uio *, cred_t *)); +# endif +# else /* SOLARIS */ +extern int fr_check __P((ip_t *, int, void *, int, mb_t **)); +extern int (*fr_checkp) __P((ip_t *, int, void *, int, mb_t **)); +# ifdef linux +extern int send_reset __P((tcpiphdr_t *, struct ifnet *)); +# else +extern int send_reset __P((tcpiphdr_t *)); +# endif +extern void ipfr_fastroute __P((mb_t *, fr_info_t *, frdest_t *)); +extern size_t mbufchainlen __P((mb_t *)); +# ifdef __sgi +# include <sys/cred.h> +extern int iplioctl __P((dev_t, int, caddr_t, int, cred_t *, int *)); +extern int iplopen __P((dev_t *, int, int, cred_t *)); +extern int iplclose __P((dev_t, int, int, cred_t *)); +extern int iplread __P((dev_t, struct uio *, cred_t *)); +extern int ipfsync __P((void)); +extern int ipfilter_sgi_attach __P((void)); +extern void ipfilter_sgi_detach __P((void)); +extern void ipfilter_sgi_intfsync __P((void)); +# else +# ifdef IPFILTER_LKM +extern int iplidentify __P((char *)); +# endif +# if (_BSDI_VERSION >= 199510) || (__FreeBSD_version >= 220000) || \ + (NetBSD >= 199511) || defined(__OpenBSD__) +# if defined(__NetBSD__) || (_BSDI_VERSION >= 199701) || defined(__OpenBSD__) +extern int iplioctl __P((dev_t, u_long, caddr_t, int, struct proc *)); +# else +extern int iplioctl __P((dev_t, int, caddr_t, int, struct proc *)); +# endif +extern int iplopen __P((dev_t, int, int, struct proc *)); +extern int iplclose __P((dev_t, int, int, struct proc *)); +# else +# if defined(__OpenBSD__) +extern int iplioctl __P((dev_t, u_long, caddr_t, int)); +# else /* __OpenBSD__ */ +# ifndef linux +extern int iplioctl __P((dev_t, int, caddr_t, int)); +# else +extern int iplioctl(struct inode *, struct file *, u_int, u_long); +# endif +# endif /* __OpenBSD__ */ +# ifndef linux +extern int iplopen __P((dev_t, int)); +extern int iplclose __P((dev_t, int)); +# else +extern int iplopen __P((struct inode *, struct file *)); +extern void iplclose __P((struct inode *, struct file *)); +# endif /* !linux */ +# endif /* (_BSDI_VERSION >= 199510) */ +# if BSD >= 199306 +extern int iplread __P((dev_t, struct uio *, int)); +# else +# ifndef linux +extern int iplread __P((dev_t, struct uio *)); +# else +extern int iplread(struct inode *, struct file *, char *, int); +# endif /* !linux */ +# endif /* BSD >= 199306 */ +# endif /* __ sgi */ +# endif /* SOLARIS */ +#endif /* #ifndef _KERNEL */ + +/* + * Post NetBSD 1.2 has the PFIL interface for packet filters. This turns + * on those hooks. We don't need any special mods in non-IP Filter code + * with this! + */ +#if (defined(NetBSD) && (NetBSD > 199609) && (NetBSD <= 1991011)) || \ + (defined(NetBSD1_2) && NetBSD1_2 > 1) +# define NETBSD_PF +#endif + +extern int ipldetach __P((void)); +extern u_short fr_tcpsum __P((mb_t *, ip_t *, tcphdr_t *, int)); +#define FR_SCANLIST(p, ip, fi, m) fr_scanlist(p, ip, fi, m) +extern int fr_scanlist __P((int, ip_t *, fr_info_t *, void *)); +extern u_short ipf_cksum __P((u_short *, int)); +extern int fr_copytolog __P((int, char *, int)); +extern void frflush __P((int, int *)); +extern frgroup_t *fr_addgroup __P((u_short, frentry_t *, int, int)); +extern frgroup_t *fr_findgroup __P((u_short, u_32_t, int, int, frgroup_t ***)); +extern void fr_delgroup __P((u_short, u_32_t, int, int)); +extern int ipl_unreach; +extern int ipl_inited; +extern u_long ipl_frouteok[2]; +extern int fr_pass; +extern int fr_flags; +extern int fr_active; +extern fr_info_t frcache[2]; +#ifdef IPFILTER_LOG +extern iplog_t **iplh[IPL_LOGMAX+1], *iplt[IPL_LOGMAX+1]; +extern int iplused[IPL_LOGMAX + 1]; +#endif +extern struct frentry *ipfilter[2][2], *ipacct[2][2]; +extern struct frgroup *ipfgroups[3][2]; +extern struct filterstats frstats[]; #endif /* __IP_FIL_H__ */ diff --git a/sys/netinet/ip_fil_compat.h b/sys/netinet/ip_fil_compat.h index 92f5189541d..8c2af6adb33 100644 --- a/sys/netinet/ip_fil_compat.h +++ b/sys/netinet/ip_fil_compat.h @@ -1,30 +1,130 @@ -/* $OpenBSD: ip_fil_compat.h,v 1.6 1997/06/23 19:03:49 kstailey Exp $ */ /* - * (C)opyright 1993, 1994, 1995 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. * * @(#)ip_compat.h 1.8 1/14/96 - * $DRId: ip_compat.h,v 2.0.1.4 1997/02/04 14:24:25 darrenr Exp $ + * $Id: ip_fil_compat.h,v 1.7 1998/01/26 04:10:40 dgregor Exp $ */ -#ifndef __IP_FIL_COMPAT_H_ -#define __IP_FIL_COMPAT_H__ +#ifndef __IP_COMPAT_H__ +#define __IP_COMPAT_H__ + +#ifndef __P +# ifdef __STDC__ +# define __P(x) x +# else +# define __P(x) () +# define const +# endif +#endif #ifndef SOLARIS #define SOLARIS (defined(sun) && (defined(__svr4__) || defined(__SVR4))) #endif -#if SOLARIS -#define MTYPE(m) ((m)->b_datap->db_type) + +#if defined(_KERNEL) && !defined(KERNEL) +# define KERNEL +#endif +#if defined(KERNEL) && !defined(_KERNEL) +# define _KERNEL +#endif +#if!defined(__KERNEL__) && defined(KERNEL) +# define __KERNEL__ +#endif + +#if defined(__SVR4) || defined(__svr4__) || defined(__sgi) +#define index strchr +# if !defined(_KERNEL) +# define bzero(a,b) memset(a,0,b) +# define bcmp memcmp +# define bcopy(a,b,c) memmove(b,a,c) +# endif +#endif + +#if defined(__sgi) || defined(bsdi) +struct ether_addr { + u_char ether_addr_octet[6]; +}; +#endif + +#if defined(__sgi) && !defined(IPFILTER_LKM) +# ifdef __STDC__ +# define IPL_EXTERN(ep) ipfilter##ep +# else +# define IPL_EXTERN(ep) ipfilter/**/ep +# endif +#else +# ifdef __STDC__ +# define IPL_EXTERN(ep) ipl##ep +# else +# define IPL_EXTERN(ep) ipl/**/ep +# endif +#endif + +#ifdef linux +# include <sys/sysmacros.h> #endif +#if SOLARIS +# define MTYPE(m) ((m)->b_datap->db_type) +# include <sys/ioccom.h> +# include <sys/sysmacros.h> +# include <sys/kmem.h> +/* + * because Solaris 2 defines these in two places :-/ + */ +# undef IPOPT_EOL +# undef IPOPT_NOP +# undef IPOPT_LSRR +# undef IPOPT_RR +# undef IPOPT_SSRR +# ifndef _KERNEL +# define _KERNEL +# undef RES_INIT +# include <inet/common.h> +# include <inet/ip.h> +# include <inet/ip_ire.h> +# undef _KERNEL +# else /* _KERNEL */ +# include <inet/common.h> +# include <inet/ip.h> +# include <inet/ip_ire.h> +# endif /* _KERNEL */ +#endif /* SOLARIS */ #define IPMINLEN(i, h) ((i)->ip_len >= ((i)->ip_hl * 4 + sizeof(struct h))) #ifndef IP_OFFMASK #define IP_OFFMASK 0x1fff #endif +#if BSD > 199306 +# define USE_QUAD_T +# define U_QUAD_T u_quad_t +# define QUAD_T quad_t +#else /* BSD > 199306 */ +# define U_QUAD_T u_long +# define QUAD_T long +#endif /* BSD > 199306 */ + +/* + * These operating systems already take care of the problem for us. + */ +#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__) || \ + defined(__sgi) +typedef u_int32_t u_32_t; +#else +/* + * Really, any arch where sizeof(long) != sizeof(int). + */ +# if defined(__alpha__) || defined(__alpha) +typedef unsigned int u_32_t; +# else +typedef unsigned long u_32_t; +# endif +#endif /* __NetBSD__ || __OpenBSD__ || __FreeBSD__ */ + #ifndef MAX #define MAX(a,b) (((a) > (b)) ? (a) : (b)) #endif @@ -85,146 +185,355 @@ #define IPOPT_EIP 145 /* EIP */ #define IPOPT_FINN 205 /* FINN */ + +#if defined(__FreeBSD__) && defined(KERNEL) +# if __FreeBSD__ < 3 +# include <machine/spl.h> +# endif +# if defined(IPFILTER_LKM) && !defined(ACTUALLY_LKM_NOT_KERNEL) +# define ACTUALLY_LKM_NOT_KERNEL +# endif +#endif /* __FreeBSD__ && KERNEL */ + /* * Build some macros and #defines to enable the same code to compile anywhere * Well, that's the idea, anyway :-) */ -#ifdef _KERNEL +#ifdef KERNEL # if SOLARIS # define MUTEX_ENTER(x) mutex_enter(x) # define MUTEX_EXIT(x) mutex_exit(x) # define MTOD(m,t) (t)((m)->b_rptr) # define IRCOPY(a,b,c) copyin((a), (b), (c)) # define IWCOPY(a,b,c) copyout((a), (b), (c)) -# else -# define MUTEX_ENTER(x) ; -# define MUTEX_EXIT(x) ; +# define FREE_MB_T(m) freemsg(m) +# define SPL_NET(x) ; +# define SPL_IMP(x) ; +# undef SPL_X +# define SPL_X(x) ; +# ifdef sparc +# define ntohs(x) (x) +# define ntohl(x) (x) +# define htons(x) (x) +# define htonl(x) (x) +# endif /* sparc */ +# define KMALLOC(a,b,c) (a) = (b)kmem_alloc((c), KM_NOSLEEP) +# define GET_MINOR(x) getminor(x) +typedef struct qif { + struct qif *qf_next; + ill_t *qf_ill; + kmutex_t qf_lock; + void *qf_iptr; + void *qf_optr; + queue_t *qf_in; + queue_t *qf_out; + struct qinit *qf_wqinfo; + struct qinit *qf_rqinfo; + struct qinit qf_wqinit; + struct qinit qf_rqinit; + mblk_t *qf_m; /* These three fields are for passing data up from */ + queue_t *qf_q; /* fr_qin and fr_qout to the packet processing. */ + int qf_off; + int qf_len; /* this field is used for in ipfr_fastroute */ + char qf_name[8]; + /* + * in case the ILL has disappeared... + */ + int qf_hl; /* header length */ +} qif_t; +extern ill_t *get_unit __P((char *)); +# define GETUNIT(n) get_unit((n)) +# else /* SOLARIS */ +# if defined(__sgi) +# include <sys/ksynch.h> +# define IPF_LOCK_PL plhi +# include <sys/sema.h> +#undef kmutex_t +typedef struct { + lock_t *l; + int pl; +} kmutex_t; +# define MUTEX_ENTER(x) (x)->pl = LOCK((x)->l, IPF_LOCK_PL); +# define MUTEX_EXIT(x) UNLOCK((x)->l, (x)->pl); +# else /* __sgi */ +# define MUTEX_ENTER(x) ; +# define MUTEX_EXIT(x) ; +# endif /* __sgi */ # ifndef linux +# define FREE_MB_T(m) m_freem(m) # define MTOD(m,t) mtod(m,t) # define IRCOPY(a,b,c) bcopy((a), (b), (c)) # define IWCOPY(a,b,c) bcopy((a), (b), (c)) -# endif +# endif /* !linux */ # endif /* SOLARIS */ # ifdef sun -# if defined(__svr4__) || defined(__SVR4) -# define GETUNIT(n) get_unit((n)) -# else +# if !SOLARIS # include <sys/kmem_alloc.h> # define GETUNIT(n) ifunit((n), IFNAMSIZ) # endif # else -# define GETUNIT(n) ifunit((n)) +# ifndef linux +# define GETUNIT(n) ifunit((n)) +# endif # endif /* sun */ -# if defined(sun) && !defined(linux) -# define UIOMOVE(a,b,c,d) uiomove(a,b,c,d) +# if defined(sun) && !defined(linux) || defined(__sgi) +# define UIOMOVE(a,b,c,d) uiomove((caddr_t)a,b,c,d) # define SLEEP(id, n) sleep((id), PZERO+1) +# define WAKEUP(id) wakeup(id) # define KFREE(x) kmem_free((char *)(x), sizeof(*(x))) -# if SOLARIS -typedef struct qif { - struct qif *qf_next; - ill_t *qf_ill; - kmutex_t qf_lock; - void *qf_iptr; - void *qf_optr; - queue_t *qf_in; - queue_t *qf_out; - void *qf_wqinfo; - void *qf_rqinfo; - int (*qf_inp)(); - int (*qf_outp)(); - mblk_t *qf_m; - int qf_len; - char qf_name[8]; - /* - * in case the ILL has disappeared... - */ - int qf_hl; /* header length */ -} qif_t; -# define SPLNET(x) ; -# undef SPLX -# define SPLX(x) ; -# ifdef sparc -# define ntohs(x) (x) -# define ntohl(x) (x) -# define htons(x) (x) -# define htonl(x) (x) -# endif -# define KMALLOC(x) kmem_alloc((x), KM_NOSLEEP) +# define KFREES(x,s) kmem_free((char *)(x), (s)) +# if !SOLARIS +extern void m_copydata __P((struct mbuf *, int, int, caddr_t)); +extern void m_copyback __P((struct mbuf *, int, int, caddr_t)); +# endif +# ifdef __sgi +# include <sys/kmem.h> +# include <sys/ddi.h> +# define KMALLOC(a,b,c) (a) = (b)kmem_alloc((c), KM_NOSLEEP) # define GET_MINOR(x) getminor(x) # else -# define KMALLOC(x) new_kmem_alloc((x), KMEM_NOSLEEP) -# endif /* __svr4__ */ +# if !SOLARIS +# define KMALLOC(a,b,c) (a) = (b)new_kmem_alloc((c), KMEM_NOSLEEP) +# endif /* SOLARIS */ +# endif /* __sgi */ # endif /* sun && !linux */ # ifndef GET_MINOR # define GET_MINOR(x) minor(x) # endif -# if BSD >= 199306 || defined(__FreeBSD__) +# if (BSD >= 199306) || defined(__FreeBSD__) # include <vm/vm.h> -# if !defined(__FreeBSD__) +# if !defined(__FreeBSD__) || (defined (__FreeBSD__) && __FreeBSD__>=3) # include <vm/vm_extern.h> # include <sys/proc.h> extern vm_map_t kmem_map; -# else +# else /* !__FreeBSD__ || (__FreeBSD__ && __FreeBSD__>=3) */ # include <vm/vm_kern.h> -# endif /* __FreeBSD__ */ -/* -** # define KMALLOC(x) kmem_alloc(kmem_map, (x)) -** # define KFREE(x) kmem_free(kmem_map, (vm_offset_t)(x), \ - sizeof(*(x))) -*/ +# endif /* !__FreeBSD__ || (__FreeBSD__ && __FreeBSD__>=3) */ # ifdef M_PFIL -# define KMALLOC(x) malloc((x), M_PFIL, M_NOWAIT) +# define KMALLOC(a, b, c) MALLOC((a), b, (c), M_PFIL, M_NOWAIT) # define KFREE(x) FREE((x), M_PFIL) +# define KFREES(x,s) FREE((x), M_PFIL) # else -# define KMALLOC(x) malloc((x), M_TEMP, M_NOWAIT) +# define KMALLOC(a, b, c) MALLOC((a), b, (c), M_TEMP, M_NOWAIT) # define KFREE(x) FREE((x), M_TEMP) -# endif +# define KFREES(x,s) FREE((x), M_TEMP) +# endif /* M_PFIL */ # define UIOMOVE(a,b,c,d) uiomove(a,b,d) # define SLEEP(id, n) tsleep((id), PPAUSE|PCATCH, n, 0) +# define WAKEUP(id) wakeup(id) # endif /* BSD */ -# if defined(NetBSD1_0) && (NetBSD1_0 > 1) -# define SPLNET(x) x = splsoftnet() +# if defined(NetBSD) && NetBSD <= 1991011 && NetBSD >= 199407 +# define SPL_NET(x) x = splsoftnet() +# define SPL_X(x) (void) splx(x) # else -# if !SOLARIS -# define SPLNET(x) x = splnet() -# define SPLX(x) (void) splx(x) +# if !SOLARIS && !defined(linux) +# define SPL_IMP(x) x = splimp() +# define SPL_NET(x) x = splnet() +# define SPL_X(x) (void) splx(x) # endif -# endif +# endif /* NetBSD && NetBSD <= 1991011 && NetBSD >= 199407 */ +# define PANIC(x,y) if (x) panic y +#else /* KERNEL */ +# define SLEEP(x,y) ; +# define WAKEUP(x) ; +# define PANIC(x,y) ; +# define MUTEX_ENTER(x) ; +# define MUTEX_EXIT(x) ; +# define SPL_NET(x) ; +# define SPL_IMP(x) ; +# undef SPL_X +# define SPL_X(x) ; +# define KMALLOC(a,b,c) (a) = (b)malloc(c) +# define KFREE(x) free(x) +# define KFREES(x,s) free(x) +# define GETUNIT(x) get_unit(x) +# define IRCOPY(a,b,c) bcopy((a), (b), (c)) +# define IWCOPY(a,b,c) bcopy((a), (b), (c)) +#endif /* KERNEL */ + +#if SOLARIS +typedef mblk_t mb_t; #else -# ifndef linux -# define MUTEX_ENTER(x) ; -# define MUTEX_EXIT(x) ; -# define SPLNET(x) ; -# define SPLX(x) ; -# define KMALLOC(x) malloc(x) -# define KFREE(x) free(x) -# define GETUNIT(x) (x) -# define IRCOPY(a,b,c) bcopy((a), (b), (c)) -# define IWCOPY(a,b,c) bcopy((a), (b), (c)) +# ifdef linux +typedef struct sk_buff mb_t; +# else +typedef struct mbuf mb_t; # endif -#endif /* KERNEL */ +#endif /* SOLARIS */ -#ifdef linux +#if defined(linux) || defined(__sgi) +/* + * These #ifdef's are here mainly for linux, but who knows, they may + * not be in other places or maybe one day linux will grow up and some + * of these will turn up there too. + */ +#ifndef ICMP_UNREACH # define ICMP_UNREACH ICMP_DEST_UNREACH +#endif +#ifndef ICMP_SOURCEQUENCH # define ICMP_SOURCEQUENCH ICMP_SOURCE_QUENCH +#endif +#ifndef ICMP_TIMXCEED # define ICMP_TIMXCEED ICMP_TIME_EXCEEDED +#endif +#ifndef ICMP_PARAMPROB # define ICMP_PARAMPROB ICMP_PARAMETERPROB - +#endif +#ifndef ICMP_TSTAMP +# define ICMP_TSTAMP ICMP_TIMESTAMP +#endif +#ifndef ICMP_TSTAMPREPLY +# define ICMP_TSTAMPREPLY ICMP_TIMESTAMPREPLY +#endif +#ifndef ICMP_IREQ +# define ICMP_IREQ ICMP_INFO_REQUEST +#endif +#ifndef ICMP_IREQREPLY +# define ICMP_IREQREPLY ICMP_INFO_REPLY +#endif +#ifndef ICMP_MASKREQ +# define ICMP_MASKREQ ICMP_ADDRESS +#endif +#ifndef ICMP_MASKREPLY +# define ICMP_MASKREPLY ICMP_ADDRESSREPLY +#endif +#ifndef IPVERSION +# define IPVERSION 4 +#endif +#ifndef IPOPT_MINOFF +# define IPOPT_MINOFF 4 +#endif +#ifndef IPOPT_COPIED +# define IPOPT_COPIED(x) ((x)&0x80) +#endif +#ifndef IPOPT_EOL +# define IPOPT_EOL 0 +#endif +#ifndef IPOPT_NOP +# define IPOPT_NOP 1 +#endif +#ifndef IP_MF +# define IP_MF ((u_short)0x2000) +#endif +#ifndef ETHERTYPE_IP +# define ETHERTYPE_IP ((u_short)0x0800) +#endif +#ifndef TH_FIN # define TH_FIN 0x01 +#endif +#ifndef TH_SYN # define TH_SYN 0x02 +#endif +#ifndef TH_RST # define TH_RST 0x04 +#endif +#ifndef TH_PUSH # define TH_PUSH 0x08 +#endif +#ifndef TH_ACK # define TH_ACK 0x10 +#endif +#ifndef TH_URG # define TH_URG 0x20 +#endif +#ifndef IPOPT_EOL +# define IPOPT_EOL 0 +#endif +#ifndef IPOPT_NOP +# define IPOPT_NOP 1 +#endif +#ifndef IPOPT_RR +# define IPOPT_RR 7 +#endif +#ifndef IPOPT_TS +# define IPOPT_TS 68 +#endif +#ifndef IPOPT_SECURITY +# define IPOPT_SECURITY 130 +#endif +#ifndef IPOPT_LSRR +# define IPOPT_LSRR 131 +#endif +#ifndef IPOPT_SATID +# define IPOPT_SATID 136 +#endif +#ifndef IPOPT_SSRR +# define IPOPT_SSRR 137 +#endif +#ifndef IPOPT_SECUR_UNCLASS +# define IPOPT_SECUR_UNCLASS ((u_short)0x0000) +#endif +#ifndef IPOPT_SECUR_CONFID +# define IPOPT_SECUR_CONFID ((u_short)0xf135) +#endif +#ifndef IPOPT_SECUR_EFTO +# define IPOPT_SECUR_EFTO ((u_short)0x789a) +#endif +#ifndef IPOPT_SECUR_MMMM +# define IPOPT_SECUR_MMMM ((u_short)0xbc4d) +#endif +#ifndef IPOPT_SECUR_RESTR +# define IPOPT_SECUR_RESTR ((u_short)0xaf13) +#endif +#ifndef IPOPT_SECUR_SECRET +# define IPOPT_SECUR_SECRET ((u_short)0xd788) +#endif +#ifndef IPOPT_SECUR_TOPSECRET +# define IPOPT_SECUR_TOPSECRET ((u_short)0x6bc5) +#endif +#ifndef IPOPT_OLEN +# define IPOPT_OLEN 1 +#endif +#endif /* linux || __sgi */ + +#ifdef linux +/* + * TCP States + */ +#define TCPS_CLOSED 0 /* closed */ +#define TCPS_LISTEN 1 /* listening for connection */ +#define TCPS_SYN_SENT 2 /* active, have sent syn */ +#define TCPS_SYN_RECEIVED 3 /* have send and received syn */ +/* states < TCPS_ESTABLISHED are those where connections not established */ +#define TCPS_ESTABLISHED 4 /* established */ +#define TCPS_CLOSE_WAIT 5 /* rcvd fin, waiting for close */ +/* states > TCPS_CLOSE_WAIT are those where user has closed */ +#define TCPS_FIN_WAIT_1 6 /* have closed, sent fin */ +#define TCPS_CLOSING 7 /* closed xchd FIN; await FIN ACK */ +#define TCPS_LAST_ACK 8 /* had fin and close; await FIN ACK */ +/* states > TCPS_CLOSE_WAIT && < TCPS_FIN_WAIT_2 await ACK of FIN */ +#define TCPS_FIN_WAIT_2 9 /* have closed, fin is acked */ +#define TCPS_TIME_WAIT 10 /* in 2*msl quiet wait after close */ + +/* + * file flags. + */ +#define FWRITE WRITE +#define FREAD READ +/* + * mbuf related problems. + */ +#define mtod(m,t) (t)((m)->data) +#define m_len len +#define m_next next + +#define IP_DF 0x8000 typedef struct { __u16 th_sport; __u16 th_dport; __u32 th_seq; __u32 th_ack; - __u8 th_x; +# if defined(__i386__) || defined(__MIPSEL__) || defined(__alpha__) ||\ + defined(vax) + __u8 th_res:4; + __u8 th_off:4; +#else + __u8 th_off:4; + __u8 th_res:4; +#endif __u8 th_flags; __u16 th_win; __u16 th_sum; @@ -235,7 +544,7 @@ typedef struct { __u16 uh_sport; __u16 uh_dport; __u16 uh_ulen; - __u16 uh_sun; + __u16 uh_sum; } udphdr_t; typedef struct { @@ -261,7 +570,7 @@ typedef struct { /* * Structure of an icmp header. */ -struct icmp { +typedef struct icmp { u_char icmp_type; /* type of message, see below */ u_char icmp_code; /* type sub code */ u_short icmp_cksum; /* ones complement cksum of struct */ @@ -298,8 +607,10 @@ struct icmp { # define icmp_ip icmp_dun.id_ip.idi_ip # define icmp_mask icmp_dun.id_mask # define icmp_data icmp_dun.id_data -}; +} icmphdr_t; +# ifndef LINUX_IPOVLY +# define LINUX_IPOVLY struct ipovly { caddr_t ih_next, ih_prev; /* for protocol sequence q's */ u_char ih_x1; /* (unused) */ @@ -308,35 +619,99 @@ struct ipovly { struct in_addr ih_src; /* source internet address */ struct in_addr ih_dst; /* destination internet address */ }; +# endif -# define SPLX(x) (void) -# define SPLNET(x) (void) +typedef struct { + __u8 ether_dhost[6]; + __u8 ether_shost[6]; + __u16 ether_type; +} ether_header_t; -# define bcopy(a,b,c) memmove(b,a,c) -# define bcmp(a,b,c) memcmp(a,b,c) +typedef struct uio { + int uio_resid; + int uio_rw; + caddr_t uio_buf; +} uio_t; -# define UNITNAME(n) dev_get((n)) -# define ifnet device +# define UIO_READ 0 +# define UIO_WRITE 1 +# define UIOMOVE(a, b, c, d) uiomove(a,b,c,d) + +/* + * For masking struct ifnet onto struct device + */ +# define if_name name + +# ifdef KERNEL +# define GETUNIT(x) dev_get(x) +# define FREE_MB_T(m) kfree_skb(m, FREE_WRITE) +# define uniqtime do_gettimeofday +# undef INT_MAX +# undef UINT_MAX +# undef LONG_MAX +# undef ULONG_MAX +# include <linux/netdevice.h> +# define SPL_X(x) +# define SPL_NET(x) +# define SPL_IMP(x) + +# define bcmp(a,b,c) memcmp(a,b,c) +# define bcopy(a,b,c) memcpy(b,a,c) +# define bzero(a,c) memset(a,0,c) -# define KMALLOC(x) kmalloc((x), GFP_ATOMIC) -# define KFREE(x) kfree_s((x), sizeof(*(x))) -# define IRCOPY(a,b,c) { \ - error = verify_area(VERIFY_READ, \ - (b) ,sizeof((b))); \ +# define UNITNAME(n) dev_get((n)) + +# define KMALLOC(a,b,c) (a) = (b)kmalloc((c), GFP_ATOMIC) +# define KFREE(x) kfree_s((x), sizeof(*(x))) +# define KFREES(x,s) kfree_s((x), (s)) +# define IRCOPY(a,b,c) { \ + error = verify_area(VERIFY_READ, (a) ,(c)); \ if (!error) \ memcpy_fromfs((b), (a), (c)); \ } -# define IWCOPY(a,b,c) { \ - error = verify_area(VERIFY_WRITE, \ - (b) ,sizeof((b))); \ +# define IWCOPY(a,b,c) { \ + error = verify_area(VERIFY_WRITE, (b), (c)); \ if (!error) \ memcpy_tofs((b), (a), (c)); \ } +# else +# define __KERNEL__ +# undef INT_MAX +# undef UINT_MAX +# undef LONG_MAX +# undef ULONG_MAX +# include <linux/netdevice.h> +# undef __KERNEL__ +# endif +# define ifnet device #else typedef struct tcphdr tcphdr_t; typedef struct udphdr udphdr_t; typedef struct icmp icmphdr_t; typedef struct ip ip_t; +typedef struct ether_header ether_header_t; #endif /* linux */ +typedef struct tcpiphdr tcpiphdr_t; -#endif /* __IP__FIL_COMPAT_H__ */ +#if defined(hpux) || defined(linux) +struct ether_addr { + char ether_addr_octet[6]; +}; +#endif + +/* + * XXX - This is one of those *awful* hacks which nobody likes + */ +#ifdef ultrix +#define A_A +#else +#define A_A & +#endif + +#ifndef ICMP_ROUTERADVERT +# define ICMP_ROUTERADVERT 9 +#endif +#ifndef ICMP_ROUTERSOLICIT +# define ICMP_ROUTERSOLICIT 10 +#endif +#endif /* __IP_COMPAT_H__ */ diff --git a/sys/netinet/ip_frag.c b/sys/netinet/ip_frag.c index accda5e5f5d..00c0ebe7879 100644 --- a/sys/netinet/ip_frag.c +++ b/sys/netinet/ip_frag.c @@ -1,16 +1,13 @@ -/* $OpenBSD: ip_frag.c,v 1.7 1997/02/11 22:23:20 kstailey Exp $ */ /* - * (C)opyright 1993,1994,1995 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[] = "@(#)ip_frag.c 1.11 3/24/96 (C) 1993-1995 Darren Reed"; -static char rcsid[] = "Id: ip_frag.c,v 2.0.1.1 1997/01/09 15:14:43 darrenr Exp"; -#endif +#if !defined(lint) +static const char sccsid[] = "@(#)ip_frag.c 1.11 3/24/96 (C) 1993-1995 Darren Reed"; +static const char rcsid[] = "@(#)$Id: ip_frag.c,v 1.8 1998/01/26 04:10:41 dgregor Exp $"; #endif #if !defined(_KERNEL) && !defined(KERNEL) @@ -20,16 +17,26 @@ static char rcsid[] = "Id: ip_frag.c,v 2.0.1.1 1997/01/09 15:14:43 darrenr Exp"; #include <sys/errno.h> #include <sys/types.h> #include <sys/param.h> +#include <sys/time.h> #include <sys/file.h> +#if defined(KERNEL) && (__FreeBSD_version >= 220000) +#include <sys/filio.h> +#include <sys/fcntl.h> +#else #include <sys/ioctl.h> +#endif #include <sys/uio.h> +#ifndef linux #include <sys/protosw.h> +#endif #include <sys/socket.h> -#ifdef _KERNEL +#if defined(_KERNEL) && !defined(linux) # include <sys/systm.h> #endif #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> @@ -45,43 +52,44 @@ static char rcsid[] = "Id: ip_frag.c,v 2.0.1.1 1997/01/09 15:14:43 darrenr Exp"; #include <netinet/in.h> #include <netinet/in_systm.h> #include <netinet/ip.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_frag.h" +#include "ip_proxy.h" #include "ip_nat.h" +#include "ip_frag.h" #include "ip_state.h" +#include "ip_auth.h" ipfr_t *ipfr_heads[IPFT_SIZE]; +ipfr_t *ipfr_nattab[IPFT_SIZE]; ipfrstat_t ipfr_stats; -u_long ipfr_inuse = 0; -u_long fr_ipfrttl = 120; /* 60 seconds */ +int ipfr_inuse = 0, + fr_ipfrttl = 120; /* 60 seconds */ #ifdef _KERNEL extern int ipfr_timer_id; #endif -#if SOLARIS -# ifdef _KERNEL +#if (SOLARIS || defined(__sgi)) && defined(_KERNEL) extern kmutex_t ipf_frag; -# else -#define bcmp(a,b,c) memcmp(a,b,c) -#define bcopy(a,b,c) memmove(b,a,c) -# endif +extern kmutex_t ipf_natfrag; +extern kmutex_t ipf_nat; #endif -# if BSD < 199306 -int ipfr_slowtimer __P((void)); -#else -void ipfr_slowtimer __P((void)); -#endif -ipfrstat_t * -ipfr_fragstats() +static ipfr_t *ipfr_new __P((ip_t *, fr_info_t *, int, ipfr_t **)); +static ipfr_t *ipfr_lookup __P((ip_t *, fr_info_t *, ipfr_t **)); + + +ipfrstat_t *ipfr_fragstats() { ipfr_stats.ifs_table = ipfr_heads; + ipfr_stats.ifs_nattab = ipfr_nattab; ipfr_stats.ifs_inuse = ipfr_inuse; return &ipfr_stats; } @@ -91,11 +99,11 @@ ipfr_fragstats() * add a new entry to the fragment cache, registering it as having come * through this box, with the result of the filter operation. */ -int -ipfr_newfrag(ip, fin, pass) - ip_t *ip; - fr_info_t *fin; - int pass; +static ipfr_t *ipfr_new(ip, fin, pass, table) +ip_t *ip; +fr_info_t *fin; +int pass; +ipfr_t *table[]; { ipfr_t **fp, *fr, frag; u_int idx; @@ -115,33 +123,75 @@ ipfr_newfrag(ip, fin, pass) /* * first, make sure it isn't already there... */ - MUTEX_ENTER(&ipf_frag); - for (fp = &ipfr_heads[idx]; (fr = *fp); fp = &fr->ipfr_next) + for (fp = &table[idx]; (fr = *fp); fp = &fr->ipfr_next) if (!bcmp((char *)&frag.ipfr_src, (char *)&fr->ipfr_src, IPFR_CMPSZ)) { ipfr_stats.ifs_exists++; - MUTEX_EXIT(&ipf_frag); - return -1; + return NULL; } - if (!(fr = (ipfr_t *)KMALLOC(sizeof(*fr)))) { + /* + * allocate some memory, if possible, if not, just record that we + * failed to do so. + */ + KMALLOC(fr, ipfr_t *, sizeof(*fr)); + if (fr == NULL) { ipfr_stats.ifs_nomem++; - MUTEX_EXIT(&ipf_frag); - return -1; + return NULL; } - if ((fr->ipfr_next = ipfr_heads[idx])) - ipfr_heads[idx]->ipfr_prev = fr; + + /* + * Instert the fragment into the fragment table, copy the struct used + * in the search using bcopy rather than reassign each field. + * Set the ttl to the default and mask out logging from "pass" + */ + if ((fr->ipfr_next = table[idx])) + table[idx]->ipfr_prev = fr; fr->ipfr_prev = NULL; - ipfr_heads[idx] = fr; + fr->ipfr_data = NULL; + table[idx] = fr; bcopy((char *)&frag.ipfr_src, (char *)&fr->ipfr_src, IPFR_CMPSZ); fr->ipfr_ttl = fr_ipfrttl; fr->ipfr_pass = pass & ~(FR_LOGFIRST|FR_LOG); + /* + * Compute the offset of the expected start of the next packet. + */ fr->ipfr_off = (ip->ip_off & 0x1fff) + (fin->fin_dlen >> 3); - *fp = fr; ipfr_stats.ifs_new++; ipfr_inuse++; + return fr; +} + + +int ipfr_newfrag(ip, fin, pass) +ip_t *ip; +fr_info_t *fin; +int pass; +{ + ipfr_t *ipf; + + MUTEX_ENTER(&ipf_frag); + ipf = ipfr_new(ip, fin, pass, ipfr_heads); MUTEX_EXIT(&ipf_frag); - return 0; + return ipf ? 0 : -1; +} + + +int ipfr_nat_newfrag(ip, fin, pass, nat) +ip_t *ip; +fr_info_t *fin; +int pass; +nat_t *nat; +{ + ipfr_t *ipf; + + MUTEX_ENTER(&ipf_natfrag); + if ((ipf = ipfr_new(ip, fin, pass, ipfr_nattab))) { + ipf->ipfr_data = nat; + nat->nat_data = ipf; + } + MUTEX_EXIT(&ipf_natfrag); + return ipf ? 0 : -1; } @@ -149,18 +199,19 @@ ipfr_newfrag(ip, fin, pass) * check the fragment cache to see if there is already a record of this packet * with its filter result known. */ -int -ipfr_knownfrag(ip, fin) - ip_t *ip; - fr_info_t *fin; +static ipfr_t *ipfr_lookup(ip, fin, table) +ip_t *ip; +fr_info_t *fin; +ipfr_t *table[]; { ipfr_t *f, frag; u_int idx; - int ret; /* * For fragments, we record protocol, packet id, TOS and both IP#'s * (these should all be the same for all fragments of a packet). + * + * build up a hash value to index the table with. */ frag.ipfr_p = ip->ip_p; idx = ip->ip_p; @@ -174,67 +225,140 @@ ipfr_knownfrag(ip, fin) idx *= 127; idx %= IPFT_SIZE; - MUTEX_ENTER(&ipf_frag); - for (f = ipfr_heads[idx]; f; f = f->ipfr_next) + /* + * check the table, careful to only compare the right amount of data + */ + for (f = table[idx]; f; f = f->ipfr_next) if (!bcmp((char *)&frag.ipfr_src, (char *)&f->ipfr_src, IPFR_CMPSZ)) { u_short atoff, off; - if (f != ipfr_heads[idx]) { + if (f != table[idx]) { /* * move fragment info. to the top of the list * to speed up searches. */ if ((f->ipfr_prev->ipfr_next = f->ipfr_next)) f->ipfr_next->ipfr_prev = f->ipfr_prev; - f->ipfr_next = ipfr_heads[idx]; - ipfr_heads[idx]->ipfr_prev = f; + f->ipfr_next = table[idx]; + table[idx]->ipfr_prev = f; f->ipfr_prev = NULL; - ipfr_heads[idx] = f; + table[idx] = f; } - ret = f->ipfr_pass; off = ip->ip_off; - atoff = (off & 0x1fff) - (fin->fin_dlen >> 3); + atoff = off + (fin->fin_dlen >> 3); /* * If we've follwed the fragments, and this is the * last (in order), shrink expiration time. */ - if (atoff == f->ipfr_off) { + if ((off & 0x1fff) == f->ipfr_off) { if (!(off & IP_MF)) f->ipfr_ttl = 1; else - f->ipfr_off = off; + f->ipfr_off = atoff; } ipfr_stats.ifs_hits++; - MUTEX_EXIT(&ipf_frag); - return ret; + return f; } + return NULL; +} + + +/* + * functional interface for NAT lookups of the NAT fragment cache + */ +nat_t *ipfr_nat_knownfrag(ip, fin) +ip_t *ip; +fr_info_t *fin; +{ + nat_t *nat; + ipfr_t *ipf; + + MUTEX_ENTER(&ipf_natfrag); + ipf = ipfr_lookup(ip, fin, ipfr_nattab); + if (ipf) { + nat = ipf->ipfr_data; + /* + * This is the last fragment for this packet. + */ + if (ipf->ipfr_ttl == 1) { + nat->nat_data = NULL; + ipf->ipfr_data = NULL; + } + } else + nat = NULL; + MUTEX_EXIT(&ipf_natfrag); + return nat; +} + + +/* + * functional interface for normal lookups of the fragment cache + */ +int ipfr_knownfrag(ip, fin) +ip_t *ip; +fr_info_t *fin; +{ + int ret; + ipfr_t *ipf; + + MUTEX_ENTER(&ipf_frag); + ipf = ipfr_lookup(ip, fin, ipfr_heads); + ret = ipf ? ipf->ipfr_pass : 0; MUTEX_EXIT(&ipf_frag); - return 0; + return ret; +} + + +/* + * forget any references to this external object. + */ +void ipfr_forget(nat) +void *nat; +{ + ipfr_t *fr; + int idx; + + MUTEX_ENTER(&ipf_natfrag); + for (idx = IPFT_SIZE - 1; idx >= 0; idx--) + for (fr = ipfr_heads[idx]; fr; fr = fr->ipfr_next) + if (fr->ipfr_data == nat) + fr->ipfr_data = NULL; + + MUTEX_EXIT(&ipf_natfrag); } /* * Free memory in use by fragment state info. kept. */ -void -ipfr_unload() +void ipfr_unload() { ipfr_t **fp, *fr; + nat_t *nat; int idx; -#if !SOLARIS && defined(_KERNEL) - int s; -#endif MUTEX_ENTER(&ipf_frag); - SPLNET(s); for (idx = IPFT_SIZE - 1; idx >= 0; idx--) for (fp = &ipfr_heads[idx]; (fr = *fp); ) { *fp = fr->ipfr_next; KFREE(fr); } - SPLX(s); MUTEX_EXIT(&ipf_frag); + + MUTEX_ENTER(&ipf_nat); + MUTEX_ENTER(&ipf_natfrag); + for (idx = IPFT_SIZE - 1; idx >= 0; idx--) + for (fp = &ipfr_nattab[idx]; (fr = *fp); ) { + *fp = fr->ipfr_next; + if ((nat = (nat_t *)fr->ipfr_data)) { + if (nat->nat_data == fr) + nat->nat_data = NULL; + } + KFREE(fr); + } + MUTEX_EXIT(&ipf_natfrag); + MUTEX_EXIT(&ipf_nat); } @@ -243,19 +367,28 @@ ipfr_unload() * Slowly expire held state for fragments. Timeouts are set * in expectation * of this being called twice per second. */ -# if BSD < 199306 -int +# if (BSD >= 199306) || SOLARIS || defined(__sgi) +void ipfr_slowtimer() # else -void +int ipfr_slowtimer() # endif -ipfr_slowtimer() { ipfr_t **fp, *fr; + nat_t *nat; int s, idx; +#ifdef __sgi + ipfilter_sgi_intfsync(); +#endif + + SPL_NET(s); MUTEX_ENTER(&ipf_frag); - SPLNET(s); + /* + * Go through the entire table, looking for entries to expire, + * decreasing the ttl by one for each entry. If it reaches 0, + * remove it from the chain and free it. + */ for (idx = IPFT_SIZE - 1; idx >= 0; idx--) for (fp = &ipfr_heads[idx]; (fr = *fp); ) { --fr->ipfr_ttl; @@ -273,17 +406,51 @@ ipfr_slowtimer() } else fp = &fr->ipfr_next; } - SPLX(s); -# if SOLARIS MUTEX_EXIT(&ipf_frag); + + /* + * Same again for the NAT table, except that if the structure also + * still points to a NAT structure, and the NAT structure points back + * at the one to be free'd, NULL the reference from the NAT struct. + * NOTE: We need to grab both mutex's early, and in this order so as + * to prevent a deadlock if both try to expire at the same time. + */ + MUTEX_ENTER(&ipf_nat); + MUTEX_ENTER(&ipf_natfrag); + for (idx = IPFT_SIZE - 1; idx >= 0; idx--) + for (fp = &ipfr_nattab[idx]; (fr = *fp); ) { + --fr->ipfr_ttl; + if (fr->ipfr_ttl == 0) { + if (fr->ipfr_prev) + fr->ipfr_prev->ipfr_next = + fr->ipfr_next; + if (fr->ipfr_next) + fr->ipfr_next->ipfr_prev = + fr->ipfr_prev; + *fp = fr->ipfr_next; + ipfr_stats.ifs_expire++; + ipfr_inuse--; + if ((nat = (nat_t *)fr->ipfr_data)) { + if (nat->nat_data == fr) + nat->nat_data = NULL; + } + KFREE(fr); + } else + fp = &fr->ipfr_next; + } + MUTEX_EXIT(&ipf_natfrag); + MUTEX_EXIT(&ipf_nat); + SPL_X(s); fr_timeoutstate(); ip_natexpire(); - ipfr_timer_id = timeout(ipfr_slowtimer, NULL, HZ/2); + fr_authexpire(); +# if SOLARIS + ipfr_timer_id = timeout(ipfr_slowtimer, NULL, drv_usectohz(500000)); # else - fr_timeoutstate(); - ip_natexpire(); +# ifndef linux ip_slowtimo(); -# if BSD < 199306 +# endif +# if (BSD < 199306) && !defined(__sgi) return 0; # endif # endif diff --git a/sys/netinet/ip_frag.h b/sys/netinet/ip_frag.h index e0b108a8f95..cd22ae5da87 100644 --- a/sys/netinet/ip_frag.h +++ b/sys/netinet/ip_frag.h @@ -1,22 +1,22 @@ -/* $OpenBSD: ip_frag.h,v 1.5 1997/02/11 22:23:22 kstailey Exp $ */ /* - * (C)opyright 1993, 1994, 1995 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. * * @(#)ip_frag.h 1.5 3/24/96 - * Id: ip_frag.h,v 2.0.1.1 1997/01/09 15:14:43 darrenr Exp + * $Id: ip_frag.h,v 1.6 1998/01/26 04:10:41 dgregor Exp $ */ -#ifndef __IP_FRAG_H_ +#ifndef __IP_FRAG_H__ #define __IP_FRAG_H__ #define IPFT_SIZE 257 typedef struct ipfr { struct ipfr *ipfr_next, *ipfr_prev; + void *ipfr_data; struct in_addr ipfr_src; struct in_addr ipfr_dst; u_short ipfr_id; @@ -36,14 +36,23 @@ typedef struct ipfrstat { u_long ifs_expire; u_long ifs_inuse; struct ipfr **ifs_table; + struct ipfr **ifs_nattab; } ipfrstat_t; #define IPFR_CMPSZ (4 + 4 + 2 + 1 + 1) -extern ipfrstat_t *ipfr_fragstats __P((void)); -extern int ipfr_newfrag __P((ip_t *, fr_info_t *, int)); -extern int ipfr_knownfrag __P((ip_t *, fr_info_t *)); -# ifdef _KERNEL -extern void ipfr_unload __P((void)); -# endif +extern int fr_ipfrttl; +extern ipfrstat_t *ipfr_fragstats __P((void)); +extern int ipfr_newfrag __P((ip_t *, fr_info_t *, int)); +extern int ipfr_nat_newfrag __P((ip_t *, fr_info_t *, int, struct nat *)); +extern nat_t *ipfr_nat_knownfrag __P((ip_t *, fr_info_t *)); +extern int ipfr_knownfrag __P((ip_t *, fr_info_t *)); +extern void ipfr_forget __P((void *)); +extern void ipfr_unload __P((void)); + +#if (BSD >= 199306) || SOLARIS || defined(__sgi) +extern void ipfr_slowtimer __P((void)); +#else +extern int ipfr_slowtimer __P((void)); +#endif #endif /* __IP_FIL_H__ */ diff --git a/sys/netinet/ip_ftp_pxy.c b/sys/netinet/ip_ftp_pxy.c new file mode 100644 index 00000000000..5d6ce1fc002 --- /dev/null +++ b/sys/netinet/ip_ftp_pxy.c @@ -0,0 +1,269 @@ +/* + * Simple FTP transparent proxy for in-kernel use. For use with the NAT + * code. + */ + +#define isdigit(x) ((x) >= '0' && (x) <= '9') + +#define IPF_FTP_PROXY + +#define IPF_MINPORTLEN 18 +#define IPF_MAXPORTLEN 30 + + +int ippr_ftp_init __P((fr_info_t *, ip_t *, tcphdr_t *, + ap_session_t *, nat_t *)); +int ippr_ftp_in __P((fr_info_t *, ip_t *, tcphdr_t *, + ap_session_t *, nat_t *)); +int ippr_ftp_out __P((fr_info_t *, ip_t *, tcphdr_t *, + ap_session_t *, nat_t *)); +u_short ipf_ftp_atoi __P((char **)); + + +int ippr_ftp_init __P((fr_info_t *, ip_t *, tcphdr_t *, ap_session_t *, + nat_t *)); +int ippr_ftp_in __P((fr_info_t *, ip_t *, tcphdr_t *, ap_session_t *, + nat_t *)); +int ippr_ftp_out __P((fr_info_t *, ip_t *, tcphdr_t *, ap_session_t *, + nat_t *)); + +u_short ipf_ftp_atoi __P((char **)); + + + +/* + * FTP application proxy initialization. + */ +int ippr_ftp_init(fin, ip, tcp, aps, nat) +fr_info_t *fin; +ip_t *ip; +tcphdr_t *tcp; +ap_session_t *aps; +nat_t *nat; +{ + aps->aps_sport = tcp->th_sport; + aps->aps_dport = tcp->th_dport; + return 0; +} + + +int ippr_ftp_in(fin, ip, tcp, aps, nat) +fr_info_t *fin; +ip_t *ip; +tcphdr_t *tcp; +ap_session_t *aps; +nat_t *nat; +{ + u_long sum1, sum2; + short sel; + + if (tcp->th_sport == aps->aps_dport) { + sum2 = (u_long)ntohl(tcp->th_ack); + sel = aps->aps_sel; + if ((aps->aps_after[!sel] > aps->aps_after[sel]) && + (sum2 > aps->aps_after[!sel])) { + sel = aps->aps_sel = !sel; /* switch to other set */ + } + if (aps->aps_seqoff[sel] && (sum2 > aps->aps_after[sel])) { + sum1 = (u_long)aps->aps_seqoff[sel]; + tcp->th_ack = htonl(sum2 - sum1); + return 2; + } + } + return 0; +} + + +/* + * ipf_ftp_atoi - implement a version of atoi which processes numbers in + * pairs separated by commas (which are expected to be in the range 0 - 255), + * returning a 16 bit number combining either side of the , as the MSB and + * LSB. + */ +u_short ipf_ftp_atoi(ptr) +char **ptr; +{ + register char *s = *ptr, c; + register u_char i = 0, j = 0; + + while ((c = *s++) && isdigit(c)) { + i *= 10; + i += c - '0'; + } + if (c != ',') { + *ptr = NULL; + return 0; + } + while ((c = *s++) && isdigit(c)) { + j *= 10; + j += c - '0'; + } + *ptr = s; + return (i << 8) | j; +} + + +int ippr_ftp_out(fin, ip, tcp, aps, nat) +fr_info_t *fin; +ip_t *ip; +tcphdr_t *tcp; +ap_session_t *aps; +nat_t *nat; +{ + register u_long sum1, sum2; + char newbuf[IPF_MAXPORTLEN+1]; + char portbuf[IPF_MAXPORTLEN+1], *s; + int ch = 0, off = (ip->ip_hl << 2) + (tcp->th_off << 2); + u_int a1, a2, a3, a4; + u_short a5, a6; + int olen, dlen, nlen = 0, inc = 0; + tcphdr_t tcph, *tcp2 = &tcph; + void *savep; + nat_t *ipn; + struct in_addr swip; + mb_t *m = *(mb_t **)fin->fin_mp; + +#if SOLARIS + mb_t *m1; + + /* skip any leading M_PROTOs */ + while(m && (MTYPE(m) != M_DATA)) + m = m->b_cont; + PANIC((!m),("ippr_ftp_out: no M_DATA")); + + dlen = msgdsize(m) - off; + bzero(portbuf, sizeof(portbuf)); + copyout_mblk(m, off, MIN(sizeof(portbuf), dlen), portbuf); +#else + dlen = mbufchainlen(m) - off; + bzero(portbuf, sizeof(portbuf)); + m_copydata(m, off, MIN(sizeof(portbuf), dlen), portbuf); +#endif + portbuf[IPF_MAXPORTLEN] = '\0'; + + if ((dlen < IPF_MINPORTLEN) || strncmp(portbuf, "PORT ", 5)) + goto adjust_seqack; + + /* + * Skip the PORT command + space + */ + s = portbuf + 5; + /* + * Pick out the address components, two at a time. + */ + (void) ipf_ftp_atoi(&s); + if (!s) + goto adjust_seqack; + (void) ipf_ftp_atoi(&s); + if (!s) + goto adjust_seqack; + a5 = ipf_ftp_atoi(&s); + if (!s) + goto adjust_seqack; + /* + * check for CR-LF at the end. + */ + if (*s != '\n' || *(s - 1) != '\r') + goto adjust_seqack; + a6 = a5 & 0xff; + a5 >>= 8; + /* + * Calculate new address parts for PORT command + */ + a1 = ntohl(ip->ip_src.s_addr); + a2 = (a1 >> 16) & 0xff; + a3 = (a1 >> 8) & 0xff; + a4 = a1 & 0xff; + a1 >>= 24; + olen = s - portbuf + 1; + (void) sprintf(newbuf, "PORT %d,%d,%d,%d,%d,%d\r\n", + a1, a2, a3, a4, a5, a6); + nlen = strlen(newbuf); + inc = nlen - olen; +#if SOLARIS + for (m1 = m; m1->b_cont; m1 = m1->b_cont) + ; + if (inc > 0) { + mblk_t *nm; + + /* alloc enough to keep same trailer space for lower driver */ + nm = allocb(nlen + m1->b_datap->db_lim - m1->b_wptr, BPRI_MED); + PANIC((!nm),("ippr_ftp_out: allocb failed")); + + nm->b_band = m1->b_band; + nm->b_wptr += nlen; + + m1->b_wptr -= olen; + PANIC((m1->b_wptr < m1->b_rptr),("ippr_ftp_out: cannot handle fragmented data block")); + + linkb(m1, nm); + } else { + m1->b_wptr += inc; + } + copyin_mblk(m, off, nlen, newbuf); +#else + if (inc < 0) + m_adj(m, inc); + /* the mbuf chain will be extended if necessary by m_copyback() */ + m_copyback(m, off, nlen, newbuf); +#endif + if (inc) { +#if SOLARIS || defined(__sgi) + sum1 = ip->ip_len; + sum2 = ip->ip_len + inc; + + /* Because ~1 == -2, We really need ~1 == -1 */ + if (sum1 > sum2) + sum2--; + sum2 -= sum1; + sum2 = (sum2 & 0xffff) + (sum2 >> 16); + + fix_outcksum(&ip->ip_sum, sum2); +#endif + ip->ip_len += inc; + } + ch = 1; + + /* + * Add skeleton NAT entry for connection which will come back the + * other way. + */ + savep = fin->fin_dp; + fin->fin_dp = (char *)tcp2; + bzero((char *)tcp2, sizeof(*tcp2)); + tcp2->th_sport = htons(a5 << 8 | a6); + tcp2->th_dport = htons(20); + swip = ip->ip_src; + ip->ip_src = nat->nat_inip; + if ((ipn = nat_new(nat->nat_ptr, ip, fin, IPN_TCP, NAT_OUTBOUND))) + ipn->nat_age = fr_defnatage; + (void) fr_addstate(ip, fin, FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE); + ip->ip_src = swip; + fin->fin_dp = (char *)savep; + +adjust_seqack: + if (tcp->th_dport == aps->aps_dport) { + sum2 = (u_long)ntohl(tcp->th_seq); + off = aps->aps_sel; + if ((aps->aps_after[!off] > aps->aps_after[off]) && + (sum2 > aps->aps_after[!off])) { + off = aps->aps_sel = !off; /* switch to other set */ + } + if (aps->aps_seqoff[off]) { + sum1 = (u_long)aps->aps_after[off] - + aps->aps_seqoff[off]; + if (sum2 > sum1) { + sum1 = (u_long)aps->aps_seqoff[off]; + sum2 += sum1; + tcp->th_seq = htonl(sum2); + ch = 1; + } + } + + if (inc && (sum2 > aps->aps_after[!off])) { + aps->aps_after[!off] = sum2 + nlen - 1; + aps->aps_seqoff[!off] = aps->aps_seqoff[off] + inc; + } + } + return ch ? 2 : 0; +} diff --git a/sys/netinet/ip_log.c b/sys/netinet/ip_log.c new file mode 100644 index 00000000000..fa04c62e9e7 --- /dev/null +++ b/sys/netinet/ip_log.c @@ -0,0 +1,473 @@ +/* + * Copyright (C) 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. + * + * $Id: ip_log.c,v 1.1 1998/01/26 04:10:43 dgregor Exp $ + */ +#ifdef IPFILTER_LOG +# ifndef SOLARIS +# define SOLARIS (defined(sun) && (defined(__svr4__) || defined(__SVR4))) +# endif + +# if defined(KERNEL) && !defined(_KERNEL) +# define _KERNEL +# endif +# ifdef __FreeBSD__ +# if defined(_KERNEL) && !defined(IPFILTER_LKM) +# include <sys/osreldate.h> +# else +# include <osreldate.h> +# endif +# endif +# ifndef _KERNEL +# include <stdio.h> +# include <string.h> +# include <stdlib.h> +# include <ctype.h> +# endif +# include <sys/errno.h> +# include <sys/types.h> +# include <sys/param.h> +# include <sys/file.h> +# if __FreeBSD_version >= 220000 && defined(_KERNEL) +# include <sys/fcntl.h> +# include <sys/filio.h> +# else +# include <sys/ioctl.h> +# endif +# include <sys/time.h> +# if defined(_KERNEL) && !defined(linux) +# include <sys/systm.h> +# endif +# include <sys/uio.h> +# if !SOLARIS +# if (NetBSD > 199609) || (OpenBSD > 199603) +# include <sys/dirent.h> +# else +# include <sys/dir.h> +# endif +# ifndef linux +# include <sys/mbuf.h> +# endif +# else +# include <sys/filio.h> +# include <sys/cred.h> +# include <sys/ddi.h> +# include <sys/sunddi.h> +# include <sys/ksynch.h> +# include <sys/kmem.h> +# include <sys/mkdev.h> +# include <sys/dditypes.h> +# include <sys/cmn_err.h> +# endif +# ifndef linux +# include <sys/protosw.h> +# endif +# include <sys/socket.h> + +# include <net/if.h> +# ifdef sun +# include <net/af.h> +# endif +# if __FreeBSD_version >= 300000 +# include <net/if_var.h> +# endif +# include <net/route.h> +# include <netinet/in.h> +# ifdef __sgi +# include <sys/ddi.h> +# ifdef IFF_DRVRLOCK /* IRIX6 */ +# include <sys/hashing.h> +# endif +# endif +# if !defined(linux) && !(defined(__sgi) && !defined(IFF_DRVRLOCK)) /*IRIX<6*/ +# include <netinet/in_var.h> +# endif +# include <netinet/in_systm.h> +# include <netinet/ip.h> +# include <netinet/tcp.h> +# include <netinet/udp.h> +# include <netinet/ip_icmp.h> +# ifndef linux +# include <netinet/ip_var.h> +# endif +# ifndef _KERNEL +# include <syslog.h> +# endif +# include "netinet/ip_fil_compat.h" +# include <netinet/tcpip.h> +# include "netinet/ip_fil.h" +# include "netinet/ip_proxy.h" +# include "netinet/ip_nat.h" +# include "netinet/ip_frag.h" +# include "netinet/ip_state.h" +# include "netinet/ip_auth.h" +# ifndef MIN +# define MIN(a,b) (((a)<(b))?(a):(b)) +# endif + + +# if SOLARIS || defined(__sgi) +extern kmutex_t ipl_mutex; +# if SOLARIS +extern kcondvar_t iplwait; +# endif +# endif + +iplog_t **iplh[IPL_LOGMAX+1], *iplt[IPL_LOGMAX+1]; +int iplused[IPL_LOGMAX+1]; +u_long iplcrc[IPL_LOGMAX+1]; +u_long iplcrcinit; +#ifdef linux +static struct wait_queue *iplwait[IPL_LOGMAX+1]; +#endif + + +/* + * Initialise log buffers & pointers. Also iniialised the CRC to a local + * secret for use in calculating the "last log checksum". + */ +void ipflog_init() +{ + struct timeval tv; + int i; + + for (i = IPL_LOGMAX; i >= 0; i--) { + iplt[i] = NULL; + iplh[i] = &iplt[i]; + iplused[i] = 0; + } +# if BSD >= 199306 || defined(__FreeBSD__) || defined(__sgi) + microtime(&tv); +# else + uniqtime(&tv); +# endif + iplcrcinit = tv.tv_sec ^ (tv.tv_usec << 8) ^ tv.tv_usec; +} + + +/* + * ipflog + * Create a log record for a packet given that it has been triggered by a + * rule (or the default setting). Calculate the transport protocol header + * size using predetermined size of a couple of popular protocols and thus + * how much data to copy into the log, including part of the data body if + * requested. + */ +int ipflog(flags, ip, fin, m) +u_int flags; +ip_t *ip; +fr_info_t *fin; +mb_t *m; +{ + ipflog_t ipfl; + register int mlen, hlen; + u_long crc; + size_t sizes[2]; + void *ptrs[2]; + int types[2]; +# if SOLARIS + ill_t *ifp = fin->fin_ifp; +# else + struct ifnet *ifp = fin->fin_ifp; +# endif + + /* + * calculate header size. + */ + hlen = fin->fin_hlen; + if (ip->ip_p == IPPROTO_TCP) + hlen += MIN(sizeof(tcphdr_t), fin->fin_dlen); + else if (ip->ip_p == IPPROTO_UDP) + hlen += MIN(sizeof(udphdr_t), fin->fin_dlen); + else if (ip->ip_p == IPPROTO_ICMP) { + struct icmp *icmp = (struct icmp *)((char *)ip + hlen); + + /* + * For ICMP, if the packet is an error packet, also include + * the information about the packet which caused the error. + */ + switch (icmp->icmp_type) + { + case ICMP_UNREACH : + case ICMP_SOURCEQUENCH : + case ICMP_REDIRECT : + case ICMP_TIMXCEED : + case ICMP_PARAMPROB : + hlen += MIN(sizeof(struct icmp) + 8, fin->fin_dlen); + break; + default : + hlen += MIN(sizeof(struct icmp), fin->fin_dlen); + break; + } + } + /* + * Get the interface number and name to which this packet is + * currently associated. + */ +# if SOLARIS + ipfl.fl_unit = (u_char)ifp->ill_ppa; + bcopy(ifp->ill_name, ipfl.fl_ifname, MIN(ifp->ill_name_length, 4)); + mlen = (flags & FR_LOGBODY) ? MIN(msgdsize(m) - hlen, 128) : 0; +# else +# if (defined(NetBSD) && (NetBSD <= 1991011) && (NetBSD >= 199603)) || \ + (defined(OpenBSD) && (OpenBSD >= 199603)) + strncpy(ipfl.fl_ifname, ifp->if_xname, IFNAMSIZ); +# else +# ifndef linux + ipfl.fl_unit = (u_char)ifp->if_unit; +# endif + if ((ipfl.fl_ifname[0] = ifp->if_name[0])) + if ((ipfl.fl_ifname[1] = ifp->if_name[1])) + if ((ipfl.fl_ifname[2] = ifp->if_name[2])) + ipfl.fl_ifname[3] = ifp->if_name[3]; +# endif + mlen = (flags & FR_LOGBODY) ? MIN(ip->ip_len - hlen, 128) : 0; +# endif + ipfl.fl_plen = (u_char)mlen; + ipfl.fl_hlen = (u_char)hlen; + ipfl.fl_rule = fin->fin_rule; + ipfl.fl_group = fin->fin_group; + ipfl.fl_flags = flags; + ptrs[0] = (void *)&ipfl; + sizes[0] = sizeof(ipfl); + types[0] = 0; +#if SOLARIS + /* + * Are we copied from the mblk or an aligned array ? + */ + if (ip == (ip_t *)m->b_rptr) { + ptrs[1] = m; + sizes[1] = hlen + mlen; + types[1] = 1; + } else { + ptrs[1] = ip; + sizes[1] = hlen + mlen; + types[1] = 0; + } +#else + ptrs[1] = m; + sizes[1] = hlen + mlen; + types[1] = 1; +#endif + crc = (ipf_cksum((u_short *)fin, FI_CSIZE) << 8) + iplcrcinit; + return ipllog(IPL_LOGIPF, crc, ptrs, sizes, types, 2); +} + + +/* + * ipllog + */ +int ipllog(dev, crc, items, itemsz, types, cnt) +int dev; +u_long crc; +void **items; +size_t *itemsz; +int *types, cnt; +{ + iplog_t *ipl; + caddr_t buf, s; + int len, i; + + /* + * Check to see if this log record has a CRC which matches the last + * record logged. If it does, just up the count on the previous one + * rather than create a new one. + */ + if (crc) { + MUTEX_ENTER(&ipl_mutex); + if ((iplcrc[dev] == crc) && *iplh[dev]) { + (*iplh[dev])->ipl_count++; + MUTEX_EXIT(&ipl_mutex); + return 1; + } + iplcrc[dev] = crc; + MUTEX_EXIT(&ipl_mutex); + } + + /* + * Get the total amount of data to be logged. + */ + for (i = 0, len = sizeof(iplog_t); i < cnt; i++) + len += itemsz[i]; + + /* + * check that we have space to record this information and can + * allocate that much. + */ + KMALLOC(buf, caddr_t, len); + if (!buf) + return 0; + MUTEX_ENTER(&ipl_mutex); + if ((iplused[dev] + len) > IPLLOGSIZE) { + MUTEX_EXIT(&ipl_mutex); + KFREES(buf, len); + return 0; + } + iplused[dev] += len; + MUTEX_EXIT(&ipl_mutex); + + /* + * advance the log pointer to the next empty record and deduct the + * amount of space we're going to use. + */ + ipl = (iplog_t *)buf; + ipl->ipl_magic = IPL_MAGIC; + ipl->ipl_count = 1; + ipl->ipl_next = NULL; + ipl->ipl_dsize = len; +# if SOLARIS || defined(sun) || defined(linux) + uniqtime((struct timeval *)&ipl->ipl_sec); +# else +# if BSD >= 199306 || defined(__FreeBSD__) || defined(__sgi) + microtime((struct timeval *)&ipl->ipl_sec); +# endif +# endif + + /* + * Loop through all the items to be logged, copying each one to the + * buffer. Use bcopy for normal data or the mb_t copyout routine. + */ + for (i = 0, s = buf + sizeof(*ipl); i < cnt; i++) { + if (types[i] == 0) + bcopy(items[i], s, itemsz[i]); + else if (types[i] == 1) { +# if SOLARIS + copyout_mblk(items[i], 0, itemsz[i], s); +# else + m_copydata(items[i], 0, itemsz[i], s); +# endif + } + s += itemsz[i]; + } + MUTEX_ENTER(&ipl_mutex); + *iplh[dev] = ipl; + iplh[dev] = &ipl->ipl_next; +# if SOLARIS + cv_signal(&iplwait); + mutex_exit(&ipl_mutex); +# else + MUTEX_EXIT(&ipl_mutex); +# ifdef linux + wake_up_interruptible(&iplwait[dev]); +# else + wakeup(&iplh[dev]); +# endif +# endif + return 1; +} + + +int ipflog_read(unit, uio) +int unit; +struct uio *uio; +{ + iplog_t *ipl; + int error = 0, dlen, copied; +# if defined(_KERNEL) && !SOLARIS + int s; +# endif + + /* + * Sanity checks. Make sure the minor # is valid and we're copying + * a valid chunk of data. + */ + if ((IPL_LOGMAX < unit) || (unit < 0)) + return ENXIO; + if (!uio->uio_resid) + return 0; + if ((uio->uio_resid < sizeof(iplog_t)) || + (uio->uio_resid > IPLLOGSIZE)) + return EINVAL; + + /* + * Lock the log so we can snapshot the variables. Wait for a signal + * if the log is empty. + */ + SPL_NET(s); + MUTEX_ENTER(&ipl_mutex); + + while (!iplused[unit] || !iplt[unit]) { +# if SOLARIS && defined(_KERNEL) + if (!cv_wait_sig(&iplwait, &ipl_mutex)) { + MUTEX_EXIT(&ipl_mutex); + return EINTR; + } +# else +# ifdef linux + interruptible_sleep_on(&iplwait[unit]); + if (current->signal & ~current->blocked) + return -EINTR; +# else + MUTEX_EXIT(&ipl_mutex); + SPL_X(s); + error = SLEEP(&iplh[unit], "ipl sleep"); + if (error) + return error; + SPL_NET(s); + MUTEX_ENTER(&ipl_mutex); +# endif /* linux */ +# endif /* SOLARIS */ + } + +# if BSD >= 199306 || defined(__FreeBSD__) + uio->uio_rw = UIO_READ; +# endif + + for (copied = 0; (ipl = iplt[unit]); copied += dlen) { + dlen = ipl->ipl_dsize; + if (dlen + sizeof(iplog_t) > uio->uio_resid) + break; + /* + * Don't hold the mutex over the uiomove call. + */ + iplt[unit] = ipl->ipl_next; + MUTEX_EXIT(&ipl_mutex); + SPL_X(s); + error = UIOMOVE((caddr_t)ipl, ipl->ipl_dsize, UIO_READ, uio); + KFREES((caddr_t)ipl, ipl->ipl_dsize); + if (error) + break; + SPL_NET(s); + MUTEX_ENTER(&ipl_mutex); + iplused[unit] -= dlen; + } + if (!ipl) { + iplused[unit] = 0; + iplh[unit] = &iplt[unit]; + } + + if (!error) { + MUTEX_EXIT(&ipl_mutex); + SPL_X(s); + } +#ifdef linux + if (!error) + return copied; + return -error; +#else + return error; +#endif +} + + +int ipflog_clear(unit) +int unit; +{ + iplog_t *ipl; + int used; + + while ((ipl = iplt[unit])) { + iplt[unit] = ipl->ipl_next; + KFREES((caddr_t)ipl, ipl->ipl_dsize); + } + iplh[unit] = &iplt[unit]; + used = iplused[unit]; + iplused[unit] = 0; + iplcrc[unit] = 0; + return used; +} +#endif /* IPFILTER_LOG */ diff --git a/sys/netinet/ip_nat.c b/sys/netinet/ip_nat.c index eeb23ec49f2..b338bde479f 100644 --- a/sys/netinet/ip_nat.c +++ b/sys/netinet/ip_nat.c @@ -1,19 +1,19 @@ -/* $OpenBSD: ip_nat.c,v 1.12 1997/06/23 19:03:49 kstailey Exp $ */ /* - * (C)opyright 1995-1996 by Darren Reed. + * Copyright (C) 1995-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. * * Added redirect stuff and a LOT of bug fixes. (mcn@EnGarde.com) - * */ -#if 0 -#if !defined(lint) && defined(LIBC_SCCS) -static char sccsid[] = "@(#)ip_nat.c 1.11 6/5/96 (C) 1995 Darren Reed"; -static char rcsid[] = "$DRId: ip_nat.c,v 2.0.1.14 1997/04/22 12:47:39 darrenr Exp $"; +#if !defined(lint) +static const char sccsid[] = "@(#)ip_nat.c 1.11 6/5/96 (C) 1995 Darren Reed"; +static const char rcsid[] = "@(#)$Id: ip_nat.c,v 1.13 1998/01/26 04:10:43 dgregor Exp $"; #endif + +#if defined(__FreeBSD__) && defined(KERNEL) && !defined(_KERNEL) +#define _KERNEL #endif #if !defined(_KERNEL) && !defined(KERNEL) @@ -22,28 +22,43 @@ static char rcsid[] = "$DRId: ip_nat.c,v 2.0.1.14 1997/04/22 12:47:39 darrenr Ex # include <stdlib.h> #endif #include <sys/errno.h> -#ifndef __OpenBSD__ -# include <sys/types.h> -#endif +#include <sys/types.h> #include <sys/param.h> +#include <sys/time.h> #include <sys/file.h> -#include <sys/ioctl.h> +#if defined(KERNEL) && (__FreeBSD_version >= 220000) +# include <sys/filio.h> +# include <sys/fcntl.h> +#else +# include <sys/ioctl.h> +#endif +#include <sys/fcntl.h> #include <sys/uio.h> -#include <sys/protosw.h> +#ifndef linux +# include <sys/protosw.h> +#endif #include <sys/socket.h> -#ifdef _KERNEL +#if defined(_KERNEL) && !defined(linux) # include <sys/systm.h> #endif #if !defined(__SVR4) && !defined(__svr4__) -# include <sys/mbuf.h> +# ifndef linux +# include <sys/mbuf.h> +# endif #else +# include <sys/filio.h> # include <sys/byteorder.h> # include <sys/dditypes.h> # include <sys/stream.h> # include <sys/kmem.h> #endif - +#if __FreeBSD_version >= 300000 +# include <sys/queue.h> +#endif #include <net/if.h> +#if __FreeBSD_version >= 300000 +# include <net/if_var.h> +#endif #ifdef sun #include <net/af.h> #endif @@ -52,61 +67,77 @@ static char rcsid[] = "$DRId: ip_nat.c,v 2.0.1.14 1997/04/22 12:47:39 darrenr Ex #include <netinet/in_systm.h> #include <netinet/ip.h> +#ifdef __sgi +# ifdef IFF_DRVRLOCK /* IRIX6 */ +#include <sys/hashing.h> +#include <netinet/in_var.h> +# endif +#endif + #ifdef RFC1825 #include <vpn/md5.h> #include <vpn/ipsec.h> extern struct ifnet vpnif; #endif -#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" #ifndef MIN #define MIN(a,b) (((a)<(b))?(a):(b)) #endif +#undef SOCKADDR_IN +#define SOCKADDR_IN struct sockaddr_in nat_t *nat_table[2][NAT_SIZE], *nat_instances = NULL; ipnat_t *nat_list = NULL; -u_long nat_inuse = 0, - fr_defnatage = 1200; +u_long fr_defnatage = 1200, /* 10 minutes (600 seconds) */ + fr_defnaticmpage = 6; /* 3 seconds */ natstat_t nat_stats; -#if SOLARIS -# ifndef _KERNEL -#define bzero(a,b) memset(a,0,b) -#define bcmp(a,b,c) memcpy(a,b,c) -#define bcopy(a,b,c) memmove(b,a,c) -# else +#if (SOLARIS || defined(__sgi)) && defined(_KERNEL) extern kmutex_t ipf_nat; -# endif #endif -static int flush_nattable __P((void)), clear_natlist __P((void)); -static void nattable_sync __P((void)), nat_delete __P((struct nat *)); -void fix_incksum __P((u_short *, u_long)); -void fix_outcksum __P((u_short *, u_long)); -nat_t *nat_new __P((ipnat_t *, ip_t *, fr_info_t *, u_short, int)); -nat_t *nat_lookupmapip __P((register int, struct in_addr, u_short, - struct in_addr, u_short)); - -void -fix_outcksum(sp, n) - u_short *sp; - u_long n; +static int nat_flushtable __P((void)); +static int nat_clearlist __P((void)); +static void nat_delete __P((struct nat *)); +static int nat_ifpaddr __P((nat_t *, void *, struct in_addr *)); + + +#define LONG_SUM(in) (((in) & 0xffff) + ((in) >> 16)) + +#define CALC_SUMD(s1, s2, sd) { \ + /* Do it twice */ \ + (s1) = ((s1) & 0xffff) + ((s1) >> 16); \ + (s1) = ((s1) & 0xffff) + ((s1) >> 16); \ + /* Do it twice */ \ + (s2) = ((s2) & 0xffff) + ((s2) >> 16); \ + (s2) = ((s2) & 0xffff) + ((s2) >> 16); \ + /* Because ~1 == -2, We really need ~1 == -1 */ \ + if ((s1) > (s2)) (s2)--; \ + (sd) = (s2) - (s1); \ + (sd) = ((sd) & 0xffff) + ((sd) >> 16); } + +void fix_outcksum(sp, n) +u_short *sp; +u_long n; { register u_short sumshort; register u_long sum1; -#ifdef sparc - sum1 = (~(*sp)) & 0xffff; -#else + if (!n) + return; sum1 = (~ntohs(*sp)) & 0xffff; -#endif sum1 += (n); sum1 = (sum1 >> 16) + (sum1 & 0xffff); /* Again */ @@ -116,14 +147,15 @@ fix_outcksum(sp, n) } -void -fix_incksum(sp, n) - u_short *sp; - u_long n; +void fix_incksum(sp, n) +u_short *sp; +u_long n; { register u_short sumshort; register u_long sum1; + if (!n) + return; #ifdef sparc sum1 = (~(*sp)) & 0xffff; #else @@ -162,21 +194,29 @@ fix_incksum(sp, n) /* * Handle ioctls which manipulate the NAT. */ -int -nat_ioctl(data, cmd, mode) - caddr_t data; - u_long cmd; - int mode; +int nat_ioctl(data, cmd, mode) +#if defined(__NetBSD__) || defined(__OpenBSD__) +u_long cmd; +#else +int cmd; +#endif +caddr_t data; +int mode; { register ipnat_t *nat, *n = NULL, **np = NULL; ipnat_t natd; - int error = 0, ret, s; + int error = 0, ret; +#if defined(_KERNEL) && !SOLARIS + int s; +#endif + + nat = NULL; /* XXX gcc -Wuninitialized */ /* * For add/delete, look to see if the NAT entry is already present */ + SPL_NET(s); MUTEX_ENTER(&ipf_nat); - SPLNET(s); if ((cmd == SIOCADNAT) || (cmd == SIOCRMNAT)) { IRCOPY(data, (char *)&natd, sizeof(natd)); nat = &natd; @@ -199,12 +239,16 @@ nat_ioctl(data, cmd, mode) error = EEXIST; break; } - if (!(n = (ipnat_t *)KMALLOC(sizeof(*n)))) { + KMALLOC(n, ipnat_t *, sizeof(*n)); + if (n == NULL) { error = ENOMEM; break; } bcopy((char *)nat, (char *)n, sizeof(*n)); n->in_ifp = (void *)GETUNIT(n->in_ifname); + if (!n->in_ifp) + n->in_ifp = (void *)-1; + n->in_apr = ap_match(n->in_p, n->in_plabel); n->in_next = *np; n->in_use = 0; n->in_space = ~(0xffffffff & ntohl(n->in_outmsk)); @@ -212,11 +256,11 @@ nat_ioctl(data, cmd, mode) n->in_space -= 2; else n->in_space = 1; /* single IP# mapping */ - if (n->in_outmsk != 0xffffffff) + if ((n->in_outmsk != 0xffffffff) && n->in_outmsk) n->in_nip = ntohl(n->in_outip) + 1; else n->in_nip = ntohl(n->in_outip); - if (n->in_redir == NAT_MAP) { + if (n->in_redir & NAT_MAP) { n->in_pnext = ntohs(n->in_pmin); /* * Multiply by the number of ports made available. @@ -227,6 +271,7 @@ nat_ioctl(data, cmd, mode) } /* Otherwise, these fields are preset */ *np = n; + nat_stats.ns_rules++; break; case SIOCRMNAT : if (!(mode & FWRITE)) { @@ -238,15 +283,20 @@ nat_ioctl(data, cmd, mode) break; } *np = n->in_next; - - KFREE(n); - nattable_sync(); + if (!n->in_use) { + if (n->in_apr) + ap_free(n->in_apr); + KFREE(n); + nat_stats.ns_rules--; + } else { + n->in_flags |= IPN_DELETE; + n->in_next = NULL; + } break; case SIOCGNATS : nat_stats.ns_table[0] = nat_table[0]; nat_stats.ns_table[1] = nat_table[1]; nat_stats.ns_list = nat_list; - nat_stats.ns_inuse = nat_inuse; IWCOPY((char *)&nat_stats, (char *)data, sizeof(nat_stats)); break; case SIOCGNATL : @@ -255,9 +305,9 @@ nat_ioctl(data, cmd, mode) IRCOPY((char *)data, (char *)&nl, sizeof(nl)); - if (nat_lookupredir(&nl)) + if (nat_lookupredir(&nl)) { IWCOPY((char *)&nl, (char *)data, sizeof(nl)); - else + } else error = ESRCH; break; } @@ -266,7 +316,8 @@ nat_ioctl(data, cmd, mode) error = EPERM; break; } - ret = flush_nattable(); + ret = nat_flushtable(); + (void) ap_unload(); IWCOPY((caddr_t)&ret, data, sizeof(ret)); break; case SIOCCNATL : @@ -274,21 +325,30 @@ nat_ioctl(data, cmd, mode) error = EPERM; break; } - ret = clear_natlist(); + ret = nat_clearlist(); IWCOPY((caddr_t)&ret, data, sizeof(ret)); break; + case FIONREAD : +#ifdef IPFILTER_LOG + IWCOPY((caddr_t)&iplused[IPL_LOGNAT], (caddr_t)data, + sizeof(iplused[IPL_LOGNAT])); +#endif + break; } - SPLX(s); MUTEX_EXIT(&ipf_nat); + SPL_X(s); return error; } -static void -nat_delete(natd) - struct nat *natd; +/* + * Delete a nat entry from the various lists and table. + */ +static void nat_delete(natd) +struct nat *natd; { register struct nat **natp, *nat; + struct ipnat *ipn; for (natp = natd->nat_hstart[0]; (nat = *natp); natp = &nat->nat_hnext[0]) @@ -304,20 +364,35 @@ nat_delete(natd) break; } - if (natd->nat_ptr) { - natd->nat_ptr->in_space++; - natd->nat_ptr->in_use--; + /* + * If there is an active reference from the nat entry to its parent + * rule, decrement the rule's reference count and free it too if no + * longer being used. + */ + if ((ipn = natd->nat_ptr)) { + ipn->in_space++; + ipn->in_use--; + if (!ipn->in_use && (ipn->in_flags & IPN_DELETE)) { + if (ipn->in_apr) + ap_free(ipn->in_apr); + KFREE(ipn); + nat_stats.ns_rules--; + } } + + /* + * If there's a fragment table entry too for this nat entry, then + * dereference that as well. + */ + ipfr_forget((void *)natd); KFREE(natd); - nat_inuse--; } /* - * flush_nattable - clear the NAT table of all mapping entries. + * nat_flushtable - clear the NAT table of all mapping entries. */ -static int -flush_nattable() +static int nat_flushtable() { register nat_t *nat, **natp; register int j = 0; @@ -340,61 +415,113 @@ flush_nattable() /* - * I know this is O(N*M), but it can't be avoided. + * nat_clearlist - delete all entries in the active NAT mapping list. */ -static void -nattable_sync() +static int nat_clearlist() { - register nat_t *nat; - register ipnat_t *np; - int i; + register ipnat_t *n, **np = &nat_list; + int i = 0; - for (i = NAT_SIZE - 1; i >= 0; i--) - for (nat = nat_instances; nat; nat = nat->nat_next) { - for (np = nat_list; np; np = np->in_next) - if (nat->nat_ptr == np) - break; - /* - * XXX - is it better to remove this if ? works the - * same if it is just "nat->nat_ptr = np". - */ - if (!np) - nat->nat_ptr = NULL; + while ((n = *np)) { + *np = n->in_next; + if (!n->in_use) { + if (n->in_apr) + ap_free(n->in_apr); + KFREE(n); + nat_stats.ns_rules--; + i++; + } else { + n->in_flags |= IPN_DELETE; + n->in_next = NULL; } + } + nat_stats.ns_inuse = 0; + return i; } /* - * clear_natlist - delete all entries in the active NAT mapping list. + * return the first IP Address associated with an interface */ -static int -clear_natlist() +static int nat_ifpaddr(nat, ifptr, inp) +nat_t *nat; +void *ifptr; +struct in_addr *inp; { - register ipnat_t *n, **np; - int i = 0; +#if SOLARIS + ill_t *ill = ifptr; +#else + struct ifnet *ifp = ifptr; +#endif + struct in_addr in; - for (np = &nat_list; (n = *np); i++) { - *np = n->in_next; - KFREE(n); +#if SOLARIS + in.s_addr = ill->ill_ipif->ipif_local_addr; +#else /* SOLARIS */ +# if linux + ; +# else /* linux */ + struct ifaddr *ifa; + struct sockaddr_in *sin; + +# if (__FreeBSD_version >= 300000) + ifa = TAILQ_FIRST(&ifp->if_addrhead); +# else +# if defined(__NetBSD__) || defined(__OpenBSD__) + ifa = ifp->if_addrlist.tqh_first; +# else +# if defined(__sgi) && defined(IFF_DRVRLOCK) /* IRIX 6 */ + ifa = &((struct in_ifaddr *)ifp->in_ifaddr)->ia_ifa; +# else + ifa = ifp->if_addrlist; +# endif +# endif /* __NetBSD__ || __OpenBSD__ */ +# endif /* __FreeBSD_version >= 300000 */ +# if (BSD < 199306) && !(/*IRIX6*/defined(__sgi) && defined(IFF_DRVRLOCK)) + sin = (SOCKADDR_IN *)&ifa->ifa_addr; +# else + sin = (SOCKADDR_IN *)ifa->ifa_addr; + while (sin && ifa && + sin->sin_family != AF_INET) { +# if (__FreeBSD_version >= 300000) + ifa = TAILQ_NEXT(ifa, ifa_link); +# else +# if defined(__NetBSD__) || defined(__OpenBSD__) + ifa = ifa->ifa_list.tqe_next; +# else + ifa = ifa->ifa_next; +# endif +# endif /* __FreeBSD_version >= 300000 */ + if (ifa) + sin = (SOCKADDR_IN *)ifa->ifa_addr; } - - nattable_sync(); - return i; + if (!ifa) + sin = NULL; + if (!sin) { + KFREE(nat); + return -1; + } +# endif /* (BSD < 199306) && (!__sgi && IFF_DRVLOCK) */ + in = sin->sin_addr; + in.s_addr = ntohl(in.s_addr); +# endif /* linux */ +#endif /* SOLARIS */ + *inp = in; + return 0; } /* * Create a new NAT table entry. */ -nat_t * -nat_new(np, ip, fin, flags, direction) - ipnat_t *np; - ip_t *ip; - fr_info_t *fin; - u_short flags; - int direction; +nat_t *nat_new(np, ip, fin, flags, direction) +ipnat_t *np; +ip_t *ip; +fr_info_t *fin; +u_short flags; +int direction; { - register u_long sum1, sum2, sumd; + register u_long sum1, sum2, sumd, l; u_short port = 0, sport = 0, dport = 0, nport = 0; struct in_addr in; tcphdr_t *tcp = NULL; @@ -409,7 +536,8 @@ nat_new(np, ip, fin, flags, direction) } /* Give me a new nat */ - if (!(nat = (nat_t *)KMALLOC(sizeof(*nat)))) + KMALLOC(nat, nat_t *, sizeof(*nat)); + if (nat == NULL) return NULL; bzero((char *)nat, sizeof(*nat)); @@ -423,10 +551,26 @@ nat_new(np, ip, fin, flags, direction) * If it's an outbound packet which doesn't match any existing * record, then create a new port */ + l = 0; do { + l++; port = 0; in.s_addr = np->in_nip; - if (nflags & IPN_TCPUDP) { + if (!in.s_addr && (np->in_outmsk == 0xffffffff)) { + if ((l > 1) || + nat_ifpaddr(nat, fin->fin_ifp, &in) == -1) { + KFREE(nat); + return NULL; + } + } else if (!in.s_addr && !np->in_outmsk) { + if (l > 1) { + KFREE(nat); + return NULL; + } + in.s_addr = ntohl(ip->ip_src.s_addr); + if (nflags & IPN_TCPUDP) + port = sport; + } else if (nflags & IPN_TCPUDP) { port = htons(np->in_pnext++); if (np->in_pnext >= ntohs(np->in_pmax)) { np->in_pnext = ntohs(np->in_pmin); @@ -444,7 +588,8 @@ nat_new(np, ip, fin, flags, direction) if ((np->in_nip & ntohl(np->in_outmsk)) > ntohl(np->in_outip)) np->in_nip = ntohl(np->in_outip) + 1; - } while (nat_inlookup(flags, ip->ip_dst, dport, in, port)); + } while (nat_inlookup(fin->fin_ifp, flags, ip->ip_dst, + dport, in, port)); /* Setup the NAT table */ nat->nat_inip = ip->ip_src; @@ -541,16 +686,146 @@ nat_new(np, ip, fin, flags, direction) nat->nat_hnext[1] = *natp; *natp = nat; nat->nat_ptr = np; - np->in_use++; + nat->nat_bytes = 0; + nat->nat_pkts = 0; + nat->nat_ifp = fin->fin_ifp; + nat->nat_dir = direction; if (direction == NAT_OUTBOUND) { if (flags & IPN_TCPUDP) - tcp->th_sport = htons(port); + tcp->th_sport = port; } else { if (flags & IPN_TCPUDP) - tcp->th_dport = htons(nport); + tcp->th_dport = nport; } nat_stats.ns_added++; - nat_inuse++; + nat_stats.ns_inuse++; + np->in_use++; + return nat; +} + + +nat_t *nat_icmpinlookup(ip, fin) +ip_t *ip; +fr_info_t *fin; +{ + icmphdr_t *icmp; + tcphdr_t *tcp = NULL; + ip_t *oip; + int flags = 0, type; + + icmp = (icmphdr_t *)fin->fin_dp; + /* + * Does it at least have the return (basic) IP header ? + * Only a basic IP header (no options) should be with an ICMP error + * header. + */ + if ((ip->ip_hl != 5) || (ip->ip_len < sizeof(*icmp) + sizeof(ip_t))) + return NULL; + type = icmp->icmp_type; + /* + * If it's not an error type, then return. + */ + if ((type != ICMP_UNREACH) && (type != ICMP_SOURCEQUENCH) && + (type != ICMP_REDIRECT) && (type != ICMP_TIMXCEED) && + (type != ICMP_PARAMPROB)) + return NULL; + + oip = (ip_t *)((char *)fin->fin_dp + 8); + if (oip->ip_p == IPPROTO_TCP) + flags = IPN_TCP; + else if (oip->ip_p == IPPROTO_UDP) + flags = IPN_UDP; + if (flags & IPN_TCPUDP) { + tcp = (tcphdr_t *)((char *)oip + (oip->ip_hl << 2)); + return nat_inlookup(fin->fin_ifp, flags, oip->ip_dst, + tcp->th_dport, oip->ip_src, tcp->th_sport); + } + return nat_inlookup(fin->fin_ifp, 0, oip->ip_src, 0, oip->ip_dst, 0); +} + + +/* + * This should *ONLY* be used for incoming packets to make sure a NAT'd ICMP + * packet gets correctly recognised. + */ +nat_t *nat_icmpin(ip, fin, nflags) +ip_t *ip; +fr_info_t *fin; +int *nflags; +{ + icmphdr_t *icmp; + nat_t *nat; + ip_t *oip; + int flags = 0; + + if (!(nat = nat_icmpinlookup(ip, fin))) + return NULL; + + *nflags = IPN_ICMPERR; + icmp = (icmphdr_t *)fin->fin_dp; + oip = (ip_t *)((char *)icmp + 8); + if (oip->ip_p == IPPROTO_TCP) + flags = IPN_TCP; + else if (oip->ip_p == IPPROTO_UDP) + flags = IPN_UDP; + /* + * Need to adjust ICMP header to include the real IP#'s and + * port #'s. Only apply a checksum change relative to the + * IP address change is it will be modified again in ip_natout + * for both address and port. Two checksum changes are + * necessary for the two header address changes. Be careful + * to only modify the checksum once for the port # and twice + * for the IP#. + */ + if (flags & IPN_TCPUDP) { + tcphdr_t *tcp = (tcphdr_t *)(oip + 1); + u_long sum1, sum2, sumd; + struct in_addr in; + + if (nat->nat_dir == NAT_OUTBOUND) { + sum1 = LONG_SUM(ntohl(oip->ip_src.s_addr)); + in = nat->nat_outip; + oip->ip_src = in; + tcp->th_sport = nat->nat_outport; + } else { + sum1 = LONG_SUM(ntohl(oip->ip_dst.s_addr)); + in = nat->nat_inip; + oip->ip_dst = in; + tcp->th_dport = nat->nat_inport; + } + + sum2 = LONG_SUM(in.s_addr); + + CALC_SUMD(sum1, sum2, sumd); + sumd = (sumd & 0xffff) + (sumd >> 16); + + if (nat->nat_dir == NAT_OUTBOUND) { + fix_incksum(&oip->ip_sum, sumd); + fix_incksum(&icmp->icmp_cksum, sumd); + } else { + fix_outcksum(&oip->ip_sum, sumd); + fix_outcksum(&icmp->icmp_cksum, sumd); + } + + /* + * TCP checksum doesn't make it into the 1st eight + * bytes but UDP does. + */ + if (ip->ip_p == IPPROTO_UDP) { + udphdr_t *udp = (udphdr_t *)tcp; + + if (udp->uh_sum) { + if (nat->nat_dir == NAT_OUTBOUND) + fix_incksum(&udp->uh_sum, + nat->nat_sumd); + else + fix_outcksum(&udp->uh_sum, + nat->nat_sumd); + } + } + } else + ip->ip_dst = nat->nat_outip; + nat->nat_age = fr_defnaticmpage; return nat; } @@ -565,11 +840,15 @@ nat_new(np, ip, fin, flags, direction) * we're looking for a table entry, based on the destination address. * NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. */ -nat_t * -nat_inlookup(flags, src, sport, mapdst, mapdport) - register int flags; - struct in_addr src, mapdst; - u_short sport, mapdport; +#ifdef __STDC__ +nat_t *nat_inlookup(void *ifp, int flags, struct in_addr src, u_short sport, struct in_addr mapdst, u_short mapdport) +#else +nat_t *nat_inlookup(ifp, flags, src, sport, mapdst, mapdport) +void *ifp; +register int flags; +struct in_addr src , mapdst; +u_short sport, mapdport; +#endif { register nat_t *nat; @@ -577,7 +856,8 @@ nat_inlookup(flags, src, sport, mapdst, mapdport) nat = nat_table[1][mapdst.s_addr % NAT_SIZE]; for (; nat; nat = nat->nat_hnext[1]) - if (nat->nat_oip.s_addr == src.s_addr && + if ((!ifp || ifp == nat->nat_ifp) && + nat->nat_oip.s_addr == src.s_addr && nat->nat_outip.s_addr == mapdst.s_addr && flags == nat->nat_flags && (!flags || (nat->nat_oport == sport && @@ -593,23 +873,29 @@ nat_inlookup(flags, src, sport, mapdst, mapdport) * we're looking for a table entry, based on the source address. * NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. */ -nat_t * -nat_outlookup(flags, src, sport, dst, dport) - register int flags; - struct in_addr src, dst; - u_short sport, dport; +#ifdef __STDC__ +nat_t *nat_outlookup(void *ifp, int flags, struct in_addr src, u_short sport, struct in_addr dst, u_short dport) +#else +nat_t *nat_outlookup(ifp, flags, src, sport, dst, dport) +void *ifp; +register int flags; +struct in_addr src , dst; +u_short sport, dport; +#endif { register nat_t *nat; flags &= IPN_TCPUDP; nat = nat_table[0][src.s_addr % NAT_SIZE]; - for (; nat; nat = nat->nat_hnext[0]) - if (nat->nat_inip.s_addr == src.s_addr && + for (; nat; nat = nat->nat_hnext[0]) { + if ((!ifp || ifp == nat->nat_ifp) && + nat->nat_inip.s_addr == src.s_addr && nat->nat_oip.s_addr == dst.s_addr && - (!flags || flags & nat->nat_flags) && (!flags || + flags == nat->nat_flags && (!flags || (nat->nat_inport == sport && nat->nat_oport == dport))) return nat; + } return NULL; } @@ -619,11 +905,15 @@ nat_outlookup(flags, src, sport, dst, dport) * real destination address/port. We use this lookup when sending a packet * out, we're looking for a table entry, based on the source address. */ -nat_t * -nat_lookupmapip(flags, mapsrc, mapsport, dst, dport) - register int flags; - struct in_addr mapsrc, dst; - u_short mapsport, dport; +#ifdef __STDC__ +nat_t *nat_lookupmapip(void *ifp, int flags, struct in_addr mapsrc, u_short mapsport, struct in_addr dst, u_short dport) +#else +nat_t *nat_lookupmapip(ifp, flags, mapsrc, mapsport, dst, dport) +void *ifp; +register int flags; +struct in_addr mapsrc , dst; +u_short mapsport, dport; +#endif { register nat_t *nat; @@ -631,8 +921,9 @@ nat_lookupmapip(flags, mapsrc, mapsport, dst, dport) nat = nat_table[1][mapsrc.s_addr % NAT_SIZE]; for (; nat; nat = nat->nat_hnext[0]) - if (nat->nat_outip.s_addr == mapsrc.s_addr && + if ((!ifp || ifp == nat->nat_ifp) && nat->nat_oip.s_addr == dst.s_addr && + nat->nat_outip.s_addr == mapsrc.s_addr && flags == nat->nat_flags && (!flags || (nat->nat_outport == mapsport && nat->nat_oport == dport))) @@ -644,9 +935,8 @@ nat_lookupmapip(flags, mapsrc, mapsport, dst, dport) /* * Lookup the NAT tables to search for a matching redirect */ -nat_t * -nat_lookupredir(np) - register natlookup_t *np; +nat_t *nat_lookupredir(np) +register natlookup_t *np; { nat_t *nat; @@ -654,10 +944,11 @@ nat_lookupredir(np) * If nl_inip is non null, this is a lookup based on the real * ip address. Else, we use the fake. */ - if ((nat = nat_outlookup(IPN_TCPUDP, np->nl_inip, np->nl_inport, - np->nl_outip, np->nl_outport))) { - np->nl_inip = nat->nat_outip; - np->nl_inport = nat->nat_outport; + if ((nat = nat_outlookup(NULL, np->nl_flags, np->nl_inip, + np->nl_inport, np->nl_outip, + np->nl_outport))) { + np->nl_realip = nat->nat_outip; + np->nl_realport = nat->nat_outport; } return nat; } @@ -667,19 +958,19 @@ nat_lookupredir(np) * Packets going out on the external interface go through this. * Here, the source address requires alteration, if anything. */ -int -ip_natout(ip, hlen, fin) - ip_t *ip; - int hlen; - fr_info_t *fin; +int ip_natout(ip, hlen, fin) +ip_t *ip; +int hlen; +fr_info_t *fin; { register ipnat_t *np; register u_long ipa; tcphdr_t *tcp = NULL; - nat_t *nat; u_short nflags = 0, sport = 0, dport = 0, *csump = NULL; struct ifnet *ifp; frentry_t *fr; + nat_t *nat; + int natadd = 1; if ((fr = fin->fin_fr) && !(fr->fr_flags & FR_DUP) && fr->fr_tif.fd_ifp && fr->fr_tif.fd_ifp != (void *)-1) @@ -702,40 +993,57 @@ ip_natout(ip, hlen, fin) ipa = ip->ip_src.s_addr; MUTEX_ENTER(&ipf_nat); - for (np = nat_list; np; np = np->in_next) - if ((np->in_ifp == ifp) && np->in_space && - (!np->in_flags || (np->in_flags & nflags)) && - ((ipa & np->in_inmsk) == np->in_inip) && - ((np->in_redir == NAT_MAP) || - (np->in_pnext == sport))) { - /* - * If there is no current entry in the nat table for - * this IP#, create one for it. - */ - if (!(nat = nat_outlookup(nflags, ip->ip_src, sport, - ip->ip_dst, dport))) { - if (np->in_redir == NAT_REDIRECT) + if ((ip->ip_off & (IP_OFFMASK|IP_MF)) && + (nat = ipfr_nat_knownfrag(ip, fin))) + natadd = 0; + else if ((nat = nat_outlookup(ifp, nflags, ip->ip_src, sport, + ip->ip_dst, dport))) + ; + else + /* + * If there is no current entry in the nat table for this IP#, + * create one for it (if there is a matching rule). + */ + for (np = nat_list; np; np = np->in_next) + if ((np->in_ifp == ifp) && np->in_space && + (!np->in_flags || (np->in_flags & nflags)) && + ((ipa & np->in_inmsk) == np->in_inip) && + ((np->in_redir & NAT_MAP) || + (np->in_pnext == sport))) { + if (*np->in_plabel && !ap_ok(ip, tcp, np)) continue; /* - * if it's a redirection, then we don't want - * to create new outgoing port stuff. + * If it's a redirection, then we don't want to + * create new outgoing port stuff. * Redirections are only for incoming * connections. */ - if (!(nat = nat_new(np, ip, fin, nflags, + if (!(np->in_redir & NAT_MAP)) + continue; + if ((nat = nat_new(np, ip, fin, nflags, NAT_OUTBOUND))) - break; +#ifdef IPFILTER_LOG + nat_log(nat, (u_short)np->in_redir); +#else + ; +#endif + break; } - ip->ip_src = nat->nat_outip; - nat->nat_age = fr_defnatage; /* 5 mins */ + if (nat) { + if (natadd && fin->fin_fi.fi_fl & FI_FRAG) + ipfr_nat_newfrag(ip, fin, 0, nat); + nat->nat_age = fr_defnatage; + ip->ip_src = nat->nat_outip; + nat->nat_bytes += ip->ip_len; + nat->nat_pkts++; /* * Fix up checksums, not by recalculating them, but * simply computing adjustments. */ -#if SOLARIS - if (np->in_redir == NAT_MAP) +#if SOLARIS || defined(__sgi) + if (nat->nat_dir == NAT_OUTBOUND) fix_outcksum(&ip->ip_sum, nat->nat_ipsumd); else fix_incksum(&ip->ip_sum, nat->nat_ipsumd); @@ -749,8 +1057,16 @@ ip_natout(ip, hlen, fin) if (ip->ip_p == IPPROTO_TCP) { csump = &tcp->th_sum; - set_tcp_age(&nat->nat_age, - nat->nat_state, ip, fin,1); + fr_tcp_age(&nat->nat_age, + nat->nat_state, ip, fin,1); + /* + * Increase this because we may have + * "keep state" following this too and + * packet storms can occur if this is + * removed too quickly. + */ + if (nat->nat_age == fr_tcpclosed) + nat->nat_age = fr_tcplastack; } else if (ip->ip_p == IPPROTO_UDP) { udphdr_t *udp = (udphdr_t *)tcp; @@ -762,7 +1078,7 @@ ip_natout(ip, hlen, fin) csump = &ic->icmp_cksum; } if (csump) { - if (np->in_redir == NAT_MAP) + if (nat->nat_dir == NAT_OUTBOUND) fix_outcksum(csump, nat->nat_sumd); else @@ -770,9 +1086,10 @@ ip_natout(ip, hlen, fin) nat->nat_sumd); } } + (void) ap_check(ip, tcp, fin, nat); nat_stats.ns_mapped[1]++; MUTEX_EXIT(&ipf_nat); - return 1; + return -2; } MUTEX_EXIT(&ipf_nat); return 0; @@ -783,18 +1100,18 @@ ip_natout(ip, hlen, fin) * Packets coming in from the external interface go through this. * Here, the destination address requires alteration, if anything. */ -int -ip_natin(ip, hlen, fin) - ip_t *ip; - int hlen; - fr_info_t *fin; +int ip_natin(ip, hlen, fin) +ip_t *ip; +int hlen; +fr_info_t *fin; { register ipnat_t *np; register struct in_addr in; struct ifnet *ifp = fin->fin_ifp; tcphdr_t *tcp = NULL; - u_short sport = 0, dport = 0, nflags = 0, *csump = NULL; + u_short sport = 0, dport = 0, *csump = NULL; nat_t *nat; + int nflags = 0, natadd = 1; if (!(ip->ip_off & 0x1fff) && !(fin->fin_fi.fi_fl & FI_SHORT)) { if (ip->ip_p == IPPROTO_TCP) @@ -811,43 +1128,58 @@ ip_natin(ip, hlen, fin) in = ip->ip_dst; MUTEX_ENTER(&ipf_nat); - for (np = nat_list; np; np = np->in_next) - if ((np->in_ifp == ifp) && - (!np->in_flags || (nflags & np->in_flags)) && - ((in.s_addr & np->in_outmsk) == np->in_outip) && - (np->in_redir == NAT_MAP || np->in_pmin == dport)) { - if (!(nat = nat_inlookup(nflags, ip->ip_src, sport, - ip->ip_dst, dport))) { - if (np->in_redir == NAT_MAP) - continue; - else { - /* - * If this rule (np) is a redirection, - * rather than a mapping, then do a - * nat_new. Otherwise, if it's just a - * mapping, do a continue; - */ - if (!(nat = nat_new(np, ip, fin, - nflags, - NAT_INBOUND))) - break; - } + + if ((ip->ip_p == IPPROTO_ICMP) && (nat = nat_icmpin(ip, fin, &nflags))) + ; + else if ((ip->ip_off & IP_OFFMASK) && + (nat = ipfr_nat_knownfrag(ip, fin))) + natadd = 0; + else if ((nat = nat_inlookup(fin->fin_ifp, nflags, ip->ip_src, sport, + ip->ip_dst, dport))) + ; + else + /* + * If there is no current entry in the nat table for this IP#, + * create one for it (if there is a matching rule). + */ + for (np = nat_list; np; np = np->in_next) + if ((np->in_ifp == ifp) && + (!np->in_flags || (nflags & np->in_flags)) && + ((in.s_addr & np->in_outmsk) == np->in_outip) && + (np->in_redir & NAT_REDIRECT) && + (!np->in_pmin || np->in_pmin == dport)) { + if ((nat = nat_new(np, ip, fin, nflags, + NAT_INBOUND))) +#ifdef IPFILTER_LOG + nat_log(nat, (u_short)np->in_redir); +#else + ; +#endif + break; } - ip->ip_dst = nat->nat_inip; + if (nat) { + if (natadd && fin->fin_fi.fi_fl & FI_FRAG) + ipfr_nat_newfrag(ip, fin, 0, nat); + (void) ap_check(ip, tcp, fin, nat); - nat->nat_age = fr_defnatage; + if (nflags != IPN_ICMPERR) + nat->nat_age = fr_defnatage; + + ip->ip_dst = nat->nat_inip; + nat->nat_bytes += ip->ip_len; + nat->nat_pkts++; /* * Fix up checksums, not by recalculating them, but * simply computing adjustments. */ -#if SOLARIS - if (np->in_redir == NAT_MAP) +#if SOLARIS || defined(__sgi) + if (nat->nat_dir == NAT_OUTBOUND) fix_incksum(&ip->ip_sum, nat->nat_ipsumd); else fix_outcksum(&ip->ip_sum, nat->nat_ipsumd); #endif - if (nflags && !(ip->ip_off & 0x1fff) && + if ((nflags & IPN_TCPUDP) && !(ip->ip_off & 0x1fff) && !(fin->fin_fi.fi_fl & FI_SHORT)) { if (nat->nat_inport) @@ -855,8 +1187,16 @@ ip_natin(ip, hlen, fin) if (ip->ip_p == IPPROTO_TCP) { csump = &tcp->th_sum; - set_tcp_age(&nat->nat_age, - nat->nat_state, ip, fin,0); + fr_tcp_age(&nat->nat_age, + nat->nat_state, ip, fin,0); + /* + * Increase this because we may have + * "keep state" following this too and + * packet storms can occur if this is + * removed too quickly. + */ + if (nat->nat_age == fr_tcpclosed) + nat->nat_age = fr_tcplastack; } else if (ip->ip_p == IPPROTO_UDP) { udphdr_t *udp = (udphdr_t *)tcp; @@ -868,7 +1208,7 @@ ip_natin(ip, hlen, fin) csump = &ic->icmp_cksum; } if (csump) { - if (np->in_redir == NAT_MAP) + if (nat->nat_dir == NAT_OUTBOUND) fix_incksum(csump, nat->nat_sumd); else @@ -878,7 +1218,7 @@ ip_natin(ip, hlen, fin) } nat_stats.ns_mapped[0]++; MUTEX_EXIT(&ipf_nat); - return 1; + return -2; } MUTEX_EXIT(&ipf_nat); return 0; @@ -888,16 +1228,12 @@ ip_natin(ip, hlen, fin) /* * Free all memory used by NAT structures allocated at runtime. */ -void -ip_natunload() +void ip_natunload() { - int s; - MUTEX_ENTER(&ipf_nat); - SPLNET(s); - (void) clear_natlist(); - (void) flush_nattable(); - SPLX(s) + (void) nat_clearlist(); + (void) nat_flushtable(); + (void) ap_unload(); MUTEX_EXIT(&ipf_nat); } @@ -906,23 +1242,128 @@ ip_natunload() * Slowly expire held state for NAT entries. Timeouts are set in * expectation of this being called twice per second. */ -void -ip_natexpire() +void ip_natexpire() { register struct nat *nat, **natp; +#if defined(_KERNEL) && !SOLARIS int s; +#endif + SPL_NET(s); MUTEX_ENTER(&ipf_nat); - SPLNET(s); for (natp = &nat_instances; (nat = *natp); ) { if (--nat->nat_age) { natp = &nat->nat_next; continue; } *natp = nat->nat_next; +#ifdef IPFILTER_LOG + nat_log(nat, NL_EXPIRE); +#endif nat_delete(nat); nat_stats.ns_expire++; } - SPLX(s); + + ap_expire(); + + MUTEX_EXIT(&ipf_nat); + SPL_X(s); +} + + +/* + */ +#ifdef __STDC__ +void ip_natsync(void *ifp) +#else +void ip_natsync(ifp) +void *ifp; +#endif +{ + register nat_t *nat; + register u_long sum1, sum2, sumd; + struct in_addr in; + ipnat_t *np; +#if defined(_KERNEL) && !SOLARIS + int s; +#endif + + SPL_NET(s); + MUTEX_ENTER(&ipf_nat); + for (nat = nat_instances; nat; nat = nat->nat_next) + if ((ifp == nat->nat_ifp) && (np = nat->nat_ptr)) + if ((np->in_outmsk == 0xffffffff) && !np->in_nip) { + /* + * Change the map-to address to be the same + * as the new one. + */ + sum1 = nat->nat_outip.s_addr; + if (nat_ifpaddr(nat, ifp, &in) == -1) + nat->nat_outip.s_addr = htonl(in.s_addr); + sum2 = nat->nat_outip.s_addr; + + /* + * Readjust the checksum adjustment to take + * into account the new IP#. + * + * Do it twice + */ + sum1 = (sum1 & 0xffff) + (sum1 >> 16); + sum1 = (sum1 & 0xffff) + (sum1 >> 16); + + /* Do it twice */ + sum2 = (sum2 & 0xffff) + (sum2 >> 16); + sum2 = (sum2 & 0xffff) + (sum2 >> 16); + + /* Because ~1 == -2, We really need ~1 == -1 */ + if (sum1 > sum2) + sum2--; + sumd = sum2 - sum1; + sumd = (sumd & 0xffff) + (sumd >> 16); + sumd += nat->nat_sumd; + nat->nat_sumd = (sumd & 0xffff) + (sumd >> 16); + } MUTEX_EXIT(&ipf_nat); + SPL_X(s); +} + + +#ifdef IPFILTER_LOG +# ifdef __STDC__ +void nat_log(struct nat *nat, u_short type) +# else +void nat_log(nat, type) +struct nat *nat; +u_short type; +# endif +{ + struct ipnat *np; + struct natlog natl; + void *items[1]; + size_t sizes[1]; + int rulen, types[1]; + + natl.nl_inip = nat->nat_inip; + natl.nl_outip = nat->nat_outip; + natl.nl_origip = nat->nat_oip; + natl.nl_bytes = nat->nat_bytes; + natl.nl_pkts = nat->nat_pkts; + natl.nl_origport = nat->nat_oport; + natl.nl_inport = nat->nat_inport; + natl.nl_outport = nat->nat_outport; + natl.nl_type = type; + natl.nl_rule = -1; + if (nat->nat_ptr) { + for (rulen = 0, np = nat_list; np; np = np->in_next, rulen++) + if (np == nat->nat_ptr) { + natl.nl_rule = rulen; + break; + } + } + items[0] = &natl; + sizes[0] = sizeof(natl); + types[0] = 0; + + (void) ipllog(IPL_LOGNAT, 0, items, sizes, types, 1); } +#endif diff --git a/sys/netinet/ip_nat.h b/sys/netinet/ip_nat.h index d8a2a18bd51..525c5538867 100644 --- a/sys/netinet/ip_nat.h +++ b/sys/netinet/ip_nat.h @@ -1,16 +1,15 @@ -/* $OpenBSD: ip_nat.h,v 1.7 1997/06/23 19:03:50 kstailey Exp $ */ /* - * (C)opyright 1995 by Darren Reed. + * Copyright (C) 1995-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. * * @(#)ip_nat.h 1.5 2/4/96 - * $DRId: ip_nat.h,v 2.0.1.9 1997/03/20 10:20:50 darrenr Exp $ + * $Id: ip_nat.h,v 1.8 1998/01/26 04:10:44 dgregor Exp $ */ -#ifndef __IP_NAT_H_ +#ifndef __IP_NAT_H__ #define __IP_NAT_H__ #ifndef SOLARIS @@ -26,7 +25,6 @@ #define SIOCGIPST _IOR('r', 85, struct ips_stat) #define SIOCFLNAT _IOWR('r', 86, int) #define SIOCCNATL _IOWR('r', 87, int) - #else #define SIOCADNAT _IOW(r, 80, struct ipnat) #define SIOCRMNAT _IOW(r, 81, struct ipnat) @@ -39,15 +37,21 @@ #endif #define NAT_SIZE 367 +#ifndef APR_LABELLEN +#define APR_LABELLEN 16 +#endif typedef struct nat { u_long nat_age; int nat_flags; u_long nat_sumd; u_long nat_ipsumd; + void *nat_data; struct in_addr nat_inip; struct in_addr nat_outip; struct in_addr nat_oip; /* other ip */ + U_QUAD_T nat_pkts; + U_QUAD_T nat_bytes; u_short nat_oport; /* other port */ u_short nat_inport; u_short nat_outport; @@ -57,11 +61,14 @@ typedef struct nat { struct nat *nat_next; struct nat *nat_hnext[2]; struct nat **nat_hstart[2]; + void *nat_ifp; + int nat_dir; } nat_t; typedef struct ipnat { struct ipnat *in_next; void *in_ifp; + void *in_apr; u_int in_space; u_int in_use; struct in_addr in_nextip; @@ -72,6 +79,9 @@ typedef struct ipnat { struct in_addr in_out[2]; int in_redir; /* 0 if it's a mapping, 1 if it's a hard redir */ char in_ifname[IFNAMSIZ]; + char in_plabel[APR_LABELLEN]; /* proxy label */ + char in_p; /* protocol */ + u_short in_dport; } ipnat_t; #define in_pmin in_port[0] /* Also holds static redir port */ @@ -82,45 +92,89 @@ typedef struct ipnat { #define in_outip in_out[0].s_addr #define in_outmsk in_out[1].s_addr -#define NAT_INBOUND 0 -#define NAT_OUTBOUND 1 +#define NAT_OUTBOUND 0 +#define NAT_INBOUND 1 -#define NAT_MAP 0 -#define NAT_REDIRECT 1 +#define NAT_MAP 0x01 +#define NAT_REDIRECT 0x02 +#define NAT_BIMAP (NAT_MAP|NAT_REDIRECT) #define IPN_CMPSIZ (sizeof(struct in_addr) * 4 + sizeof(u_short) * 3 + \ - sizeof(int) + IFNAMSIZ) + sizeof(int) + IFNAMSIZ + APR_LABELLEN + sizeof(char)) typedef struct natlookup { struct in_addr nl_inip; struct in_addr nl_outip; + struct in_addr nl_realip; + int nl_flags; u_short nl_inport; u_short nl_outport; + u_short nl_realport; } natlookup_t; typedef struct natstat { u_long ns_mapped[2]; + u_long ns_rules; u_long ns_added; u_long ns_expire; u_long ns_inuse; + u_long ns_logged; + u_long ns_logfail; nat_t **ns_table[2]; ipnat_t *ns_list; } natstat_t; -#define IPN_ANY 0 -#define IPN_TCP 1 -#define IPN_UDP 2 -#define IPN_TCPUDP 3 - -extern nat_t *nat_table[2][NAT_SIZE]; -extern int nat_ioctl __P((caddr_t, u_long, int)); -extern nat_t *nat_outlookup __P((int, struct in_addr, u_short, struct in_addr, - u_short)); -extern nat_t *nat_inlookup __P((int, struct in_addr, u_short, struct in_addr, - u_short)); -extern nat_t *nat_lookupredir __P((natlookup_t *)); -extern int ip_natout __P((ip_t *, int, fr_info_t *)); -extern int ip_natin __P((ip_t *, int, fr_info_t *)); -extern void ip_natunload __P((void)); -extern void ip_natexpire __P((void)); +#define IPN_ANY 0x00 +#define IPN_TCP 0x01 +#define IPN_UDP 0x02 +#define IPN_TCPUDP 0x03 +#define IPN_DELETE 0x04 +#define IPN_ICMPERR 0x08 + + +typedef struct natlog { + struct in_addr nl_origip; + struct in_addr nl_outip; + struct in_addr nl_inip; + u_short nl_origport; + u_short nl_outport; + u_short nl_inport; + u_short nl_type; + int nl_rule; + U_QUAD_T nl_pkts; + U_QUAD_T nl_bytes; +} natlog_t; + + +#define NL_NEWMAP NAT_MAP +#define NL_NEWRDR NAT_REDIRECT +#define NL_EXPIRE 0xffff + + +extern void ip_natsync __P((void *)); +extern u_long fr_defnatage; +extern u_long fr_defnaticmpage; +extern nat_t *nat_table[2][NAT_SIZE]; +#if defined(__NetBSD__) || defined(__OpenBSD__) +extern int nat_ioctl __P((caddr_t, u_long, int)); +#else +extern int nat_ioctl __P((caddr_t, int, int)); +#endif +extern nat_t *nat_new __P((ipnat_t *, ip_t *, fr_info_t *, u_short, int)); +extern nat_t *nat_outlookup __P((void *, int, struct in_addr, u_short, + struct in_addr, u_short)); +extern nat_t *nat_inlookup __P((void *, int, struct in_addr, u_short, + struct in_addr, u_short)); +extern nat_t *nat_lookupredir __P((natlookup_t *)); +extern nat_t *nat_lookupmapip __P((void *, int, struct in_addr, u_short, + struct in_addr, u_short)); +extern nat_t *nat_icmpinlookup __P((ip_t *, fr_info_t *)); +extern nat_t *nat_icmpin __P((ip_t *, fr_info_t *, int *)); + +extern int ip_natout __P((ip_t *, int, fr_info_t *)); +extern int ip_natin __P((ip_t *, int, fr_info_t *)); +extern void ip_natunload __P((void)), ip_natexpire __P((void)); +extern void nat_log __P((struct nat *, u_short)); +extern void fix_incksum __P((u_short *, u_long)); +extern void fix_outcksum __P((u_short *, u_long)); #endif /* __IP_NAT_H__ */ diff --git a/sys/netinet/ip_proxy.c b/sys/netinet/ip_proxy.c new file mode 100644 index 00000000000..be571686302 --- /dev/null +++ b/sys/netinet/ip_proxy.c @@ -0,0 +1,319 @@ +/* + * Copyright (C) 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 !defined(lint) +static const char rcsid[] = "@(#)$Id: ip_proxy.c,v 1.1 1998/01/26 04:10:45 dgregor Exp $"; +#endif + +#if defined(__FreeBSD__) && defined(KERNEL) && !defined(_KERNEL) +# define _KERNEL +#endif + +#if !defined(_KERNEL) && !defined(KERNEL) +# include <stdio.h> +# include <string.h> +# include <stdlib.h> +#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> +#include <sys/fcntl.h> +#include <sys/uio.h> +#ifndef linux +# include <sys/protosw.h> +#endif +#include <sys/socket.h> +#if defined(_KERNEL) +# if !defined(linux) +# include <sys/systm.h> +# else +# include <linux/string.h> +# endif +#endif +#if !defined(__SVR4) && !defined(__svr4__) +# ifndef linux +# include <sys/mbuf.h> +# endif +#else +# include <sys/byteorder.h> +# include <sys/dditypes.h> +# include <sys/stream.h> +# include <sys/kmem.h> +#endif +#if __FreeBSD__ > 2 +# include <sys/queue.h> +#endif +#include <net/if.h> +#ifdef sun +# include <net/af.h> +#endif +#include <net/route.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#ifndef linux +# include <netinet/ip_var.h> +#endif +#include <netinet/tcp.h> +#include <netinet/udp.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_state.h" + +#ifndef MIN +#define MIN(a,b) (((a)<(b))?(a):(b)) +#endif + +static ap_session_t *ap_find __P((ip_t *, tcphdr_t *)); +static ap_session_t *ap_new_session __P((aproxy_t *, ip_t *, tcphdr_t *, + fr_info_t *, nat_t *)); + +#define AP_SESS_SIZE 53 + +#if defined(_KERNEL) && !defined(linux) +#include "netinet/ip_ftp_pxy.c" +#endif + +ap_session_t *ap_sess_tab[AP_SESS_SIZE]; +aproxy_t ap_proxies[] = { +#ifdef IPF_FTP_PROXY + { "ftp", (char)IPPROTO_TCP, 0, 0, ippr_ftp_init, ippr_ftp_in, ippr_ftp_out }, +#endif + { "", '\0', 0, 0, NULL, NULL } +}; + + +int ap_ok(ip, tcp, nat) +ip_t *ip; +tcphdr_t *tcp; +ipnat_t *nat; +{ + aproxy_t *apr = nat->in_apr; + u_short dport = nat->in_dport; + + if (!apr || (apr && (apr->apr_flags & APR_DELETE)) || + (ip->ip_p != apr->apr_p)) + return 0; + if ((tcp && (tcp->th_dport != dport)) || (!tcp && dport)) + return 0; + return 1; +} + + +static ap_session_t *ap_find(ip, tcp) +ip_t *ip; +tcphdr_t *tcp; +{ + struct in_addr src, dst; + register u_long hv; + register u_short sp, dp; + register ap_session_t *aps; + register u_char p = ip->ip_p; + + src = ip->ip_src, dst = ip->ip_dst; + sp = dp = 0; /* XXX gcc -Wunitialized */ + + hv = ip->ip_src.s_addr ^ ip->ip_dst.s_addr; + hv *= 651733; + if (tcp) { + sp = tcp->th_sport; + dp = tcp->th_dport; + hv ^= (sp + dp); + hv *= 5; + } + hv %= AP_SESS_SIZE; + + for (aps = ap_sess_tab[hv]; aps; aps = aps->aps_next) + if ((aps->aps_p == p) && + IPPAIR(aps->aps_src, aps->aps_dst, src, dst)) { + if (tcp) { + if (PAIRS(aps->aps_sport, aps->aps_dport, + sp, dp)) + break; + } else + break; + } + return aps; +} + + +/* + * Allocate a new application proxy structure and fill it in with the + * relevant details. call the init function once complete, prior to + * returning. + */ +static ap_session_t *ap_new_session(apr, ip, tcp, fin, nat) +aproxy_t *apr; +ip_t *ip; +tcphdr_t *tcp; +fr_info_t *fin; +nat_t *nat; +{ + register ap_session_t *aps; + u_short dport; + u_long hv; + + if (!apr || (apr && (apr->apr_flags & APR_DELETE)) || + (ip->ip_p != apr->apr_p)) + return NULL; + dport = nat->nat_ptr->in_dport; + if ((tcp && (tcp->th_dport != dport)) || (!tcp && dport)) + return NULL; + + hv = ip->ip_src.s_addr ^ ip->ip_dst.s_addr; + hv *= 651733; + if (tcp) { + hv ^= (tcp->th_sport + tcp->th_dport); + hv *= 5; + } + hv %= AP_SESS_SIZE; + + KMALLOC(aps, ap_session_t *, sizeof(*aps)); + if (!aps) + return NULL; + bzero((char *)aps, sizeof(*aps)); + aps->aps_apr = apr; + aps->aps_src = ip->ip_src; + aps->aps_dst = ip->ip_dst; + aps->aps_p = ip->ip_p; + aps->aps_tout = 1200; /* XXX */ + if (tcp) { + aps->aps_sport = tcp->th_sport; + aps->aps_dport = tcp->th_dport; + } + aps->aps_data = NULL; + aps->aps_psiz = 0; + aps->aps_next = ap_sess_tab[hv]; + ap_sess_tab[hv] = aps; + (void) (*apr->apr_init)(fin, ip, tcp, aps, nat); + return aps; +} + + +/* + * check to see if a packet should be passed through an active proxy routine + * if one has been setup for it. + */ +int ap_check(ip, tcp, fin, nat) +ip_t *ip; +tcphdr_t *tcp; +fr_info_t *fin; +nat_t *nat; +{ + ap_session_t *aps; + aproxy_t *apr; + int err; + + if (!(fin->fin_fi.fi_fl & FI_TCPUDP)) + tcp = NULL; + + if ((aps = ap_find(ip, tcp)) || + (aps = ap_new_session(nat->nat_ptr->in_apr, ip, tcp, fin, nat))) { + if (ip->ip_p == IPPROTO_TCP) { + /* + * verify that the checksum is correct. If not, then + * don't do anything with this packet. + */ + if (tcp->th_sum != fr_tcpsum(*(mb_t **)fin->fin_mp, + ip, tcp, ip->ip_len)) { + frstats[fin->fin_out].fr_tcpbad++; + return -1; + } + fr_tcp_age(&aps->aps_tout, aps->aps_state, ip, fin, + tcp->th_sport == aps->aps_sport); + } + + apr = aps->aps_apr; + err = 0; + if (fin->fin_out) { + if (apr->apr_outpkt) + err = (*apr->apr_outpkt)(fin, ip, tcp, + aps, nat); + } else { + if (apr->apr_inpkt) + err = (*apr->apr_inpkt)(fin, ip, tcp, + aps, nat); + } + if (err == 2) { + tcp->th_sum = fr_tcpsum(*(mb_t **)fin->fin_mp, ip, + tcp, ip->ip_len); + err = 0; + } + return err; + } + return -1; +} + + +aproxy_t *ap_match(pr, name) +u_char pr; +char *name; +{ + aproxy_t *ap; + + for (ap = ap_proxies; ap->apr_p; ap++) + if ((ap->apr_p == pr) && + !strncmp(name, ap->apr_label, sizeof(ap->apr_label))) { + ap->apr_ref++; + return ap; + } + return NULL; +} + + +void ap_free(ap) +aproxy_t *ap; +{ + ap->apr_ref--; +} + + +void aps_free(aps) +ap_session_t *aps; +{ + if (aps->aps_data && aps->aps_psiz) + KFREES(aps->aps_data, aps->aps_psiz); + KFREE(aps); +} + + +void ap_unload() +{ + ap_session_t *aps; + int i; + + for (i = 0; i < AP_SESS_SIZE; i++) + while ((aps = ap_sess_tab[i])) { + ap_sess_tab[i] = aps->aps_next; + aps_free(aps); + } +} + + +void ap_expire() +{ + ap_session_t *aps, **apsp; + int i; + + for (i = 0; i < AP_SESS_SIZE; i++) + for (apsp = &ap_sess_tab[i]; (aps = *apsp); ) { + aps->aps_tout--; + if (!aps->aps_tout) { + ap_sess_tab[i] = aps->aps_next; + aps_free(aps); + *apsp = aps->aps_next; + } else + apsp = &aps->aps_next; + } +} diff --git a/sys/netinet/ip_proxy.h b/sys/netinet/ip_proxy.h new file mode 100644 index 00000000000..f802657bf1b --- /dev/null +++ b/sys/netinet/ip_proxy.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 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. + * + * $Id: ip_proxy.h,v 1.1 1998/01/26 04:10:45 dgregor Exp $ + */ + +#ifndef __IP_PROXY_H__ +#define __IP_PROXY_H__ + +#ifndef SOLARIS +#define SOLARIS (defined(sun) && (defined(__svr4__) || defined(__SVR4))) +#endif + +#ifndef APR_LABELLEN +#define APR_LABELLEN 16 +#endif +#define AP_SESS_SIZE 53 + +struct nat; +struct ipnat; + +typedef struct ap_tcp { + u_short apt_sport; /* source port */ + u_short apt_dport; /* destination port */ + short apt_sel; /* seqoff/after set selector */ + short apt_seqoff[2]; /* sequence # difference */ + tcp_seq apt_after[2]; /* don't change seq-off until after this */ + u_char apt_state[2]; /* connection state */ +} ap_tcp_t; + +typedef struct ap_udp { + u_short apu_sport; /* source port */ + u_short apu_dport; /* destination port */ +} ap_udp_t; + +typedef struct ap_session { + struct aproxy *aps_apr; + struct in_addr aps_src; /* source IP# */ + struct in_addr aps_dst; /* destination IP# */ + u_char aps_p; /* protocol */ + union { + struct ap_tcp apu_tcp; + struct ap_udp apu_udp; + } aps_un; + u_int aps_flags; + QUAD_T aps_bytes; /* bytes sent */ + QUAD_T aps_pkts; /* packets sent */ + u_long aps_tout; /* time left before expiring */ + void *aps_data; /* private data */ + int aps_psiz; /* size of private data */ + struct ap_session *aps_next; +} ap_session_t ; + +#define aps_sport aps_un.apu_tcp.apt_sport +#define aps_dport aps_un.apu_tcp.apt_dport +#define aps_sel aps_un.apu_tcp.apt_sel +#define aps_seqoff aps_un.apu_tcp.apt_seqoff +#define aps_after aps_un.apu_tcp.apt_after +#define aps_state aps_un.apu_tcp.apt_state + + +typedef struct aproxy { + char apr_label[APR_LABELLEN]; /* Proxy label # */ + u_char apr_p; /* protocol */ + int apr_ref; /* +1 per rule referencing it */ + int apr_flags; + int (* apr_init) __P((fr_info_t *, ip_t *, tcphdr_t *, + ap_session_t *, struct nat *)); + int (* apr_inpkt) __P((fr_info_t *, ip_t *, tcphdr_t *, + ap_session_t *, struct nat *)); + int (* apr_outpkt) __P((fr_info_t *, ip_t *, tcphdr_t *, + ap_session_t *, struct nat *)); +} aproxy_t; + +#define APR_DELETE 1 + + +extern ap_session_t *ap_sess_tab[AP_SESS_SIZE]; +extern aproxy_t ap_proxies[]; + +extern int ap_ok __P((ip_t *, tcphdr_t *, struct ipnat *)); +extern void ap_unload __P((void)); +extern void ap_free __P((aproxy_t *)); +extern void aps_free __P((ap_session_t *)); +extern int ap_check __P((ip_t *, tcphdr_t *, fr_info_t *, struct nat *)); +extern aproxy_t *ap_match __P((u_char, char *)); +extern void ap_expire __P((void)); + +#endif /* __IP_PROXY_H__ */ diff --git a/sys/netinet/ip_state.c b/sys/netinet/ip_state.c index 469129165f2..ce70b16f9a3 100644 --- a/sys/netinet/ip_state.c +++ b/sys/netinet/ip_state.c @@ -1,36 +1,49 @@ -/* $OpenBSD: ip_state.c,v 1.9 1997/06/23 19:03:51 kstailey Exp $ */ /* - * (C)opyright 1995 by Darren Reed. + * Copyright (C) 1995-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[] = "@(#)ip_state.c 1.8 6/5/96 (C) 1993-1995 Darren Reed"; -static char rcsid[] = "$DRId: ip_state.c,v 2.0.1.5 1997/04/13 22:29:18 darrenr Exp $"; -#endif +#if !defined(lint) +static const char sccsid[] = "@(#)ip_state.c 1.8 6/5/96 (C) 1993-1995 Darren Reed"; +static const char rcsid[] = "@(#)$Id: ip_state.c,v 1.10 1998/01/26 04:10:46 dgregor Exp $"; #endif -#if !defined(_KERNEL) && !defined(KERNEL) +#if !defined(_KERNEL) && !defined(KERNEL) && !defined(__KERNEL__) # include <stdlib.h> # include <string.h> +#else +# ifdef linux +# include <linux/kernel.h> +# include <linux/module.h> +# endif #endif #include <sys/errno.h> #include <sys/types.h> #include <sys/param.h> #include <sys/file.h> -#include <sys/ioctl.h> +#if defined(KERNEL) && (__FreeBSD_version >= 220000) +# include <sys/filio.h> +# include <sys/fcntl.h> +#else +# include <sys/ioctl.h> +#endif +#include <sys/time.h> #include <sys/uio.h> +#ifndef linux #include <sys/protosw.h> +#endif #include <sys/socket.h> -#ifdef _KERNEL +#if defined(_KERNEL) && !defined(linux) # include <sys/systm.h> #endif #if !defined(__SVR4) && !defined(__svr4__) -# include <sys/mbuf.h> +# ifndef linux +# include <sys/mbuf.h> +# endif #else +# include <sys/filio.h> # include <sys/byteorder.h> # include <sys/dditypes.h> # include <sys/stream.h> @@ -45,37 +58,31 @@ static char rcsid[] = "$DRId: ip_state.c,v 2.0.1.5 1997/04/13 22:29:18 darrenr E #include <netinet/in.h> #include <netinet/in_systm.h> #include <netinet/ip.h> -#include <netinet/ip_var.h> #include <netinet/tcp.h> -#include <netinet/tcp_fsm.h> +#ifndef linux +# include <netinet/ip_var.h> +# include <netinet/tcp_fsm.h> +#endif #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_nat.h" +#include "ip_frag.h" +#include "ip_proxy.h" #include "ip_state.h" #ifndef MIN #define MIN(a,b) (((a)<(b))?(a):(b)) #endif -#ifndef _KERNEL -int fr_tcpstate __P((register ipstate_t *, fr_info_t *, ip_t *, tcphdr_t *, - u_short, ipstate_t **)); -#else -int fr_tcpstate __P((register ipstate_t *, fr_info_t *, ip_t *, tcphdr_t *, - u_short)); -#endif - #define TCP_CLOSE (TH_FIN|TH_RST) ipstate_t *ips_table[IPSTATE_SIZE]; int ips_num = 0; ips_stat_t ips_stats; -#if SOLARIS +#if (SOLARIS || defined(__sgi)) && defined(_KERNEL) extern kmutex_t ipf_state; -# if !defined(_KERNEL) -#define bcopy(a,b,c) memmove(b,a,c) -# endif #endif @@ -90,8 +97,7 @@ u_long fr_tcpidletimeout = FIVE_DAYS, fr_icmptimeout = 120; -ips_stat_t * -fr_statetstats() +ips_stat_t *fr_statetstats() { ips_stats.iss_active = ips_num; ips_stats.iss_table = ips_table; @@ -99,19 +105,108 @@ fr_statetstats() } -#define PAIRS(s1,d1,s2,d2) ((((s1) == (s2)) && ((d1) == (d2))) ||\ - (((s1) == (d2)) && ((d1) == (s2)))) -#define IPPAIR(s1,d1,s2,d2) PAIRS((s1).s_addr, (d1).s_addr, \ - (s2).s_addr, (d2).s_addr) +/* + * flush state tables. two actions currently defined: + * which == 0 : flush all state table entries + * which == 1 : flush TCP connections which have started to close but are + * stuck for some reason. + */ +int fr_state_flush(which) +int which; +{ + register int i; + register ipstate_t *is, **isp; +#if defined(_KERNEL) && !SOLARIS + int s; +#endif + int delete, removed = 0; + + SPL_NET(s); + MUTEX_ENTER(&ipf_state); + for (i = 0; i < IPSTATE_SIZE; i++) + for (isp = &ips_table[i]; (is = *isp); ) { + delete = 0; + + switch (which) + { + case 0 : + delete = 1; + break; + case 1 : + if ((is->is_p == IPPROTO_TCP) && + (((is->is_state[0] <= TCPS_ESTABLISHED) && + (is->is_state[1] > TCPS_ESTABLISHED)) || + ((is->is_state[1] <= TCPS_ESTABLISHED) && + (is->is_state[0] > TCPS_ESTABLISHED)))) + delete = 1; + break; + } + + if (delete) { + *isp = is->is_next; + if (is->is_p == IPPROTO_TCP) + ips_stats.iss_fin++; + else + ips_stats.iss_expire++; +#ifdef IPFILTER_LOG + ipstate_log(is, ISL_FLUSH); +#endif + KFREE(is); + ips_num--; + removed++; + } else + isp = &is->is_next; + } + MUTEX_EXIT(&ipf_state); + SPL_X(s); + return removed; +} + + +int fr_state_ioctl(data, cmd, mode) +caddr_t data; +#if defined(__NetBSD__) || defined(__OpenBSD__) +u_long cmd; +#else +int cmd; +#endif +int mode; +{ + int arg, ret, error = 0; + + switch (cmd) + { + case SIOCIPFFL : + IRCOPY(data, (caddr_t)&arg, sizeof(arg)); + if (arg == 0 || arg == 1) { + ret = fr_state_flush(arg); + IWCOPY((caddr_t)&ret, data, sizeof(ret)); + } else + error = EINVAL; + break; + case SIOCGIPST : + IWCOPY((caddr_t)fr_statetstats(), data, sizeof(ips_stat_t)); + break; + case FIONREAD : +#ifdef IPFILTER_LOG + IWCOPY((caddr_t)&iplused[IPL_LOGSTATE], (caddr_t)data, + sizeof(iplused[IPL_LOGSTATE])); +#endif + break; + default : + return EINVAL; + } + return error; +} + /* * Create a new ipstate structure and hang it off the hash table. */ -int -fr_addstate(ip, fin, pass) - ip_t *ip; - fr_info_t *fin; - u_int pass; +int fr_addstate(ip, fin, pass) +ip_t *ip; +fr_info_t *fin; +u_int pass; { ipstate_t ips; register ipstate_t *is = &ips; @@ -174,14 +269,14 @@ fr_addstate(ip, fin, pass) is->is_dwin = is->is_swin; /* start them the same */ ips_stats.iss_tcp++; /* - * If we're creating state for a starting connectoin, start the + * If we're creating state for a starting connection, start the * timer on it as we'll never see an error if it fails to * connect. */ if ((tcp->th_flags & (TH_SYN|TH_ACK)) == TH_SYN) is->is_ack = 0; /* Trumpet WinSock 'ism */ - set_tcp_age(&is->is_age, is->is_state, ip, fin, - tcp->th_sport == is->is_sport); + fr_tcp_age(&is->is_age, is->is_state, ip, fin, + tcp->th_sport == is->is_sport); break; } case IPPROTO_UDP : @@ -198,7 +293,8 @@ fr_addstate(ip, fin, pass) return -1; } - if (!(is = (ipstate_t *)KMALLOC(sizeof(*is)))) { + KMALLOC(is, ipstate_t *, sizeof(*is)); + if (is == NULL) { ips_stats.iss_nomem++; return -1; } @@ -208,10 +304,17 @@ fr_addstate(ip, fin, pass) is->is_next = ips_table[hv]; ips_table[hv] = is; is->is_pass = pass; + is->is_pkts = 1; + is->is_bytes = ip->ip_len; if (pass & FR_LOGFIRST) is->is_pass &= ~(FR_LOGFIRST|FR_LOG); ips_num++; +#ifdef IPFILTER_LOG + ipstate_log(is, ISL_NEW); +#endif MUTEX_EXIT(&ipf_state); + if (fin->fin_fi.fi_fl & FI_FRAG) + ipfr_newfrag(ip, fin, pass ^ FR_KEEPSTATE); return 0; } @@ -221,19 +324,12 @@ fr_addstate(ip, fin, pass) * change timeout depending on whether new packet is a SYN-ACK returning for a * SYN or a RST or FIN which indicate time to close up shop. */ -int -fr_tcpstate(is, fin, ip, tcp, sport -#ifndef _KERNEL - ,isp) - ipstate_t **isp; -#else - ) -#endif - register ipstate_t *is; - fr_info_t *fin; - ip_t *ip; - tcphdr_t *tcp; - u_short sport; +int fr_tcpstate(is, fin, ip, tcp, sport) +register ipstate_t *is; +fr_info_t *fin; +ip_t *ip; +tcphdr_t *tcp; +u_short sport; { register int seqskew, ackskew; register u_short swin, dwin; @@ -251,16 +347,21 @@ fr_tcpstate(is, fin, ip, tcp, sport ack = source ? is->is_ack : is->is_seq; if (source) { + if (!is->is_seq) + /* + * Must be an outgoing SYN-ACK in reply to a SYN. + */ + is->is_seq = seq; seqskew = seq - is->is_seq; ackskew = ack - is->is_ack; } else { - seqskew = ack - is->is_seq; if (!is->is_ack) /* * Must be a SYN-ACK in reply to a SYN. */ is->is_ack = seq; ackskew = seq - is->is_ack; + seqskew = ack - is->is_seq; } /* @@ -295,11 +396,13 @@ fr_tcpstate(is, fin, ip, tcp, sport is->is_dwin = ntohs(tcp->th_win); } ips_stats.iss_hits++; + is->is_pkts++; + is->is_bytes += ip->ip_len; /* * Nearing end of connection, start timeout. */ - set_tcp_age(&is->is_age, is->is_state, ip, fin, - tcp->th_sport == is->is_sport); + fr_tcp_age(&is->is_age, is->is_state, ip, fin, + tcp->th_sport == is->is_sport); return 1; } return 0; @@ -309,10 +412,9 @@ fr_tcpstate(is, fin, ip, tcp, sport /* * Check if a packet has a registered state. */ -int -fr_checkstate(ip, fin) - ip_t *ip; - fr_info_t *fin; +int fr_checkstate(ip, fin) +ip_t *ip; +fr_info_t *fin; { register struct in_addr dst, src; register ipstate_t *is, **isp; @@ -354,6 +456,8 @@ fr_checkstate(ip, fin) is->is_icmp.ics_type != ic->icmp_type) continue; is->is_age = fr_icmptimeout; + is->is_pkts++; + is->is_bytes += ip->ip_len; ips_stats.iss_hits++; pass = is->is_pass; MUTEX_EXIT(&ipf_state); @@ -373,15 +477,12 @@ fr_checkstate(ip, fin) if ((is->is_p == pr) && PAIRS(sport, dport, is->is_sport, is->is_dport) && IPPAIR(src, dst, is->is_src, is->is_dst)) - if (fr_tcpstate(is, fin, ip, tcp, sport -#ifndef _KERNEL - , NULL -#endif - )) { + if (fr_tcpstate(is, fin, ip, tcp, sport)) { pass = is->is_pass; #ifdef _KERNEL MUTEX_EXIT(&ipf_state); #else + if (tcp->th_flags & TCP_CLOSE) { *isp = is->is_next; isp = &ips_table[hv]; @@ -410,6 +511,8 @@ fr_checkstate(ip, fin) PAIRS(sport, dport, is->is_sport, is->is_dport) && IPPAIR(src, dst, is->is_src, is->is_dst)) { ips_stats.iss_hits++; + is->is_pkts++; + is->is_bytes += ip->ip_len; is->is_age = fr_udptimeout; pass = is->is_pass; MUTEX_EXIT(&ipf_state); @@ -429,38 +532,35 @@ fr_checkstate(ip, fin) /* * Free memory in use by all state info. kept. */ -void -fr_stateunload() +void fr_stateunload() { register int i; register ipstate_t *is, **isp; - int s; MUTEX_ENTER(&ipf_state); - SPLNET(s); for (i = 0; i < IPSTATE_SIZE; i++) for (isp = &ips_table[i]; (is = *isp); ) { *isp = is->is_next; KFREE(is); } - SPLX(s); MUTEX_EXIT(&ipf_state); } /* - * Slowly expire held state for things like UDP and ICMP. Timeouts are set + * Slowly expire held state for thingslike UDP and ICMP. Timeouts are set * in expectation of this being called twice per second. */ -void -fr_timeoutstate() +void fr_timeoutstate() { register int i; register ipstate_t *is, **isp; +#if defined(_KERNEL) && !SOLARIS int s; +#endif + SPL_NET(s); MUTEX_ENTER(&ipf_state); - SPLNET(s); for (i = 0; i < IPSTATE_SIZE; i++) for (isp = &ips_table[i]; (is = *isp); ) if (is->is_age && !--is->is_age) { @@ -469,12 +569,15 @@ fr_timeoutstate() ips_stats.iss_fin++; else ips_stats.iss_expire++; +#ifdef IPFILTER_LOG + ipstate_log(is, ISL_EXPIRE); +#endif KFREE(is); ips_num--; } else isp = &is->is_next; - SPLX(s); MUTEX_EXIT(&ipf_state); + SPL_X(s); } @@ -482,13 +585,12 @@ fr_timeoutstate() * Original idea freom Pradeep Krishnan for use primarily with NAT code. * (pkrishna@netcom.com) */ -void -set_tcp_age(age, state, ip, fin, dir) - long *age; - u_char *state; - ip_t *ip; - fr_info_t *fin; - int dir; +void fr_tcp_age(age, state, ip, fin, dir) +u_long *age; +u_char *state; +ip_t *ip; +fr_info_t *fin; +int dir; { tcphdr_t *tcp = (tcphdr_t *)fin->fin_dp; u_char flags = tcp->th_flags; @@ -565,3 +667,38 @@ set_tcp_age(age, state, ip, fin, dir) break; } } + + +#ifdef IPFILTER_LOG +void ipstate_log(is, type) +struct ipstate *is; +u_short type; +{ + struct ipslog ipsl; + void *items[1]; + size_t sizes[1]; + int types[1]; + + ipsl.isl_pkts = is->is_pkts; + ipsl.isl_bytes = is->is_bytes; + ipsl.isl_src = is->is_src; + ipsl.isl_dst = is->is_dst; + ipsl.isl_p = is->is_p; + ipsl.isl_flags = is->is_flags; + ipsl.isl_type = type; + if (ipsl.isl_p == IPPROTO_TCP || ipsl.isl_p == IPPROTO_UDP) { + ipsl.isl_sport = is->is_sport; + ipsl.isl_dport = is->is_dport; + } else if (ipsl.isl_p == IPPROTO_ICMP) + ipsl.isl_itype = is->is_icmp.ics_type; + else { + ipsl.isl_ps.isl_filler[0] = 0; + ipsl.isl_ps.isl_filler[1] = 0; + } + items[0] = &ipsl; + sizes[0] = sizeof(ipsl); + types[0] = 0; + + (void) ipllog(IPL_LOGSTATE, 0, items, sizes, types, 1); +} +#endif diff --git a/sys/netinet/ip_state.h b/sys/netinet/ip_state.h index 09c730cda9e..76780f18e7f 100644 --- a/sys/netinet/ip_state.h +++ b/sys/netinet/ip_state.h @@ -1,13 +1,12 @@ -/* $OpenBSD: ip_state.h,v 1.5 1997/06/23 19:03:51 kstailey Exp $ */ /* - * (C)opyright 1995 by Darren Reed. + * Copyright (C) 1995-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. * * @(#)ip_state.h 1.3 1/12/96 (C) 1995 Darren Reed - * $DRId: ip_state.h,v 2.0.1.2 1997/03/20 10:20:53 darrenr Exp $ + * $Id: ip_state.h,v 1.6 1998/01/26 04:10:46 dgregor Exp $ */ #ifndef __IP_STATE_H__ #define __IP_STATE_H__ @@ -15,6 +14,12 @@ #define IPSTATE_SIZE 257 #define IPSTATE_MAX 2048 /* Maximum number of states held */ +#define PAIRS(s1,d1,s2,d2) ((((s1) == (s2)) && ((d1) == (d2))) ||\ + (((s1) == (d2)) && ((d1) == (s2)))) +#define IPPAIR(s1,d1,s2,d2) PAIRS((s1).s_addr, (d1).s_addr, \ + (s2).s_addr, (d2).s_addr) + + typedef struct udpstate { u_short us_sport; u_short us_dport; @@ -40,6 +45,8 @@ typedef struct ipstate { struct ipstate *is_next; u_long is_age; u_int is_pass; + U_QUAD_T is_pkts; + U_QUAD_T is_bytes; struct in_addr is_src; struct in_addr is_dst; u_char is_p; @@ -64,6 +71,31 @@ typedef struct ipstate { #define TH_OPENING (TH_SYN|TH_ACK) + +typedef struct ipslog { + U_QUAD_T isl_pkts; + U_QUAD_T isl_bytes; + struct in_addr isl_src; + struct in_addr isl_dst; + u_char isl_p; + u_char isl_flags; + u_short isl_type; + union { + u_short isl_filler[2]; + u_short isl_ports[2]; + u_short isl_icmp; + } isl_ps; +} ipslog_t; + +#define isl_sport isl_ps.isl_ports[0] +#define isl_dport isl_ps.isl_ports[1] +#define isl_itype isl_ps.isl_icmp + +#define ISL_NEW 0 +#define ISL_EXPIRE 0xffff +#define ISL_FLUSH 0xfffe + + typedef struct ips_stat { u_long iss_hits; u_long iss_miss; @@ -75,15 +107,32 @@ typedef struct ips_stat { u_long iss_expire; u_long iss_fin; u_long iss_active; + u_long iss_logged; + u_long iss_logfail; ipstate_t **iss_table; } ips_stat_t; -extern ips_stat_t *fr_statetstats __P((void)); -extern int fr_addstate __P((ip_t *, fr_info_t *, u_int)); -extern int fr_checkstate __P((ip_t *, fr_info_t *)); -extern void fr_timeoutstate __P((void)); -extern void set_tcp_age __P((long *, u_char *, ip_t *, fr_info_t *, int)); -# ifdef _KERNEL -extern void fr_stateunload __P((void)); -# endif + +extern u_long fr_tcpidletimeout; +extern u_long fr_tcpclosewait; +extern u_long fr_tcplastack; +extern u_long fr_tcptimeout; +extern u_long fr_tcpclosed; +extern u_long fr_udptimeout; +extern u_long fr_icmptimeout; +extern int fr_tcpstate __P((ipstate_t *, fr_info_t *, ip_t *, + tcphdr_t *, u_short)); +extern ips_stat_t *fr_statetstats __P((void)); +extern int fr_addstate __P((ip_t *, fr_info_t *, u_int)); +extern int fr_checkstate __P((ip_t *, fr_info_t *)); +extern void fr_timeoutstate __P((void)); +extern void fr_tcp_age __P((u_long *, u_char *, ip_t *, fr_info_t *, int)); +extern int fr_state_flush __P((int)); +extern void fr_stateunload __P((void)); +extern void ipstate_log __P((struct ipstate *, u_short)); +#if defined(__NetBSD__) || defined(__OpenBSD__) +extern int fr_state_ioctl __P((caddr_t, u_long, int)); +#else +extern int fr_state_ioctl __P((caddr_t, int, int)); +#endif #endif /* __IP_STATE_H__ */ |