Index: head/sys/contrib/ipfilter/netinet/fil.c =================================================================== --- head/sys/contrib/ipfilter/netinet/fil.c (revision 60856) +++ head/sys/contrib/ipfilter/netinet/fil.c (revision 60857) @@ -1,2013 +1,2013 @@ /* * Copyright (C) 1993-2000 by Darren Reed. * * Redistribution and use in source and binary forms are permitted * provided that this notice is preserved and due credit is given * to the original author and the contributors. */ #if !defined(lint) static const char sccsid[] = "@(#)fil.c 1.36 6/5/96 (C) 1993-1996 Darren Reed"; /* static const char rcsid[] = "@(#)$Id: fil.c,v 2.3.2.16 2000/01/27 08:49:37 darrenr Exp $"; */ static const char rcsid[] = "@(#)$FreeBSD$"; #endif #if defined(_KERNEL) && defined(__FreeBSD_version) && \ (__FreeBSD_version >= 400000) && !defined(KLD_MODULE) #include "opt_inet6.h" #endif #include #include #include #include #include #if defined(__NetBSD__) && (NetBSD >= 199905) && !defined(IPFILTER_LKM) && \ defined(_KERNEL) # include "opt_ipfilter_log.h" #endif #if (defined(KERNEL) || defined(_KERNEL)) && defined(__FreeBSD_version) && \ (__FreeBSD_version >= 220000) # include # include #else # include #endif #if (defined(_KERNEL) || defined(KERNEL)) && !defined(linux) # include #else # include # include # include #endif #include #if !defined(__SVR4) && !defined(__svr4__) # ifndef linux # include # endif #else # include # if SOLARIS2 < 5 # include # endif # include #endif #ifndef linux # include # include #endif #include #ifdef sun # include #endif #include #include #include #include #ifndef linux # include #endif #if defined(__sgi) && defined(IFF_DRVRLOCK) /* IRIX 6 */ # include # include #endif #include #include #include #include "netinet/ip_compat.h" #ifdef USE_INET6 # include # if !SOLARIS && defined(_KERNEL) # include # endif #endif #include #include "netinet/ip_fil.h" #include "netinet/ip_proxy.h" #include "netinet/ip_nat.h" #include "netinet/ip_frag.h" #include "netinet/ip_state.h" #include "netinet/ip_auth.h" # if defined(__FreeBSD_version) && (__FreeBSD_version >= 300000) # include # if defined(_KERNEL) && !defined(IPFILTER_LKM) # include "opt_ipfilter.h" # endif # endif #ifndef MIN # define MIN(a,b) (((a)<(b))?(a):(b)) #endif #include "netinet/ipl.h" #include #ifndef _KERNEL # include "ipf.h" # include "ipt.h" extern int opts; # define FR_VERBOSE(verb_pr) verbose verb_pr # define FR_DEBUG(verb_pr) debug verb_pr # define IPLLOG(a, c, d, e) ipllog() #else /* #ifndef _KERNEL */ # define FR_VERBOSE(verb_pr) # define FR_DEBUG(verb_pr) # define IPLLOG(a, c, d, e) ipflog(a, c, d, e) # if SOLARIS || defined(__sgi) extern KRWLOCK_T ipf_mutex, ipf_auth, ipf_nat; extern kmutex_t ipf_rw; # endif # if SOLARIS # define FR_NEWAUTH(m, fi, ip, qif) fr_newauth((mb_t *)m, fi, \ ip, qif) # define SEND_RESET(ip, qif, if, fin) send_reset(fin, ip, qif) # else /* SOLARIS */ # define FR_NEWAUTH(m, fi, ip, qif) fr_newauth((mb_t *)m, fi, ip) # define SEND_RESET(ip, qif, if, fin) send_reset(fin, ip) # endif /* SOLARIS || __sgi */ #endif /* _KERNEL */ struct filterstats frstats[2] = {{0,0,0,0,0},{0,0,0,0,0}}; struct frentry *ipfilter[2][2] = { { NULL, NULL }, { NULL, NULL } }, #ifdef USE_INET6 *ipfilter6[2][2] = { { NULL, NULL }, { NULL, NULL } }, *ipacct6[2][2] = { { NULL, NULL }, { NULL, NULL } }, #endif *ipacct[2][2] = { { NULL, NULL }, { NULL, NULL } }; struct frgroup *ipfgroups[3][2]; int fr_flags = IPF_LOGGING; int fr_active = 0; int fr_chksrc = 0; #if defined(IPFILTER_DEFAULT_BLOCK) int fr_pass = FR_NOMATCH|FR_BLOCK; #else int fr_pass = (IPF_DEFAULT_PASS|FR_NOMATCH); #endif char ipfilter_version[] = IPL_VERSION; fr_info_t frcache[2]; static int frflushlist __P((int, minor_t, int *, frentry_t **)); #ifdef _KERNEL static void frsynclist __P((frentry_t *)); #endif /* * 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; { u_short optmsk = 0, secmsk = 0, auth = 0; int i, mv, ol, off, p, plen, v; fr_ip_t *fi = &fin->fin_fi; struct optlist *op; u_char *s, opt; tcphdr_t *tcp; fin->fin_rev = 0; fin->fin_fr = NULL; fin->fin_tcpf = 0; fin->fin_data[0] = 0; fin->fin_data[1] = 0; fin->fin_rule = -1; fin->fin_group = -1; #ifdef _KERNEL fin->fin_icode = ipl_unreach; #endif v = fin->fin_v; fi->fi_v = v; fin->fin_hlen = hlen; if (v == 4) { fin->fin_id = ip->ip_id; fi->fi_tos = ip->ip_tos; off = (ip->ip_off & IP_OFFMASK) << 3; tcp = (tcphdr_t *)((char *)ip + hlen); (*(((u_short *)fi) + 1)) = (*(((u_short *)ip) + 4)); fi->fi_src.i6[1] = 0; fi->fi_src.i6[2] = 0; fi->fi_src.i6[3] = 0; fi->fi_dst.i6[1] = 0; fi->fi_dst.i6[2] = 0; fi->fi_dst.i6[3] = 0; fi->fi_saddr = ip->ip_src.s_addr; fi->fi_daddr = ip->ip_dst.s_addr; p = ip->ip_p; fi->fi_fl = (hlen > sizeof(ip_t)) ? FI_OPTIONS : 0; if (ip->ip_off & 0x3fff) fi->fi_fl |= FI_FRAG; plen = ip->ip_len; fin->fin_dlen = plen - hlen; } #ifdef USE_INET6 else if (v == 6) { ip6_t *ip6 = (ip6_t *)ip; off = 0; p = ip6->ip6_nxt; fi->fi_p = p; fi->fi_ttl = ip6->ip6_hlim; tcp = (tcphdr_t *)(ip6 + 1); fi->fi_src.in6 = ip6->ip6_src; fi->fi_dst.in6 = ip6->ip6_dst; fin->fin_id = (u_short)(ip6->ip6_flow & 0xffff); fi->fi_tos = 0; fi->fi_fl = 0; plen = ntohs(ip6->ip6_plen); fin->fin_dlen = plen; } #endif else return; fin->fin_off = off; fin->fin_plen = plen; fin->fin_dp = (void *)tcp; switch (p) { case IPPROTO_ICMP : { int minicmpsz = sizeof(struct icmp); icmphdr_t *icmp; icmp = (icmphdr_t *)tcp; if (!off && (icmp->icmp_type == ICMP_ECHOREPLY || icmp->icmp_type == ICMP_ECHO)) minicmpsz = ICMP_MINLEN; /* type(1) + code(1) + cksum(2) + id(2) seq(2) + * 3*timestamp(3*4) */ else if (!off && (icmp->icmp_type == ICMP_TSTAMP || icmp->icmp_type == ICMP_TSTAMPREPLY)) minicmpsz = 20; /* type(1) + code(1) + cksum(2) + id(2) seq(2) + mask(4) */ else if (!off && (icmp->icmp_type == ICMP_MASKREQ || icmp->icmp_type == ICMP_MASKREPLY)) minicmpsz = 12; if ((!(plen >= hlen + minicmpsz) && !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; #ifdef USE_INET6 if (v == 6) { if (plen < sizeof(struct tcphdr)) fi->fi_fl |= FI_SHORT; } else #endif if (v == 4) { 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; #ifdef USE_INET6 if (v == 6) { if (plen < sizeof(struct udphdr)) fi->fi_fl |= FI_SHORT; } else #endif if (v == 4) { 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; } #ifdef USE_INET6 if (v == 6) { fi->fi_optmsk = 0; fi->fi_secmsk = 0; fi->fi_auth = 0; return; } #endif for (s = (u_char *)(ip + 1), hlen -= (int)sizeof(*ip); hlen > 0; ) { opt = *s; if (opt == '\0') break; else if (opt == IPOPT_NOP) ol = 1; else { if (hlen < 2) break; ol = (int)*(s + 1); if (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(ft, fin) frtuc_t *ft; 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)ft->ftu_dcmp)) { po = ft->ftu_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 <= ft->ftu_dtop)) err = 0; else if (!--i && /* In range */ (tup <= po || tup >= ft->ftu_dtop)) err = 0; } /* * compare source ports */ if (err && (i = (int)ft->ftu_scmp)) { po = ft->ftu_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 <= ft->ftu_stop)) err = 0; else if (!--i && /* In range */ (tup <= po || tup >= ft->ftu_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 !(ft->ftu_tcpf | ft->ftu_tcpfm); /* * Match the flags ? If not, abort this match. */ if (ft->ftu_tcpfm && ft->ftu_tcpf != (fin->fin_tcpf & ft->ftu_tcpfm)) { FR_DEBUG(("f. %#x & %#x != %#x\n", fin->fin_tcpf, ft->ftu_tcpfm, ft->ftu_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) u_32_t 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, skip = 0, logged = 0; u_32_t passt; fr = fin->fin_fr; fin->fin_fr = NULL; fin->fin_rule = 0; fin->fin_group = 0; if (fin->fin_v == 4) off = ip->ip_off & IP_OFFMASK; else off = 0; pass |= (fi->fi_fl << 24); if ((fi->fi_fl & FI_TCPUDP) && (fin->fin_dlen > 3) && !off) portcmp = 1; for (rulen = 0; fr; fr = fr->fr_next, rulen++) { if (skip) { skip--; continue; } /* * In all checks below, a null (zero) value in the * filter struture is taken to mean a wildcard. * * check that we are working for the right interface */ #ifdef _KERNEL # if BSD >= 199306 if (fin->fin_out != 0) { if ((fr->fr_oifa && fr->fr_oifa != ((mb_t *)m)->m_pkthdr.rcvif) || (fr->fr_ifa && fr->fr_ifa != fin->fin_ifp)) continue; } else # endif 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' : (pass & FR_AUTH) ? 'a' : 'b')); if (fr->fr_ifa && fr->fr_ifa != fin->fin_ifp) continue; FR_VERBOSE((":i")); #endif { register u_32_t *ld, *lm, *lip; register int i; lip = (u_32_t *)fi; lm = (u_32_t *)&fr->fr_mip; ld = (u_32_t *)&fr->fr_ip; i = ((*lip & *lm) != *ld); FR_DEBUG(("0. %#08x & %#08x != %#08x\n", *lip, *lm, *ld)); if (i) continue; /* * We now know whether the packet version and the * rule version match, along with protocol, ttl and * tos. */ lip++, lm++, ld++; /* * Unrolled loops (4 each, for 32 bits). */ i |= ((*lip & *lm) != *ld) << 19; FR_DEBUG(("1a. %#08x & %#08x != %#08x\n", *lip, *lm, *ld)); if (fi->fi_v == 6) { lip++, lm++, ld++; i |= ((*lip & *lm) != *ld) << 19; FR_DEBUG(("1b. %#08x & %#08x != %#08x\n", *lip, *lm, *ld)); lip++, lm++, ld++; i |= ((*lip & *lm) != *ld) << 19; FR_DEBUG(("1c. %#08x & %#08x != %#08x\n", *lip, *lm, *ld)); lip++, lm++, ld++; i |= ((*lip & *lm) != *ld) << 19; FR_DEBUG(("1d. %#08x & %#08x != %#08x\n", *lip, *lm, *ld)); } else { lip += 3; lm += 3; ld += 3; } i ^= (fr->fr_flags & FR_NOTSRCIP); if (i) continue; lip++, lm++, ld++; i |= ((*lip & *lm) != *ld) << 20; FR_DEBUG(("2a. %#08x & %#08x != %#08x\n", *lip, *lm, *ld)); if (fi->fi_v == 6) { lip++, lm++, ld++; i |= ((*lip & *lm) != *ld) << 20; FR_DEBUG(("2b. %#08x & %#08x != %#08x\n", *lip, *lm, *ld)); lip++, lm++, ld++; i |= ((*lip & *lm) != *ld) << 20; FR_DEBUG(("2c. %#08x & %#08x != %#08x\n", *lip, *lm, *ld)); lip++, lm++, ld++; i |= ((*lip & *lm) != *ld) << 20; FR_DEBUG(("2d. %#08x & %#08x != %#08x\n", *lip, *lm, *ld)); } else { lip += 3; lm += 3; ld += 3; } i ^= (fr->fr_flags & FR_NOTDSTIP); if (i) continue; lip++, lm++, ld++; i |= ((*lip & *lm) != *ld); FR_DEBUG(("3. %#08x & %#08x != %#08x\n", *lip, *lm, *ld)); lip++, lm++, ld++; i |= ((*lip & *lm) != *ld); FR_DEBUG(("4. %#08x & %#08x != %#08x\n", *lip, *lm, *ld)); if (i) continue; } /* * If a fragment, then only the first has what we're looking * for here... */ if (!portcmp && (fr->fr_dcmp || fr->fr_scmp || fr->fr_tcpf || fr->fr_tcpfm)) continue; if (fi->fi_fl & FI_TCPUDP) { if (!fr_tcpudpchk(&fr->fr_tuc, fin)) continue; } else if (fr->fr_icmpm || fr->fr_icmp) { if ((fi->fi_p != IPPROTO_ICMP) || off || (fin->fin_dlen < 2)) continue; 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; } } FR_VERBOSE(("*")); /* * Just log this packet... */ passt = fr->fr_flags; if ((passt & FR_CALLNOW) && fr->fr_func) passt = (*fr->fr_func)(passt, ip, fin); fin->fin_fr = fr; #ifdef IPFILTER_LOG if ((passt & FR_LOGMASK) == FR_LOG) { if (!IPLLOG(passt, ip, fin, m)) { if (passt & FR_LOGORBLOCK) passt |= FR_BLOCK|FR_QUICK; ATOMIC_INCL(frstats[fin->fin_out].fr_skip); } ATOMIC_INCL(frstats[fin->fin_out].fr_pkl); logged = 1; } #endif /* IPFILTER_LOG */ if (!(skip = fr->fr_skip) && (passt & FR_LOGMASK) != FR_LOG) pass = passt; FR_DEBUG(("pass %#x\n", pass)); ATOMIC_INCL(fr->fr_hits); if (pass & FR_ACCOUNT) fr->fr_bytes += (U_QUAD_T)ip->ip_len; else fin->fin_icode = fr->fr_icode; fin->fin_rule = rulen; fin->fin_group = fr->fr_group; if (fr->fr_grp) { fin->fin_fr = fr->fr_grp; pass = fr_scanlist(pass, ip, fin, m); if (fin->fin_fr == NULL) { fin->fin_rule = rulen; fin->fin_group = fr->fr_group; fin->fin_fr = fr; } if (pass & FR_DONTCACHE) logged = 1; } if (pass & FR_QUICK) break; } if (logged) pass |= FR_DONTCACHE; return pass; } /* * frcheck - filter check * check using source and destination addresses/ports in a packet whether * or not to pass it on or not. */ int fr_check(ip, hlen, ifp, out #if defined(_KERNEL) && SOLARIS , qif, mp) qif_t *qif; #else , mp) #endif mb_t **mp; ip_t *ip; int hlen; void *ifp; int out; { /* * The above really sucks, but short of writing a diff */ fr_info_t frinfo, *fc; register fr_info_t *fin = &frinfo; int changed, error = EHOSTUNREACH, v = ip->ip_v; frentry_t *fr = NULL, *list; u_32_t pass, apass; #if !SOLARIS || !defined(_KERNEL) register mb_t *m = *mp; #endif #ifdef _KERNEL mb_t *mc = NULL; # if !defined(__SVR4) && !defined(__svr4__) # ifdef __sgi char hbuf[(0xf << 2) + sizeof(struct icmp) + sizeof(ip_t) + 8]; # endif int up; # ifdef M_CANFASTFWD /* * XXX For now, IP Filter and fast-forwarding of cached flows * XXX are mutually exclusive. Eventually, IP Filter should * XXX get a "can-fast-forward" filter rule. */ m->m_flags &= ~M_CANFASTFWD; # endif /* M_CANFASTFWD */ # ifdef CSUM_DELAY_DATA /* * disable delayed checksums. */ if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) { in_delayed_cksum(m); m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA; } # endif /* CSUM_DELAY_DATA */ if ((ip->ip_p == IPPROTO_TCP || ip->ip_p == IPPROTO_UDP || ip->ip_p == IPPROTO_ICMP)) { int plen = 0; if ((ip->ip_off & IP_OFFMASK) == 0) switch(ip->ip_p) { case IPPROTO_TCP: plen = sizeof(tcphdr_t); break; case IPPROTO_UDP: plen = sizeof(udphdr_t); break; /* 96 - enough for complete ICMP error IP header */ case IPPROTO_ICMP: plen = ICMPERR_MAXPKTLEN - sizeof(ip_t); break; } up = MIN(hlen + plen, ip->ip_len); if (up > m->m_len) { # ifdef __sgi /* Under IRIX, avoid m_pullup as it makes ping panic */ if ((up > sizeof(hbuf)) || (m_length(m) < up)) { ATOMIC_INCL(frstats[out].fr_pull[1]); return -1; } m_copydata(m, 0, up, hbuf); ATOMIC_INCL(frstats[out].fr_pull[0]); ip = (ip_t *)hbuf; # else /* __ sgi */ # ifndef linux if ((*mp = m_pullup(m, up)) == 0) { ATOMIC_INCL(frstats[out].fr_pull[1]); return -1; } else { ATOMIC_INCL(frstats[out].fr_pull[0]); m = *mp; ip = mtod(m, ip_t *); } # endif /* !linux */ # endif /* __sgi */ } else up = 0; } else up = 0; # endif /* !defined(__SVR4) && !defined(__svr4__) */ # if SOLARIS mb_t *m = qif->qf_m; if ((u_int)ip & 0x3) return 2; fin->fin_qfm = m; fin->fin_qif = qif; # endif # ifdef USE_INET6 if (v == 6) { ATOMIC_INCL(frstats[0].fr_ipv6[out]); } else # endif if (!out && fr_chksrc && !fr_verifysrc(ip->ip_src, ifp)) { ATOMIC_INCL(frstats[0].fr_badsrc); # if !SOLARIS m_freem(m); # endif return error; } #endif /* _KERNEL */ /* * Be careful here: ip_id is in network byte order when called * from ip_output() */ if ((out) && (v == 4)) ip->ip_id = ntohs(ip->ip_id); changed = 0; fin->fin_v = v; fin->fin_ifp = ifp; fin->fin_out = out; fin->fin_mp = mp; fr_makefrip(hlen, ip, fin); pass = fr_pass; if (fin->fin_fi.fi_fl & FI_SHORT) { ATOMIC_INCL(frstats[out].fr_short); } READ_ENTER(&ipf_mutex); if (fin->fin_fi.fi_fl & FI_SHORT) ATOMIC_INCL(frstats[out].fr_short); /* * Check auth now. This, combined with the check below to see if apass * is 0 is to ensure that we don't count the packet twice, which can * otherwise occur when we reprocess it. As it is, we only count it * after it has no auth. table matchup. This also stops NAT from * occuring until after the packet has been auth'd. */ apass = fr_checkauth(ip, fin); if (!out) { #ifdef USE_INET6 if (v == 6) list = ipacct6[0][fr_active]; else #endif list = ipacct[0][fr_active]; changed = ip_natin(ip, fin); if (!apass && (fin->fin_fr = list) && (fr_scanlist(FR_NOMATCH, ip, fin, m) & FR_ACCOUNT)) { ATOMIC_INCL(frstats[0].fr_acct); } } if (apass || (!(fr = ipfr_knownfrag(ip, fin)) && !(fr = fr_checkstate(ip, fin)))) { /* * If a packet is found in the auth table, then skip checking * the access lists for permission but we do need to consider * the result as if it were from the ACL's. */ if (!apass) { fc = frcache + out; if (!bcmp((char *)fin, (char *)fc, FI_CSIZE)) { /* * copy cached data so we can unlock the mutex * earlier. */ bcopy((char *)fc, (char *)fin, FI_COPYSIZE); ATOMIC_INCL(frstats[out].fr_chit); if ((fr = fin->fin_fr)) { ATOMIC_INCL(fr->fr_hits); pass = fr->fr_flags; } } else { #ifdef USE_INET6 if (v == 6) list = ipfilter6[out][fr_active]; else #endif list = ipfilter[out][fr_active]; if ((fin->fin_fr = list)) pass = fr_scanlist(fr_pass, ip, fin, m); if (!(pass & (FR_KEEPSTATE|FR_DONTCACHE))) bcopy((char *)fin, (char *)fc, FI_COPYSIZE); if (pass & FR_NOMATCH) { ATOMIC_INCL(frstats[out].fr_nom); } } fr = fin->fin_fr; } else pass = apass; /* * If we fail to add a packet to the authorization queue, * then we drop the packet later. However, if it was added * then pretend we've dropped it already. */ if ((pass & FR_AUTH)) if (fr_newauth((mb_t *)m, fin, ip) != 0) #ifdef _KERNEL m = *mp = NULL; #else ; #endif if (pass & FR_PREAUTH) { READ_ENTER(&ipf_auth); if ((fin->fin_fr = ipauth) && (pass = fr_scanlist(0, ip, fin, m))) { ATOMIC_INCL(fr_authstats.fas_hits); } else { ATOMIC_INCL(fr_authstats.fas_miss); } RWLOCK_EXIT(&ipf_auth); } fin->fin_fr = fr; if ((pass & (FR_KEEPFRAG|FR_KEEPSTATE)) == FR_KEEPFRAG) { if (fin->fin_fi.fi_fl & FI_FRAG) { if (ipfr_newfrag(ip, fin, pass) == -1) { ATOMIC_INCL(frstats[out].fr_bnfr); } else { ATOMIC_INCL(frstats[out].fr_nfr); } } else { ATOMIC_INCL(frstats[out].fr_cfr); } } if (pass & FR_KEEPSTATE) { if (fr_addstate(ip, fin, 0) == NULL) { ATOMIC_INCL(frstats[out].fr_bads); } else { ATOMIC_INCL(frstats[out].fr_ads); } } } else if (fr != NULL) { pass = fr->fr_flags; if (pass & FR_LOGFIRST) pass &= ~(FR_LOGFIRST|FR_LOG); } if (fr && fr->fr_func && !(pass & FR_CALLNOW)) pass = (*fr->fr_func)(pass, ip, fin); /* * Only count/translate packets which will be passed on, out the * interface. */ if (out && (pass & FR_PASS)) { #ifdef USE_INET6 if (v == 6) list = ipacct6[0][fr_active]; else #endif list = ipacct[0][fr_active]; if ((fin->fin_fr = list) && (fr_scanlist(FR_NOMATCH, ip, fin, m) & FR_ACCOUNT)) { ATOMIC_INCL(frstats[1].fr_acct); } fin->fin_fr = fr; changed = ip_natout(ip, fin); } else fin->fin_fr = fr; RWLOCK_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; ATOMIC_INCL(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; ATOMIC_INCL(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; ATOMIC_INCL(frstats[out].fr_bpkl); logit: if (!IPLLOG(pass, ip, fin, m)) { ATOMIC_INCL(frstats[out].fr_skip); if ((pass & (FR_PASS|FR_LOGORBLOCK)) == (FR_PASS|FR_LOGORBLOCK)) pass ^= FR_PASS|FR_BLOCK; } } } #endif /* IPFILTER_LOG */ if ((out) && (v == 4)) ip->ip_id = htons(ip->ip_id); #ifdef _KERNEL /* * Only allow FR_DUP to work if a rule matched - it makes no sense to * set FR_DUP as a "default" as there are no instructions about where * to send the packet. */ if (fr && (pass & FR_DUP)) # if SOLARIS mc = dupmsg(m); # else # ifndef linux mc = m_copy(m, 0, M_COPYALL); # else ; # endif # endif #endif if (pass & FR_PASS) { ATOMIC_INCL(frstats[out].fr_pass); } else if (pass & FR_BLOCK) { ATOMIC_INCL(frstats[out].fr_block); /* * Should we return an ICMP packet to indicate error * status passing through the packet filter ? * WARNING: ICMP error packets AND TCP RST packets should * ONLY be sent in repsonse to incoming packets. Sending them * in response to outbound packets can result in a panic on * some operating systems. */ if (!out) { #ifdef _KERNEL if (pass & FR_RETICMP) { int dst; if ((pass & FR_RETMASK) == FR_FAKEICMP) dst = 1; else dst = 0; send_icmp_err(ip, ICMP_UNREACH, fin, dst); ATOMIC_INCL(frstats[0].fr_ret); } else if (((pass & FR_RETMASK) == FR_RETRST) && !(fin->fin_fi.fi_fl & FI_SHORT)) { if (send_reset(ip, fin) == 0) { ATOMIC_INCL(frstats[1].fr_ret); } } #else if ((pass & FR_RETMASK) == FR_RETICMP) { verbose("- ICMP unreachable sent\n"); ATOMIC_INCL(frstats[0].fr_ret); } else if ((pass & FR_RETMASK) == FR_FAKEICMP) { verbose("- forged ICMP unreachable sent\n"); ATOMIC_INCL(frstats[0].fr_ret); } else if (((pass & FR_RETMASK) == FR_RETRST) && !(fin->fin_fi.fi_fl & FI_SHORT)) { verbose("- TCP RST sent\n"); ATOMIC_INCL(frstats[1].fr_ret); } #endif } else { if (pass & FR_RETRST) error = ECONNRESET; } } /* * If we didn't drop off the bottom of the list of rules (and thus * the 'current' rule fr is not NULL), then we may have some extra * instructions about what to do with a packet. * Once we're finished return to our caller, freeing the packet if * we are dropping it (* BSD ONLY *). */ if ((changed == -1) && (pass & FR_PASS)) { pass &= ~FR_PASS; pass |= FR_BLOCK; } #if defined(_KERNEL) # if !SOLARIS # if !defined(linux) if (fr) { frdest_t *fdp = &fr->fr_tif; if (((pass & FR_FASTROUTE) && !out) || (fdp->fd_ifp && fdp->fd_ifp != (struct ifnet *)-1)) { if (ipfr_fastroute(m, fin, fdp) == 0) m = *mp = NULL; } if (mc) ipfr_fastroute(mc, fin, &fr->fr_dif); } if (!(pass & FR_PASS) && m) m_freem(m); # ifdef __sgi else if (changed && up && m) m_copyback(m, 0, up, hbuf); # endif # endif /* !linux */ # else /* !SOLARIS */ if (fr) { frdest_t *fdp = &fr->fr_tif; if (((pass & FR_FASTROUTE) && !out) || (fdp->fd_ifp && fdp->fd_ifp != (struct ifnet *)-1)) { if (ipfr_fastroute(qif, ip, m, mp, fin, fdp) == 0) m = *mp = NULL; } if (mc) ipfr_fastroute(qif, ip, mc, mp, fin, &fr->fr_dif); } # endif /* !SOLARIS */ return (pass & FR_PASS) ? 0 : error; #else /* _KERNEL */ if (pass & FR_NOMATCH) return 1; if (pass & FR_PASS) return 0; if (pass & FR_AUTH) return -2; return -1; #endif /* _KERNEL */ } /* * ipf_cksum * addr should be 16bit aligned and len is in bytes. * length is in bytes */ u_short ipf_cksum(addr, len) register u_short *addr; register int len; { register u_32_t sum = 0; for (sum = 0; len > 1; len -= 2) sum += *addr++; /* mop up an odd byte, if necessary */ if (len == 1) sum += *(u_char *)addr; /* * add back carry outs from top 16 bits to low 16 bits */ sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ sum += (sum >> 16); /* add carry */ return (u_short)(~sum); } /* * NB: This function assumes we've pullup'd enough for all of the IP header * and the TCP header. We also assume that data blocks aren't allocated in * odd sizes. */ u_short fr_tcpsum(m, ip, tcp) mb_t *m; ip_t *ip; tcphdr_t *tcp; { u_short *sp, slen, ts; u_int sum, sum2; int hlen; /* * Add up IP Header portion */ hlen = ip->ip_hl << 2; slen = ip->ip_len - hlen; sum = htons((u_short)ip->ip_p); sum += htons(slen); sp = (u_short *)&ip->ip_src; sum += *sp++; /* ip_src */ sum += *sp++; sum += *sp++; /* ip_dst */ sum += *sp++; ts = tcp->th_sum; tcp->th_sum = 0; #ifdef KERNEL # if SOLARIS sum2 = ip_cksum(m, hlen, sum); /* hlen == offset */ sum2 = (sum2 & 0xffff) + (sum2 >> 16); sum2 = ~sum2 & 0xffff; # else /* SOLARIS */ # if defined(BSD) || defined(sun) # if BSD >= 199306 m->m_data += hlen; # else m->m_off += hlen; # endif m->m_len -= hlen; sum2 = in_cksum(m, slen); m->m_len += hlen; # if BSD >= 199306 m->m_data -= hlen; # else m->m_off -= hlen; # endif /* * Both sum and sum2 are partial sums, so combine them together. */ sum = (sum & 0xffff) + (sum >> 16); sum = ~sum & 0xffff; sum2 += sum; sum2 = (sum2 & 0xffff) + (sum2 >> 16); # else /* defined(BSD) || defined(sun) */ { union { u_char c[2]; u_short s; } bytes; u_short len = ip->ip_len; # if defined(__sgi) int add; # endif /* * Add up IP Header portion */ sp = (u_short *)&ip->ip_src; len -= (ip->ip_hl << 2); sum = ntohs(IPPROTO_TCP); sum += htons(len); sum += *sp++; /* ip_src */ sum += *sp++; sum += *sp++; /* ip_dst */ sum += *sp++; if (sp != (u_short *)tcp) sp = (u_short *)tcp; sum += *sp++; /* sport */ sum += *sp++; /* dport */ sum += *sp++; /* seq */ sum += *sp++; sum += *sp++; /* ack */ sum += *sp++; sum += *sp++; /* off */ sum += *sp++; /* win */ sum += *sp++; /* Skip over checksum */ sum += *sp++; /* urp */ # ifdef __sgi /* * In case we had to copy the IP & TCP header out of mbufs, * skip over the mbuf bits which are the header */ if ((caddr_t)ip != mtod(m, caddr_t)) { hlen = (caddr_t)sp - (caddr_t)ip; while (hlen) { add = MIN(hlen, m->m_len); sp = (u_short *)(mtod(m, caddr_t) + add); hlen -= add; if (add == m->m_len) { m = m->m_next; if (!hlen) { if (!m) break; sp = mtod(m, u_short *); } PANIC((!m),("fr_tcpsum(1): not enough data")); } } } # endif if (!(len -= sizeof(*tcp))) goto nodata; while (len > 1) { if (((caddr_t)sp - mtod(m, caddr_t)) >= m->m_len) { m = m->m_next; PANIC((!m),("fr_tcpsum(2): not enough data")); sp = mtod(m, u_short *); } if (((caddr_t)(sp + 1) - mtod(m, caddr_t)) > m->m_len) { bytes.c[0] = *(u_char *)sp; m = m->m_next; PANIC((!m),("fr_tcpsum(3): not enough data")); sp = mtod(m, u_short *); bytes.c[1] = *(u_char *)sp; sum += bytes.s; sp = (u_short *)((u_char *)sp + 1); } if ((u_long)sp & 1) { bcopy((char *)sp++, (char *)&bytes.s, sizeof(bytes.s)); sum += bytes.s; } else sum += *sp++; len -= 2; } if (len) sum += ntohs(*(u_char *)sp << 8); nodata: while (sum > 0xffff) sum = (sum & 0xffff) + (sum >> 16); sum2 = (u_short)(~sum & 0xffff); } # endif /* defined(BSD) || defined(sun) */ # endif /* SOLARIS */ #else /* KERNEL */ sum2 = 0; #endif /* KERNEL */ tcp->th_sum = ts; return sum2; } #if defined(_KERNEL) && ( ((BSD < 199306) && !SOLARIS) || defined(__sgi) ) /* * Copyright (c) 1982, 1986, 1988, 1991, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)uipc_mbuf.c 8.2 (Berkeley) 1/4/94 * $Id: fil.c,v 2.35.2.8 2000/05/22 10:26:09 darrenr Exp $ */ /* * Copy data from an mbuf chain starting "off" bytes from the beginning, * continuing for "len" bytes, into the indicated buffer. */ void m_copydata(m, off, len, cp) register mb_t *m; register int off; register int len; caddr_t cp; { register unsigned count; if (off < 0 || len < 0) panic("m_copydata"); while (off > 0) { if (m == 0) panic("m_copydata"); if (off < m->m_len) break; off -= m->m_len; m = m->m_next; } while (len > 0) { if (m == 0) panic("m_copydata"); count = MIN(m->m_len - off, len); bcopy(mtod(m, caddr_t) + off, cp, count); len -= count; cp += count; off = 0; m = m->m_next; } } # ifndef linux /* * Copy data from a buffer back into the indicated mbuf chain, * starting "off" bytes from the beginning, extending the mbuf * chain if necessary. */ void m_copyback(m0, off, len, cp) struct mbuf *m0; register int off; register int len; caddr_t cp; { register int mlen; register struct mbuf *m = m0, *n; int totlen = 0; if (m0 == 0) return; while (off > (mlen = m->m_len)) { off -= mlen; totlen += mlen; if (m->m_next == 0) { n = m_getclr(M_DONTWAIT, m->m_type); if (n == 0) goto out; n->m_len = min(MLEN, len + off); m->m_next = n; } m = m->m_next; } while (len > 0) { mlen = min (m->m_len - off, len); bcopy(cp, off + mtod(m, caddr_t), (unsigned)mlen); cp += mlen; len -= mlen; mlen += off; off = 0; totlen += mlen; if (len == 0) break; if (m->m_next == 0) { n = m_get(M_DONTWAIT, m->m_type); if (n == 0) break; n->m_len = min(MLEN, len); m->m_next = n; } m = m->m_next; } out: #if 0 if (((m = m0)->m_flags & M_PKTHDR) && (m->m_pkthdr.len < totlen)) m->m_pkthdr.len = totlen; #endif return; } # endif /* linux */ #endif /* (_KERNEL) && ( ((BSD < 199306) && !SOLARIS) || __sgi) */ frgroup_t *fr_findgroup(num, flags, which, set, fgpp) u_32_t num, flags; minor_t which; int set; frgroup_t ***fgpp; { frgroup_t *fg, **fgp; if (which == IPL_LOGAUTH) fgp = &ipfgroups[2][set]; else if (flags & FR_ACCOUNT) fgp = &ipfgroups[1][set]; else if (flags & (FR_OUTQUE|FR_INQUE)) fgp = &ipfgroups[0][set]; else return NULL; num &= 0xffff; while ((fg = *fgp)) if (fg->fg_num == num) break; else fgp = &fg->fg_next; if (fgpp) *fgpp = fgp; return fg; } frgroup_t *fr_addgroup(num, fp, which, set) u_32_t num; frentry_t *fp; minor_t which; int set; { frgroup_t *fg, **fgp; if ((fg = fr_findgroup(num, fp->fr_flags, which, set, &fgp))) return fg; KMALLOC(fg, frgroup_t *); if (fg) { - fg->fg_num = num & 0xffff; + fg->fg_num = num; fg->fg_next = *fgp; fg->fg_head = fp; fg->fg_start = &fp->fr_grp; *fgp = fg; } return fg; } void fr_delgroup(num, flags, which, set) u_32_t num, flags; minor_t which; int set; { frgroup_t *fg, **fgp; if (!(fg = fr_findgroup(num, flags, which, set, &fgp))) return; *fgp = fg->fg_next; KFREE(fg); } /* * recursively flush rules from the list, descending groups as they are * encountered. if a rule is the head of a group and it has lost all its * group members, then also delete the group reference. */ static int frflushlist(set, unit, nfreedp, listp) int set; minor_t unit; int *nfreedp; frentry_t **listp; { register int freed = 0, i; register frentry_t *fp; while ((fp = *listp)) { *listp = fp->fr_next; if (fp->fr_grp) { i = frflushlist(set, unit, nfreedp, &fp->fr_grp); MUTEX_ENTER(&ipf_rw); fp->fr_ref -= i; MUTEX_EXIT(&ipf_rw); } ATOMIC_DEC32(fp->fr_ref); if (fp->fr_grhead) { fr_delgroup(fp->fr_grhead, fp->fr_flags, unit, set); fp->fr_grhead = 0; } if (fp->fr_ref == 0) { KFREE(fp); freed++; } else fp->fr_next = NULL; } *nfreedp += freed; return freed; } int frflush(unit, flags) minor_t unit; int flags; { int flushed = 0, set; if (unit != IPL_LOGIPF) return 0; WRITE_ENTER(&ipf_mutex); bzero((char *)frcache, sizeof(frcache[0]) * 2); set = fr_active; if (flags & FR_INACTIVE) set = 1 - set; if (flags & FR_OUTQUE) { #ifdef USE_INET6 (void) frflushlist(set, unit, &flushed, &ipfilter6[1][set]); (void) frflushlist(set, unit, &flushed, &ipacct6[1][set]); #endif (void) frflushlist(set, unit, &flushed, &ipfilter[1][set]); (void) frflushlist(set, unit, &flushed, &ipacct[1][set]); } if (flags & FR_INQUE) { #ifdef USE_INET6 (void) frflushlist(set, unit, &flushed, &ipfilter6[0][set]); (void) frflushlist(set, unit, &flushed, &ipacct6[0][set]); #endif (void) frflushlist(set, unit, &flushed, &ipfilter[0][set]); (void) frflushlist(set, unit, &flushed, &ipacct[0][set]); } RWLOCK_EXIT(&ipf_mutex); return flushed; } char *memstr(src, dst, slen, dlen) char *src, *dst; int slen, dlen; { char *s = NULL; while (dlen >= slen) { if (bcmp(src, dst, slen) == 0) { s = dst; break; } dst++; dlen--; } return s; } void fixskip(listp, rp, addremove) frentry_t **listp, *rp; int addremove; { frentry_t *fp; int rules = 0, rn = 0; for (fp = *listp; fp && (fp != rp); fp = fp->fr_next, rules++) ; if (!fp) return; for (fp = *listp; fp && (fp != rp); fp = fp->fr_next, rn++) if (fp->fr_skip && (rn + fp->fr_skip >= rules)) fp->fr_skip += addremove; } #ifdef _KERNEL /* * count consecutive 1's in bit mask. If the mask generated by counting * consecutive 1's is different to that passed, return -1, else return # * of bits. */ int countbits(ip) u_32_t ip; { u_32_t ipn; int cnt = 0, i, j; ip = ipn = ntohl(ip); for (i = 32; i; i--, ipn *= 2) if (ipn & 0x80000000) cnt++; else break; ipn = 0; for (i = 32, j = cnt; i; i--, j--) { ipn *= 2; if (j > 0) ipn++; } if (ipn == ip) return cnt; return -1; } /* * return the first IP Address associated with an interface */ int fr_ifpaddr(v, ifptr, inp) int v; void *ifptr; struct in_addr *inp; { # ifdef USE_INET6 struct in6_addr *inp6 = NULL; # endif # if SOLARIS ill_t *ill = ifptr; # else struct ifnet *ifp = ifptr; # endif struct in_addr in; # if SOLARIS # ifdef USE_INET6 if (v == 6) { struct in6_addr in6; /* * First is always link local. */ if (ill->ill_ipif->ipif_next) in6 = ill->ill_ipif->ipif_next->ipif_v6lcl_addr; else bzero((char *)&in6, sizeof(in6)); bcopy((char *)&in6, (char *)inp, sizeof(in6)); } else # endif { in.s_addr = ill->ill_ipif->ipif_local_addr; *inp = in; } # else /* SOLARIS */ # if linux ; # else /* linux */ struct sockaddr_in *sin; struct ifaddr *ifa; # if (__FreeBSD_version >= 300000) ifa = TAILQ_FIRST(&ifp->if_addrhead); # else # if defined(__NetBSD__) || defined(__OpenBSD__) ifa = ifp->if_addrlist.tqh_first; # else # if defined(__sgi) && defined(IFF_DRVRLOCK) /* IRIX 6 */ ifa = &((struct in_ifaddr *)ifp->in_ifaddr)->ia_ifa; # else ifa = ifp->if_addrlist; # endif # endif /* __NetBSD__ || __OpenBSD__ */ # endif /* __FreeBSD_version >= 300000 */ # if (BSD < 199306) && !(/*IRIX6*/defined(__sgi) && defined(IFF_DRVRLOCK)) sin = (struct sockaddr_in *)&ifa->ifa_addr; # else sin = (struct sockaddr_in *)ifa->ifa_addr; while (sin && ifa) { if ((v == 4) && (sin->sin_family == AF_INET)) break; # ifdef USE_INET6 if ((v == 6) && (sin->sin_family == AF_INET6)) { inp6 = &((struct sockaddr_in6 *)sin)->sin6_addr; if (!IN6_IS_ADDR_LINKLOCAL(inp6) && !IN6_IS_ADDR_LOOPBACK(inp6)) break; } # endif # if (__FreeBSD_version >= 300000) ifa = TAILQ_NEXT(ifa, ifa_link); # else # if defined(__NetBSD__) || defined(__OpenBSD__) ifa = ifa->ifa_list.tqe_next; # else ifa = ifa->ifa_next; # endif # endif /* __FreeBSD_version >= 300000 */ if (ifa) sin = (struct sockaddr_in *)ifa->ifa_addr; } if (ifa == NULL) sin = NULL; if (sin == NULL) return -1; # endif /* (BSD < 199306) && (!__sgi && IFF_DRVLOCK) */ # ifdef USE_INET6 if (v == 6) bcopy((char *)inp6, (char *)inp, sizeof(*inp6)); else # endif { in = sin->sin_addr; *inp = in; } # endif /* linux */ # endif /* SOLARIS */ return 0; } static void frsynclist(fr) register frentry_t *fr; { for (; fr; fr = fr->fr_next) { if (fr->fr_ifa != NULL) { fr->fr_ifa = GETUNIT(fr->fr_ifname, fr->fr_ip.fi_v); if (fr->fr_ifa == NULL) fr->fr_ifa = (void *)-1; } if (fr->fr_grp) frsynclist(fr->fr_grp); } } void frsync() { # if !SOLARIS register struct ifnet *ifp; # if defined(__OpenBSD__) || ((NetBSD >= 199511) && (NetBSD < 1991011)) || \ (defined(__FreeBSD_version) && (__FreeBSD_version >= 300000)) # if (NetBSD >= 199905) || defined(__OpenBSD__) for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_list.tqe_next) # else for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_link.tqe_next) # endif # else for (ifp = ifnet; ifp; ifp = ifp->if_next) # endif { ip_natsync(ifp); ip_statesync(ifp); } # endif WRITE_ENTER(&ipf_mutex); frsynclist(ipacct[0][fr_active]); frsynclist(ipacct[1][fr_active]); frsynclist(ipfilter[0][fr_active]); frsynclist(ipfilter[1][fr_active]); #ifdef USE_INET6 frsynclist(ipacct6[0][fr_active]); frsynclist(ipacct6[1][fr_active]); frsynclist(ipfilter6[0][fr_active]); frsynclist(ipfilter6[1][fr_active]); #endif RWLOCK_EXIT(&ipf_mutex); } /* * In the functions below, bcopy() is called because the pointer being * copied _from_ in this instance is a pointer to a char buf (which could * end up being unaligned) and on the kernel's local stack. */ int ircopyptr(a, b, c) void *a, *b; size_t c; { caddr_t ca; int err; #if SOLARIS copyin(a, &ca, sizeof(ca)); #else bcopy(a, &ca, sizeof(ca)); #endif err = copyin(ca, b, c); return err; } int iwcopyptr(a, b, c) void *a, *b; size_t c; { caddr_t ca; int err; #if SOLARIS copyin(b, &ca, sizeof(ca)); #else bcopy(b, &ca, sizeof(ca)); #endif err = copyout(a, ca, c); return err; } #else /* _KERNEL */ /* * return the first IP Address associated with an interface */ int fr_ifpaddr(v, ifptr, inp) int v; void *ifptr; struct in_addr *inp; { return 0; } int ircopyptr(a, b, c) void *a, *b; size_t c; { caddr_t ca; bcopy(a, &ca, sizeof(ca)); bcopy(ca, b, c); return 0; } int iwcopyptr(a, b, c) void *a, *b; size_t c; { caddr_t ca; bcopy(b, &ca, sizeof(ca)); bcopy(a, ca, c); return 0; } #endif int fr_lock(data, lockp) caddr_t data; int *lockp; { int arg, error; error = IRCOPY(data, (caddr_t)&arg, sizeof(arg)); if (!error) { error = IWCOPY((caddr_t)lockp, data, sizeof(*lockp)); if (!error) *lockp = arg; } return error; } void fr_getstat(fiop) friostat_t *fiop; { bcopy((char *)frstats, (char *)fiop->f_st, sizeof(filterstats_t) * 2); fiop->f_locks[0] = fr_state_lock; fiop->f_locks[1] = fr_nat_lock; fiop->f_locks[2] = fr_frag_lock; fiop->f_locks[3] = fr_auth_lock; fiop->f_fin[0] = ipfilter[0][0]; fiop->f_fin[1] = ipfilter[0][1]; fiop->f_fout[0] = ipfilter[1][0]; fiop->f_fout[1] = ipfilter[1][1]; fiop->f_acctin[0] = ipacct[0][0]; fiop->f_acctin[1] = ipacct[0][1]; fiop->f_acctout[0] = ipacct[1][0]; fiop->f_acctout[1] = ipacct[1][1]; #ifdef USE_INET6 fiop->f_fin6[0] = ipfilter6[0][0]; fiop->f_fin6[1] = ipfilter6[0][1]; fiop->f_fout6[0] = ipfilter6[1][0]; fiop->f_fout6[1] = ipfilter6[1][1]; fiop->f_acctin6[0] = ipacct6[0][0]; fiop->f_acctin6[1] = ipacct6[0][1]; fiop->f_acctout6[0] = ipacct6[1][0]; fiop->f_acctout6[1] = ipacct6[1][1]; #endif fiop->f_active = fr_active; fiop->f_froute[0] = ipl_frouteok[0]; fiop->f_froute[1] = ipl_frouteok[1]; fiop->f_running = fr_running; fiop->f_groups[0][0] = ipfgroups[0][0]; fiop->f_groups[0][1] = ipfgroups[0][1]; fiop->f_groups[1][0] = ipfgroups[1][0]; fiop->f_groups[1][1] = ipfgroups[1][1]; fiop->f_groups[2][0] = ipfgroups[2][0]; fiop->f_groups[2][1] = ipfgroups[2][1]; #ifdef IPFILTER_LOG fiop->f_logging = 1; #else fiop->f_logging = 0; #endif fiop->f_defpass = fr_pass; strncpy(fiop->f_version, ipfilter_version, sizeof(fiop->f_version)); } #ifdef USE_INET6 int icmptoicmp6types[ICMP_MAXTYPE+1] = { ICMP6_ECHO_REPLY, /* 0: ICMP_ECHOREPLY */ -1, /* 1: UNUSED */ -1, /* 2: UNUSED */ ICMP6_DST_UNREACH, /* 3: ICMP_UNREACH */ -1, /* 4: ICMP_SOURCEQUENCH */ ND_REDIRECT, /* 5: ICMP_REDIRECT */ -1, /* 6: UNUSED */ -1, /* 7: UNUSED */ ICMP6_ECHO_REQUEST, /* 8: ICMP_ECHO */ -1, /* 9: UNUSED */ -1, /* 10: UNUSED */ ICMP6_TIME_EXCEEDED, /* 11: ICMP_TIMXCEED */ ICMP6_PARAM_PROB, /* 12: ICMP_PARAMPROB */ -1, /* 13: ICMP_TSTAMP */ -1, /* 14: ICMP_TSTAMPREPLY */ -1, /* 15: ICMP_IREQ */ -1, /* 16: ICMP_IREQREPLY */ -1, /* 17: ICMP_MASKREQ */ -1, /* 18: ICMP_MASKREPLY */ }; int icmptoicmp6unreach[ICMP_MAX_UNREACH] = { ICMP6_DST_UNREACH_ADDR, /* 0: ICMP_UNREACH_NET */ ICMP6_DST_UNREACH_ADDR, /* 1: ICMP_UNREACH_HOST */ -1, /* 2: ICMP_UNREACH_PROTOCOL */ ICMP6_DST_UNREACH_NOPORT, /* 3: ICMP_UNREACH_PORT */ -1, /* 4: ICMP_UNREACH_NEEDFRAG */ ICMP6_DST_UNREACH_NOTNEIGHBOR, /* 5: ICMP_UNREACH_SRCFAIL */ ICMP6_DST_UNREACH_ADDR, /* 6: ICMP_UNREACH_NET_UNKNOWN */ ICMP6_DST_UNREACH_ADDR, /* 7: ICMP_UNREACH_HOST_UNKNOWN */ -1, /* 8: ICMP_UNREACH_ISOLATED */ ICMP6_DST_UNREACH_ADMIN, /* 9: ICMP_UNREACH_NET_PROHIB */ ICMP6_DST_UNREACH_ADMIN, /* 10: ICMP_UNREACH_HOST_PROHIB */ -1, /* 11: ICMP_UNREACH_TOSNET */ -1, /* 12: ICMP_UNREACH_TOSHOST */ ICMP6_DST_UNREACH_ADMIN, /* 13: ICMP_UNREACH_ADMIN_PROHIBIT */ }; #endif Index: head/sys/contrib/ipfilter/netinet/ip_auth.c =================================================================== --- head/sys/contrib/ipfilter/netinet/ip_auth.c (revision 60856) +++ head/sys/contrib/ipfilter/netinet/ip_auth.c (revision 60857) @@ -1,550 +1,551 @@ /* * Copyright (C) 1998-2000 by Darren Reed & Guido van Rooij. * * Redistribution and use in source and binary forms are permitted * provided that this notice is preserved and due credit is given * to the original author and the contributors. */ #if !defined(lint) /*static const char rcsid[] = "@(#)$Id: ip_auth.c,v 2.1.2.2 2000/01/16 10:12:14 darrenr Exp $";*/ static const char rcsid[] = "@(#)$FreeBSD$"; #endif #include #include #include #include #include #if !defined(_KERNEL) && !defined(KERNEL) # include # include # include #endif #if (defined(KERNEL) || defined(_KERNEL)) && (__FreeBSD_version >= 220000) # include # include #else # include #endif #include #ifndef linux # include #endif #include #if (defined(_KERNEL) || defined(KERNEL)) && !defined(linux) # include #endif #if !defined(__SVR4) && !defined(__svr4__) # ifndef linux # include # endif #else # include # include # ifdef _KERNEL # include # endif # include # include #endif #if _BSDI_VERSION >= 199802 # include #endif #if defined(__NetBSD__) || defined(__OpenBSD__) || defined(bsdi) # include #endif #include #ifdef sun # include #endif #include #include #include #include #ifndef KERNEL # define KERNEL # define NOT_KERNEL #endif #ifndef linux # include #endif #ifdef NOT_KERNEL # undef KERNEL #endif #ifdef __sgi # ifdef IFF_DRVRLOCK /* IRIX6 */ # include # endif #endif #include #if defined(__sgi) && !defined(IFF_DRVRLOCK) /* IRIX < 6 */ extern struct ifqueue ipintrq; /* ip packet input queue */ #else # ifndef linux # if __FreeBSD_version >= 300000 # include # endif # include # include # endif #endif #include #include #include "netinet/ip_compat.h" #include #include "netinet/ip_fil.h" #include "netinet/ip_auth.h" #if !SOLARIS && !defined(linux) # include # ifdef __FreeBSD__ # include # endif #endif #if (__FreeBSD_version >= 300000) # include # if (defined(_KERNEL) || defined(KERNEL)) && !defined(IPFILTER_LKM) # include # include # endif #endif #if (SOLARIS || defined(__sgi)) && defined(_KERNEL) extern KRWLOCK_T ipf_auth; extern kmutex_t ipf_authmx; # if SOLARIS extern kcondvar_t ipfauthwait; # endif #endif #ifdef linux static struct wait_queue *ipfauthwait = NULL; #endif int fr_authsize = FR_NUMAUTH; int fr_authused = 0; int fr_defaultauthage = 600; int fr_auth_lock = 0; fr_authstat_t fr_authstats; static frauth_t fr_auth[FR_NUMAUTH]; mb_t *fr_authpkts[FR_NUMAUTH]; static int fr_authstart = 0, fr_authend = 0, fr_authnext = 0; static frauthent_t *fae_list = NULL; frentry_t *ipauth = NULL; /* * Check if a packet has authorization. If the packet is found to match an * authorization result and that would result in a feedback loop (i.e. it * will end up returning FR_AUTH) then return FR_BLOCK instead. */ u_32_t fr_checkauth(ip, fin) ip_t *ip; fr_info_t *fin; { u_short id = ip->ip_id; u_32_t pass; int i; if (fr_auth_lock) return 0; READ_ENTER(&ipf_auth); for (i = fr_authstart; i != fr_authend; ) { /* * index becomes -2 only after an SIOCAUTHW. Check this in * case the same packet gets sent again and it hasn't yet been * auth'd. */ if ((fr_auth[i].fra_index == -2) && (id == fr_auth[i].fra_info.fin_id) && !bcmp((char *)fin,(char *)&fr_auth[i].fra_info,FI_CSIZE)) { /* * Avoid feedback loop. */ if (!(pass = fr_auth[i].fra_pass) || (pass & FR_AUTH)) pass = FR_BLOCK; RWLOCK_EXIT(&ipf_auth); WRITE_ENTER(&ipf_auth); fr_authstats.fas_hits++; fr_auth[i].fra_index = -1; fr_authused--; if (i == fr_authstart) { while (fr_auth[i].fra_index == -1) { i++; if (i == FR_NUMAUTH) i = 0; fr_authstart = i; if (i == fr_authend) break; } if (fr_authstart == fr_authend) { fr_authnext = 0; fr_authstart = fr_authend = 0; } } RWLOCK_EXIT(&ipf_auth); return pass; } i++; if (i == FR_NUMAUTH) i = 0; } fr_authstats.fas_miss++; RWLOCK_EXIT(&ipf_auth); return 0; } /* * Check if we have room in the auth array to hold details for another packet. * If we do, store it and wake up any user programs which are waiting to * hear about these events. */ int fr_newauth(m, fin, ip) mb_t *m; fr_info_t *fin; ip_t *ip; { #if defined(_KERNEL) && SOLARIS qif_t *qif = fin->fin_qif; #endif int i; if (fr_auth_lock) return 0; WRITE_ENTER(&ipf_auth); if (fr_authstart > fr_authend) { fr_authstats.fas_nospace++; RWLOCK_EXIT(&ipf_auth); return 0; } else { if ((fr_authstart == 0) && (fr_authend == FR_NUMAUTH - 1)) { fr_authstats.fas_nospace++; RWLOCK_EXIT(&ipf_auth); return 0; } } fr_authstats.fas_added++; fr_authused++; i = fr_authend++; if (fr_authend == FR_NUMAUTH) fr_authend = 0; RWLOCK_EXIT(&ipf_auth); fr_auth[i].fra_index = i; fr_auth[i].fra_pass = 0; fr_auth[i].fra_age = fr_defaultauthage; bcopy((char *)fin, (char *)&fr_auth[i].fra_info, sizeof(*fin)); #if !defined(sparc) && !defined(m68k) /* * No need to copyback here as we want to undo the changes, not keep * them. */ # if SOLARIS && defined(_KERNEL) if ((ip == (ip_t *)m->b_rptr) && (ip->ip_v == 4)) # endif { register u_short bo; bo = ip->ip_len; ip->ip_len = htons(bo); # if !SOLARIS && !defined(__NetBSD__) /* 4.4BSD converts this ip_input.c, but I don't in solaris.c */ bo = ip->ip_id; ip->ip_id = htons(bo); # endif bo = ip->ip_off; ip->ip_off = htons(bo); } #endif #if SOLARIS && defined(_KERNEL) m->b_rptr -= qif->qf_off; fr_authpkts[i] = *(mblk_t **)fin->fin_mp; fr_auth[i].fra_q = qif->qf_q; cv_signal(&ipfauthwait); #else fr_authpkts[i] = m; # if defined(linux) && defined(_KERNEL) wake_up_interruptible(&ipfauthwait); # else WAKEUP(&fr_authnext); # endif #endif return 1; } int fr_auth_ioctl(data, cmd, fr, frptr) caddr_t data; #if defined(__NetBSD__) || defined(__OpenBSD__) || (FreeBSD_version >= 300003) u_long cmd; #else int cmd; #endif frentry_t *fr, **frptr; { mb_t *m; #if defined(_KERNEL) && !SOLARIS struct ifqueue *ifq; #endif frauth_t auth, *au = &auth; frauthent_t *fae, **faep; int i, error = 0; switch (cmd) { case SIOCSTLCK : error = fr_lock(data, &fr_auth_lock); break; case SIOCINIFR : case SIOCRMIFR : case SIOCADIFR : error = EINVAL; break; case SIOCINAFR : error = EINVAL; break; case SIOCRMAFR : case SIOCADAFR : for (faep = &fae_list; (fae = *faep); ) if (&fae->fae_fr == fr) break; else faep = &fae->fae_next; if (cmd == SIOCRMAFR) { if (!fae) error = ESRCH; else { WRITE_ENTER(&ipf_auth); *faep = fae->fae_next; *frptr = fr->fr_next; RWLOCK_EXIT(&ipf_auth); KFREE(fae); } } else { KMALLOC(fae, frauthent_t *); if (fae != NULL) { bcopy((char *)fr, (char *)&fae->fae_fr, sizeof(*fr)); WRITE_ENTER(&ipf_auth); fae->fae_age = fr_defaultauthage; fae->fae_fr.fr_hits = 0; fae->fae_fr.fr_next = *frptr; *frptr = &fae->fae_fr; fae->fae_next = *faep; *faep = fae; ipauth = &fae_list->fae_fr; RWLOCK_EXIT(&ipf_auth); } else error = ENOMEM; } break; case SIOCATHST: READ_ENTER(&ipf_auth); fr_authstats.fas_faelist = fae_list; RWLOCK_EXIT(&ipf_auth); error = IWCOPYPTR((char *)&fr_authstats, data, sizeof(fr_authstats)); break; case SIOCAUTHW: fr_authioctlloop: READ_ENTER(&ipf_auth); if ((fr_authnext != fr_authend) && fr_authpkts[fr_authnext]) { error = IWCOPYPTR((char *)&fr_auth[fr_authnext], data, sizeof(fr_info_t)); RWLOCK_EXIT(&ipf_auth); if (error) break; WRITE_ENTER(&ipf_auth); fr_authnext++; if (fr_authnext == FR_NUMAUTH) fr_authnext = 0; RWLOCK_EXIT(&ipf_auth); return 0; } #ifdef _KERNEL # if SOLARIS mutex_enter(&ipf_authmx); if (!cv_wait_sig(&ipfauthwait, &ipf_authmx)) { mutex_exit(&ipf_authmx); return EINTR; } mutex_exit(&ipf_authmx); # else # ifdef linux interruptible_sleep_on(&ipfauthwait); if (current->signal & ~current->blocked) error = -EINTR; # else error = SLEEP(&fr_authnext, "fr_authnext"); # endif # endif #endif RWLOCK_EXIT(&ipf_auth); if (!error) goto fr_authioctlloop; break; case SIOCAUTHR: error = IRCOPYPTR(data, (caddr_t)&auth, sizeof(auth)); if (error) return error; WRITE_ENTER(&ipf_auth); + i = au->fra_index; if ((i < 0) || (i > FR_NUMAUTH) || (fr_auth[i].fra_info.fin_id != au->fra_info.fin_id)) { RWLOCK_EXIT(&ipf_auth); return EINVAL; } m = fr_authpkts[i]; fr_auth[i].fra_index = -2; fr_auth[i].fra_pass = au->fra_pass; fr_authpkts[i] = NULL; #ifdef _KERNEL RWLOCK_EXIT(&ipf_auth); # ifndef linux if (m && au->fra_info.fin_out) { # if SOLARIS error = fr_qout(fr_auth[i].fra_q, m); # else /* SOLARIS */ # if (_BSDI_VERSION >= 199802) || defined(__OpenBSD__) error = ip_output(m, NULL, NULL, IP_FORWARDING, NULL, NULL); # else error = ip_output(m, NULL, NULL, IP_FORWARDING, NULL); # endif # endif /* SOLARIS */ if (error) fr_authstats.fas_sendfail++; else fr_authstats.fas_sendok++; } else if (m) { # if SOLARIS error = fr_qin(fr_auth[i].fra_q, m); # else /* SOLARIS */ ifq = &ipintrq; if (IF_QFULL(ifq)) { IF_DROP(ifq); m_freem(m); error = ENOBUFS; } else { IF_ENQUEUE(ifq, m); schednetisr(NETISR_IP); } # endif /* SOLARIS */ if (error) fr_authstats.fas_quefail++; else fr_authstats.fas_queok++; } else error = EINVAL; # endif # if SOLARIS if (error) error = EINVAL; # else /* * If we experience an error which will result in the packet * not being processed, make sure we advance to the next one. */ if (error == ENOBUFS) { fr_authused--; fr_auth[i].fra_index = -1; fr_auth[i].fra_pass = 0; if (i == fr_authstart) { while (fr_auth[i].fra_index == -1) { i++; if (i == FR_NUMAUTH) i = 0; fr_authstart = i; if (i == fr_authend) break; } if (fr_authstart == fr_authend) { fr_authnext = 0; fr_authstart = fr_authend = 0; } } } # endif #endif /* _KERNEL */ break; default : error = EINVAL; break; } return error; } #ifdef _KERNEL /* * Free all network buffer memory used to keep saved packets. */ void fr_authunload() { register int i; register frauthent_t *fae, **faep; mb_t *m; WRITE_ENTER(&ipf_auth); for (i = 0; i < FR_NUMAUTH; i++) { if ((m = fr_authpkts[i])) { FREE_MB_T(m); fr_authpkts[i] = NULL; fr_auth[i].fra_index = -1; } } for (faep = &fae_list; (fae = *faep); ) { *faep = fae->fae_next; KFREE(fae); } ipauth = NULL; RWLOCK_EXIT(&ipf_auth); } /* * Slowly expire held auth records. Timeouts are set * in expectation of this being called twice per second. */ void fr_authexpire() { register int i; register frauth_t *fra; register frauthent_t *fae, **faep; mb_t *m; #if !SOLARIS int s; #endif if (fr_auth_lock) return; SPL_NET(s); WRITE_ENTER(&ipf_auth); for (i = 0, fra = fr_auth; i < FR_NUMAUTH; i++, fra++) { if ((!--fra->fra_age) && (m = fr_authpkts[i])) { FREE_MB_T(m); fr_authpkts[i] = NULL; fr_auth[i].fra_index = -1; fr_authstats.fas_expire++; fr_authused--; } } for (faep = &fae_list; (fae = *faep); ) { if (!--fae->fae_age) { *faep = fae->fae_next; KFREE(fae); fr_authstats.fas_expire++; } else faep = &fae->fae_next; } ipauth = &fae_list->fae_fr; RWLOCK_EXIT(&ipf_auth); SPL_X(s); } #endif Index: head/sys/contrib/ipfilter/netinet/ip_compat.h =================================================================== --- head/sys/contrib/ipfilter/netinet/ip_compat.h (revision 60856) +++ head/sys/contrib/ipfilter/netinet/ip_compat.h (revision 60857) @@ -1,837 +1,987 @@ /* - * Copyright (C) 1993-1998 by Darren Reed. + * Copyright (C) 1993-2000 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.1.2.3 1999/11/18 13:55:26 darrenr Exp $ * $FreeBSD$ */ #ifndef __IP_COMPAT_H__ #define __IP_COMPAT_H__ #ifndef __P # ifdef __STDC__ # define __P(x) x # else # define __P(x) () # endif #endif #ifndef __STDC__ # undef const # define const #endif #ifndef SOLARIS #define SOLARIS (defined(sun) && (defined(__svr4__) || defined(__SVR4))) #endif +#if SOLARIS2 >= 8 +# ifndef USE_INET6 +# define USE_INET6 +# endif +#endif #if defined(_KERNEL) || defined(KERNEL) || defined(__KERNEL__) # undef KERNEL # undef _KERNEL # undef __KERNEL__ # define KERNEL # define _KERNEL # define __KERNEL__ #endif #if defined(__SVR4) || defined(__svr4__) || defined(__sgi) #define index strchr # if !defined(KERNEL) # define bzero(a,b) memset(a,0,b) # define bcmp memcmp # define bcopy(a,b,c) memmove(b,a,c) # endif #endif #ifndef offsetof #define offsetof(t,m) (int)((&((t *)0L)->m)) #endif #if defined(__sgi) || defined(bsdi) struct ether_addr { u_char ether_addr_octet[6]; }; #endif #if defined(__sgi) && !defined(IPFILTER_LKM) # ifdef __STDC__ # define IPL_EXTERN(ep) ipfilter##ep # else # define IPL_EXTERN(ep) ipfilter/**/ep # endif #else # ifdef __STDC__ # define IPL_EXTERN(ep) ipl##ep # else # define IPL_EXTERN(ep) ipl/**/ep # endif #endif #ifdef linux # include #endif #if SOLARIS # define MTYPE(m) ((m)->b_datap->db_type) # include # include # include # include /* * because Solaris 2 defines these in two places :-/ */ # undef IPOPT_EOL # undef IPOPT_NOP # undef IPOPT_LSRR # undef IPOPT_RR # undef IPOPT_SSRR # ifndef KERNEL # define _KERNEL # undef RES_INIT +# if SOLARIS2 >= 8 +# include +# endif # include # include # include # undef _KERNEL # else /* _KERNEL */ +# if SOLARIS2 >= 8 +# include +# endif # include # include # include # endif /* _KERNEL */ # if SOLARIS2 >= 8 +# include # include -# include # define ipif_local_addr ipif_lcl_addr +/* Only defined in private include file */ +# ifndef V4_PART_OF_V6 +# define V4_PART_OF_V6(v6) v6.s6_addr32[3] +# endif # endif #else # if !defined(__sgi) typedef int minor_t; #endif #endif /* SOLARIS */ #define IPMINLEN(i, h) ((i)->ip_len >= ((i)->ip_hl * 4 + sizeof(struct h))) #ifndef IP_OFFMASK #define IP_OFFMASK 0x1fff #endif #if BSD > 199306 # define USE_QUAD_T # define U_QUAD_T u_quad_t # define QUAD_T quad_t #else /* BSD > 199306 */ # define U_QUAD_T u_long # define QUAD_T long #endif /* BSD > 199306 */ + /* * These operating systems already take care of the problem for us. */ #if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__) || \ defined(__sgi) typedef u_int32_t u_32_t; +# if defined(_KERNEL) && !defined(IPFILTER_LKM) +# if defined(__NetBSD_Version__) && (__NetBSD_Version__ >= 104110000) +# include "opt_inet.h" +# endif +# if defined(__FreeBSD_version) && (__FreeBSD_version >= 400000) && \ + !defined(KLD_MODULE) +# include "opt_inet6.h" +# endif +# ifdef INET6 +# define USE_INET6 +# endif +# endif #else /* * Really, any arch where sizeof(long) != sizeof(int). */ # if defined(__alpha__) || defined(__alpha) || defined(_LP64) typedef unsigned int u_32_t; # else -typedef unsigned long u_32_t; +# if SOLARIS2 >= 6 +typedef uint32_t u_32_t; +# else +typedef unsigned int u_32_t; +# endif # endif #endif /* __NetBSD__ || __OpenBSD__ || __FreeBSD__ || __sgi */ +#ifdef USE_INET6 +# if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__) +# include +# ifdef _KERNEL +# include +# endif +typedef struct ip6_hdr ip6_t; +# endif +union i6addr { + u_32_t i6[4]; + struct in_addr in4; + struct in6_addr in6; +}; +#else +union i6addr { + u_32_t i6[4]; + struct in_addr in4; +}; +#endif + +#define IP6CMP(a,b) bcmp((char *)&(a), (char *)&(b), sizeof(a)) +#define IP6EQ(a,b) (bcmp((char *)&(a), (char *)&(b), sizeof(a)) == 0) +#define IP6NEQ(a,b) (bcmp((char *)&(a), (char *)&(b), sizeof(a)) != 0) + #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 */ -#if defined(__FreeBSD__) && defined(KERNEL) +#if defined(__FreeBSD__) && (defined(KERNEL) || defined(_KERNEL)) # if __FreeBSD__ < 3 # include +# else +# if __FreeBSD__ == 3 +# if defined(IPFILTER_LKM) && !defined(ACTUALLY_LKM_NOT_KERNEL) +# define ACTUALLY_LKM_NOT_KERNEL +# endif +# endif # endif -# if defined(IPFILTER_LKM) && !defined(ACTUALLY_LKM_NOT_KERNEL) -# define ACTUALLY_LKM_NOT_KERNEL -# endif #endif /* __FreeBSD__ && KERNEL */ /* * Build some macros and #defines to enable the same code to compile anywhere * Well, that's the idea, anyway :-) */ +#if !SOLARIS || (SOLARIS2 < 6) || !defined(KERNEL) +# define ATOMIC_INCL ATOMIC_INC +# define ATOMIC_INC64 ATOMIC_INC +# define ATOMIC_INC32 ATOMIC_INC +# define ATOMIC_INC16 ATOMIC_INC +# define ATOMIC_DECL ATOMIC_DEC +# define ATOMIC_DEC64 ATOMIC_DEC +# define ATOMIC_DEC32 ATOMIC_DEC +# define ATOMIC_DEC16 ATOMIC_DEC +#endif #ifdef KERNEL # if SOLARIS -# define ATOMIC_INC(x) { mutex_enter(&ipf_rw); (x)++; \ +# if SOLARIS2 >= 6 +# include +# if SOLARIS2 == 6 +# define ATOMIC_INCL(x) atomic_add_long((uint32_t*)&(x), 1) +# define ATOMIC_DECL(x) atomic_add_long((uint32_t*)&(x), -1) +# else +# define ATOMIC_INCL(x) atomic_add_long(&(x), 1) +# define ATOMIC_DECL(x) atomic_add_long(&(x), -1) +# endif +# define ATOMIC_INC64(x) atomic_add_64((uint64_t*)&(x), 1) +# define ATOMIC_INC32(x) atomic_add_32((uint32_t*)&(x), 1) +# define ATOMIC_INC16(x) atomic_add_16((uint16_t*)&(x), 1) +# define ATOMIC_DEC64(x) atomic_add_64((uint64_t*)&(x), -1) +# define ATOMIC_DEC32(x) atomic_add_32((uint32_t*)&(x), -1) +# define ATOMIC_DEC16(x) atomic_add_16((uint16_t*)&(x), -1) +# else +# define ATOMIC_INC(x) { mutex_enter(&ipf_rw); (x)++; \ mutex_exit(&ipf_rw); } -# define ATOMIC_DEC(x) { mutex_enter(&ipf_rw); (x)--; \ +# define ATOMIC_DEC(x) { mutex_enter(&ipf_rw); (x)--; \ mutex_exit(&ipf_rw); } +# endif # define MUTEX_ENTER(x) mutex_enter(x) # if 1 # define KRWLOCK_T krwlock_t # define READ_ENTER(x) rw_enter(x, RW_READER) # define WRITE_ENTER(x) rw_enter(x, RW_WRITER) # define RW_UPGRADE(x) { if (rw_tryupgrade(x) == 0) { \ rw_exit(x); \ rw_enter(x, RW_WRITER); } \ } # define MUTEX_DOWNGRADE(x) rw_downgrade(x) # define RWLOCK_INIT(x, y, z) rw_init((x), (y), RW_DRIVER, (z)) # define RWLOCK_EXIT(x) rw_exit(x) # define RW_DESTROY(x) rw_destroy(x) # else # define KRWLOCK_T kmutex_t # define READ_ENTER(x) mutex_enter(x) # define WRITE_ENTER(x) mutex_enter(x) # define MUTEX_DOWNGRADE(x) ; # define RWLOCK_INIT(x, y, z) mutex_init((x), (y), MUTEX_DRIVER, (z)) # define RWLOCK_EXIT(x) mutex_exit(x) # define RW_DESTROY(x) mutex_destroy(x) # endif +# define MUTEX_INIT(x, y, z) mutex_init((x), (y), MUTEX_DRIVER, (z)) +# define MUTEX_DESTROY(x) mutex_destroy(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)) +# define IRCOPYPTR ircopyptr +# define IWCOPYPTR iwcopyptr # define FREE_MB_T(m) freemsg(m) # define SPL_NET(x) ; # define SPL_IMP(x) ; # undef SPL_X # define SPL_X(x) ; # ifdef sparc # define ntohs(x) (x) # define ntohl(x) (x) # define htons(x) (x) # define htonl(x) (x) # endif /* sparc */ # define KMALLOC(a,b) (a) = (b)kmem_alloc(sizeof(*(a)), KM_NOSLEEP) # define KMALLOCS(a,b,c) (a) = (b)kmem_alloc((c), KM_NOSLEEP) # define GET_MINOR(x) getminor(x) typedef struct qif { struct qif *qf_next; ill_t *qf_ill; kmutex_t qf_lock; void *qf_iptr; void *qf_optr; queue_t *qf_in; queue_t *qf_out; struct qinit *qf_wqinfo; struct qinit *qf_rqinfo; struct qinit qf_wqinit; struct qinit qf_rqinit; mblk_t *qf_m; /* These three fields are for passing data up from */ queue_t *qf_q; /* fr_qin and fr_qout to the packet processing. */ size_t qf_off; size_t qf_len; /* this field is used for in ipfr_fastroute */ char qf_name[8]; /* * in case the ILL has disappeared... */ size_t qf_hl; /* header length */ + int qf_sap; } qif_t; -extern ill_t *get_unit __P((char *)); -# define GETUNIT(n) get_unit((n)) +extern ill_t *get_unit __P((char *, int)); +# define GETUNIT(n, v) get_unit(n, v) +# define IFNAME(x) ((ill_t *)x)->ill_name # else /* SOLARIS */ # if defined(__sgi) # define hz HZ # include # define IPF_LOCK_PL plhi # include #undef kmutex_t typedef struct { lock_t *l; int pl; } kmutex_t; # define ATOMIC_INC(x) { MUTEX_ENTER(&ipf_rw); \ (x)++; MUTEX_EXIT(&ipf_rw); } # define ATOMIC_DEC(x) { MUTEX_ENTER(&ipf_rw); \ (x)--; MUTEX_EXIT(&ipf_rw); } # define MUTEX_ENTER(x) (x)->pl = LOCK((x)->l, IPF_LOCK_PL); # define KRWLOCK_T kmutex_t # define READ_ENTER(x) MUTEX_ENTER(x) # define WRITE_ENTER(x) MUTEX_ENTER(x) # define RW_UPGRADE(x) ; # define MUTEX_DOWNGRADE(x) ; -# define RWLOCK_EXIT(x) MUTEX_EXIT(x) -# define MUTEX_EXIT(x) UNLOCK((x)->l, (x)->pl); +# define RWLOCK_EXIT(x) MUTEX_EXIT(x) +# define MUTEX_EXIT(x) UNLOCK((x)->l, (x)->pl); +# define MUTEX_INIT(x,y,z) (x).l = LOCK_ALLOC((uchar_t)-1, IPF_LOCK_PL, (lkinfo_t *)-1, KM_NOSLEEP) +# define MUTEX_DESTROY(x) LOCK_DEALLOC((x).l) # else /* __sgi */ # define ATOMIC_INC(x) (x)++ # define ATOMIC_DEC(x) (x)-- # define MUTEX_ENTER(x) ; -# define READ_ENTER(x) ; -# define WRITE_ENTER(x) ; -# define RW_UPGRADE(x) ; +# define READ_ENTER(x) ; +# define WRITE_ENTER(x) ; +# define RW_UPGRADE(x) ; # define MUTEX_DOWNGRADE(x) ; -# define RWLOCK_EXIT(x) ; -# define MUTEX_EXIT(x) ; +# define RWLOCK_EXIT(x) ; +# define MUTEX_EXIT(x) ; +# define MUTEX_INIT(x,y,z) ; +# define MUTEX_DESTROY(x) ; # endif /* __sgi */ # ifndef linux # define FREE_MB_T(m) m_freem(m) # define MTOD(m,t) mtod(m,t) -# define IRCOPY(a,b,c) bcopy((a), (b), (c)) -# define IWCOPY(a,b,c) bcopy((a), (b), (c)) +# define IRCOPY(a,b,c) (bcopy((a), (b), (c)), 0) +# define IWCOPY(a,b,c) (bcopy((a), (b), (c)), 0) +# define IRCOPYPTR ircopyptr +# define IWCOPYPTR iwcopyptr # endif /* !linux */ # endif /* SOLARIS */ # ifdef sun # if !SOLARIS # include -# define GETUNIT(n) ifunit((n), IFNAMSIZ) +# define GETUNIT(n, v) ifunit(n, IFNAMSIZ) +# define IFNAME(x) ((struct ifnet *)x)->if_name # endif # else # ifndef linux -# define GETUNIT(n) ifunit((n)) +# define GETUNIT(n, v) ifunit(n) +# if (defined(NetBSD) && (NetBSD <= 1991011) && (NetBSD >= 199606)) || \ + (defined(OpenBSD) && (OpenBSD >= 199603)) +# define IFNAME(x) ((struct ifnet *)x)->if_xname +# else +# define IFNAME(x) ((struct ifnet *)x)->if_name +# endif # endif # endif /* sun */ # if defined(sun) && !defined(linux) || defined(__sgi) # define UIOMOVE(a,b,c,d) uiomove((caddr_t)a,b,c,d) # define SLEEP(id, n) sleep((id), PZERO+1) # define WAKEUP(id) wakeup(id) # define KFREE(x) kmem_free((char *)(x), sizeof(*(x))) # define KFREES(x,s) kmem_free((char *)(x), (s)) # if !SOLARIS extern void m_copydata __P((struct mbuf *, int, int, caddr_t)); extern void m_copyback __P((struct mbuf *, int, int, caddr_t)); # endif # ifdef __sgi # include # include # define KMALLOC(a,b) (a) = (b)kmem_alloc(sizeof(*(a)), KM_NOSLEEP) # define KMALLOCS(a,b,c) (a) = (b)kmem_alloc((c), KM_NOSLEEP) # define GET_MINOR(x) getminor(x) # else # if !SOLARIS # define KMALLOC(a,b) (a) = (b)new_kmem_alloc(sizeof(*(a)), \ KMEM_NOSLEEP) # define KMALLOCS(a,b,c) (a) = (b)new_kmem_alloc((c), KMEM_NOSLEEP) # endif /* SOLARIS */ # endif /* __sgi */ # endif /* sun && !linux */ # ifndef GET_MINOR # define GET_MINOR(x) minor(x) # endif # if (BSD >= 199306) || defined(__FreeBSD__) # include # if !defined(__FreeBSD__) || (defined (__FreeBSD__) && __FreeBSD__>=3) # include # include extern vm_map_t kmem_map; # else /* !__FreeBSD__ || (__FreeBSD__ && __FreeBSD__>=3) */ # include # endif /* !__FreeBSD__ || (__FreeBSD__ && __FreeBSD__>=3) */ # ifdef M_PFIL # define KMALLOC(a, b) MALLOC((a), b, sizeof(*(a)), M_PFIL, M_NOWAIT) # define KMALLOCS(a, b, c) MALLOC((a), b, (c), M_PFIL, M_NOWAIT) # define KFREE(x) FREE((x), M_PFIL) # define KFREES(x,s) FREE((x), M_PFIL) # else # define KMALLOC(a, b) MALLOC((a), b, sizeof(*(a)), M_TEMP, M_NOWAIT) # define KMALLOCS(a, b, c) MALLOC((a), b, (c), M_TEMP, M_NOWAIT) # define KFREE(x) FREE((x), M_TEMP) # define KFREES(x,s) FREE((x), M_TEMP) # endif /* M_PFIL */ # define UIOMOVE(a,b,c,d) uiomove(a,b,d) # define SLEEP(id, n) tsleep((id), PPAUSE|PCATCH, n, 0) # define WAKEUP(id) wakeup(id) # endif /* BSD */ # if defined(NetBSD) && NetBSD <= 1991011 && NetBSD >= 199407 # define SPL_NET(x) x = splsoftnet() # define SPL_X(x) (void) splx(x) # else # if !SOLARIS && !defined(linux) # define SPL_IMP(x) x = splimp() # define SPL_NET(x) x = splnet() # define SPL_X(x) (void) splx(x) # endif # endif /* NetBSD && NetBSD <= 1991011 && NetBSD >= 199407 */ # define PANIC(x,y) if (x) panic y #else /* KERNEL */ # define SLEEP(x,y) ; # define WAKEUP(x) ; # define PANIC(x,y) ; # define ATOMIC_INC(x) (x)++ # define ATOMIC_DEC(x) (x)-- # define MUTEX_ENTER(x) ; # define READ_ENTER(x) ; +# define MUTEX_INIT(x,y,z) ; +# define MUTEX_DESTROY(x) ; # define WRITE_ENTER(x) ; # define RW_UPGRADE(x) ; # define MUTEX_DOWNGRADE(x) ; # define RWLOCK_EXIT(x) ; # define MUTEX_EXIT(x) ; # define SPL_NET(x) ; # define SPL_IMP(x) ; # undef SPL_X # define SPL_X(x) ; # define KMALLOC(a,b) (a) = (b)malloc(sizeof(*a)) # define KMALLOCS(a,b,c) (a) = (b)malloc(c) # define KFREE(x) free(x) # define KFREES(x,s) free(x) -# define GETUNIT(x) get_unit(x) -# define IRCOPY(a,b,c) bcopy((a), (b), (c)) -# define IWCOPY(a,b,c) bcopy((a), (b), (c)) +# define GETUNIT(x, v) get_unit(x,v) +# define IRCOPY(a,b,c) (bcopy((a), (b), (c)), 0) +# define IWCOPY(a,b,c) (bcopy((a), (b), (c)), 0) +# define IRCOPYPTR ircopyptr +# define IWCOPYPTR iwcopyptr #endif /* KERNEL */ #if SOLARIS typedef mblk_t mb_t; # if SOLARIS2 >= 7 # ifdef lint # define ALIGN32(ptr) (ptr ? 0L : 0L) # define ALIGN16(ptr) (ptr ? 0L : 0L) # else # define ALIGN32(ptr) (ptr) # define ALIGN16(ptr) (ptr) # endif # endif #else # ifdef linux # ifndef kernel typedef struct mb { struct mb *next; u_int len; u_char *data; } mb_t; # else typedef struct sk_buff mb_t; # endif # else typedef struct mbuf mb_t; # endif #endif /* SOLARIS */ #if defined(linux) || defined(__sgi) /* * These #ifdef's are here mainly for linux, but who knows, they may * not be in other places or maybe one day linux will grow up and some * of these will turn up there too. */ #ifndef ICMP_MINLEN # define ICMP_MINLEN 8 #endif #ifndef ICMP_UNREACH # define ICMP_UNREACH ICMP_DEST_UNREACH #endif #ifndef ICMP_SOURCEQUENCH # define ICMP_SOURCEQUENCH ICMP_SOURCE_QUENCH #endif #ifndef ICMP_TIMXCEED # define ICMP_TIMXCEED ICMP_TIME_EXCEEDED #endif #ifndef ICMP_PARAMPROB # define ICMP_PARAMPROB ICMP_PARAMETERPROB #endif #ifndef ICMP_TSTAMP # define ICMP_TSTAMP ICMP_TIMESTAMP #endif #ifndef ICMP_TSTAMPREPLY # define ICMP_TSTAMPREPLY ICMP_TIMESTAMPREPLY #endif #ifndef ICMP_IREQ # define ICMP_IREQ ICMP_INFO_REQUEST #endif #ifndef ICMP_IREQREPLY # define ICMP_IREQREPLY ICMP_INFO_REPLY #endif #ifndef ICMP_MASKREQ # define ICMP_MASKREQ ICMP_ADDRESS #endif #ifndef ICMP_MASKREPLY # define ICMP_MASKREPLY ICMP_ADDRESSREPLY #endif #ifndef IPVERSION # define IPVERSION 4 #endif #ifndef IPOPT_MINOFF # define IPOPT_MINOFF 4 #endif #ifndef IPOPT_COPIED # define IPOPT_COPIED(x) ((x)&0x80) #endif #ifndef IPOPT_EOL # define IPOPT_EOL 0 #endif #ifndef IPOPT_NOP # define IPOPT_NOP 1 #endif #ifndef IP_MF # define IP_MF ((u_short)0x2000) #endif #ifndef ETHERTYPE_IP # define ETHERTYPE_IP ((u_short)0x0800) #endif #ifndef TH_FIN # define TH_FIN 0x01 #endif #ifndef TH_SYN # define TH_SYN 0x02 #endif #ifndef TH_RST # define TH_RST 0x04 #endif #ifndef TH_PUSH # define TH_PUSH 0x08 #endif #ifndef TH_ACK # define TH_ACK 0x10 #endif #ifndef TH_URG # define TH_URG 0x20 #endif #ifndef IPOPT_EOL # define IPOPT_EOL 0 #endif #ifndef IPOPT_NOP # define IPOPT_NOP 1 #endif #ifndef IPOPT_RR # define IPOPT_RR 7 #endif #ifndef IPOPT_TS # define IPOPT_TS 68 #endif #ifndef IPOPT_SECURITY # define IPOPT_SECURITY 130 #endif #ifndef IPOPT_LSRR # define IPOPT_LSRR 131 #endif #ifndef IPOPT_SATID # define IPOPT_SATID 136 #endif #ifndef IPOPT_SSRR # define IPOPT_SSRR 137 #endif #ifndef IPOPT_SECUR_UNCLASS # define IPOPT_SECUR_UNCLASS ((u_short)0x0000) #endif #ifndef IPOPT_SECUR_CONFID # define IPOPT_SECUR_CONFID ((u_short)0xf135) #endif #ifndef IPOPT_SECUR_EFTO # define IPOPT_SECUR_EFTO ((u_short)0x789a) #endif #ifndef IPOPT_SECUR_MMMM # define IPOPT_SECUR_MMMM ((u_short)0xbc4d) #endif #ifndef IPOPT_SECUR_RESTR # define IPOPT_SECUR_RESTR ((u_short)0xaf13) #endif #ifndef IPOPT_SECUR_SECRET # define IPOPT_SECUR_SECRET ((u_short)0xd788) #endif #ifndef IPOPT_SECUR_TOPSECRET # define IPOPT_SECUR_TOPSECRET ((u_short)0x6bc5) #endif #ifndef IPOPT_OLEN # define IPOPT_OLEN 1 #endif #endif /* linux || __sgi */ #ifdef linux #include /* * TCP States */ #define TCPS_CLOSED 0 /* closed */ #define TCPS_LISTEN 1 /* listening for connection */ #define TCPS_SYN_SENT 2 /* active, have sent syn */ #define TCPS_SYN_RECEIVED 3 /* have send and received syn */ /* states < TCPS_ESTABLISHED are those where connections not established */ #define TCPS_ESTABLISHED 4 /* established */ #define TCPS_CLOSE_WAIT 5 /* rcvd fin, waiting for close */ /* states > TCPS_CLOSE_WAIT are those where user has closed */ #define TCPS_FIN_WAIT_1 6 /* have closed, sent fin */ #define TCPS_CLOSING 7 /* closed xchd FIN; await FIN ACK */ #define TCPS_LAST_ACK 8 /* had fin and close; await FIN ACK */ /* states > TCPS_CLOSE_WAIT && < TCPS_FIN_WAIT_2 await ACK of FIN */ #define TCPS_FIN_WAIT_2 9 /* have closed, fin is acked */ #define TCPS_TIME_WAIT 10 /* in 2*msl quiet wait after close */ /* * file flags. */ #ifdef WRITE #define FWRITE WRITE #define FREAD READ #else #define FWRITE _IOC_WRITE #define FREAD _IOC_READ #endif /* * mbuf related problems. */ #define mtod(m,t) (t)((m)->data) #define m_len len #define m_next next #ifdef IP_DF #undef IP_DF #endif #define IP_DF 0x4000 typedef struct { __u16 th_sport; __u16 th_dport; __u32 th_seq; __u32 th_ack; # if defined(__i386__) || defined(__MIPSEL__) || defined(__alpha__) ||\ defined(vax) __u8 th_res:4; __u8 th_off:4; #else __u8 th_off:4; __u8 th_res:4; #endif __u8 th_flags; __u16 th_win; __u16 th_sum; __u16 th_urp; } tcphdr_t; typedef struct { __u16 uh_sport; __u16 uh_dport; __u16 uh_ulen; __u16 uh_sum; } udphdr_t; typedef struct { # if defined(__i386__) || defined(__MIPSEL__) || defined(__alpha__) ||\ defined(vax) __u8 ip_hl:4; __u8 ip_v:4; # else __u8 ip_v:4; __u8 ip_hl: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. */ typedef struct icmp { __u8 icmp_type; /* type of message, see below */ __u8 icmp_code; /* type sub code */ __u16 icmp_cksum; /* ones complement cksum of struct */ union { __u8 ih_pptr; /* ICMP_PARAMPROB */ struct in_addr ih_gwaddr; /* ICMP_REDIRECT */ struct ih_idseq { __u16 icd_id; __u16 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 } icmphdr_t; # ifndef LINUX_IPOVLY # define LINUX_IPOVLY struct ipovly { caddr_t ih_next, ih_prev; /* for protocol sequence q's */ u_char ih_x1; /* (unused) */ 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 */ }; # endif typedef struct { __u8 ether_dhost[6]; __u8 ether_shost[6]; __u16 ether_type; } ether_header_t; typedef struct uio { int uio_resid; int uio_rw; caddr_t uio_buf; } uio_t; # define UIO_READ 0 # define UIO_WRITE 1 # define UIOMOVE(a, b, c, d) uiomove(a,b,c,d) /* * For masking struct ifnet onto struct device */ # define if_name name # ifdef KERNEL -# define GETUNIT(x) dev_get(x) +# define GETUNIT(x, v) dev_get(x) # define FREE_MB_T(m) kfree_skb(m, FREE_WRITE) # define uniqtime do_gettimeofday # undef INT_MAX # undef UINT_MAX # undef LONG_MAX # undef ULONG_MAX # include # define SPL_X(x) # define SPL_NET(x) # define SPL_IMP(x) # define bcmp(a,b,c) memcmp(a,b,c) # define bcopy(a,b,c) memcpy(b,a,c) # define bzero(a,c) memset(a,0,c) # define UNITNAME(n) dev_get((n)) # define KMALLOC(a,b) (a) = (b)kmalloc(sizeof(*(a)), GFP_ATOMIC) # define KMALLOCS(a,b,c) (a) = (b)kmalloc((c), GFP_ATOMIC) # define KFREE(x) kfree_s((x), sizeof(*(x))) # define KFREES(x,s) kfree_s((x), (s)) -# define IRCOPY(a,b,c) { \ - error = verify_area(VERIFY_READ, (a) ,(c)); \ - if (!error) \ - memcpy_fromfs((b), (a), (c)); \ - } -# define IWCOPY(a,b,c) { \ - error = verify_area(VERIFY_WRITE, (b), (c)); \ - if (!error) \ - memcpy_tofs((b), (a), (c)); \ - } +#define IRCOPY(const void *a, void *b, size_t c) { \ + int error; \ + + error = verify_area(VERIFY_READ, a ,c); \ + if (!error) \ + memcpy_fromfs(b, a, c); \ + return error; \ +} +static inline int IWCOPY(const void *a, void *b, size_t c) +{ + int error; + + error = verify_area(VERIFY_WRITE, b, c); + if (!error) + memcpy_tofs(b, a, c); + return error; +} +static inline int IRCOPYPTR(const void *a, void *b, size_t c) { + caddr_t ca; + int error; + + error = verify_area(VERIFY_READ, a ,sizeof(ca)); + if (!error) { + memcpy_fromfs(ca, a, sizeof(ca)); + error = verify_area(VERIFY_READ, ca , c); + if (!error) + memcpy_fromfs(b, ca, c); + } + return error; +} +static inline int IWCOPYPTR(const void *a, void *b, size_t c) { + caddr_t ca; + int error; + + + error = verify_area(VERIFY_READ, b ,sizeof(ca)); + if (!error) { + memcpy_fromfs(ca, b, sizeof(ca)); + error = verify_area(VERIFY_WRITE, ca, c); + if (!error) + memcpy_tofs(ca, a, c); + } + return error; +} # else # define __KERNEL__ # undef INT_MAX # undef UINT_MAX # undef LONG_MAX # undef ULONG_MAX # define s8 __s8 # define u8 __u8 # define s16 __s16 # define u16 __u16 # define s32 __s32 # define u32 __u32 # include # undef __KERNEL__ # endif # define ifnet device #else typedef struct tcphdr tcphdr_t; typedef struct udphdr udphdr_t; typedef struct icmp icmphdr_t; typedef struct ip ip_t; typedef struct ether_header ether_header_t; #endif /* linux */ typedef struct tcpiphdr tcpiphdr_t; #if defined(hpux) || defined(linux) struct ether_addr { char ether_addr_octet[6]; }; #endif /* * XXX - This is one of those *awful* hacks which nobody likes */ #ifdef ultrix #define A_A #else #define A_A & #endif +#define TCPF_ALL (TH_FIN|TH_SYN|TH_RST|TH_PUSH|TH_ACK|TH_URG) + #ifndef ICMP_ROUTERADVERT # define ICMP_ROUTERADVERT 9 #endif #ifndef ICMP_ROUTERSOLICIT # define ICMP_ROUTERSOLICIT 10 #endif +#undef ICMP_MAX_UNREACH +#define ICMP_MAX_UNREACH 14 +#undef ICMP_MAXTYPE +#define ICMP_MAXTYPE 18 /* * ICMP error replies have an IP header (20 bytes), 8 bytes of ICMP data, * another IP header and then 64 bits of data, totalling 56. Of course, * the last 64 bits is dependant on that being available. */ #define ICMPERR_ICMPHLEN 8 #define ICMPERR_IPICMPHLEN (20 + 8) #define ICMPERR_MINPKTLEN (20 + 8 + 20) #define ICMPERR_MAXPKTLEN (20 + 8 + 20 + 8) +#define ICMP6ERR_MINPKTLEN (20 + 8) #endif /* __IP_COMPAT_H__ */ Index: head/sys/contrib/ipfilter/netinet/ip_ftp_pxy.c =================================================================== --- head/sys/contrib/ipfilter/netinet/ip_ftp_pxy.c (revision 60856) +++ head/sys/contrib/ipfilter/netinet/ip_ftp_pxy.c (revision 60857) @@ -1,458 +1,755 @@ /* * Simple FTP transparent proxy for in-kernel use. For use with the NAT * code. * $FreeBSD$ */ #if SOLARIS && defined(_KERNEL) extern kmutex_t ipf_rw; #endif #define isdigit(x) ((x) >= '0' && (x) <= '9') +#define isupper(x) ((unsigned)((x) - 'A') <= 'Z' - 'A') #define IPF_FTP_PROXY #define IPF_MINPORTLEN 18 #define IPF_MAXPORTLEN 30 #define IPF_MIN227LEN 39 #define IPF_MAX227LEN 51 +#define IPF_FTPBUFSZ 96 /* This *MUST* be >= 53! */ +int ippr_ftp_client __P((fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int)); +int ippr_ftp_complete __P((char *, size_t)); +int ippr_ftp_in __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *)); int ippr_ftp_init __P((void)); +int ippr_ftp_new __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *)); int ippr_ftp_out __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *)); -int ippr_ftp_in __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *)); -int ippr_ftp_portmsg __P((fr_info_t *, ip_t *, nat_t *)); -int ippr_ftp_pasvmsg __P((fr_info_t *, ip_t *, nat_t *)); +int ippr_ftp_pasv __P((fr_info_t *, ip_t *, nat_t *, ftpside_t *, int)); +int ippr_ftp_port __P((fr_info_t *, ip_t *, nat_t *, ftpside_t *, int)); +int ippr_ftp_process __P((fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int)); +int ippr_ftp_server __P((fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int)); +int ippr_ftp_valid __P((char *, size_t)); +u_short ippr_ftp_atoi __P((char **)); -u_short ipf_ftp_atoi __P((char **)); - static frentry_t natfr; +int ippr_ftp_pasvonly = 0; /* * Initialize local structures. */ int ippr_ftp_init() { bzero((char *)&natfr, sizeof(natfr)); natfr.fr_ref = 1; natfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; return 0; } -/* - * ipf_ftp_atoi - implement a version of atoi which processes numbers in - * pairs separated by commas (which are expected to be in the range 0 - 255), - * returning a 16 bit number combining either side of the , as the MSB and - * LSB. - */ -u_short ipf_ftp_atoi(ptr) -char **ptr; +int ippr_ftp_new(fin, ip, aps, nat) +fr_info_t *fin; +ip_t *ip; +ap_session_t *aps; +nat_t *nat; { - register char *s = *ptr, c; - register u_char i = 0, j = 0; + ftpinfo_t *ftp; + ftpside_t *f; - while ((c = *s++) && isdigit(c)) { - i *= 10; - i += c - '0'; - } - if (c != ',') { - *ptr = NULL; - return 0; - } - while ((c = *s++) && isdigit(c)) { - j *= 10; - j += c - '0'; - } - *ptr = s; - return (i << 8) | j; + KMALLOC(ftp, ftpinfo_t *); + if (ftp == NULL) + return -1; + aps->aps_data = ftp; + aps->aps_psiz = sizeof(ftpinfo_t); + + bzero((char *)ftp, sizeof(*ftp)); + f = &ftp->ftp_side[0]; + f->ftps_rptr = f->ftps_buf; + f->ftps_wptr = f->ftps_buf; + f = &ftp->ftp_side[1]; + f->ftps_rptr = f->ftps_buf; + f->ftps_wptr = f->ftps_buf; + return 0; } -int ippr_ftp_portmsg(fin, ip, nat) +int ippr_ftp_port(fin, ip, nat, f, dlen) fr_info_t *fin; ip_t *ip; nat_t *nat; +ftpside_t *f; +int dlen; { - char portbuf[IPF_MAXPORTLEN + 1], newbuf[IPF_MAXPORTLEN + 1], *s; tcphdr_t *tcp, tcph, *tcp2 = &tcph; - size_t nlen = 0, dlen, olen; + char newbuf[IPF_FTPBUFSZ], *s; u_short a5, a6, sp, dp; u_int a1, a2, a3, a4; struct in_addr swip; - int off, inc = 0; + size_t nlen, olen; fr_info_t fi; + int inc, off; nat_t *ipn; mb_t *m; #if SOLARIS mb_t *m1; #endif tcp = (tcphdr_t *)fin->fin_dp; - bzero(portbuf, sizeof(portbuf)); - off = (ip->ip_hl << 2) + (tcp->th_off << 2); - -#if SOLARIS - m = fin->fin_qfm; - - dlen = msgdsize(m) - off; - if (dlen > 0) - copyout_mblk(m, off, MIN(sizeof(portbuf), dlen), portbuf); -#else - m = *(mb_t **)fin->fin_mp; - - dlen = mbufchainlen(m) - off; - if (dlen > 0) - m_copydata(m, off, MIN(sizeof(portbuf), dlen), portbuf); -#endif - if (dlen == 0) + off = f->ftps_seq - ntohl(tcp->th_seq); + if (off < 0) return 0; - portbuf[sizeof(portbuf) - 1] = '\0'; - *newbuf = '\0'; - if (!strncmp(portbuf, "PORT ", 5)) { - if (dlen < IPF_MINPORTLEN) - return 0; - } else + /* + * Check for client sending out PORT message. + */ + if (dlen < IPF_MINPORTLEN) return 0; + /* + * Count the number of bytes in the PORT message is. + */ + if (off < 0) + return 0; + off += fin->fin_hlen + (tcp->th_off << 2); /* * Skip the PORT command + space */ - s = portbuf + 5; + s = f->ftps_rptr + 5; /* * Pick out the address components, two at a time. */ - a1 = ipf_ftp_atoi(&s); + a1 = ippr_ftp_atoi(&s); if (!s) return 0; - a2 = ipf_ftp_atoi(&s); + a2 = ippr_ftp_atoi(&s); if (!s) return 0; - /* * check that IP address in the PORT/PASV reply is the same as the * sender of the command - prevents using PORT for port scanning. */ a1 <<= 16; a1 |= a2; if (a1 != ntohl(nat->nat_inip.s_addr)) return 0; - a5 = ipf_ftp_atoi(&s); + a5 = ippr_ftp_atoi(&s); if (!s) return 0; if (*s == ')') s++; /* * check for CR-LF at the end. */ if (*s == '\n') s--; if ((*s == '\r') && (*(s + 1) == '\n')) { s += 2; a6 = a5 & 0xff; } else return 0; a5 >>= 8; /* * Calculate new address parts for PORT command */ a1 = ntohl(ip->ip_src.s_addr); a2 = (a1 >> 16) & 0xff; a3 = (a1 >> 8) & 0xff; a4 = a1 & 0xff; a1 >>= 24; - olen = s - portbuf; + olen = s - f->ftps_rptr; + /* DO NOT change this to sprintf! */ (void) sprintf(newbuf, "%s %u,%u,%u,%u,%u,%u\r\n", "PORT", a1, a2, a3, a4, a5, a6); nlen = strlen(newbuf); inc = nlen - olen; + if ((inc + ip->ip_len) > 65535) + return 0; + #if SOLARIS + m = fin->fin_qfm; for (m1 = m; m1->b_cont; m1 = m1->b_cont) ; if ((inc > 0) && (m1->b_datap->db_lim - m1->b_wptr < inc)) { mblk_t *nm; /* alloc enough to keep same trailer space for lower driver */ nm = allocb(nlen, BPRI_MED); PANIC((!nm),("ippr_ftp_out: allocb failed")); nm->b_band = m1->b_band; nm->b_wptr += nlen; m1->b_wptr -= olen; PANIC((m1->b_wptr < m1->b_rptr), ("ippr_ftp_out: cannot handle fragmented data block")); linkb(m1, nm); } else { if (m1->b_datap->db_struiolim == m1->b_wptr) m1->b_datap->db_struiolim += inc; m1->b_datap->db_struioflag &= ~STRUIO_IP; m1->b_wptr += inc; } copyin_mblk(m, off, nlen, newbuf); #else + m = *((mb_t **)fin->fin_mp); if (inc < 0) m_adj(m, inc); /* the mbuf chain will be extended if necessary by m_copyback() */ m_copyback(m, off, nlen, newbuf); #endif if (inc != 0) { #if SOLARIS || defined(__sgi) register u_32_t sum1, sum2; sum1 = ip->ip_len; sum2 = ip->ip_len + inc; /* Because ~1 == -2, We really need ~1 == -1 */ if (sum1 > sum2) sum2--; sum2 -= sum1; sum2 = (sum2 & 0xffff) + (sum2 >> 16); fix_outcksum(&ip->ip_sum, sum2, 0); #endif ip->ip_len += inc; } /* * Add skeleton NAT entry for connection which will come back the * other way. */ sp = htons(a5 << 8 | a6); /* + * Don't allow the PORT command to specify a port < 1024 due to + * security crap. + */ + if (ntohs(sp) < 1024) + return 0; + /* * The server may not make the connection back from port 20, but * it is the most likely so use it here to check for a conflicting * mapping. */ dp = htons(fin->fin_data[1] - 1); ipn = nat_outlookup(fin->fin_ifp, IPN_TCP, nat->nat_p, nat->nat_inip, ip->ip_dst, (dp << 16) | sp); if (ipn == NULL) { + int slen; + + slen = ip->ip_len; + ip->ip_len = fin->fin_hlen + sizeof(*tcp2); bcopy((char *)fin, (char *)&fi, sizeof(fi)); bzero((char *)tcp2, sizeof(*tcp2)); tcp2->th_win = htons(8192); tcp2->th_sport = sp; + tcp2->th_off = 5; tcp2->th_dport = 0; /* XXX - don't specify remote port */ fi.fin_data[0] = ntohs(sp); fi.fin_data[1] = 0; fi.fin_dp = (char *)tcp2; swip = ip->ip_src; ip->ip_src = nat->nat_inip; ipn = nat_new(nat->nat_ptr, ip, &fi, IPN_TCP|FI_W_DPORT, NAT_OUTBOUND); if (ipn != NULL) { ipn->nat_age = fr_defnatage; (void) fr_addstate(ip, &fi, FI_W_DPORT); } + ip->ip_len = slen; ip->ip_src = swip; } return inc; } -int ippr_ftp_out(fin, ip, aps, nat) +int ippr_ftp_client(fin, ip, nat, ftp, dlen) fr_info_t *fin; -ip_t *ip; -ap_session_t *aps; nat_t *nat; +ftpinfo_t *ftp; +ip_t *ip; +int dlen; { - return ippr_ftp_portmsg(fin, ip, nat); + char *rptr, *wptr; + ftpside_t *f; + int inc; + + inc = 0; + f = &ftp->ftp_side[0]; + rptr = f->ftps_rptr; + wptr = f->ftps_wptr; + + if ((ftp->ftp_passok == 0) && !strncmp(rptr, "USER ", 5)) + ftp->ftp_passok = 1; + else if ((ftp->ftp_passok == 2) && !strncmp(rptr, "PASS ", 5)) + ftp->ftp_passok = 3; + else if ((ftp->ftp_passok == 4) && !ippr_ftp_pasvonly && + !strncmp(rptr, "PORT ", 5)) { + inc = ippr_ftp_port(fin, ip, nat, f, dlen); + } + + while ((*rptr++ != '\n') && (rptr < wptr)) + ; + f->ftps_seq += rptr - f->ftps_rptr; + f->ftps_rptr = rptr; + return inc; } -int ippr_ftp_pasvmsg(fin, ip, nat) +int ippr_ftp_pasv(fin, ip, nat, f, dlen) fr_info_t *fin; ip_t *ip; nat_t *nat; +ftpside_t *f; +int dlen; { - char portbuf[IPF_MAX227LEN + 1], newbuf[IPF_MAX227LEN + 1], *s; - int off, olen, dlen, nlen = 0, inc = 0; - tcphdr_t tcph, *tcp2 = &tcph; + tcphdr_t *tcp, tcph, *tcp2 = &tcph; struct in_addr swip, swip2; - u_short a5, a6, dp, sp; + u_short a5, a6, sp, dp; u_int a1, a2, a3, a4; - tcphdr_t *tcp; fr_info_t fi; + int inc, off; nat_t *ipn; - mb_t *m; -#if SOLARIS - mb_t *m1; -#endif + char *s; - tcp = (tcphdr_t *)fin->fin_dp; - off = (ip->ip_hl << 2) + (tcp->th_off << 2); - m = *(mb_t **)fin->fin_mp; - bzero(portbuf, sizeof(portbuf)); - -#if SOLARIS - m = fin->fin_qfm; - - dlen = msgdsize(m) - off; - if (dlen > 0) - copyout_mblk(m, off, MIN(sizeof(portbuf), dlen), portbuf); -#else - dlen = mbufchainlen(m) - off; - if (dlen > 0) - m_copydata(m, off, MIN(sizeof(portbuf), dlen), portbuf); -#endif - if (dlen == 0) + /* + * Check for PASV reply message. + */ + if (dlen < IPF_MIN227LEN) return 0; - portbuf[sizeof(portbuf) - 1] = '\0'; - *newbuf = '\0'; + else if (strncmp(f->ftps_rptr, "227 Entering Passive Mode", 25)) + return 0; - if (!strncmp(portbuf, "227 ", 4)) { - if (dlen < IPF_MIN227LEN) - return 0; - else if (strncmp(portbuf, "227 Entering Passive Mode", 25)) - return 0; - } else + /* + * Count the number of bytes in the 227 reply is. + */ + tcp = (tcphdr_t *)fin->fin_dp; + off = f->ftps_seq - ntohl(tcp->th_seq); + if (off < 0) return 0; + + off += fin->fin_hlen + (tcp->th_off << 2); /* * Skip the PORT command + space */ - s = portbuf + 25; + s = f->ftps_rptr + 25; while (*s && !isdigit(*s)) s++; /* * Pick out the address components, two at a time. */ - a1 = ipf_ftp_atoi(&s); + a1 = ippr_ftp_atoi(&s); if (!s) return 0; - a2 = ipf_ftp_atoi(&s); + a2 = ippr_ftp_atoi(&s); if (!s) return 0; /* * check that IP address in the PORT/PASV reply is the same as the * sender of the command - prevents using PORT for port scanning. */ a1 <<= 16; a1 |= a2; if (a1 != ntohl(nat->nat_oip.s_addr)) return 0; - a5 = ipf_ftp_atoi(&s); + a5 = ippr_ftp_atoi(&s); if (!s) return 0; if (*s == ')') s++; if (*s == '\n') s--; /* * check for CR-LF at the end. */ if ((*s == '\r') && (*(s + 1) == '\n')) { s += 2; a6 = a5 & 0xff; } else return 0; a5 >>= 8; /* * Calculate new address parts for 227 reply */ a1 = ntohl(ip->ip_src.s_addr); a2 = (a1 >> 16) & 0xff; a3 = (a1 >> 8) & 0xff; a4 = a1 & 0xff; a1 >>= 24; - olen = s - portbuf; + inc = 0; +#if 0 + olen = s - f->ftps_rptr; (void) sprintf(newbuf, "%s %u,%u,%u,%u,%u,%u\r\n", "227 Entering Passive Mode", a1, a2, a3, a4, a5, a6); - nlen = strlen(newbuf); inc = nlen - olen; + if ((inc + ip->ip_len) > 65535) + return 0; + #if SOLARIS + m = fin->fin_qfm; for (m1 = m; m1->b_cont; m1 = m1->b_cont) ; if ((inc > 0) && (m1->b_datap->db_lim - m1->b_wptr < inc)) { mblk_t *nm; /* alloc enough to keep same trailer space for lower driver */ nm = allocb(nlen, BPRI_MED); PANIC((!nm),("ippr_ftp_out: allocb failed")); nm->b_band = m1->b_band; nm->b_wptr += nlen; m1->b_wptr -= olen; PANIC((m1->b_wptr < m1->b_rptr), ("ippr_ftp_out: cannot handle fragmented data block")); linkb(m1, nm); } else { m1->b_wptr += inc; } - copyin_mblk(m, off, nlen, newbuf); + /*copyin_mblk(m, off, nlen, newbuf);*/ #else + m = *((mb_t **)fin->fin_mp); if (inc < 0) m_adj(m, inc); /* the mbuf chain will be extended if necessary by m_copyback() */ - m_copyback(m, off, nlen, newbuf); + /*m_copyback(m, off, nlen, newbuf);*/ #endif if (inc != 0) { #if SOLARIS || defined(__sgi) register u_32_t sum1, sum2; sum1 = ip->ip_len; sum2 = ip->ip_len + inc; /* Because ~1 == -2, We really need ~1 == -1 */ if (sum1 > sum2) sum2--; sum2 -= sum1; sum2 = (sum2 & 0xffff) + (sum2 >> 16); fix_outcksum(&ip->ip_sum, sum2, 0); #endif ip->ip_len += inc; } +#endif /* * Add skeleton NAT entry for connection which will come back the * other way. */ sp = 0; dp = htons(fin->fin_data[1] - 1); ipn = nat_outlookup(fin->fin_ifp, IPN_TCP, nat->nat_p, nat->nat_inip, ip->ip_dst, (dp << 16) | sp); if (ipn == NULL) { + int slen; + + slen = ip->ip_len; + ip->ip_len = fin->fin_hlen + sizeof(*tcp2); bcopy((char *)fin, (char *)&fi, sizeof(fi)); bzero((char *)tcp2, sizeof(*tcp2)); tcp2->th_win = htons(8192); tcp2->th_sport = 0; /* XXX - fake it for nat_new */ + tcp2->th_off = 5; fi.fin_data[0] = a5 << 8 | a6; tcp2->th_dport = htons(fi.fin_data[0]); fi.fin_data[1] = 0; fi.fin_dp = (char *)tcp2; swip = ip->ip_src; swip2 = ip->ip_dst; ip->ip_dst = ip->ip_src; ip->ip_src = nat->nat_inip; ipn = nat_new(nat->nat_ptr, ip, &fi, IPN_TCP|FI_W_SPORT, NAT_OUTBOUND); if (ipn != NULL) { ipn->nat_age = fr_defnatage; (void) fr_addstate(ip, &fi, FI_W_SPORT); } + ip->ip_len = slen; ip->ip_src = swip; ip->ip_dst = swip2; } return inc; } +int ippr_ftp_server(fin, ip, nat, ftp, dlen) +fr_info_t *fin; +ip_t *ip; +nat_t *nat; +ftpinfo_t *ftp; +int dlen; +{ + char *rptr, *wptr; + ftpside_t *f; + int inc; + + inc = 0; + f = &ftp->ftp_side[1]; + rptr = f->ftps_rptr; + wptr = f->ftps_wptr; + + if ((ftp->ftp_passok == 1) && !strncmp(rptr, "331", 3)) + ftp->ftp_passok = 2; + else if ((ftp->ftp_passok == 3) && !strncmp(rptr, "230", 3)) + ftp->ftp_passok = 4; + else if ((ftp->ftp_passok == 3) && !strncmp(rptr, "530", 3)) + ftp->ftp_passok = 0; + else if ((ftp->ftp_passok == 4) && !strncmp(rptr, "227 ", 4)) { + inc = ippr_ftp_pasv(fin, ip, nat, f, dlen); + } + while ((*rptr++ != '\n') && (rptr < wptr)) + ; + f->ftps_seq += rptr - f->ftps_rptr; + f->ftps_rptr = rptr; + return inc; +} + + +/* + * Look to see if the buffer starts with something which we recognise as + * being the correct syntax for the FTP protocol. + */ +int ippr_ftp_valid(buf, len) +char *buf; +size_t len; +{ + register char *s, c; + register size_t i = len; + + if (i < 5) + return 2; + s = buf; + c = *s++; + i--; + + if (isdigit(c)) { + c = *s++; + i--; + if (isdigit(c)) { + c = *s++; + i--; + if (isdigit(c)) { + c = *s++; + i--; + if ((c != '-') && (c != ' ')) + return 1; + } else + return 1; + } else + return 1; + } else if (isupper(c)) { + c = *s++; + i--; + if (isupper(c)) { + c = *s++; + i--; + if (isupper(c)) { + c = *s++; + i--; + if (isupper(c)) { + c = *s++; + i--; + if ((c != ' ') && (c != '\r')) + return 1; + } else if ((c != ' ') && (c != '\r')) + return 1; + } else + return 1; + } else + return 1; + } else + return 1; + for (; i; i--) { + c = *s++; + if (c == '\n') + return 0; + } + return 2; +} + + +int ippr_ftp_process(fin, ip, nat, ftp, rv) +fr_info_t *fin; +ip_t *ip; +nat_t *nat; +ftpinfo_t *ftp; +int rv; +{ + int mlen, len, off, inc, i; + char *rptr, *wptr; + tcphdr_t *tcp; + ftpside_t *f; + mb_t *m; + + tcp = (tcphdr_t *)fin->fin_dp; + off = fin->fin_hlen + (tcp->th_off << 2); + +#if SOLARIS + m = fin->fin_qfm; +#else + m = *((mb_t **)fin->fin_mp); +#endif + +#if SOLARIS + mlen = msgdsize(m) - off; +#else + mlen = mbufchainlen(m) - off; +#endif + if (!mlen) + return 0; + + inc = 0; + f = &ftp->ftp_side[rv]; + rptr = f->ftps_rptr; + wptr = f->ftps_wptr; + if ((wptr == f->ftps_buf) && (f->ftps_seq <= ntohl(tcp->th_seq))) + f->ftps_seq = ntohl(tcp->th_seq); + + /* + * XXX - Ideally, this packet should get dropped because we now know + * that it is out of order (and there is no real danger in doing so + * apart from causing packets to go through here ordered). + */ + if (ntohl(tcp->th_seq) != f->ftps_seq + (wptr - rptr)) { + return APR_ERR(0); + } + + while (mlen > 0) { + len = MIN(mlen, FTP_BUFSZ / 2); + +#if SOLARIS + copyout_mblk(m, off, len, wptr); +#else + m_copydata(m, off, len, wptr); +#endif + mlen -= len; + off += len; + wptr += len; + f->ftps_wptr = wptr; + if (f->ftps_junk == 2) + f->ftps_junk = ippr_ftp_valid(rptr, wptr - rptr); + + while ((f->ftps_junk == 0) && (wptr > rptr)) { + f->ftps_junk = ippr_ftp_valid(rptr, wptr - rptr); + if (f->ftps_junk == 0) { + len = wptr - rptr; + f->ftps_rptr = rptr; + if (rv) + inc += ippr_ftp_server(fin, ip, nat, + ftp, len); + else + inc += ippr_ftp_client(fin, ip, nat, + ftp, len); + rptr = f->ftps_rptr; + } + } + + while ((f->ftps_junk == 1) && (rptr < wptr)) { + while ((rptr < wptr) && (*rptr != '\r')) + rptr++; + + if ((*rptr == '\r') && (rptr + 1 < wptr)) { + if (*(rptr + 1) == '\n') { + rptr += 2; + f->ftps_junk = 0; + } else + rptr++; + } + f->ftps_seq += rptr - f->ftps_rptr; + f->ftps_rptr = rptr; + } + + if (rptr == wptr) { + rptr = wptr = f->ftps_buf; + } else { + if ((wptr > f->ftps_buf + FTP_BUFSZ / 2)) { + i = wptr - rptr; + if ((rptr == f->ftps_buf) || + (wptr - rptr > FTP_BUFSZ / 2)) { + f->ftps_seq += i; + f->ftps_junk = 1; + rptr = wptr = f->ftps_buf; + } else { + bcopy(rptr, f->ftps_buf, i); + wptr = f->ftps_buf + i; + rptr = f->ftps_buf; + } + } + f->ftps_rptr = rptr; + f->ftps_wptr = wptr; + } + } + + f->ftps_rptr = rptr; + f->ftps_wptr = wptr; + return inc; +} + + +int ippr_ftp_out(fin, ip, aps, nat) +fr_info_t *fin; +ip_t *ip; +ap_session_t *aps; +nat_t *nat; +{ + ftpinfo_t *ftp; + + ftp = aps->aps_data; + if (ftp == NULL) + return 0; + return ippr_ftp_process(fin, ip, nat, ftp, 0); +} + + int ippr_ftp_in(fin, ip, aps, nat) fr_info_t *fin; ip_t *ip; ap_session_t *aps; nat_t *nat; { + ftpinfo_t *ftp; - return ippr_ftp_pasvmsg(fin, ip, nat); + ftp = aps->aps_data; + if (ftp == NULL) + return 0; + return ippr_ftp_process(fin, ip, nat, ftp, 1); +} + + +/* + * ippr_ftp_atoi - implement a version of atoi which processes numbers in + * pairs separated by commas (which are expected to be in the range 0 - 255), + * returning a 16 bit number combining either side of the , as the MSB and + * LSB. + */ +u_short ippr_ftp_atoi(ptr) +char **ptr; +{ + register char *s = *ptr, c; + register u_char i = 0, j = 0; + + while ((c = *s++) && isdigit(c)) { + i *= 10; + i += c - '0'; + } + if (c != ',') { + *ptr = NULL; + return 0; + } + while ((c = *s++) && isdigit(c)) { + j *= 10; + j += c - '0'; + } + *ptr = s; + return (i << 8) | j; } Index: head/sys/contrib/ipfilter/netinet/ip_log.c =================================================================== --- head/sys/contrib/ipfilter/netinet/ip_log.c (revision 60856) +++ head/sys/contrib/ipfilter/netinet/ip_log.c (revision 60857) @@ -1,499 +1,500 @@ /* - * Copyright (C) 1997-1998 by Darren Reed. + * Copyright (C) 1997-2000 by Darren Reed. * * Redistribution and use in source and binary forms are permitted * provided that this notice is preserved and due credit is given * to the original author and the contributors. * * $Id: ip_log.c,v 2.1.2.2 1999/09/21 11:55:44 darrenr Exp $ * $FreeBSD$ */ #include #if defined(KERNEL) && !defined(_KERNEL) # define _KERNEL #endif #if defined(__NetBSD__) && (NetBSD >= 199905) && !defined(IPFILTER_LKM) # include "opt_ipfilter_log.h" #endif #ifdef __FreeBSD__ # if defined(_KERNEL) && !defined(IPFILTER_LKM) -# if !defined(__FreeBSD_version) -# include -# endif # if defined(__FreeBSD_version) && (__FreeBSD_version >= 300000) # include "opt_ipfilter.h" # endif +# else +# include # endif #endif #ifdef IPFILTER_LOG # ifndef SOLARIS # define SOLARIS (defined(sun) && (defined(__svr4__) || defined(__SVR4))) # endif # ifndef _KERNEL # include # include # include # include # endif # include # include # include # if __FreeBSD_version >= 220000 && defined(_KERNEL) # include # include # else # include # endif # include # if defined(_KERNEL) && !defined(linux) # include # endif # include # if !SOLARIS # if (NetBSD > 199609) || (OpenBSD > 199603) || (__FreeBSD_version >= 300000) # include # else # include # endif # ifndef linux # include # endif # else # include # include # include # include # include # include # include # include # include # endif # ifndef linux # include # endif # include # include # ifdef sun # include # endif # if __FreeBSD_version >= 300000 # include # endif # include # include # ifdef __sgi # include # ifdef IFF_DRVRLOCK /* IRIX6 */ # include # endif # endif # if !defined(linux) && !(defined(__sgi) && !defined(IFF_DRVRLOCK)) /*IRIX<6*/ # include # endif # include # include # include # include # include # ifndef linux # include # endif # ifndef _KERNEL # include # endif # include "netinet/ip_compat.h" # include # include "netinet/ip_fil.h" # include "netinet/ip_proxy.h" # include "netinet/ip_nat.h" # include "netinet/ip_frag.h" # include "netinet/ip_state.h" # include "netinet/ip_auth.h" # if (__FreeBSD_version >= 300000) # include # endif # ifndef MIN # define MIN(a,b) (((a)<(b))?(a):(b)) # endif # if SOLARIS || defined(__sgi) extern kmutex_t ipl_mutex; # if SOLARIS extern kcondvar_t iplwait; # endif # endif iplog_t **iplh[IPL_LOGMAX+1], *iplt[IPL_LOGMAX+1], *ipll[IPL_LOGMAX+1]; size_t iplused[IPL_LOGMAX+1]; -fr_info_t iplcrc[IPL_LOGMAX+1]; +static fr_info_t iplcrc[IPL_LOGMAX+1]; # ifdef linux static struct wait_queue *iplwait[IPL_LOGMAX+1]; # endif /* * Initialise log buffers & pointers. Also iniialised the CRC to a local * secret for use in calculating the "last log checksum". */ void ipflog_init() { int i; for (i = IPL_LOGMAX; i >= 0; i--) { iplt[i] = NULL; ipll[i] = NULL; iplh[i] = &iplt[i]; iplused[i] = 0; bzero((char *)&iplcrc[i], sizeof(iplcrc[i])); } } /* * ipflog * Create a log record for a packet given that it has been triggered by a * rule (or the default setting). Calculate the transport protocol header * size using predetermined size of a couple of popular protocols and thus * how much data to copy into the log, including part of the data body if * requested. */ int ipflog(flags, ip, fin, m) u_int flags; ip_t *ip; fr_info_t *fin; mb_t *m; { ipflog_t ipfl; register size_t mlen, hlen; size_t sizes[2]; void *ptrs[2]; int types[2]; + u_char p; # if SOLARIS ill_t *ifp = fin->fin_ifp; # else struct ifnet *ifp = fin->fin_ifp; # endif /* * calculate header size. */ hlen = fin->fin_hlen; - if ((ip->ip_off & IP_OFFMASK) == 0) { - if (ip->ip_p == IPPROTO_TCP) + if (fin->fin_off == 0) { + p = fin->fin_fi.fi_p; + if (p == IPPROTO_TCP) hlen += MIN(sizeof(tcphdr_t), fin->fin_dlen); - else if (ip->ip_p == IPPROTO_UDP) + else if (p == IPPROTO_UDP) hlen += MIN(sizeof(udphdr_t), fin->fin_dlen); - else if (ip->ip_p == IPPROTO_ICMP) { - struct icmp *icmp; + else if (p == IPPROTO_ICMP) { + struct icmp *icmp; - icmp = (struct icmp *)((char *)ip + hlen); + icmp = (struct icmp *)fin->fin_dp; /* * For ICMP, if the packet is an error packet, also * include the information about the packet which * caused the error. */ switch (icmp->icmp_type) { case ICMP_UNREACH : case ICMP_SOURCEQUENCH : case ICMP_REDIRECT : case ICMP_TIMXCEED : case ICMP_PARAMPROB : hlen += MIN(sizeof(struct icmp) + 8, fin->fin_dlen); break; default : hlen += MIN(sizeof(struct icmp), fin->fin_dlen); break; } } } /* * Get the interface number and name to which this packet is * currently associated. */ # if SOLARIS ipfl.fl_unit = (u_char)ifp->ill_ppa; bcopy(ifp->ill_name, ipfl.fl_ifname, MIN(ifp->ill_name_length, 4)); mlen = (flags & FR_LOGBODY) ? MIN(msgdsize(m) - hlen, 128) : 0; # else # if (defined(NetBSD) && (NetBSD <= 1991011) && (NetBSD >= 199603)) || \ (defined(OpenBSD) && (OpenBSD >= 199603)) strncpy(ipfl.fl_ifname, ifp->if_xname, IFNAMSIZ); # else # ifndef linux ipfl.fl_unit = (u_char)ifp->if_unit; # endif if ((ipfl.fl_ifname[0] = ifp->if_name[0])) if ((ipfl.fl_ifname[1] = ifp->if_name[1])) if ((ipfl.fl_ifname[2] = ifp->if_name[2])) ipfl.fl_ifname[3] = ifp->if_name[3]; # endif - mlen = (flags & FR_LOGBODY) ? MIN(ip->ip_len - hlen, 128) : 0; + mlen = (flags & FR_LOGBODY) ? MIN(fin->fin_plen - hlen, 128) : 0; # endif ipfl.fl_plen = (u_char)mlen; ipfl.fl_hlen = (u_char)hlen; ipfl.fl_rule = fin->fin_rule; ipfl.fl_group = fin->fin_group; if (fin->fin_fr != NULL) ipfl.fl_loglevel = fin->fin_fr->fr_loglevel; else ipfl.fl_loglevel = 0xffff; ipfl.fl_flags = flags; ptrs[0] = (void *)&ipfl; sizes[0] = sizeof(ipfl); types[0] = 0; # if SOLARIS /* * Are we copied from the mblk or an aligned array ? */ if (ip == (ip_t *)m->b_rptr) { ptrs[1] = m; sizes[1] = hlen + mlen; types[1] = 1; } else { ptrs[1] = ip; sizes[1] = hlen + mlen; types[1] = 0; } # else ptrs[1] = m; sizes[1] = hlen + mlen; types[1] = 1; # endif return ipllog(IPL_LOGIPF, fin, ptrs, sizes, types, 2); } /* * ipllog */ int ipllog(dev, fin, items, itemsz, types, cnt) int dev; fr_info_t *fin; void **items; size_t *itemsz; int *types, cnt; { caddr_t buf, s; iplog_t *ipl; size_t len; int i; /* * Check to see if this log record has a CRC which matches the last * record logged. If it does, just up the count on the previous one * rather than create a new one. */ MUTEX_ENTER(&ipl_mutex); if (fin != NULL) { if ((ipll[dev] != NULL) && bcmp((char *)fin, (char *)&iplcrc[dev], FI_CSIZE) == 0) { ipll[dev]->ipl_count++; MUTEX_EXIT(&ipl_mutex); return 1; } bcopy((char *)fin, (char *)&iplcrc[dev], FI_CSIZE); } else bzero((char *)&iplcrc[dev], FI_CSIZE); MUTEX_EXIT(&ipl_mutex); /* * Get the total amount of data to be logged. */ for (i = 0, len = sizeof(iplog_t); i < cnt; i++) len += itemsz[i]; /* * check that we have space to record this information and can * allocate that much. */ KMALLOCS(buf, caddr_t, len); if (!buf) return 0; MUTEX_ENTER(&ipl_mutex); if ((iplused[dev] + len) > IPLLOGSIZE) { MUTEX_EXIT(&ipl_mutex); KFREES(buf, len); return 0; } iplused[dev] += len; MUTEX_EXIT(&ipl_mutex); /* * advance the log pointer to the next empty record and deduct the * amount of space we're going to use. */ ipl = (iplog_t *)buf; ipl->ipl_magic = IPL_MAGIC; ipl->ipl_count = 1; ipl->ipl_next = NULL; ipl->ipl_dsize = len; # if SOLARIS || defined(sun) || defined(linux) uniqtime((struct timeval *)&ipl->ipl_sec); # else # if BSD >= 199306 || defined(__FreeBSD__) || defined(__sgi) microtime((struct timeval *)&ipl->ipl_sec); # endif # endif /* * Loop through all the items to be logged, copying each one to the * buffer. Use bcopy for normal data or the mb_t copyout routine. */ for (i = 0, s = buf + sizeof(*ipl); i < cnt; i++) { if (types[i] == 0) bcopy(items[i], s, itemsz[i]); else if (types[i] == 1) { # if SOLARIS copyout_mblk(items[i], 0, itemsz[i], s); # else m_copydata(items[i], 0, itemsz[i], s); # endif } s += itemsz[i]; } MUTEX_ENTER(&ipl_mutex); ipll[dev] = ipl; *iplh[dev] = ipl; iplh[dev] = &ipl->ipl_next; # if SOLARIS cv_signal(&iplwait); mutex_exit(&ipl_mutex); # else MUTEX_EXIT(&ipl_mutex); # ifdef linux wake_up_interruptible(&iplwait[dev]); # else wakeup(&iplh[dev]); # endif # endif return 1; } int ipflog_read(unit, uio) minor_t unit; struct uio *uio; { size_t dlen, copied; int error = 0; iplog_t *ipl; # if defined(_KERNEL) && !SOLARIS int s; # endif /* * Sanity checks. Make sure the minor # is valid and we're copying * a valid chunk of data. */ if (IPL_LOGMAX < unit) return ENXIO; if (!uio->uio_resid) return 0; if ((uio->uio_resid < sizeof(iplog_t)) || (uio->uio_resid > IPLLOGSIZE)) return EINVAL; /* * Lock the log so we can snapshot the variables. Wait for a signal * if the log is empty. */ SPL_NET(s); MUTEX_ENTER(&ipl_mutex); while (!iplused[unit] || !iplt[unit]) { # if SOLARIS && defined(_KERNEL) if (!cv_wait_sig(&iplwait, &ipl_mutex)) { MUTEX_EXIT(&ipl_mutex); return EINTR; } # else # ifdef linux interruptible_sleep_on(&iplwait[unit]); if (current->signal & ~current->blocked) return -EINTR; # else MUTEX_EXIT(&ipl_mutex); SPL_X(s); error = SLEEP(&iplh[unit], "ipl sleep"); if (error) return error; SPL_NET(s); MUTEX_ENTER(&ipl_mutex); # endif /* linux */ # endif /* SOLARIS */ } # if BSD >= 199306 || defined(__FreeBSD__) uio->uio_rw = UIO_READ; # endif for (copied = 0; (ipl = iplt[unit]); copied += dlen) { dlen = ipl->ipl_dsize; if (dlen > uio->uio_resid) break; /* * Don't hold the mutex over the uiomove call. */ iplt[unit] = ipl->ipl_next; iplused[unit] -= dlen; MUTEX_EXIT(&ipl_mutex); SPL_X(s); error = UIOMOVE((caddr_t)ipl, dlen, UIO_READ, uio); if (error) { SPL_NET(s); MUTEX_ENTER(&ipl_mutex); ipl->ipl_next = iplt[unit]; iplt[unit] = ipl; iplused[unit] += dlen; break; } KFREES((caddr_t)ipl, dlen); SPL_NET(s); MUTEX_ENTER(&ipl_mutex); } if (!iplt[unit]) { iplused[unit] = 0; iplh[unit] = &iplt[unit]; ipll[unit] = NULL; } MUTEX_EXIT(&ipl_mutex); SPL_X(s); # ifdef linux if (!error) return (int)copied; return -error; # else return error; # endif } int ipflog_clear(unit) minor_t unit; { iplog_t *ipl; int used; MUTEX_ENTER(&ipl_mutex); while ((ipl = iplt[unit])) { iplt[unit] = ipl->ipl_next; KFREES((caddr_t)ipl, ipl->ipl_dsize); } iplh[unit] = &iplt[unit]; ipll[unit] = NULL; used = iplused[unit]; iplused[unit] = 0; bzero((char *)&iplcrc[unit], FI_CSIZE); MUTEX_EXIT(&ipl_mutex); return used; } #endif /* IPFILTER_LOG */ Index: head/sys/contrib/ipfilter/netinet/ip_nat.c =================================================================== --- head/sys/contrib/ipfilter/netinet/ip_nat.c (revision 60856) +++ head/sys/contrib/ipfilter/netinet/ip_nat.c (revision 60857) @@ -1,2301 +1,2302 @@ /* * Copyright (C) 1995-2000 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) static const char sccsid[] = "@(#)ip_nat.c 1.11 6/5/96 (C) 1995 Darren Reed"; /*static const char rcsid[] = "@(#)$Id: ip_nat.c,v 2.2.2.12 2000/01/24 12:43:40 darrenr Exp $";*/ static const char rcsid[] = "@(#)$FreeBSD$"; #endif #if defined(__FreeBSD__) && defined(KERNEL) && !defined(_KERNEL) #define _KERNEL #endif #include #include #include #include #include #if defined(__NetBSD__) && (NetBSD >= 199905) && !defined(IPFILTER_LKM) && \ defined(_KERNEL) # include "opt_ipfilter_log.h" #endif #if !defined(_KERNEL) && !defined(KERNEL) # include # include # include #endif #if (defined(KERNEL) || defined(_KERNEL)) && (__FreeBSD_version >= 220000) # include # include #else # include #endif #include #include #ifndef linux # include #endif #include #if defined(_KERNEL) && !defined(linux) # include #endif #if !defined(__SVR4) && !defined(__svr4__) # ifndef linux # include # endif #else # include # include # ifdef _KERNEL # include # endif # include # include #endif #if __FreeBSD_version >= 300000 # include #endif #include #if __FreeBSD_version >= 300000 # include # if defined(_KERNEL) && !defined(IPFILTER_LKM) # include "opt_ipfilter.h" # endif #endif #ifdef sun # include #endif #include #include #include #include #ifdef __sgi # ifdef IFF_DRVRLOCK /* IRIX6 */ #include #include # endif #endif #ifdef RFC1825 # include # include extern struct ifnet vpnif; #endif #ifndef linux # include #endif #include #include #include #include "netinet/ip_compat.h" #include #include "netinet/ip_fil.h" #include "netinet/ip_proxy.h" #include "netinet/ip_nat.h" #include "netinet/ip_frag.h" #include "netinet/ip_state.h" #if (__FreeBSD_version >= 300000) # include #endif #ifndef MIN # define MIN(a,b) (((a)<(b))?(a):(b)) #endif #undef SOCKADDR_IN #define SOCKADDR_IN struct sockaddr_in nat_t **nat_table[2] = { NULL, NULL }, *nat_instances = NULL; ipnat_t *nat_list = NULL; u_int ipf_nattable_sz = NAT_TABLE_SZ; u_int ipf_natrules_sz = NAT_SIZE; u_int ipf_rdrrules_sz = RDR_SIZE; u_int ipf_hostmap_sz = HOSTMAP_SIZE; u_32_t nat_masks = 0; u_32_t rdr_masks = 0; ipnat_t **nat_rules = NULL; ipnat_t **rdr_rules = NULL; hostmap_t **maptable = NULL; u_long fr_defnatage = DEF_NAT_AGE, fr_defnaticmpage = 6; /* 3 seconds */ static natstat_t nat_stats; int fr_nat_lock = 0; #if (SOLARIS || defined(__sgi)) && defined(_KERNEL) extern kmutex_t ipf_rw, ipf_hostmap; extern KRWLOCK_T ipf_nat; #endif static int nat_flushtable __P((void)); static int nat_clearlist __P((void)); static void nat_addnat __P((struct ipnat *)); static void nat_addrdr __P((struct ipnat *)); +static void nat_delete __P((struct nat *)); static void nat_delrdr __P((struct ipnat *)); static void nat_delnat __P((struct ipnat *)); static int fr_natgetent __P((caddr_t)); static int fr_natgetsz __P((caddr_t)); static int fr_natputent __P((caddr_t)); static int nat_match __P((fr_info_t *, ipnat_t *, ip_t *)); static hostmap_t *nat_hostmap __P((ipnat_t *, struct in_addr, struct in_addr)); static void nat_hostmapdel __P((struct hostmap *)); int nat_init() { KMALLOCS(nat_table[0], nat_t **, sizeof(nat_t *) * ipf_nattable_sz); if (nat_table[0] != NULL) bzero((char *)nat_table[0], ipf_nattable_sz * sizeof(nat_t *)); else return -1; KMALLOCS(nat_table[1], nat_t **, sizeof(nat_t *) * ipf_nattable_sz); if (nat_table[1] != NULL) bzero((char *)nat_table[1], ipf_nattable_sz * sizeof(nat_t *)); else return -1; KMALLOCS(nat_rules, ipnat_t **, sizeof(ipnat_t *) * ipf_natrules_sz); if (nat_rules != NULL) bzero((char *)nat_rules, ipf_natrules_sz * sizeof(ipnat_t *)); else return -1; KMALLOCS(rdr_rules, ipnat_t **, sizeof(ipnat_t *) * ipf_rdrrules_sz); if (rdr_rules != NULL) bzero((char *)rdr_rules, ipf_rdrrules_sz * sizeof(ipnat_t *)); else return -1; KMALLOCS(maptable, hostmap_t **, sizeof(hostmap_t *) * ipf_hostmap_sz); if (maptable != NULL) bzero((char *)maptable, sizeof(hostmap_t *) * ipf_hostmap_sz); else return -1; return 0; } static void nat_addrdr(n) ipnat_t *n; { ipnat_t **np; u_32_t j; u_int hv; int k; k = countbits(n->in_outmsk); if ((k >= 0) && (k != 32)) rdr_masks |= 1 << k; j = (n->in_outip & n->in_outmsk); hv = NAT_HASH_FN(j, 0, ipf_rdrrules_sz); np = rdr_rules + hv; while (*np != NULL) np = &(*np)->in_rnext; n->in_rnext = NULL; n->in_prnext = np; *np = n; } static void nat_addnat(n) ipnat_t *n; { ipnat_t **np; u_32_t j; u_int hv; int k; k = countbits(n->in_inmsk); if ((k >= 0) && (k != 32)) nat_masks |= 1 << k; j = (n->in_inip & n->in_inmsk); hv = NAT_HASH_FN(j, 0, ipf_natrules_sz); np = nat_rules + hv; while (*np != NULL) np = &(*np)->in_mnext; n->in_mnext = NULL; n->in_pmnext = np; *np = n; } static void nat_delrdr(n) ipnat_t *n; { if (n->in_rnext) n->in_rnext->in_prnext = n->in_prnext; *n->in_prnext = n->in_rnext; } static void nat_delnat(n) ipnat_t *n; { if (n->in_mnext) n->in_mnext->in_pmnext = n->in_pmnext; *n->in_pmnext = n->in_mnext; } /* * check if an ip address has already been allocated for a given mapping that * is not doing port based translation. */ static struct hostmap *nat_hostmap(np, real, map) ipnat_t *np; struct in_addr real; struct in_addr map; { hostmap_t *hm; u_int hv; MUTEX_ENTER(&ipf_hostmap); hv = real.s_addr % HOSTMAP_SIZE; for (hm = maptable[hv]; hm; hm = hm->hm_next) if ((hm->hm_realip.s_addr == real.s_addr) && (np == hm->hm_ipnat)) { hm->hm_ref++; MUTEX_EXIT(&ipf_hostmap); return hm; } KMALLOC(hm, hostmap_t *); if (hm) { hm->hm_next = maptable[hv]; hm->hm_pnext = maptable + hv; if (maptable[hv]) maptable[hv]->hm_pnext = &hm->hm_next; maptable[hv] = hm; hm->hm_ipnat = np; hm->hm_realip = real; hm->hm_mapip = map; hm->hm_ref = 1; } MUTEX_EXIT(&ipf_hostmap); return hm; } static void nat_hostmapdel(hm) struct hostmap *hm; { MUTEX_ENTER(&ipf_hostmap); ATOMIC_DEC32(hm->hm_ref); if (hm->hm_ref == 0) { if (hm->hm_next) hm->hm_next->hm_pnext = hm->hm_pnext; *hm->hm_pnext = hm->hm_next; KFREE(hm); } MUTEX_EXIT(&ipf_hostmap); } void fix_outcksum(sp, n , len) u_short *sp; u_32_t n; int len; { register u_short sumshort; register u_32_t sum1; if (!n) return; #if SOLARIS2 >= 6 else if (n & NAT_HW_CKSUM) { *sp = n & 0xffff; return; } #endif sum1 = (~ntohs(*sp)) & 0xffff; 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 , len) u_short *sp; u_32_t n; int len; { register u_short sumshort; register u_32_t sum1; if (!n) return; #if SOLARIS2 >= 6 else if (n & NAT_HW_CKSUM) { *sp = n & 0xffff; return; } #endif #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) #if defined(__NetBSD__) || defined(__OpenBSD__) || (__FreeBSD_version >= 300003) u_long cmd; #else int cmd; #endif caddr_t data; int mode; { register ipnat_t *nat, *nt, *n = NULL, **np = NULL; int error = 0, ret, arg; ipnat_t natd; u_32_t i, j; #if (BSD >= 199306) && defined(_KERNEL) if ((securelevel >= 2) && (mode & FWRITE)) return EPERM; #endif nat = NULL; /* XXX gcc -Wuninitialized */ KMALLOC(nt, ipnat_t *); if ((cmd == SIOCADNAT) || (cmd == SIOCRMNAT)) error = IRCOPYPTR(data, (char *)&natd, sizeof(natd)); else if (cmd == SIOCIPFFL) /* SIOCFLNAT & SIOCCNATL */ error = IRCOPY(data, (char *)&arg, sizeof(arg)); if (error) goto done; /* * For add/delete, look to see if the NAT entry is already present */ WRITE_ENTER(&ipf_nat); if ((cmd == SIOCADNAT) || (cmd == SIOCRMNAT)) { nat = &natd; nat->in_flags &= IPN_USERFLAGS; if ((nat->in_redir & NAT_MAPBLK) == 0) { if ((nat->in_flags & IPN_SPLIT) == 0) nat->in_inip &= nat->in_inmsk; if ((nat->in_flags & IPN_IPRANGE) == 0) nat->in_outip &= nat->in_outmsk; } 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) { #ifdef IPFILTER_LOG case SIOCIPFFB : { int tmp; if (!(mode & FWRITE)) error = EPERM; else { tmp = ipflog_clear(IPL_LOGNAT); IWCOPY((char *)&tmp, (char *)data, sizeof(tmp)); } break; } #endif case SIOCADNAT : if (!(mode & FWRITE)) { error = EPERM; break; } if (n) { error = EEXIST; break; } if (nt == NULL) { error = ENOMEM; break; } n = nt; nt = NULL; bcopy((char *)nat, (char *)n, sizeof(*n)); n->in_ifp = (void *)GETUNIT(n->in_ifname, 4); if (!n->in_ifp) n->in_ifp = (void *)-1; if (n->in_plabel[0] != '\0') { n->in_apr = appr_match(n->in_p, n->in_plabel); if (!n->in_apr) { error = ENOENT; break; } } n->in_next = NULL; *np = n; if (n->in_redir & NAT_REDIRECT) nat_addrdr(n); if (n->in_redir & (NAT_MAP|NAT_MAPBLK)) nat_addnat(n); n->in_use = 0; if (n->in_redir & NAT_MAPBLK) n->in_space = USABLE_PORTS * ~ntohl(n->in_outmsk); else if (n->in_flags & IPN_AUTOPORTMAP) n->in_space = USABLE_PORTS * ~ntohl(n->in_inmsk); else if (n->in_flags & IPN_IPRANGE) n->in_space = ntohl(n->in_outmsk) - ntohl(n->in_outip); else if (n->in_flags & IPN_SPLIT) n->in_space = 2; else n->in_space = ~ntohl(n->in_outmsk); /* * Calculate the number of valid IP addresses in the output * mapping range. In all cases, the range is inclusive of * the start and ending IP addresses. * If to a CIDR address, lose 2: broadcast + network address * (so subtract 1) * If to a range, add one. * If to a single IP address, set to 1. */ if (n->in_space) { if ((n->in_flags & IPN_IPRANGE) != 0) n->in_space += 1; else n->in_space -= 1; } else n->in_space = 1; if ((n->in_outmsk != 0xffffffff) && (n->in_outmsk != 0) && ((n->in_flags & (IPN_IPRANGE|IPN_SPLIT)) == 0)) n->in_nip = ntohl(n->in_outip) + 1; else if ((n->in_flags & IPN_SPLIT) && (n->in_redir & NAT_REDIRECT)) n->in_nip = ntohl(n->in_inip); 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) + 1); /* * Because two different sources can map to * different destinations but use the same * local IP#/port #. * If the result is smaller than in_space, then * we may have wrapped around 32bits. */ i = n->in_inmsk; if ((i != 0) && (i != 0xffffffff)) { j = n->in_space * (~ntohl(i) + 1); if (j >= n->in_space) n->in_space = j; else n->in_space = 0xffffffff; } } /* * If no protocol is specified, multiple by 256. */ if ((n->in_flags & IPN_TCPUDP) == 0) { j = n->in_space * 256; if (j >= n->in_space) n->in_space = j; else n->in_space = 0xffffffff; } } /* Otherwise, these fields are preset */ n = NULL; nat_stats.ns_rules++; break; case SIOCRMNAT : if (!(mode & FWRITE)) { error = EPERM; n = NULL; break; } if (!n) { error = ESRCH; break; } if (n->in_redir & NAT_REDIRECT) nat_delrdr(n); if (n->in_redir & (NAT_MAPBLK|NAT_MAP)) nat_delnat(n); if (nat_list == NULL) { nat_masks = 0; rdr_masks = 0; } *np = n->in_next; if (!n->in_use) { if (n->in_apr) appr_free(n->in_apr); KFREE(n); nat_stats.ns_rules--; } else { n->in_flags |= IPN_DELETE; n->in_next = NULL; } n = NULL; break; case SIOCGNATS : MUTEX_DOWNGRADE(&ipf_nat); 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_nattab_sz = ipf_nattable_sz; nat_stats.ns_rultab_sz = ipf_natrules_sz; nat_stats.ns_rdrtab_sz = ipf_rdrrules_sz; nat_stats.ns_instances = nat_instances; nat_stats.ns_apslist = ap_sess_list; error = IWCOPYPTR((char *)&nat_stats, (char *)data, sizeof(nat_stats)); break; case SIOCGNATL : { natlookup_t nl; MUTEX_DOWNGRADE(&ipf_nat); error = IRCOPYPTR((char *)data, (char *)&nl, sizeof(nl)); if (error) break; if (nat_lookupredir(&nl)) { error = IWCOPYPTR((char *)&nl, (char *)data, sizeof(nl)); } else error = ESRCH; break; } case SIOCIPFFL : /* old SIOCFLNAT & SIOCCNATL */ if (!(mode & FWRITE)) { error = EPERM; break; } error = 0; if (arg == 0) ret = nat_flushtable(); else if (arg == 1) ret = nat_clearlist(); else error = EINVAL; MUTEX_DOWNGRADE(&ipf_nat); if (!error) { error = IWCOPY((caddr_t)&ret, data, sizeof(ret)); if (error) error = EFAULT; } break; case SIOCSTLCK : error = IRCOPY(data, (caddr_t)&arg, sizeof(arg)); if (!error) { error = IWCOPY((caddr_t)&fr_nat_lock, data, sizeof(fr_nat_lock)); if (!error) fr_nat_lock = arg; } break; case SIOCSTPUT : if (fr_nat_lock) error = fr_natputent(data); else error = EACCES; break; case SIOCSTGSZ : if (fr_nat_lock) error = fr_natgetsz(data); else error = EACCES; break; case SIOCSTGET : if (fr_nat_lock) error = fr_natgetent(data); else error = EACCES; break; case FIONREAD : #ifdef IPFILTER_LOG MUTEX_DOWNGRADE(&ipf_nat); error = IWCOPY((caddr_t)&iplused[IPL_LOGNAT], (caddr_t)data, sizeof(iplused[IPL_LOGNAT])); #endif break; default : error = EINVAL; break; } RWLOCK_EXIT(&ipf_nat); /* READ/WRITE */ done: if (nt) KFREE(nt); return error; } static int fr_natgetsz(data) caddr_t data; { ap_session_t *aps; nat_t *nat, *n; int error = 0; natget_t ng; error = IRCOPY(data, (caddr_t)&ng, sizeof(ng)); if (error) return EFAULT; nat = ng.ng_ptr; if (!nat) { nat = nat_instances; ng.ng_sz = 0; if (nat == NULL) { error = IWCOPY((caddr_t)&ng, data, sizeof(ng)); if (error) error = EFAULT; return error; } } else { /* * Make sure the pointer we're copying from exists in the * current list of entries. Security precaution to prevent * copying of random kernel data. */ for (n = nat_instances; n; n = n->nat_next) if (n == nat) break; if (!n) return ESRCH; } ng.ng_sz = sizeof(nat_save_t); aps = nat->nat_aps; if ((aps != NULL) && (aps->aps_data != 0)) { ng.ng_sz += sizeof(ap_session_t); ng.ng_sz += aps->aps_psiz; } error = IWCOPY((caddr_t)&ng, data, sizeof(ng)); if (error) error = EFAULT; return error; } static int fr_natgetent(data) caddr_t data; { nat_save_t ipn, *ipnp, *ipnn; register nat_t *n, *nat; ap_session_t *aps; int error; error = IRCOPY(data, (caddr_t)&ipnp, sizeof(ipnp)); if (error) return EFAULT; error = IRCOPY((caddr_t)ipnp, (caddr_t)&ipn, sizeof(ipn)); if (error) return EFAULT; nat = ipn.ipn_next; if (!nat) { nat = nat_instances; if (nat == NULL) { if (nat_instances == NULL) return ENOENT; return 0; } } else { /* * Make sure the pointer we're copying from exists in the * current list of entries. Security precaution to prevent * copying of random kernel data. */ for (n = nat_instances; n; n = n->nat_next) if (n == nat) break; if (!n) return ESRCH; } ipn.ipn_next = nat->nat_next; ipn.ipn_dsize = 0; bcopy((char *)nat, (char *)&ipn.ipn_nat, sizeof(ipn.ipn_nat)); ipn.ipn_nat.nat_data = NULL; if (nat->nat_ptr) { bcopy((char *)nat->nat_ptr, (char *)&ipn.ipn_ipnat, sizeof(ipn.ipn_ipnat)); } if (nat->nat_fr) bcopy((char *)nat->nat_fr, (char *)&ipn.ipn_rule, sizeof(ipn.ipn_rule)); if ((aps = nat->nat_aps)) { ipn.ipn_dsize = sizeof(*aps); if (aps->aps_data) ipn.ipn_dsize += aps->aps_psiz; KMALLOCS(ipnn, nat_save_t *, sizeof(*ipnn) + ipn.ipn_dsize); if (ipnn == NULL) return NULL; bcopy((char *)&ipn, (char *)ipnn, sizeof(ipn)); bcopy((char *)aps, ipn.ipn_data, sizeof(*aps)); if (aps->aps_data) { bcopy(aps->aps_data, ipn.ipn_data + sizeof(*aps), aps->aps_psiz); ipn.ipn_dsize += aps->aps_psiz; } error = IWCOPY((caddr_t)ipnn, ipnp, sizeof(ipn) + ipn.ipn_dsize); if (error) return EFAULT; KFREES(ipnn, sizeof(*ipnn) + ipn.ipn_dsize); } else { error = IWCOPY((caddr_t)&ipn, ipnp, sizeof(ipn)); if (error) return EFAULT; } return 0; } static int fr_natputent(data) caddr_t data; { nat_save_t ipn, *ipnp, *ipnn; register nat_t *n, *nat; ap_session_t *aps; frentry_t *fr; ipnat_t *in; int error; error = IRCOPY(data, (caddr_t)&ipnp, sizeof(ipnp)); if (error) return EFAULT; error = IRCOPY((caddr_t)ipnp, (caddr_t)&ipn, sizeof(ipn)); if (error) return EFAULT; if (ipn.ipn_dsize) { KMALLOCS(ipnn, nat_save_t *, sizeof(ipn) + ipn.ipn_dsize); if (ipnn == NULL) return ENOMEM; bcopy((char *)&ipn, (char *)ipnn, sizeof(ipn)); error = IRCOPY((caddr_t)ipnp, (caddr_t)ipn.ipn_data, ipn.ipn_dsize); if (error) return EFAULT; } else ipnn = NULL; KMALLOC(nat, nat_t *); if (nat == NULL) return ENOMEM; bcopy((char *)&ipn.ipn_nat, (char *)nat, sizeof(*nat)); /* * Initialize all these so that nat_delete() doesn't cause a crash. */ nat->nat_hstart[0] = NULL; nat->nat_hstart[1] = NULL; fr = nat->nat_fr; nat->nat_fr = NULL; aps = nat->nat_aps; nat->nat_aps = NULL; in = nat->nat_ptr; nat->nat_ptr = NULL; nat->nat_data = NULL; /* * Restore the rule associated with this nat session */ if (in) { KMALLOC(in, ipnat_t *); if (in == NULL) { error = ENOMEM; goto junkput; } nat->nat_ptr = in; bcopy((char *)&ipn.ipn_ipnat, (char *)in, sizeof(*in)); in->in_use = 1; in->in_flags |= IPN_DELETE; in->in_next = NULL; in->in_rnext = NULL; in->in_prnext = NULL; in->in_mnext = NULL; in->in_pmnext = NULL; in->in_ifp = GETUNIT(in->in_ifname, 4); if (in->in_plabel[0] != '\0') { in->in_apr = appr_match(in->in_p, in->in_plabel); } } /* * Restore ap_session_t structure. Include the private data allocated * if it was there. */ if (aps) { KMALLOC(aps, ap_session_t *); if (aps == NULL) { error = ENOMEM; goto junkput; } nat->nat_aps = aps; aps->aps_next = ap_sess_list; ap_sess_list = aps; bcopy(ipnn->ipn_data, (char *)aps, sizeof(*aps)); if (in) aps->aps_apr = in->in_apr; if (aps->aps_psiz) { KMALLOCS(aps->aps_data, void *, aps->aps_psiz); if (aps->aps_data == NULL) { error = ENOMEM; goto junkput; } bcopy(ipnn->ipn_data + sizeof(*aps), aps->aps_data, aps->aps_psiz); } else { aps->aps_psiz = 0; aps->aps_data = NULL; } } /* * If there was a filtering rule associated with this entry then * build up a new one. */ if (fr != NULL) { if (nat->nat_flags & FI_NEWFR) { KMALLOC(fr, frentry_t *); nat->nat_fr = fr; if (fr == NULL) { error = ENOMEM; goto junkput; } bcopy((char *)&ipn.ipn_fr, (char *)fr, sizeof(*fr)); ipn.ipn_nat.nat_fr = fr; error = IWCOPY((caddr_t)&ipn, ipnp, sizeof(ipn)); if (error) { error = EFAULT; goto junkput; } } else { for (n = nat_instances; n; n = n->nat_next) if (n->nat_fr == fr) break; if (!n) { error = ESRCH; goto junkput; } } } if (ipnn) KFREES(ipnn, sizeof(ipn) + ipn.ipn_dsize); nat_insert(nat); return 0; junkput: if (ipnn) KFREES(ipnn, sizeof(ipn) + ipn.ipn_dsize); if (nat) nat_delete(nat); return error; } /* * Delete a nat entry from the various lists and table. */ static void nat_delete(natd) struct nat *natd; { register struct nat **natp, *nat; struct ipnat *ipn; for (natp = natd->nat_hstart[0]; natp && (nat = *natp); natp = &nat->nat_hnext[0]) if (nat == natd) { *natp = nat->nat_hnext[0]; break; } for (natp = natd->nat_hstart[1]; natp && (nat = *natp); natp = &nat->nat_hnext[1]) if (nat == natd) { *natp = nat->nat_hnext[1]; break; } if (natd->nat_fr != NULL) { ATOMIC_DEC32(natd->nat_fr->fr_ref); } if (natd->nat_hm != NULL) nat_hostmapdel(natd->nat_hm); /* * If there is an active reference from the nat entry to its parent * rule, decrement the rule's reference count and free it too if no * longer being used. */ ipn = natd->nat_ptr; if (ipn != NULL) { ipn->in_space++; ipn->in_use--; if (!ipn->in_use && (ipn->in_flags & IPN_DELETE)) { if (ipn->in_apr) appr_free(ipn->in_apr); KFREE(ipn); nat_stats.ns_rules--; } } MUTEX_DESTROY(&natd->nat_lock); /* * If there's a fragment table entry too for this nat entry, then * dereference that as well. */ ipfr_forget((void *)natd); aps_free(natd->nat_aps); nat_stats.ns_inuse--; KFREE(natd); } /* * nat_flushtable - clear the NAT table of all mapping entries. */ static int nat_flushtable() { register nat_t *nat, **natp; register int j = 0; /* * ALL NAT mappings deleted, so lets just make the deletions * quicker. */ if (nat_table[0] != NULL) bzero((char *)nat_table[0], sizeof(nat_table[0]) * ipf_nattable_sz); if (nat_table[1] != NULL) bzero((char *)nat_table[1], sizeof(nat_table[1]) * ipf_nattable_sz); for (natp = &nat_instances; (nat = *natp); ) { *natp = nat->nat_next; nat_delete(nat); j++; } nat_stats.ns_inuse = 0; return j; } /* * nat_clearlist - delete all rules in the active NAT mapping list. */ static int nat_clearlist() { register ipnat_t *n, **np = &nat_list; int i = 0; if (nat_rules != NULL) bzero((char *)nat_rules, sizeof(*nat_rules) * ipf_natrules_sz); if (rdr_rules != NULL) bzero((char *)rdr_rules, sizeof(*rdr_rules) * ipf_rdrrules_sz); while ((n = *np)) { *np = n->in_next; if (!n->in_use) { if (n->in_apr) appr_free(n->in_apr); KFREE(n); nat_stats.ns_rules--; } else { n->in_flags |= IPN_DELETE; n->in_next = NULL; } i++; } nat_masks = 0; rdr_masks = 0; return i; } /* * Create a new NAT table entry. * NOTE: assumes write lock on ipf_nat has been obtained already. */ nat_t *nat_new(np, ip, fin, flags, direction) ipnat_t *np; ip_t *ip; fr_info_t *fin; u_int flags; int direction; { register u_32_t sum1, sum2, sumd, l; u_short port = 0, sport = 0, dport = 0, nport = 0; struct in_addr in, inb; tcphdr_t *tcp = NULL; hostmap_t *hm = NULL; nat_t *nat, *natl; u_short nflags; #if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) qif_t *qf = fin->fin_qif; #endif 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 */ KMALLOC(nat, nat_t *); if (nat == NULL) { nat_stats.ns_memfail++; return NULL; } bzero((char *)nat, sizeof(*nat)); nat->nat_flags = flags; /* * Search the current table for a match. */ if (direction == NAT_OUTBOUND) { /* * Values at which the search for a free resouce starts. */ u_32_t st_ip; u_short st_port; /* * If it's an outbound packet which doesn't match any existing * record, then create a new port */ l = 0; st_ip = np->in_nip; st_port = np->in_pnext; do { port = 0; in.s_addr = htonl(np->in_nip); if (l == 0) { /* * Check to see if there is an existing NAT * setup for this IP address pair. */ hm = nat_hostmap(np, ip->ip_src, in); if (hm != NULL) in.s_addr = hm->hm_mapip.s_addr; } else if ((l == 1) && (hm != NULL)) { nat_hostmapdel(hm); hm = NULL; } in.s_addr = ntohl(in.s_addr); nat->nat_hm = hm; if ((np->in_outmsk == 0xffffffff) && (np->in_pnext == 0)) { if (l > 0) goto badnat; } if (np->in_redir & NAT_MAPBLK) { if ((l >= np->in_ppip) || ((l > 0) && !(flags & IPN_TCPUDP))) goto badnat; /* * map-block - Calculate destination address. */ in.s_addr = ntohl(ip->ip_src.s_addr); in.s_addr &= ntohl(~np->in_inmsk); inb.s_addr = in.s_addr; in.s_addr /= np->in_ippip; in.s_addr &= ntohl(~np->in_outmsk); in.s_addr += ntohl(np->in_outip); /* * Calculate destination port. */ if ((flags & IPN_TCPUDP) && (np->in_ppip != 0)) { port = ntohs(sport) + l; port %= np->in_ppip; port += np->in_ppip * (inb.s_addr % np->in_ippip); port += MAPBLK_MINPORT; port = htons(port); } } else if (!np->in_outip && (np->in_outmsk == 0xffffffff)) { /* * 0/32 - use the interface's IP address. */ if ((l > 0) || fr_ifpaddr(4, fin->fin_ifp, &in) == -1) goto badnat; in.s_addr = ntohl(in.s_addr); } else if (!np->in_outip && !np->in_outmsk) { /* * 0/0 - use the original source address/port. */ if (l > 0) goto badnat; in.s_addr = ntohl(ip->ip_src.s_addr); } else if ((np->in_outmsk != 0xffffffff) && (np->in_pnext == 0) && ((l > 0) || (hm == NULL))) np->in_nip++; natl = NULL; if ((nflags & IPN_TCPUDP) && ((np->in_redir & NAT_MAPBLK) == 0) && (np->in_flags & IPN_AUTOPORTMAP)) { if ((l > 0) && (l % np->in_ppip == 0)) { if (l > np->in_space) { goto badnat; } else if ((l > np->in_ppip) && np->in_outmsk != 0xffffffff) np->in_nip++; } if (np->in_ppip != 0) { port = ntohs(sport); port += (l % np->in_ppip); port %= np->in_ppip; port += np->in_ppip * (ntohl(ip->ip_src.s_addr) % np->in_ippip); port += MAPBLK_MINPORT; port = htons(port); } } else if (((np->in_redir & NAT_MAPBLK) == 0) && (nflags & IPN_TCPUDP) && (np->in_pnext != 0)) { port = htons(np->in_pnext++); if (np->in_pnext > ntohs(np->in_pmax)) { np->in_pnext = ntohs(np->in_pmin); if (np->in_outmsk != 0xffffffff) np->in_nip++; } } if (np->in_flags & IPN_IPRANGE) { if (np->in_nip > ntohl(np->in_outmsk)) np->in_nip = ntohl(np->in_outip); } else { if ((np->in_outmsk != 0xffffffff) && ((np->in_nip + 1) & ntohl(np->in_outmsk)) > ntohl(np->in_outip)) np->in_nip = ntohl(np->in_outip) + 1; } if (!port && (flags & IPN_TCPUDP)) port = sport; /* * Here we do a lookup of the connection as seen from * the outside. If an IP# pair already exists, try * again. So if you have A->B becomes C->B, you can * also have D->E become C->E but not D->B causing * another C->B. Also take protocol and ports into * account when determining whether a pre-existing * NAT setup will cause an external conflict where * this is appropriate. */ inb.s_addr = htonl(in.s_addr); natl = nat_inlookup(fin->fin_ifp, flags & ~FI_WILDP, (u_int)ip->ip_p, ip->ip_dst, inb, (port << 16) | dport); /* * Has the search wrapped around and come back to the * start ? */ if ((natl != NULL) && (np->in_pnext != 0) && (st_port == np->in_pnext) && (np->in_nip != 0) && (st_ip == np->in_nip)) goto badnat; l++; } while (natl != NULL); if (np->in_space > 0) np->in_space--; /* 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; if (nat->nat_hm == NULL) nat->nat_hm = nat_hostmap(np, ip->ip_src, nat->nat_outip); sum1 = LONG_SUM(ntohl(ip->ip_src.s_addr)) + ntohs(sport); sum2 = LONG_SUM(in.s_addr) + ntohs(port); if (flags & IPN_TCPUDP) { nat->nat_inport = sport; nat->nat_outport = port; /* sport */ 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. */ if (np->in_flags & IPN_SPLIT) { in.s_addr = np->in_nip; if (np->in_inip == htonl(in.s_addr)) np->in_nip = ntohl(np->in_inmsk); else { np->in_nip = ntohl(np->in_inip); if (np->in_flags & IPN_ROUNDR) { nat_delrdr(np); nat_addrdr(np); } } } else { in.s_addr = ntohl(np->in_inip); if (np->in_flags & IPN_ROUNDR) { nat_delrdr(np); nat_addrdr(np); } } if (!np->in_pnext) nport = dport; else { /* * Whilst not optimized for the case where * pmin == pmax, the gain is not significant. */ nport = ntohs(dport) - ntohs(np->in_pmin) + ntohs(np->in_pnext); nport = htons(nport); } /* * When the redirect-to address is set to 0.0.0.0, just * assume a blank `forwarding' of the packet. We don't * setup any translation for this either. */ if (in.s_addr == 0) { if (nport == dport) goto badnat; in.s_addr = ntohl(ip->ip_dst.s_addr); } nat->nat_inip.s_addr = htonl(in.s_addr); nat->nat_outip = ip->ip_dst; nat->nat_oip = ip->ip_src; sum1 = LONG_SUM(ntohl(ip->ip_dst.s_addr)) + ntohs(dport); sum2 = LONG_SUM(in.s_addr) + ntohs(nport); if (flags & IPN_TCPUDP) { nat->nat_inport = nport; nat->nat_outport = dport; nat->nat_oport = sport; } } CALC_SUMD(sum1, sum2, sumd); nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); #if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) if ((flags == IPN_TCP) && dohwcksum && (qf->qf_ill->ill_ick.ick_magic == ICK_M_CTL_MAGIC)) { if (direction == NAT_OUTBOUND) sum1 = LONG_SUM(ntohl(in.s_addr)); else sum1 = LONG_SUM(ntohl(ip->ip_src.s_addr)); sum1 += LONG_SUM(ntohl(ip->ip_dst.s_addr)); sum1 += 30; sum1 = (sum1 & 0xffff) + (sum1 >> 16); nat->nat_sumd[1] = NAT_HW_CKSUM|(sum1 & 0xffff); } else #endif nat->nat_sumd[1] = nat->nat_sumd[0]; if ((flags & IPN_TCPUDP) && ((sport != port) || (dport != nport))) { if (direction == NAT_OUTBOUND) sum1 = LONG_SUM(ntohl(ip->ip_src.s_addr)); else sum1 = LONG_SUM(ntohl(ip->ip_dst.s_addr)); sum2 = LONG_SUM(in.s_addr); CALC_SUMD(sum1, sum2, sumd); nat->nat_ipsumd = (sumd & 0xffff) + (sumd >> 16); } else nat->nat_ipsumd = nat->nat_sumd[0]; in.s_addr = htonl(in.s_addr); #ifdef _KERNEL strncpy(nat->nat_ifname, IFNAME(fin->fin_ifp), IFNAMSIZ); #endif nat_insert(nat); nat->nat_dir = direction; nat->nat_ifp = fin->fin_ifp; nat->nat_ptr = np; nat->nat_p = ip->ip_p; nat->nat_bytes = 0; nat->nat_pkts = 0; nat->nat_fr = fin->fin_fr; if (nat->nat_fr != NULL) { ATOMIC_INC32(nat->nat_fr->fr_ref); } if (direction == NAT_OUTBOUND) { if (flags & IPN_TCPUDP) tcp->th_sport = port; } else { if (flags & IPN_TCPUDP) tcp->th_dport = nport; } np->in_use++; return nat; badnat: nat_stats.ns_badnat++; if ((hm = nat->nat_hm) != NULL) nat_hostmapdel(hm); KFREE(nat); return NULL; } void nat_insert(nat) nat_t *nat; { nat_t **natp; u_int hv; MUTEX_INIT(&nat->nat_lock, "nat entry lock", NULL); nat->nat_age = fr_defnatage; nat->nat_ifname[sizeof(nat->nat_ifname) - 1] = '\0'; if (nat->nat_ifname[0] !='\0') { nat->nat_ifp = GETUNIT(nat->nat_ifname, 4); } nat->nat_next = nat_instances; nat_instances = nat; hv = NAT_HASH_FN(nat->nat_inip.s_addr, nat->nat_inport, ipf_nattable_sz); natp = &nat_table[0][hv]; nat->nat_hstart[0] = natp; nat->nat_hnext[0] = *natp; *natp = nat; hv = NAT_HASH_FN(nat->nat_outip.s_addr, nat->nat_outport, ipf_nattable_sz); natp = &nat_table[1][hv]; nat->nat_hstart[1] = natp; nat->nat_hnext[1] = *natp; *natp = nat; nat_stats.ns_added++; nat_stats.ns_inuse++; } nat_t *nat_icmplookup(ip, fin, dir) ip_t *ip; fr_info_t *fin; int dir; { icmphdr_t *icmp; tcphdr_t *tcp = NULL; ip_t *oip; int flags = 0, type; icmp = (icmphdr_t *)fin->fin_dp; /* * Does it at least have the return (basic) IP header ? * Only a basic IP header (no options) should be with an ICMP error * header. */ if ((ip->ip_hl != 5) || (ip->ip_len < ICMPERR_MINPKTLEN)) return NULL; type = icmp->icmp_type; /* * If it's not an error type, then return. */ if ((type != ICMP_UNREACH) && (type != ICMP_SOURCEQUENCH) && (type != ICMP_REDIRECT) && (type != ICMP_TIMXCEED) && (type != ICMP_PARAMPROB)) return NULL; oip = (ip_t *)((char *)fin->fin_dp + 8); if (ip->ip_len < ICMPERR_MAXPKTLEN + ((oip->ip_hl - 5) << 2)) return NULL; if (oip->ip_p == IPPROTO_TCP) flags = IPN_TCP; else if (oip->ip_p == IPPROTO_UDP) flags = IPN_UDP; if (flags & IPN_TCPUDP) { tcp = (tcphdr_t *)((char *)oip + (oip->ip_hl << 2)); if (dir == NAT_INBOUND) return nat_inlookup(fin->fin_ifp, flags, (u_int)oip->ip_p, oip->ip_dst, oip->ip_src, (tcp->th_sport << 16) | tcp->th_dport); else return nat_outlookup(fin->fin_ifp, flags, (u_int)oip->ip_p, oip->ip_dst, oip->ip_src, (tcp->th_sport << 16) | tcp->th_dport); } if (dir == NAT_INBOUND) return nat_inlookup(fin->fin_ifp, 0, (u_int)oip->ip_p, oip->ip_dst, oip->ip_src, 0); else return nat_outlookup(fin->fin_ifp, 0, (u_int)oip->ip_p, oip->ip_dst, oip->ip_src, 0); } /* * This should *ONLY* be used for incoming packets to make sure a NAT'd ICMP * packet gets correctly recognised. */ nat_t *nat_icmp(ip, fin, nflags, dir) ip_t *ip; fr_info_t *fin; u_int *nflags; int dir; { u_32_t sum1, sum2, sumd; struct in_addr in; icmphdr_t *icmp; nat_t *nat; ip_t *oip; int flags = 0; if ((ip->ip_v != 4) || !(nat = nat_icmplookup(ip, fin, dir))) return NULL; *nflags = IPN_ICMPERR; icmp = (icmphdr_t *)fin->fin_dp; oip = (ip_t *)&icmp->icmp_ip; if (oip->ip_p == IPPROTO_TCP) flags = IPN_TCP; else if (oip->ip_p == IPPROTO_UDP) flags = IPN_UDP; /* * Need to adjust ICMP header to include the real IP#'s and * port #'s. Only apply a checksum change relative to the * IP address change is it will be modified again in ip_natout * for both address and port. Two checksum changes are * necessary for the two header address changes. Be careful * to only modify the checksum once for the port # and twice * for the IP#. */ if (nat->nat_dir == NAT_OUTBOUND) { sum1 = LONG_SUM(ntohl(oip->ip_src.s_addr)); in = nat->nat_inip; oip->ip_src = in; } else { sum1 = LONG_SUM(ntohl(oip->ip_dst.s_addr)); in = nat->nat_outip; oip->ip_dst = in; } sum2 = LONG_SUM(ntohl(in.s_addr)); CALC_SUMD(sum1, sum2, sumd); if (nat->nat_dir == NAT_OUTBOUND) { fix_incksum(&oip->ip_sum, sumd, 0); sumd += (sumd & 0xffff); while (sumd > 0xffff) sumd = (sumd & 0xffff) + (sumd >> 16); fix_outcksum(&icmp->icmp_cksum, sumd, 0); } else { fix_outcksum(&oip->ip_sum, sumd, 0); sumd += (sumd & 0xffff); while (sumd > 0xffff) sumd = (sumd & 0xffff) + (sumd >> 16); /* fix_incksum(&icmp->icmp_cksum, sumd, 0); */ } if ((flags & IPN_TCPUDP) != 0) { tcphdr_t *tcp; /* XXX - what if this is bogus hl and we go off the end ? */ tcp = (tcphdr_t *)((((char *)oip) + (oip->ip_hl << 2))); if (nat->nat_dir == NAT_OUTBOUND) { if (tcp->th_sport != nat->nat_inport) { sum1 = ntohs(tcp->th_sport); sum2 = ntohs(nat->nat_inport); CALC_SUMD(sum1, sum2, sumd); tcp->th_sport = nat->nat_inport; fix_outcksum(&icmp->icmp_cksum, sumd, 0); } } else { if (tcp->th_dport != nat->nat_outport) { sum1 = ntohs(tcp->th_dport); sum2 = ntohs(nat->nat_outport); CALC_SUMD(sum1, sum2, sumd); tcp->th_dport = nat->nat_outport; fix_incksum(&icmp->icmp_cksum, sumd, 0); } } } nat->nat_age = fr_defnaticmpage; 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(ifp, flags, p, src, mapdst, ports) void *ifp; register u_int flags, p; struct in_addr src , mapdst; u_32_t ports; { register u_short sport, mapdport; register nat_t *nat; register int nflags; u_int hv; mapdport = ports >> 16; sport = ports & 0xffff; flags &= IPN_TCPUDP; hv = NAT_HASH_FN(mapdst.s_addr, mapdport, ipf_nattable_sz); nat = nat_table[1][hv]; for (; nat; nat = nat->nat_hnext[1]) { nflags = nat->nat_flags; if ((!ifp || ifp == nat->nat_ifp) && nat->nat_oip.s_addr == src.s_addr && nat->nat_outip.s_addr == mapdst.s_addr && (((p == 0) && (flags == (nat->nat_flags & IPN_TCPUDP))) || (p == nat->nat_p)) && (!flags || (((nat->nat_oport == sport) || (nflags & FI_W_DPORT)) && ((nat->nat_outport == mapdport) || (nflags & FI_W_SPORT))))) 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(ifp, flags, p, src, dst, ports) void *ifp; register u_int flags, p; struct in_addr src , dst; u_32_t ports; { register u_short sport, dport; register nat_t *nat; register int nflags; u_int hv; sport = ports & 0xffff; dport = ports >> 16; flags &= IPN_TCPUDP; hv = NAT_HASH_FN(src.s_addr, sport, ipf_nattable_sz); nat = nat_table[0][hv]; for (; nat; nat = nat->nat_hnext[0]) { nflags = nat->nat_flags; if ((!ifp || ifp == nat->nat_ifp) && nat->nat_inip.s_addr == src.s_addr && nat->nat_oip.s_addr == dst.s_addr && (((p == 0) && (flags == (nat->nat_flags & IPN_TCPUDP))) || (p == nat->nat_p)) && (!flags || ((nat->nat_inport == sport || nflags & FI_W_SPORT) && (nat->nat_oport == dport || nflags & FI_W_DPORT)))) return nat; } return NULL; } /* * Lookup the NAT tables to search for a matching redirect */ nat_t *nat_lookupredir(np) register natlookup_t *np; { u_32_t ports; nat_t *nat; ports = (np->nl_outport << 16) | np->nl_inport; /* * 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(NULL, np->nl_flags, 0, np->nl_inip, np->nl_outip, ports))) { np->nl_realip = nat->nat_outip; np->nl_realport = nat->nat_outport; } return nat; } static int nat_match(fin, np, ip) fr_info_t *fin; ipnat_t *np; ip_t *ip; { frtuc_t *ft; if (ip->ip_v != 4) return 0; if (np->in_p && ip->ip_p != np->in_p) return 0; if (fin->fin_out) { if (!(np->in_redir && (NAT_MAP|NAT_MAPBLK))) return 0; if ((fin->fin_fi.fi_saddr & np->in_inmsk) != np->in_inip) return 0; if ((fin->fin_fi.fi_daddr & np->in_srcmsk) != np->in_srcip) return 0; } else { if (!(np->in_redir && NAT_REDIRECT)) return 0; } ft = &np->in_tuc; if (!(fin->fin_fi.fi_fl & FI_TCPUDP)) { if (ft->ftu_scmp || ft->ftu_dcmp) return 0; return 1; } return fr_tcpudpchk(ft, fin); } /* * Packets going out on the external interface go through this. * Here, the source address requires alteration, if anything. */ int ip_natout(ip, fin) ip_t *ip; fr_info_t *fin; { register ipnat_t *np = NULL; register u_32_t ipa; tcphdr_t *tcp = NULL; u_short sport = 0, dport = 0, *csump = NULL; struct ifnet *ifp; int natadd = 1; frentry_t *fr; u_int nflags = 0, hv, msk; u_32_t iph; nat_t *nat; int i; if (nat_list == NULL || (fr_nat_lock)) return 0; 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 & IP_OFFMASK) && !(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 & IPN_TCPUDP)) { tcp = (tcphdr_t *)fin->fin_dp; sport = tcp->th_sport; dport = tcp->th_dport; } } ipa = ip->ip_src.s_addr; READ_ENTER(&ipf_nat); if ((ip->ip_p == IPPROTO_ICMP) && (nat = nat_icmp(ip, fin, &nflags, NAT_OUTBOUND))) ; else if ((ip->ip_off & (IP_OFFMASK|IP_MF)) && (nat = ipfr_nat_knownfrag(ip, fin))) natadd = 0; else if ((nat = nat_outlookup(ifp, nflags, (u_int)ip->ip_p, ip->ip_src, ip->ip_dst, (dport << 16) | sport))) { nflags = nat->nat_flags; if ((nflags & (FI_W_SPORT|FI_W_DPORT)) != 0) { if ((nflags & FI_W_SPORT) && (nat->nat_inport != sport)) nat->nat_inport = sport; else if ((nflags & FI_W_DPORT) && (nat->nat_oport != dport)) nat->nat_oport = dport; if (nat->nat_outport == 0) nat->nat_outport = sport; nat->nat_flags &= ~(FI_W_DPORT|FI_W_SPORT); nflags = nat->nat_flags; } } else { RWLOCK_EXIT(&ipf_nat); WRITE_ENTER(&ipf_nat); /* * If there is no current entry in the nat table for this IP#, * create one for it (if there is a matching rule). */ msk = 0xffffffff; i = 32; maskloop: iph = ipa & htonl(msk); hv = NAT_HASH_FN(iph, 0, ipf_natrules_sz); for (np = nat_rules[hv]; np; np = np->in_mnext) { if ((np->in_ifp && (np->in_ifp != ifp)) || !np->in_space) continue; if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags)) continue; if (np->in_flags & IPN_FILTER) { if (!nat_match(fin, np, ip)) continue; } else if ((ipa & np->in_inmsk) != np->in_inip) continue; if (np->in_redir & (NAT_MAP|NAT_MAPBLK)) { if (*np->in_plabel && !appr_ok(ip, tcp, np)) continue; /* * If it's a redirection, then we don't want to * create new outgoing port stuff. * Redirections are only for incoming * connections. */ if (!(np->in_redir & (NAT_MAP|NAT_MAPBLK))) continue; if ((nat = nat_new(np, ip, fin, (u_int)nflags, NAT_OUTBOUND))) { np->in_hits++; #ifdef IPFILTER_LOG nat_log(nat, (u_int)np->in_redir); #endif break; } } } if ((np == NULL) && (i > 0)) { do { i--; msk <<= 1; } while ((i >= 0) && ((nat_masks & (1 << i)) == 0)); if (i >= 0) goto maskloop; } MUTEX_DOWNGRADE(&ipf_nat); } if (nat) { np = nat->nat_ptr; if (natadd && fin->fin_fi.fi_fl & FI_FRAG) ipfr_nat_newfrag(ip, fin, 0, nat); ip->ip_src = nat->nat_outip; MUTEX_ENTER(&nat->nat_lock); nat->nat_age = fr_defnatage; nat->nat_bytes += ip->ip_len; nat->nat_pkts++; MUTEX_EXIT(&nat->nat_lock); /* * Fix up checksums, not by recalculating them, but * simply computing adjustments. */ #if SOLARIS || defined(__sgi) if (nat->nat_dir == NAT_OUTBOUND) fix_outcksum(&ip->ip_sum, nat->nat_ipsumd, 0); else fix_incksum(&ip->ip_sum, nat->nat_ipsumd, 0); #endif if (!(ip->ip_off & IP_OFFMASK) && !(fin->fin_fi.fi_fl & FI_SHORT)) { if ((nat->nat_outport != 0) && (nflags & IPN_TCPUDP)) { tcp->th_sport = nat->nat_outport; fin->fin_data[0] = ntohs(tcp->th_sport); } if (ip->ip_p == IPPROTO_TCP) { csump = &tcp->th_sum; MUTEX_ENTER(&nat->nat_lock); fr_tcp_age(&nat->nat_age, nat->nat_tcpstate, fin, 1); if (nat->nat_age < fr_defnaticmpage) nat->nat_age = fr_defnaticmpage; #ifdef LARGE_NAT else if (nat->nat_age > fr_defnatage) nat->nat_age = fr_defnatage; #endif /* * Increase this because we may have * "keep state" following this too and * packet storms can occur if this is * removed too quickly. */ if (nat->nat_age == fr_tcpclosed) nat->nat_age = fr_tcplastack; MUTEX_EXIT(&nat->nat_lock); } 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) { nat->nat_age = fr_defnaticmpage; } if (csump) { if (nat->nat_dir == NAT_OUTBOUND) fix_outcksum(csump, nat->nat_sumd[1], ip->ip_len); else fix_incksum(csump, nat->nat_sumd[1], ip->ip_len); } } if ((np->in_apr != NULL) && (np->in_dport == 0 || (tcp != NULL && dport == np->in_dport))) { i = appr_check(ip, fin, nat); if (i == 0) i = 1; } else i = 1; ATOMIC_INCL(nat_stats.ns_mapped[1]); RWLOCK_EXIT(&ipf_nat); /* READ */ return i; } RWLOCK_EXIT(&ipf_nat); /* READ/WRITE */ return 0; } /* * Packets coming in from the external interface go through this. * Here, the destination address requires alteration, if anything. */ int ip_natin(ip, fin) ip_t *ip; fr_info_t *fin; { register struct in_addr src; register struct in_addr in; register ipnat_t *np; u_int nflags = 0, natadd = 1, hv, msk; struct ifnet *ifp = fin->fin_ifp; tcphdr_t *tcp = NULL; u_short sport = 0, dport = 0, *csump = NULL; nat_t *nat; u_32_t iph; int i; if ((nat_list == NULL) || (ip->ip_v != 4) || (fr_nat_lock)) return 0; if (!(ip->ip_off & IP_OFFMASK) && !(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 & IPN_TCPUDP)) { tcp = (tcphdr_t *)fin->fin_dp; dport = tcp->th_dport; sport = tcp->th_sport; } } in = ip->ip_dst; /* make sure the source address is to be redirected */ src = ip->ip_src; READ_ENTER(&ipf_nat); if ((ip->ip_p == IPPROTO_ICMP) && (nat = nat_icmp(ip, fin, &nflags, NAT_INBOUND))) ; else if ((ip->ip_off & IP_OFFMASK) && (nat = ipfr_nat_knownfrag(ip, fin))) natadd = 0; else if ((nat = nat_inlookup(fin->fin_ifp, nflags, (u_int)ip->ip_p, ip->ip_src, in, (dport << 16) | sport))) { nflags = nat->nat_flags; if ((nflags & (FI_W_SPORT|FI_W_DPORT)) != 0) { if ((nat->nat_oport != sport) && (nflags & FI_W_DPORT)) nat->nat_oport = sport; else if ((nat->nat_outport != dport) && (nflags & FI_W_SPORT)) nat->nat_outport = dport; nat->nat_flags &= ~(FI_W_SPORT|FI_W_DPORT); nflags = nat->nat_flags; } } else { RWLOCK_EXIT(&ipf_nat); WRITE_ENTER(&ipf_nat); /* * If there is no current entry in the nat table for this IP#, * create one for it (if there is a matching rule). */ msk = 0xffffffff; i = 32; maskloop: iph = in.s_addr & htonl(msk); hv = NAT_HASH_FN(iph, 0, ipf_rdrrules_sz); for (np = rdr_rules[hv]; np; np = np->in_rnext) { if ((np->in_ifp && (np->in_ifp != ifp)) || (np->in_p && (np->in_p != ip->ip_p)) || (np->in_flags && !(nflags & np->in_flags))) continue; if (np->in_flags & IPN_FILTER) { if (!nat_match(fin, np, ip)) continue; } else if ((in.s_addr & np->in_outmsk) != np->in_outip) continue; if ((np->in_redir & NAT_REDIRECT) && (!np->in_pmin || ((ntohs(np->in_pmax) >= ntohs(dport)) && (ntohs(dport) >= ntohs(np->in_pmin))))) if ((nat = nat_new(np, ip, fin, nflags, NAT_INBOUND))) { np->in_hits++; #ifdef IPFILTER_LOG nat_log(nat, (u_int)np->in_redir); #endif break; } } if ((np == NULL) && (i > 0)) { do { i--; msk <<= 1; } while ((i >= 0) && ((rdr_masks & (1 << i)) == 0)); if (i >= 0) goto maskloop; } MUTEX_DOWNGRADE(&ipf_nat); } if (nat) { np = nat->nat_ptr; fin->fin_fr = nat->nat_fr; if (natadd && fin->fin_fi.fi_fl & FI_FRAG) ipfr_nat_newfrag(ip, fin, 0, nat); if ((np->in_apr != NULL) && (np->in_dport == 0 || (tcp != NULL && sport == np->in_dport))) { i = appr_check(ip, fin, nat); if (i == -1) { RWLOCK_EXIT(&ipf_nat); return i; } } MUTEX_ENTER(&nat->nat_lock); if (nflags != IPN_ICMPERR) nat->nat_age = fr_defnatage; nat->nat_bytes += ip->ip_len; nat->nat_pkts++; MUTEX_EXIT(&nat->nat_lock); ip->ip_dst = nat->nat_inip; fin->fin_fi.fi_daddr = nat->nat_inip.s_addr; /* * Fix up checksums, not by recalculating them, but * simply computing adjustments. */ #if SOLARIS || defined(__sgi) if (nat->nat_dir == NAT_OUTBOUND) fix_incksum(&ip->ip_sum, nat->nat_ipsumd, 0); else fix_outcksum(&ip->ip_sum, nat->nat_ipsumd, 0); #endif if (!(ip->ip_off & IP_OFFMASK) && !(fin->fin_fi.fi_fl & FI_SHORT)) { if ((nat->nat_inport != 0) && (nflags & IPN_TCPUDP)) { tcp->th_dport = nat->nat_inport; fin->fin_data[1] = ntohs(tcp->th_dport); } if (ip->ip_p == IPPROTO_TCP) { csump = &tcp->th_sum; MUTEX_ENTER(&nat->nat_lock); fr_tcp_age(&nat->nat_age, nat->nat_tcpstate, fin, 0); if (nat->nat_age < fr_defnaticmpage) nat->nat_age = fr_defnaticmpage; #ifdef LARGE_NAT else if (nat->nat_age > fr_defnatage) nat->nat_age = fr_defnatage; #endif /* * Increase this because we may have * "keep state" following this too and * packet storms can occur if this is * removed too quickly. */ if (nat->nat_age == fr_tcpclosed) nat->nat_age = fr_tcplastack; MUTEX_EXIT(&nat->nat_lock); } 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) { nat->nat_age = fr_defnaticmpage; } if (csump) { if (nat->nat_dir == NAT_OUTBOUND) fix_incksum(csump, nat->nat_sumd[0], 0); else fix_outcksum(csump, nat->nat_sumd[0], 0); } } ATOMIC_INCL(nat_stats.ns_mapped[0]); RWLOCK_EXIT(&ipf_nat); /* READ */ return 1; } RWLOCK_EXIT(&ipf_nat); /* READ/WRITE */ return 0; } /* * Free all memory used by NAT structures allocated at runtime. */ void ip_natunload() { WRITE_ENTER(&ipf_nat); (void) nat_clearlist(); (void) nat_flushtable(); RWLOCK_EXIT(&ipf_nat); if (nat_table[0] != NULL) { KFREES(nat_table[0], sizeof(nat_t *) * ipf_nattable_sz); nat_table[0] = NULL; } if (nat_table[1] != NULL) { KFREES(nat_table[1], sizeof(nat_t *) * ipf_nattable_sz); nat_table[1] = NULL; } if (nat_rules != NULL) { KFREES(nat_rules, sizeof(ipnat_t *) * ipf_natrules_sz); nat_rules = NULL; } if (rdr_rules != NULL) { KFREES(rdr_rules, sizeof(ipnat_t *) * ipf_rdrrules_sz); rdr_rules = NULL; } if (maptable != NULL) { KFREES(maptable, sizeof(hostmap_t *) * ipf_hostmap_sz); maptable = NULL; } } /* * 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; #if defined(_KERNEL) && !SOLARIS int s; #endif SPL_NET(s); WRITE_ENTER(&ipf_nat); for (natp = &nat_instances; (nat = *natp); ) { nat->nat_age--; if (nat->nat_age) { natp = &nat->nat_next; continue; } *natp = nat->nat_next; #ifdef IPFILTER_LOG nat_log(nat, NL_EXPIRE); #endif nat_delete(nat); nat_stats.ns_expire++; } RWLOCK_EXIT(&ipf_nat); SPL_X(s); } /* */ void ip_natsync(ifp) void *ifp; { register ipnat_t *n; register nat_t *nat; register u_32_t sum1, sum2, sumd; struct in_addr in; ipnat_t *np; void *ifp2; #if defined(_KERNEL) && !SOLARIS int s; #endif /* * Change IP addresses for NAT sessions for any protocol except TCP * since it will break the TCP connection anyway. */ SPL_NET(s); WRITE_ENTER(&ipf_nat); for (nat = nat_instances; nat; nat = nat->nat_next) if (((ifp == NULL) || (ifp == nat->nat_ifp)) && !(nat->nat_flags & IPN_TCP) && (np = nat->nat_ptr) && (np->in_outmsk == 0xffffffff) && !np->in_nip) { ifp2 = nat->nat_ifp; /* * Change the map-to address to be the same as the * new one. */ sum1 = nat->nat_outip.s_addr; if (fr_ifpaddr(4, ifp2, &in) != -1) nat->nat_outip = in; sum2 = nat->nat_outip.s_addr; if (sum1 == sum2) continue; /* * Readjust the checksum adjustment to take into * account the new IP#. */ CALC_SUMD(sum1, sum2, sumd); /* XXX - dont change for TCP when solaris does * hardware checksumming. */ sumd += nat->nat_sumd[0]; nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); nat->nat_sumd[1] = nat->nat_sumd[0]; } for (n = nat_list; (n != NULL); n = n->in_next) if (n->in_ifp == ifp) { n->in_ifp = (void *)GETUNIT(n->in_ifname, 4); if (!n->in_ifp) n->in_ifp = (void *)-1; } RWLOCK_EXIT(&ipf_nat); SPL_X(s); } #ifdef IPFILTER_LOG void nat_log(nat, type) struct nat *nat; u_int type; { struct ipnat *np; struct natlog natl; void *items[1]; size_t sizes[1]; int rulen, types[1]; natl.nl_inip = nat->nat_inip; natl.nl_outip = nat->nat_outip; natl.nl_origip = nat->nat_oip; natl.nl_bytes = nat->nat_bytes; natl.nl_pkts = nat->nat_pkts; natl.nl_origport = nat->nat_oport; natl.nl_inport = nat->nat_inport; natl.nl_outport = nat->nat_outport; natl.nl_p = nat->nat_p; natl.nl_type = type; natl.nl_rule = -1; #ifndef LARGE_NAT if (nat->nat_ptr != NULL) { for (rulen = 0, np = nat_list; np; np = np->in_next, rulen++) if (np == nat->nat_ptr) { natl.nl_rule = rulen; break; } } #endif items[0] = &natl; sizes[0] = sizeof(natl); types[0] = 0; (void) ipllog(IPL_LOGNAT, NULL, items, sizes, types, 1); } #endif Index: head/sys/contrib/ipfilter/netinet/ip_nat.h =================================================================== --- head/sys/contrib/ipfilter/netinet/ip_nat.h (revision 60856) +++ head/sys/contrib/ipfilter/netinet/ip_nat.h (revision 60857) @@ -1,294 +1,295 @@ /* * Copyright (C) 1995-2000 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.1.2.3 2000/01/24 12:44:24 darrenr Exp $ * $FreeBSD$ */ #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', 60, struct ipnat *) #define SIOCRMNAT _IOW('r', 61, struct ipnat *) #define SIOCGNATS _IOWR('r', 62, struct natstat *) #define SIOCGNATL _IOWR('r', 63, struct natlookup *) #else #define SIOCADNAT _IOW(r, 60, struct ipnat *) #define SIOCRMNAT _IOW(r, 61, struct ipnat *) #define SIOCGNATS _IOWR(r, 62, struct natstat *) #define SIOCGNATL _IOWR(r, 63, struct natlookup *) #endif #undef LARGE_NAT /* define this if you're setting up a system to NAT * LARGE numbers of networks/hosts - i.e. in the * hundreds or thousands. In such a case, you should * also change the RDR_SIZE and NAT_SIZE below to more * appropriate sizes. The figures below were used for * a setup with 1000-2000 networks to NAT. */ #define NAT_SIZE 127 #define RDR_SIZE 127 #define HOSTMAP_SIZE 127 #define NAT_TABLE_SZ 127 #ifdef LARGE_NAT #undef NAT_SIZE #undef RDR_SIZE #undef NAT_TABLE_SZ #undef HOSTMAP_SIZE 127 #define NAT_SIZE 2047 #define RDR_SIZE 2047 #define NAT_TABLE_SZ 16383 #define HOSTMAP_SIZE 8191 #endif #ifndef APR_LABELLEN #define APR_LABELLEN 16 #endif #define NAT_HW_CKSUM 0x80000000 #define DEF_NAT_AGE 1200 /* 10 minutes (600 seconds) */ struct ap_session; typedef struct nat { u_long nat_age; int nat_flags; u_32_t nat_sumd[2]; u_32_t nat_ipsumd; void *nat_data; struct ap_session *nat_aps; /* proxy session */ struct frentry *nat_fr; /* filter rule ptr if appropriate */ struct in_addr nat_inip; struct in_addr nat_outip; struct in_addr nat_oip; /* other ip */ U_QUAD_T nat_pkts; U_QUAD_T nat_bytes; u_short nat_oport; /* other port */ u_short nat_inport; u_short nat_outport; u_short nat_use; u_char nat_tcpstate[2]; u_char nat_p; /* protocol for NAT */ struct ipnat *nat_ptr; /* pointer back to the rule */ struct hostmap *nat_hm; struct nat *nat_next; struct nat *nat_hnext[2]; struct nat **nat_hstart[2]; void *nat_ifp; int nat_dir; char nat_ifname[IFNAMSIZ]; #if SOLARIS || defined(_sgi) kmutex_t nat_lock; #endif } nat_t; typedef struct ipnat { struct ipnat *in_next; struct ipnat *in_rnext; struct ipnat **in_prnext; struct ipnat *in_mnext; struct ipnat **in_pmnext; void *in_ifp; void *in_apr; u_long in_space; u_int in_use; u_int in_hits; struct in_addr in_nextip; u_short in_pnext; u_short in_ppip; /* ports per IP */ u_short in_ippip; /* IP #'s per IP# */ u_short in_flags; /* From here to in_dport must be reflected */ u_short in_port[2]; /* correctly in IPN_CMPSIZ */ struct in_addr in_in[2]; struct in_addr in_out[2]; struct in_addr in_src[2]; struct frtuc in_tuc; int in_redir; /* 0 if it's a mapping, 1 if it's a hard redir */ char in_ifname[IFNAMSIZ]; char in_plabel[APR_LABELLEN]; /* proxy label */ char in_p; /* protocol */ } 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 in_srcip in_src[0].s_addr #define in_srcmsk in_src[1].s_addr #define in_scmp in_tuc.ftu_scmp #define in_dcmp in_tuc.ftu_dcmp #define in_stop in_tuc.ftu_stop #define in_dtop in_tuc.ftu_dtop #define in_sport in_tuc.ftu_sport #define in_dport in_tuc.ftu_dport #define NAT_OUTBOUND 0 #define NAT_INBOUND 1 #define NAT_MAP 0x01 #define NAT_REDIRECT 0x02 #define NAT_BIMAP (NAT_MAP|NAT_REDIRECT) #define NAT_MAPBLK 0x04 #define MAPBLK_MINPORT 1024 /* don't use reserved ports for src port */ #define USABLE_PORTS (65536 - MAPBLK_MINPORT) #define IPN_CMPSIZ (sizeof(ipnat_t) - offsetof(ipnat_t, in_flags)) typedef struct natlookup { struct in_addr nl_inip; struct in_addr nl_outip; struct in_addr nl_realip; int nl_flags; u_short nl_inport; u_short nl_outport; u_short nl_realport; } natlookup_t; typedef struct nat_save { void *ipn_next; struct nat ipn_nat; struct ipnat ipn_ipnat; struct frentry ipn_fr; int ipn_dsize; char ipn_data[4]; } nat_save_t; #define ipn_rule ipn_nat.nat_fr typedef struct natget { void *ng_ptr; int ng_sz; } natget_t; typedef struct hostmap { struct hostmap *hm_next; struct hostmap **hm_pnext; struct ipnat *hm_ipnat; struct in_addr hm_realip; struct in_addr hm_mapip; int hm_ref; } hostmap_t; typedef struct natstat { u_long ns_mapped[2]; u_long ns_rules; u_long ns_added; u_long ns_expire; u_long ns_inuse; u_long ns_logged; u_long ns_logfail; u_long ns_memfail; u_long ns_badnat; nat_t **ns_table[2]; ipnat_t *ns_list; void *ns_apslist; u_int ns_nattab_sz; u_int ns_rultab_sz; u_int ns_rdrtab_sz; nat_t *ns_instances; } natstat_t; #define IPN_ANY 0x000 #define IPN_TCP 0x001 #define IPN_UDP 0x002 #define IPN_TCPUDP (IPN_TCP|IPN_UDP) #define IPN_DELETE 0x004 #define IPN_ICMPERR 0x008 #define IPN_RF (IPN_TCPUDP|IPN_DELETE|IPN_ICMPERR) #define IPN_AUTOPORTMAP 0x010 #define IPN_IPRANGE 0x020 #define IPN_USERFLAGS (IPN_TCPUDP|IPN_AUTOPORTMAP|IPN_IPRANGE|\ IPN_SPLIT|IPN_ROUNDR|IPN_FILTER) #define IPN_FILTER 0x040 #define IPN_SPLIT 0x080 #define IPN_ROUNDR 0x100 typedef struct natlog { struct in_addr nl_origip; struct in_addr nl_outip; struct in_addr nl_inip; u_short nl_origport; u_short nl_outport; u_short nl_inport; u_short nl_type; int nl_rule; U_QUAD_T nl_pkts; U_QUAD_T nl_bytes; u_char nl_p; } natlog_t; #define NL_NEWMAP NAT_MAP #define NL_NEWRDR NAT_REDIRECT #define NL_EXPIRE 0xffff #define NAT_HASH_FN(k,l,m) (((k) + ((k) >> 12) + l) % (m)) #define LONG_SUM(in) (((in) & 0xffff) + ((in) >> 16)) #define CALC_SUMD(s1, s2, sd) { \ (s1) = ((s1) & 0xffff) + ((s1) >> 16); \ (s2) = ((s2) & 0xffff) + ((s2) >> 16); \ /* Do it twice */ \ (s1) = ((s1) & 0xffff) + ((s1) >> 16); \ (s2) = ((s2) & 0xffff) + ((s2) >> 16); \ /* Because ~1 == -2, We really need ~1 == -1 */ \ if ((s1) > (s2)) (s2)--; \ (sd) = (s2) - (s1); \ (sd) = ((sd) & 0xffff) + ((sd) >> 16); } extern u_int ipf_nattable_sz; extern u_int ipf_natrules_sz; extern u_int ipf_rdrrules_sz; extern int fr_nat_lock; extern void ip_natsync __P((void *)); extern u_long fr_defnatage; extern u_long fr_defnaticmpage; extern nat_t **nat_table[2]; extern nat_t *nat_instances; extern ipnat_t **nat_rules; extern ipnat_t **rdr_rules; extern natstat_t nat_stats; #if defined(__NetBSD__) || defined(__OpenBSD__) || (__FreeBSD_version >= 300003) extern int nat_ioctl __P((caddr_t, u_long, int)); #else extern int nat_ioctl __P((caddr_t, int, int)); #endif extern int nat_init __P((void)); extern nat_t *nat_new __P((ipnat_t *, ip_t *, fr_info_t *, u_int, int)); extern nat_t *nat_outlookup __P((void *, u_int, u_int, struct in_addr, struct in_addr, u_32_t)); extern nat_t *nat_inlookup __P((void *, u_int, u_int, struct in_addr, struct in_addr, u_32_t)); extern nat_t *nat_maplookup __P((void *, u_int, struct in_addr, struct in_addr)); extern nat_t *nat_lookupredir __P((natlookup_t *)); extern nat_t *nat_icmplookup __P((ip_t *, fr_info_t *, int)); extern nat_t *nat_icmp __P((ip_t *, fr_info_t *, u_int *, int)); extern void nat_insert __P((nat_t *)); +extern int ip_natout __P((ip_t *, fr_info_t *)); extern int ip_natin __P((ip_t *, fr_info_t *)); extern void ip_natunload __P((void)), ip_natexpire __P((void)); extern void nat_log __P((struct nat *, u_int)); extern void fix_incksum __P((u_short *, u_32_t, int)); extern void fix_outcksum __P((u_short *, u_32_t, int)); #endif /* __IP_NAT_H__ */ Index: head/sys/contrib/ipfilter/netinet/ip_raudio_pxy.c =================================================================== --- head/sys/contrib/ipfilter/netinet/ip_raudio_pxy.c (revision 60856) +++ head/sys/contrib/ipfilter/netinet/ip_raudio_pxy.c (revision 60857) @@ -1,303 +1,307 @@ /* * $FreeBSD$ */ #if SOLARIS && defined(_KERNEL) extern kmutex_t ipf_rw; #endif #define IPF_RAUDIO_PROXY int ippr_raudio_init __P((void)); int ippr_raudio_new __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *)); int ippr_raudio_in __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *)); int ippr_raudio_out __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *)); static frentry_t raudiofr; /* * Real Audio application proxy initialization. */ int ippr_raudio_init() { bzero((char *)&raudiofr, sizeof(raudiofr)); raudiofr.fr_ref = 1; raudiofr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; return 0; } /* * Setup for a new proxy to handle Real Audio. */ int ippr_raudio_new(fin, ip, aps, nat) fr_info_t *fin; ip_t *ip; ap_session_t *aps; nat_t *nat; { raudio_t *rap; KMALLOCS(aps->aps_data, void *, sizeof(raudio_t)); - if (aps->aps_data != NULL) { - bzero(aps->aps_data, sizeof(raudio_t)); - rap = aps->aps_data; - aps->aps_psiz = sizeof(raudio_t); - rap->rap_mode = RAP_M_TCP; /* default is for TCP */ - } + if (aps->aps_data == NULL) + return -1; + + bzero(aps->aps_data, sizeof(raudio_t)); + rap = aps->aps_data; + aps->aps_psiz = sizeof(raudio_t); + rap->rap_mode = RAP_M_TCP; /* default is for TCP */ return 0; } int ippr_raudio_out(fin, ip, aps, nat) fr_info_t *fin; ip_t *ip; ap_session_t *aps; nat_t *nat; { raudio_t *rap = aps->aps_data; unsigned char membuf[512 + 1], *s; u_short id = 0; tcphdr_t *tcp; int off, dlen; int len = 0; mb_t *m; #if SOLARIS mb_t *m1; #endif /* * If we've already processed the start messages, then nothing left * for the proxy to do. */ if (rap->rap_eos == 1) return 0; tcp = (tcphdr_t *)fin->fin_dp; off = (ip->ip_hl << 2) + (tcp->th_off << 2); bzero(membuf, sizeof(membuf)); #if SOLARIS m = fin->fin_qfm; dlen = msgdsize(m) - off; if (dlen <= 0) return 0; copyout_mblk(m, off, MIN(sizeof(membuf), dlen), (char *)membuf); #else m = *(mb_t **)fin->fin_mp; dlen = mbufchainlen(m) - off; if (dlen <= 0) return 0; m_copydata(m, off, MIN(sizeof(membuf), dlen), (char *)membuf); #endif /* * In all the startup parsing, ensure that we don't go outside * the packet buffer boundary. */ /* * Look for the start of connection "PNA" string if not seen yet. */ if (rap->rap_seenpna == 0) { s = (u_char *)memstr("PNA", (char *)membuf, 3, dlen); if (s == NULL) return 0; s += 3; rap->rap_seenpna = 1; } else s = membuf; /* * Directly after the PNA will be the version number of this * connection. */ if (rap->rap_seenpna == 1 && rap->rap_seenver == 0) { if ((s + 1) - membuf < dlen) { rap->rap_version = (*s << 8) | *(s + 1); s += 2; rap->rap_seenver = 1; } else return 0; } /* * Now that we've been past the PNA and version number, we're into the * startup messages block. This ends when a message with an ID of 0. */ while ((rap->rap_eos == 0) && ((s + 1) - membuf < dlen)) { if (rap->rap_gotid == 0) { id = (*s << 8) | *(s + 1); s += 2; rap->rap_gotid = 1; if (id == RA_ID_END) { rap->rap_eos = 1; break; } } else if (rap->rap_gotlen == 0) { len = (*s << 8) | *(s + 1); s += 2; rap->rap_gotlen = 1; } if (rap->rap_gotid == 1 && rap->rap_gotlen == 1) { if (id == RA_ID_UDP) { rap->rap_mode &= ~RAP_M_TCP; rap->rap_mode |= RAP_M_UDP; rap->rap_plport = (*s << 8) | *(s + 1); } else if (id == RA_ID_ROBUST) { rap->rap_mode |= RAP_M_ROBUST; rap->rap_prport = (*s << 8) | *(s + 1); } s += len; rap->rap_gotlen = 0; rap->rap_gotid = 0; } } return 0; } int ippr_raudio_in(fin, ip, aps, nat) fr_info_t *fin; ip_t *ip; ap_session_t *aps; nat_t *nat; { unsigned char membuf[IPF_MAXPORTLEN + 1], *s; tcphdr_t *tcp, tcph, *tcp2 = &tcph; raudio_t *rap = aps->aps_data; struct in_addr swa, swb; u_int a1, a2, a3, a4; + int off, dlen, slen; u_short sp, dp; - int off, dlen; fr_info_t fi; tcp_seq seq; nat_t *ipn; u_char swp; mb_t *m; #if SOLARIS mb_t *m1; #endif /* * Wait until we've seen the end of the start messages and even then * only proceed further if we're using UDP. If they want to use TCP * then data is sent back on the same channel that is already open. */ if (rap->rap_sdone != 0) return 0; tcp = (tcphdr_t *)fin->fin_dp; off = (ip->ip_hl << 2) + (tcp->th_off << 2); m = *(mb_t **)fin->fin_mp; #if SOLARIS m = fin->fin_qfm; dlen = msgdsize(m) - off; if (dlen <= 0) return 0; bzero(membuf, sizeof(membuf)); copyout_mblk(m, off, MIN(sizeof(membuf), dlen), (char *)membuf); #else dlen = mbufchainlen(m) - off; if (dlen <= 0) return 0; bzero(membuf, sizeof(membuf)); m_copydata(m, off, MIN(sizeof(membuf), dlen), (char *)membuf); #endif seq = ntohl(tcp->th_seq); /* * Check to see if the data in this packet is of interest to us. * We only care for the first 19 bytes coming back from the server. */ if (rap->rap_sseq == 0) { s = (u_char *)memstr("PNA", (char *)membuf, 3, dlen); if (s == NULL) return 0; a1 = s - membuf; dlen -= a1; a1 = 0; rap->rap_sseq = seq; a2 = MIN(dlen, sizeof(rap->rap_svr)); } else if (seq <= rap->rap_sseq + sizeof(rap->rap_svr)) { /* * seq # which is the start of data and from that the offset * into the buffer array. */ a1 = seq - rap->rap_sseq; a2 = MIN(dlen, sizeof(rap->rap_svr)); a2 -= a1; s = membuf; } else return 0; for (a3 = a1, a4 = a2; (a4 > 0) && (a3 < 19) && (a3 >= 0); a4--,a3++) { rap->rap_sbf |= (1 << a3); rap->rap_svr[a3] = *s++; } if ((rap->rap_sbf != 0x7ffff) || (!rap->rap_eos)) /* 19 bits */ return 0; rap->rap_sdone = 1; s = (u_char *)rap->rap_svr + 11; if (((*s << 8) | *(s + 1)) == RA_ID_ROBUST) { s += 2; rap->rap_srport = (*s << 8) | *(s + 1); } swp = ip->ip_p; swa = ip->ip_src; swb = ip->ip_dst; ip->ip_p = IPPROTO_UDP; ip->ip_src = nat->nat_inip; ip->ip_dst = nat->nat_oip; bcopy((char *)fin, (char *)&fi, sizeof(fi)); bzero((char *)tcp2, sizeof(*tcp2)); + tcp2->th_off = 5; fi.fin_dp = (char *)tcp2; fi.fin_fr = &raudiofr; tcp2->th_win = htons(8192); + slen = ip->ip_len; + ip->ip_len = fin->fin_hlen + sizeof(*tcp); if (((rap->rap_mode & RAP_M_UDP_ROBUST) == RAP_M_UDP_ROBUST) && (rap->rap_srport != 0)) { dp = rap->rap_srport; sp = rap->rap_prport; tcp2->th_sport = htons(sp); tcp2->th_dport = htons(dp); fi.fin_data[0] = dp; fi.fin_data[1] = sp; ipn = nat_new(nat->nat_ptr, ip, &fi, - IPN_UDP | (sp ? 0 : FI_W_SPORT), - NAT_OUTBOUND); + IPN_UDP | (sp ? 0 : FI_W_SPORT), NAT_OUTBOUND); if (ipn != NULL) { ipn->nat_age = fr_defnatage; (void) fr_addstate(ip, &fi, sp ? 0 : FI_W_SPORT); } } if ((rap->rap_mode & RAP_M_UDP) == RAP_M_UDP) { sp = rap->rap_plport; tcp2->th_sport = htons(sp); tcp2->th_dport = 0; /* XXX - don't specify remote port */ fi.fin_data[0] = sp; fi.fin_data[1] = 0; ipn = nat_new(nat->nat_ptr, ip, &fi, IPN_UDP|FI_W_DPORT, NAT_OUTBOUND); if (ipn != NULL) { ipn->nat_age = fr_defnatage; (void) fr_addstate(ip, &fi, FI_W_DPORT); } } - + ip->ip_p = swp; + ip->ip_len = slen; ip->ip_src = swa; ip->ip_dst = swb; return 0; } Index: head/sys/contrib/ipfilter/netinet/ip_rcmd_pxy.c =================================================================== --- head/sys/contrib/ipfilter/netinet/ip_rcmd_pxy.c (revision 60856) +++ head/sys/contrib/ipfilter/netinet/ip_rcmd_pxy.c (revision 60857) @@ -1,157 +1,166 @@ /* + * $Id: ip_rcmd_pxy.c,v 1.4.2.1 2000/05/06 11:19:34 darrenr Exp $ + */ +/* * Simple RCMD transparent proxy for in-kernel use. For use with the NAT * code. * $FreeBSD$ */ #if SOLARIS && defined(_KERNEL) extern kmutex_t ipf_rw; #endif #define isdigit(x) ((x) >= '0' && (x) <= '9') #define IPF_RCMD_PROXY int ippr_rcmd_init __P((void)); int ippr_rcmd_new __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *)); int ippr_rcmd_out __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *)); u_short ipf_rcmd_atoi __P((char *)); int ippr_rcmd_portmsg __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *)); static frentry_t rcmdfr; /* * RCMD application proxy initialization. */ int ippr_rcmd_init() { bzero((char *)&rcmdfr, sizeof(rcmdfr)); rcmdfr.fr_ref = 1; rcmdfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; return 0; } /* * Setup for a new RCMD proxy. */ int ippr_rcmd_new(fin, ip, aps, nat) fr_info_t *fin; ip_t *ip; ap_session_t *aps; nat_t *nat; { tcphdr_t *tcp = (tcphdr_t *)fin->fin_dp; aps->aps_psiz = sizeof(u_32_t); KMALLOCS(aps->aps_data, u_32_t *, sizeof(u_32_t)); if (aps->aps_data == NULL) return -1; *(u_32_t *)aps->aps_data = 0; aps->aps_sport = tcp->th_sport; aps->aps_dport = tcp->th_dport; return 0; } /* * ipf_rcmd_atoi - implement a simple version of atoi */ u_short ipf_rcmd_atoi(ptr) char *ptr; { register char *s = ptr, c; register u_short i = 0; while ((c = *s++) && isdigit(c)) { i *= 10; i += c - '0'; } return i; } int ippr_rcmd_portmsg(fin, ip, aps, nat) fr_info_t *fin; ip_t *ip; ap_session_t *aps; nat_t *nat; { char portbuf[8], *s; struct in_addr swip; u_short sp, dp; int off, dlen; tcphdr_t *tcp, tcph, *tcp2 = &tcph; fr_info_t fi; nat_t *ipn; mb_t *m; #if SOLARIS mb_t *m1; #endif tcp = (tcphdr_t *)fin->fin_dp; off = (ip->ip_hl << 2) + (tcp->th_off << 2); m = *(mb_t **)fin->fin_mp; #if SOLARIS m = fin->fin_qfm; dlen = msgdsize(m) - off; bzero(portbuf, sizeof(portbuf)); copyout_mblk(m, off, MIN(sizeof(portbuf), dlen), portbuf); #else dlen = mbufchainlen(m) - off; bzero(portbuf, sizeof(portbuf)); m_copydata(m, off, MIN(sizeof(portbuf), dlen), portbuf); #endif if ((*(u_32_t *)aps->aps_data != 0) && (tcp->th_seq != *(u_32_t *)aps->aps_data)) return 0; portbuf[sizeof(portbuf) - 1] = '\0'; s = portbuf; sp = ipf_rcmd_atoi(s); if (!sp) return 0; /* * Add skeleton NAT entry for connection which will come back the * other way. */ sp = htons(sp); dp = htons(fin->fin_data[1]); ipn = nat_outlookup(fin->fin_ifp, IPN_TCP, nat->nat_p, nat->nat_inip, ip->ip_dst, (dp << 16) | sp); if (ipn == NULL) { + int slen; + + slen = ip->ip_len; + ip->ip_len = fin->fin_hlen + sizeof(*tcp); bcopy((char *)fin, (char *)&fi, sizeof(fi)); bzero((char *)tcp2, sizeof(*tcp2)); tcp2->th_win = htons(8192); tcp2->th_sport = sp; tcp2->th_dport = 0; /* XXX - don't specify remote port */ + tcp2->th_off = 5; fi.fin_data[0] = ntohs(sp); fi.fin_data[1] = 0; fi.fin_dp = (char *)tcp2; swip = ip->ip_src; ip->ip_src = nat->nat_inip; ipn = nat_new(nat->nat_ptr, ip, &fi, IPN_TCP|FI_W_DPORT, NAT_OUTBOUND); if (ipn != NULL) { ipn->nat_age = fr_defnatage; fi.fin_fr = &rcmdfr; (void) fr_addstate(ip, &fi, FI_W_DPORT); } + ip->ip_len = slen; ip->ip_src = swip; } return 0; } int ippr_rcmd_out(fin, ip, aps, nat) fr_info_t *fin; ip_t *ip; ap_session_t *aps; nat_t *nat; { return ippr_rcmd_portmsg(fin, ip, aps, nat); } Index: head/sys/contrib/ipfilter/netinet/ipl.h =================================================================== --- head/sys/contrib/ipfilter/netinet/ipl.h (revision 60856) +++ head/sys/contrib/ipfilter/netinet/ipl.h (revision 60857) @@ -1,17 +1,17 @@ /* - * Copyright (C) 1993-1999 by Darren Reed. + * Copyright (C) 1993-2000 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. * * @(#)ipl.h 1.21 6/5/96 * $FreeBSD$ */ #ifndef __IPL_H__ #define __IPL_H__ -#define IPL_VERSION "IP Filter: v3.3.8" +#define IPL_VERSION "IP Filter: v3.4.4" #endif Index: head/sys/contrib/ipfilter/netinet/mlfk_ipl.c =================================================================== --- head/sys/contrib/ipfilter/netinet/mlfk_ipl.c (revision 60856) +++ head/sys/contrib/ipfilter/netinet/mlfk_ipl.c (revision 60857) @@ -1,180 +1,181 @@ /* * Copyright 1999 Guido van Rooij. All rights reserved. * * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE HOLDER OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ + #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static dev_t ipf_devs[IPL_LOGMAX + 1]; SYSCTL_DECL(_net_inet); SYSCTL_NODE(_net_inet, OID_AUTO, ipf, CTLFLAG_RW, 0, "IPF"); SYSCTL_INT(_net_inet_ipf, OID_AUTO, fr_flags, CTLFLAG_RW, &fr_flags, 0, ""); SYSCTL_INT(_net_inet_ipf, OID_AUTO, fr_pass, CTLFLAG_RW, &fr_pass, 0, ""); SYSCTL_INT(_net_inet_ipf, OID_AUTO, fr_active, CTLFLAG_RD, &fr_active, 0, ""); SYSCTL_INT(_net_inet_ipf, OID_AUTO, fr_tcpidletimeout, CTLFLAG_RW, &fr_tcpidletimeout, 0, ""); SYSCTL_INT(_net_inet_ipf, OID_AUTO, fr_tcpclosewait, CTLFLAG_RW, &fr_tcpclosewait, 0, ""); SYSCTL_INT(_net_inet_ipf, OID_AUTO, fr_tcplastack, CTLFLAG_RW, &fr_tcplastack, 0, ""); SYSCTL_INT(_net_inet_ipf, OID_AUTO, fr_tcptimeout, CTLFLAG_RW, &fr_tcptimeout, 0, ""); SYSCTL_INT(_net_inet_ipf, OID_AUTO, fr_tcpclosed, CTLFLAG_RW, &fr_tcpclosed, 0, ""); SYSCTL_INT(_net_inet_ipf, OID_AUTO, fr_udptimeout, CTLFLAG_RW, &fr_udptimeout, 0, ""); SYSCTL_INT(_net_inet_ipf, OID_AUTO, fr_icmptimeout, CTLFLAG_RW, &fr_icmptimeout, 0, ""); SYSCTL_INT(_net_inet_ipf, OID_AUTO, fr_defnatage, CTLFLAG_RW, &fr_defnatage, 0, ""); SYSCTL_INT(_net_inet_ipf, OID_AUTO, fr_ipfrttl, CTLFLAG_RW, &fr_ipfrttl, 0, ""); SYSCTL_INT(_net_inet_ipf, OID_AUTO, ipl_unreach, CTLFLAG_RW, &ipl_unreach, 0, ""); SYSCTL_INT(_net_inet_ipf, OID_AUTO, fr_running, CTLFLAG_RD, &fr_running, 0, ""); SYSCTL_INT(_net_inet_ipf, OID_AUTO, fr_authsize, CTLFLAG_RD, &fr_authsize, 0, ""); SYSCTL_INT(_net_inet_ipf, OID_AUTO, fr_authused, CTLFLAG_RD, &fr_authused, 0, ""); SYSCTL_INT(_net_inet_ipf, OID_AUTO, fr_defaultauthage, CTLFLAG_RW, &fr_defaultauthage, 0, ""); #define CDEV_MAJOR 79 static struct cdevsw ipl_cdevsw = { /* open */ iplopen, /* close */ iplclose, /* read */ iplread, /* write */ nowrite, /* ioctl */ iplioctl, /* poll */ nopoll, /* mmap */ nommap, /* strategy */ nostrategy, /* name */ "ipl", /* maj */ CDEV_MAJOR, /* dump */ nodump, /* psize */ nopsize, /* flags */ 0, /* bmaj */ -1 }; static int ipfilter_modevent(module_t mod, int type, void *unused) { char *c; int i, error = 0; switch (type) { case MOD_LOAD : + error = iplattach(); if (error) break; c = NULL; for(i=strlen(IPL_NAME); i>0; i--) if (IPL_NAME[i] == '/') { c = &IPL_NAME[i+1]; break; } if (!c) c = IPL_NAME; ipf_devs[IPL_LOGIPF] = make_dev(&ipl_cdevsw, IPL_LOGIPF, 0, 0, 0600, c); c = NULL; for(i=strlen(IPL_NAT); i>0; i--) if (IPL_NAT[i] == '/') { c = &IPL_NAT[i+1]; break; } if (!c) c = IPL_NAT; ipf_devs[IPL_LOGNAT] = make_dev(&ipl_cdevsw, IPL_LOGNAT, 0, 0, 0600, c); c = NULL; for(i=strlen(IPL_STATE); i>0; i--) if (IPL_STATE[i] == '/') { c = &IPL_STATE[i+1]; break; } if (!c) c = IPL_STATE; ipf_devs[IPL_LOGSTATE] = make_dev(&ipl_cdevsw, IPL_LOGSTATE, 0, 0, 0600, c); c = NULL; for(i=strlen(IPL_AUTH); i>0; i--) if (IPL_AUTH[i] == '/') { c = &IPL_AUTH[i+1]; break; } if (!c) c = IPL_AUTH; ipf_devs[IPL_LOGAUTH] = make_dev(&ipl_cdevsw, IPL_LOGAUTH, 0, 0, 0600, c); break; case MOD_UNLOAD : destroy_dev(ipf_devs[IPL_LOGIPF]); destroy_dev(ipf_devs[IPL_LOGNAT]); destroy_dev(ipf_devs[IPL_LOGSTATE]); destroy_dev(ipf_devs[IPL_LOGAUTH]); - cdevsw_remove(&ipl_cdevsw); error = ipldetach(); break; default: error = EINVAL; break; } return error; } static moduledata_t ipfiltermod = { - "ipfilter", + IPL_VERSION, ipfilter_modevent, 0 }; DECLARE_MODULE(ipfilter, ipfiltermod, SI_SUB_PROTO_DOMAIN, SI_ORDER_ANY); Index: head/sys/netinet/fil.c =================================================================== --- head/sys/netinet/fil.c (revision 60856) +++ head/sys/netinet/fil.c (revision 60857) @@ -1,2013 +1,2013 @@ /* * Copyright (C) 1993-2000 by Darren Reed. * * Redistribution and use in source and binary forms are permitted * provided that this notice is preserved and due credit is given * to the original author and the contributors. */ #if !defined(lint) static const char sccsid[] = "@(#)fil.c 1.36 6/5/96 (C) 1993-1996 Darren Reed"; /* static const char rcsid[] = "@(#)$Id: fil.c,v 2.3.2.16 2000/01/27 08:49:37 darrenr Exp $"; */ static const char rcsid[] = "@(#)$FreeBSD$"; #endif #if defined(_KERNEL) && defined(__FreeBSD_version) && \ (__FreeBSD_version >= 400000) && !defined(KLD_MODULE) #include "opt_inet6.h" #endif #include #include #include #include #include #if defined(__NetBSD__) && (NetBSD >= 199905) && !defined(IPFILTER_LKM) && \ defined(_KERNEL) # include "opt_ipfilter_log.h" #endif #if (defined(KERNEL) || defined(_KERNEL)) && defined(__FreeBSD_version) && \ (__FreeBSD_version >= 220000) # include # include #else # include #endif #if (defined(_KERNEL) || defined(KERNEL)) && !defined(linux) # include #else # include # include # include #endif #include #if !defined(__SVR4) && !defined(__svr4__) # ifndef linux # include # endif #else # include # if SOLARIS2 < 5 # include # endif # include #endif #ifndef linux # include # include #endif #include #ifdef sun # include #endif #include #include #include #include #ifndef linux # include #endif #if defined(__sgi) && defined(IFF_DRVRLOCK) /* IRIX 6 */ # include # include #endif #include #include #include #include "netinet/ip_compat.h" #ifdef USE_INET6 # include # if !SOLARIS && defined(_KERNEL) # include # endif #endif #include #include "netinet/ip_fil.h" #include "netinet/ip_proxy.h" #include "netinet/ip_nat.h" #include "netinet/ip_frag.h" #include "netinet/ip_state.h" #include "netinet/ip_auth.h" # if defined(__FreeBSD_version) && (__FreeBSD_version >= 300000) # include # if defined(_KERNEL) && !defined(IPFILTER_LKM) # include "opt_ipfilter.h" # endif # endif #ifndef MIN # define MIN(a,b) (((a)<(b))?(a):(b)) #endif #include "netinet/ipl.h" #include #ifndef _KERNEL # include "ipf.h" # include "ipt.h" extern int opts; # define FR_VERBOSE(verb_pr) verbose verb_pr # define FR_DEBUG(verb_pr) debug verb_pr # define IPLLOG(a, c, d, e) ipllog() #else /* #ifndef _KERNEL */ # define FR_VERBOSE(verb_pr) # define FR_DEBUG(verb_pr) # define IPLLOG(a, c, d, e) ipflog(a, c, d, e) # if SOLARIS || defined(__sgi) extern KRWLOCK_T ipf_mutex, ipf_auth, ipf_nat; extern kmutex_t ipf_rw; # endif # if SOLARIS # define FR_NEWAUTH(m, fi, ip, qif) fr_newauth((mb_t *)m, fi, \ ip, qif) # define SEND_RESET(ip, qif, if, fin) send_reset(fin, ip, qif) # else /* SOLARIS */ # define FR_NEWAUTH(m, fi, ip, qif) fr_newauth((mb_t *)m, fi, ip) # define SEND_RESET(ip, qif, if, fin) send_reset(fin, ip) # endif /* SOLARIS || __sgi */ #endif /* _KERNEL */ struct filterstats frstats[2] = {{0,0,0,0,0},{0,0,0,0,0}}; struct frentry *ipfilter[2][2] = { { NULL, NULL }, { NULL, NULL } }, #ifdef USE_INET6 *ipfilter6[2][2] = { { NULL, NULL }, { NULL, NULL } }, *ipacct6[2][2] = { { NULL, NULL }, { NULL, NULL } }, #endif *ipacct[2][2] = { { NULL, NULL }, { NULL, NULL } }; struct frgroup *ipfgroups[3][2]; int fr_flags = IPF_LOGGING; int fr_active = 0; int fr_chksrc = 0; #if defined(IPFILTER_DEFAULT_BLOCK) int fr_pass = FR_NOMATCH|FR_BLOCK; #else int fr_pass = (IPF_DEFAULT_PASS|FR_NOMATCH); #endif char ipfilter_version[] = IPL_VERSION; fr_info_t frcache[2]; static int frflushlist __P((int, minor_t, int *, frentry_t **)); #ifdef _KERNEL static void frsynclist __P((frentry_t *)); #endif /* * 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; { u_short optmsk = 0, secmsk = 0, auth = 0; int i, mv, ol, off, p, plen, v; fr_ip_t *fi = &fin->fin_fi; struct optlist *op; u_char *s, opt; tcphdr_t *tcp; fin->fin_rev = 0; fin->fin_fr = NULL; fin->fin_tcpf = 0; fin->fin_data[0] = 0; fin->fin_data[1] = 0; fin->fin_rule = -1; fin->fin_group = -1; #ifdef _KERNEL fin->fin_icode = ipl_unreach; #endif v = fin->fin_v; fi->fi_v = v; fin->fin_hlen = hlen; if (v == 4) { fin->fin_id = ip->ip_id; fi->fi_tos = ip->ip_tos; off = (ip->ip_off & IP_OFFMASK) << 3; tcp = (tcphdr_t *)((char *)ip + hlen); (*(((u_short *)fi) + 1)) = (*(((u_short *)ip) + 4)); fi->fi_src.i6[1] = 0; fi->fi_src.i6[2] = 0; fi->fi_src.i6[3] = 0; fi->fi_dst.i6[1] = 0; fi->fi_dst.i6[2] = 0; fi->fi_dst.i6[3] = 0; fi->fi_saddr = ip->ip_src.s_addr; fi->fi_daddr = ip->ip_dst.s_addr; p = ip->ip_p; fi->fi_fl = (hlen > sizeof(ip_t)) ? FI_OPTIONS : 0; if (ip->ip_off & 0x3fff) fi->fi_fl |= FI_FRAG; plen = ip->ip_len; fin->fin_dlen = plen - hlen; } #ifdef USE_INET6 else if (v == 6) { ip6_t *ip6 = (ip6_t *)ip; off = 0; p = ip6->ip6_nxt; fi->fi_p = p; fi->fi_ttl = ip6->ip6_hlim; tcp = (tcphdr_t *)(ip6 + 1); fi->fi_src.in6 = ip6->ip6_src; fi->fi_dst.in6 = ip6->ip6_dst; fin->fin_id = (u_short)(ip6->ip6_flow & 0xffff); fi->fi_tos = 0; fi->fi_fl = 0; plen = ntohs(ip6->ip6_plen); fin->fin_dlen = plen; } #endif else return; fin->fin_off = off; fin->fin_plen = plen; fin->fin_dp = (void *)tcp; switch (p) { case IPPROTO_ICMP : { int minicmpsz = sizeof(struct icmp); icmphdr_t *icmp; icmp = (icmphdr_t *)tcp; if (!off && (icmp->icmp_type == ICMP_ECHOREPLY || icmp->icmp_type == ICMP_ECHO)) minicmpsz = ICMP_MINLEN; /* type(1) + code(1) + cksum(2) + id(2) seq(2) + * 3*timestamp(3*4) */ else if (!off && (icmp->icmp_type == ICMP_TSTAMP || icmp->icmp_type == ICMP_TSTAMPREPLY)) minicmpsz = 20; /* type(1) + code(1) + cksum(2) + id(2) seq(2) + mask(4) */ else if (!off && (icmp->icmp_type == ICMP_MASKREQ || icmp->icmp_type == ICMP_MASKREPLY)) minicmpsz = 12; if ((!(plen >= hlen + minicmpsz) && !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; #ifdef USE_INET6 if (v == 6) { if (plen < sizeof(struct tcphdr)) fi->fi_fl |= FI_SHORT; } else #endif if (v == 4) { 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; #ifdef USE_INET6 if (v == 6) { if (plen < sizeof(struct udphdr)) fi->fi_fl |= FI_SHORT; } else #endif if (v == 4) { 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; } #ifdef USE_INET6 if (v == 6) { fi->fi_optmsk = 0; fi->fi_secmsk = 0; fi->fi_auth = 0; return; } #endif for (s = (u_char *)(ip + 1), hlen -= (int)sizeof(*ip); hlen > 0; ) { opt = *s; if (opt == '\0') break; else if (opt == IPOPT_NOP) ol = 1; else { if (hlen < 2) break; ol = (int)*(s + 1); if (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(ft, fin) frtuc_t *ft; 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)ft->ftu_dcmp)) { po = ft->ftu_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 <= ft->ftu_dtop)) err = 0; else if (!--i && /* In range */ (tup <= po || tup >= ft->ftu_dtop)) err = 0; } /* * compare source ports */ if (err && (i = (int)ft->ftu_scmp)) { po = ft->ftu_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 <= ft->ftu_stop)) err = 0; else if (!--i && /* In range */ (tup <= po || tup >= ft->ftu_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 !(ft->ftu_tcpf | ft->ftu_tcpfm); /* * Match the flags ? If not, abort this match. */ if (ft->ftu_tcpfm && ft->ftu_tcpf != (fin->fin_tcpf & ft->ftu_tcpfm)) { FR_DEBUG(("f. %#x & %#x != %#x\n", fin->fin_tcpf, ft->ftu_tcpfm, ft->ftu_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) u_32_t 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, skip = 0, logged = 0; u_32_t passt; fr = fin->fin_fr; fin->fin_fr = NULL; fin->fin_rule = 0; fin->fin_group = 0; if (fin->fin_v == 4) off = ip->ip_off & IP_OFFMASK; else off = 0; pass |= (fi->fi_fl << 24); if ((fi->fi_fl & FI_TCPUDP) && (fin->fin_dlen > 3) && !off) portcmp = 1; for (rulen = 0; fr; fr = fr->fr_next, rulen++) { if (skip) { skip--; continue; } /* * In all checks below, a null (zero) value in the * filter struture is taken to mean a wildcard. * * check that we are working for the right interface */ #ifdef _KERNEL # if BSD >= 199306 if (fin->fin_out != 0) { if ((fr->fr_oifa && fr->fr_oifa != ((mb_t *)m)->m_pkthdr.rcvif) || (fr->fr_ifa && fr->fr_ifa != fin->fin_ifp)) continue; } else # endif 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' : (pass & FR_AUTH) ? 'a' : 'b')); if (fr->fr_ifa && fr->fr_ifa != fin->fin_ifp) continue; FR_VERBOSE((":i")); #endif { register u_32_t *ld, *lm, *lip; register int i; lip = (u_32_t *)fi; lm = (u_32_t *)&fr->fr_mip; ld = (u_32_t *)&fr->fr_ip; i = ((*lip & *lm) != *ld); FR_DEBUG(("0. %#08x & %#08x != %#08x\n", *lip, *lm, *ld)); if (i) continue; /* * We now know whether the packet version and the * rule version match, along with protocol, ttl and * tos. */ lip++, lm++, ld++; /* * Unrolled loops (4 each, for 32 bits). */ i |= ((*lip & *lm) != *ld) << 19; FR_DEBUG(("1a. %#08x & %#08x != %#08x\n", *lip, *lm, *ld)); if (fi->fi_v == 6) { lip++, lm++, ld++; i |= ((*lip & *lm) != *ld) << 19; FR_DEBUG(("1b. %#08x & %#08x != %#08x\n", *lip, *lm, *ld)); lip++, lm++, ld++; i |= ((*lip & *lm) != *ld) << 19; FR_DEBUG(("1c. %#08x & %#08x != %#08x\n", *lip, *lm, *ld)); lip++, lm++, ld++; i |= ((*lip & *lm) != *ld) << 19; FR_DEBUG(("1d. %#08x & %#08x != %#08x\n", *lip, *lm, *ld)); } else { lip += 3; lm += 3; ld += 3; } i ^= (fr->fr_flags & FR_NOTSRCIP); if (i) continue; lip++, lm++, ld++; i |= ((*lip & *lm) != *ld) << 20; FR_DEBUG(("2a. %#08x & %#08x != %#08x\n", *lip, *lm, *ld)); if (fi->fi_v == 6) { lip++, lm++, ld++; i |= ((*lip & *lm) != *ld) << 20; FR_DEBUG(("2b. %#08x & %#08x != %#08x\n", *lip, *lm, *ld)); lip++, lm++, ld++; i |= ((*lip & *lm) != *ld) << 20; FR_DEBUG(("2c. %#08x & %#08x != %#08x\n", *lip, *lm, *ld)); lip++, lm++, ld++; i |= ((*lip & *lm) != *ld) << 20; FR_DEBUG(("2d. %#08x & %#08x != %#08x\n", *lip, *lm, *ld)); } else { lip += 3; lm += 3; ld += 3; } i ^= (fr->fr_flags & FR_NOTDSTIP); if (i) continue; lip++, lm++, ld++; i |= ((*lip & *lm) != *ld); FR_DEBUG(("3. %#08x & %#08x != %#08x\n", *lip, *lm, *ld)); lip++, lm++, ld++; i |= ((*lip & *lm) != *ld); FR_DEBUG(("4. %#08x & %#08x != %#08x\n", *lip, *lm, *ld)); if (i) continue; } /* * If a fragment, then only the first has what we're looking * for here... */ if (!portcmp && (fr->fr_dcmp || fr->fr_scmp || fr->fr_tcpf || fr->fr_tcpfm)) continue; if (fi->fi_fl & FI_TCPUDP) { if (!fr_tcpudpchk(&fr->fr_tuc, fin)) continue; } else if (fr->fr_icmpm || fr->fr_icmp) { if ((fi->fi_p != IPPROTO_ICMP) || off || (fin->fin_dlen < 2)) continue; 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; } } FR_VERBOSE(("*")); /* * Just log this packet... */ passt = fr->fr_flags; if ((passt & FR_CALLNOW) && fr->fr_func) passt = (*fr->fr_func)(passt, ip, fin); fin->fin_fr = fr; #ifdef IPFILTER_LOG if ((passt & FR_LOGMASK) == FR_LOG) { if (!IPLLOG(passt, ip, fin, m)) { if (passt & FR_LOGORBLOCK) passt |= FR_BLOCK|FR_QUICK; ATOMIC_INCL(frstats[fin->fin_out].fr_skip); } ATOMIC_INCL(frstats[fin->fin_out].fr_pkl); logged = 1; } #endif /* IPFILTER_LOG */ if (!(skip = fr->fr_skip) && (passt & FR_LOGMASK) != FR_LOG) pass = passt; FR_DEBUG(("pass %#x\n", pass)); ATOMIC_INCL(fr->fr_hits); if (pass & FR_ACCOUNT) fr->fr_bytes += (U_QUAD_T)ip->ip_len; else fin->fin_icode = fr->fr_icode; fin->fin_rule = rulen; fin->fin_group = fr->fr_group; if (fr->fr_grp) { fin->fin_fr = fr->fr_grp; pass = fr_scanlist(pass, ip, fin, m); if (fin->fin_fr == NULL) { fin->fin_rule = rulen; fin->fin_group = fr->fr_group; fin->fin_fr = fr; } if (pass & FR_DONTCACHE) logged = 1; } if (pass & FR_QUICK) break; } if (logged) pass |= FR_DONTCACHE; return pass; } /* * frcheck - filter check * check using source and destination addresses/ports in a packet whether * or not to pass it on or not. */ int fr_check(ip, hlen, ifp, out #if defined(_KERNEL) && SOLARIS , qif, mp) qif_t *qif; #else , mp) #endif mb_t **mp; ip_t *ip; int hlen; void *ifp; int out; { /* * The above really sucks, but short of writing a diff */ fr_info_t frinfo, *fc; register fr_info_t *fin = &frinfo; int changed, error = EHOSTUNREACH, v = ip->ip_v; frentry_t *fr = NULL, *list; u_32_t pass, apass; #if !SOLARIS || !defined(_KERNEL) register mb_t *m = *mp; #endif #ifdef _KERNEL mb_t *mc = NULL; # if !defined(__SVR4) && !defined(__svr4__) # ifdef __sgi char hbuf[(0xf << 2) + sizeof(struct icmp) + sizeof(ip_t) + 8]; # endif int up; # ifdef M_CANFASTFWD /* * XXX For now, IP Filter and fast-forwarding of cached flows * XXX are mutually exclusive. Eventually, IP Filter should * XXX get a "can-fast-forward" filter rule. */ m->m_flags &= ~M_CANFASTFWD; # endif /* M_CANFASTFWD */ # ifdef CSUM_DELAY_DATA /* * disable delayed checksums. */ if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) { in_delayed_cksum(m); m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA; } # endif /* CSUM_DELAY_DATA */ if ((ip->ip_p == IPPROTO_TCP || ip->ip_p == IPPROTO_UDP || ip->ip_p == IPPROTO_ICMP)) { int plen = 0; if ((ip->ip_off & IP_OFFMASK) == 0) switch(ip->ip_p) { case IPPROTO_TCP: plen = sizeof(tcphdr_t); break; case IPPROTO_UDP: plen = sizeof(udphdr_t); break; /* 96 - enough for complete ICMP error IP header */ case IPPROTO_ICMP: plen = ICMPERR_MAXPKTLEN - sizeof(ip_t); break; } up = MIN(hlen + plen, ip->ip_len); if (up > m->m_len) { # ifdef __sgi /* Under IRIX, avoid m_pullup as it makes ping panic */ if ((up > sizeof(hbuf)) || (m_length(m) < up)) { ATOMIC_INCL(frstats[out].fr_pull[1]); return -1; } m_copydata(m, 0, up, hbuf); ATOMIC_INCL(frstats[out].fr_pull[0]); ip = (ip_t *)hbuf; # else /* __ sgi */ # ifndef linux if ((*mp = m_pullup(m, up)) == 0) { ATOMIC_INCL(frstats[out].fr_pull[1]); return -1; } else { ATOMIC_INCL(frstats[out].fr_pull[0]); m = *mp; ip = mtod(m, ip_t *); } # endif /* !linux */ # endif /* __sgi */ } else up = 0; } else up = 0; # endif /* !defined(__SVR4) && !defined(__svr4__) */ # if SOLARIS mb_t *m = qif->qf_m; if ((u_int)ip & 0x3) return 2; fin->fin_qfm = m; fin->fin_qif = qif; # endif # ifdef USE_INET6 if (v == 6) { ATOMIC_INCL(frstats[0].fr_ipv6[out]); } else # endif if (!out && fr_chksrc && !fr_verifysrc(ip->ip_src, ifp)) { ATOMIC_INCL(frstats[0].fr_badsrc); # if !SOLARIS m_freem(m); # endif return error; } #endif /* _KERNEL */ /* * Be careful here: ip_id is in network byte order when called * from ip_output() */ if ((out) && (v == 4)) ip->ip_id = ntohs(ip->ip_id); changed = 0; fin->fin_v = v; fin->fin_ifp = ifp; fin->fin_out = out; fin->fin_mp = mp; fr_makefrip(hlen, ip, fin); pass = fr_pass; if (fin->fin_fi.fi_fl & FI_SHORT) { ATOMIC_INCL(frstats[out].fr_short); } READ_ENTER(&ipf_mutex); if (fin->fin_fi.fi_fl & FI_SHORT) ATOMIC_INCL(frstats[out].fr_short); /* * Check auth now. This, combined with the check below to see if apass * is 0 is to ensure that we don't count the packet twice, which can * otherwise occur when we reprocess it. As it is, we only count it * after it has no auth. table matchup. This also stops NAT from * occuring until after the packet has been auth'd. */ apass = fr_checkauth(ip, fin); if (!out) { #ifdef USE_INET6 if (v == 6) list = ipacct6[0][fr_active]; else #endif list = ipacct[0][fr_active]; changed = ip_natin(ip, fin); if (!apass && (fin->fin_fr = list) && (fr_scanlist(FR_NOMATCH, ip, fin, m) & FR_ACCOUNT)) { ATOMIC_INCL(frstats[0].fr_acct); } } if (apass || (!(fr = ipfr_knownfrag(ip, fin)) && !(fr = fr_checkstate(ip, fin)))) { /* * If a packet is found in the auth table, then skip checking * the access lists for permission but we do need to consider * the result as if it were from the ACL's. */ if (!apass) { fc = frcache + out; if (!bcmp((char *)fin, (char *)fc, FI_CSIZE)) { /* * copy cached data so we can unlock the mutex * earlier. */ bcopy((char *)fc, (char *)fin, FI_COPYSIZE); ATOMIC_INCL(frstats[out].fr_chit); if ((fr = fin->fin_fr)) { ATOMIC_INCL(fr->fr_hits); pass = fr->fr_flags; } } else { #ifdef USE_INET6 if (v == 6) list = ipfilter6[out][fr_active]; else #endif list = ipfilter[out][fr_active]; if ((fin->fin_fr = list)) pass = fr_scanlist(fr_pass, ip, fin, m); if (!(pass & (FR_KEEPSTATE|FR_DONTCACHE))) bcopy((char *)fin, (char *)fc, FI_COPYSIZE); if (pass & FR_NOMATCH) { ATOMIC_INCL(frstats[out].fr_nom); } } fr = fin->fin_fr; } else pass = apass; /* * If we fail to add a packet to the authorization queue, * then we drop the packet later. However, if it was added * then pretend we've dropped it already. */ if ((pass & FR_AUTH)) if (fr_newauth((mb_t *)m, fin, ip) != 0) #ifdef _KERNEL m = *mp = NULL; #else ; #endif if (pass & FR_PREAUTH) { READ_ENTER(&ipf_auth); if ((fin->fin_fr = ipauth) && (pass = fr_scanlist(0, ip, fin, m))) { ATOMIC_INCL(fr_authstats.fas_hits); } else { ATOMIC_INCL(fr_authstats.fas_miss); } RWLOCK_EXIT(&ipf_auth); } fin->fin_fr = fr; if ((pass & (FR_KEEPFRAG|FR_KEEPSTATE)) == FR_KEEPFRAG) { if (fin->fin_fi.fi_fl & FI_FRAG) { if (ipfr_newfrag(ip, fin, pass) == -1) { ATOMIC_INCL(frstats[out].fr_bnfr); } else { ATOMIC_INCL(frstats[out].fr_nfr); } } else { ATOMIC_INCL(frstats[out].fr_cfr); } } if (pass & FR_KEEPSTATE) { if (fr_addstate(ip, fin, 0) == NULL) { ATOMIC_INCL(frstats[out].fr_bads); } else { ATOMIC_INCL(frstats[out].fr_ads); } } } else if (fr != NULL) { pass = fr->fr_flags; if (pass & FR_LOGFIRST) pass &= ~(FR_LOGFIRST|FR_LOG); } if (fr && fr->fr_func && !(pass & FR_CALLNOW)) pass = (*fr->fr_func)(pass, ip, fin); /* * Only count/translate packets which will be passed on, out the * interface. */ if (out && (pass & FR_PASS)) { #ifdef USE_INET6 if (v == 6) list = ipacct6[0][fr_active]; else #endif list = ipacct[0][fr_active]; if ((fin->fin_fr = list) && (fr_scanlist(FR_NOMATCH, ip, fin, m) & FR_ACCOUNT)) { ATOMIC_INCL(frstats[1].fr_acct); } fin->fin_fr = fr; changed = ip_natout(ip, fin); } else fin->fin_fr = fr; RWLOCK_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; ATOMIC_INCL(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; ATOMIC_INCL(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; ATOMIC_INCL(frstats[out].fr_bpkl); logit: if (!IPLLOG(pass, ip, fin, m)) { ATOMIC_INCL(frstats[out].fr_skip); if ((pass & (FR_PASS|FR_LOGORBLOCK)) == (FR_PASS|FR_LOGORBLOCK)) pass ^= FR_PASS|FR_BLOCK; } } } #endif /* IPFILTER_LOG */ if ((out) && (v == 4)) ip->ip_id = htons(ip->ip_id); #ifdef _KERNEL /* * Only allow FR_DUP to work if a rule matched - it makes no sense to * set FR_DUP as a "default" as there are no instructions about where * to send the packet. */ if (fr && (pass & FR_DUP)) # if SOLARIS mc = dupmsg(m); # else # ifndef linux mc = m_copy(m, 0, M_COPYALL); # else ; # endif # endif #endif if (pass & FR_PASS) { ATOMIC_INCL(frstats[out].fr_pass); } else if (pass & FR_BLOCK) { ATOMIC_INCL(frstats[out].fr_block); /* * Should we return an ICMP packet to indicate error * status passing through the packet filter ? * WARNING: ICMP error packets AND TCP RST packets should * ONLY be sent in repsonse to incoming packets. Sending them * in response to outbound packets can result in a panic on * some operating systems. */ if (!out) { #ifdef _KERNEL if (pass & FR_RETICMP) { int dst; if ((pass & FR_RETMASK) == FR_FAKEICMP) dst = 1; else dst = 0; send_icmp_err(ip, ICMP_UNREACH, fin, dst); ATOMIC_INCL(frstats[0].fr_ret); } else if (((pass & FR_RETMASK) == FR_RETRST) && !(fin->fin_fi.fi_fl & FI_SHORT)) { if (send_reset(ip, fin) == 0) { ATOMIC_INCL(frstats[1].fr_ret); } } #else if ((pass & FR_RETMASK) == FR_RETICMP) { verbose("- ICMP unreachable sent\n"); ATOMIC_INCL(frstats[0].fr_ret); } else if ((pass & FR_RETMASK) == FR_FAKEICMP) { verbose("- forged ICMP unreachable sent\n"); ATOMIC_INCL(frstats[0].fr_ret); } else if (((pass & FR_RETMASK) == FR_RETRST) && !(fin->fin_fi.fi_fl & FI_SHORT)) { verbose("- TCP RST sent\n"); ATOMIC_INCL(frstats[1].fr_ret); } #endif } else { if (pass & FR_RETRST) error = ECONNRESET; } } /* * If we didn't drop off the bottom of the list of rules (and thus * the 'current' rule fr is not NULL), then we may have some extra * instructions about what to do with a packet. * Once we're finished return to our caller, freeing the packet if * we are dropping it (* BSD ONLY *). */ if ((changed == -1) && (pass & FR_PASS)) { pass &= ~FR_PASS; pass |= FR_BLOCK; } #if defined(_KERNEL) # if !SOLARIS # if !defined(linux) if (fr) { frdest_t *fdp = &fr->fr_tif; if (((pass & FR_FASTROUTE) && !out) || (fdp->fd_ifp && fdp->fd_ifp != (struct ifnet *)-1)) { if (ipfr_fastroute(m, fin, fdp) == 0) m = *mp = NULL; } if (mc) ipfr_fastroute(mc, fin, &fr->fr_dif); } if (!(pass & FR_PASS) && m) m_freem(m); # ifdef __sgi else if (changed && up && m) m_copyback(m, 0, up, hbuf); # endif # endif /* !linux */ # else /* !SOLARIS */ if (fr) { frdest_t *fdp = &fr->fr_tif; if (((pass & FR_FASTROUTE) && !out) || (fdp->fd_ifp && fdp->fd_ifp != (struct ifnet *)-1)) { if (ipfr_fastroute(qif, ip, m, mp, fin, fdp) == 0) m = *mp = NULL; } if (mc) ipfr_fastroute(qif, ip, mc, mp, fin, &fr->fr_dif); } # endif /* !SOLARIS */ return (pass & FR_PASS) ? 0 : error; #else /* _KERNEL */ if (pass & FR_NOMATCH) return 1; if (pass & FR_PASS) return 0; if (pass & FR_AUTH) return -2; return -1; #endif /* _KERNEL */ } /* * ipf_cksum * addr should be 16bit aligned and len is in bytes. * length is in bytes */ u_short ipf_cksum(addr, len) register u_short *addr; register int len; { register u_32_t sum = 0; for (sum = 0; len > 1; len -= 2) sum += *addr++; /* mop up an odd byte, if necessary */ if (len == 1) sum += *(u_char *)addr; /* * add back carry outs from top 16 bits to low 16 bits */ sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ sum += (sum >> 16); /* add carry */ return (u_short)(~sum); } /* * NB: This function assumes we've pullup'd enough for all of the IP header * and the TCP header. We also assume that data blocks aren't allocated in * odd sizes. */ u_short fr_tcpsum(m, ip, tcp) mb_t *m; ip_t *ip; tcphdr_t *tcp; { u_short *sp, slen, ts; u_int sum, sum2; int hlen; /* * Add up IP Header portion */ hlen = ip->ip_hl << 2; slen = ip->ip_len - hlen; sum = htons((u_short)ip->ip_p); sum += htons(slen); sp = (u_short *)&ip->ip_src; sum += *sp++; /* ip_src */ sum += *sp++; sum += *sp++; /* ip_dst */ sum += *sp++; ts = tcp->th_sum; tcp->th_sum = 0; #ifdef KERNEL # if SOLARIS sum2 = ip_cksum(m, hlen, sum); /* hlen == offset */ sum2 = (sum2 & 0xffff) + (sum2 >> 16); sum2 = ~sum2 & 0xffff; # else /* SOLARIS */ # if defined(BSD) || defined(sun) # if BSD >= 199306 m->m_data += hlen; # else m->m_off += hlen; # endif m->m_len -= hlen; sum2 = in_cksum(m, slen); m->m_len += hlen; # if BSD >= 199306 m->m_data -= hlen; # else m->m_off -= hlen; # endif /* * Both sum and sum2 are partial sums, so combine them together. */ sum = (sum & 0xffff) + (sum >> 16); sum = ~sum & 0xffff; sum2 += sum; sum2 = (sum2 & 0xffff) + (sum2 >> 16); # else /* defined(BSD) || defined(sun) */ { union { u_char c[2]; u_short s; } bytes; u_short len = ip->ip_len; # if defined(__sgi) int add; # endif /* * Add up IP Header portion */ sp = (u_short *)&ip->ip_src; len -= (ip->ip_hl << 2); sum = ntohs(IPPROTO_TCP); sum += htons(len); sum += *sp++; /* ip_src */ sum += *sp++; sum += *sp++; /* ip_dst */ sum += *sp++; if (sp != (u_short *)tcp) sp = (u_short *)tcp; sum += *sp++; /* sport */ sum += *sp++; /* dport */ sum += *sp++; /* seq */ sum += *sp++; sum += *sp++; /* ack */ sum += *sp++; sum += *sp++; /* off */ sum += *sp++; /* win */ sum += *sp++; /* Skip over checksum */ sum += *sp++; /* urp */ # ifdef __sgi /* * In case we had to copy the IP & TCP header out of mbufs, * skip over the mbuf bits which are the header */ if ((caddr_t)ip != mtod(m, caddr_t)) { hlen = (caddr_t)sp - (caddr_t)ip; while (hlen) { add = MIN(hlen, m->m_len); sp = (u_short *)(mtod(m, caddr_t) + add); hlen -= add; if (add == m->m_len) { m = m->m_next; if (!hlen) { if (!m) break; sp = mtod(m, u_short *); } PANIC((!m),("fr_tcpsum(1): not enough data")); } } } # endif if (!(len -= sizeof(*tcp))) goto nodata; while (len > 1) { if (((caddr_t)sp - mtod(m, caddr_t)) >= m->m_len) { m = m->m_next; PANIC((!m),("fr_tcpsum(2): not enough data")); sp = mtod(m, u_short *); } if (((caddr_t)(sp + 1) - mtod(m, caddr_t)) > m->m_len) { bytes.c[0] = *(u_char *)sp; m = m->m_next; PANIC((!m),("fr_tcpsum(3): not enough data")); sp = mtod(m, u_short *); bytes.c[1] = *(u_char *)sp; sum += bytes.s; sp = (u_short *)((u_char *)sp + 1); } if ((u_long)sp & 1) { bcopy((char *)sp++, (char *)&bytes.s, sizeof(bytes.s)); sum += bytes.s; } else sum += *sp++; len -= 2; } if (len) sum += ntohs(*(u_char *)sp << 8); nodata: while (sum > 0xffff) sum = (sum & 0xffff) + (sum >> 16); sum2 = (u_short)(~sum & 0xffff); } # endif /* defined(BSD) || defined(sun) */ # endif /* SOLARIS */ #else /* KERNEL */ sum2 = 0; #endif /* KERNEL */ tcp->th_sum = ts; return sum2; } #if defined(_KERNEL) && ( ((BSD < 199306) && !SOLARIS) || defined(__sgi) ) /* * Copyright (c) 1982, 1986, 1988, 1991, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)uipc_mbuf.c 8.2 (Berkeley) 1/4/94 * $Id: fil.c,v 2.35.2.8 2000/05/22 10:26:09 darrenr Exp $ */ /* * Copy data from an mbuf chain starting "off" bytes from the beginning, * continuing for "len" bytes, into the indicated buffer. */ void m_copydata(m, off, len, cp) register mb_t *m; register int off; register int len; caddr_t cp; { register unsigned count; if (off < 0 || len < 0) panic("m_copydata"); while (off > 0) { if (m == 0) panic("m_copydata"); if (off < m->m_len) break; off -= m->m_len; m = m->m_next; } while (len > 0) { if (m == 0) panic("m_copydata"); count = MIN(m->m_len - off, len); bcopy(mtod(m, caddr_t) + off, cp, count); len -= count; cp += count; off = 0; m = m->m_next; } } # ifndef linux /* * Copy data from a buffer back into the indicated mbuf chain, * starting "off" bytes from the beginning, extending the mbuf * chain if necessary. */ void m_copyback(m0, off, len, cp) struct mbuf *m0; register int off; register int len; caddr_t cp; { register int mlen; register struct mbuf *m = m0, *n; int totlen = 0; if (m0 == 0) return; while (off > (mlen = m->m_len)) { off -= mlen; totlen += mlen; if (m->m_next == 0) { n = m_getclr(M_DONTWAIT, m->m_type); if (n == 0) goto out; n->m_len = min(MLEN, len + off); m->m_next = n; } m = m->m_next; } while (len > 0) { mlen = min (m->m_len - off, len); bcopy(cp, off + mtod(m, caddr_t), (unsigned)mlen); cp += mlen; len -= mlen; mlen += off; off = 0; totlen += mlen; if (len == 0) break; if (m->m_next == 0) { n = m_get(M_DONTWAIT, m->m_type); if (n == 0) break; n->m_len = min(MLEN, len); m->m_next = n; } m = m->m_next; } out: #if 0 if (((m = m0)->m_flags & M_PKTHDR) && (m->m_pkthdr.len < totlen)) m->m_pkthdr.len = totlen; #endif return; } # endif /* linux */ #endif /* (_KERNEL) && ( ((BSD < 199306) && !SOLARIS) || __sgi) */ frgroup_t *fr_findgroup(num, flags, which, set, fgpp) u_32_t num, flags; minor_t which; int set; frgroup_t ***fgpp; { frgroup_t *fg, **fgp; if (which == IPL_LOGAUTH) fgp = &ipfgroups[2][set]; else if (flags & FR_ACCOUNT) fgp = &ipfgroups[1][set]; else if (flags & (FR_OUTQUE|FR_INQUE)) fgp = &ipfgroups[0][set]; else return NULL; num &= 0xffff; while ((fg = *fgp)) if (fg->fg_num == num) break; else fgp = &fg->fg_next; if (fgpp) *fgpp = fgp; return fg; } frgroup_t *fr_addgroup(num, fp, which, set) u_32_t num; frentry_t *fp; minor_t which; int set; { frgroup_t *fg, **fgp; if ((fg = fr_findgroup(num, fp->fr_flags, which, set, &fgp))) return fg; KMALLOC(fg, frgroup_t *); if (fg) { - fg->fg_num = num & 0xffff; + fg->fg_num = num; fg->fg_next = *fgp; fg->fg_head = fp; fg->fg_start = &fp->fr_grp; *fgp = fg; } return fg; } void fr_delgroup(num, flags, which, set) u_32_t num, flags; minor_t which; int set; { frgroup_t *fg, **fgp; if (!(fg = fr_findgroup(num, flags, which, set, &fgp))) return; *fgp = fg->fg_next; KFREE(fg); } /* * recursively flush rules from the list, descending groups as they are * encountered. if a rule is the head of a group and it has lost all its * group members, then also delete the group reference. */ static int frflushlist(set, unit, nfreedp, listp) int set; minor_t unit; int *nfreedp; frentry_t **listp; { register int freed = 0, i; register frentry_t *fp; while ((fp = *listp)) { *listp = fp->fr_next; if (fp->fr_grp) { i = frflushlist(set, unit, nfreedp, &fp->fr_grp); MUTEX_ENTER(&ipf_rw); fp->fr_ref -= i; MUTEX_EXIT(&ipf_rw); } ATOMIC_DEC32(fp->fr_ref); if (fp->fr_grhead) { fr_delgroup(fp->fr_grhead, fp->fr_flags, unit, set); fp->fr_grhead = 0; } if (fp->fr_ref == 0) { KFREE(fp); freed++; } else fp->fr_next = NULL; } *nfreedp += freed; return freed; } int frflush(unit, flags) minor_t unit; int flags; { int flushed = 0, set; if (unit != IPL_LOGIPF) return 0; WRITE_ENTER(&ipf_mutex); bzero((char *)frcache, sizeof(frcache[0]) * 2); set = fr_active; if (flags & FR_INACTIVE) set = 1 - set; if (flags & FR_OUTQUE) { #ifdef USE_INET6 (void) frflushlist(set, unit, &flushed, &ipfilter6[1][set]); (void) frflushlist(set, unit, &flushed, &ipacct6[1][set]); #endif (void) frflushlist(set, unit, &flushed, &ipfilter[1][set]); (void) frflushlist(set, unit, &flushed, &ipacct[1][set]); } if (flags & FR_INQUE) { #ifdef USE_INET6 (void) frflushlist(set, unit, &flushed, &ipfilter6[0][set]); (void) frflushlist(set, unit, &flushed, &ipacct6[0][set]); #endif (void) frflushlist(set, unit, &flushed, &ipfilter[0][set]); (void) frflushlist(set, unit, &flushed, &ipacct[0][set]); } RWLOCK_EXIT(&ipf_mutex); return flushed; } char *memstr(src, dst, slen, dlen) char *src, *dst; int slen, dlen; { char *s = NULL; while (dlen >= slen) { if (bcmp(src, dst, slen) == 0) { s = dst; break; } dst++; dlen--; } return s; } void fixskip(listp, rp, addremove) frentry_t **listp, *rp; int addremove; { frentry_t *fp; int rules = 0, rn = 0; for (fp = *listp; fp && (fp != rp); fp = fp->fr_next, rules++) ; if (!fp) return; for (fp = *listp; fp && (fp != rp); fp = fp->fr_next, rn++) if (fp->fr_skip && (rn + fp->fr_skip >= rules)) fp->fr_skip += addremove; } #ifdef _KERNEL /* * count consecutive 1's in bit mask. If the mask generated by counting * consecutive 1's is different to that passed, return -1, else return # * of bits. */ int countbits(ip) u_32_t ip; { u_32_t ipn; int cnt = 0, i, j; ip = ipn = ntohl(ip); for (i = 32; i; i--, ipn *= 2) if (ipn & 0x80000000) cnt++; else break; ipn = 0; for (i = 32, j = cnt; i; i--, j--) { ipn *= 2; if (j > 0) ipn++; } if (ipn == ip) return cnt; return -1; } /* * return the first IP Address associated with an interface */ int fr_ifpaddr(v, ifptr, inp) int v; void *ifptr; struct in_addr *inp; { # ifdef USE_INET6 struct in6_addr *inp6 = NULL; # endif # if SOLARIS ill_t *ill = ifptr; # else struct ifnet *ifp = ifptr; # endif struct in_addr in; # if SOLARIS # ifdef USE_INET6 if (v == 6) { struct in6_addr in6; /* * First is always link local. */ if (ill->ill_ipif->ipif_next) in6 = ill->ill_ipif->ipif_next->ipif_v6lcl_addr; else bzero((char *)&in6, sizeof(in6)); bcopy((char *)&in6, (char *)inp, sizeof(in6)); } else # endif { in.s_addr = ill->ill_ipif->ipif_local_addr; *inp = in; } # else /* SOLARIS */ # if linux ; # else /* linux */ struct sockaddr_in *sin; struct ifaddr *ifa; # if (__FreeBSD_version >= 300000) ifa = TAILQ_FIRST(&ifp->if_addrhead); # else # if defined(__NetBSD__) || defined(__OpenBSD__) ifa = ifp->if_addrlist.tqh_first; # else # if defined(__sgi) && defined(IFF_DRVRLOCK) /* IRIX 6 */ ifa = &((struct in_ifaddr *)ifp->in_ifaddr)->ia_ifa; # else ifa = ifp->if_addrlist; # endif # endif /* __NetBSD__ || __OpenBSD__ */ # endif /* __FreeBSD_version >= 300000 */ # if (BSD < 199306) && !(/*IRIX6*/defined(__sgi) && defined(IFF_DRVRLOCK)) sin = (struct sockaddr_in *)&ifa->ifa_addr; # else sin = (struct sockaddr_in *)ifa->ifa_addr; while (sin && ifa) { if ((v == 4) && (sin->sin_family == AF_INET)) break; # ifdef USE_INET6 if ((v == 6) && (sin->sin_family == AF_INET6)) { inp6 = &((struct sockaddr_in6 *)sin)->sin6_addr; if (!IN6_IS_ADDR_LINKLOCAL(inp6) && !IN6_IS_ADDR_LOOPBACK(inp6)) break; } # endif # if (__FreeBSD_version >= 300000) ifa = TAILQ_NEXT(ifa, ifa_link); # else # if defined(__NetBSD__) || defined(__OpenBSD__) ifa = ifa->ifa_list.tqe_next; # else ifa = ifa->ifa_next; # endif # endif /* __FreeBSD_version >= 300000 */ if (ifa) sin = (struct sockaddr_in *)ifa->ifa_addr; } if (ifa == NULL) sin = NULL; if (sin == NULL) return -1; # endif /* (BSD < 199306) && (!__sgi && IFF_DRVLOCK) */ # ifdef USE_INET6 if (v == 6) bcopy((char *)inp6, (char *)inp, sizeof(*inp6)); else # endif { in = sin->sin_addr; *inp = in; } # endif /* linux */ # endif /* SOLARIS */ return 0; } static void frsynclist(fr) register frentry_t *fr; { for (; fr; fr = fr->fr_next) { if (fr->fr_ifa != NULL) { fr->fr_ifa = GETUNIT(fr->fr_ifname, fr->fr_ip.fi_v); if (fr->fr_ifa == NULL) fr->fr_ifa = (void *)-1; } if (fr->fr_grp) frsynclist(fr->fr_grp); } } void frsync() { # if !SOLARIS register struct ifnet *ifp; # if defined(__OpenBSD__) || ((NetBSD >= 199511) && (NetBSD < 1991011)) || \ (defined(__FreeBSD_version) && (__FreeBSD_version >= 300000)) # if (NetBSD >= 199905) || defined(__OpenBSD__) for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_list.tqe_next) # else for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_link.tqe_next) # endif # else for (ifp = ifnet; ifp; ifp = ifp->if_next) # endif { ip_natsync(ifp); ip_statesync(ifp); } # endif WRITE_ENTER(&ipf_mutex); frsynclist(ipacct[0][fr_active]); frsynclist(ipacct[1][fr_active]); frsynclist(ipfilter[0][fr_active]); frsynclist(ipfilter[1][fr_active]); #ifdef USE_INET6 frsynclist(ipacct6[0][fr_active]); frsynclist(ipacct6[1][fr_active]); frsynclist(ipfilter6[0][fr_active]); frsynclist(ipfilter6[1][fr_active]); #endif RWLOCK_EXIT(&ipf_mutex); } /* * In the functions below, bcopy() is called because the pointer being * copied _from_ in this instance is a pointer to a char buf (which could * end up being unaligned) and on the kernel's local stack. */ int ircopyptr(a, b, c) void *a, *b; size_t c; { caddr_t ca; int err; #if SOLARIS copyin(a, &ca, sizeof(ca)); #else bcopy(a, &ca, sizeof(ca)); #endif err = copyin(ca, b, c); return err; } int iwcopyptr(a, b, c) void *a, *b; size_t c; { caddr_t ca; int err; #if SOLARIS copyin(b, &ca, sizeof(ca)); #else bcopy(b, &ca, sizeof(ca)); #endif err = copyout(a, ca, c); return err; } #else /* _KERNEL */ /* * return the first IP Address associated with an interface */ int fr_ifpaddr(v, ifptr, inp) int v; void *ifptr; struct in_addr *inp; { return 0; } int ircopyptr(a, b, c) void *a, *b; size_t c; { caddr_t ca; bcopy(a, &ca, sizeof(ca)); bcopy(ca, b, c); return 0; } int iwcopyptr(a, b, c) void *a, *b; size_t c; { caddr_t ca; bcopy(b, &ca, sizeof(ca)); bcopy(a, ca, c); return 0; } #endif int fr_lock(data, lockp) caddr_t data; int *lockp; { int arg, error; error = IRCOPY(data, (caddr_t)&arg, sizeof(arg)); if (!error) { error = IWCOPY((caddr_t)lockp, data, sizeof(*lockp)); if (!error) *lockp = arg; } return error; } void fr_getstat(fiop) friostat_t *fiop; { bcopy((char *)frstats, (char *)fiop->f_st, sizeof(filterstats_t) * 2); fiop->f_locks[0] = fr_state_lock; fiop->f_locks[1] = fr_nat_lock; fiop->f_locks[2] = fr_frag_lock; fiop->f_locks[3] = fr_auth_lock; fiop->f_fin[0] = ipfilter[0][0]; fiop->f_fin[1] = ipfilter[0][1]; fiop->f_fout[0] = ipfilter[1][0]; fiop->f_fout[1] = ipfilter[1][1]; fiop->f_acctin[0] = ipacct[0][0]; fiop->f_acctin[1] = ipacct[0][1]; fiop->f_acctout[0] = ipacct[1][0]; fiop->f_acctout[1] = ipacct[1][1]; #ifdef USE_INET6 fiop->f_fin6[0] = ipfilter6[0][0]; fiop->f_fin6[1] = ipfilter6[0][1]; fiop->f_fout6[0] = ipfilter6[1][0]; fiop->f_fout6[1] = ipfilter6[1][1]; fiop->f_acctin6[0] = ipacct6[0][0]; fiop->f_acctin6[1] = ipacct6[0][1]; fiop->f_acctout6[0] = ipacct6[1][0]; fiop->f_acctout6[1] = ipacct6[1][1]; #endif fiop->f_active = fr_active; fiop->f_froute[0] = ipl_frouteok[0]; fiop->f_froute[1] = ipl_frouteok[1]; fiop->f_running = fr_running; fiop->f_groups[0][0] = ipfgroups[0][0]; fiop->f_groups[0][1] = ipfgroups[0][1]; fiop->f_groups[1][0] = ipfgroups[1][0]; fiop->f_groups[1][1] = ipfgroups[1][1]; fiop->f_groups[2][0] = ipfgroups[2][0]; fiop->f_groups[2][1] = ipfgroups[2][1]; #ifdef IPFILTER_LOG fiop->f_logging = 1; #else fiop->f_logging = 0; #endif fiop->f_defpass = fr_pass; strncpy(fiop->f_version, ipfilter_version, sizeof(fiop->f_version)); } #ifdef USE_INET6 int icmptoicmp6types[ICMP_MAXTYPE+1] = { ICMP6_ECHO_REPLY, /* 0: ICMP_ECHOREPLY */ -1, /* 1: UNUSED */ -1, /* 2: UNUSED */ ICMP6_DST_UNREACH, /* 3: ICMP_UNREACH */ -1, /* 4: ICMP_SOURCEQUENCH */ ND_REDIRECT, /* 5: ICMP_REDIRECT */ -1, /* 6: UNUSED */ -1, /* 7: UNUSED */ ICMP6_ECHO_REQUEST, /* 8: ICMP_ECHO */ -1, /* 9: UNUSED */ -1, /* 10: UNUSED */ ICMP6_TIME_EXCEEDED, /* 11: ICMP_TIMXCEED */ ICMP6_PARAM_PROB, /* 12: ICMP_PARAMPROB */ -1, /* 13: ICMP_TSTAMP */ -1, /* 14: ICMP_TSTAMPREPLY */ -1, /* 15: ICMP_IREQ */ -1, /* 16: ICMP_IREQREPLY */ -1, /* 17: ICMP_MASKREQ */ -1, /* 18: ICMP_MASKREPLY */ }; int icmptoicmp6unreach[ICMP_MAX_UNREACH] = { ICMP6_DST_UNREACH_ADDR, /* 0: ICMP_UNREACH_NET */ ICMP6_DST_UNREACH_ADDR, /* 1: ICMP_UNREACH_HOST */ -1, /* 2: ICMP_UNREACH_PROTOCOL */ ICMP6_DST_UNREACH_NOPORT, /* 3: ICMP_UNREACH_PORT */ -1, /* 4: ICMP_UNREACH_NEEDFRAG */ ICMP6_DST_UNREACH_NOTNEIGHBOR, /* 5: ICMP_UNREACH_SRCFAIL */ ICMP6_DST_UNREACH_ADDR, /* 6: ICMP_UNREACH_NET_UNKNOWN */ ICMP6_DST_UNREACH_ADDR, /* 7: ICMP_UNREACH_HOST_UNKNOWN */ -1, /* 8: ICMP_UNREACH_ISOLATED */ ICMP6_DST_UNREACH_ADMIN, /* 9: ICMP_UNREACH_NET_PROHIB */ ICMP6_DST_UNREACH_ADMIN, /* 10: ICMP_UNREACH_HOST_PROHIB */ -1, /* 11: ICMP_UNREACH_TOSNET */ -1, /* 12: ICMP_UNREACH_TOSHOST */ ICMP6_DST_UNREACH_ADMIN, /* 13: ICMP_UNREACH_ADMIN_PROHIBIT */ }; #endif Index: head/sys/netinet/ip_auth.c =================================================================== --- head/sys/netinet/ip_auth.c (revision 60856) +++ head/sys/netinet/ip_auth.c (revision 60857) @@ -1,550 +1,551 @@ /* * Copyright (C) 1998-2000 by Darren Reed & Guido van Rooij. * * Redistribution and use in source and binary forms are permitted * provided that this notice is preserved and due credit is given * to the original author and the contributors. */ #if !defined(lint) /*static const char rcsid[] = "@(#)$Id: ip_auth.c,v 2.1.2.2 2000/01/16 10:12:14 darrenr Exp $";*/ static const char rcsid[] = "@(#)$FreeBSD$"; #endif #include #include #include #include #include #if !defined(_KERNEL) && !defined(KERNEL) # include # include # include #endif #if (defined(KERNEL) || defined(_KERNEL)) && (__FreeBSD_version >= 220000) # include # include #else # include #endif #include #ifndef linux # include #endif #include #if (defined(_KERNEL) || defined(KERNEL)) && !defined(linux) # include #endif #if !defined(__SVR4) && !defined(__svr4__) # ifndef linux # include # endif #else # include # include # ifdef _KERNEL # include # endif # include # include #endif #if _BSDI_VERSION >= 199802 # include #endif #if defined(__NetBSD__) || defined(__OpenBSD__) || defined(bsdi) # include #endif #include #ifdef sun # include #endif #include #include #include #include #ifndef KERNEL # define KERNEL # define NOT_KERNEL #endif #ifndef linux # include #endif #ifdef NOT_KERNEL # undef KERNEL #endif #ifdef __sgi # ifdef IFF_DRVRLOCK /* IRIX6 */ # include # endif #endif #include #if defined(__sgi) && !defined(IFF_DRVRLOCK) /* IRIX < 6 */ extern struct ifqueue ipintrq; /* ip packet input queue */ #else # ifndef linux # if __FreeBSD_version >= 300000 # include # endif # include # include # endif #endif #include #include #include "netinet/ip_compat.h" #include #include "netinet/ip_fil.h" #include "netinet/ip_auth.h" #if !SOLARIS && !defined(linux) # include # ifdef __FreeBSD__ # include # endif #endif #if (__FreeBSD_version >= 300000) # include # if (defined(_KERNEL) || defined(KERNEL)) && !defined(IPFILTER_LKM) # include # include # endif #endif #if (SOLARIS || defined(__sgi)) && defined(_KERNEL) extern KRWLOCK_T ipf_auth; extern kmutex_t ipf_authmx; # if SOLARIS extern kcondvar_t ipfauthwait; # endif #endif #ifdef linux static struct wait_queue *ipfauthwait = NULL; #endif int fr_authsize = FR_NUMAUTH; int fr_authused = 0; int fr_defaultauthage = 600; int fr_auth_lock = 0; fr_authstat_t fr_authstats; static frauth_t fr_auth[FR_NUMAUTH]; mb_t *fr_authpkts[FR_NUMAUTH]; static int fr_authstart = 0, fr_authend = 0, fr_authnext = 0; static frauthent_t *fae_list = NULL; frentry_t *ipauth = NULL; /* * Check if a packet has authorization. If the packet is found to match an * authorization result and that would result in a feedback loop (i.e. it * will end up returning FR_AUTH) then return FR_BLOCK instead. */ u_32_t fr_checkauth(ip, fin) ip_t *ip; fr_info_t *fin; { u_short id = ip->ip_id; u_32_t pass; int i; if (fr_auth_lock) return 0; READ_ENTER(&ipf_auth); for (i = fr_authstart; i != fr_authend; ) { /* * index becomes -2 only after an SIOCAUTHW. Check this in * case the same packet gets sent again and it hasn't yet been * auth'd. */ if ((fr_auth[i].fra_index == -2) && (id == fr_auth[i].fra_info.fin_id) && !bcmp((char *)fin,(char *)&fr_auth[i].fra_info,FI_CSIZE)) { /* * Avoid feedback loop. */ if (!(pass = fr_auth[i].fra_pass) || (pass & FR_AUTH)) pass = FR_BLOCK; RWLOCK_EXIT(&ipf_auth); WRITE_ENTER(&ipf_auth); fr_authstats.fas_hits++; fr_auth[i].fra_index = -1; fr_authused--; if (i == fr_authstart) { while (fr_auth[i].fra_index == -1) { i++; if (i == FR_NUMAUTH) i = 0; fr_authstart = i; if (i == fr_authend) break; } if (fr_authstart == fr_authend) { fr_authnext = 0; fr_authstart = fr_authend = 0; } } RWLOCK_EXIT(&ipf_auth); return pass; } i++; if (i == FR_NUMAUTH) i = 0; } fr_authstats.fas_miss++; RWLOCK_EXIT(&ipf_auth); return 0; } /* * Check if we have room in the auth array to hold details for another packet. * If we do, store it and wake up any user programs which are waiting to * hear about these events. */ int fr_newauth(m, fin, ip) mb_t *m; fr_info_t *fin; ip_t *ip; { #if defined(_KERNEL) && SOLARIS qif_t *qif = fin->fin_qif; #endif int i; if (fr_auth_lock) return 0; WRITE_ENTER(&ipf_auth); if (fr_authstart > fr_authend) { fr_authstats.fas_nospace++; RWLOCK_EXIT(&ipf_auth); return 0; } else { if ((fr_authstart == 0) && (fr_authend == FR_NUMAUTH - 1)) { fr_authstats.fas_nospace++; RWLOCK_EXIT(&ipf_auth); return 0; } } fr_authstats.fas_added++; fr_authused++; i = fr_authend++; if (fr_authend == FR_NUMAUTH) fr_authend = 0; RWLOCK_EXIT(&ipf_auth); fr_auth[i].fra_index = i; fr_auth[i].fra_pass = 0; fr_auth[i].fra_age = fr_defaultauthage; bcopy((char *)fin, (char *)&fr_auth[i].fra_info, sizeof(*fin)); #if !defined(sparc) && !defined(m68k) /* * No need to copyback here as we want to undo the changes, not keep * them. */ # if SOLARIS && defined(_KERNEL) if ((ip == (ip_t *)m->b_rptr) && (ip->ip_v == 4)) # endif { register u_short bo; bo = ip->ip_len; ip->ip_len = htons(bo); # if !SOLARIS && !defined(__NetBSD__) /* 4.4BSD converts this ip_input.c, but I don't in solaris.c */ bo = ip->ip_id; ip->ip_id = htons(bo); # endif bo = ip->ip_off; ip->ip_off = htons(bo); } #endif #if SOLARIS && defined(_KERNEL) m->b_rptr -= qif->qf_off; fr_authpkts[i] = *(mblk_t **)fin->fin_mp; fr_auth[i].fra_q = qif->qf_q; cv_signal(&ipfauthwait); #else fr_authpkts[i] = m; # if defined(linux) && defined(_KERNEL) wake_up_interruptible(&ipfauthwait); # else WAKEUP(&fr_authnext); # endif #endif return 1; } int fr_auth_ioctl(data, cmd, fr, frptr) caddr_t data; #if defined(__NetBSD__) || defined(__OpenBSD__) || (FreeBSD_version >= 300003) u_long cmd; #else int cmd; #endif frentry_t *fr, **frptr; { mb_t *m; #if defined(_KERNEL) && !SOLARIS struct ifqueue *ifq; #endif frauth_t auth, *au = &auth; frauthent_t *fae, **faep; int i, error = 0; switch (cmd) { case SIOCSTLCK : error = fr_lock(data, &fr_auth_lock); break; case SIOCINIFR : case SIOCRMIFR : case SIOCADIFR : error = EINVAL; break; case SIOCINAFR : error = EINVAL; break; case SIOCRMAFR : case SIOCADAFR : for (faep = &fae_list; (fae = *faep); ) if (&fae->fae_fr == fr) break; else faep = &fae->fae_next; if (cmd == SIOCRMAFR) { if (!fae) error = ESRCH; else { WRITE_ENTER(&ipf_auth); *faep = fae->fae_next; *frptr = fr->fr_next; RWLOCK_EXIT(&ipf_auth); KFREE(fae); } } else { KMALLOC(fae, frauthent_t *); if (fae != NULL) { bcopy((char *)fr, (char *)&fae->fae_fr, sizeof(*fr)); WRITE_ENTER(&ipf_auth); fae->fae_age = fr_defaultauthage; fae->fae_fr.fr_hits = 0; fae->fae_fr.fr_next = *frptr; *frptr = &fae->fae_fr; fae->fae_next = *faep; *faep = fae; ipauth = &fae_list->fae_fr; RWLOCK_EXIT(&ipf_auth); } else error = ENOMEM; } break; case SIOCATHST: READ_ENTER(&ipf_auth); fr_authstats.fas_faelist = fae_list; RWLOCK_EXIT(&ipf_auth); error = IWCOPYPTR((char *)&fr_authstats, data, sizeof(fr_authstats)); break; case SIOCAUTHW: fr_authioctlloop: READ_ENTER(&ipf_auth); if ((fr_authnext != fr_authend) && fr_authpkts[fr_authnext]) { error = IWCOPYPTR((char *)&fr_auth[fr_authnext], data, sizeof(fr_info_t)); RWLOCK_EXIT(&ipf_auth); if (error) break; WRITE_ENTER(&ipf_auth); fr_authnext++; if (fr_authnext == FR_NUMAUTH) fr_authnext = 0; RWLOCK_EXIT(&ipf_auth); return 0; } #ifdef _KERNEL # if SOLARIS mutex_enter(&ipf_authmx); if (!cv_wait_sig(&ipfauthwait, &ipf_authmx)) { mutex_exit(&ipf_authmx); return EINTR; } mutex_exit(&ipf_authmx); # else # ifdef linux interruptible_sleep_on(&ipfauthwait); if (current->signal & ~current->blocked) error = -EINTR; # else error = SLEEP(&fr_authnext, "fr_authnext"); # endif # endif #endif RWLOCK_EXIT(&ipf_auth); if (!error) goto fr_authioctlloop; break; case SIOCAUTHR: error = IRCOPYPTR(data, (caddr_t)&auth, sizeof(auth)); if (error) return error; WRITE_ENTER(&ipf_auth); + i = au->fra_index; if ((i < 0) || (i > FR_NUMAUTH) || (fr_auth[i].fra_info.fin_id != au->fra_info.fin_id)) { RWLOCK_EXIT(&ipf_auth); return EINVAL; } m = fr_authpkts[i]; fr_auth[i].fra_index = -2; fr_auth[i].fra_pass = au->fra_pass; fr_authpkts[i] = NULL; #ifdef _KERNEL RWLOCK_EXIT(&ipf_auth); # ifndef linux if (m && au->fra_info.fin_out) { # if SOLARIS error = fr_qout(fr_auth[i].fra_q, m); # else /* SOLARIS */ # if (_BSDI_VERSION >= 199802) || defined(__OpenBSD__) error = ip_output(m, NULL, NULL, IP_FORWARDING, NULL, NULL); # else error = ip_output(m, NULL, NULL, IP_FORWARDING, NULL); # endif # endif /* SOLARIS */ if (error) fr_authstats.fas_sendfail++; else fr_authstats.fas_sendok++; } else if (m) { # if SOLARIS error = fr_qin(fr_auth[i].fra_q, m); # else /* SOLARIS */ ifq = &ipintrq; if (IF_QFULL(ifq)) { IF_DROP(ifq); m_freem(m); error = ENOBUFS; } else { IF_ENQUEUE(ifq, m); schednetisr(NETISR_IP); } # endif /* SOLARIS */ if (error) fr_authstats.fas_quefail++; else fr_authstats.fas_queok++; } else error = EINVAL; # endif # if SOLARIS if (error) error = EINVAL; # else /* * If we experience an error which will result in the packet * not being processed, make sure we advance to the next one. */ if (error == ENOBUFS) { fr_authused--; fr_auth[i].fra_index = -1; fr_auth[i].fra_pass = 0; if (i == fr_authstart) { while (fr_auth[i].fra_index == -1) { i++; if (i == FR_NUMAUTH) i = 0; fr_authstart = i; if (i == fr_authend) break; } if (fr_authstart == fr_authend) { fr_authnext = 0; fr_authstart = fr_authend = 0; } } } # endif #endif /* _KERNEL */ break; default : error = EINVAL; break; } return error; } #ifdef _KERNEL /* * Free all network buffer memory used to keep saved packets. */ void fr_authunload() { register int i; register frauthent_t *fae, **faep; mb_t *m; WRITE_ENTER(&ipf_auth); for (i = 0; i < FR_NUMAUTH; i++) { if ((m = fr_authpkts[i])) { FREE_MB_T(m); fr_authpkts[i] = NULL; fr_auth[i].fra_index = -1; } } for (faep = &fae_list; (fae = *faep); ) { *faep = fae->fae_next; KFREE(fae); } ipauth = NULL; RWLOCK_EXIT(&ipf_auth); } /* * Slowly expire held auth records. Timeouts are set * in expectation of this being called twice per second. */ void fr_authexpire() { register int i; register frauth_t *fra; register frauthent_t *fae, **faep; mb_t *m; #if !SOLARIS int s; #endif if (fr_auth_lock) return; SPL_NET(s); WRITE_ENTER(&ipf_auth); for (i = 0, fra = fr_auth; i < FR_NUMAUTH; i++, fra++) { if ((!--fra->fra_age) && (m = fr_authpkts[i])) { FREE_MB_T(m); fr_authpkts[i] = NULL; fr_auth[i].fra_index = -1; fr_authstats.fas_expire++; fr_authused--; } } for (faep = &fae_list; (fae = *faep); ) { if (!--fae->fae_age) { *faep = fae->fae_next; KFREE(fae); fr_authstats.fas_expire++; } else faep = &fae->fae_next; } ipauth = &fae_list->fae_fr; RWLOCK_EXIT(&ipf_auth); SPL_X(s); } #endif Index: head/sys/netinet/ip_compat.h =================================================================== --- head/sys/netinet/ip_compat.h (revision 60856) +++ head/sys/netinet/ip_compat.h (revision 60857) @@ -1,837 +1,987 @@ /* - * Copyright (C) 1993-1998 by Darren Reed. + * Copyright (C) 1993-2000 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.1.2.3 1999/11/18 13:55:26 darrenr Exp $ * $FreeBSD$ */ #ifndef __IP_COMPAT_H__ #define __IP_COMPAT_H__ #ifndef __P # ifdef __STDC__ # define __P(x) x # else # define __P(x) () # endif #endif #ifndef __STDC__ # undef const # define const #endif #ifndef SOLARIS #define SOLARIS (defined(sun) && (defined(__svr4__) || defined(__SVR4))) #endif +#if SOLARIS2 >= 8 +# ifndef USE_INET6 +# define USE_INET6 +# endif +#endif #if defined(_KERNEL) || defined(KERNEL) || defined(__KERNEL__) # undef KERNEL # undef _KERNEL # undef __KERNEL__ # define KERNEL # define _KERNEL # define __KERNEL__ #endif #if defined(__SVR4) || defined(__svr4__) || defined(__sgi) #define index strchr # if !defined(KERNEL) # define bzero(a,b) memset(a,0,b) # define bcmp memcmp # define bcopy(a,b,c) memmove(b,a,c) # endif #endif #ifndef offsetof #define offsetof(t,m) (int)((&((t *)0L)->m)) #endif #if defined(__sgi) || defined(bsdi) struct ether_addr { u_char ether_addr_octet[6]; }; #endif #if defined(__sgi) && !defined(IPFILTER_LKM) # ifdef __STDC__ # define IPL_EXTERN(ep) ipfilter##ep # else # define IPL_EXTERN(ep) ipfilter/**/ep # endif #else # ifdef __STDC__ # define IPL_EXTERN(ep) ipl##ep # else # define IPL_EXTERN(ep) ipl/**/ep # endif #endif #ifdef linux # include #endif #if SOLARIS # define MTYPE(m) ((m)->b_datap->db_type) # include # include # include # include /* * because Solaris 2 defines these in two places :-/ */ # undef IPOPT_EOL # undef IPOPT_NOP # undef IPOPT_LSRR # undef IPOPT_RR # undef IPOPT_SSRR # ifndef KERNEL # define _KERNEL # undef RES_INIT +# if SOLARIS2 >= 8 +# include +# endif # include # include # include # undef _KERNEL # else /* _KERNEL */ +# if SOLARIS2 >= 8 +# include +# endif # include # include # include # endif /* _KERNEL */ # if SOLARIS2 >= 8 +# include # include -# include # define ipif_local_addr ipif_lcl_addr +/* Only defined in private include file */ +# ifndef V4_PART_OF_V6 +# define V4_PART_OF_V6(v6) v6.s6_addr32[3] +# endif # endif #else # if !defined(__sgi) typedef int minor_t; #endif #endif /* SOLARIS */ #define IPMINLEN(i, h) ((i)->ip_len >= ((i)->ip_hl * 4 + sizeof(struct h))) #ifndef IP_OFFMASK #define IP_OFFMASK 0x1fff #endif #if BSD > 199306 # define USE_QUAD_T # define U_QUAD_T u_quad_t # define QUAD_T quad_t #else /* BSD > 199306 */ # define U_QUAD_T u_long # define QUAD_T long #endif /* BSD > 199306 */ + /* * These operating systems already take care of the problem for us. */ #if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__) || \ defined(__sgi) typedef u_int32_t u_32_t; +# if defined(_KERNEL) && !defined(IPFILTER_LKM) +# if defined(__NetBSD_Version__) && (__NetBSD_Version__ >= 104110000) +# include "opt_inet.h" +# endif +# if defined(__FreeBSD_version) && (__FreeBSD_version >= 400000) && \ + !defined(KLD_MODULE) +# include "opt_inet6.h" +# endif +# ifdef INET6 +# define USE_INET6 +# endif +# endif #else /* * Really, any arch where sizeof(long) != sizeof(int). */ # if defined(__alpha__) || defined(__alpha) || defined(_LP64) typedef unsigned int u_32_t; # else -typedef unsigned long u_32_t; +# if SOLARIS2 >= 6 +typedef uint32_t u_32_t; +# else +typedef unsigned int u_32_t; +# endif # endif #endif /* __NetBSD__ || __OpenBSD__ || __FreeBSD__ || __sgi */ +#ifdef USE_INET6 +# if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__) +# include +# ifdef _KERNEL +# include +# endif +typedef struct ip6_hdr ip6_t; +# endif +union i6addr { + u_32_t i6[4]; + struct in_addr in4; + struct in6_addr in6; +}; +#else +union i6addr { + u_32_t i6[4]; + struct in_addr in4; +}; +#endif + +#define IP6CMP(a,b) bcmp((char *)&(a), (char *)&(b), sizeof(a)) +#define IP6EQ(a,b) (bcmp((char *)&(a), (char *)&(b), sizeof(a)) == 0) +#define IP6NEQ(a,b) (bcmp((char *)&(a), (char *)&(b), sizeof(a)) != 0) + #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 */ -#if defined(__FreeBSD__) && defined(KERNEL) +#if defined(__FreeBSD__) && (defined(KERNEL) || defined(_KERNEL)) # if __FreeBSD__ < 3 # include +# else +# if __FreeBSD__ == 3 +# if defined(IPFILTER_LKM) && !defined(ACTUALLY_LKM_NOT_KERNEL) +# define ACTUALLY_LKM_NOT_KERNEL +# endif +# endif # endif -# if defined(IPFILTER_LKM) && !defined(ACTUALLY_LKM_NOT_KERNEL) -# define ACTUALLY_LKM_NOT_KERNEL -# endif #endif /* __FreeBSD__ && KERNEL */ /* * Build some macros and #defines to enable the same code to compile anywhere * Well, that's the idea, anyway :-) */ +#if !SOLARIS || (SOLARIS2 < 6) || !defined(KERNEL) +# define ATOMIC_INCL ATOMIC_INC +# define ATOMIC_INC64 ATOMIC_INC +# define ATOMIC_INC32 ATOMIC_INC +# define ATOMIC_INC16 ATOMIC_INC +# define ATOMIC_DECL ATOMIC_DEC +# define ATOMIC_DEC64 ATOMIC_DEC +# define ATOMIC_DEC32 ATOMIC_DEC +# define ATOMIC_DEC16 ATOMIC_DEC +#endif #ifdef KERNEL # if SOLARIS -# define ATOMIC_INC(x) { mutex_enter(&ipf_rw); (x)++; \ +# if SOLARIS2 >= 6 +# include +# if SOLARIS2 == 6 +# define ATOMIC_INCL(x) atomic_add_long((uint32_t*)&(x), 1) +# define ATOMIC_DECL(x) atomic_add_long((uint32_t*)&(x), -1) +# else +# define ATOMIC_INCL(x) atomic_add_long(&(x), 1) +# define ATOMIC_DECL(x) atomic_add_long(&(x), -1) +# endif +# define ATOMIC_INC64(x) atomic_add_64((uint64_t*)&(x), 1) +# define ATOMIC_INC32(x) atomic_add_32((uint32_t*)&(x), 1) +# define ATOMIC_INC16(x) atomic_add_16((uint16_t*)&(x), 1) +# define ATOMIC_DEC64(x) atomic_add_64((uint64_t*)&(x), -1) +# define ATOMIC_DEC32(x) atomic_add_32((uint32_t*)&(x), -1) +# define ATOMIC_DEC16(x) atomic_add_16((uint16_t*)&(x), -1) +# else +# define ATOMIC_INC(x) { mutex_enter(&ipf_rw); (x)++; \ mutex_exit(&ipf_rw); } -# define ATOMIC_DEC(x) { mutex_enter(&ipf_rw); (x)--; \ +# define ATOMIC_DEC(x) { mutex_enter(&ipf_rw); (x)--; \ mutex_exit(&ipf_rw); } +# endif # define MUTEX_ENTER(x) mutex_enter(x) # if 1 # define KRWLOCK_T krwlock_t # define READ_ENTER(x) rw_enter(x, RW_READER) # define WRITE_ENTER(x) rw_enter(x, RW_WRITER) # define RW_UPGRADE(x) { if (rw_tryupgrade(x) == 0) { \ rw_exit(x); \ rw_enter(x, RW_WRITER); } \ } # define MUTEX_DOWNGRADE(x) rw_downgrade(x) # define RWLOCK_INIT(x, y, z) rw_init((x), (y), RW_DRIVER, (z)) # define RWLOCK_EXIT(x) rw_exit(x) # define RW_DESTROY(x) rw_destroy(x) # else # define KRWLOCK_T kmutex_t # define READ_ENTER(x) mutex_enter(x) # define WRITE_ENTER(x) mutex_enter(x) # define MUTEX_DOWNGRADE(x) ; # define RWLOCK_INIT(x, y, z) mutex_init((x), (y), MUTEX_DRIVER, (z)) # define RWLOCK_EXIT(x) mutex_exit(x) # define RW_DESTROY(x) mutex_destroy(x) # endif +# define MUTEX_INIT(x, y, z) mutex_init((x), (y), MUTEX_DRIVER, (z)) +# define MUTEX_DESTROY(x) mutex_destroy(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)) +# define IRCOPYPTR ircopyptr +# define IWCOPYPTR iwcopyptr # define FREE_MB_T(m) freemsg(m) # define SPL_NET(x) ; # define SPL_IMP(x) ; # undef SPL_X # define SPL_X(x) ; # ifdef sparc # define ntohs(x) (x) # define ntohl(x) (x) # define htons(x) (x) # define htonl(x) (x) # endif /* sparc */ # define KMALLOC(a,b) (a) = (b)kmem_alloc(sizeof(*(a)), KM_NOSLEEP) # define KMALLOCS(a,b,c) (a) = (b)kmem_alloc((c), KM_NOSLEEP) # define GET_MINOR(x) getminor(x) typedef struct qif { struct qif *qf_next; ill_t *qf_ill; kmutex_t qf_lock; void *qf_iptr; void *qf_optr; queue_t *qf_in; queue_t *qf_out; struct qinit *qf_wqinfo; struct qinit *qf_rqinfo; struct qinit qf_wqinit; struct qinit qf_rqinit; mblk_t *qf_m; /* These three fields are for passing data up from */ queue_t *qf_q; /* fr_qin and fr_qout to the packet processing. */ size_t qf_off; size_t qf_len; /* this field is used for in ipfr_fastroute */ char qf_name[8]; /* * in case the ILL has disappeared... */ size_t qf_hl; /* header length */ + int qf_sap; } qif_t; -extern ill_t *get_unit __P((char *)); -# define GETUNIT(n) get_unit((n)) +extern ill_t *get_unit __P((char *, int)); +# define GETUNIT(n, v) get_unit(n, v) +# define IFNAME(x) ((ill_t *)x)->ill_name # else /* SOLARIS */ # if defined(__sgi) # define hz HZ # include # define IPF_LOCK_PL plhi # include #undef kmutex_t typedef struct { lock_t *l; int pl; } kmutex_t; # define ATOMIC_INC(x) { MUTEX_ENTER(&ipf_rw); \ (x)++; MUTEX_EXIT(&ipf_rw); } # define ATOMIC_DEC(x) { MUTEX_ENTER(&ipf_rw); \ (x)--; MUTEX_EXIT(&ipf_rw); } # define MUTEX_ENTER(x) (x)->pl = LOCK((x)->l, IPF_LOCK_PL); # define KRWLOCK_T kmutex_t # define READ_ENTER(x) MUTEX_ENTER(x) # define WRITE_ENTER(x) MUTEX_ENTER(x) # define RW_UPGRADE(x) ; # define MUTEX_DOWNGRADE(x) ; -# define RWLOCK_EXIT(x) MUTEX_EXIT(x) -# define MUTEX_EXIT(x) UNLOCK((x)->l, (x)->pl); +# define RWLOCK_EXIT(x) MUTEX_EXIT(x) +# define MUTEX_EXIT(x) UNLOCK((x)->l, (x)->pl); +# define MUTEX_INIT(x,y,z) (x).l = LOCK_ALLOC((uchar_t)-1, IPF_LOCK_PL, (lkinfo_t *)-1, KM_NOSLEEP) +# define MUTEX_DESTROY(x) LOCK_DEALLOC((x).l) # else /* __sgi */ # define ATOMIC_INC(x) (x)++ # define ATOMIC_DEC(x) (x)-- # define MUTEX_ENTER(x) ; -# define READ_ENTER(x) ; -# define WRITE_ENTER(x) ; -# define RW_UPGRADE(x) ; +# define READ_ENTER(x) ; +# define WRITE_ENTER(x) ; +# define RW_UPGRADE(x) ; # define MUTEX_DOWNGRADE(x) ; -# define RWLOCK_EXIT(x) ; -# define MUTEX_EXIT(x) ; +# define RWLOCK_EXIT(x) ; +# define MUTEX_EXIT(x) ; +# define MUTEX_INIT(x,y,z) ; +# define MUTEX_DESTROY(x) ; # endif /* __sgi */ # ifndef linux # define FREE_MB_T(m) m_freem(m) # define MTOD(m,t) mtod(m,t) -# define IRCOPY(a,b,c) bcopy((a), (b), (c)) -# define IWCOPY(a,b,c) bcopy((a), (b), (c)) +# define IRCOPY(a,b,c) (bcopy((a), (b), (c)), 0) +# define IWCOPY(a,b,c) (bcopy((a), (b), (c)), 0) +# define IRCOPYPTR ircopyptr +# define IWCOPYPTR iwcopyptr # endif /* !linux */ # endif /* SOLARIS */ # ifdef sun # if !SOLARIS # include -# define GETUNIT(n) ifunit((n), IFNAMSIZ) +# define GETUNIT(n, v) ifunit(n, IFNAMSIZ) +# define IFNAME(x) ((struct ifnet *)x)->if_name # endif # else # ifndef linux -# define GETUNIT(n) ifunit((n)) +# define GETUNIT(n, v) ifunit(n) +# if (defined(NetBSD) && (NetBSD <= 1991011) && (NetBSD >= 199606)) || \ + (defined(OpenBSD) && (OpenBSD >= 199603)) +# define IFNAME(x) ((struct ifnet *)x)->if_xname +# else +# define IFNAME(x) ((struct ifnet *)x)->if_name +# endif # endif # endif /* sun */ # if defined(sun) && !defined(linux) || defined(__sgi) # define UIOMOVE(a,b,c,d) uiomove((caddr_t)a,b,c,d) # define SLEEP(id, n) sleep((id), PZERO+1) # define WAKEUP(id) wakeup(id) # define KFREE(x) kmem_free((char *)(x), sizeof(*(x))) # define KFREES(x,s) kmem_free((char *)(x), (s)) # if !SOLARIS extern void m_copydata __P((struct mbuf *, int, int, caddr_t)); extern void m_copyback __P((struct mbuf *, int, int, caddr_t)); # endif # ifdef __sgi # include # include # define KMALLOC(a,b) (a) = (b)kmem_alloc(sizeof(*(a)), KM_NOSLEEP) # define KMALLOCS(a,b,c) (a) = (b)kmem_alloc((c), KM_NOSLEEP) # define GET_MINOR(x) getminor(x) # else # if !SOLARIS # define KMALLOC(a,b) (a) = (b)new_kmem_alloc(sizeof(*(a)), \ KMEM_NOSLEEP) # define KMALLOCS(a,b,c) (a) = (b)new_kmem_alloc((c), KMEM_NOSLEEP) # endif /* SOLARIS */ # endif /* __sgi */ # endif /* sun && !linux */ # ifndef GET_MINOR # define GET_MINOR(x) minor(x) # endif # if (BSD >= 199306) || defined(__FreeBSD__) # include # if !defined(__FreeBSD__) || (defined (__FreeBSD__) && __FreeBSD__>=3) # include # include extern vm_map_t kmem_map; # else /* !__FreeBSD__ || (__FreeBSD__ && __FreeBSD__>=3) */ # include # endif /* !__FreeBSD__ || (__FreeBSD__ && __FreeBSD__>=3) */ # ifdef M_PFIL # define KMALLOC(a, b) MALLOC((a), b, sizeof(*(a)), M_PFIL, M_NOWAIT) # define KMALLOCS(a, b, c) MALLOC((a), b, (c), M_PFIL, M_NOWAIT) # define KFREE(x) FREE((x), M_PFIL) # define KFREES(x,s) FREE((x), M_PFIL) # else # define KMALLOC(a, b) MALLOC((a), b, sizeof(*(a)), M_TEMP, M_NOWAIT) # define KMALLOCS(a, b, c) MALLOC((a), b, (c), M_TEMP, M_NOWAIT) # define KFREE(x) FREE((x), M_TEMP) # define KFREES(x,s) FREE((x), M_TEMP) # endif /* M_PFIL */ # define UIOMOVE(a,b,c,d) uiomove(a,b,d) # define SLEEP(id, n) tsleep((id), PPAUSE|PCATCH, n, 0) # define WAKEUP(id) wakeup(id) # endif /* BSD */ # if defined(NetBSD) && NetBSD <= 1991011 && NetBSD >= 199407 # define SPL_NET(x) x = splsoftnet() # define SPL_X(x) (void) splx(x) # else # if !SOLARIS && !defined(linux) # define SPL_IMP(x) x = splimp() # define SPL_NET(x) x = splnet() # define SPL_X(x) (void) splx(x) # endif # endif /* NetBSD && NetBSD <= 1991011 && NetBSD >= 199407 */ # define PANIC(x,y) if (x) panic y #else /* KERNEL */ # define SLEEP(x,y) ; # define WAKEUP(x) ; # define PANIC(x,y) ; # define ATOMIC_INC(x) (x)++ # define ATOMIC_DEC(x) (x)-- # define MUTEX_ENTER(x) ; # define READ_ENTER(x) ; +# define MUTEX_INIT(x,y,z) ; +# define MUTEX_DESTROY(x) ; # define WRITE_ENTER(x) ; # define RW_UPGRADE(x) ; # define MUTEX_DOWNGRADE(x) ; # define RWLOCK_EXIT(x) ; # define MUTEX_EXIT(x) ; # define SPL_NET(x) ; # define SPL_IMP(x) ; # undef SPL_X # define SPL_X(x) ; # define KMALLOC(a,b) (a) = (b)malloc(sizeof(*a)) # define KMALLOCS(a,b,c) (a) = (b)malloc(c) # define KFREE(x) free(x) # define KFREES(x,s) free(x) -# define GETUNIT(x) get_unit(x) -# define IRCOPY(a,b,c) bcopy((a), (b), (c)) -# define IWCOPY(a,b,c) bcopy((a), (b), (c)) +# define GETUNIT(x, v) get_unit(x,v) +# define IRCOPY(a,b,c) (bcopy((a), (b), (c)), 0) +# define IWCOPY(a,b,c) (bcopy((a), (b), (c)), 0) +# define IRCOPYPTR ircopyptr +# define IWCOPYPTR iwcopyptr #endif /* KERNEL */ #if SOLARIS typedef mblk_t mb_t; # if SOLARIS2 >= 7 # ifdef lint # define ALIGN32(ptr) (ptr ? 0L : 0L) # define ALIGN16(ptr) (ptr ? 0L : 0L) # else # define ALIGN32(ptr) (ptr) # define ALIGN16(ptr) (ptr) # endif # endif #else # ifdef linux # ifndef kernel typedef struct mb { struct mb *next; u_int len; u_char *data; } mb_t; # else typedef struct sk_buff mb_t; # endif # else typedef struct mbuf mb_t; # endif #endif /* SOLARIS */ #if defined(linux) || defined(__sgi) /* * These #ifdef's are here mainly for linux, but who knows, they may * not be in other places or maybe one day linux will grow up and some * of these will turn up there too. */ #ifndef ICMP_MINLEN # define ICMP_MINLEN 8 #endif #ifndef ICMP_UNREACH # define ICMP_UNREACH ICMP_DEST_UNREACH #endif #ifndef ICMP_SOURCEQUENCH # define ICMP_SOURCEQUENCH ICMP_SOURCE_QUENCH #endif #ifndef ICMP_TIMXCEED # define ICMP_TIMXCEED ICMP_TIME_EXCEEDED #endif #ifndef ICMP_PARAMPROB # define ICMP_PARAMPROB ICMP_PARAMETERPROB #endif #ifndef ICMP_TSTAMP # define ICMP_TSTAMP ICMP_TIMESTAMP #endif #ifndef ICMP_TSTAMPREPLY # define ICMP_TSTAMPREPLY ICMP_TIMESTAMPREPLY #endif #ifndef ICMP_IREQ # define ICMP_IREQ ICMP_INFO_REQUEST #endif #ifndef ICMP_IREQREPLY # define ICMP_IREQREPLY ICMP_INFO_REPLY #endif #ifndef ICMP_MASKREQ # define ICMP_MASKREQ ICMP_ADDRESS #endif #ifndef ICMP_MASKREPLY # define ICMP_MASKREPLY ICMP_ADDRESSREPLY #endif #ifndef IPVERSION # define IPVERSION 4 #endif #ifndef IPOPT_MINOFF # define IPOPT_MINOFF 4 #endif #ifndef IPOPT_COPIED # define IPOPT_COPIED(x) ((x)&0x80) #endif #ifndef IPOPT_EOL # define IPOPT_EOL 0 #endif #ifndef IPOPT_NOP # define IPOPT_NOP 1 #endif #ifndef IP_MF # define IP_MF ((u_short)0x2000) #endif #ifndef ETHERTYPE_IP # define ETHERTYPE_IP ((u_short)0x0800) #endif #ifndef TH_FIN # define TH_FIN 0x01 #endif #ifndef TH_SYN # define TH_SYN 0x02 #endif #ifndef TH_RST # define TH_RST 0x04 #endif #ifndef TH_PUSH # define TH_PUSH 0x08 #endif #ifndef TH_ACK # define TH_ACK 0x10 #endif #ifndef TH_URG # define TH_URG 0x20 #endif #ifndef IPOPT_EOL # define IPOPT_EOL 0 #endif #ifndef IPOPT_NOP # define IPOPT_NOP 1 #endif #ifndef IPOPT_RR # define IPOPT_RR 7 #endif #ifndef IPOPT_TS # define IPOPT_TS 68 #endif #ifndef IPOPT_SECURITY # define IPOPT_SECURITY 130 #endif #ifndef IPOPT_LSRR # define IPOPT_LSRR 131 #endif #ifndef IPOPT_SATID # define IPOPT_SATID 136 #endif #ifndef IPOPT_SSRR # define IPOPT_SSRR 137 #endif #ifndef IPOPT_SECUR_UNCLASS # define IPOPT_SECUR_UNCLASS ((u_short)0x0000) #endif #ifndef IPOPT_SECUR_CONFID # define IPOPT_SECUR_CONFID ((u_short)0xf135) #endif #ifndef IPOPT_SECUR_EFTO # define IPOPT_SECUR_EFTO ((u_short)0x789a) #endif #ifndef IPOPT_SECUR_MMMM # define IPOPT_SECUR_MMMM ((u_short)0xbc4d) #endif #ifndef IPOPT_SECUR_RESTR # define IPOPT_SECUR_RESTR ((u_short)0xaf13) #endif #ifndef IPOPT_SECUR_SECRET # define IPOPT_SECUR_SECRET ((u_short)0xd788) #endif #ifndef IPOPT_SECUR_TOPSECRET # define IPOPT_SECUR_TOPSECRET ((u_short)0x6bc5) #endif #ifndef IPOPT_OLEN # define IPOPT_OLEN 1 #endif #endif /* linux || __sgi */ #ifdef linux #include /* * TCP States */ #define TCPS_CLOSED 0 /* closed */ #define TCPS_LISTEN 1 /* listening for connection */ #define TCPS_SYN_SENT 2 /* active, have sent syn */ #define TCPS_SYN_RECEIVED 3 /* have send and received syn */ /* states < TCPS_ESTABLISHED are those where connections not established */ #define TCPS_ESTABLISHED 4 /* established */ #define TCPS_CLOSE_WAIT 5 /* rcvd fin, waiting for close */ /* states > TCPS_CLOSE_WAIT are those where user has closed */ #define TCPS_FIN_WAIT_1 6 /* have closed, sent fin */ #define TCPS_CLOSING 7 /* closed xchd FIN; await FIN ACK */ #define TCPS_LAST_ACK 8 /* had fin and close; await FIN ACK */ /* states > TCPS_CLOSE_WAIT && < TCPS_FIN_WAIT_2 await ACK of FIN */ #define TCPS_FIN_WAIT_2 9 /* have closed, fin is acked */ #define TCPS_TIME_WAIT 10 /* in 2*msl quiet wait after close */ /* * file flags. */ #ifdef WRITE #define FWRITE WRITE #define FREAD READ #else #define FWRITE _IOC_WRITE #define FREAD _IOC_READ #endif /* * mbuf related problems. */ #define mtod(m,t) (t)((m)->data) #define m_len len #define m_next next #ifdef IP_DF #undef IP_DF #endif #define IP_DF 0x4000 typedef struct { __u16 th_sport; __u16 th_dport; __u32 th_seq; __u32 th_ack; # if defined(__i386__) || defined(__MIPSEL__) || defined(__alpha__) ||\ defined(vax) __u8 th_res:4; __u8 th_off:4; #else __u8 th_off:4; __u8 th_res:4; #endif __u8 th_flags; __u16 th_win; __u16 th_sum; __u16 th_urp; } tcphdr_t; typedef struct { __u16 uh_sport; __u16 uh_dport; __u16 uh_ulen; __u16 uh_sum; } udphdr_t; typedef struct { # if defined(__i386__) || defined(__MIPSEL__) || defined(__alpha__) ||\ defined(vax) __u8 ip_hl:4; __u8 ip_v:4; # else __u8 ip_v:4; __u8 ip_hl: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. */ typedef struct icmp { __u8 icmp_type; /* type of message, see below */ __u8 icmp_code; /* type sub code */ __u16 icmp_cksum; /* ones complement cksum of struct */ union { __u8 ih_pptr; /* ICMP_PARAMPROB */ struct in_addr ih_gwaddr; /* ICMP_REDIRECT */ struct ih_idseq { __u16 icd_id; __u16 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 } icmphdr_t; # ifndef LINUX_IPOVLY # define LINUX_IPOVLY struct ipovly { caddr_t ih_next, ih_prev; /* for protocol sequence q's */ u_char ih_x1; /* (unused) */ 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 */ }; # endif typedef struct { __u8 ether_dhost[6]; __u8 ether_shost[6]; __u16 ether_type; } ether_header_t; typedef struct uio { int uio_resid; int uio_rw; caddr_t uio_buf; } uio_t; # define UIO_READ 0 # define UIO_WRITE 1 # define UIOMOVE(a, b, c, d) uiomove(a,b,c,d) /* * For masking struct ifnet onto struct device */ # define if_name name # ifdef KERNEL -# define GETUNIT(x) dev_get(x) +# define GETUNIT(x, v) dev_get(x) # define FREE_MB_T(m) kfree_skb(m, FREE_WRITE) # define uniqtime do_gettimeofday # undef INT_MAX # undef UINT_MAX # undef LONG_MAX # undef ULONG_MAX # include # define SPL_X(x) # define SPL_NET(x) # define SPL_IMP(x) # define bcmp(a,b,c) memcmp(a,b,c) # define bcopy(a,b,c) memcpy(b,a,c) # define bzero(a,c) memset(a,0,c) # define UNITNAME(n) dev_get((n)) # define KMALLOC(a,b) (a) = (b)kmalloc(sizeof(*(a)), GFP_ATOMIC) # define KMALLOCS(a,b,c) (a) = (b)kmalloc((c), GFP_ATOMIC) # define KFREE(x) kfree_s((x), sizeof(*(x))) # define KFREES(x,s) kfree_s((x), (s)) -# define IRCOPY(a,b,c) { \ - error = verify_area(VERIFY_READ, (a) ,(c)); \ - if (!error) \ - memcpy_fromfs((b), (a), (c)); \ - } -# define IWCOPY(a,b,c) { \ - error = verify_area(VERIFY_WRITE, (b), (c)); \ - if (!error) \ - memcpy_tofs((b), (a), (c)); \ - } +#define IRCOPY(const void *a, void *b, size_t c) { \ + int error; \ + + error = verify_area(VERIFY_READ, a ,c); \ + if (!error) \ + memcpy_fromfs(b, a, c); \ + return error; \ +} +static inline int IWCOPY(const void *a, void *b, size_t c) +{ + int error; + + error = verify_area(VERIFY_WRITE, b, c); + if (!error) + memcpy_tofs(b, a, c); + return error; +} +static inline int IRCOPYPTR(const void *a, void *b, size_t c) { + caddr_t ca; + int error; + + error = verify_area(VERIFY_READ, a ,sizeof(ca)); + if (!error) { + memcpy_fromfs(ca, a, sizeof(ca)); + error = verify_area(VERIFY_READ, ca , c); + if (!error) + memcpy_fromfs(b, ca, c); + } + return error; +} +static inline int IWCOPYPTR(const void *a, void *b, size_t c) { + caddr_t ca; + int error; + + + error = verify_area(VERIFY_READ, b ,sizeof(ca)); + if (!error) { + memcpy_fromfs(ca, b, sizeof(ca)); + error = verify_area(VERIFY_WRITE, ca, c); + if (!error) + memcpy_tofs(ca, a, c); + } + return error; +} # else # define __KERNEL__ # undef INT_MAX # undef UINT_MAX # undef LONG_MAX # undef ULONG_MAX # define s8 __s8 # define u8 __u8 # define s16 __s16 # define u16 __u16 # define s32 __s32 # define u32 __u32 # include # undef __KERNEL__ # endif # define ifnet device #else typedef struct tcphdr tcphdr_t; typedef struct udphdr udphdr_t; typedef struct icmp icmphdr_t; typedef struct ip ip_t; typedef struct ether_header ether_header_t; #endif /* linux */ typedef struct tcpiphdr tcpiphdr_t; #if defined(hpux) || defined(linux) struct ether_addr { char ether_addr_octet[6]; }; #endif /* * XXX - This is one of those *awful* hacks which nobody likes */ #ifdef ultrix #define A_A #else #define A_A & #endif +#define TCPF_ALL (TH_FIN|TH_SYN|TH_RST|TH_PUSH|TH_ACK|TH_URG) + #ifndef ICMP_ROUTERADVERT # define ICMP_ROUTERADVERT 9 #endif #ifndef ICMP_ROUTERSOLICIT # define ICMP_ROUTERSOLICIT 10 #endif +#undef ICMP_MAX_UNREACH +#define ICMP_MAX_UNREACH 14 +#undef ICMP_MAXTYPE +#define ICMP_MAXTYPE 18 /* * ICMP error replies have an IP header (20 bytes), 8 bytes of ICMP data, * another IP header and then 64 bits of data, totalling 56. Of course, * the last 64 bits is dependant on that being available. */ #define ICMPERR_ICMPHLEN 8 #define ICMPERR_IPICMPHLEN (20 + 8) #define ICMPERR_MINPKTLEN (20 + 8 + 20) #define ICMPERR_MAXPKTLEN (20 + 8 + 20 + 8) +#define ICMP6ERR_MINPKTLEN (20 + 8) #endif /* __IP_COMPAT_H__ */ Index: head/sys/netinet/ip_ftp_pxy.c =================================================================== --- head/sys/netinet/ip_ftp_pxy.c (revision 60856) +++ head/sys/netinet/ip_ftp_pxy.c (revision 60857) @@ -1,458 +1,755 @@ /* * Simple FTP transparent proxy for in-kernel use. For use with the NAT * code. * $FreeBSD$ */ #if SOLARIS && defined(_KERNEL) extern kmutex_t ipf_rw; #endif #define isdigit(x) ((x) >= '0' && (x) <= '9') +#define isupper(x) ((unsigned)((x) - 'A') <= 'Z' - 'A') #define IPF_FTP_PROXY #define IPF_MINPORTLEN 18 #define IPF_MAXPORTLEN 30 #define IPF_MIN227LEN 39 #define IPF_MAX227LEN 51 +#define IPF_FTPBUFSZ 96 /* This *MUST* be >= 53! */ +int ippr_ftp_client __P((fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int)); +int ippr_ftp_complete __P((char *, size_t)); +int ippr_ftp_in __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *)); int ippr_ftp_init __P((void)); +int ippr_ftp_new __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *)); int ippr_ftp_out __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *)); -int ippr_ftp_in __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *)); -int ippr_ftp_portmsg __P((fr_info_t *, ip_t *, nat_t *)); -int ippr_ftp_pasvmsg __P((fr_info_t *, ip_t *, nat_t *)); +int ippr_ftp_pasv __P((fr_info_t *, ip_t *, nat_t *, ftpside_t *, int)); +int ippr_ftp_port __P((fr_info_t *, ip_t *, nat_t *, ftpside_t *, int)); +int ippr_ftp_process __P((fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int)); +int ippr_ftp_server __P((fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int)); +int ippr_ftp_valid __P((char *, size_t)); +u_short ippr_ftp_atoi __P((char **)); -u_short ipf_ftp_atoi __P((char **)); - static frentry_t natfr; +int ippr_ftp_pasvonly = 0; /* * Initialize local structures. */ int ippr_ftp_init() { bzero((char *)&natfr, sizeof(natfr)); natfr.fr_ref = 1; natfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; return 0; } -/* - * ipf_ftp_atoi - implement a version of atoi which processes numbers in - * pairs separated by commas (which are expected to be in the range 0 - 255), - * returning a 16 bit number combining either side of the , as the MSB and - * LSB. - */ -u_short ipf_ftp_atoi(ptr) -char **ptr; +int ippr_ftp_new(fin, ip, aps, nat) +fr_info_t *fin; +ip_t *ip; +ap_session_t *aps; +nat_t *nat; { - register char *s = *ptr, c; - register u_char i = 0, j = 0; + ftpinfo_t *ftp; + ftpside_t *f; - while ((c = *s++) && isdigit(c)) { - i *= 10; - i += c - '0'; - } - if (c != ',') { - *ptr = NULL; - return 0; - } - while ((c = *s++) && isdigit(c)) { - j *= 10; - j += c - '0'; - } - *ptr = s; - return (i << 8) | j; + KMALLOC(ftp, ftpinfo_t *); + if (ftp == NULL) + return -1; + aps->aps_data = ftp; + aps->aps_psiz = sizeof(ftpinfo_t); + + bzero((char *)ftp, sizeof(*ftp)); + f = &ftp->ftp_side[0]; + f->ftps_rptr = f->ftps_buf; + f->ftps_wptr = f->ftps_buf; + f = &ftp->ftp_side[1]; + f->ftps_rptr = f->ftps_buf; + f->ftps_wptr = f->ftps_buf; + return 0; } -int ippr_ftp_portmsg(fin, ip, nat) +int ippr_ftp_port(fin, ip, nat, f, dlen) fr_info_t *fin; ip_t *ip; nat_t *nat; +ftpside_t *f; +int dlen; { - char portbuf[IPF_MAXPORTLEN + 1], newbuf[IPF_MAXPORTLEN + 1], *s; tcphdr_t *tcp, tcph, *tcp2 = &tcph; - size_t nlen = 0, dlen, olen; + char newbuf[IPF_FTPBUFSZ], *s; u_short a5, a6, sp, dp; u_int a1, a2, a3, a4; struct in_addr swip; - int off, inc = 0; + size_t nlen, olen; fr_info_t fi; + int inc, off; nat_t *ipn; mb_t *m; #if SOLARIS mb_t *m1; #endif tcp = (tcphdr_t *)fin->fin_dp; - bzero(portbuf, sizeof(portbuf)); - off = (ip->ip_hl << 2) + (tcp->th_off << 2); - -#if SOLARIS - m = fin->fin_qfm; - - dlen = msgdsize(m) - off; - if (dlen > 0) - copyout_mblk(m, off, MIN(sizeof(portbuf), dlen), portbuf); -#else - m = *(mb_t **)fin->fin_mp; - - dlen = mbufchainlen(m) - off; - if (dlen > 0) - m_copydata(m, off, MIN(sizeof(portbuf), dlen), portbuf); -#endif - if (dlen == 0) + off = f->ftps_seq - ntohl(tcp->th_seq); + if (off < 0) return 0; - portbuf[sizeof(portbuf) - 1] = '\0'; - *newbuf = '\0'; - if (!strncmp(portbuf, "PORT ", 5)) { - if (dlen < IPF_MINPORTLEN) - return 0; - } else + /* + * Check for client sending out PORT message. + */ + if (dlen < IPF_MINPORTLEN) return 0; + /* + * Count the number of bytes in the PORT message is. + */ + if (off < 0) + return 0; + off += fin->fin_hlen + (tcp->th_off << 2); /* * Skip the PORT command + space */ - s = portbuf + 5; + s = f->ftps_rptr + 5; /* * Pick out the address components, two at a time. */ - a1 = ipf_ftp_atoi(&s); + a1 = ippr_ftp_atoi(&s); if (!s) return 0; - a2 = ipf_ftp_atoi(&s); + a2 = ippr_ftp_atoi(&s); if (!s) return 0; - /* * check that IP address in the PORT/PASV reply is the same as the * sender of the command - prevents using PORT for port scanning. */ a1 <<= 16; a1 |= a2; if (a1 != ntohl(nat->nat_inip.s_addr)) return 0; - a5 = ipf_ftp_atoi(&s); + a5 = ippr_ftp_atoi(&s); if (!s) return 0; if (*s == ')') s++; /* * check for CR-LF at the end. */ if (*s == '\n') s--; if ((*s == '\r') && (*(s + 1) == '\n')) { s += 2; a6 = a5 & 0xff; } else return 0; a5 >>= 8; /* * Calculate new address parts for PORT command */ a1 = ntohl(ip->ip_src.s_addr); a2 = (a1 >> 16) & 0xff; a3 = (a1 >> 8) & 0xff; a4 = a1 & 0xff; a1 >>= 24; - olen = s - portbuf; + olen = s - f->ftps_rptr; + /* DO NOT change this to sprintf! */ (void) sprintf(newbuf, "%s %u,%u,%u,%u,%u,%u\r\n", "PORT", a1, a2, a3, a4, a5, a6); nlen = strlen(newbuf); inc = nlen - olen; + if ((inc + ip->ip_len) > 65535) + return 0; + #if SOLARIS + m = fin->fin_qfm; for (m1 = m; m1->b_cont; m1 = m1->b_cont) ; if ((inc > 0) && (m1->b_datap->db_lim - m1->b_wptr < inc)) { mblk_t *nm; /* alloc enough to keep same trailer space for lower driver */ nm = allocb(nlen, BPRI_MED); PANIC((!nm),("ippr_ftp_out: allocb failed")); nm->b_band = m1->b_band; nm->b_wptr += nlen; m1->b_wptr -= olen; PANIC((m1->b_wptr < m1->b_rptr), ("ippr_ftp_out: cannot handle fragmented data block")); linkb(m1, nm); } else { if (m1->b_datap->db_struiolim == m1->b_wptr) m1->b_datap->db_struiolim += inc; m1->b_datap->db_struioflag &= ~STRUIO_IP; m1->b_wptr += inc; } copyin_mblk(m, off, nlen, newbuf); #else + m = *((mb_t **)fin->fin_mp); if (inc < 0) m_adj(m, inc); /* the mbuf chain will be extended if necessary by m_copyback() */ m_copyback(m, off, nlen, newbuf); #endif if (inc != 0) { #if SOLARIS || defined(__sgi) register u_32_t sum1, sum2; sum1 = ip->ip_len; sum2 = ip->ip_len + inc; /* Because ~1 == -2, We really need ~1 == -1 */ if (sum1 > sum2) sum2--; sum2 -= sum1; sum2 = (sum2 & 0xffff) + (sum2 >> 16); fix_outcksum(&ip->ip_sum, sum2, 0); #endif ip->ip_len += inc; } /* * Add skeleton NAT entry for connection which will come back the * other way. */ sp = htons(a5 << 8 | a6); /* + * Don't allow the PORT command to specify a port < 1024 due to + * security crap. + */ + if (ntohs(sp) < 1024) + return 0; + /* * The server may not make the connection back from port 20, but * it is the most likely so use it here to check for a conflicting * mapping. */ dp = htons(fin->fin_data[1] - 1); ipn = nat_outlookup(fin->fin_ifp, IPN_TCP, nat->nat_p, nat->nat_inip, ip->ip_dst, (dp << 16) | sp); if (ipn == NULL) { + int slen; + + slen = ip->ip_len; + ip->ip_len = fin->fin_hlen + sizeof(*tcp2); bcopy((char *)fin, (char *)&fi, sizeof(fi)); bzero((char *)tcp2, sizeof(*tcp2)); tcp2->th_win = htons(8192); tcp2->th_sport = sp; + tcp2->th_off = 5; tcp2->th_dport = 0; /* XXX - don't specify remote port */ fi.fin_data[0] = ntohs(sp); fi.fin_data[1] = 0; fi.fin_dp = (char *)tcp2; swip = ip->ip_src; ip->ip_src = nat->nat_inip; ipn = nat_new(nat->nat_ptr, ip, &fi, IPN_TCP|FI_W_DPORT, NAT_OUTBOUND); if (ipn != NULL) { ipn->nat_age = fr_defnatage; (void) fr_addstate(ip, &fi, FI_W_DPORT); } + ip->ip_len = slen; ip->ip_src = swip; } return inc; } -int ippr_ftp_out(fin, ip, aps, nat) +int ippr_ftp_client(fin, ip, nat, ftp, dlen) fr_info_t *fin; -ip_t *ip; -ap_session_t *aps; nat_t *nat; +ftpinfo_t *ftp; +ip_t *ip; +int dlen; { - return ippr_ftp_portmsg(fin, ip, nat); + char *rptr, *wptr; + ftpside_t *f; + int inc; + + inc = 0; + f = &ftp->ftp_side[0]; + rptr = f->ftps_rptr; + wptr = f->ftps_wptr; + + if ((ftp->ftp_passok == 0) && !strncmp(rptr, "USER ", 5)) + ftp->ftp_passok = 1; + else if ((ftp->ftp_passok == 2) && !strncmp(rptr, "PASS ", 5)) + ftp->ftp_passok = 3; + else if ((ftp->ftp_passok == 4) && !ippr_ftp_pasvonly && + !strncmp(rptr, "PORT ", 5)) { + inc = ippr_ftp_port(fin, ip, nat, f, dlen); + } + + while ((*rptr++ != '\n') && (rptr < wptr)) + ; + f->ftps_seq += rptr - f->ftps_rptr; + f->ftps_rptr = rptr; + return inc; } -int ippr_ftp_pasvmsg(fin, ip, nat) +int ippr_ftp_pasv(fin, ip, nat, f, dlen) fr_info_t *fin; ip_t *ip; nat_t *nat; +ftpside_t *f; +int dlen; { - char portbuf[IPF_MAX227LEN + 1], newbuf[IPF_MAX227LEN + 1], *s; - int off, olen, dlen, nlen = 0, inc = 0; - tcphdr_t tcph, *tcp2 = &tcph; + tcphdr_t *tcp, tcph, *tcp2 = &tcph; struct in_addr swip, swip2; - u_short a5, a6, dp, sp; + u_short a5, a6, sp, dp; u_int a1, a2, a3, a4; - tcphdr_t *tcp; fr_info_t fi; + int inc, off; nat_t *ipn; - mb_t *m; -#if SOLARIS - mb_t *m1; -#endif + char *s; - tcp = (tcphdr_t *)fin->fin_dp; - off = (ip->ip_hl << 2) + (tcp->th_off << 2); - m = *(mb_t **)fin->fin_mp; - bzero(portbuf, sizeof(portbuf)); - -#if SOLARIS - m = fin->fin_qfm; - - dlen = msgdsize(m) - off; - if (dlen > 0) - copyout_mblk(m, off, MIN(sizeof(portbuf), dlen), portbuf); -#else - dlen = mbufchainlen(m) - off; - if (dlen > 0) - m_copydata(m, off, MIN(sizeof(portbuf), dlen), portbuf); -#endif - if (dlen == 0) + /* + * Check for PASV reply message. + */ + if (dlen < IPF_MIN227LEN) return 0; - portbuf[sizeof(portbuf) - 1] = '\0'; - *newbuf = '\0'; + else if (strncmp(f->ftps_rptr, "227 Entering Passive Mode", 25)) + return 0; - if (!strncmp(portbuf, "227 ", 4)) { - if (dlen < IPF_MIN227LEN) - return 0; - else if (strncmp(portbuf, "227 Entering Passive Mode", 25)) - return 0; - } else + /* + * Count the number of bytes in the 227 reply is. + */ + tcp = (tcphdr_t *)fin->fin_dp; + off = f->ftps_seq - ntohl(tcp->th_seq); + if (off < 0) return 0; + + off += fin->fin_hlen + (tcp->th_off << 2); /* * Skip the PORT command + space */ - s = portbuf + 25; + s = f->ftps_rptr + 25; while (*s && !isdigit(*s)) s++; /* * Pick out the address components, two at a time. */ - a1 = ipf_ftp_atoi(&s); + a1 = ippr_ftp_atoi(&s); if (!s) return 0; - a2 = ipf_ftp_atoi(&s); + a2 = ippr_ftp_atoi(&s); if (!s) return 0; /* * check that IP address in the PORT/PASV reply is the same as the * sender of the command - prevents using PORT for port scanning. */ a1 <<= 16; a1 |= a2; if (a1 != ntohl(nat->nat_oip.s_addr)) return 0; - a5 = ipf_ftp_atoi(&s); + a5 = ippr_ftp_atoi(&s); if (!s) return 0; if (*s == ')') s++; if (*s == '\n') s--; /* * check for CR-LF at the end. */ if ((*s == '\r') && (*(s + 1) == '\n')) { s += 2; a6 = a5 & 0xff; } else return 0; a5 >>= 8; /* * Calculate new address parts for 227 reply */ a1 = ntohl(ip->ip_src.s_addr); a2 = (a1 >> 16) & 0xff; a3 = (a1 >> 8) & 0xff; a4 = a1 & 0xff; a1 >>= 24; - olen = s - portbuf; + inc = 0; +#if 0 + olen = s - f->ftps_rptr; (void) sprintf(newbuf, "%s %u,%u,%u,%u,%u,%u\r\n", "227 Entering Passive Mode", a1, a2, a3, a4, a5, a6); - nlen = strlen(newbuf); inc = nlen - olen; + if ((inc + ip->ip_len) > 65535) + return 0; + #if SOLARIS + m = fin->fin_qfm; for (m1 = m; m1->b_cont; m1 = m1->b_cont) ; if ((inc > 0) && (m1->b_datap->db_lim - m1->b_wptr < inc)) { mblk_t *nm; /* alloc enough to keep same trailer space for lower driver */ nm = allocb(nlen, BPRI_MED); PANIC((!nm),("ippr_ftp_out: allocb failed")); nm->b_band = m1->b_band; nm->b_wptr += nlen; m1->b_wptr -= olen; PANIC((m1->b_wptr < m1->b_rptr), ("ippr_ftp_out: cannot handle fragmented data block")); linkb(m1, nm); } else { m1->b_wptr += inc; } - copyin_mblk(m, off, nlen, newbuf); + /*copyin_mblk(m, off, nlen, newbuf);*/ #else + m = *((mb_t **)fin->fin_mp); if (inc < 0) m_adj(m, inc); /* the mbuf chain will be extended if necessary by m_copyback() */ - m_copyback(m, off, nlen, newbuf); + /*m_copyback(m, off, nlen, newbuf);*/ #endif if (inc != 0) { #if SOLARIS || defined(__sgi) register u_32_t sum1, sum2; sum1 = ip->ip_len; sum2 = ip->ip_len + inc; /* Because ~1 == -2, We really need ~1 == -1 */ if (sum1 > sum2) sum2--; sum2 -= sum1; sum2 = (sum2 & 0xffff) + (sum2 >> 16); fix_outcksum(&ip->ip_sum, sum2, 0); #endif ip->ip_len += inc; } +#endif /* * Add skeleton NAT entry for connection which will come back the * other way. */ sp = 0; dp = htons(fin->fin_data[1] - 1); ipn = nat_outlookup(fin->fin_ifp, IPN_TCP, nat->nat_p, nat->nat_inip, ip->ip_dst, (dp << 16) | sp); if (ipn == NULL) { + int slen; + + slen = ip->ip_len; + ip->ip_len = fin->fin_hlen + sizeof(*tcp2); bcopy((char *)fin, (char *)&fi, sizeof(fi)); bzero((char *)tcp2, sizeof(*tcp2)); tcp2->th_win = htons(8192); tcp2->th_sport = 0; /* XXX - fake it for nat_new */ + tcp2->th_off = 5; fi.fin_data[0] = a5 << 8 | a6; tcp2->th_dport = htons(fi.fin_data[0]); fi.fin_data[1] = 0; fi.fin_dp = (char *)tcp2; swip = ip->ip_src; swip2 = ip->ip_dst; ip->ip_dst = ip->ip_src; ip->ip_src = nat->nat_inip; ipn = nat_new(nat->nat_ptr, ip, &fi, IPN_TCP|FI_W_SPORT, NAT_OUTBOUND); if (ipn != NULL) { ipn->nat_age = fr_defnatage; (void) fr_addstate(ip, &fi, FI_W_SPORT); } + ip->ip_len = slen; ip->ip_src = swip; ip->ip_dst = swip2; } return inc; } +int ippr_ftp_server(fin, ip, nat, ftp, dlen) +fr_info_t *fin; +ip_t *ip; +nat_t *nat; +ftpinfo_t *ftp; +int dlen; +{ + char *rptr, *wptr; + ftpside_t *f; + int inc; + + inc = 0; + f = &ftp->ftp_side[1]; + rptr = f->ftps_rptr; + wptr = f->ftps_wptr; + + if ((ftp->ftp_passok == 1) && !strncmp(rptr, "331", 3)) + ftp->ftp_passok = 2; + else if ((ftp->ftp_passok == 3) && !strncmp(rptr, "230", 3)) + ftp->ftp_passok = 4; + else if ((ftp->ftp_passok == 3) && !strncmp(rptr, "530", 3)) + ftp->ftp_passok = 0; + else if ((ftp->ftp_passok == 4) && !strncmp(rptr, "227 ", 4)) { + inc = ippr_ftp_pasv(fin, ip, nat, f, dlen); + } + while ((*rptr++ != '\n') && (rptr < wptr)) + ; + f->ftps_seq += rptr - f->ftps_rptr; + f->ftps_rptr = rptr; + return inc; +} + + +/* + * Look to see if the buffer starts with something which we recognise as + * being the correct syntax for the FTP protocol. + */ +int ippr_ftp_valid(buf, len) +char *buf; +size_t len; +{ + register char *s, c; + register size_t i = len; + + if (i < 5) + return 2; + s = buf; + c = *s++; + i--; + + if (isdigit(c)) { + c = *s++; + i--; + if (isdigit(c)) { + c = *s++; + i--; + if (isdigit(c)) { + c = *s++; + i--; + if ((c != '-') && (c != ' ')) + return 1; + } else + return 1; + } else + return 1; + } else if (isupper(c)) { + c = *s++; + i--; + if (isupper(c)) { + c = *s++; + i--; + if (isupper(c)) { + c = *s++; + i--; + if (isupper(c)) { + c = *s++; + i--; + if ((c != ' ') && (c != '\r')) + return 1; + } else if ((c != ' ') && (c != '\r')) + return 1; + } else + return 1; + } else + return 1; + } else + return 1; + for (; i; i--) { + c = *s++; + if (c == '\n') + return 0; + } + return 2; +} + + +int ippr_ftp_process(fin, ip, nat, ftp, rv) +fr_info_t *fin; +ip_t *ip; +nat_t *nat; +ftpinfo_t *ftp; +int rv; +{ + int mlen, len, off, inc, i; + char *rptr, *wptr; + tcphdr_t *tcp; + ftpside_t *f; + mb_t *m; + + tcp = (tcphdr_t *)fin->fin_dp; + off = fin->fin_hlen + (tcp->th_off << 2); + +#if SOLARIS + m = fin->fin_qfm; +#else + m = *((mb_t **)fin->fin_mp); +#endif + +#if SOLARIS + mlen = msgdsize(m) - off; +#else + mlen = mbufchainlen(m) - off; +#endif + if (!mlen) + return 0; + + inc = 0; + f = &ftp->ftp_side[rv]; + rptr = f->ftps_rptr; + wptr = f->ftps_wptr; + if ((wptr == f->ftps_buf) && (f->ftps_seq <= ntohl(tcp->th_seq))) + f->ftps_seq = ntohl(tcp->th_seq); + + /* + * XXX - Ideally, this packet should get dropped because we now know + * that it is out of order (and there is no real danger in doing so + * apart from causing packets to go through here ordered). + */ + if (ntohl(tcp->th_seq) != f->ftps_seq + (wptr - rptr)) { + return APR_ERR(0); + } + + while (mlen > 0) { + len = MIN(mlen, FTP_BUFSZ / 2); + +#if SOLARIS + copyout_mblk(m, off, len, wptr); +#else + m_copydata(m, off, len, wptr); +#endif + mlen -= len; + off += len; + wptr += len; + f->ftps_wptr = wptr; + if (f->ftps_junk == 2) + f->ftps_junk = ippr_ftp_valid(rptr, wptr - rptr); + + while ((f->ftps_junk == 0) && (wptr > rptr)) { + f->ftps_junk = ippr_ftp_valid(rptr, wptr - rptr); + if (f->ftps_junk == 0) { + len = wptr - rptr; + f->ftps_rptr = rptr; + if (rv) + inc += ippr_ftp_server(fin, ip, nat, + ftp, len); + else + inc += ippr_ftp_client(fin, ip, nat, + ftp, len); + rptr = f->ftps_rptr; + } + } + + while ((f->ftps_junk == 1) && (rptr < wptr)) { + while ((rptr < wptr) && (*rptr != '\r')) + rptr++; + + if ((*rptr == '\r') && (rptr + 1 < wptr)) { + if (*(rptr + 1) == '\n') { + rptr += 2; + f->ftps_junk = 0; + } else + rptr++; + } + f->ftps_seq += rptr - f->ftps_rptr; + f->ftps_rptr = rptr; + } + + if (rptr == wptr) { + rptr = wptr = f->ftps_buf; + } else { + if ((wptr > f->ftps_buf + FTP_BUFSZ / 2)) { + i = wptr - rptr; + if ((rptr == f->ftps_buf) || + (wptr - rptr > FTP_BUFSZ / 2)) { + f->ftps_seq += i; + f->ftps_junk = 1; + rptr = wptr = f->ftps_buf; + } else { + bcopy(rptr, f->ftps_buf, i); + wptr = f->ftps_buf + i; + rptr = f->ftps_buf; + } + } + f->ftps_rptr = rptr; + f->ftps_wptr = wptr; + } + } + + f->ftps_rptr = rptr; + f->ftps_wptr = wptr; + return inc; +} + + +int ippr_ftp_out(fin, ip, aps, nat) +fr_info_t *fin; +ip_t *ip; +ap_session_t *aps; +nat_t *nat; +{ + ftpinfo_t *ftp; + + ftp = aps->aps_data; + if (ftp == NULL) + return 0; + return ippr_ftp_process(fin, ip, nat, ftp, 0); +} + + int ippr_ftp_in(fin, ip, aps, nat) fr_info_t *fin; ip_t *ip; ap_session_t *aps; nat_t *nat; { + ftpinfo_t *ftp; - return ippr_ftp_pasvmsg(fin, ip, nat); + ftp = aps->aps_data; + if (ftp == NULL) + return 0; + return ippr_ftp_process(fin, ip, nat, ftp, 1); +} + + +/* + * ippr_ftp_atoi - implement a version of atoi which processes numbers in + * pairs separated by commas (which are expected to be in the range 0 - 255), + * returning a 16 bit number combining either side of the , as the MSB and + * LSB. + */ +u_short ippr_ftp_atoi(ptr) +char **ptr; +{ + register char *s = *ptr, c; + register u_char i = 0, j = 0; + + while ((c = *s++) && isdigit(c)) { + i *= 10; + i += c - '0'; + } + if (c != ',') { + *ptr = NULL; + return 0; + } + while ((c = *s++) && isdigit(c)) { + j *= 10; + j += c - '0'; + } + *ptr = s; + return (i << 8) | j; } Index: head/sys/netinet/ip_log.c =================================================================== --- head/sys/netinet/ip_log.c (revision 60856) +++ head/sys/netinet/ip_log.c (revision 60857) @@ -1,499 +1,500 @@ /* - * Copyright (C) 1997-1998 by Darren Reed. + * Copyright (C) 1997-2000 by Darren Reed. * * Redistribution and use in source and binary forms are permitted * provided that this notice is preserved and due credit is given * to the original author and the contributors. * * $Id: ip_log.c,v 2.1.2.2 1999/09/21 11:55:44 darrenr Exp $ * $FreeBSD$ */ #include #if defined(KERNEL) && !defined(_KERNEL) # define _KERNEL #endif #if defined(__NetBSD__) && (NetBSD >= 199905) && !defined(IPFILTER_LKM) # include "opt_ipfilter_log.h" #endif #ifdef __FreeBSD__ # if defined(_KERNEL) && !defined(IPFILTER_LKM) -# if !defined(__FreeBSD_version) -# include -# endif # if defined(__FreeBSD_version) && (__FreeBSD_version >= 300000) # include "opt_ipfilter.h" # endif +# else +# include # endif #endif #ifdef IPFILTER_LOG # ifndef SOLARIS # define SOLARIS (defined(sun) && (defined(__svr4__) || defined(__SVR4))) # endif # ifndef _KERNEL # include # include # include # include # endif # include # include # include # if __FreeBSD_version >= 220000 && defined(_KERNEL) # include # include # else # include # endif # include # if defined(_KERNEL) && !defined(linux) # include # endif # include # if !SOLARIS # if (NetBSD > 199609) || (OpenBSD > 199603) || (__FreeBSD_version >= 300000) # include # else # include # endif # ifndef linux # include # endif # else # include # include # include # include # include # include # include # include # include # endif # ifndef linux # include # endif # include # include # ifdef sun # include # endif # if __FreeBSD_version >= 300000 # include # endif # include # include # ifdef __sgi # include # ifdef IFF_DRVRLOCK /* IRIX6 */ # include # endif # endif # if !defined(linux) && !(defined(__sgi) && !defined(IFF_DRVRLOCK)) /*IRIX<6*/ # include # endif # include # include # include # include # include # ifndef linux # include # endif # ifndef _KERNEL # include # endif # include "netinet/ip_compat.h" # include # include "netinet/ip_fil.h" # include "netinet/ip_proxy.h" # include "netinet/ip_nat.h" # include "netinet/ip_frag.h" # include "netinet/ip_state.h" # include "netinet/ip_auth.h" # if (__FreeBSD_version >= 300000) # include # endif # ifndef MIN # define MIN(a,b) (((a)<(b))?(a):(b)) # endif # if SOLARIS || defined(__sgi) extern kmutex_t ipl_mutex; # if SOLARIS extern kcondvar_t iplwait; # endif # endif iplog_t **iplh[IPL_LOGMAX+1], *iplt[IPL_LOGMAX+1], *ipll[IPL_LOGMAX+1]; size_t iplused[IPL_LOGMAX+1]; -fr_info_t iplcrc[IPL_LOGMAX+1]; +static fr_info_t iplcrc[IPL_LOGMAX+1]; # ifdef linux static struct wait_queue *iplwait[IPL_LOGMAX+1]; # endif /* * Initialise log buffers & pointers. Also iniialised the CRC to a local * secret for use in calculating the "last log checksum". */ void ipflog_init() { int i; for (i = IPL_LOGMAX; i >= 0; i--) { iplt[i] = NULL; ipll[i] = NULL; iplh[i] = &iplt[i]; iplused[i] = 0; bzero((char *)&iplcrc[i], sizeof(iplcrc[i])); } } /* * ipflog * Create a log record for a packet given that it has been triggered by a * rule (or the default setting). Calculate the transport protocol header * size using predetermined size of a couple of popular protocols and thus * how much data to copy into the log, including part of the data body if * requested. */ int ipflog(flags, ip, fin, m) u_int flags; ip_t *ip; fr_info_t *fin; mb_t *m; { ipflog_t ipfl; register size_t mlen, hlen; size_t sizes[2]; void *ptrs[2]; int types[2]; + u_char p; # if SOLARIS ill_t *ifp = fin->fin_ifp; # else struct ifnet *ifp = fin->fin_ifp; # endif /* * calculate header size. */ hlen = fin->fin_hlen; - if ((ip->ip_off & IP_OFFMASK) == 0) { - if (ip->ip_p == IPPROTO_TCP) + if (fin->fin_off == 0) { + p = fin->fin_fi.fi_p; + if (p == IPPROTO_TCP) hlen += MIN(sizeof(tcphdr_t), fin->fin_dlen); - else if (ip->ip_p == IPPROTO_UDP) + else if (p == IPPROTO_UDP) hlen += MIN(sizeof(udphdr_t), fin->fin_dlen); - else if (ip->ip_p == IPPROTO_ICMP) { - struct icmp *icmp; + else if (p == IPPROTO_ICMP) { + struct icmp *icmp; - icmp = (struct icmp *)((char *)ip + hlen); + icmp = (struct icmp *)fin->fin_dp; /* * For ICMP, if the packet is an error packet, also * include the information about the packet which * caused the error. */ switch (icmp->icmp_type) { case ICMP_UNREACH : case ICMP_SOURCEQUENCH : case ICMP_REDIRECT : case ICMP_TIMXCEED : case ICMP_PARAMPROB : hlen += MIN(sizeof(struct icmp) + 8, fin->fin_dlen); break; default : hlen += MIN(sizeof(struct icmp), fin->fin_dlen); break; } } } /* * Get the interface number and name to which this packet is * currently associated. */ # if SOLARIS ipfl.fl_unit = (u_char)ifp->ill_ppa; bcopy(ifp->ill_name, ipfl.fl_ifname, MIN(ifp->ill_name_length, 4)); mlen = (flags & FR_LOGBODY) ? MIN(msgdsize(m) - hlen, 128) : 0; # else # if (defined(NetBSD) && (NetBSD <= 1991011) && (NetBSD >= 199603)) || \ (defined(OpenBSD) && (OpenBSD >= 199603)) strncpy(ipfl.fl_ifname, ifp->if_xname, IFNAMSIZ); # else # ifndef linux ipfl.fl_unit = (u_char)ifp->if_unit; # endif if ((ipfl.fl_ifname[0] = ifp->if_name[0])) if ((ipfl.fl_ifname[1] = ifp->if_name[1])) if ((ipfl.fl_ifname[2] = ifp->if_name[2])) ipfl.fl_ifname[3] = ifp->if_name[3]; # endif - mlen = (flags & FR_LOGBODY) ? MIN(ip->ip_len - hlen, 128) : 0; + mlen = (flags & FR_LOGBODY) ? MIN(fin->fin_plen - hlen, 128) : 0; # endif ipfl.fl_plen = (u_char)mlen; ipfl.fl_hlen = (u_char)hlen; ipfl.fl_rule = fin->fin_rule; ipfl.fl_group = fin->fin_group; if (fin->fin_fr != NULL) ipfl.fl_loglevel = fin->fin_fr->fr_loglevel; else ipfl.fl_loglevel = 0xffff; ipfl.fl_flags = flags; ptrs[0] = (void *)&ipfl; sizes[0] = sizeof(ipfl); types[0] = 0; # if SOLARIS /* * Are we copied from the mblk or an aligned array ? */ if (ip == (ip_t *)m->b_rptr) { ptrs[1] = m; sizes[1] = hlen + mlen; types[1] = 1; } else { ptrs[1] = ip; sizes[1] = hlen + mlen; types[1] = 0; } # else ptrs[1] = m; sizes[1] = hlen + mlen; types[1] = 1; # endif return ipllog(IPL_LOGIPF, fin, ptrs, sizes, types, 2); } /* * ipllog */ int ipllog(dev, fin, items, itemsz, types, cnt) int dev; fr_info_t *fin; void **items; size_t *itemsz; int *types, cnt; { caddr_t buf, s; iplog_t *ipl; size_t len; int i; /* * Check to see if this log record has a CRC which matches the last * record logged. If it does, just up the count on the previous one * rather than create a new one. */ MUTEX_ENTER(&ipl_mutex); if (fin != NULL) { if ((ipll[dev] != NULL) && bcmp((char *)fin, (char *)&iplcrc[dev], FI_CSIZE) == 0) { ipll[dev]->ipl_count++; MUTEX_EXIT(&ipl_mutex); return 1; } bcopy((char *)fin, (char *)&iplcrc[dev], FI_CSIZE); } else bzero((char *)&iplcrc[dev], FI_CSIZE); MUTEX_EXIT(&ipl_mutex); /* * Get the total amount of data to be logged. */ for (i = 0, len = sizeof(iplog_t); i < cnt; i++) len += itemsz[i]; /* * check that we have space to record this information and can * allocate that much. */ KMALLOCS(buf, caddr_t, len); if (!buf) return 0; MUTEX_ENTER(&ipl_mutex); if ((iplused[dev] + len) > IPLLOGSIZE) { MUTEX_EXIT(&ipl_mutex); KFREES(buf, len); return 0; } iplused[dev] += len; MUTEX_EXIT(&ipl_mutex); /* * advance the log pointer to the next empty record and deduct the * amount of space we're going to use. */ ipl = (iplog_t *)buf; ipl->ipl_magic = IPL_MAGIC; ipl->ipl_count = 1; ipl->ipl_next = NULL; ipl->ipl_dsize = len; # if SOLARIS || defined(sun) || defined(linux) uniqtime((struct timeval *)&ipl->ipl_sec); # else # if BSD >= 199306 || defined(__FreeBSD__) || defined(__sgi) microtime((struct timeval *)&ipl->ipl_sec); # endif # endif /* * Loop through all the items to be logged, copying each one to the * buffer. Use bcopy for normal data or the mb_t copyout routine. */ for (i = 0, s = buf + sizeof(*ipl); i < cnt; i++) { if (types[i] == 0) bcopy(items[i], s, itemsz[i]); else if (types[i] == 1) { # if SOLARIS copyout_mblk(items[i], 0, itemsz[i], s); # else m_copydata(items[i], 0, itemsz[i], s); # endif } s += itemsz[i]; } MUTEX_ENTER(&ipl_mutex); ipll[dev] = ipl; *iplh[dev] = ipl; iplh[dev] = &ipl->ipl_next; # if SOLARIS cv_signal(&iplwait); mutex_exit(&ipl_mutex); # else MUTEX_EXIT(&ipl_mutex); # ifdef linux wake_up_interruptible(&iplwait[dev]); # else wakeup(&iplh[dev]); # endif # endif return 1; } int ipflog_read(unit, uio) minor_t unit; struct uio *uio; { size_t dlen, copied; int error = 0; iplog_t *ipl; # if defined(_KERNEL) && !SOLARIS int s; # endif /* * Sanity checks. Make sure the minor # is valid and we're copying * a valid chunk of data. */ if (IPL_LOGMAX < unit) return ENXIO; if (!uio->uio_resid) return 0; if ((uio->uio_resid < sizeof(iplog_t)) || (uio->uio_resid > IPLLOGSIZE)) return EINVAL; /* * Lock the log so we can snapshot the variables. Wait for a signal * if the log is empty. */ SPL_NET(s); MUTEX_ENTER(&ipl_mutex); while (!iplused[unit] || !iplt[unit]) { # if SOLARIS && defined(_KERNEL) if (!cv_wait_sig(&iplwait, &ipl_mutex)) { MUTEX_EXIT(&ipl_mutex); return EINTR; } # else # ifdef linux interruptible_sleep_on(&iplwait[unit]); if (current->signal & ~current->blocked) return -EINTR; # else MUTEX_EXIT(&ipl_mutex); SPL_X(s); error = SLEEP(&iplh[unit], "ipl sleep"); if (error) return error; SPL_NET(s); MUTEX_ENTER(&ipl_mutex); # endif /* linux */ # endif /* SOLARIS */ } # if BSD >= 199306 || defined(__FreeBSD__) uio->uio_rw = UIO_READ; # endif for (copied = 0; (ipl = iplt[unit]); copied += dlen) { dlen = ipl->ipl_dsize; if (dlen > uio->uio_resid) break; /* * Don't hold the mutex over the uiomove call. */ iplt[unit] = ipl->ipl_next; iplused[unit] -= dlen; MUTEX_EXIT(&ipl_mutex); SPL_X(s); error = UIOMOVE((caddr_t)ipl, dlen, UIO_READ, uio); if (error) { SPL_NET(s); MUTEX_ENTER(&ipl_mutex); ipl->ipl_next = iplt[unit]; iplt[unit] = ipl; iplused[unit] += dlen; break; } KFREES((caddr_t)ipl, dlen); SPL_NET(s); MUTEX_ENTER(&ipl_mutex); } if (!iplt[unit]) { iplused[unit] = 0; iplh[unit] = &iplt[unit]; ipll[unit] = NULL; } MUTEX_EXIT(&ipl_mutex); SPL_X(s); # ifdef linux if (!error) return (int)copied; return -error; # else return error; # endif } int ipflog_clear(unit) minor_t unit; { iplog_t *ipl; int used; MUTEX_ENTER(&ipl_mutex); while ((ipl = iplt[unit])) { iplt[unit] = ipl->ipl_next; KFREES((caddr_t)ipl, ipl->ipl_dsize); } iplh[unit] = &iplt[unit]; ipll[unit] = NULL; used = iplused[unit]; iplused[unit] = 0; bzero((char *)&iplcrc[unit], FI_CSIZE); MUTEX_EXIT(&ipl_mutex); return used; } #endif /* IPFILTER_LOG */ Index: head/sys/netinet/ip_nat.c =================================================================== --- head/sys/netinet/ip_nat.c (revision 60856) +++ head/sys/netinet/ip_nat.c (revision 60857) @@ -1,2301 +1,2302 @@ /* * Copyright (C) 1995-2000 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) static const char sccsid[] = "@(#)ip_nat.c 1.11 6/5/96 (C) 1995 Darren Reed"; /*static const char rcsid[] = "@(#)$Id: ip_nat.c,v 2.2.2.12 2000/01/24 12:43:40 darrenr Exp $";*/ static const char rcsid[] = "@(#)$FreeBSD$"; #endif #if defined(__FreeBSD__) && defined(KERNEL) && !defined(_KERNEL) #define _KERNEL #endif #include #include #include #include #include #if defined(__NetBSD__) && (NetBSD >= 199905) && !defined(IPFILTER_LKM) && \ defined(_KERNEL) # include "opt_ipfilter_log.h" #endif #if !defined(_KERNEL) && !defined(KERNEL) # include # include # include #endif #if (defined(KERNEL) || defined(_KERNEL)) && (__FreeBSD_version >= 220000) # include # include #else # include #endif #include #include #ifndef linux # include #endif #include #if defined(_KERNEL) && !defined(linux) # include #endif #if !defined(__SVR4) && !defined(__svr4__) # ifndef linux # include # endif #else # include # include # ifdef _KERNEL # include # endif # include # include #endif #if __FreeBSD_version >= 300000 # include #endif #include #if __FreeBSD_version >= 300000 # include # if defined(_KERNEL) && !defined(IPFILTER_LKM) # include "opt_ipfilter.h" # endif #endif #ifdef sun # include #endif #include #include #include #include #ifdef __sgi # ifdef IFF_DRVRLOCK /* IRIX6 */ #include #include # endif #endif #ifdef RFC1825 # include # include extern struct ifnet vpnif; #endif #ifndef linux # include #endif #include #include #include #include "netinet/ip_compat.h" #include #include "netinet/ip_fil.h" #include "netinet/ip_proxy.h" #include "netinet/ip_nat.h" #include "netinet/ip_frag.h" #include "netinet/ip_state.h" #if (__FreeBSD_version >= 300000) # include #endif #ifndef MIN # define MIN(a,b) (((a)<(b))?(a):(b)) #endif #undef SOCKADDR_IN #define SOCKADDR_IN struct sockaddr_in nat_t **nat_table[2] = { NULL, NULL }, *nat_instances = NULL; ipnat_t *nat_list = NULL; u_int ipf_nattable_sz = NAT_TABLE_SZ; u_int ipf_natrules_sz = NAT_SIZE; u_int ipf_rdrrules_sz = RDR_SIZE; u_int ipf_hostmap_sz = HOSTMAP_SIZE; u_32_t nat_masks = 0; u_32_t rdr_masks = 0; ipnat_t **nat_rules = NULL; ipnat_t **rdr_rules = NULL; hostmap_t **maptable = NULL; u_long fr_defnatage = DEF_NAT_AGE, fr_defnaticmpage = 6; /* 3 seconds */ static natstat_t nat_stats; int fr_nat_lock = 0; #if (SOLARIS || defined(__sgi)) && defined(_KERNEL) extern kmutex_t ipf_rw, ipf_hostmap; extern KRWLOCK_T ipf_nat; #endif static int nat_flushtable __P((void)); static int nat_clearlist __P((void)); static void nat_addnat __P((struct ipnat *)); static void nat_addrdr __P((struct ipnat *)); +static void nat_delete __P((struct nat *)); static void nat_delrdr __P((struct ipnat *)); static void nat_delnat __P((struct ipnat *)); static int fr_natgetent __P((caddr_t)); static int fr_natgetsz __P((caddr_t)); static int fr_natputent __P((caddr_t)); static int nat_match __P((fr_info_t *, ipnat_t *, ip_t *)); static hostmap_t *nat_hostmap __P((ipnat_t *, struct in_addr, struct in_addr)); static void nat_hostmapdel __P((struct hostmap *)); int nat_init() { KMALLOCS(nat_table[0], nat_t **, sizeof(nat_t *) * ipf_nattable_sz); if (nat_table[0] != NULL) bzero((char *)nat_table[0], ipf_nattable_sz * sizeof(nat_t *)); else return -1; KMALLOCS(nat_table[1], nat_t **, sizeof(nat_t *) * ipf_nattable_sz); if (nat_table[1] != NULL) bzero((char *)nat_table[1], ipf_nattable_sz * sizeof(nat_t *)); else return -1; KMALLOCS(nat_rules, ipnat_t **, sizeof(ipnat_t *) * ipf_natrules_sz); if (nat_rules != NULL) bzero((char *)nat_rules, ipf_natrules_sz * sizeof(ipnat_t *)); else return -1; KMALLOCS(rdr_rules, ipnat_t **, sizeof(ipnat_t *) * ipf_rdrrules_sz); if (rdr_rules != NULL) bzero((char *)rdr_rules, ipf_rdrrules_sz * sizeof(ipnat_t *)); else return -1; KMALLOCS(maptable, hostmap_t **, sizeof(hostmap_t *) * ipf_hostmap_sz); if (maptable != NULL) bzero((char *)maptable, sizeof(hostmap_t *) * ipf_hostmap_sz); else return -1; return 0; } static void nat_addrdr(n) ipnat_t *n; { ipnat_t **np; u_32_t j; u_int hv; int k; k = countbits(n->in_outmsk); if ((k >= 0) && (k != 32)) rdr_masks |= 1 << k; j = (n->in_outip & n->in_outmsk); hv = NAT_HASH_FN(j, 0, ipf_rdrrules_sz); np = rdr_rules + hv; while (*np != NULL) np = &(*np)->in_rnext; n->in_rnext = NULL; n->in_prnext = np; *np = n; } static void nat_addnat(n) ipnat_t *n; { ipnat_t **np; u_32_t j; u_int hv; int k; k = countbits(n->in_inmsk); if ((k >= 0) && (k != 32)) nat_masks |= 1 << k; j = (n->in_inip & n->in_inmsk); hv = NAT_HASH_FN(j, 0, ipf_natrules_sz); np = nat_rules + hv; while (*np != NULL) np = &(*np)->in_mnext; n->in_mnext = NULL; n->in_pmnext = np; *np = n; } static void nat_delrdr(n) ipnat_t *n; { if (n->in_rnext) n->in_rnext->in_prnext = n->in_prnext; *n->in_prnext = n->in_rnext; } static void nat_delnat(n) ipnat_t *n; { if (n->in_mnext) n->in_mnext->in_pmnext = n->in_pmnext; *n->in_pmnext = n->in_mnext; } /* * check if an ip address has already been allocated for a given mapping that * is not doing port based translation. */ static struct hostmap *nat_hostmap(np, real, map) ipnat_t *np; struct in_addr real; struct in_addr map; { hostmap_t *hm; u_int hv; MUTEX_ENTER(&ipf_hostmap); hv = real.s_addr % HOSTMAP_SIZE; for (hm = maptable[hv]; hm; hm = hm->hm_next) if ((hm->hm_realip.s_addr == real.s_addr) && (np == hm->hm_ipnat)) { hm->hm_ref++; MUTEX_EXIT(&ipf_hostmap); return hm; } KMALLOC(hm, hostmap_t *); if (hm) { hm->hm_next = maptable[hv]; hm->hm_pnext = maptable + hv; if (maptable[hv]) maptable[hv]->hm_pnext = &hm->hm_next; maptable[hv] = hm; hm->hm_ipnat = np; hm->hm_realip = real; hm->hm_mapip = map; hm->hm_ref = 1; } MUTEX_EXIT(&ipf_hostmap); return hm; } static void nat_hostmapdel(hm) struct hostmap *hm; { MUTEX_ENTER(&ipf_hostmap); ATOMIC_DEC32(hm->hm_ref); if (hm->hm_ref == 0) { if (hm->hm_next) hm->hm_next->hm_pnext = hm->hm_pnext; *hm->hm_pnext = hm->hm_next; KFREE(hm); } MUTEX_EXIT(&ipf_hostmap); } void fix_outcksum(sp, n , len) u_short *sp; u_32_t n; int len; { register u_short sumshort; register u_32_t sum1; if (!n) return; #if SOLARIS2 >= 6 else if (n & NAT_HW_CKSUM) { *sp = n & 0xffff; return; } #endif sum1 = (~ntohs(*sp)) & 0xffff; 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 , len) u_short *sp; u_32_t n; int len; { register u_short sumshort; register u_32_t sum1; if (!n) return; #if SOLARIS2 >= 6 else if (n & NAT_HW_CKSUM) { *sp = n & 0xffff; return; } #endif #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) #if defined(__NetBSD__) || defined(__OpenBSD__) || (__FreeBSD_version >= 300003) u_long cmd; #else int cmd; #endif caddr_t data; int mode; { register ipnat_t *nat, *nt, *n = NULL, **np = NULL; int error = 0, ret, arg; ipnat_t natd; u_32_t i, j; #if (BSD >= 199306) && defined(_KERNEL) if ((securelevel >= 2) && (mode & FWRITE)) return EPERM; #endif nat = NULL; /* XXX gcc -Wuninitialized */ KMALLOC(nt, ipnat_t *); if ((cmd == SIOCADNAT) || (cmd == SIOCRMNAT)) error = IRCOPYPTR(data, (char *)&natd, sizeof(natd)); else if (cmd == SIOCIPFFL) /* SIOCFLNAT & SIOCCNATL */ error = IRCOPY(data, (char *)&arg, sizeof(arg)); if (error) goto done; /* * For add/delete, look to see if the NAT entry is already present */ WRITE_ENTER(&ipf_nat); if ((cmd == SIOCADNAT) || (cmd == SIOCRMNAT)) { nat = &natd; nat->in_flags &= IPN_USERFLAGS; if ((nat->in_redir & NAT_MAPBLK) == 0) { if ((nat->in_flags & IPN_SPLIT) == 0) nat->in_inip &= nat->in_inmsk; if ((nat->in_flags & IPN_IPRANGE) == 0) nat->in_outip &= nat->in_outmsk; } 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) { #ifdef IPFILTER_LOG case SIOCIPFFB : { int tmp; if (!(mode & FWRITE)) error = EPERM; else { tmp = ipflog_clear(IPL_LOGNAT); IWCOPY((char *)&tmp, (char *)data, sizeof(tmp)); } break; } #endif case SIOCADNAT : if (!(mode & FWRITE)) { error = EPERM; break; } if (n) { error = EEXIST; break; } if (nt == NULL) { error = ENOMEM; break; } n = nt; nt = NULL; bcopy((char *)nat, (char *)n, sizeof(*n)); n->in_ifp = (void *)GETUNIT(n->in_ifname, 4); if (!n->in_ifp) n->in_ifp = (void *)-1; if (n->in_plabel[0] != '\0') { n->in_apr = appr_match(n->in_p, n->in_plabel); if (!n->in_apr) { error = ENOENT; break; } } n->in_next = NULL; *np = n; if (n->in_redir & NAT_REDIRECT) nat_addrdr(n); if (n->in_redir & (NAT_MAP|NAT_MAPBLK)) nat_addnat(n); n->in_use = 0; if (n->in_redir & NAT_MAPBLK) n->in_space = USABLE_PORTS * ~ntohl(n->in_outmsk); else if (n->in_flags & IPN_AUTOPORTMAP) n->in_space = USABLE_PORTS * ~ntohl(n->in_inmsk); else if (n->in_flags & IPN_IPRANGE) n->in_space = ntohl(n->in_outmsk) - ntohl(n->in_outip); else if (n->in_flags & IPN_SPLIT) n->in_space = 2; else n->in_space = ~ntohl(n->in_outmsk); /* * Calculate the number of valid IP addresses in the output * mapping range. In all cases, the range is inclusive of * the start and ending IP addresses. * If to a CIDR address, lose 2: broadcast + network address * (so subtract 1) * If to a range, add one. * If to a single IP address, set to 1. */ if (n->in_space) { if ((n->in_flags & IPN_IPRANGE) != 0) n->in_space += 1; else n->in_space -= 1; } else n->in_space = 1; if ((n->in_outmsk != 0xffffffff) && (n->in_outmsk != 0) && ((n->in_flags & (IPN_IPRANGE|IPN_SPLIT)) == 0)) n->in_nip = ntohl(n->in_outip) + 1; else if ((n->in_flags & IPN_SPLIT) && (n->in_redir & NAT_REDIRECT)) n->in_nip = ntohl(n->in_inip); 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) + 1); /* * Because two different sources can map to * different destinations but use the same * local IP#/port #. * If the result is smaller than in_space, then * we may have wrapped around 32bits. */ i = n->in_inmsk; if ((i != 0) && (i != 0xffffffff)) { j = n->in_space * (~ntohl(i) + 1); if (j >= n->in_space) n->in_space = j; else n->in_space = 0xffffffff; } } /* * If no protocol is specified, multiple by 256. */ if ((n->in_flags & IPN_TCPUDP) == 0) { j = n->in_space * 256; if (j >= n->in_space) n->in_space = j; else n->in_space = 0xffffffff; } } /* Otherwise, these fields are preset */ n = NULL; nat_stats.ns_rules++; break; case SIOCRMNAT : if (!(mode & FWRITE)) { error = EPERM; n = NULL; break; } if (!n) { error = ESRCH; break; } if (n->in_redir & NAT_REDIRECT) nat_delrdr(n); if (n->in_redir & (NAT_MAPBLK|NAT_MAP)) nat_delnat(n); if (nat_list == NULL) { nat_masks = 0; rdr_masks = 0; } *np = n->in_next; if (!n->in_use) { if (n->in_apr) appr_free(n->in_apr); KFREE(n); nat_stats.ns_rules--; } else { n->in_flags |= IPN_DELETE; n->in_next = NULL; } n = NULL; break; case SIOCGNATS : MUTEX_DOWNGRADE(&ipf_nat); 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_nattab_sz = ipf_nattable_sz; nat_stats.ns_rultab_sz = ipf_natrules_sz; nat_stats.ns_rdrtab_sz = ipf_rdrrules_sz; nat_stats.ns_instances = nat_instances; nat_stats.ns_apslist = ap_sess_list; error = IWCOPYPTR((char *)&nat_stats, (char *)data, sizeof(nat_stats)); break; case SIOCGNATL : { natlookup_t nl; MUTEX_DOWNGRADE(&ipf_nat); error = IRCOPYPTR((char *)data, (char *)&nl, sizeof(nl)); if (error) break; if (nat_lookupredir(&nl)) { error = IWCOPYPTR((char *)&nl, (char *)data, sizeof(nl)); } else error = ESRCH; break; } case SIOCIPFFL : /* old SIOCFLNAT & SIOCCNATL */ if (!(mode & FWRITE)) { error = EPERM; break; } error = 0; if (arg == 0) ret = nat_flushtable(); else if (arg == 1) ret = nat_clearlist(); else error = EINVAL; MUTEX_DOWNGRADE(&ipf_nat); if (!error) { error = IWCOPY((caddr_t)&ret, data, sizeof(ret)); if (error) error = EFAULT; } break; case SIOCSTLCK : error = IRCOPY(data, (caddr_t)&arg, sizeof(arg)); if (!error) { error = IWCOPY((caddr_t)&fr_nat_lock, data, sizeof(fr_nat_lock)); if (!error) fr_nat_lock = arg; } break; case SIOCSTPUT : if (fr_nat_lock) error = fr_natputent(data); else error = EACCES; break; case SIOCSTGSZ : if (fr_nat_lock) error = fr_natgetsz(data); else error = EACCES; break; case SIOCSTGET : if (fr_nat_lock) error = fr_natgetent(data); else error = EACCES; break; case FIONREAD : #ifdef IPFILTER_LOG MUTEX_DOWNGRADE(&ipf_nat); error = IWCOPY((caddr_t)&iplused[IPL_LOGNAT], (caddr_t)data, sizeof(iplused[IPL_LOGNAT])); #endif break; default : error = EINVAL; break; } RWLOCK_EXIT(&ipf_nat); /* READ/WRITE */ done: if (nt) KFREE(nt); return error; } static int fr_natgetsz(data) caddr_t data; { ap_session_t *aps; nat_t *nat, *n; int error = 0; natget_t ng; error = IRCOPY(data, (caddr_t)&ng, sizeof(ng)); if (error) return EFAULT; nat = ng.ng_ptr; if (!nat) { nat = nat_instances; ng.ng_sz = 0; if (nat == NULL) { error = IWCOPY((caddr_t)&ng, data, sizeof(ng)); if (error) error = EFAULT; return error; } } else { /* * Make sure the pointer we're copying from exists in the * current list of entries. Security precaution to prevent * copying of random kernel data. */ for (n = nat_instances; n; n = n->nat_next) if (n == nat) break; if (!n) return ESRCH; } ng.ng_sz = sizeof(nat_save_t); aps = nat->nat_aps; if ((aps != NULL) && (aps->aps_data != 0)) { ng.ng_sz += sizeof(ap_session_t); ng.ng_sz += aps->aps_psiz; } error = IWCOPY((caddr_t)&ng, data, sizeof(ng)); if (error) error = EFAULT; return error; } static int fr_natgetent(data) caddr_t data; { nat_save_t ipn, *ipnp, *ipnn; register nat_t *n, *nat; ap_session_t *aps; int error; error = IRCOPY(data, (caddr_t)&ipnp, sizeof(ipnp)); if (error) return EFAULT; error = IRCOPY((caddr_t)ipnp, (caddr_t)&ipn, sizeof(ipn)); if (error) return EFAULT; nat = ipn.ipn_next; if (!nat) { nat = nat_instances; if (nat == NULL) { if (nat_instances == NULL) return ENOENT; return 0; } } else { /* * Make sure the pointer we're copying from exists in the * current list of entries. Security precaution to prevent * copying of random kernel data. */ for (n = nat_instances; n; n = n->nat_next) if (n == nat) break; if (!n) return ESRCH; } ipn.ipn_next = nat->nat_next; ipn.ipn_dsize = 0; bcopy((char *)nat, (char *)&ipn.ipn_nat, sizeof(ipn.ipn_nat)); ipn.ipn_nat.nat_data = NULL; if (nat->nat_ptr) { bcopy((char *)nat->nat_ptr, (char *)&ipn.ipn_ipnat, sizeof(ipn.ipn_ipnat)); } if (nat->nat_fr) bcopy((char *)nat->nat_fr, (char *)&ipn.ipn_rule, sizeof(ipn.ipn_rule)); if ((aps = nat->nat_aps)) { ipn.ipn_dsize = sizeof(*aps); if (aps->aps_data) ipn.ipn_dsize += aps->aps_psiz; KMALLOCS(ipnn, nat_save_t *, sizeof(*ipnn) + ipn.ipn_dsize); if (ipnn == NULL) return NULL; bcopy((char *)&ipn, (char *)ipnn, sizeof(ipn)); bcopy((char *)aps, ipn.ipn_data, sizeof(*aps)); if (aps->aps_data) { bcopy(aps->aps_data, ipn.ipn_data + sizeof(*aps), aps->aps_psiz); ipn.ipn_dsize += aps->aps_psiz; } error = IWCOPY((caddr_t)ipnn, ipnp, sizeof(ipn) + ipn.ipn_dsize); if (error) return EFAULT; KFREES(ipnn, sizeof(*ipnn) + ipn.ipn_dsize); } else { error = IWCOPY((caddr_t)&ipn, ipnp, sizeof(ipn)); if (error) return EFAULT; } return 0; } static int fr_natputent(data) caddr_t data; { nat_save_t ipn, *ipnp, *ipnn; register nat_t *n, *nat; ap_session_t *aps; frentry_t *fr; ipnat_t *in; int error; error = IRCOPY(data, (caddr_t)&ipnp, sizeof(ipnp)); if (error) return EFAULT; error = IRCOPY((caddr_t)ipnp, (caddr_t)&ipn, sizeof(ipn)); if (error) return EFAULT; if (ipn.ipn_dsize) { KMALLOCS(ipnn, nat_save_t *, sizeof(ipn) + ipn.ipn_dsize); if (ipnn == NULL) return ENOMEM; bcopy((char *)&ipn, (char *)ipnn, sizeof(ipn)); error = IRCOPY((caddr_t)ipnp, (caddr_t)ipn.ipn_data, ipn.ipn_dsize); if (error) return EFAULT; } else ipnn = NULL; KMALLOC(nat, nat_t *); if (nat == NULL) return ENOMEM; bcopy((char *)&ipn.ipn_nat, (char *)nat, sizeof(*nat)); /* * Initialize all these so that nat_delete() doesn't cause a crash. */ nat->nat_hstart[0] = NULL; nat->nat_hstart[1] = NULL; fr = nat->nat_fr; nat->nat_fr = NULL; aps = nat->nat_aps; nat->nat_aps = NULL; in = nat->nat_ptr; nat->nat_ptr = NULL; nat->nat_data = NULL; /* * Restore the rule associated with this nat session */ if (in) { KMALLOC(in, ipnat_t *); if (in == NULL) { error = ENOMEM; goto junkput; } nat->nat_ptr = in; bcopy((char *)&ipn.ipn_ipnat, (char *)in, sizeof(*in)); in->in_use = 1; in->in_flags |= IPN_DELETE; in->in_next = NULL; in->in_rnext = NULL; in->in_prnext = NULL; in->in_mnext = NULL; in->in_pmnext = NULL; in->in_ifp = GETUNIT(in->in_ifname, 4); if (in->in_plabel[0] != '\0') { in->in_apr = appr_match(in->in_p, in->in_plabel); } } /* * Restore ap_session_t structure. Include the private data allocated * if it was there. */ if (aps) { KMALLOC(aps, ap_session_t *); if (aps == NULL) { error = ENOMEM; goto junkput; } nat->nat_aps = aps; aps->aps_next = ap_sess_list; ap_sess_list = aps; bcopy(ipnn->ipn_data, (char *)aps, sizeof(*aps)); if (in) aps->aps_apr = in->in_apr; if (aps->aps_psiz) { KMALLOCS(aps->aps_data, void *, aps->aps_psiz); if (aps->aps_data == NULL) { error = ENOMEM; goto junkput; } bcopy(ipnn->ipn_data + sizeof(*aps), aps->aps_data, aps->aps_psiz); } else { aps->aps_psiz = 0; aps->aps_data = NULL; } } /* * If there was a filtering rule associated with this entry then * build up a new one. */ if (fr != NULL) { if (nat->nat_flags & FI_NEWFR) { KMALLOC(fr, frentry_t *); nat->nat_fr = fr; if (fr == NULL) { error = ENOMEM; goto junkput; } bcopy((char *)&ipn.ipn_fr, (char *)fr, sizeof(*fr)); ipn.ipn_nat.nat_fr = fr; error = IWCOPY((caddr_t)&ipn, ipnp, sizeof(ipn)); if (error) { error = EFAULT; goto junkput; } } else { for (n = nat_instances; n; n = n->nat_next) if (n->nat_fr == fr) break; if (!n) { error = ESRCH; goto junkput; } } } if (ipnn) KFREES(ipnn, sizeof(ipn) + ipn.ipn_dsize); nat_insert(nat); return 0; junkput: if (ipnn) KFREES(ipnn, sizeof(ipn) + ipn.ipn_dsize); if (nat) nat_delete(nat); return error; } /* * Delete a nat entry from the various lists and table. */ static void nat_delete(natd) struct nat *natd; { register struct nat **natp, *nat; struct ipnat *ipn; for (natp = natd->nat_hstart[0]; natp && (nat = *natp); natp = &nat->nat_hnext[0]) if (nat == natd) { *natp = nat->nat_hnext[0]; break; } for (natp = natd->nat_hstart[1]; natp && (nat = *natp); natp = &nat->nat_hnext[1]) if (nat == natd) { *natp = nat->nat_hnext[1]; break; } if (natd->nat_fr != NULL) { ATOMIC_DEC32(natd->nat_fr->fr_ref); } if (natd->nat_hm != NULL) nat_hostmapdel(natd->nat_hm); /* * If there is an active reference from the nat entry to its parent * rule, decrement the rule's reference count and free it too if no * longer being used. */ ipn = natd->nat_ptr; if (ipn != NULL) { ipn->in_space++; ipn->in_use--; if (!ipn->in_use && (ipn->in_flags & IPN_DELETE)) { if (ipn->in_apr) appr_free(ipn->in_apr); KFREE(ipn); nat_stats.ns_rules--; } } MUTEX_DESTROY(&natd->nat_lock); /* * If there's a fragment table entry too for this nat entry, then * dereference that as well. */ ipfr_forget((void *)natd); aps_free(natd->nat_aps); nat_stats.ns_inuse--; KFREE(natd); } /* * nat_flushtable - clear the NAT table of all mapping entries. */ static int nat_flushtable() { register nat_t *nat, **natp; register int j = 0; /* * ALL NAT mappings deleted, so lets just make the deletions * quicker. */ if (nat_table[0] != NULL) bzero((char *)nat_table[0], sizeof(nat_table[0]) * ipf_nattable_sz); if (nat_table[1] != NULL) bzero((char *)nat_table[1], sizeof(nat_table[1]) * ipf_nattable_sz); for (natp = &nat_instances; (nat = *natp); ) { *natp = nat->nat_next; nat_delete(nat); j++; } nat_stats.ns_inuse = 0; return j; } /* * nat_clearlist - delete all rules in the active NAT mapping list. */ static int nat_clearlist() { register ipnat_t *n, **np = &nat_list; int i = 0; if (nat_rules != NULL) bzero((char *)nat_rules, sizeof(*nat_rules) * ipf_natrules_sz); if (rdr_rules != NULL) bzero((char *)rdr_rules, sizeof(*rdr_rules) * ipf_rdrrules_sz); while ((n = *np)) { *np = n->in_next; if (!n->in_use) { if (n->in_apr) appr_free(n->in_apr); KFREE(n); nat_stats.ns_rules--; } else { n->in_flags |= IPN_DELETE; n->in_next = NULL; } i++; } nat_masks = 0; rdr_masks = 0; return i; } /* * Create a new NAT table entry. * NOTE: assumes write lock on ipf_nat has been obtained already. */ nat_t *nat_new(np, ip, fin, flags, direction) ipnat_t *np; ip_t *ip; fr_info_t *fin; u_int flags; int direction; { register u_32_t sum1, sum2, sumd, l; u_short port = 0, sport = 0, dport = 0, nport = 0; struct in_addr in, inb; tcphdr_t *tcp = NULL; hostmap_t *hm = NULL; nat_t *nat, *natl; u_short nflags; #if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) qif_t *qf = fin->fin_qif; #endif 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 */ KMALLOC(nat, nat_t *); if (nat == NULL) { nat_stats.ns_memfail++; return NULL; } bzero((char *)nat, sizeof(*nat)); nat->nat_flags = flags; /* * Search the current table for a match. */ if (direction == NAT_OUTBOUND) { /* * Values at which the search for a free resouce starts. */ u_32_t st_ip; u_short st_port; /* * If it's an outbound packet which doesn't match any existing * record, then create a new port */ l = 0; st_ip = np->in_nip; st_port = np->in_pnext; do { port = 0; in.s_addr = htonl(np->in_nip); if (l == 0) { /* * Check to see if there is an existing NAT * setup for this IP address pair. */ hm = nat_hostmap(np, ip->ip_src, in); if (hm != NULL) in.s_addr = hm->hm_mapip.s_addr; } else if ((l == 1) && (hm != NULL)) { nat_hostmapdel(hm); hm = NULL; } in.s_addr = ntohl(in.s_addr); nat->nat_hm = hm; if ((np->in_outmsk == 0xffffffff) && (np->in_pnext == 0)) { if (l > 0) goto badnat; } if (np->in_redir & NAT_MAPBLK) { if ((l >= np->in_ppip) || ((l > 0) && !(flags & IPN_TCPUDP))) goto badnat; /* * map-block - Calculate destination address. */ in.s_addr = ntohl(ip->ip_src.s_addr); in.s_addr &= ntohl(~np->in_inmsk); inb.s_addr = in.s_addr; in.s_addr /= np->in_ippip; in.s_addr &= ntohl(~np->in_outmsk); in.s_addr += ntohl(np->in_outip); /* * Calculate destination port. */ if ((flags & IPN_TCPUDP) && (np->in_ppip != 0)) { port = ntohs(sport) + l; port %= np->in_ppip; port += np->in_ppip * (inb.s_addr % np->in_ippip); port += MAPBLK_MINPORT; port = htons(port); } } else if (!np->in_outip && (np->in_outmsk == 0xffffffff)) { /* * 0/32 - use the interface's IP address. */ if ((l > 0) || fr_ifpaddr(4, fin->fin_ifp, &in) == -1) goto badnat; in.s_addr = ntohl(in.s_addr); } else if (!np->in_outip && !np->in_outmsk) { /* * 0/0 - use the original source address/port. */ if (l > 0) goto badnat; in.s_addr = ntohl(ip->ip_src.s_addr); } else if ((np->in_outmsk != 0xffffffff) && (np->in_pnext == 0) && ((l > 0) || (hm == NULL))) np->in_nip++; natl = NULL; if ((nflags & IPN_TCPUDP) && ((np->in_redir & NAT_MAPBLK) == 0) && (np->in_flags & IPN_AUTOPORTMAP)) { if ((l > 0) && (l % np->in_ppip == 0)) { if (l > np->in_space) { goto badnat; } else if ((l > np->in_ppip) && np->in_outmsk != 0xffffffff) np->in_nip++; } if (np->in_ppip != 0) { port = ntohs(sport); port += (l % np->in_ppip); port %= np->in_ppip; port += np->in_ppip * (ntohl(ip->ip_src.s_addr) % np->in_ippip); port += MAPBLK_MINPORT; port = htons(port); } } else if (((np->in_redir & NAT_MAPBLK) == 0) && (nflags & IPN_TCPUDP) && (np->in_pnext != 0)) { port = htons(np->in_pnext++); if (np->in_pnext > ntohs(np->in_pmax)) { np->in_pnext = ntohs(np->in_pmin); if (np->in_outmsk != 0xffffffff) np->in_nip++; } } if (np->in_flags & IPN_IPRANGE) { if (np->in_nip > ntohl(np->in_outmsk)) np->in_nip = ntohl(np->in_outip); } else { if ((np->in_outmsk != 0xffffffff) && ((np->in_nip + 1) & ntohl(np->in_outmsk)) > ntohl(np->in_outip)) np->in_nip = ntohl(np->in_outip) + 1; } if (!port && (flags & IPN_TCPUDP)) port = sport; /* * Here we do a lookup of the connection as seen from * the outside. If an IP# pair already exists, try * again. So if you have A->B becomes C->B, you can * also have D->E become C->E but not D->B causing * another C->B. Also take protocol and ports into * account when determining whether a pre-existing * NAT setup will cause an external conflict where * this is appropriate. */ inb.s_addr = htonl(in.s_addr); natl = nat_inlookup(fin->fin_ifp, flags & ~FI_WILDP, (u_int)ip->ip_p, ip->ip_dst, inb, (port << 16) | dport); /* * Has the search wrapped around and come back to the * start ? */ if ((natl != NULL) && (np->in_pnext != 0) && (st_port == np->in_pnext) && (np->in_nip != 0) && (st_ip == np->in_nip)) goto badnat; l++; } while (natl != NULL); if (np->in_space > 0) np->in_space--; /* 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; if (nat->nat_hm == NULL) nat->nat_hm = nat_hostmap(np, ip->ip_src, nat->nat_outip); sum1 = LONG_SUM(ntohl(ip->ip_src.s_addr)) + ntohs(sport); sum2 = LONG_SUM(in.s_addr) + ntohs(port); if (flags & IPN_TCPUDP) { nat->nat_inport = sport; nat->nat_outport = port; /* sport */ 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. */ if (np->in_flags & IPN_SPLIT) { in.s_addr = np->in_nip; if (np->in_inip == htonl(in.s_addr)) np->in_nip = ntohl(np->in_inmsk); else { np->in_nip = ntohl(np->in_inip); if (np->in_flags & IPN_ROUNDR) { nat_delrdr(np); nat_addrdr(np); } } } else { in.s_addr = ntohl(np->in_inip); if (np->in_flags & IPN_ROUNDR) { nat_delrdr(np); nat_addrdr(np); } } if (!np->in_pnext) nport = dport; else { /* * Whilst not optimized for the case where * pmin == pmax, the gain is not significant. */ nport = ntohs(dport) - ntohs(np->in_pmin) + ntohs(np->in_pnext); nport = htons(nport); } /* * When the redirect-to address is set to 0.0.0.0, just * assume a blank `forwarding' of the packet. We don't * setup any translation for this either. */ if (in.s_addr == 0) { if (nport == dport) goto badnat; in.s_addr = ntohl(ip->ip_dst.s_addr); } nat->nat_inip.s_addr = htonl(in.s_addr); nat->nat_outip = ip->ip_dst; nat->nat_oip = ip->ip_src; sum1 = LONG_SUM(ntohl(ip->ip_dst.s_addr)) + ntohs(dport); sum2 = LONG_SUM(in.s_addr) + ntohs(nport); if (flags & IPN_TCPUDP) { nat->nat_inport = nport; nat->nat_outport = dport; nat->nat_oport = sport; } } CALC_SUMD(sum1, sum2, sumd); nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); #if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) if ((flags == IPN_TCP) && dohwcksum && (qf->qf_ill->ill_ick.ick_magic == ICK_M_CTL_MAGIC)) { if (direction == NAT_OUTBOUND) sum1 = LONG_SUM(ntohl(in.s_addr)); else sum1 = LONG_SUM(ntohl(ip->ip_src.s_addr)); sum1 += LONG_SUM(ntohl(ip->ip_dst.s_addr)); sum1 += 30; sum1 = (sum1 & 0xffff) + (sum1 >> 16); nat->nat_sumd[1] = NAT_HW_CKSUM|(sum1 & 0xffff); } else #endif nat->nat_sumd[1] = nat->nat_sumd[0]; if ((flags & IPN_TCPUDP) && ((sport != port) || (dport != nport))) { if (direction == NAT_OUTBOUND) sum1 = LONG_SUM(ntohl(ip->ip_src.s_addr)); else sum1 = LONG_SUM(ntohl(ip->ip_dst.s_addr)); sum2 = LONG_SUM(in.s_addr); CALC_SUMD(sum1, sum2, sumd); nat->nat_ipsumd = (sumd & 0xffff) + (sumd >> 16); } else nat->nat_ipsumd = nat->nat_sumd[0]; in.s_addr = htonl(in.s_addr); #ifdef _KERNEL strncpy(nat->nat_ifname, IFNAME(fin->fin_ifp), IFNAMSIZ); #endif nat_insert(nat); nat->nat_dir = direction; nat->nat_ifp = fin->fin_ifp; nat->nat_ptr = np; nat->nat_p = ip->ip_p; nat->nat_bytes = 0; nat->nat_pkts = 0; nat->nat_fr = fin->fin_fr; if (nat->nat_fr != NULL) { ATOMIC_INC32(nat->nat_fr->fr_ref); } if (direction == NAT_OUTBOUND) { if (flags & IPN_TCPUDP) tcp->th_sport = port; } else { if (flags & IPN_TCPUDP) tcp->th_dport = nport; } np->in_use++; return nat; badnat: nat_stats.ns_badnat++; if ((hm = nat->nat_hm) != NULL) nat_hostmapdel(hm); KFREE(nat); return NULL; } void nat_insert(nat) nat_t *nat; { nat_t **natp; u_int hv; MUTEX_INIT(&nat->nat_lock, "nat entry lock", NULL); nat->nat_age = fr_defnatage; nat->nat_ifname[sizeof(nat->nat_ifname) - 1] = '\0'; if (nat->nat_ifname[0] !='\0') { nat->nat_ifp = GETUNIT(nat->nat_ifname, 4); } nat->nat_next = nat_instances; nat_instances = nat; hv = NAT_HASH_FN(nat->nat_inip.s_addr, nat->nat_inport, ipf_nattable_sz); natp = &nat_table[0][hv]; nat->nat_hstart[0] = natp; nat->nat_hnext[0] = *natp; *natp = nat; hv = NAT_HASH_FN(nat->nat_outip.s_addr, nat->nat_outport, ipf_nattable_sz); natp = &nat_table[1][hv]; nat->nat_hstart[1] = natp; nat->nat_hnext[1] = *natp; *natp = nat; nat_stats.ns_added++; nat_stats.ns_inuse++; } nat_t *nat_icmplookup(ip, fin, dir) ip_t *ip; fr_info_t *fin; int dir; { icmphdr_t *icmp; tcphdr_t *tcp = NULL; ip_t *oip; int flags = 0, type; icmp = (icmphdr_t *)fin->fin_dp; /* * Does it at least have the return (basic) IP header ? * Only a basic IP header (no options) should be with an ICMP error * header. */ if ((ip->ip_hl != 5) || (ip->ip_len < ICMPERR_MINPKTLEN)) return NULL; type = icmp->icmp_type; /* * If it's not an error type, then return. */ if ((type != ICMP_UNREACH) && (type != ICMP_SOURCEQUENCH) && (type != ICMP_REDIRECT) && (type != ICMP_TIMXCEED) && (type != ICMP_PARAMPROB)) return NULL; oip = (ip_t *)((char *)fin->fin_dp + 8); if (ip->ip_len < ICMPERR_MAXPKTLEN + ((oip->ip_hl - 5) << 2)) return NULL; if (oip->ip_p == IPPROTO_TCP) flags = IPN_TCP; else if (oip->ip_p == IPPROTO_UDP) flags = IPN_UDP; if (flags & IPN_TCPUDP) { tcp = (tcphdr_t *)((char *)oip + (oip->ip_hl << 2)); if (dir == NAT_INBOUND) return nat_inlookup(fin->fin_ifp, flags, (u_int)oip->ip_p, oip->ip_dst, oip->ip_src, (tcp->th_sport << 16) | tcp->th_dport); else return nat_outlookup(fin->fin_ifp, flags, (u_int)oip->ip_p, oip->ip_dst, oip->ip_src, (tcp->th_sport << 16) | tcp->th_dport); } if (dir == NAT_INBOUND) return nat_inlookup(fin->fin_ifp, 0, (u_int)oip->ip_p, oip->ip_dst, oip->ip_src, 0); else return nat_outlookup(fin->fin_ifp, 0, (u_int)oip->ip_p, oip->ip_dst, oip->ip_src, 0); } /* * This should *ONLY* be used for incoming packets to make sure a NAT'd ICMP * packet gets correctly recognised. */ nat_t *nat_icmp(ip, fin, nflags, dir) ip_t *ip; fr_info_t *fin; u_int *nflags; int dir; { u_32_t sum1, sum2, sumd; struct in_addr in; icmphdr_t *icmp; nat_t *nat; ip_t *oip; int flags = 0; if ((ip->ip_v != 4) || !(nat = nat_icmplookup(ip, fin, dir))) return NULL; *nflags = IPN_ICMPERR; icmp = (icmphdr_t *)fin->fin_dp; oip = (ip_t *)&icmp->icmp_ip; if (oip->ip_p == IPPROTO_TCP) flags = IPN_TCP; else if (oip->ip_p == IPPROTO_UDP) flags = IPN_UDP; /* * Need to adjust ICMP header to include the real IP#'s and * port #'s. Only apply a checksum change relative to the * IP address change is it will be modified again in ip_natout * for both address and port. Two checksum changes are * necessary for the two header address changes. Be careful * to only modify the checksum once for the port # and twice * for the IP#. */ if (nat->nat_dir == NAT_OUTBOUND) { sum1 = LONG_SUM(ntohl(oip->ip_src.s_addr)); in = nat->nat_inip; oip->ip_src = in; } else { sum1 = LONG_SUM(ntohl(oip->ip_dst.s_addr)); in = nat->nat_outip; oip->ip_dst = in; } sum2 = LONG_SUM(ntohl(in.s_addr)); CALC_SUMD(sum1, sum2, sumd); if (nat->nat_dir == NAT_OUTBOUND) { fix_incksum(&oip->ip_sum, sumd, 0); sumd += (sumd & 0xffff); while (sumd > 0xffff) sumd = (sumd & 0xffff) + (sumd >> 16); fix_outcksum(&icmp->icmp_cksum, sumd, 0); } else { fix_outcksum(&oip->ip_sum, sumd, 0); sumd += (sumd & 0xffff); while (sumd > 0xffff) sumd = (sumd & 0xffff) + (sumd >> 16); /* fix_incksum(&icmp->icmp_cksum, sumd, 0); */ } if ((flags & IPN_TCPUDP) != 0) { tcphdr_t *tcp; /* XXX - what if this is bogus hl and we go off the end ? */ tcp = (tcphdr_t *)((((char *)oip) + (oip->ip_hl << 2))); if (nat->nat_dir == NAT_OUTBOUND) { if (tcp->th_sport != nat->nat_inport) { sum1 = ntohs(tcp->th_sport); sum2 = ntohs(nat->nat_inport); CALC_SUMD(sum1, sum2, sumd); tcp->th_sport = nat->nat_inport; fix_outcksum(&icmp->icmp_cksum, sumd, 0); } } else { if (tcp->th_dport != nat->nat_outport) { sum1 = ntohs(tcp->th_dport); sum2 = ntohs(nat->nat_outport); CALC_SUMD(sum1, sum2, sumd); tcp->th_dport = nat->nat_outport; fix_incksum(&icmp->icmp_cksum, sumd, 0); } } } nat->nat_age = fr_defnaticmpage; 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(ifp, flags, p, src, mapdst, ports) void *ifp; register u_int flags, p; struct in_addr src , mapdst; u_32_t ports; { register u_short sport, mapdport; register nat_t *nat; register int nflags; u_int hv; mapdport = ports >> 16; sport = ports & 0xffff; flags &= IPN_TCPUDP; hv = NAT_HASH_FN(mapdst.s_addr, mapdport, ipf_nattable_sz); nat = nat_table[1][hv]; for (; nat; nat = nat->nat_hnext[1]) { nflags = nat->nat_flags; if ((!ifp || ifp == nat->nat_ifp) && nat->nat_oip.s_addr == src.s_addr && nat->nat_outip.s_addr == mapdst.s_addr && (((p == 0) && (flags == (nat->nat_flags & IPN_TCPUDP))) || (p == nat->nat_p)) && (!flags || (((nat->nat_oport == sport) || (nflags & FI_W_DPORT)) && ((nat->nat_outport == mapdport) || (nflags & FI_W_SPORT))))) 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(ifp, flags, p, src, dst, ports) void *ifp; register u_int flags, p; struct in_addr src , dst; u_32_t ports; { register u_short sport, dport; register nat_t *nat; register int nflags; u_int hv; sport = ports & 0xffff; dport = ports >> 16; flags &= IPN_TCPUDP; hv = NAT_HASH_FN(src.s_addr, sport, ipf_nattable_sz); nat = nat_table[0][hv]; for (; nat; nat = nat->nat_hnext[0]) { nflags = nat->nat_flags; if ((!ifp || ifp == nat->nat_ifp) && nat->nat_inip.s_addr == src.s_addr && nat->nat_oip.s_addr == dst.s_addr && (((p == 0) && (flags == (nat->nat_flags & IPN_TCPUDP))) || (p == nat->nat_p)) && (!flags || ((nat->nat_inport == sport || nflags & FI_W_SPORT) && (nat->nat_oport == dport || nflags & FI_W_DPORT)))) return nat; } return NULL; } /* * Lookup the NAT tables to search for a matching redirect */ nat_t *nat_lookupredir(np) register natlookup_t *np; { u_32_t ports; nat_t *nat; ports = (np->nl_outport << 16) | np->nl_inport; /* * 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(NULL, np->nl_flags, 0, np->nl_inip, np->nl_outip, ports))) { np->nl_realip = nat->nat_outip; np->nl_realport = nat->nat_outport; } return nat; } static int nat_match(fin, np, ip) fr_info_t *fin; ipnat_t *np; ip_t *ip; { frtuc_t *ft; if (ip->ip_v != 4) return 0; if (np->in_p && ip->ip_p != np->in_p) return 0; if (fin->fin_out) { if (!(np->in_redir && (NAT_MAP|NAT_MAPBLK))) return 0; if ((fin->fin_fi.fi_saddr & np->in_inmsk) != np->in_inip) return 0; if ((fin->fin_fi.fi_daddr & np->in_srcmsk) != np->in_srcip) return 0; } else { if (!(np->in_redir && NAT_REDIRECT)) return 0; } ft = &np->in_tuc; if (!(fin->fin_fi.fi_fl & FI_TCPUDP)) { if (ft->ftu_scmp || ft->ftu_dcmp) return 0; return 1; } return fr_tcpudpchk(ft, fin); } /* * Packets going out on the external interface go through this. * Here, the source address requires alteration, if anything. */ int ip_natout(ip, fin) ip_t *ip; fr_info_t *fin; { register ipnat_t *np = NULL; register u_32_t ipa; tcphdr_t *tcp = NULL; u_short sport = 0, dport = 0, *csump = NULL; struct ifnet *ifp; int natadd = 1; frentry_t *fr; u_int nflags = 0, hv, msk; u_32_t iph; nat_t *nat; int i; if (nat_list == NULL || (fr_nat_lock)) return 0; 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 & IP_OFFMASK) && !(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 & IPN_TCPUDP)) { tcp = (tcphdr_t *)fin->fin_dp; sport = tcp->th_sport; dport = tcp->th_dport; } } ipa = ip->ip_src.s_addr; READ_ENTER(&ipf_nat); if ((ip->ip_p == IPPROTO_ICMP) && (nat = nat_icmp(ip, fin, &nflags, NAT_OUTBOUND))) ; else if ((ip->ip_off & (IP_OFFMASK|IP_MF)) && (nat = ipfr_nat_knownfrag(ip, fin))) natadd = 0; else if ((nat = nat_outlookup(ifp, nflags, (u_int)ip->ip_p, ip->ip_src, ip->ip_dst, (dport << 16) | sport))) { nflags = nat->nat_flags; if ((nflags & (FI_W_SPORT|FI_W_DPORT)) != 0) { if ((nflags & FI_W_SPORT) && (nat->nat_inport != sport)) nat->nat_inport = sport; else if ((nflags & FI_W_DPORT) && (nat->nat_oport != dport)) nat->nat_oport = dport; if (nat->nat_outport == 0) nat->nat_outport = sport; nat->nat_flags &= ~(FI_W_DPORT|FI_W_SPORT); nflags = nat->nat_flags; } } else { RWLOCK_EXIT(&ipf_nat); WRITE_ENTER(&ipf_nat); /* * If there is no current entry in the nat table for this IP#, * create one for it (if there is a matching rule). */ msk = 0xffffffff; i = 32; maskloop: iph = ipa & htonl(msk); hv = NAT_HASH_FN(iph, 0, ipf_natrules_sz); for (np = nat_rules[hv]; np; np = np->in_mnext) { if ((np->in_ifp && (np->in_ifp != ifp)) || !np->in_space) continue; if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags)) continue; if (np->in_flags & IPN_FILTER) { if (!nat_match(fin, np, ip)) continue; } else if ((ipa & np->in_inmsk) != np->in_inip) continue; if (np->in_redir & (NAT_MAP|NAT_MAPBLK)) { if (*np->in_plabel && !appr_ok(ip, tcp, np)) continue; /* * If it's a redirection, then we don't want to * create new outgoing port stuff. * Redirections are only for incoming * connections. */ if (!(np->in_redir & (NAT_MAP|NAT_MAPBLK))) continue; if ((nat = nat_new(np, ip, fin, (u_int)nflags, NAT_OUTBOUND))) { np->in_hits++; #ifdef IPFILTER_LOG nat_log(nat, (u_int)np->in_redir); #endif break; } } } if ((np == NULL) && (i > 0)) { do { i--; msk <<= 1; } while ((i >= 0) && ((nat_masks & (1 << i)) == 0)); if (i >= 0) goto maskloop; } MUTEX_DOWNGRADE(&ipf_nat); } if (nat) { np = nat->nat_ptr; if (natadd && fin->fin_fi.fi_fl & FI_FRAG) ipfr_nat_newfrag(ip, fin, 0, nat); ip->ip_src = nat->nat_outip; MUTEX_ENTER(&nat->nat_lock); nat->nat_age = fr_defnatage; nat->nat_bytes += ip->ip_len; nat->nat_pkts++; MUTEX_EXIT(&nat->nat_lock); /* * Fix up checksums, not by recalculating them, but * simply computing adjustments. */ #if SOLARIS || defined(__sgi) if (nat->nat_dir == NAT_OUTBOUND) fix_outcksum(&ip->ip_sum, nat->nat_ipsumd, 0); else fix_incksum(&ip->ip_sum, nat->nat_ipsumd, 0); #endif if (!(ip->ip_off & IP_OFFMASK) && !(fin->fin_fi.fi_fl & FI_SHORT)) { if ((nat->nat_outport != 0) && (nflags & IPN_TCPUDP)) { tcp->th_sport = nat->nat_outport; fin->fin_data[0] = ntohs(tcp->th_sport); } if (ip->ip_p == IPPROTO_TCP) { csump = &tcp->th_sum; MUTEX_ENTER(&nat->nat_lock); fr_tcp_age(&nat->nat_age, nat->nat_tcpstate, fin, 1); if (nat->nat_age < fr_defnaticmpage) nat->nat_age = fr_defnaticmpage; #ifdef LARGE_NAT else if (nat->nat_age > fr_defnatage) nat->nat_age = fr_defnatage; #endif /* * Increase this because we may have * "keep state" following this too and * packet storms can occur if this is * removed too quickly. */ if (nat->nat_age == fr_tcpclosed) nat->nat_age = fr_tcplastack; MUTEX_EXIT(&nat->nat_lock); } 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) { nat->nat_age = fr_defnaticmpage; } if (csump) { if (nat->nat_dir == NAT_OUTBOUND) fix_outcksum(csump, nat->nat_sumd[1], ip->ip_len); else fix_incksum(csump, nat->nat_sumd[1], ip->ip_len); } } if ((np->in_apr != NULL) && (np->in_dport == 0 || (tcp != NULL && dport == np->in_dport))) { i = appr_check(ip, fin, nat); if (i == 0) i = 1; } else i = 1; ATOMIC_INCL(nat_stats.ns_mapped[1]); RWLOCK_EXIT(&ipf_nat); /* READ */ return i; } RWLOCK_EXIT(&ipf_nat); /* READ/WRITE */ return 0; } /* * Packets coming in from the external interface go through this. * Here, the destination address requires alteration, if anything. */ int ip_natin(ip, fin) ip_t *ip; fr_info_t *fin; { register struct in_addr src; register struct in_addr in; register ipnat_t *np; u_int nflags = 0, natadd = 1, hv, msk; struct ifnet *ifp = fin->fin_ifp; tcphdr_t *tcp = NULL; u_short sport = 0, dport = 0, *csump = NULL; nat_t *nat; u_32_t iph; int i; if ((nat_list == NULL) || (ip->ip_v != 4) || (fr_nat_lock)) return 0; if (!(ip->ip_off & IP_OFFMASK) && !(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 & IPN_TCPUDP)) { tcp = (tcphdr_t *)fin->fin_dp; dport = tcp->th_dport; sport = tcp->th_sport; } } in = ip->ip_dst; /* make sure the source address is to be redirected */ src = ip->ip_src; READ_ENTER(&ipf_nat); if ((ip->ip_p == IPPROTO_ICMP) && (nat = nat_icmp(ip, fin, &nflags, NAT_INBOUND))) ; else if ((ip->ip_off & IP_OFFMASK) && (nat = ipfr_nat_knownfrag(ip, fin))) natadd = 0; else if ((nat = nat_inlookup(fin->fin_ifp, nflags, (u_int)ip->ip_p, ip->ip_src, in, (dport << 16) | sport))) { nflags = nat->nat_flags; if ((nflags & (FI_W_SPORT|FI_W_DPORT)) != 0) { if ((nat->nat_oport != sport) && (nflags & FI_W_DPORT)) nat->nat_oport = sport; else if ((nat->nat_outport != dport) && (nflags & FI_W_SPORT)) nat->nat_outport = dport; nat->nat_flags &= ~(FI_W_SPORT|FI_W_DPORT); nflags = nat->nat_flags; } } else { RWLOCK_EXIT(&ipf_nat); WRITE_ENTER(&ipf_nat); /* * If there is no current entry in the nat table for this IP#, * create one for it (if there is a matching rule). */ msk = 0xffffffff; i = 32; maskloop: iph = in.s_addr & htonl(msk); hv = NAT_HASH_FN(iph, 0, ipf_rdrrules_sz); for (np = rdr_rules[hv]; np; np = np->in_rnext) { if ((np->in_ifp && (np->in_ifp != ifp)) || (np->in_p && (np->in_p != ip->ip_p)) || (np->in_flags && !(nflags & np->in_flags))) continue; if (np->in_flags & IPN_FILTER) { if (!nat_match(fin, np, ip)) continue; } else if ((in.s_addr & np->in_outmsk) != np->in_outip) continue; if ((np->in_redir & NAT_REDIRECT) && (!np->in_pmin || ((ntohs(np->in_pmax) >= ntohs(dport)) && (ntohs(dport) >= ntohs(np->in_pmin))))) if ((nat = nat_new(np, ip, fin, nflags, NAT_INBOUND))) { np->in_hits++; #ifdef IPFILTER_LOG nat_log(nat, (u_int)np->in_redir); #endif break; } } if ((np == NULL) && (i > 0)) { do { i--; msk <<= 1; } while ((i >= 0) && ((rdr_masks & (1 << i)) == 0)); if (i >= 0) goto maskloop; } MUTEX_DOWNGRADE(&ipf_nat); } if (nat) { np = nat->nat_ptr; fin->fin_fr = nat->nat_fr; if (natadd && fin->fin_fi.fi_fl & FI_FRAG) ipfr_nat_newfrag(ip, fin, 0, nat); if ((np->in_apr != NULL) && (np->in_dport == 0 || (tcp != NULL && sport == np->in_dport))) { i = appr_check(ip, fin, nat); if (i == -1) { RWLOCK_EXIT(&ipf_nat); return i; } } MUTEX_ENTER(&nat->nat_lock); if (nflags != IPN_ICMPERR) nat->nat_age = fr_defnatage; nat->nat_bytes += ip->ip_len; nat->nat_pkts++; MUTEX_EXIT(&nat->nat_lock); ip->ip_dst = nat->nat_inip; fin->fin_fi.fi_daddr = nat->nat_inip.s_addr; /* * Fix up checksums, not by recalculating them, but * simply computing adjustments. */ #if SOLARIS || defined(__sgi) if (nat->nat_dir == NAT_OUTBOUND) fix_incksum(&ip->ip_sum, nat->nat_ipsumd, 0); else fix_outcksum(&ip->ip_sum, nat->nat_ipsumd, 0); #endif if (!(ip->ip_off & IP_OFFMASK) && !(fin->fin_fi.fi_fl & FI_SHORT)) { if ((nat->nat_inport != 0) && (nflags & IPN_TCPUDP)) { tcp->th_dport = nat->nat_inport; fin->fin_data[1] = ntohs(tcp->th_dport); } if (ip->ip_p == IPPROTO_TCP) { csump = &tcp->th_sum; MUTEX_ENTER(&nat->nat_lock); fr_tcp_age(&nat->nat_age, nat->nat_tcpstate, fin, 0); if (nat->nat_age < fr_defnaticmpage) nat->nat_age = fr_defnaticmpage; #ifdef LARGE_NAT else if (nat->nat_age > fr_defnatage) nat->nat_age = fr_defnatage; #endif /* * Increase this because we may have * "keep state" following this too and * packet storms can occur if this is * removed too quickly. */ if (nat->nat_age == fr_tcpclosed) nat->nat_age = fr_tcplastack; MUTEX_EXIT(&nat->nat_lock); } 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) { nat->nat_age = fr_defnaticmpage; } if (csump) { if (nat->nat_dir == NAT_OUTBOUND) fix_incksum(csump, nat->nat_sumd[0], 0); else fix_outcksum(csump, nat->nat_sumd[0], 0); } } ATOMIC_INCL(nat_stats.ns_mapped[0]); RWLOCK_EXIT(&ipf_nat); /* READ */ return 1; } RWLOCK_EXIT(&ipf_nat); /* READ/WRITE */ return 0; } /* * Free all memory used by NAT structures allocated at runtime. */ void ip_natunload() { WRITE_ENTER(&ipf_nat); (void) nat_clearlist(); (void) nat_flushtable(); RWLOCK_EXIT(&ipf_nat); if (nat_table[0] != NULL) { KFREES(nat_table[0], sizeof(nat_t *) * ipf_nattable_sz); nat_table[0] = NULL; } if (nat_table[1] != NULL) { KFREES(nat_table[1], sizeof(nat_t *) * ipf_nattable_sz); nat_table[1] = NULL; } if (nat_rules != NULL) { KFREES(nat_rules, sizeof(ipnat_t *) * ipf_natrules_sz); nat_rules = NULL; } if (rdr_rules != NULL) { KFREES(rdr_rules, sizeof(ipnat_t *) * ipf_rdrrules_sz); rdr_rules = NULL; } if (maptable != NULL) { KFREES(maptable, sizeof(hostmap_t *) * ipf_hostmap_sz); maptable = NULL; } } /* * 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; #if defined(_KERNEL) && !SOLARIS int s; #endif SPL_NET(s); WRITE_ENTER(&ipf_nat); for (natp = &nat_instances; (nat = *natp); ) { nat->nat_age--; if (nat->nat_age) { natp = &nat->nat_next; continue; } *natp = nat->nat_next; #ifdef IPFILTER_LOG nat_log(nat, NL_EXPIRE); #endif nat_delete(nat); nat_stats.ns_expire++; } RWLOCK_EXIT(&ipf_nat); SPL_X(s); } /* */ void ip_natsync(ifp) void *ifp; { register ipnat_t *n; register nat_t *nat; register u_32_t sum1, sum2, sumd; struct in_addr in; ipnat_t *np; void *ifp2; #if defined(_KERNEL) && !SOLARIS int s; #endif /* * Change IP addresses for NAT sessions for any protocol except TCP * since it will break the TCP connection anyway. */ SPL_NET(s); WRITE_ENTER(&ipf_nat); for (nat = nat_instances; nat; nat = nat->nat_next) if (((ifp == NULL) || (ifp == nat->nat_ifp)) && !(nat->nat_flags & IPN_TCP) && (np = nat->nat_ptr) && (np->in_outmsk == 0xffffffff) && !np->in_nip) { ifp2 = nat->nat_ifp; /* * Change the map-to address to be the same as the * new one. */ sum1 = nat->nat_outip.s_addr; if (fr_ifpaddr(4, ifp2, &in) != -1) nat->nat_outip = in; sum2 = nat->nat_outip.s_addr; if (sum1 == sum2) continue; /* * Readjust the checksum adjustment to take into * account the new IP#. */ CALC_SUMD(sum1, sum2, sumd); /* XXX - dont change for TCP when solaris does * hardware checksumming. */ sumd += nat->nat_sumd[0]; nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); nat->nat_sumd[1] = nat->nat_sumd[0]; } for (n = nat_list; (n != NULL); n = n->in_next) if (n->in_ifp == ifp) { n->in_ifp = (void *)GETUNIT(n->in_ifname, 4); if (!n->in_ifp) n->in_ifp = (void *)-1; } RWLOCK_EXIT(&ipf_nat); SPL_X(s); } #ifdef IPFILTER_LOG void nat_log(nat, type) struct nat *nat; u_int type; { struct ipnat *np; struct natlog natl; void *items[1]; size_t sizes[1]; int rulen, types[1]; natl.nl_inip = nat->nat_inip; natl.nl_outip = nat->nat_outip; natl.nl_origip = nat->nat_oip; natl.nl_bytes = nat->nat_bytes; natl.nl_pkts = nat->nat_pkts; natl.nl_origport = nat->nat_oport; natl.nl_inport = nat->nat_inport; natl.nl_outport = nat->nat_outport; natl.nl_p = nat->nat_p; natl.nl_type = type; natl.nl_rule = -1; #ifndef LARGE_NAT if (nat->nat_ptr != NULL) { for (rulen = 0, np = nat_list; np; np = np->in_next, rulen++) if (np == nat->nat_ptr) { natl.nl_rule = rulen; break; } } #endif items[0] = &natl; sizes[0] = sizeof(natl); types[0] = 0; (void) ipllog(IPL_LOGNAT, NULL, items, sizes, types, 1); } #endif Index: head/sys/netinet/ip_nat.h =================================================================== --- head/sys/netinet/ip_nat.h (revision 60856) +++ head/sys/netinet/ip_nat.h (revision 60857) @@ -1,294 +1,295 @@ /* * Copyright (C) 1995-2000 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.1.2.3 2000/01/24 12:44:24 darrenr Exp $ * $FreeBSD$ */ #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', 60, struct ipnat *) #define SIOCRMNAT _IOW('r', 61, struct ipnat *) #define SIOCGNATS _IOWR('r', 62, struct natstat *) #define SIOCGNATL _IOWR('r', 63, struct natlookup *) #else #define SIOCADNAT _IOW(r, 60, struct ipnat *) #define SIOCRMNAT _IOW(r, 61, struct ipnat *) #define SIOCGNATS _IOWR(r, 62, struct natstat *) #define SIOCGNATL _IOWR(r, 63, struct natlookup *) #endif #undef LARGE_NAT /* define this if you're setting up a system to NAT * LARGE numbers of networks/hosts - i.e. in the * hundreds or thousands. In such a case, you should * also change the RDR_SIZE and NAT_SIZE below to more * appropriate sizes. The figures below were used for * a setup with 1000-2000 networks to NAT. */ #define NAT_SIZE 127 #define RDR_SIZE 127 #define HOSTMAP_SIZE 127 #define NAT_TABLE_SZ 127 #ifdef LARGE_NAT #undef NAT_SIZE #undef RDR_SIZE #undef NAT_TABLE_SZ #undef HOSTMAP_SIZE 127 #define NAT_SIZE 2047 #define RDR_SIZE 2047 #define NAT_TABLE_SZ 16383 #define HOSTMAP_SIZE 8191 #endif #ifndef APR_LABELLEN #define APR_LABELLEN 16 #endif #define NAT_HW_CKSUM 0x80000000 #define DEF_NAT_AGE 1200 /* 10 minutes (600 seconds) */ struct ap_session; typedef struct nat { u_long nat_age; int nat_flags; u_32_t nat_sumd[2]; u_32_t nat_ipsumd; void *nat_data; struct ap_session *nat_aps; /* proxy session */ struct frentry *nat_fr; /* filter rule ptr if appropriate */ struct in_addr nat_inip; struct in_addr nat_outip; struct in_addr nat_oip; /* other ip */ U_QUAD_T nat_pkts; U_QUAD_T nat_bytes; u_short nat_oport; /* other port */ u_short nat_inport; u_short nat_outport; u_short nat_use; u_char nat_tcpstate[2]; u_char nat_p; /* protocol for NAT */ struct ipnat *nat_ptr; /* pointer back to the rule */ struct hostmap *nat_hm; struct nat *nat_next; struct nat *nat_hnext[2]; struct nat **nat_hstart[2]; void *nat_ifp; int nat_dir; char nat_ifname[IFNAMSIZ]; #if SOLARIS || defined(_sgi) kmutex_t nat_lock; #endif } nat_t; typedef struct ipnat { struct ipnat *in_next; struct ipnat *in_rnext; struct ipnat **in_prnext; struct ipnat *in_mnext; struct ipnat **in_pmnext; void *in_ifp; void *in_apr; u_long in_space; u_int in_use; u_int in_hits; struct in_addr in_nextip; u_short in_pnext; u_short in_ppip; /* ports per IP */ u_short in_ippip; /* IP #'s per IP# */ u_short in_flags; /* From here to in_dport must be reflected */ u_short in_port[2]; /* correctly in IPN_CMPSIZ */ struct in_addr in_in[2]; struct in_addr in_out[2]; struct in_addr in_src[2]; struct frtuc in_tuc; int in_redir; /* 0 if it's a mapping, 1 if it's a hard redir */ char in_ifname[IFNAMSIZ]; char in_plabel[APR_LABELLEN]; /* proxy label */ char in_p; /* protocol */ } 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 in_srcip in_src[0].s_addr #define in_srcmsk in_src[1].s_addr #define in_scmp in_tuc.ftu_scmp #define in_dcmp in_tuc.ftu_dcmp #define in_stop in_tuc.ftu_stop #define in_dtop in_tuc.ftu_dtop #define in_sport in_tuc.ftu_sport #define in_dport in_tuc.ftu_dport #define NAT_OUTBOUND 0 #define NAT_INBOUND 1 #define NAT_MAP 0x01 #define NAT_REDIRECT 0x02 #define NAT_BIMAP (NAT_MAP|NAT_REDIRECT) #define NAT_MAPBLK 0x04 #define MAPBLK_MINPORT 1024 /* don't use reserved ports for src port */ #define USABLE_PORTS (65536 - MAPBLK_MINPORT) #define IPN_CMPSIZ (sizeof(ipnat_t) - offsetof(ipnat_t, in_flags)) typedef struct natlookup { struct in_addr nl_inip; struct in_addr nl_outip; struct in_addr nl_realip; int nl_flags; u_short nl_inport; u_short nl_outport; u_short nl_realport; } natlookup_t; typedef struct nat_save { void *ipn_next; struct nat ipn_nat; struct ipnat ipn_ipnat; struct frentry ipn_fr; int ipn_dsize; char ipn_data[4]; } nat_save_t; #define ipn_rule ipn_nat.nat_fr typedef struct natget { void *ng_ptr; int ng_sz; } natget_t; typedef struct hostmap { struct hostmap *hm_next; struct hostmap **hm_pnext; struct ipnat *hm_ipnat; struct in_addr hm_realip; struct in_addr hm_mapip; int hm_ref; } hostmap_t; typedef struct natstat { u_long ns_mapped[2]; u_long ns_rules; u_long ns_added; u_long ns_expire; u_long ns_inuse; u_long ns_logged; u_long ns_logfail; u_long ns_memfail; u_long ns_badnat; nat_t **ns_table[2]; ipnat_t *ns_list; void *ns_apslist; u_int ns_nattab_sz; u_int ns_rultab_sz; u_int ns_rdrtab_sz; nat_t *ns_instances; } natstat_t; #define IPN_ANY 0x000 #define IPN_TCP 0x001 #define IPN_UDP 0x002 #define IPN_TCPUDP (IPN_TCP|IPN_UDP) #define IPN_DELETE 0x004 #define IPN_ICMPERR 0x008 #define IPN_RF (IPN_TCPUDP|IPN_DELETE|IPN_ICMPERR) #define IPN_AUTOPORTMAP 0x010 #define IPN_IPRANGE 0x020 #define IPN_USERFLAGS (IPN_TCPUDP|IPN_AUTOPORTMAP|IPN_IPRANGE|\ IPN_SPLIT|IPN_ROUNDR|IPN_FILTER) #define IPN_FILTER 0x040 #define IPN_SPLIT 0x080 #define IPN_ROUNDR 0x100 typedef struct natlog { struct in_addr nl_origip; struct in_addr nl_outip; struct in_addr nl_inip; u_short nl_origport; u_short nl_outport; u_short nl_inport; u_short nl_type; int nl_rule; U_QUAD_T nl_pkts; U_QUAD_T nl_bytes; u_char nl_p; } natlog_t; #define NL_NEWMAP NAT_MAP #define NL_NEWRDR NAT_REDIRECT #define NL_EXPIRE 0xffff #define NAT_HASH_FN(k,l,m) (((k) + ((k) >> 12) + l) % (m)) #define LONG_SUM(in) (((in) & 0xffff) + ((in) >> 16)) #define CALC_SUMD(s1, s2, sd) { \ (s1) = ((s1) & 0xffff) + ((s1) >> 16); \ (s2) = ((s2) & 0xffff) + ((s2) >> 16); \ /* Do it twice */ \ (s1) = ((s1) & 0xffff) + ((s1) >> 16); \ (s2) = ((s2) & 0xffff) + ((s2) >> 16); \ /* Because ~1 == -2, We really need ~1 == -1 */ \ if ((s1) > (s2)) (s2)--; \ (sd) = (s2) - (s1); \ (sd) = ((sd) & 0xffff) + ((sd) >> 16); } extern u_int ipf_nattable_sz; extern u_int ipf_natrules_sz; extern u_int ipf_rdrrules_sz; extern int fr_nat_lock; extern void ip_natsync __P((void *)); extern u_long fr_defnatage; extern u_long fr_defnaticmpage; extern nat_t **nat_table[2]; extern nat_t *nat_instances; extern ipnat_t **nat_rules; extern ipnat_t **rdr_rules; extern natstat_t nat_stats; #if defined(__NetBSD__) || defined(__OpenBSD__) || (__FreeBSD_version >= 300003) extern int nat_ioctl __P((caddr_t, u_long, int)); #else extern int nat_ioctl __P((caddr_t, int, int)); #endif extern int nat_init __P((void)); extern nat_t *nat_new __P((ipnat_t *, ip_t *, fr_info_t *, u_int, int)); extern nat_t *nat_outlookup __P((void *, u_int, u_int, struct in_addr, struct in_addr, u_32_t)); extern nat_t *nat_inlookup __P((void *, u_int, u_int, struct in_addr, struct in_addr, u_32_t)); extern nat_t *nat_maplookup __P((void *, u_int, struct in_addr, struct in_addr)); extern nat_t *nat_lookupredir __P((natlookup_t *)); extern nat_t *nat_icmplookup __P((ip_t *, fr_info_t *, int)); extern nat_t *nat_icmp __P((ip_t *, fr_info_t *, u_int *, int)); extern void nat_insert __P((nat_t *)); +extern int ip_natout __P((ip_t *, fr_info_t *)); extern int ip_natin __P((ip_t *, fr_info_t *)); extern void ip_natunload __P((void)), ip_natexpire __P((void)); extern void nat_log __P((struct nat *, u_int)); extern void fix_incksum __P((u_short *, u_32_t, int)); extern void fix_outcksum __P((u_short *, u_32_t, int)); #endif /* __IP_NAT_H__ */ Index: head/sys/netinet/ip_raudio_pxy.c =================================================================== --- head/sys/netinet/ip_raudio_pxy.c (revision 60856) +++ head/sys/netinet/ip_raudio_pxy.c (revision 60857) @@ -1,303 +1,307 @@ /* * $FreeBSD$ */ #if SOLARIS && defined(_KERNEL) extern kmutex_t ipf_rw; #endif #define IPF_RAUDIO_PROXY int ippr_raudio_init __P((void)); int ippr_raudio_new __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *)); int ippr_raudio_in __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *)); int ippr_raudio_out __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *)); static frentry_t raudiofr; /* * Real Audio application proxy initialization. */ int ippr_raudio_init() { bzero((char *)&raudiofr, sizeof(raudiofr)); raudiofr.fr_ref = 1; raudiofr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; return 0; } /* * Setup for a new proxy to handle Real Audio. */ int ippr_raudio_new(fin, ip, aps, nat) fr_info_t *fin; ip_t *ip; ap_session_t *aps; nat_t *nat; { raudio_t *rap; KMALLOCS(aps->aps_data, void *, sizeof(raudio_t)); - if (aps->aps_data != NULL) { - bzero(aps->aps_data, sizeof(raudio_t)); - rap = aps->aps_data; - aps->aps_psiz = sizeof(raudio_t); - rap->rap_mode = RAP_M_TCP; /* default is for TCP */ - } + if (aps->aps_data == NULL) + return -1; + + bzero(aps->aps_data, sizeof(raudio_t)); + rap = aps->aps_data; + aps->aps_psiz = sizeof(raudio_t); + rap->rap_mode = RAP_M_TCP; /* default is for TCP */ return 0; } int ippr_raudio_out(fin, ip, aps, nat) fr_info_t *fin; ip_t *ip; ap_session_t *aps; nat_t *nat; { raudio_t *rap = aps->aps_data; unsigned char membuf[512 + 1], *s; u_short id = 0; tcphdr_t *tcp; int off, dlen; int len = 0; mb_t *m; #if SOLARIS mb_t *m1; #endif /* * If we've already processed the start messages, then nothing left * for the proxy to do. */ if (rap->rap_eos == 1) return 0; tcp = (tcphdr_t *)fin->fin_dp; off = (ip->ip_hl << 2) + (tcp->th_off << 2); bzero(membuf, sizeof(membuf)); #if SOLARIS m = fin->fin_qfm; dlen = msgdsize(m) - off; if (dlen <= 0) return 0; copyout_mblk(m, off, MIN(sizeof(membuf), dlen), (char *)membuf); #else m = *(mb_t **)fin->fin_mp; dlen = mbufchainlen(m) - off; if (dlen <= 0) return 0; m_copydata(m, off, MIN(sizeof(membuf), dlen), (char *)membuf); #endif /* * In all the startup parsing, ensure that we don't go outside * the packet buffer boundary. */ /* * Look for the start of connection "PNA" string if not seen yet. */ if (rap->rap_seenpna == 0) { s = (u_char *)memstr("PNA", (char *)membuf, 3, dlen); if (s == NULL) return 0; s += 3; rap->rap_seenpna = 1; } else s = membuf; /* * Directly after the PNA will be the version number of this * connection. */ if (rap->rap_seenpna == 1 && rap->rap_seenver == 0) { if ((s + 1) - membuf < dlen) { rap->rap_version = (*s << 8) | *(s + 1); s += 2; rap->rap_seenver = 1; } else return 0; } /* * Now that we've been past the PNA and version number, we're into the * startup messages block. This ends when a message with an ID of 0. */ while ((rap->rap_eos == 0) && ((s + 1) - membuf < dlen)) { if (rap->rap_gotid == 0) { id = (*s << 8) | *(s + 1); s += 2; rap->rap_gotid = 1; if (id == RA_ID_END) { rap->rap_eos = 1; break; } } else if (rap->rap_gotlen == 0) { len = (*s << 8) | *(s + 1); s += 2; rap->rap_gotlen = 1; } if (rap->rap_gotid == 1 && rap->rap_gotlen == 1) { if (id == RA_ID_UDP) { rap->rap_mode &= ~RAP_M_TCP; rap->rap_mode |= RAP_M_UDP; rap->rap_plport = (*s << 8) | *(s + 1); } else if (id == RA_ID_ROBUST) { rap->rap_mode |= RAP_M_ROBUST; rap->rap_prport = (*s << 8) | *(s + 1); } s += len; rap->rap_gotlen = 0; rap->rap_gotid = 0; } } return 0; } int ippr_raudio_in(fin, ip, aps, nat) fr_info_t *fin; ip_t *ip; ap_session_t *aps; nat_t *nat; { unsigned char membuf[IPF_MAXPORTLEN + 1], *s; tcphdr_t *tcp, tcph, *tcp2 = &tcph; raudio_t *rap = aps->aps_data; struct in_addr swa, swb; u_int a1, a2, a3, a4; + int off, dlen, slen; u_short sp, dp; - int off, dlen; fr_info_t fi; tcp_seq seq; nat_t *ipn; u_char swp; mb_t *m; #if SOLARIS mb_t *m1; #endif /* * Wait until we've seen the end of the start messages and even then * only proceed further if we're using UDP. If they want to use TCP * then data is sent back on the same channel that is already open. */ if (rap->rap_sdone != 0) return 0; tcp = (tcphdr_t *)fin->fin_dp; off = (ip->ip_hl << 2) + (tcp->th_off << 2); m = *(mb_t **)fin->fin_mp; #if SOLARIS m = fin->fin_qfm; dlen = msgdsize(m) - off; if (dlen <= 0) return 0; bzero(membuf, sizeof(membuf)); copyout_mblk(m, off, MIN(sizeof(membuf), dlen), (char *)membuf); #else dlen = mbufchainlen(m) - off; if (dlen <= 0) return 0; bzero(membuf, sizeof(membuf)); m_copydata(m, off, MIN(sizeof(membuf), dlen), (char *)membuf); #endif seq = ntohl(tcp->th_seq); /* * Check to see if the data in this packet is of interest to us. * We only care for the first 19 bytes coming back from the server. */ if (rap->rap_sseq == 0) { s = (u_char *)memstr("PNA", (char *)membuf, 3, dlen); if (s == NULL) return 0; a1 = s - membuf; dlen -= a1; a1 = 0; rap->rap_sseq = seq; a2 = MIN(dlen, sizeof(rap->rap_svr)); } else if (seq <= rap->rap_sseq + sizeof(rap->rap_svr)) { /* * seq # which is the start of data and from that the offset * into the buffer array. */ a1 = seq - rap->rap_sseq; a2 = MIN(dlen, sizeof(rap->rap_svr)); a2 -= a1; s = membuf; } else return 0; for (a3 = a1, a4 = a2; (a4 > 0) && (a3 < 19) && (a3 >= 0); a4--,a3++) { rap->rap_sbf |= (1 << a3); rap->rap_svr[a3] = *s++; } if ((rap->rap_sbf != 0x7ffff) || (!rap->rap_eos)) /* 19 bits */ return 0; rap->rap_sdone = 1; s = (u_char *)rap->rap_svr + 11; if (((*s << 8) | *(s + 1)) == RA_ID_ROBUST) { s += 2; rap->rap_srport = (*s << 8) | *(s + 1); } swp = ip->ip_p; swa = ip->ip_src; swb = ip->ip_dst; ip->ip_p = IPPROTO_UDP; ip->ip_src = nat->nat_inip; ip->ip_dst = nat->nat_oip; bcopy((char *)fin, (char *)&fi, sizeof(fi)); bzero((char *)tcp2, sizeof(*tcp2)); + tcp2->th_off = 5; fi.fin_dp = (char *)tcp2; fi.fin_fr = &raudiofr; tcp2->th_win = htons(8192); + slen = ip->ip_len; + ip->ip_len = fin->fin_hlen + sizeof(*tcp); if (((rap->rap_mode & RAP_M_UDP_ROBUST) == RAP_M_UDP_ROBUST) && (rap->rap_srport != 0)) { dp = rap->rap_srport; sp = rap->rap_prport; tcp2->th_sport = htons(sp); tcp2->th_dport = htons(dp); fi.fin_data[0] = dp; fi.fin_data[1] = sp; ipn = nat_new(nat->nat_ptr, ip, &fi, - IPN_UDP | (sp ? 0 : FI_W_SPORT), - NAT_OUTBOUND); + IPN_UDP | (sp ? 0 : FI_W_SPORT), NAT_OUTBOUND); if (ipn != NULL) { ipn->nat_age = fr_defnatage; (void) fr_addstate(ip, &fi, sp ? 0 : FI_W_SPORT); } } if ((rap->rap_mode & RAP_M_UDP) == RAP_M_UDP) { sp = rap->rap_plport; tcp2->th_sport = htons(sp); tcp2->th_dport = 0; /* XXX - don't specify remote port */ fi.fin_data[0] = sp; fi.fin_data[1] = 0; ipn = nat_new(nat->nat_ptr, ip, &fi, IPN_UDP|FI_W_DPORT, NAT_OUTBOUND); if (ipn != NULL) { ipn->nat_age = fr_defnatage; (void) fr_addstate(ip, &fi, FI_W_DPORT); } } - + ip->ip_p = swp; + ip->ip_len = slen; ip->ip_src = swa; ip->ip_dst = swb; return 0; } Index: head/sys/netinet/ip_rcmd_pxy.c =================================================================== --- head/sys/netinet/ip_rcmd_pxy.c (revision 60856) +++ head/sys/netinet/ip_rcmd_pxy.c (revision 60857) @@ -1,157 +1,166 @@ /* + * $Id: ip_rcmd_pxy.c,v 1.4.2.1 2000/05/06 11:19:34 darrenr Exp $ + */ +/* * Simple RCMD transparent proxy for in-kernel use. For use with the NAT * code. * $FreeBSD$ */ #if SOLARIS && defined(_KERNEL) extern kmutex_t ipf_rw; #endif #define isdigit(x) ((x) >= '0' && (x) <= '9') #define IPF_RCMD_PROXY int ippr_rcmd_init __P((void)); int ippr_rcmd_new __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *)); int ippr_rcmd_out __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *)); u_short ipf_rcmd_atoi __P((char *)); int ippr_rcmd_portmsg __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *)); static frentry_t rcmdfr; /* * RCMD application proxy initialization. */ int ippr_rcmd_init() { bzero((char *)&rcmdfr, sizeof(rcmdfr)); rcmdfr.fr_ref = 1; rcmdfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; return 0; } /* * Setup for a new RCMD proxy. */ int ippr_rcmd_new(fin, ip, aps, nat) fr_info_t *fin; ip_t *ip; ap_session_t *aps; nat_t *nat; { tcphdr_t *tcp = (tcphdr_t *)fin->fin_dp; aps->aps_psiz = sizeof(u_32_t); KMALLOCS(aps->aps_data, u_32_t *, sizeof(u_32_t)); if (aps->aps_data == NULL) return -1; *(u_32_t *)aps->aps_data = 0; aps->aps_sport = tcp->th_sport; aps->aps_dport = tcp->th_dport; return 0; } /* * ipf_rcmd_atoi - implement a simple version of atoi */ u_short ipf_rcmd_atoi(ptr) char *ptr; { register char *s = ptr, c; register u_short i = 0; while ((c = *s++) && isdigit(c)) { i *= 10; i += c - '0'; } return i; } int ippr_rcmd_portmsg(fin, ip, aps, nat) fr_info_t *fin; ip_t *ip; ap_session_t *aps; nat_t *nat; { char portbuf[8], *s; struct in_addr swip; u_short sp, dp; int off, dlen; tcphdr_t *tcp, tcph, *tcp2 = &tcph; fr_info_t fi; nat_t *ipn; mb_t *m; #if SOLARIS mb_t *m1; #endif tcp = (tcphdr_t *)fin->fin_dp; off = (ip->ip_hl << 2) + (tcp->th_off << 2); m = *(mb_t **)fin->fin_mp; #if SOLARIS m = fin->fin_qfm; dlen = msgdsize(m) - off; bzero(portbuf, sizeof(portbuf)); copyout_mblk(m, off, MIN(sizeof(portbuf), dlen), portbuf); #else dlen = mbufchainlen(m) - off; bzero(portbuf, sizeof(portbuf)); m_copydata(m, off, MIN(sizeof(portbuf), dlen), portbuf); #endif if ((*(u_32_t *)aps->aps_data != 0) && (tcp->th_seq != *(u_32_t *)aps->aps_data)) return 0; portbuf[sizeof(portbuf) - 1] = '\0'; s = portbuf; sp = ipf_rcmd_atoi(s); if (!sp) return 0; /* * Add skeleton NAT entry for connection which will come back the * other way. */ sp = htons(sp); dp = htons(fin->fin_data[1]); ipn = nat_outlookup(fin->fin_ifp, IPN_TCP, nat->nat_p, nat->nat_inip, ip->ip_dst, (dp << 16) | sp); if (ipn == NULL) { + int slen; + + slen = ip->ip_len; + ip->ip_len = fin->fin_hlen + sizeof(*tcp); bcopy((char *)fin, (char *)&fi, sizeof(fi)); bzero((char *)tcp2, sizeof(*tcp2)); tcp2->th_win = htons(8192); tcp2->th_sport = sp; tcp2->th_dport = 0; /* XXX - don't specify remote port */ + tcp2->th_off = 5; fi.fin_data[0] = ntohs(sp); fi.fin_data[1] = 0; fi.fin_dp = (char *)tcp2; swip = ip->ip_src; ip->ip_src = nat->nat_inip; ipn = nat_new(nat->nat_ptr, ip, &fi, IPN_TCP|FI_W_DPORT, NAT_OUTBOUND); if (ipn != NULL) { ipn->nat_age = fr_defnatage; fi.fin_fr = &rcmdfr; (void) fr_addstate(ip, &fi, FI_W_DPORT); } + ip->ip_len = slen; ip->ip_src = swip; } return 0; } int ippr_rcmd_out(fin, ip, aps, nat) fr_info_t *fin; ip_t *ip; ap_session_t *aps; nat_t *nat; { return ippr_rcmd_portmsg(fin, ip, aps, nat); } Index: head/sys/netinet/ipl.h =================================================================== --- head/sys/netinet/ipl.h (revision 60856) +++ head/sys/netinet/ipl.h (revision 60857) @@ -1,17 +1,17 @@ /* - * Copyright (C) 1993-1999 by Darren Reed. + * Copyright (C) 1993-2000 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. * * @(#)ipl.h 1.21 6/5/96 * $FreeBSD$ */ #ifndef __IPL_H__ #define __IPL_H__ -#define IPL_VERSION "IP Filter: v3.3.8" +#define IPL_VERSION "IP Filter: v3.4.4" #endif Index: head/sys/netinet/mlfk_ipl.c =================================================================== --- head/sys/netinet/mlfk_ipl.c (revision 60856) +++ head/sys/netinet/mlfk_ipl.c (revision 60857) @@ -1,180 +1,181 @@ /* * Copyright 1999 Guido van Rooij. All rights reserved. * * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE HOLDER OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ + #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static dev_t ipf_devs[IPL_LOGMAX + 1]; SYSCTL_DECL(_net_inet); SYSCTL_NODE(_net_inet, OID_AUTO, ipf, CTLFLAG_RW, 0, "IPF"); SYSCTL_INT(_net_inet_ipf, OID_AUTO, fr_flags, CTLFLAG_RW, &fr_flags, 0, ""); SYSCTL_INT(_net_inet_ipf, OID_AUTO, fr_pass, CTLFLAG_RW, &fr_pass, 0, ""); SYSCTL_INT(_net_inet_ipf, OID_AUTO, fr_active, CTLFLAG_RD, &fr_active, 0, ""); SYSCTL_INT(_net_inet_ipf, OID_AUTO, fr_tcpidletimeout, CTLFLAG_RW, &fr_tcpidletimeout, 0, ""); SYSCTL_INT(_net_inet_ipf, OID_AUTO, fr_tcpclosewait, CTLFLAG_RW, &fr_tcpclosewait, 0, ""); SYSCTL_INT(_net_inet_ipf, OID_AUTO, fr_tcplastack, CTLFLAG_RW, &fr_tcplastack, 0, ""); SYSCTL_INT(_net_inet_ipf, OID_AUTO, fr_tcptimeout, CTLFLAG_RW, &fr_tcptimeout, 0, ""); SYSCTL_INT(_net_inet_ipf, OID_AUTO, fr_tcpclosed, CTLFLAG_RW, &fr_tcpclosed, 0, ""); SYSCTL_INT(_net_inet_ipf, OID_AUTO, fr_udptimeout, CTLFLAG_RW, &fr_udptimeout, 0, ""); SYSCTL_INT(_net_inet_ipf, OID_AUTO, fr_icmptimeout, CTLFLAG_RW, &fr_icmptimeout, 0, ""); SYSCTL_INT(_net_inet_ipf, OID_AUTO, fr_defnatage, CTLFLAG_RW, &fr_defnatage, 0, ""); SYSCTL_INT(_net_inet_ipf, OID_AUTO, fr_ipfrttl, CTLFLAG_RW, &fr_ipfrttl, 0, ""); SYSCTL_INT(_net_inet_ipf, OID_AUTO, ipl_unreach, CTLFLAG_RW, &ipl_unreach, 0, ""); SYSCTL_INT(_net_inet_ipf, OID_AUTO, fr_running, CTLFLAG_RD, &fr_running, 0, ""); SYSCTL_INT(_net_inet_ipf, OID_AUTO, fr_authsize, CTLFLAG_RD, &fr_authsize, 0, ""); SYSCTL_INT(_net_inet_ipf, OID_AUTO, fr_authused, CTLFLAG_RD, &fr_authused, 0, ""); SYSCTL_INT(_net_inet_ipf, OID_AUTO, fr_defaultauthage, CTLFLAG_RW, &fr_defaultauthage, 0, ""); #define CDEV_MAJOR 79 static struct cdevsw ipl_cdevsw = { /* open */ iplopen, /* close */ iplclose, /* read */ iplread, /* write */ nowrite, /* ioctl */ iplioctl, /* poll */ nopoll, /* mmap */ nommap, /* strategy */ nostrategy, /* name */ "ipl", /* maj */ CDEV_MAJOR, /* dump */ nodump, /* psize */ nopsize, /* flags */ 0, /* bmaj */ -1 }; static int ipfilter_modevent(module_t mod, int type, void *unused) { char *c; int i, error = 0; switch (type) { case MOD_LOAD : + error = iplattach(); if (error) break; c = NULL; for(i=strlen(IPL_NAME); i>0; i--) if (IPL_NAME[i] == '/') { c = &IPL_NAME[i+1]; break; } if (!c) c = IPL_NAME; ipf_devs[IPL_LOGIPF] = make_dev(&ipl_cdevsw, IPL_LOGIPF, 0, 0, 0600, c); c = NULL; for(i=strlen(IPL_NAT); i>0; i--) if (IPL_NAT[i] == '/') { c = &IPL_NAT[i+1]; break; } if (!c) c = IPL_NAT; ipf_devs[IPL_LOGNAT] = make_dev(&ipl_cdevsw, IPL_LOGNAT, 0, 0, 0600, c); c = NULL; for(i=strlen(IPL_STATE); i>0; i--) if (IPL_STATE[i] == '/') { c = &IPL_STATE[i+1]; break; } if (!c) c = IPL_STATE; ipf_devs[IPL_LOGSTATE] = make_dev(&ipl_cdevsw, IPL_LOGSTATE, 0, 0, 0600, c); c = NULL; for(i=strlen(IPL_AUTH); i>0; i--) if (IPL_AUTH[i] == '/') { c = &IPL_AUTH[i+1]; break; } if (!c) c = IPL_AUTH; ipf_devs[IPL_LOGAUTH] = make_dev(&ipl_cdevsw, IPL_LOGAUTH, 0, 0, 0600, c); break; case MOD_UNLOAD : destroy_dev(ipf_devs[IPL_LOGIPF]); destroy_dev(ipf_devs[IPL_LOGNAT]); destroy_dev(ipf_devs[IPL_LOGSTATE]); destroy_dev(ipf_devs[IPL_LOGAUTH]); - cdevsw_remove(&ipl_cdevsw); error = ipldetach(); break; default: error = EINVAL; break; } return error; } static moduledata_t ipfiltermod = { - "ipfilter", + IPL_VERSION, ipfilter_modevent, 0 }; DECLARE_MODULE(ipfilter, ipfiltermod, SI_SUB_PROTO_DOMAIN, SI_ORDER_ANY);