diff options
author | peter <peter@FreeBSD.org> | 1997-03-02 15:49:41 +0000 |
---|---|---|
committer | peter <peter@FreeBSD.org> | 1997-03-02 15:49:41 +0000 |
commit | f0c7769b1f466a6462b72c64c304a829acc48555 (patch) | |
tree | 398841aa120f249bb97fd26c4bbf90be8ca5d4e1 /sys/netinet | |
download | FreeBSD-src-f0c7769b1f466a6462b72c64c304a829acc48555.zip FreeBSD-src-f0c7769b1f466a6462b72c64c304a829acc48555.tar.gz |
Replacement import of ipfilter 3.1.7 components used in kernel.
(This is to repair the vendor branching)
Diffstat (limited to 'sys/netinet')
-rw-r--r-- | sys/netinet/fil.c | 762 | ||||
-rw-r--r-- | sys/netinet/ip_compat.h | 342 | ||||
-rw-r--r-- | sys/netinet/ip_fil.c | 885 | ||||
-rw-r--r-- | sys/netinet/ip_fil.h | 293 | ||||
-rw-r--r-- | sys/netinet/ip_frag.c | 278 | ||||
-rw-r--r-- | sys/netinet/ip_frag.h | 47 | ||||
-rw-r--r-- | sys/netinet/ip_nat.c | 886 | ||||
-rw-r--r-- | sys/netinet/ip_nat.h | 118 | ||||
-rw-r--r-- | sys/netinet/ip_state.c | 536 | ||||
-rw-r--r-- | sys/netinet/ip_state.h | 86 |
10 files changed, 4233 insertions, 0 deletions
diff --git a/sys/netinet/fil.c b/sys/netinet/fil.c new file mode 100644 index 0000000..de776f9 --- /dev/null +++ b/sys/netinet/fil.c @@ -0,0 +1,762 @@ +/* + * (C)opyright 1993-1996 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[] = "@(#)fil.c 1.36 6/5/96 (C) 1993-1996 Darren Reed"; +static char rcsid[] = "$Id: fil.c,v 2.0.1.4 1997/02/04 13:59:41 darrenr Exp $"; +#endif + +#include <sys/errno.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/file.h> +#include <sys/ioctl.h> +#if defined(_KERNEL) || defined(KERNEL) +# include <sys/systm.h> +#else +# include <stdio.h> +# include <string.h> +#endif +#include <sys/uio.h> +#if !defined(__SVR4) && !defined(__svr4__) +# include <sys/mbuf.h> +#else +# include <sys/byteorder.h> +# include <sys/dditypes.h> +# include <sys/stream.h> +#endif +#include <sys/protosw.h> +#include <sys/socket.h> +#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> +#include <netinet/ip_var.h> +#include <netinet/tcp.h> +#include <netinet/udp.h> +#include <netinet/tcpip.h> +#include <netinet/ip_icmp.h> +#include "ip_fil.h" +#include "ip_compat.h" +#include "ip_nat.h" +#include "ip_frag.h" +#include "ip_state.h" +#ifndef MIN +#define MIN(a,b) (((a)<(b))?(a):(b)) +#endif + +#ifndef _KERNEL +#include "ipf.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) +# 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(); +# if SOLARIS +extern int icmp_error(), ipfr_fastroute(); +extern kmutex_t ipf_mutex, ipl_mutex; +# else +extern void ipfr_fastroute(); +# endif +extern int ipl_unreach, ipllog(); +#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(ip) +# if BSD < 199103 +# 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) \ + icmp_error(b, t, c, (src).s_addr, if) +# endif +#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 } }; +int fr_flags = 0, fr_active = 0; + +fr_info_t frcache[2]; + + +/* + * bit values for identifying presence of individual IP options + */ +struct optlist ipopts[20] = { + { IPOPT_NOP, 0x000001 }, + { IPOPT_RR, 0x000002 }, + { IPOPT_ZSU, 0x000004 }, + { IPOPT_MTUP, 0x000008 }, + { IPOPT_MTUR, 0x000010 }, + { IPOPT_ENCODE, 0x000020 }, + { IPOPT_TS, 0x000040 }, + { IPOPT_TR, 0x000080 }, + { IPOPT_SECURITY, 0x000100 }, + { IPOPT_LSRR, 0x000200 }, + { IPOPT_E_SEC, 0x000400 }, + { IPOPT_CIPSO, 0x000800 }, + { IPOPT_SATID, 0x001000 }, + { IPOPT_SSRR, 0x002000 }, + { IPOPT_ADDEXT, 0x004000 }, + { IPOPT_VISA, 0x008000 }, + { IPOPT_IMITD, 0x010000 }, + { IPOPT_EIP, 0x020000 }, + { IPOPT_FINN, 0x040000 }, + { 0, 0x000000 } +}; + +/* + * bit values for identifying presence of individual IP security options + */ +struct optlist secopt[8] = { + { IPSO_CLASS_RES4, 0x01 }, + { IPSO_CLASS_TOPS, 0x02 }, + { IPSO_CLASS_SECR, 0x04 }, + { IPSO_CLASS_RES3, 0x08 }, + { IPSO_CLASS_CONF, 0x10 }, + { IPSO_CLASS_UNCL, 0x20 }, + { IPSO_CLASS_RES2, 0x40 }, + { IPSO_CLASS_RES1, 0x80 } +}; + + +/* + * 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; +{ + struct optlist *op; + tcphdr_t *tcp; + fr_ip_t *fi = &fin->fin_fi; + u_short optmsk = 0, secmsk = 0, auth = 0; + int i, mv, ol, off; + u_char *s, opt; + + fin->fin_fr = NULL; + fin->fin_tcpf = 0; + fin->fin_data[0] = 0; + fin->fin_data[1] = 0; + fin->fin_rule = -1; +#ifdef _KERNEL + fin->fin_icode = ipl_unreach; +#endif + fi->fi_v = ip->ip_v; + fi->fi_tos = ip->ip_tos; + fin->fin_hlen = hlen; + fin->fin_dlen = ip->ip_len - hlen; + tcp = (tcphdr_t *)((char *)ip + hlen); + fin->fin_dp = (void *)tcp; + (*(((u_short *)fi) + 1)) = (*(((u_short *)ip) + 4)); + (*(((u_long *)fi) + 1)) = (*(((u_long *)ip) + 3)); + (*(((u_long *)fi) + 2)) = (*(((u_long *)ip) + 4)); + + fi->fi_fl = (hlen > sizeof(struct ip)) ? FI_OPTIONS : 0; + off = (ip->ip_off & 0x1fff) << 3; + if (ip->ip_off & 0x3fff) + fi->fi_fl |= FI_FRAG; + switch (ip->ip_p) + { + case IPPROTO_ICMP : + if ((!IPMINLEN(ip, icmp) && !off) || + (off && off < sizeof(struct icmp))) + fi->fi_fl |= FI_SHORT; + if (fin->fin_dlen > 1) + fin->fin_data[0] = *(u_short *)tcp; + break; + case IPPROTO_TCP : + fi->fi_fl |= FI_TCPUDP; + if ((!IPMINLEN(ip, tcphdr) && !off) || + (off && off < sizeof(struct tcphdr))) + fi->fi_fl |= FI_SHORT; + if (!(fi->fi_fl & FI_SHORT) && !off) + fin->fin_tcpf = tcp->th_flags; + goto getports; + case IPPROTO_UDP : + fi->fi_fl |= FI_TCPUDP; + if ((!IPMINLEN(ip, udphdr) && !off) || + (off && off < sizeof(struct udphdr))) + fi->fi_fl |= FI_SHORT; +getports: + if (!off && (fin->fin_dlen > 3)) { + fin->fin_data[0] = ntohs(tcp->th_sport); + fin->fin_data[1] = ntohs(tcp->th_dport); + } + break; + default : + break; + } + + + for (s = (u_char *)(ip + 1), hlen -= sizeof(*ip); hlen; ) { + if (!(opt = *s)) + break; + ol = (opt == IPOPT_NOP) ? 1 : (int)*(s+1); + if (opt > 1 && (ol < 2 || ol > hlen)) + break; + for (i = 9, mv = 4; mv >= 0; ) { + op = ipopts + i; + if (opt == (u_char)op->ol_val) { + optmsk |= op->ol_bit; + if (opt == IPOPT_SECURITY) { + struct optlist *sp; + u_char sec; + int j, m; + + sec = *(s + 2); /* classification */ + for (j = 3, m = 2; m >= 0; ) { + sp = secopt + j; + if (sec == sp->ol_val) { + secmsk |= sp->ol_bit; + auth = *(s + 3); + auth *= 256; + auth += *(s + 4); + break; + } + if (sec < sp->ol_val) + j -= m--; + else + j += m--; + } + } + break; + } + if (opt < op->ol_val) + i -= mv--; + else + i += mv--; + } + hlen -= ol; + s += ol; + } + if (auth && !(auth & 0x0100)) + auth &= 0xff00; + fi->fi_optmsk = optmsk; + fi->fi_secmsk = secmsk; + fi->fi_auth = auth; +} + + +/* + * 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; +{ + register u_short po, tup; + register char i; + register int err = 1; + + /* + * Both ports should *always* be in the first fragment. + * So far, I cannot find any cases where they can not be. + * + * compare destination ports + */ + if ((i = (int)fr->fr_dcmp)) { + po = fr->fr_dport; + tup = fin->fin_data[1]; + /* + * Do opposite test to that required and + * continue if that succeeds. + */ + if (!--i && tup != po) /* EQUAL */ + err = 0; + else if (!--i && tup == po) /* NOTEQUAL */ + err = 0; + else if (!--i && tup >= po) /* LESSTHAN */ + err = 0; + else if (!--i && tup <= po) /* GREATERTHAN */ + err = 0; + else if (!--i && tup > po) /* LT or EQ */ + err = 0; + else if (!--i && tup < po) /* GT or EQ */ + err = 0; + else if (!--i && /* Out of range */ + (tup >= po && tup <= fr->fr_dtop)) + err = 0; + else if (!--i && /* In range */ + (tup <= po || tup >= fr->fr_dtop)) + err = 0; + } + /* + * compare source ports + */ + if (err && (i = (int)fr->fr_scmp)) { + po = fr->fr_sport; + tup = fin->fin_data[0]; + if (!--i && tup != po) + err = 0; + else if (!--i && tup == po) + err = 0; + else if (!--i && tup >= po) + err = 0; + else if (!--i && tup <= po) + err = 0; + else if (!--i && tup > po) + err = 0; + else if (!--i && tup < po) + err = 0; + else if (!--i && /* Out of range */ + (tup >= po && tup <= fr->fr_stop)) + err = 0; + else if (!--i && /* In range */ + (tup <= po || tup >= fr->fr_stop)) + err = 0; + } + + /* + * If we don't have all the TCP/UDP header, then how can we + * expect to do any sort of match on it ? If we were looking for + * TCP flags, then NO match. If not, then match (which should + * satisfy the "short" class too). + */ + if (err && (fin->fin_fi.fi_p == IPPROTO_TCP)) { + if (fin->fin_fi.fi_fl & FI_SHORT) + return !(fr->fr_tcpf | fr->fr_tcpfm); + /* + * Match the flags ? If not, abort this match. + */ + if (fr->fr_tcpf && + fr->fr_tcpf != (fin->fin_tcpf & fr->fr_tcpfm)) { + FR_DEBUG(("f. %#x & %#x != %#x\n", fin->fin_tcpf, + fr->fr_tcpfm, fr->fr_tcpf)); + err = 0; + } + } + return err; +} + +/* + * Check the input/output list of rules for a match and result. + * 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; +{ + register struct frentry *fr; + register fr_ip_t *fi = &fin->fin_fi; + int rulen, portcmp = 0, off; + + fr = fin->fin_fr; + fin->fin_fr = NULL; + fin->fin_rule = 0; + off = ip->ip_off & 0x1fff; + pass |= (fi->fi_fl << 20); + + if ((fi->fi_fl & FI_TCPUDP) && (fin->fin_dlen > 3) && !off) + portcmp = 1; + + for (rulen = 0; fr; fr = fr->fr_next, rulen++) { + /* + * In all checks below, a null (zero) value in the + * filter struture is taken to mean a wildcard. + * + * check that we are working for the right interface + */ +#ifdef _KERNEL + if (fr->fr_ifa && fr->fr_ifa != fin->fin_ifp) + continue; +#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)) + continue; + FR_VERBOSE((":i")); +#endif + { + register u_long *ld, *lm, *lip; + register int i; + + lip = (u_long *)fi; + lm = (u_long *)&fr->fr_mip; + ld = (u_long *)&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]); + FR_IFDEBUG(i,continue,("1. %#08x & %#08x != %#08x\n", + lip[1], lm[1], ld[1])); + i |= ((lip[2] & lm[2]) != ld[2]); + FR_IFDEBUG(i,continue,("2. %#08x & %#08x != %#08x\n", + lip[2], lm[2], ld[2])); + i |= ((lip[3] & lm[3]) != ld[3]); + FR_IFDEBUG(i,continue,("3. %#08x & %#08x != %#08x\n", + lip[3], lm[3], ld[3])); + i |= ((lip[4] & lm[4]) != ld[4]); + FR_IFDEBUG(i,continue,("4. %#08x & %#08x != %#08x\n", + lip[4], lm[4], ld[4])); + if (i) + continue; + } + + /* + * If a fragment, then only the first has what we're looking + * for here... + */ + if (fi->fi_fl & FI_TCPUDP) { + if (portcmp) { + if (!fr_tcpudpchk(fr, fin)) + continue; + } else if (fr->fr_dcmp || fr->fr_scmp || fr->fr_tcpf || + fr->fr_tcpfm) + continue; + } else if (fi->fi_p == IPPROTO_ICMP) { + if (!off && (fin->fin_dlen > 1)) { + if ((fin->fin_data[0] & fr->fr_icmpm) != + fr->fr_icmp) { + FR_DEBUG(("i. %#x & %#x != %#x\n", + fin->fin_data[0], + fr->fr_icmpm, fr->fr_icmp)); + continue; + } + } else if (fr->fr_icmpm || fr->fr_icmp) + continue; + } + FR_VERBOSE(("*")); + /* + * Just log this packet... + */ + 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)) + frstats[fin->fin_out].fr_skip++; + frstats[fin->fin_out].fr_pkl++; + } +#endif /* IPFILTER_LOG */ + FR_DEBUG(("pass %#x\n", pass)); + fr->fr_hits++; + if (pass & FR_ACCOUNT) + fr->fr_bytes += ip->ip_len; + else + fin->fin_icode = fr->fr_icode; + fin->fin_rule = rulen; + fin->fin_fr = fr; + if (pass & FR_QUICK) + break; + } + return pass; +} + + +/* + * frcheck - filter check + * 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 +#else +) +#endif +ip_t *ip; +int hlen; +struct ifnet *ifp; +int out; +{ + /* + * The above really sucks, but short of writing a diff + */ + fr_info_t frinfo, *fc; + register fr_info_t *fin = &frinfo; + frentry_t *fr = NULL; + int pass, changed; + +#if !defined(__SVR4) && !defined(__svr4__) && defined(_KERNEL) + register struct mbuf *m = *mp; + struct mbuf *mc = NULL; + + 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); + + if (up > m->m_len) { + 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 *); + } + } + } +#endif +#if SOLARIS && defined(_KERNEL) + mblk_t *mc = NULL, *m = qif->qf_m; +#endif + fr_makefrip(hlen, ip, fin); + fin->fin_ifp = ifp; + fin->fin_out = out; + + MUTEX_ENTER(&ipf_mutex); + if (!out) { + changed = ip_natin(ip, hlen, fin); + if ((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++; + 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 = FR_NOMATCH; + if ((fin->fin_fr = ipfilter[out][fr_active])) + pass = FR_SCANLIST(FR_NOMATCH, ip, fin, m); + bcopy((char *)fin, (char *)fc, FI_CSIZE); + if (pass & FR_NOMATCH) { + frstats[out].fr_nom++; +#ifdef NOMATCH + pass |= NOMATCH; +#endif + } + } + fr = fin->fin_fr; + + 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++; + } + if (pass & FR_KEEPSTATE) { + if (fr_addstate(ip, fin, pass) == -1) + frstats[out].fr_bads++; + else + frstats[out].fr_ads++; + } + } + + if (fr && fr->fr_func) + pass = (*fr->fr_func)(pass, ip, fin); + + if (out) { + if ((fin->fin_fr = ipacct[1][fr_active]) && + (FR_SCANLIST(FR_NOMATCH, ip, fin, m) & FR_ACCOUNT)) + frstats[1].fr_acct++; + fin->fin_fr = NULL; + changed = ip_natout(ip, hlen, fin); + } + fin->fin_fr = fr; + MUTEX_EXIT(&ipf_mutex); + +#ifdef IPFILTER_LOG + if ((fr_flags & FF_LOGGING) || (pass & FR_LOGMASK)) { + if ((fr_flags & FF_LOGNOMATCH) && (pass & FR_NOMATCH)) { + pass |= FF_LOGNOMATCH; + frstats[out].fr_npkl++; + goto logit; + } else if (((pass & FR_LOGMASK) == FR_LOGP) || + ((pass & FR_PASS) && (fr_flags & FF_LOGPASS))) { + if ((pass & FR_LOGMASK) != FR_LOGP) + pass |= FF_LOGPASS; + frstats[out].fr_ppkl++; + goto logit; + } else if (((pass & FR_LOGMASK) == FR_LOGB) || + ((pass & FR_BLOCK) && (fr_flags & FF_LOGBLOCK))) { + if ((pass & FR_LOGMASK) != FR_LOGB) + pass |= FF_LOGBLOCK; + frstats[out].fr_bpkl++; +logit: + if (!ipllog(pass, ip, fin, m)) { + frstats[out].fr_skip++; + if ((pass & (FR_PASS|FR_LOGORBLOCK)) == + (FR_PASS|FR_LOGORBLOCK)) + pass ^= FR_PASS|FR_BLOCK; + } + } + } +#endif /* IPFILTER_LOG */ + + if (pass & FR_PASS) + frstats[out].fr_pass++; + else if (pass & FR_BLOCK) { + frstats[out].fr_block++; + /* + * Should we return an ICMP packet to indicate error + * status passing through the packet filter ? + */ +#ifdef _KERNEL + if (pass & FR_RETICMP) { +# if SOLARIS + ICMP_ERROR(q, ip, ICMP_UNREACH, fin->fin_icode, + qif, ip->ip_src); +# else + ICMP_ERROR(m, ip, ICMP_UNREACH, fin->fin_icode, + ifp, ip->ip_src); + m = NULL; /* freed by icmp_error() */ +# endif + + frstats[0].fr_ret++; + } else if ((pass & FR_RETRST) && + !(fin->fin_fi.fi_fl & FI_SHORT)) { + if (SEND_RESET(ip, qif, q) == 0) + frstats[1].fr_ret++; + } +#else + if (pass & FR_RETICMP) { + verbose("- ICMP unreachable sent\n"); + frstats[0].fr_ret++; + } else if ((pass & FR_RETRST) && + !(fin->fin_fi.fi_fl & FI_SHORT)) { + verbose("- TCP RST sent\n"); + frstats[1].fr_ret++; + } +#endif + } +#ifdef _KERNEL +# if !SOLARIS + if (pass & FR_DUP) + mc = m_copy(m, 0, M_COPYALL); + if (fr) { + frdest_t *fdp = &fr->fr_tif; + + if ((pass & FR_FASTROUTE) || + (fdp->fd_ifp && fdp->fd_ifp != (struct ifnet *)-1)) { + ipfr_fastroute(m, fin, fdp); + m = *mp = NULL; + pass = 0; + } + if (mc) + ipfr_fastroute(mc, fin, &fr->fr_dif); + } + if (!(pass & FR_PASS) && m) + m_freem(m); + return (pass & FR_PASS) ? 0 : -1; +# else + if (pass & FR_DUP) + mc = dupmsg(m); + if (fr) { + frdest_t *fdp = &fr->fr_tif; + + if ((pass & FR_FASTROUTE) || + (fdp->fd_ifp && fdp->fd_ifp != (struct ifnet *)-1)) { + ipfr_fastroute(qif, ip, m, mp, fin, fdp); + m = *mp = NULL; + } + if (mc) + ipfr_fastroute(qif, ip, mc, mp, fin, &fr->fr_dif); + } + return (pass & FR_PASS) ? changed : -1; +# endif +#else + if (pass & FR_NOMATCH) + return 1; + if (pass & FR_PASS) + return 0; + return -1; +#endif +} + + +#ifdef IPFILTER_LOG +# if !(defined(_KERNEL)) +static void ipllog() +{ + verbose("l"); +} +# endif + + +int fr_copytolog(buf, len) +char *buf; +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; + } + if (len && tail) { + clen = MIN(tail, len); + bcopy(buf, iplh, clen); + len -= clen; + iplh += clen; + } + return len; +} +#endif diff --git a/sys/netinet/ip_compat.h b/sys/netinet/ip_compat.h new file mode 100644 index 0000000..5a36cc3 --- /dev/null +++ b/sys/netinet/ip_compat.h @@ -0,0 +1,342 @@ +/* + * (C)opyright 1993, 1994, 1995 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 + * $Id: ip_compat.h,v 2.0.1.4 1997/02/04 14:24:25 darrenr Exp $ + */ + +#ifndef __IP_COMPAT_H_ +#define __IP_COMPAT_H__ + +#ifndef SOLARIS +#define SOLARIS (defined(sun) && (defined(__svr4__) || defined(__SVR4))) +#endif +#if SOLARIS +#define MTYPE(m) ((m)->b_datap->db_type) +#endif +#define IPMINLEN(i, h) ((i)->ip_len >= ((i)->ip_hl * 4 + sizeof(struct h))) + +#ifndef IP_OFFMASK +#define IP_OFFMASK 0x1fff +#endif + +#ifndef MAX +#define MAX(a,b) (((a) > (b)) ? (a) : (b)) +#endif + +/* + * Security Options for Intenet Protocol (IPSO) as defined in RFC 1108. + * + * Basic Option + * + * 00000001 - (Reserved 4) + * 00111101 - Top Secret + * 01011010 - Secret + * 10010110 - Confidential + * 01100110 - (Reserved 3) + * 11001100 - (Reserved 2) + * 10101011 - Unclassified + * 11110001 - (Reserved 1) + */ +#define IPSO_CLASS_RES4 0x01 +#define IPSO_CLASS_TOPS 0x3d +#define IPSO_CLASS_SECR 0x5a +#define IPSO_CLASS_CONF 0x96 +#define IPSO_CLASS_RES3 0x66 +#define IPSO_CLASS_RES2 0xcc +#define IPSO_CLASS_UNCL 0xab +#define IPSO_CLASS_RES1 0xf1 + +#define IPSO_AUTH_GENSER 0x80 +#define IPSO_AUTH_ESI 0x40 +#define IPSO_AUTH_SCI 0x20 +#define IPSO_AUTH_NSA 0x10 +#define IPSO_AUTH_DOE 0x08 +#define IPSO_AUTH_UN 0x06 +#define IPSO_AUTH_FTE 0x01 + +/* + * IP option #defines + */ +/*#define IPOPT_RR 7 */ +#define IPOPT_ZSU 10 /* ZSU */ +#define IPOPT_MTUP 11 /* MTUP */ +#define IPOPT_MTUR 12 /* MTUR */ +#define IPOPT_ENCODE 15 /* ENCODE */ +/*#define IPOPT_TS 68 */ +#define IPOPT_TR 82 /* TR */ +/*#define IPOPT_SECURITY 130 */ +/*#define IPOPT_LSRR 131 */ +#define IPOPT_E_SEC 133 /* E-SEC */ +#define IPOPT_CIPSO 134 /* CIPSO */ +/*#define IPOPT_SATID 136 */ +#ifndef IPOPT_SID +# define IPOPT_SID IPOPT_SATID +#endif +/*#define IPOPT_SSRR 137 */ +#define IPOPT_ADDEXT 147 /* ADDEXT */ +#define IPOPT_VISA 142 /* VISA */ +#define IPOPT_IMITD 144 /* IMITD */ +#define IPOPT_EIP 145 /* EIP */ +#define IPOPT_FINN 205 /* FINN */ + + +/* + * Build some macros and #defines to enable the same code to compile anywhere + * Well, that's the idea, anyway :-) + */ +#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) ; +# ifndef linux +# 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 /* SOLARIS */ + +# ifdef sun +# if defined(__svr4__) || defined(__SVR4) +# define GETUNIT(n) get_unit((n)) +# else +# include <sys/kmem_alloc.h> +# define GETUNIT(n) ifunit((n), IFNAMSIZ) +# endif +# else +# define GETUNIT(n) ifunit((n)) +# endif /* sun */ + +# if defined(sun) && !defined(linux) +# define UIOMOVE(a,b,c,d) uiomove(a,b,c,d) +# define SLEEP(id, n) sleep((id), PZERO+1) +# 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 GET_MINOR(x) getminor(x) +# else +# define KMALLOC(x) new_kmem_alloc((x), KMEM_NOSLEEP) +# endif /* __svr4__ */ +# endif /* sun && !linux */ +# ifndef GET_MINOR +# define GET_MINOR(x) minor(x) +# endif +# if BSD >= 199306 || defined(__FreeBSD__) +# include <vm/vm.h> +# if !defined(__FreeBSD__) +# include <vm/vm_extern.h> +# include <sys/proc.h> +extern vm_map_t kmem_map; +# else +# 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))) +*/ +# ifdef M_PFIL +# define KMALLOC(x) malloc((x), M_PFIL, M_NOWAIT) +# define KFREE(x) FREE((x), M_PFIL) +# else +# define KMALLOC(x) malloc((x), M_TEMP, M_NOWAIT) +# define KFREE(x) FREE((x), M_TEMP) +# endif +# define UIOMOVE(a,b,c,d) uiomove(a,b,d) +# define SLEEP(id, n) tsleep((id), PPAUSE|PCATCH, n, 0) +# endif /* BSD */ +# if defined(NetBSD1_0) && (NetBSD1_0 > 1) +# define SPLNET(x) x = splsoftnet() +# else +# if !SOLARIS +# define SPLNET(x) x = splnet() +# define SPLX(x) (void) splx(x) +# endif +# endif +#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)) +# endif +#endif /* KERNEL */ + +#ifdef linux +# define ICMP_UNREACH ICMP_DEST_UNREACH +# define ICMP_SOURCEQUENCH ICMP_SOURCE_QUENCH +# define ICMP_TIMXCEED ICMP_TIME_EXCEEDED +# define ICMP_PARAMPROB ICMP_PARAMETERPROB + +# define TH_FIN 0x01 +# define TH_SYN 0x02 +# define TH_RST 0x04 +# define TH_PUSH 0x08 +# define TH_ACK 0x10 +# define TH_URG 0x20 + +typedef struct { + __u16 th_sport; + __u16 th_dport; + __u32 th_seq; + __u32 th_ack; + __u8 th_x; + __u8 th_flags; + __u16 th_win; + __u16 th_sum; + __u16 th_urp; +} tcphdr_t; + +typedef struct { + __u16 uh_sport; + __u16 uh_dport; + __u16 uh_ulen; + __u16 uh_sun; +} udphdr_t; + +typedef struct { +# if defined(__i386__) || defined(__MIPSEL__) || defined(__alpha__) ||\ + defined(vax) + __u8 ip_hl:4; + __u8 ip_v:4; +# else + __u8 ip_hl:4; + __u8 ip_v:4; +# endif + __u8 ip_tos; + __u16 ip_len; + __u16 ip_id; + __u16 ip_off; + __u8 ip_ttl; + __u8 ip_p; + __u16 ip_sum; + struct in_addr ip_src; + struct in_addr ip_dst; +} ip_t; + +/* + * Structure of an icmp header. + */ +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 */ + union { + u_char ih_pptr; /* ICMP_PARAMPROB */ + struct in_addr ih_gwaddr; /* ICMP_REDIRECT */ + struct ih_idseq { + n_short icd_id; + n_short icd_seq; + } ih_idseq; + int ih_void; + } icmp_hun; +# define icmp_pptr icmp_hun.ih_pptr +# define icmp_gwaddr icmp_hun.ih_gwaddr +# define icmp_id icmp_hun.ih_idseq.icd_id +# define icmp_seq icmp_hun.ih_idseq.icd_seq +# define icmp_void icmp_hun.ih_void + union { + struct id_ts { + n_time its_otime; + n_time its_rtime; + n_time its_ttime; + } id_ts; + struct id_ip { + ip_t idi_ip; + /* options and then 64 bits of data */ + } id_ip; + u_long id_mask; + char id_data[1]; + } icmp_dun; +# define icmp_otime icmp_dun.id_ts.its_otime +# define icmp_rtime icmp_dun.id_ts.its_rtime +# define icmp_ttime icmp_dun.id_ts.its_ttime +# define icmp_ip icmp_dun.id_ip.idi_ip +# define icmp_mask icmp_dun.id_mask +# define icmp_data icmp_dun.id_data +}; + +struct ipovly { + caddr_t ih_next, ih_prev; /* for protocol sequence q's */ + u_char ih_x1; /* (unused) */ + u_char ih_pr; /* protocol */ + short ih_len; /* protocol length */ + struct in_addr ih_src; /* source internet address */ + struct in_addr ih_dst; /* destination internet address */ +}; + +# define SPLX(x) (void) +# define SPLNET(x) (void) + +# define bcopy(a,b,c) memmove(b,a,c) +# define bcmp(a,b,c) memcmp(a,b,c) + +# define UNITNAME(n) dev_get((n)) +# define ifnet device + +# 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))); \ + if (!error) \ + memcpy_fromfs((b), (a), (c)); \ + } +# define IWCOPY(a,b,c) { \ + error = verify_area(VERIFY_WRITE, \ + (b) ,sizeof((b))); \ + if (!error) \ + memcpy_tofs((b), (a), (c)); \ + } +#else +typedef struct tcphdr tcphdr_t; +typedef struct udphdr udphdr_t; +typedef struct icmp icmphdr_t; +typedef struct ip ip_t; +#endif /* linux */ + +#endif /* __IP_COMPAT_H__ */ diff --git a/sys/netinet/ip_fil.c b/sys/netinet/ip_fil.c new file mode 100644 index 0000000..7a24434 --- /dev/null +++ b/sys/netinet/ip_fil.c @@ -0,0 +1,885 @@ +/* + * (C)opyright 1993,1994,1995 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[] = "$Id: ip_fil.c,v 2.0.1.5 1997/01/29 13:41:45 darrenr Exp $"; +#endif + +#include <sys/errno.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/file.h> +#include <sys/ioctl.h> +#include <sys/systm.h> +#include <sys/uio.h> +#include <sys/mbuf.h> +#include <sys/protosw.h> +#include <sys/socket.h> + +#include <net/if.h> +#ifdef sun +#include <net/af.h> +#endif +#include <net/route.h> +#include <netinet/in.h> +#include <netinet/in_var.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netinet/ip_var.h> +#include <netinet/tcp.h> +#include <netinet/udp.h> +#include <netinet/tcpip.h> +#include <netinet/ip_icmp.h> +#include <syslog.h> +#include "ip_fil.h" +#include "ip_compat.h" +#include "ip_frag.h" +#include "ip_nat.h" +#include "ip_state.h" +#ifndef MIN +#define MIN(a,b) (((a)<(b))?(a):(b)) +#endif + +extern fr_flags, fr_active; +extern struct protosw inetsw[]; +extern int (*fr_checkp)(); +#if BSD < 199306 +extern int ipfr_slowtimer(); +static int (*fr_saveslowtimo)(); +extern int tcp_ttl; +#else +extern void ipfr_slowtimer(); +static void (*fr_saveslowtimo)(); +#endif + +int ipl_inited = 0; +int ipl_unreach = ICMP_UNREACH_FILTER; +int send_reset(); + +#ifdef IPFILTER_LOG +# define LOGSIZE 8192 +int ipllog(); +char iplbuf[LOGSIZE]; +caddr_t iplh = iplbuf, iplt = iplbuf; +static int iplused = 0; +#endif /* IPFILTER_LOG */ +static void frflush(); +static int frrequest(); +static int (*fr_savep)(); + +#if _BSDI_VERSION >= 199501 +# 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 +int iplioctl __P((dev_t, int, caddr_t, int, struct proc *)); + +struct cfdriver iplcd = { + NULL, "ipl", NULL, NULL, DV_DULL, 0 +}; + +struct devsw iplsw = { + &iplcd, + iplopen, iplclose, iplread, nowrite, iplioctl, noselect, nommap, + nostrat, nodump, nopsize, 0, + nostop +}; +#endif /* _BSDI_VERSION >= 199501 */ + +#ifdef IPFILTER_LKM +int iplidentify(s) +char *s; +{ + if (strcmp(s, "ipl") == 0) + return 1; + return 0; +} +#endif /* IPFILTER_LKM */ + + +int iplattach() +{ + int s; + + SPLNET(s); + if (ipl_inited || (fr_checkp == fr_check)) { + printf("IP Filter: already initialized\n"); + SPLX(s); + return EBUSY; + } + ipl_inited = 1; + bzero((char *)nat_table, sizeof(nat_t *) * NAT_SIZE * 2); + fr_savep = fr_checkp; + fr_checkp = fr_check; + fr_saveslowtimo = inetsw[0].pr_slowtimo; + inetsw[0].pr_slowtimo = ipfr_slowtimer; + SPLX(s); + return 0; +} + + +int ipldetach() +{ + int s, i = FR_INQUE|FR_OUTQUE; + + SPLNET(s); + if (!ipl_inited) + { + printf("IP Filter: not initialized\n"); + SPLX(s); + return EBUSY; + } + + fr_checkp = fr_savep; + inetsw[0].pr_slowtimo = fr_saveslowtimo; + frflush((caddr_t)&i); + ipl_inited = 0; + + ipfr_unload(); + ip_natunload(); + fr_stateunload(); + + SPLX(s); + return 0; +} + + +static void frzerostats(data) +caddr_t data; +{ + struct friostat fio; + + bcopy((char *)frstats, (char *)fio.f_st, + sizeof(struct filterstats) * 2); + fio.f_fin[0] = ipfilter[0][0]; + fio.f_fin[1] = ipfilter[0][1]; + fio.f_fout[0] = ipfilter[1][0]; + fio.f_fout[1] = ipfilter[1][1]; + fio.f_acctin[0] = ipacct[0][0]; + fio.f_acctin[1] = ipacct[0][1]; + fio.f_acctout[0] = ipacct[1][0]; + fio.f_acctout[1] = ipacct[1][1]; + fio.f_active = fr_active; + 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; +#else +) +#endif +dev_t dev; +int cmd; +caddr_t data; +int mode; +{ + int error = 0, s, unit; + + unit = minor(dev); + if (unit != 0) + return ENXIO; + + SPLNET(s); + switch (cmd) { + case FIONREAD : +#ifdef IPFILTER_LOG + *(int *)data = iplused; +#endif + break; +#ifndef IPFILTER_LKM + case SIOCFRENB : + { + u_int enable; + + if (!(mode & FWRITE)) + error = EPERM; + else { + IRCOPY(data, (caddr_t)&enable, sizeof(enable)); + if (enable) + error = iplattach(); + else + error = ipldetach(); + } + break; + } +#endif + case SIOCSETFF : + if (!(mode & FWRITE)) + error = EPERM; + else + IRCOPY(data, (caddr_t)&fr_flags, sizeof(fr_flags)); + break; + case SIOCGETFF : + IWCOPY((caddr_t)&fr_flags, data, sizeof(fr_flags)); + break; + case SIOCINAFR : + case SIOCRMAFR : + case SIOCADAFR : + case SIOCZRLST : + if (!(mode & FWRITE)) + error = EPERM; + else + error = frrequest(cmd, data, fr_active); + break; + case SIOCINIFR : + case SIOCRMIFR : + case SIOCADIFR : + if (!(mode & FWRITE)) + error = EPERM; + else + error = frrequest(cmd, data, 1 - fr_active); + break; + case SIOCSWAPA : + if (!(mode & FWRITE)) + error = EPERM; + else { + bzero((char *)frcache, sizeof(frcache[0]) * 2); + *(u_int *)data = fr_active; + fr_active = 1 - fr_active; + } + break; + case SIOCGETFS : + { + struct friostat fio; + + bcopy((char *)frstats, (char *)fio.f_st, + sizeof(struct filterstats) * 2); + fio.f_fin[0] = ipfilter[0][0]; + fio.f_fin[1] = ipfilter[0][1]; + fio.f_fout[0] = ipfilter[1][0]; + fio.f_fout[1] = ipfilter[1][1]; + fio.f_acctin[0] = ipacct[0][0]; + fio.f_acctin[1] = ipacct[0][1]; + fio.f_acctout[0] = ipacct[1][0]; + fio.f_acctout[1] = ipacct[1][1]; + fio.f_active = fr_active; + IWCOPY((caddr_t)&fio, data, sizeof(fio)); + break; + } + case SIOCFRZST : + if (!(mode & FWRITE)) + error = EPERM; + else + frzerostats(data); + break; + case SIOCIPFFL : + if (!(mode & FWRITE)) + error = EPERM; + else + frflush(data); + break; +#ifdef IPFILTER_LOG + case SIOCIPFFB : + if (!(mode & FWRITE)) + error = EPERM; + else { + *(int *)data = iplused; + iplh = iplt = iplbuf; + iplused = 0; + } + 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)); + break; + default : + error = EINVAL; + break; + } + SPLX(s); + return error; +} + + +static int frrequest(req, data, set) +int req, set; +caddr_t data; +{ + register frentry_t *fp, *f, **fprev; + register frentry_t **ftail; + frentry_t fr; + frdest_t *fdp; + struct frentry frd; + int error = 0, in; + + fp = &fr; + IRCOPY(data, (caddr_t)fp, sizeof(*fp)); + + bzero((char *)frcache, sizeof(frcache[0]) * 2); + + in = (fp->fr_flags & FR_INQUE) ? 0 : 1; + if (fp->fr_flags & FR_ACCOUNT) { + ftail = fprev = &ipacct[in][set]; + } else if (fp->fr_flags & (FR_OUTQUE|FR_INQUE)) + ftail = fprev = &ipfilter[in][set]; + else + return ESRCH; + + IRCOPY((char *)fp, (char *)&frd, sizeof(frd)); + fp = &frd; + if (*fp->fr_ifname) { + fp->fr_ifa = GETUNIT(fp->fr_ifname); + if (!fp->fr_ifa) + fp->fr_ifa = (struct ifnet *)-1; + } + + fdp = &fp->fr_dif; + fp->fr_flags &= ~FR_DUP; + if (*fdp->fd_ifname) { + fdp->fd_ifp = GETUNIT(fdp->fd_ifname); + if (!fdp->fd_ifp) + fdp->fd_ifp = (struct ifnet *)-1; + else + fp->fr_flags |= FR_DUP; + } + + fdp = &fp->fr_tif; + if (*fdp->fd_ifname) { + fdp->fd_ifp = GETUNIT(fdp->fd_ifname); + if (!fdp->fd_ifp) + fdp->fd_ifp = (struct ifnet *)-1; + } + + /* + * Look for a matching filter rule, but don't include the next or + * interface pointer in the comparison (fr_next, fr_ifa). + */ + for (; (f = *ftail); ftail = &f->fr_next) + if (bcmp((char *)&f->fr_ip, (char *)&fp->fr_ip, + FR_CMPSIZ) == 0) + break; + + /* + * If zero'ing statistics, copy current to caller and zero. + */ + if (req == SIOCZRLST) { + if (!f) + return ESRCH; + IWCOPY((caddr_t)f, data, sizeof(*f)); + f->fr_hits = 0; + f->fr_bytes = 0; + return 0; + } + + if (!f) { + ftail = fprev; + if (req != SIOCINAFR && req != SIOCINIFR) + while ((f = *ftail)) + ftail = &f->fr_next; + else if (fp->fr_hits) + while (--fp->fr_hits && (f = *ftail)) + ftail = &f->fr_next; + f = NULL; + } + + if (req == SIOCDELFR || req == SIOCRMIFR) { + if (!f) + error = ESRCH; + else { + *ftail = f->fr_next; + (void) KFREE(f); + } + } else { + if (f) + error = EEXIST; + else { + if ((f = (struct frentry *)KMALLOC(sizeof(*f)))) { + bcopy((char *)fp, (char *)f, sizeof(*f)); + f->fr_hits = 0; + f->fr_next = *ftail; + *ftail = f; + } else + error = ENOMEM; + } + } + return (error); +} + + +#if !defined(linux) +/* + * routines below for saving IP headers to buffer + */ +int iplopen(dev, flags +#if _BSDI_VERSION >= 199501 +, devtype, p) +int devtype; +struct proc *p; +#else +) +#endif +dev_t dev; +int flags; +{ + u_int min = minor(dev); + + if (min) + min = ENXIO; + return min; +} + + +int iplclose(dev, flags +#if _BSDI_VERSION >= 199501 +, devtype, p) +int devtype; +struct proc *p; +#else +) +#endif +dev_t dev; +int flags; +{ + u_int min = minor(dev); + + if (min) + min = ENXIO; + 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. + */ +# if BSD >= 199306 +int iplread(dev, uio, ioflag) +int ioflag; +# else +int iplread(dev, uio) +# endif +dev_t dev; +register struct uio *uio; +{ + 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; +# 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)) + 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 + /* + * Gauranteed to succeed from above + */ + (void) fr_copytolog(&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; +{ + struct tcpiphdr *tp; + struct ip *ip; + struct tcphdr *tcp; + struct mbuf *m; + int tlen = 0; + + if (ti->ti_flags & TH_RST) + return -1; /* feedback loop */ +#if BSD < 199306 + m = m_get(M_DONTWAIT, MT_HEADER); +#else + m = m_gethdr(M_DONTWAIT, MT_HEADER); + m->m_data += max_linkhdr; +#endif + if (m == NULL) + return -1; + + if (ti->ti_flags & TH_SYN) + tlen = 1; + m->m_len = sizeof (struct tcpiphdr); +#if BSD >= 199306 + m->m_pkthdr.len = sizeof (struct tcpiphdr); + m->m_pkthdr.rcvif = (struct ifnet *)0; +#endif + bzero(mtod(m, char *), sizeof(struct tcpiphdr)); + ip = mtod(m, struct ip *); + tp = mtod(m, struct tcpiphdr *); + tcp = (struct tcphdr *)((char *)ip + sizeof(struct ip)); + + ip->ip_src.s_addr = ti->ti_dst.s_addr; + ip->ip_dst.s_addr = ti->ti_src.s_addr; + tcp->th_dport = ti->ti_sport; + tcp->th_sport = ti->ti_dport; + tcp->th_ack = htonl(ntohl(ti->ti_seq) + tlen); + tcp->th_off = sizeof(struct tcphdr) >> 2; + tcp->th_flags = TH_RST|TH_ACK; + tp->ti_pr = ((struct ip *)ti)->ip_p; + tp->ti_len = htons(sizeof(struct tcphdr)); + tcp->th_sum = in_cksum(m, sizeof(struct tcpiphdr)); + + 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 + ip->ip_ttl = tcp_ttl; +#else + ip->ip_ttl = ip_defttl; +#endif + + /* + * extra 0 in case of multicast + */ + (void) ip_output(m, (struct mbuf *)0, 0, 0, 0); + return 0; +} + + +#ifndef IPFILTER_LKM +void iplinit() +{ + (void) iplattach(); + ip_init(); +} +#endif + + +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; + register struct route *ro; + struct ifnet *ifp = fdp->fd_ifp; + int len, off, error = 0; + int hlen = fin->fin_hlen; + struct route iproute; + struct sockaddr_in *dst; + + ip = mtod(m0, struct ip *); + /* + * Route packet. + */ + ro = &iproute; + bzero((caddr_t)ro, sizeof (*ro)); + 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__) +# ifdef RTF_CLONING + rtalloc_ign(ro, RTF_CLONING); +# else + rtalloc_ign(ro, RTF_PRCLONING); +# endif +#else + rtalloc(ro); +#endif + if (!ifp) { + if (!(fin->fin_fr->fr_flags & FR_FASTROUTE)) { + error = -2; + goto bad; + } + if (ro->ro_rt == 0 || (ifp = ro->ro_rt->rt_ifp) == 0) { + if (in_localaddr(ip->ip_dst)) + error = EHOSTUNREACH; + else + error = ENETUNREACH; + goto bad; + } + if (ro->ro_rt->rt_flags & RTF_GATEWAY) + dst = (struct sockaddr_in *)&ro->ro_rt->rt_gateway; + } + ro->ro_rt->rt_use++; + + /* + * For input packets which are being "fastrouted", they won't + * go back through output filtering and miss their chance to get + * NAT'd. + */ + (void) ip_natout(ip, hlen, fin); + if (fin->fin_out) + ip->ip_sum = 0; + /* + * If small enough for interface, can just send directly. + */ + if (ip->ip_len <= ifp->if_mtu) { +#ifndef sparc + ip->ip_id = htons(ip->ip_id); + ip->ip_len = htons(ip->ip_len); + ip->ip_off = htons(ip->ip_off); +#endif + if (!ip->ip_sum) + ip->ip_sum = in_cksum(m, hlen); +#if BSD >= 199306 + error = (*ifp->if_output)(ifp, m, (struct sockaddr *)dst, + ro->ro_rt); + +#else + error = (*ifp->if_output)(ifp, m, (struct sockaddr *)dst); +#endif + goto done; + } + /* + * Too large for interface; fragment if possible. + * Must be able to put at least 8 bytes per fragment. + */ + if (ip->ip_off & IP_DF) { + error = EMSGSIZE; + goto bad; + } + len = (ifp->if_mtu - hlen) &~ 7; + if (len < 8) { + error = EMSGSIZE; + goto bad; + } + + { + int mhlen, firstlen = len; + struct mbuf **mnext = &m->m_act; + + /* + * Loop through length of segment after first fragment, + * make new header and copy data of each part and link onto chain. + */ + m0 = m; + mhlen = sizeof (struct ip); + for (off = hlen + len; off < ip->ip_len; off += len) { + MGET(m, M_DONTWAIT, MT_HEADER); + if (m == 0) { + error = ENOBUFS; + goto bad; + } +#if BSD >= 199306 + m->m_data += max_linkhdr; +#else + m->m_off = MMAXOFF - hlen; +#endif + mhip = mtod(m, struct ip *); + bcopy((char *)ip, (char *)mhip, sizeof(*ip)); + if (hlen > sizeof (struct ip)) { + mhlen = ip_optcopy(ip, mhip) + sizeof (struct ip); + mhip->ip_hl = mhlen >> 2; + } + m->m_len = mhlen; + mhip->ip_off = ((off - hlen) >> 3) + (ip->ip_off & ~IP_MF); + if (ip->ip_off & IP_MF) + mhip->ip_off |= IP_MF; + if (off + len >= ip->ip_len) + len = ip->ip_len - off; + else + mhip->ip_off |= IP_MF; + mhip->ip_len = htons((u_short)(len + mhlen)); + m->m_next = m_copy(m0, off, len); + if (m->m_next == 0) { + error = ENOBUFS; /* ??? */ + goto sendorfree; + } +#ifndef sparc + mhip->ip_off = htons((u_short)mhip->ip_off); +#endif + mhip->ip_sum = 0; + mhip->ip_sum = in_cksum(m, mhlen); + *mnext = m; + mnext = &m->m_act; + } + /* + * Update first fragment by trimming what's been copied out + * and updating header, then send each fragment (in order). + */ + m_adj(m0, hlen + firstlen - ip->ip_len); + ip->ip_len = htons((u_short)(hlen + firstlen)); + ip->ip_off = htons((u_short)(ip->ip_off | IP_MF)); + ip->ip_sum = 0; + ip->ip_sum = in_cksum(m0, hlen); +sendorfree: + for (m = m0; m; m = m0) { + m0 = m->m_act; + m->m_act = 0; + if (error == 0) +#if BSD >= 199306 + error = (*ifp->if_output)(ifp, m, + (struct sockaddr *)dst, ro->ro_rt); +#else + error = (*ifp->if_output)(ifp, m, + (struct sockaddr *)dst); +#endif + else + m_freem(m); + } + } +done: + if (ro->ro_rt) { + RTFREE(ro->ro_rt); + } + return; +bad: + m_freem(m); + goto done; +} diff --git a/sys/netinet/ip_fil.h b/sys/netinet/ip_fil.h new file mode 100644 index 0000000..389a161 --- /dev/null +++ b/sys/netinet/ip_fil.h @@ -0,0 +1,293 @@ +/* + * (C)opyright 1993-1996 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 $ + */ + +#ifndef __IP_FIL_H__ +#define __IP_FIL_H__ + +#ifndef SOLARIS +#define SOLARIS (defined(sun) && (defined(__svr4__) || defined(__SVR4))) +#endif + +#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> +# endif +#endif + +#if defined(__STDC__) || defined(__GNUC__) +#define SIOCADAFR _IOW('r', 60, struct frentry) +#define SIOCRMAFR _IOW('r', 61, struct frentry) +#define SIOCSETFF _IOW('r', 62, u_int) +#define SIOCGETFF _IOR('r', 63, u_int) +#define SIOCGETFS _IOR('r', 64, struct friostat) +#define SIOCIPFFL _IOWR('r', 65, int) +#define SIOCIPFFB _IOR('r', 66, int) +#define SIOCADIFR _IOW('r', 67, struct frentry) +#define SIOCRMIFR _IOW('r', 68, struct frentry) +#define SIOCSWAPA _IOR('r', 69, u_int) +#define SIOCINAFR _IOW('r', 70, struct frentry) +#define SIOCINIFR _IOW('r', 71, struct frentry) +#define SIOCFRENB _IOW('r', 72, u_int) +#define SIOCFRSYN _IOW('r', 73, u_int) +#define SIOCFRZST _IOWR('r', 74, struct friostat) +#define SIOCZRLST _IOWR('r', 75, struct frentry) +#else +#define SIOCADAFR _IOW(r, 60, struct frentry) +#define SIOCRMAFR _IOW(r, 61, struct frentry) +#define SIOCSETFF _IOW(r, 62, u_int) +#define SIOCGETFF _IOR(r, 63, u_int) +#define SIOCGETFS _IOR(r, 64, struct friostat) +#define SIOCIPFFL _IOWR(r, 65, int) +#define SIOCIPFFB _IOR(r, 66, int) +#define SIOCADIFR _IOW(r, 67, struct frentry) +#define SIOCRMIFR _IOW(r, 68, struct frentry) +#define SIOCSWAPA _IOR(r, 69, u_int) +#define SIOCINAFR _IOW(r, 70, struct frentry) +#define SIOCINIFR _IOW(r, 71, struct frentry) +#define SIOCFRENB _IOW(r, 72, u_int) +#define SIOCFRSYN _IOW(r, 73, u_int) +#define SIOCFRZST _IOWR(r, 74, struct friostat) +#define SIOCZRLST _IOWR(r, 75, struct frentry) +#endif +#define SIOCADDFR SIOCADAFR +#define SIOCDELFR SIOCRMAFR +#define SIOCINSFR SIOCINAFR + +typedef struct fr_ip { + u_char fi_v:4; /* IP version */ + u_char fi_fl:4; /* packet flags */ + u_char fi_tos; + u_char fi_ttl; + u_char fi_p; + struct in_addr fi_src; + struct in_addr fi_dst; + u_long 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 + +typedef struct fr_info { + struct fr_ip fin_fi; + void *fin_ifp; + u_short fin_data[2]; + u_short fin_out; + u_char fin_tcpf; + u_char fin_icode; + u_short fin_rule; + u_short fin_hlen; + u_short fin_dlen; + char *fin_dp; /* start of data past IP header */ + struct frentry *fin_fr; +} fr_info_t; + +#define FI_CSIZE (sizeof(struct fr_ip) + 11) + +typedef struct frdest { + void *fd_ifp; + struct in_addr fd_ip; + char fd_ifname[IFNAMSIZ]; +} frdest_t; + +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*/ + /* + * Fields after this may not change whilst in the kernel. + */ + struct fr_ip fr_ip; + struct fr_ip fr_mip; /* mask structure */ + + u_char fr_tcpfm; /* tcp flags mask */ + u_char fr_tcpf; /* tcp flags */ + + u_short fr_icmpm; /* data for ICMP packets (mask) */ + u_short fr_icmp; + + u_char fr_scmp; /* data for port comparisons */ + u_char fr_dcmp; + u_short fr_dport; + 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)(); /* call this function */ + char fr_icode; /* return ICMP code */ + char fr_ifname[IFNAMSIZ]; + struct frdest fr_tif; /* "to" interface */ + struct frdest fr_dif; /* duplicate packet interfaces */ +} frentry_t; + +#define fr_proto fr_ip.fi_p +#define fr_ttl fr_ip.fi_ttl +#define fr_tos fr_ip.fi_tos +#define fr_dst fr_ip.fi_dst +#define fr_src fr_ip.fi_src +#define fr_dmsk fr_mip.fi_dst +#define fr_smsk fr_mip.fi_src + +#ifndef offsetof +#define offsetof(t,m) (int)((&((t *)0L)->m)) +#endif +#define FR_CMPSIZ (sizeof(struct frentry) - offsetof(frentry_t, fr_ip)) + +/* + * fr_flags +*/ +#define FR_BLOCK 0x00001 +#define FR_PASS 0x00002 +#define FR_OUTQUE 0x00004 +#define FR_INQUE 0x00008 +#define FR_LOG 0x00010 /* Log */ +#define FR_LOGB 0x00011 /* Log-fail */ +#define FR_LOGP 0x00012 /* Log-pass */ +#define FR_LOGBODY 0x00020 /* Log the body */ +#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_ACCOUNT 0x00400 /* count packet bytes */ +#define FR_KEEPFRAG 0x00800 /* keep fragment information */ +#define FR_KEEPSTATE 0x01000 /* keep `connection' state information */ +#define FR_INACTIVE 0x02000 +#define FR_QUICK 0x04000 /* match & stop processing list */ +#define FR_FASTROUTE 0x08000 /* bypass normal routing */ +#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_LOGMASK (FR_LOG|FR_LOGP|FR_LOGB) +/* + * recognized flags for SIOCGETFF and SIOCSETFF + */ +#define FF_LOGPASS 0x100000 +#define FF_LOGBLOCK 0x200000 +#define FF_LOGNOMATCH 0x400000 +#define FF_LOGGING (FF_LOGPASS|FF_LOGBLOCK|FF_LOGNOMATCH) +#define FF_BLOCKNONIP 0x800000 /* Solaris2 Only */ + +#define FR_NONE 0 +#define FR_EQUAL 1 +#define FR_NEQUAL 2 +#define FR_LESST 3 +#define FR_GREATERT 4 +#define FR_LESSTE 5 +#define FR_GREATERTE 6 +#define FR_OUTRANGE 7 +#define FR_INRANGE 8 + +typedef struct filterstats { + u_long fr_pass; /* packets allowed */ + u_long fr_block; /* packets denied */ + u_long fr_nom; /* packets which don't match any rule */ + u_long fr_ppkl; /* packets allowed and logged */ + u_long fr_bpkl; /* packets denied and logged */ + u_long fr_npkl; /* packets unmatched and logged */ + u_long fr_pkl; /* packets logged */ + u_long fr_skip; /* packets to be logged but buffer full */ + u_long fr_ret; /* packets for which a return is sent */ + u_long fr_acct; /* packets for which counting was performed */ + u_long fr_bnfr; /* bad attempts to allocate fragment state */ + u_long fr_nfr; /* new fragment state kept */ + u_long fr_cfr; /* add new fragment state but complete pkt */ + 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_pull[2]; /* good and bad pullup attempts */ +#if SOLARIS + u_long fr_bad; /* bad IP packets to the filter */ + u_long fr_notip; /* packets passed through no on ip queue */ + u_long fr_drop; /* packets dropped - no info for them! */ +#endif +} filterstats_t; + +/* + * For SIOCGETFS + */ +typedef struct friostat { + struct filterstats f_st[2]; + struct frentry *f_fin[2]; + struct frentry *f_fout[2]; + struct frentry *f_acctin[2]; + struct frentry *f_acctout[2]; + int f_active; +} friostat_t; + +typedef struct optlist { + u_short ol_val; + 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. + */ +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 */ +#if (defined(NetBSD) && (NetBSD <= 1991011) && (NetBSD >= 199603)) + u_long flags; + u_char ifname[IFNAMSIZ]; /* = 32 bytes */ +#else + u_long flags:24; + u_long unit:8; + u_char ifname[4]; /* = 20 bytes */ +#endif +} ipl_ci_t; + + +#ifndef ICMP_UNREACH_FILTER +#define ICMP_UNREACH_FILTER 13 +#endif + +#define IPMINLEN(i, h) ((i)->ip_len >= ((i)->ip_hl * 4 + sizeof(struct h))) +#define IPLLOGSIZE 8192 + +extern int fr_check(); +extern int fr_copytolog(); +extern fr_info_t frcache[]; +extern char *iplh, *iplt; +extern char iplbuf[IPLLOGSIZE]; + +#ifdef _KERNEL + +extern struct frentry *ipfilter[2][2], *ipacct[2][2]; +extern struct filterstats frstats[]; +# if SOLARIS +extern int ipfsync(); +# endif +#endif /* _KERNEL */ +#endif /* __IP_FIL_H__ */ diff --git a/sys/netinet/ip_frag.c b/sys/netinet/ip_frag.c new file mode 100644 index 0000000..6665404 --- /dev/null +++ b/sys/netinet/ip_frag.c @@ -0,0 +1,278 @@ +/* + * (C)opyright 1993,1994,1995 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_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(_KERNEL) && !defined(KERNEL) +# include <string.h> +# include <stdlib.h> +#endif +#include <sys/errno.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/file.h> +#include <sys/ioctl.h> +#include <sys/uio.h> +#include <sys/protosw.h> +#include <sys/socket.h> +#ifdef _KERNEL +# include <sys/systm.h> +#endif +#if !defined(__SVR4) && !defined(__svr4__) +# include <sys/mbuf.h> +#else +# include <sys/byteorder.h> +# include <sys/dditypes.h> +# include <sys/stream.h> +# include <sys/kmem.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> +#include <netinet/ip_var.h> +#include <netinet/tcp.h> +#include <netinet/udp.h> +#include <netinet/tcpip.h> +#include <netinet/ip_icmp.h> +#include "ip_fil.h" +#include "ip_compat.h" +#include "ip_frag.h" +#include "ip_nat.h" +#include "ip_state.h" + +ipfr_t *ipfr_heads[IPFT_SIZE]; +ipfrstat_t ipfr_stats; +u_long ipfr_inuse = 0, + fr_ipfrttl = 120; /* 60 seconds */ +#ifdef _KERNEL +extern int ipfr_timer_id; +#endif +#if SOLARIS +# ifdef _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 +#endif + + +ipfrstat_t *ipfr_fragstats() +{ + ipfr_stats.ifs_table = ipfr_heads; + ipfr_stats.ifs_inuse = ipfr_inuse; + return &ipfr_stats; +} + + +/* + * 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; +{ + ipfr_t **fp, *fr, frag; + u_int idx; + + frag.ipfr_p = ip->ip_p; + idx = ip->ip_p; + frag.ipfr_id = ip->ip_id; + idx += ip->ip_id; + frag.ipfr_tos = ip->ip_tos; + frag.ipfr_src.s_addr = ip->ip_src.s_addr; + idx += ip->ip_src.s_addr; + frag.ipfr_dst.s_addr = ip->ip_dst.s_addr; + idx += ip->ip_dst.s_addr; + idx *= 127; + idx %= IPFT_SIZE; + + /* + * first, make sure it isn't already there... + */ + MUTEX_ENTER(&ipf_frag); + for (fp = &ipfr_heads[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; + } + + if (!(fr = (ipfr_t *)KMALLOC(sizeof(*fr)))) { + ipfr_stats.ifs_nomem++; + MUTEX_EXIT(&ipf_frag); + return -1; + } + if ((fr->ipfr_next = ipfr_heads[idx])) + ipfr_heads[idx]->ipfr_prev = fr; + fr->ipfr_prev = NULL; + ipfr_heads[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); + fr->ipfr_off = (ip->ip_off & 0x1fff) + (fin->fin_dlen >> 3); + *fp = fr; + ipfr_stats.ifs_new++; + ipfr_inuse++; + MUTEX_EXIT(&ipf_frag); + return 0; +} + + +/* + * 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; +{ + 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). + */ + frag.ipfr_p = ip->ip_p; + idx = ip->ip_p; + frag.ipfr_id = ip->ip_id; + idx += ip->ip_id; + frag.ipfr_tos = ip->ip_tos; + frag.ipfr_src.s_addr = ip->ip_src.s_addr; + idx += ip->ip_src.s_addr; + frag.ipfr_dst.s_addr = ip->ip_dst.s_addr; + idx += ip->ip_dst.s_addr; + idx *= 127; + idx %= IPFT_SIZE; + + MUTEX_ENTER(&ipf_frag); + for (f = ipfr_heads[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]) { + /* + * 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_prev = NULL; + ipfr_heads[idx] = f; + } + ret = f->ipfr_pass; + off = ip->ip_off; + atoff = (off & 0x1fff) - (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 & IP_MF)) + f->ipfr_ttl = 1; + else + f->ipfr_off = off; + } + ipfr_stats.ifs_hits++; + MUTEX_EXIT(&ipf_frag); + return ret; + } + MUTEX_EXIT(&ipf_frag); + return 0; +} + + +/* + * Free memory in use by fragment state info. kept. + */ +void ipfr_unload() +{ + ipfr_t **fp, *fr; + 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); +} + + +#ifdef _KERNEL +/* + * Slowly expire held state for fragments. Timeouts are set * in expectation + * of this being called twice per second. + */ +# if BSD < 199306 +int ipfr_slowtimer() +# else +void ipfr_slowtimer() +# endif +{ + ipfr_t **fp, *fr; + int s, idx; + + MUTEX_ENTER(&ipf_frag); + SPLNET(s); + + for (idx = IPFT_SIZE - 1; idx >= 0; idx--) + for (fp = &ipfr_heads[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--; + KFREE(fr); + } else + fp = &fr->ipfr_next; + } + SPLX(s); +# if SOLARIS + MUTEX_EXIT(&ipf_frag); + fr_timeoutstate(); + ip_natexpire(); + ipfr_timer_id = timeout(ipfr_slowtimer, NULL, HZ/2); +# else + fr_timeoutstate(); + ip_natexpire(); + ip_slowtimo(); +# if BSD < 199306 + return 0; +# endif +# endif +} +#endif /* defined(_KERNEL) */ diff --git a/sys/netinet/ip_frag.h b/sys/netinet/ip_frag.h new file mode 100644 index 0000000..a356785 --- /dev/null +++ b/sys/netinet/ip_frag.h @@ -0,0 +1,47 @@ +/* + * (C)opyright 1993, 1994, 1995 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 $ + */ + +#ifndef __IP_FRAG_H_ +#define __IP_FRAG_H__ + +#define IPFT_SIZE 257 + +typedef struct ipfr { + struct ipfr *ipfr_next, *ipfr_prev; + struct in_addr ipfr_src; + struct in_addr ipfr_dst; + u_short ipfr_id; + u_char ipfr_p; + u_char ipfr_tos; + u_short ipfr_off; + u_short ipfr_ttl; + u_char ipfr_pass; +} ipfr_t; + + +typedef struct ipfrstat { + u_long ifs_exists; /* add & already exists */ + u_long ifs_nomem; + u_long ifs_new; + u_long ifs_hits; + u_long ifs_expire; + u_long ifs_inuse; + struct ipfr **ifs_table; +} ipfrstat_t; + +#define IPFR_CMPSZ (4 + 4 + 2 + 1 + 1) + +extern ipfrstat_t *ipfr_fragstats(); +extern int ipfr_newfrag(), ipfr_knownfrag(); +# ifdef _KERNEL +extern void ipfr_unload(); +# endif +#endif /* __IP_FIL_H__ */ diff --git a/sys/netinet/ip_nat.c b/sys/netinet/ip_nat.c new file mode 100644 index 0000000..afe9761 --- /dev/null +++ b/sys/netinet/ip_nat.c @@ -0,0 +1,886 @@ +/* + * (C)opyright 1995-1996 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 !defined(lint) && defined(LIBC_SCCS) +static char sccsid[] = "@(#)ip_nat.c 1.11 6/5/96 (C) 1995 Darren Reed"; +static char rcsid[] = "$Id: ip_nat.c,v 2.0.1.10 1997/02/08 06:38:49 darrenr Exp $"; +#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/file.h> +#include <sys/ioctl.h> +#include <sys/uio.h> +#include <sys/protosw.h> +#include <sys/socket.h> +#ifdef _KERNEL +# include <sys/systm.h> +#endif +#if !defined(__SVR4) && !defined(__svr4__) +# include <sys/mbuf.h> +#else +# include <sys/byteorder.h> +# include <sys/dditypes.h> +# include <sys/stream.h> +# include <sys/kmem.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> + +#ifdef RFC1825 +#include <vpn/md5.h> +#include <vpn/ipsec.h> +extern struct ifnet vpnif; +#endif + +#include <netinet/ip_var.h> +#include <netinet/tcp.h> +#include <netinet/udp.h> +#include <netinet/tcpip.h> +#include <netinet/ip_icmp.h> +#include "ip_fil.h" +#include "ip_compat.h" +#include "ip_nat.h" +#include "ip_state.h" +#ifndef MIN +#define MIN(a,b) (((a)<(b))?(a):(b)) +#endif + +nat_t *nat_table[2][NAT_SIZE], *nat_instances = NULL; +ipnat_t *nat_list = NULL; +u_long nat_inuse = 0, + fr_defnatage = 1200; +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 +extern kmutex_t ipf_nat; +# endif +#endif + +static int flush_nattable(), clear_natlist(); +static void nattable_sync(); + +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 + sum1 = (~ntohs(*sp)) & 0xffff; +#endif + sum1 += (n); + sum1 = (sum1 >> 16) + (sum1 & 0xffff); + /* Again */ + sum1 = (sum1 >> 16) + (sum1 & 0xffff); + sumshort = ~(u_short)sum1; + *(sp) = htons(sumshort); +} + + +void fix_incksum(sp, n) +u_short *sp; +u_long n; +{ + register u_short sumshort; + register u_long sum1; + +#ifdef sparc + sum1 = (~(*sp)) & 0xffff; +#else + sum1 = (~ntohs(*sp)) & 0xffff; +#endif + sum1 += ~(n) & 0xffff; + sum1 = (sum1 >> 16) + (sum1 & 0xffff); + /* Again */ + sum1 = (sum1 >> 16) + (sum1 & 0xffff); + sumshort = ~(u_short)sum1; + *(sp) = htons(sumshort); +} + + +/* + * How the NAT is organised and works. + * + * Inside (interface y) NAT Outside (interface x) + * -------------------- -+- ------------------------------------- + * Packet going | out, processsed by ip_natout() for x + * ------------> | ------------> + * src=10.1.1.1 | src=192.1.1.1 + * | + * | in, processed by ip_natin() for x + * <------------ | <------------ + * dst=10.1.1.1 | dst=192.1.1.1 + * -------------------- -+- ------------------------------------- + * ip_natout() - changes ip_src and if required, sport + * - creates a new mapping, if required. + * ip_natin() - changes ip_dst and if required, dport + * + * In the NAT table, internal source is recorded as "in" and externally + * seen as "out". + */ + +/* + * Handle ioctls which manipulate the NAT. + */ +int nat_ioctl(data, cmd, mode) +caddr_t data; +int cmd, mode; +{ + register ipnat_t *nat, *n = NULL, **np = NULL; + ipnat_t natd; + int error = 0, ret; + + /* + * For add/delete, look to see if the NAT entry is already present + */ + MUTEX_ENTER(&ipf_nat); + if ((cmd == SIOCADNAT) || (cmd == SIOCRMNAT)) { + IRCOPY(data, (char *)&natd, sizeof(natd)); + nat = &natd; + for (np = &nat_list; (n = *np); np = &n->in_next) + if (!bcmp((char *)&nat->in_flags, (char *)&n->in_flags, + IPN_CMPSIZ)) + break; + } + + switch (cmd) + { + case SIOCADNAT : + if (!(mode & FWRITE)) { + error = EPERM; + break; + } + if (n) { + error = EEXIST; + break; + } + if (!(n = (ipnat_t *)KMALLOC(sizeof(*n)))) { + error = ENOMEM; + break; + } + IRCOPY((char *)data, (char *)n, sizeof(*n)); + n->in_ifp = (void *)GETUNIT(n->in_ifname); + n->in_next = *np; + n->in_use = 0; + n->in_space = ~(0xffffffff & ntohl(n->in_outmsk)); + if (n->in_space) /* lose 2: broadcast + network address */ + n->in_space -= 2; + else + n->in_space = 1; /* single IP# mapping */ + if (n->in_outmsk != 0xffffffff) + n->in_nip = ntohl(n->in_outip) + 1; + else + n->in_nip = ntohl(n->in_outip); + if (n->in_redir == NAT_MAP) { + n->in_pnext = ntohs(n->in_pmin); + /* + * Multiply by the number of ports made available. + */ + if (ntohs(n->in_pmax) > ntohs(n->in_pmin)) + n->in_space *= (ntohs(n->in_pmax) - + ntohs(n->in_pmin)); + } + /* Otherwise, these fields are preset */ + *np = n; + break; + case SIOCRMNAT : + if (!(mode & FWRITE)) { + error = EPERM; + break; + } + if (!n) { + error = ESRCH; + break; + } + *np = n->in_next; + + KFREE(n); + nattable_sync(); + 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 : + { + natlookup_t nl; + + IRCOPY((char *)data, (char *)&nl, sizeof(nl)); + + if (nat_lookupredir(&nl)) + IWCOPY((char *)&nl, (char *)data, sizeof(nl)); + else + error = ESRCH; + break; + } + case SIOCFLNAT : + if (!(mode & FWRITE)) { + error = EPERM; + break; + } + ret = flush_nattable(); + IWCOPY((caddr_t)&ret, data, sizeof(ret)); + break; + case SIOCCNATL : + if (!(mode & FWRITE)) { + error = EPERM; + break; + } + ret = clear_natlist(); + IWCOPY((caddr_t)&ret, data, sizeof(ret)); + break; + } + MUTEX_EXIT(&ipf_nat); + return error; +} + + +static void nat_delete(natd) +struct nat *natd; +{ + register struct nat **natp, *nat; + + for (natp = natd->nat_hstart[0]; (nat = *natp); + natp = &nat->nat_hnext[0]) + if (nat == natd) { + *natp = nat->nat_hnext[0]; + break; + } + + for (natp = natd->nat_hstart[1]; (nat = *natp); + natp = &nat->nat_hnext[1]) + if (nat == natd) { + *natp = nat->nat_hnext[1]; + break; + } + + if (natd->nat_ptr) { + natd->nat_ptr->in_space++; + natd->nat_ptr->in_use--; + } + KFREE(natd); + nat_inuse--; +} + + +/* + * flush_nattable - clear the NAT table of all mapping entries. + */ +static int flush_nattable() +{ + register nat_t *nat, **natp; + register int j = 0; + + /* + * Everything will be deleted, so lets just make it the deletions + * quicker. + */ + bzero((char *)nat_table[0], sizeof(nat_table[0])); + bzero((char *)nat_table[1], sizeof(nat_table[1])); + + for (natp = &nat_instances; (nat = *natp); ) { + *natp = nat->nat_next; + nat_delete(nat); + j++; + } + + return j; +} + + +/* + * I know this is O(N*M), but it can't be avoided. + */ +static void nattable_sync() +{ + register nat_t *nat; + register ipnat_t *np; + int i; + + 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; + } +} + + +/* + * clear_natlist - delete all entries in the active NAT mapping list. + */ +static int clear_natlist() +{ + register ipnat_t *n, **np; + int i = 0; + + for (np = &nat_list; (n = *np); i++) { + *np = n->in_next; + KFREE(n); + } + + nattable_sync(); + return i; +} + + +/* + * 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; +{ + register u_long sum1, sum2, sumd; + u_short port = 0, sport = 0, dport = 0, nport = 0; + struct in_addr in; + tcphdr_t *tcp = NULL; + nat_t *nat, **natp; + u_short nflags; + + nflags = flags & np->in_flags; + if (flags & IPN_TCPUDP) { + tcp = (tcphdr_t *)fin->fin_dp; + sport = tcp->th_sport; + dport = tcp->th_dport; + } + + /* Give me a new nat */ + if (!(nat = (nat_t *)KMALLOC(sizeof(*nat)))) + return NULL; + + bzero((char *)nat, sizeof(*nat)); + + /* + * Search the current table for a match. + */ + if (direction == NAT_OUTBOUND) { + /* + * If it's an outbound packet which doesn't match any existing + * record, then create a new port + */ + do { + port = 0; + in.s_addr = np->in_nip; + if (nflags & IPN_TCPUDP) { + port = htons(np->in_pnext++); + if (np->in_pnext >= ntohs(np->in_pmax)) { + np->in_pnext = ntohs(np->in_pmin); + np->in_space--; + if (np->in_outmsk != 0xffffffff) + np->in_nip++; + } + } else if (np->in_outmsk != 0xffffffff) { + np->in_space--; + np->in_nip++; + } + + if (!port && (flags & IPN_TCPUDP)) + port = sport; + 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)); + + /* Setup the NAT table */ + nat->nat_inip = ip->ip_src; + nat->nat_outip.s_addr = htonl(in.s_addr); + nat->nat_oip = ip->ip_dst; + + sum1 = (ntohl(ip->ip_src.s_addr) & 0xffff) + + (ntohl(ip->ip_src.s_addr) >> 16) + ntohs(sport); + + sum2 = (in.s_addr & 0xffff) + (in.s_addr >> 16) + ntohs(port); + + if (flags & IPN_TCPUDP) { + nat->nat_inport = sport; + nat->nat_outport = port; + nat->nat_oport = dport; + } + } else { + + /* + * Otherwise, it's an inbound packet. Most likely, we don't + * want to rewrite source ports and source addresses. Instead, + * we want to rewrite to a fixed internal address and fixed + * internal port. + */ + in.s_addr = ntohl(np->in_inip); + if (!(nport = np->in_pnext)) + nport = dport; + + nat->nat_inip.s_addr = htonl(in.s_addr); + nat->nat_outip = ip->ip_dst; + nat->nat_oip = ip->ip_src; + + sum1 = (ntohl(ip->ip_dst.s_addr) & 0xffff) + + (ntohl(ip->ip_dst.s_addr) >> 16) + ntohs(dport); + + sum2 = (in.s_addr & 0xffff) + (in.s_addr >> 16) + ntohs(nport); + + if (flags & IPN_TCPUDP) { + nat->nat_inport = nport; + nat->nat_outport = dport; + nat->nat_oport = sport; + } + } + + /* 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); + + if (sum1 > sum2) + sum2--; /* Because ~1 == -2, We really need ~1 == -1 */ + sumd = sum2 - sum1; + sumd = (sumd & 0xffff) + (sumd >> 16); + nat->nat_sumd = (sumd & 0xffff) + (sumd >> 16); + + if ((flags & IPN_TCPUDP) && ((sport != port) || (dport != nport))) { + if (direction == NAT_OUTBOUND) + sum1 = (ntohl(ip->ip_src.s_addr) & 0xffff) + + (ntohl(ip->ip_src.s_addr) >> 16); + else + sum1 = (ntohl(ip->ip_dst.s_addr) & 0xffff) + + (ntohl(ip->ip_dst.s_addr) >> 16); + + sum2 = (in.s_addr & 0xffff) + (in.s_addr >> 16); + + /* 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); + + if (sum1 > sum2) + sum2--; /* Because ~1 == -2, We really need ~1 == -1 */ + sumd = sum2 - sum1; + sumd = (sumd & 0xffff) + (sumd >> 16); + nat->nat_ipsumd = (sumd & 0xffff) + (sumd >> 16); + } else + nat->nat_ipsumd = nat->nat_sumd; + + in.s_addr = htonl(in.s_addr); + nat->nat_next = nat_instances; + nat_instances = nat; + natp = &nat_table[0][nat->nat_inip.s_addr % NAT_SIZE]; + nat->nat_hstart[0] = natp; + nat->nat_hnext[0] = *natp; + *natp = nat; + natp = &nat_table[1][nat->nat_outip.s_addr % NAT_SIZE]; + nat->nat_hstart[1] = natp; + nat->nat_hnext[1] = *natp; + *natp = nat; + nat->nat_ptr = np; + np->in_use++; + if (direction == NAT_OUTBOUND) { + if (flags & IPN_TCPUDP) + tcp->th_sport = htons(port); + } else { + if (flags & IPN_TCPUDP) + tcp->th_dport = htons(nport); + } + nat_stats.ns_added++; + nat_inuse++; + return nat; +} + + +/* + * NB: these lookups don't lock access to the list, it assume it has already + * been done! + */ +/* + * Lookup a nat entry based on the mapped destination ip address/port and + * real source address/port. We use this lookup when receiving a packet, + * 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; +{ + register nat_t *nat; + + flags &= IPN_TCPUDP; + + 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 && + nat->nat_outip.s_addr == mapdst.s_addr && + (!flags || (nat->nat_oport == sport && + nat->nat_outport == mapdport))) + return nat; + return NULL; +} + + +/* + * Lookup a nat entry based on the source 'real' ip address/port and + * destination address/port. We use this lookup when sending a packet out, + * 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; +{ + 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 && + nat->nat_oip.s_addr == dst.s_addr && + (!flags || (nat->nat_inport == sport && + nat->nat_oport == dport))) + return nat; + return NULL; +} + + +/* + * Lookup a nat entry based on the mapped source ip address/port and + * 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; +{ + register nat_t *nat; + + flags &= IPN_TCPUDP; + + 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 && + nat->nat_oip.s_addr == dst.s_addr && + (!flags || (nat->nat_outport == mapsport && + nat->nat_oport == dport))) + return nat; + return NULL; +} + + +/* + * Lookup the NAT tables to search for a matching redirect + */ +nat_t *nat_lookupredir(np) +register natlookup_t *np; +{ + nat_t *nat; + + /* + * 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; + } + return nat; +} + + +/* + * 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; +{ + 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; + + if ((fr = fin->fin_fr) && !(fr->fr_flags & FR_DUP) && + fr->fr_tif.fd_ifp && fr->fr_tif.fd_ifp != (void *)-1) + ifp = fr->fr_tif.fd_ifp; + else + ifp = fin->fin_ifp; + + if (!(ip->ip_off & 0x1fff) && !(fin->fin_fi.fi_fl & FI_SHORT)) { + if (ip->ip_p == IPPROTO_TCP) + nflags = IPN_TCP; + else if (ip->ip_p == IPPROTO_UDP) + nflags = IPN_UDP; + if (nflags) { + tcp = (tcphdr_t *)fin->fin_dp; + sport = tcp->th_sport; + dport = tcp->th_dport; + } + } + + 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) + continue; + /* + * 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, + NAT_OUTBOUND))) + break; + } + ip->ip_src = nat->nat_outip; + + nat->nat_age = fr_defnatage; /* 5 mins */ + + /* + * Fix up checksums, not by recalculating them, but + * simply computing adjustments. + */ +#if SOLARIS + if (np->in_redir == NAT_MAP) + fix_outcksum(&ip->ip_sum, nat->nat_ipsumd); + else + fix_incksum(&ip->ip_sum, nat->nat_ipsumd); +#endif + + if (nflags && !(ip->ip_off & 0x1fff) && + !(fin->fin_fi.fi_fl & FI_SHORT)) { + + if (nat->nat_outport) + tcp->th_sport = nat->nat_outport; + + if (ip->ip_p == IPPROTO_TCP) { + csump = &tcp->th_sum; + set_tcp_age(&nat->nat_age, + nat->nat_state, ip, fin,1); + } else if (ip->ip_p == IPPROTO_UDP) { + udphdr_t *udp = (udphdr_t *)tcp; + + if (udp->uh_sum) + csump = &udp->uh_sum; + } else if (ip->ip_p == IPPROTO_ICMP) { + icmphdr_t *ic = (icmphdr_t *)tcp; + + csump = &ic->icmp_cksum; + } + if (csump) { + if (np->in_redir == NAT_MAP) + fix_outcksum(csump, + nat->nat_sumd); + else + fix_incksum(csump, + nat->nat_sumd); + } + } + nat_stats.ns_mapped[1]++; + MUTEX_EXIT(&ipf_nat); + return 1; + } + MUTEX_EXIT(&ipf_nat); + return 0; +} + + +/* + * 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; +{ + 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; + nat_t *nat; + + if (!(ip->ip_off & 0x1fff) && !(fin->fin_fi.fi_fl & FI_SHORT)) { + if (ip->ip_p == IPPROTO_TCP) + nflags = IPN_TCP; + else if (ip->ip_p == IPPROTO_UDP) + nflags = IPN_UDP; + if (nflags) { + tcp = (tcphdr_t *)((char *)ip + hlen); + dport = tcp->th_dport; + sport = tcp->th_sport; + } + } + + 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; + } + } + ip->ip_dst = nat->nat_inip; + + nat->nat_age = fr_defnatage; + + /* + * Fix up checksums, not by recalculating them, but + * simply computing adjustments. + */ +#if SOLARIS + if (np->in_redir == NAT_MAP) + fix_incksum(&ip->ip_sum, nat->nat_ipsumd); + else + fix_outcksum(&ip->ip_sum, nat->nat_ipsumd); +#endif + if (nflags && !(ip->ip_off & 0x1fff) && + !(fin->fin_fi.fi_fl & FI_SHORT)) { + + if (nat->nat_inport) + tcp->th_dport = nat->nat_inport; + + if (ip->ip_p == IPPROTO_TCP) { + csump = &tcp->th_sum; + set_tcp_age(&nat->nat_age, + nat->nat_state, ip, fin,0); + } else if (ip->ip_p == IPPROTO_UDP) { + udphdr_t *udp = (udphdr_t *)tcp; + + if (udp->uh_sum) + csump = &udp->uh_sum; + } else if (ip->ip_p == IPPROTO_ICMP) { + icmphdr_t *ic = (icmphdr_t *)tcp; + + csump = &ic->icmp_cksum; + } + if (csump) { + if (np->in_redir == NAT_MAP) + fix_incksum(csump, + nat->nat_sumd); + else + fix_outcksum(csump, + nat->nat_sumd); + } + } + nat_stats.ns_mapped[0]++; + MUTEX_EXIT(&ipf_nat); + return 1; + } + MUTEX_EXIT(&ipf_nat); + return 0; +} + + +/* + * Free all memory used by NAT structures allocated at runtime. + */ +void ip_natunload() +{ + MUTEX_ENTER(&ipf_nat); + + (void) clear_natlist(); + (void) flush_nattable(); + + MUTEX_EXIT(&ipf_nat); +} + + +/* + * Slowly expire held state for NAT entries. Timeouts are set in + * expectation of this being called twice per second. + */ +void ip_natexpire() +{ + register struct nat *nat, **natp; + + MUTEX_ENTER(&ipf_nat); + for (natp = &nat_instances; (nat = *natp); natp = &nat->nat_next) { + if (--nat->nat_age) + continue; + *natp = nat->nat_next; + nat_delete(nat); + nat_stats.ns_expire++; + } + MUTEX_EXIT(&ipf_nat); +} diff --git a/sys/netinet/ip_nat.h b/sys/netinet/ip_nat.h new file mode 100644 index 0000000..d64183a --- /dev/null +++ b/sys/netinet/ip_nat.h @@ -0,0 +1,118 @@ +/* + * (C)opyright 1995 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 + * $Id: ip_nat.h,v 2.0.1.7 1997/01/30 12:39:41 darrenr Exp $ + */ + +#ifndef __IP_NAT_H_ +#define __IP_NAT_H__ + +#ifndef SOLARIS +#define SOLARIS (defined(sun) && (defined(__svr4__) || defined(__SVR4))) +#endif + +#if defined(__STDC__) || defined(__GNUC__) +#define SIOCADNAT _IOW('r', 80, struct ipnat) +#define SIOCRMNAT _IOW('r', 81, struct ipnat) +#define SIOCGNATS _IOR('r', 82, struct natstat) +#define SIOCGNATL _IOWR('r', 83, struct natlookup) +#define SIOCGFRST _IOR('r', 84, struct ipfrstat) +#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) +#define SIOCGNATS _IOR(r, 82, struct natstat) +#define SIOCGNATL _IOWR(r, 83, struct natlookup) +#define SIOCGFRST _IOR(r, 84, struct ipfrstat) +#define SIOCGIPST _IOR(r, 85, struct ips_stat) +#define SIOCFLNAT _IOWR(r, 86, int) +#define SIOCCNATL _IOWR(r, 87, int) +#endif + +#define NAT_SIZE 367 + +typedef struct nat { + int nat_age; + u_long nat_sumd; + u_long nat_ipsumd; + struct in_addr nat_inip; + struct in_addr nat_outip; + struct in_addr nat_oip; /* other ip */ + u_short nat_oport; /* other port */ + u_short nat_inport; + u_short nat_outport; + u_short nat_use; + u_char nat_state[2]; + struct ipnat *nat_ptr; + struct nat *nat_next; + struct nat *nat_hnext[2]; + struct nat **nat_hstart[2]; +} nat_t; + +typedef struct ipnat { + struct ipnat *in_next; + void *in_ifp; + u_int in_space; + u_int in_use; + struct in_addr in_nextip; + u_short in_pnext; + u_short in_flags; + u_short in_port[2]; + struct in_addr in_in[2]; + 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]; +} ipnat_t; + +#define in_pmin in_port[0] /* Also holds static redir port */ +#define in_pmax in_port[1] +#define in_nip in_nextip.s_addr +#define in_inip in_in[0].s_addr +#define in_inmsk in_in[1].s_addr +#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_MAP 0 +#define NAT_REDIRECT 1 + +#define IPN_CMPSIZ (sizeof(struct in_addr) * 4 + sizeof(u_short) * 3 + \ + sizeof(int)) + +typedef struct natlookup { + struct in_addr nl_inip; + struct in_addr nl_outip; + u_short nl_inport; + u_short nl_outport; +} natlookup_t; + +typedef struct natstat { + u_long ns_mapped[2]; + u_long ns_added; + u_long ns_expire; + u_long ns_inuse; + 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(); +extern nat_t *nat_outlookup(), *nat_inlookup(), *nat_lookupredir(); +extern int ip_natout(), ip_natin(); +extern void ip_natunload(), ip_natexpire(); +#endif /* __IP_NAT_H__ */ diff --git a/sys/netinet/ip_state.c b/sys/netinet/ip_state.c new file mode 100644 index 0000000..62a49aa --- /dev/null +++ b/sys/netinet/ip_state.c @@ -0,0 +1,536 @@ +/* + * (C)opyright 1995 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_state.c 1.8 6/5/96 (C) 1993-1995 Darren Reed"; +static char rcsid[] = "$Id: ip_state.c,v 2.0.1.2 1997/01/09 15:22:45 darrenr 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/file.h> +#include <sys/ioctl.h> +#include <sys/uio.h> +#include <sys/protosw.h> +#include <sys/socket.h> +#ifdef _KERNEL +# include <sys/systm.h> +#endif +#if !defined(__SVR4) && !defined(__svr4__) +# include <sys/mbuf.h> +#else +# include <sys/byteorder.h> +# include <sys/dditypes.h> +# include <sys/stream.h> +# include <sys/kmem.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> +#include <netinet/ip_var.h> +#include <netinet/tcp.h> +#include <netinet/tcp_fsm.h> +#include <netinet/udp.h> +#include <netinet/tcpip.h> +#include <netinet/ip_icmp.h> +#include "ip_fil.h" +#include "ip_compat.h" +#include "ip_state.h" +#ifndef MIN +#define MIN(a,b) (((a)<(b))?(a):(b)) +#endif + +void set_tcp_age(); + +#define TCP_CLOSE (TH_FIN|TH_RST) + +ipstate_t *ips_table[IPSTATE_SIZE]; +int ips_num = 0; +ips_stat_t ips_stats; +#if SOLARIS +extern kmutex_t ipf_state; +# if !defined(_KERNEL) +#define bcopy(a,b,c) memmove(b,a,c) +# endif +#endif + + +#define FIVE_DAYS (2 * 5 * 86400) /* 5 days: half closed session */ + +u_long fr_tcpidletimeout = FIVE_DAYS, + fr_tcpclosewait = 60, + fr_tcplastack = 20, + fr_tcptimeout = 120, + fr_tcpclosed = 1, + fr_udptimeout = 120, + fr_icmptimeout = 120; + + +ips_stat_t *fr_statetstats() +{ + ips_stats.iss_active = ips_num; + ips_stats.iss_table = ips_table; + return &ips_stats; +} + + +#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) + +/* + * 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; +{ + ipstate_t ips; + register ipstate_t *is = &ips; + register u_int hv; + + if ((ip->ip_off & 0x1fff) || (fin->fin_fi.fi_fl & FI_SHORT)) + return -1; + if (ips_num == IPSTATE_MAX) { + ips_stats.iss_max++; + return -1; + } + ips.is_age = 1; + ips.is_state[0] = 0; + ips.is_state[1] = 0; + /* + * Copy and calculate... + */ + hv = (is->is_p = ip->ip_p); + hv += (is->is_src.s_addr = ip->ip_src.s_addr); + hv += (is->is_dst.s_addr = ip->ip_dst.s_addr); + + switch (ip->ip_p) + { + case IPPROTO_ICMP : + { + struct icmp *ic = (struct icmp *)fin->fin_dp; + + switch (ic->icmp_type) + { + case ICMP_ECHO : + is->is_icmp.ics_type = 0; + hv += (is->is_icmp.ics_id = ic->icmp_id); + hv += (is->is_icmp.ics_seq = ic->icmp_seq); + break; + case ICMP_TSTAMP : + case ICMP_IREQ : + case ICMP_MASKREQ : + is->is_icmp.ics_type = ic->icmp_type + 1; + break; + default : + return -1; + } + ips_stats.iss_icmp++; + is->is_age = fr_icmptimeout; + break; + } + case IPPROTO_TCP : + { + register tcphdr_t *tcp = (tcphdr_t *)fin->fin_dp; + + /* + * The endian of the ports doesn't matter, but the ack and + * sequence numbers do as we do mathematics on them later. + */ + hv += (is->is_dport = tcp->th_dport); + hv += (is->is_sport = tcp->th_sport); + is->is_seq = ntohl(tcp->th_seq); + is->is_ack = ntohl(tcp->th_ack); + is->is_swin = ntohs(tcp->th_win); + 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 + * 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); + break; + } + case IPPROTO_UDP : + { + register tcphdr_t *tcp = (tcphdr_t *)fin->fin_dp; + + hv += (is->is_dport = tcp->th_dport); + hv += (is->is_sport = tcp->th_sport); + ips_stats.iss_udp++; + is->is_age = fr_udptimeout; + break; + } + default : + return -1; + } + + if (!(is = (ipstate_t *)KMALLOC(sizeof(*is)))) { + ips_stats.iss_nomem++; + return -1; + } + bcopy((char *)&ips, (char *)is, sizeof(*is)); + hv %= IPSTATE_SIZE; + MUTEX_ENTER(&ipf_state); + is->is_next = ips_table[hv]; + ips_table[hv] = is; + is->is_pass = pass; + if (pass & FR_LOGFIRST) + is->is_pass &= ~(FR_LOGFIRST|FR_LOG); + ips_num++; + MUTEX_EXIT(&ipf_state); + return 0; +} + + +/* + * check to see if a packet with TCP headers fits within the TCP window. + * 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; +{ + register int seqskew, ackskew; + register u_short swin, dwin; + register tcp_seq seq, ack; + int source; + + /* + * Find difference between last checked packet and this packet. + */ + seq = ntohl(tcp->th_seq); + ack = ntohl(tcp->th_ack); + if (sport == is->is_sport) { + 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; + } + + /* + * Make skew values absolute + */ + if (seqskew < 0) + seqskew = -seqskew; + if (ackskew < 0) + ackskew = -ackskew; + + /* + * If the difference in sequence and ack numbers is within the + * window size of the connection, store these values and match + * the packet. + */ + if ((source = (sport == is->is_sport))) { + swin = is->is_swin; + dwin = is->is_dwin; + } else { + dwin = is->is_swin; + swin = is->is_dwin; + } + + if ((seqskew <= swin) && (ackskew <= dwin)) { + if (source) { + is->is_seq = seq; + is->is_ack = ack; + is->is_swin = ntohs(tcp->th_win); + } else { + is->is_seq = ack; + is->is_ack = seq; + is->is_dwin = ntohs(tcp->th_win); + } + ips_stats.iss_hits++; + /* + * Nearing end of connection, start timeout. + */ + set_tcp_age(&is->is_age, is->is_state, ip, fin, + tcp->th_sport == is->is_sport); + return 1; + } + return 0; +} + + +/* + * Check if a packet has a registered state. + */ +int fr_checkstate(ip, fin) +ip_t *ip; +fr_info_t *fin; +{ + register struct in_addr dst, src; + register ipstate_t *is, **isp; + register u_char pr; + struct icmp *ic; + tcphdr_t *tcp; + u_int hv, hlen; + + if ((ip->ip_off & 0x1fff) || (fin->fin_fi.fi_fl & FI_SHORT)) + return 0; + + hlen = fin->fin_hlen; + tcp = (tcphdr_t *)((char *)ip + hlen); + ic = (struct icmp *)tcp; + hv = (pr = ip->ip_p); + hv += (src.s_addr = ip->ip_src.s_addr); + hv += (dst.s_addr = ip->ip_dst.s_addr); + + /* + * Search the hash table for matching packet header info. + */ + switch (ip->ip_p) + { + case IPPROTO_ICMP : + hv += ic->icmp_id; + hv += ic->icmp_seq; + hv %= IPSTATE_SIZE; + MUTEX_ENTER(&ipf_state); + for (isp = &ips_table[hv]; (is = *isp); isp = &is->is_next) + if ((is->is_p == pr) && + (ic->icmp_id == is->is_icmp.ics_id) && + (ic->icmp_seq == is->is_icmp.ics_seq) && + IPPAIR(src, dst, is->is_src, is->is_dst)) { + /* + * If we have type 0 stored, allow any icmp + * replies through. + */ + if (is->is_icmp.ics_type && + is->is_icmp.ics_type != ic->icmp_type) + continue; + is->is_age = fr_icmptimeout; + ips_stats.iss_hits++; + MUTEX_EXIT(&ipf_state); + return is->is_pass; + } + MUTEX_EXIT(&ipf_state); + break; + case IPPROTO_TCP : + { + register u_short dport = tcp->th_dport, sport = tcp->th_sport; + + hv += dport; + hv += sport; + hv %= IPSTATE_SIZE; + MUTEX_ENTER(&ipf_state); + for (isp = &ips_table[hv]; (is = *isp); isp = &is->is_next) { + 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)) { +#ifdef _KERNEL + MUTEX_EXIT(&ipf_state); + return is->is_pass; +#else + int pass = is->is_pass; + + if (tcp->th_flags & TCP_CLOSE) { + *isp = is->is_next; + isp = &ips_table[hv]; + KFREE(is); + } + return pass; +#endif + } + } + MUTEX_EXIT(&ipf_state); + break; + } + case IPPROTO_UDP : + { + register u_short dport = tcp->th_dport, sport = tcp->th_sport; + + hv += dport; + hv += sport; + hv %= IPSTATE_SIZE; + /* + * Nothing else to match on but ports. and IP#'s + */ + MUTEX_ENTER(&ipf_state); + for (is = ips_table[hv]; is; is = is->is_next) + if ((is->is_p == pr) && + PAIRS(sport, dport, is->is_sport, is->is_dport) && + IPPAIR(src, dst, is->is_src, is->is_dst)) { + ips_stats.iss_hits++; + is->is_age = fr_udptimeout; + MUTEX_EXIT(&ipf_state); + return is->is_pass; + } + MUTEX_EXIT(&ipf_state); + break; + } + default : + break; + } + ips_stats.iss_miss++; + return 0; +} + + +/* + * Free memory in use by all state info. kept. + */ +void fr_stateunload() +{ + register int i; + register ipstate_t *is, **isp; + + MUTEX_ENTER(&ipf_state); + for (i = 0; i < IPSTATE_SIZE; i++) + for (isp = &ips_table[i]; (is = *isp); ) { + *isp = is->is_next; + KFREE(is); + } + MUTEX_EXIT(&ipf_state); +} + + +/* + * Slowly expire held state for thingslike UDP and ICMP. Timeouts are set + * in expectation of this being called twice per second. + */ +void fr_timeoutstate() +{ + register int i; + register ipstate_t *is, **isp; + + MUTEX_ENTER(&ipf_state); + for (i = 0; i < IPSTATE_SIZE; i++) + for (isp = &ips_table[i]; (is = *isp); ) + if (is->is_age && !--is->is_age) { + *isp = is->is_next; + if (is->is_p == IPPROTO_TCP) + ips_stats.iss_fin++; + else + ips_stats.iss_expire++; + KFREE(is); + ips_num--; + } else + isp = &is->is_next; + MUTEX_EXIT(&ipf_state); +} + + +/* + * Original idea freom Pradeep Krishnan for use primarily with NAT code. + * (pkrishna@netcom.com) + */ +void set_tcp_age(age, state, ip, fin, dir) +int *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; + int dlen, ostate; + + ostate = state[1 - dir]; + + dlen = ip->ip_len - fin->fin_hlen - (tcp->th_off << 2); + + if (flags & TH_RST) { + if (!(tcp->th_flags & TH_PUSH) && !dlen) { + *age = fr_tcpclosed; + state[dir] = TCPS_CLOSED; + } else { + *age = fr_tcpclosewait; + state[dir] = TCPS_CLOSE_WAIT; + } + return; + } + + *age = fr_tcptimeout; /* 1 min */ + + switch(state[dir]) + { + case TCPS_FIN_WAIT_2: + case TCPS_CLOSED: + if ((flags & TH_OPENING) == TH_OPENING) + state[dir] = TCPS_SYN_RECEIVED; + else if (flags & TH_SYN) + state[dir] = TCPS_SYN_SENT; + break; + case TCPS_SYN_RECEIVED: + if ((flags & (TH_FIN|TH_ACK)) == TH_ACK) { + state[dir] = TCPS_ESTABLISHED; + *age = fr_tcpidletimeout; + } + break; + case TCPS_SYN_SENT: + if ((flags & (TH_FIN|TH_ACK)) == TH_ACK) { + state[dir] = TCPS_ESTABLISHED; + *age = fr_tcpidletimeout; + } + break; + case TCPS_ESTABLISHED: + if (flags & TH_FIN) { + state[dir] = TCPS_CLOSE_WAIT; + if (!(flags & TH_PUSH) && !dlen && + ostate > TCPS_ESTABLISHED) + *age = fr_tcplastack; + else + *age = fr_tcpclosewait; + } else + *age = fr_tcpidletimeout; + break; + case TCPS_CLOSE_WAIT: + if ((flags & TH_FIN) && !(flags & TH_PUSH) && !dlen && + ostate > TCPS_ESTABLISHED) { + *age = fr_tcplastack; + state[dir] = TCPS_LAST_ACK; + } else + *age = fr_tcpclosewait; + break; + case TCPS_LAST_ACK: + if (flags & TH_ACK) { + state[dir] = TCPS_FIN_WAIT_2; + if (!(flags & TH_PUSH) && !dlen && + ostate > TCPS_ESTABLISHED) + *age = fr_tcplastack; + else { + *age = fr_tcpclosewait; + state[dir] = TCPS_CLOSE_WAIT; + } + } + break; + } +} diff --git a/sys/netinet/ip_state.h b/sys/netinet/ip_state.h new file mode 100644 index 0000000..ee30b98 --- /dev/null +++ b/sys/netinet/ip_state.h @@ -0,0 +1,86 @@ +/* + * (C)opyright 1995 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 + * $Id: ip_state.h,v 2.0.1.1 1997/01/09 15:14:43 darrenr Exp $ + */ +#ifndef __IP_STATE_H__ +#define __IP_STATE_H__ + +#define IPSTATE_SIZE 257 +#define IPSTATE_MAX 2048 /* Maximum number of states held */ + +typedef struct udpstate { + u_short us_sport; + u_short us_dport; +} udpstate_t; + +typedef struct icmpstate { + u_short ics_id; + u_short ics_seq; + u_char ics_type; +} icmpstate_t; + +typedef struct tcpstate { + u_short ts_sport; + u_short ts_dport; + u_long ts_seq; + u_long ts_ack; + u_short ts_swin; + u_short ts_dwin; + u_char ts_state[2]; +} tcpstate_t; + +typedef struct ipstate { + struct ipstate *is_next; + int is_age; + u_int is_pass; + struct in_addr is_src; + struct in_addr is_dst; + u_char is_p; + u_char is_flags; + union { + icmpstate_t is_ics; + tcpstate_t is_ts; + udpstate_t is_us; + } is_ps; +} ipstate_t; + +#define is_icmp is_ps.is_ics +#define is_tcp is_ps.is_ts +#define is_udp is_ps.is_us +#define is_seq is_tcp.ts_seq +#define is_ack is_tcp.ts_ack +#define is_dwin is_tcp.ts_dwin +#define is_swin is_tcp.ts_swin +#define is_sport is_tcp.ts_sport +#define is_dport is_tcp.ts_dport +#define is_state is_tcp.ts_state + +#define TH_OPENING (TH_SYN|TH_ACK) + +typedef struct ips_stat { + u_long iss_hits; + u_long iss_miss; + u_long iss_max; + u_long iss_tcp; + u_long iss_udp; + u_long iss_icmp; + u_long iss_nomem; + u_long iss_expire; + u_long iss_fin; + u_long iss_active; + ipstate_t **iss_table; +} ips_stat_t; + +extern ips_stat_t *fr_statetstats(); +extern int fr_addstate(), fr_checkstate(); +extern void fr_timeoutstate(), set_tcp_age(); +# ifdef _KERNEL +extern void fr_stateunload(); +# endif +#endif /* __IP_STATE_H__ */ |