diff options
Diffstat (limited to 'sys/contrib/ipfilter/netinet/ip_nat.c')
-rw-r--r-- | sys/contrib/ipfilter/netinet/ip_nat.c | 465 |
1 files changed, 282 insertions, 183 deletions
diff --git a/sys/contrib/ipfilter/netinet/ip_nat.c b/sys/contrib/ipfilter/netinet/ip_nat.c index 363ab24..60dd7ed 100644 --- a/sys/contrib/ipfilter/netinet/ip_nat.c +++ b/sys/contrib/ipfilter/netinet/ip_nat.c @@ -10,6 +10,9 @@ #define _KERNEL #endif +#ifdef __sgi +# include <sys/ptimers.h> +#endif #include <sys/errno.h> #include <sys/types.h> #include <sys/param.h> @@ -31,7 +34,6 @@ # include <sys/ioctl.h> #endif #include <sys/fcntl.h> -#include <sys/uio.h> #ifndef linux # include <sys/protosw.h> #endif @@ -93,10 +95,10 @@ extern struct ifnet vpnif; #include "netinet/ip_compat.h" #include <netinet/tcpip.h> #include "netinet/ip_fil.h" -#include "netinet/ip_proxy.h" #include "netinet/ip_nat.h" #include "netinet/ip_frag.h" #include "netinet/ip_state.h" +#include "netinet/ip_proxy.h" #if (__FreeBSD_version >= 300000) # include <sys/malloc.h> #endif @@ -135,7 +137,6 @@ 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 *)); @@ -144,7 +145,7 @@ 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 void nat_tabmove __P((nat_t *, u_32_t)); +static void nat_tabmove __P((fr_info_t *, nat_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)); @@ -503,7 +504,7 @@ int mode; 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); + n->in_apr = appr_lookup(n->in_p, n->in_plabel); if (!n->in_apr) { error = ENOENT; break; @@ -904,6 +905,7 @@ caddr_t data; nat->nat_ptr = NULL; nat->nat_hm = NULL; nat->nat_data = NULL; + nat->nat_ifp = GETUNIT(nat->nat_ifname, 4); /* * Restore the rule associated with this nat session @@ -925,7 +927,7 @@ caddr_t data; 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); + in->in_apr = appr_lookup(in->in_p, in->in_plabel); } } @@ -1018,6 +1020,8 @@ struct nat *natd; if (natd->nat_hnext[1]) natd->nat_hnext[1]->nat_phnext[1] = natd->nat_phnext[1]; *natd->nat_phnext[1] = natd->nat_hnext[1]; + if (natd->nat_me != NULL) + *natd->nat_me = NULL; if (natd->nat_fr != NULL) { ATOMIC_DEC32(natd->nat_fr->fr_ref); @@ -1057,6 +1061,7 @@ struct nat *natd; /* * nat_flushtable - clear the NAT table of all mapping entries. + * (this is for the dynamic mappings) */ static int nat_flushtable() { @@ -1089,8 +1094,9 @@ static int nat_flushtable() /* * nat_clearlist - delete all rules in the active NAT mapping list. + * (this is for NAT/RDR rules) */ -static int nat_clearlist() +int nat_clearlist() { register ipnat_t *n, **np = &nat_list; int i = 0; @@ -1121,22 +1127,25 @@ static int nat_clearlist() /* * Create a new NAT table entry. - * NOTE: assumes write lock on ipf_nat has been obtained already. + * NOTE: Assumes write lock on ipf_nat has been obtained already. + * If you intend on changing this, beware: appr_new() may call nat_new() + * recursively! */ -nat_t *nat_new(np, ip, fin, flags, direction) -ipnat_t *np; -ip_t *ip; +nat_t *nat_new(fin, ip, np, natsave, flags, direction) fr_info_t *fin; +ip_t *ip; +ipnat_t *np; +nat_t **natsave; 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; + u_short nflags, sp, dp; 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 @@ -1144,8 +1153,8 @@ int direction; nflags = flags & np->in_flags; if (flags & IPN_TCPUDP) { tcp = (tcphdr_t *)fin->fin_dp; - sport = tcp->th_sport; - dport = tcp->th_dport; + sport = htons(fin->fin_data[0]); + dport = htons(fin->fin_data[1]); } /* Give me a new nat */ @@ -1185,7 +1194,7 @@ int direction; * Check to see if there is an existing NAT * setup for this IP address pair. */ - hm = nat_hostmap(np, ip->ip_src, in); + hm = nat_hostmap(np, fin->fin_src, in); if (hm != NULL) in.s_addr = hm->hm_mapip.s_addr; } else if ((l == 1) && (hm != NULL)) { @@ -1209,7 +1218,7 @@ int direction; /* * map-block - Calculate destination address. */ - in.s_addr = ntohl(ip->ip_src.s_addr); + in.s_addr = ntohl(fin->fin_saddr); in.s_addr &= ntohl(~np->in_inmsk); inb.s_addr = in.s_addr; in.s_addr /= np->in_ippip; @@ -1242,7 +1251,7 @@ int direction; */ if (l > 0) goto badnat; - in.s_addr = ntohl(ip->ip_src.s_addr); + in.s_addr = ntohl(fin->fin_saddr); } else if ((np->in_outmsk != 0xffffffff) && (np->in_pnext == 0) && ((l > 0) || (hm == NULL))) @@ -1264,7 +1273,7 @@ int direction; port += (l % np->in_ppip); port %= np->in_ppip; port += np->in_ppip * - (ntohl(ip->ip_src.s_addr) % + (ntohl(fin->fin_saddr) % np->in_ippip); port += MAPBLK_MINPORT; port = htons(port); @@ -1304,9 +1313,15 @@ int direction; * 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, 1); + sp = fin->fin_data[0]; + dp = fin->fin_data[1]; + fin->fin_data[0] = fin->fin_data[1]; + fin->fin_data[1] = htons(port); + natl = nat_inlookup(fin, flags & ~FI_WILDP, + (u_int)fin->fin_p, fin->fin_dst, + inb, 1); + fin->fin_data[0] = sp; + fin->fin_data[1] = dp; /* * Has the search wrapped around and come back to the @@ -1323,14 +1338,14 @@ int direction; np->in_space--; /* Setup the NAT table */ - nat->nat_inip = ip->ip_src; + nat->nat_inip = fin->fin_src; nat->nat_outip.s_addr = htonl(in.s_addr); - nat->nat_oip = ip->ip_dst; + nat->nat_oip = fin->fin_dst; if (nat->nat_hm == NULL) - nat->nat_hm = nat_hostmap(np, ip->ip_src, + nat->nat_hm = nat_hostmap(np, fin->fin_src, nat->nat_outip); - sum1 = LONG_SUM(ntohl(ip->ip_src.s_addr)) + ntohs(sport); + sum1 = LONG_SUM(ntohl(fin->fin_saddr)) + ntohs(sport); sum2 = LONG_SUM(in.s_addr) + ntohs(port); if (flags & IPN_TCPUDP) { @@ -1370,27 +1385,26 @@ int direction; * 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); + if (np->in_pmin != np->in_pmax) { + nport = ntohs(dport) - ntohs(np->in_pmin) + + ntohs(np->in_pnext); + nport = ntohs(nport); + } else + nport = np->in_pnext; } /* * 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. + * assume a blank `forwarding' of the packet. */ - if (in.s_addr == 0) { - if (nport == dport) - goto badnat; - in.s_addr = ntohl(ip->ip_dst.s_addr); - } + if (in.s_addr == 0) + in.s_addr = ntohl(fin->fin_daddr); nat->nat_inip.s_addr = htonl(in.s_addr); - nat->nat_outip = ip->ip_dst; - nat->nat_oip = ip->ip_src; + nat->nat_outip = fin->fin_dst; + nat->nat_oip = fin->fin_src; - sum1 = LONG_SUM(ntohl(ip->ip_dst.s_addr)) + ntohs(dport); + sum1 = LONG_SUM(ntohl(fin->fin_daddr)) + ntohs(dport); sum2 = LONG_SUM(in.s_addr) + ntohs(nport); if (flags & IPN_TCPUDP) { @@ -1408,8 +1422,8 @@ int direction; 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 = LONG_SUM(ntohl(fin->fin_saddr)); + sum1 += LONG_SUM(ntohl(fin->fin_daddr)); sum1 += IPPROTO_TCP; sum1 = (sum1 & 0xffff) + (sum1 >> 16); nat->nat_sumd[1] = NAT_HW_CKSUM|(sum1 & 0xffff); @@ -1419,9 +1433,9 @@ int direction; if ((flags & IPN_TCPUDP) && ((sport != port) || (dport != nport))) { if (direction == NAT_OUTBOUND) - sum1 = LONG_SUM(ntohl(ip->ip_src.s_addr)); + sum1 = LONG_SUM(ntohl(fin->fin_saddr)); else - sum1 = LONG_SUM(ntohl(ip->ip_dst.s_addr)); + sum1 = LONG_SUM(ntohl(fin->fin_daddr)); sum2 = LONG_SUM(in.s_addr); @@ -1432,15 +1446,13 @@ int direction; in.s_addr = htonl(in.s_addr); -#ifdef _KERNEL strncpy(nat->nat_ifname, IFNAME(fin->fin_ifp), IFNAMSIZ); -#endif - nat_insert(nat); + nat->nat_me = natsave; nat->nat_dir = direction; nat->nat_ifp = fin->fin_ifp; nat->nat_ptr = np; - nat->nat_p = ip->ip_p; + nat->nat_p = fin->fin_p; nat->nat_bytes = 0; nat->nat_pkts = 0; nat->nat_fr = fin->fin_fr; @@ -1454,6 +1466,13 @@ int direction; if (flags & IPN_TCPUDP) tcp->th_dport = nport; } + + nat_insert(nat); + + if ((np->in_apr != NULL) && (np->in_dport == 0 || + (tcp != NULL && dport == np->in_dport))) + (void) appr_new(fin, ip, nat); + np->in_use++; #ifdef IPFILTER_LOG nat_log(nat, (u_int)np->in_redir); @@ -1468,6 +1487,10 @@ badnat: } +/* + * Insert a NAT entry into the hash tables for searching and add it to the + * list of active NAT entries. Adjust global counters when complete. + */ void nat_insert(nat) nat_t *nat; { @@ -1495,10 +1518,10 @@ nat_t *nat; hv2 = NAT_HASH_FN(nat->nat_oip.s_addr, hv2 + nat->nat_oport, ipf_nattable_sz); } else { - hv1 = NAT_HASH_FN(nat->nat_inip.s_addr, 0, 0xffffffff); - hv1 = NAT_HASH_FN(nat->nat_oip.s_addr, hv1, ipf_nattable_sz); - hv2 = NAT_HASH_FN(nat->nat_outip.s_addr, 0, 0xffffffff); - hv2 = NAT_HASH_FN(nat->nat_oip.s_addr, hv2, ipf_nattable_sz); + hv1 = NAT_HASH_FN(nat->nat_oip.s_addr, nat->nat_inip.s_addr, + ipf_nattable_sz); + hv2 = NAT_HASH_FN(nat->nat_oip.s_addr, nat->nat_outip.s_addr, + ipf_nattable_sz); } natp = &nat_table[0][hv1]; @@ -1584,25 +1607,36 @@ int dir; else if (oip->ip_p == IPPROTO_UDP) flags = IPN_UDP; if (flags & IPN_TCPUDP) { + u_short data[2]; + nat_t *nat; + minlen += 8; /* + 64bits of data to get ports */ if (ip->ip_len < ICMPERR_IPICMPHLEN + minlen) return NULL; + + data[0] = fin->fin_data[0]; + data[1] = fin->fin_data[1]; 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, 0); - 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, 0); + fin->fin_data[0] = ntohs(tcp->th_dport); + fin->fin_data[1] = ntohs(tcp->th_sport); + + if (dir == NAT_INBOUND) { + nat = nat_inlookup(fin, flags, (u_int)oip->ip_p, + oip->ip_dst, oip->ip_src, 0); + } else { + nat = nat_outlookup(fin, flags, (u_int)oip->ip_p, + oip->ip_dst, oip->ip_src, 0); + } + fin->fin_data[0] = data[0]; + fin->fin_data[1] = data[1]; + return nat; } if (dir == NAT_INBOUND) - return nat_inlookup(fin->fin_ifp, 0, (u_int)oip->ip_p, - oip->ip_dst, oip->ip_src, 0, 0); + return nat_inlookup(fin, 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, 0); + return nat_outlookup(fin, 0, (u_int)oip->ip_p, + oip->ip_dst, oip->ip_src, 0); } @@ -1622,7 +1656,7 @@ int dir; udphdr_t *udp; nat_t *nat; ip_t *oip; - int flags = 0; + int flags; if ((fin->fin_fl & FI_SHORT) || (fin->fin_off != 0)) return NULL; @@ -1631,6 +1665,8 @@ int dir; */ if ((ip->ip_v != 4) || !(nat = nat_icmplookup(ip, fin, dir))) return NULL; + + flags = 0; *nflags = IPN_ICMPERR; icmp = (icmphdr_t *)fin->fin_dp; oip = (ip_t *)&icmp->icmp_ip; @@ -1920,23 +1956,32 @@ int dir; * 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, rw) -void *ifp; +nat_t *nat_inlookup(fin, flags, p, src, mapdst, rw) +fr_info_t *fin; register u_int flags, p; struct in_addr src , mapdst; -u_32_t ports; int rw; { register u_short sport, dport; register nat_t *nat; register int nflags; register u_32_t dst; + ipnat_t *ipn; + void *ifp; u_int hv; + if (fin != NULL) + ifp = fin->fin_ifp; + else + ifp = NULL; dst = mapdst.s_addr; - dport = ports >> 16; - sport = ports & 0xffff; - flags &= IPN_TCPUDP; + if (flags & IPN_TCPUDP) { + sport = htons(fin->fin_data[0]); + dport = htons(fin->fin_data[1]); + } else { + sport = 0; + dport = 0; + } hv = NAT_HASH_FN(dst, dport, 0xffffffff); hv = NAT_HASH_FN(src.s_addr, hv + sport, ipf_nattable_sz); @@ -1946,19 +1991,34 @@ int rw; if ((!ifp || ifp == nat->nat_ifp) && nat->nat_oip.s_addr == src.s_addr && nat->nat_outip.s_addr == dst && - (((p == 0) && (flags == (nat->nat_flags & IPN_TCPUDP))) - || (p == nat->nat_p)) && (!flags || - (((nat->nat_oport == sport) || (nflags & FI_W_DPORT)) && - ((nat->nat_outport == dport) || (nflags & FI_W_SPORT))))) + ((p == 0) || (p == nat->nat_p))) { + switch (p) + { + case IPPROTO_TCP : + case IPPROTO_UDP : + if (nat->nat_oport != sport) + continue; + if (nat->nat_outport != dport) + continue; + break; + default : + break; + } + + ipn = nat->nat_ptr; + if ((ipn != NULL) && (nat->nat_aps != NULL)) + if (appr_match(fin, nat) != 0) + continue; return nat; + } } - if (!nat_stats.ns_wilds || !(flags & IPN_TCPUDP)) + if (!nat_stats.ns_wilds || !(flags & FI_WILDP)) return NULL; if (!rw) { RWLOCK_EXIT(&ipf_nat); } hv = NAT_HASH_FN(dst, 0, 0xffffffff); - hv = NAT_HASH_FN(src.s_addr, hv, ipf_nattable_sz); + hv = NAT_HASH_FN(src.s_addr, dst, ipf_nattable_sz); if (!rw) { WRITE_ENTER(&ipf_nat); } @@ -1967,8 +2027,6 @@ int rw; nflags = nat->nat_flags; if (ifp && ifp != nat->nat_ifp) continue; - if (!(nflags & IPN_TCPUDP)) - continue; if (!(nflags & FI_WILDP)) continue; if (nat->nat_oip.s_addr != src.s_addr || @@ -1976,7 +2034,7 @@ int rw; continue; if (((nat->nat_oport == sport) || (nflags & FI_W_DPORT)) && ((nat->nat_outport == dport) || (nflags & FI_W_SPORT))) { - nat_tabmove(nat, ports); + nat_tabmove(fin, nat); break; } } @@ -1992,21 +2050,18 @@ int rw; * original was placed in the table without hashing on the ports and we now * want to include hashing on port numbers. */ -static void nat_tabmove(nat, ports) +static void nat_tabmove(fin, nat) +fr_info_t *fin; nat_t *nat; -u_32_t ports; { register u_short sport, dport; + u_int hv, nflags; nat_t **natp; - u_int hv; - dport = ports >> 16; - sport = ports & 0xffff; + nflags = nat->nat_flags; - if (nat->nat_oport == dport) { - nat->nat_inport = sport; - nat->nat_outport = sport; - } + sport = ntohs(fin->fin_data[0]); + dport = ntohs(fin->fin_data[1]); /* * Remove the NAT entry from the old location @@ -2048,23 +2103,29 @@ u_32_t ports; * 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, rw) -void *ifp; +nat_t *nat_outlookup(fin, flags, p, src, dst, rw) +fr_info_t *fin; register u_int flags, p; struct in_addr src , dst; -u_32_t ports; int rw; { register u_short sport, dport; register nat_t *nat; register int nflags; + ipnat_t *ipn; u_32_t srcip; + void *ifp; u_int hv; - sport = ports & 0xffff; - dport = ports >> 16; - flags &= IPN_TCPUDP; + ifp = fin->fin_ifp; srcip = src.s_addr; + if (flags & IPN_TCPUDP) { + sport = ntohs(fin->fin_data[0]); + dport = ntohs(fin->fin_data[1]); + } else { + sport = 0; + dport = 0; + } hv = NAT_HASH_FN(srcip, sport, 0xffffffff); hv = NAT_HASH_FN(dst.s_addr, hv + dport, ipf_nattable_sz); @@ -2075,19 +2136,34 @@ int rw; if ((!ifp || ifp == nat->nat_ifp) && nat->nat_inip.s_addr == srcip && nat->nat_oip.s_addr == dst.s_addr && - (((p == 0) && (flags == (nflags & IPN_TCPUDP))) - || (p == nat->nat_p)) && (!flags || - ((nat->nat_inport == sport || nflags & FI_W_SPORT) && - (nat->nat_oport == dport || nflags & FI_W_DPORT)))) + ((p == 0) || (p == nat->nat_p))) { + switch (p) + { + case IPPROTO_TCP : + case IPPROTO_UDP : + if (nat->nat_oport != dport) + continue; + if (nat->nat_inport != sport) + continue; + break; + default : + break; + } + + ipn = nat->nat_ptr; + if ((ipn != NULL) && (nat->nat_aps != NULL)) + if (appr_match(fin, nat) != 0) + continue; return nat; + } } - if (!nat_stats.ns_wilds || !(flags & IPN_TCPUDP)) + if (!nat_stats.ns_wilds || !(flags & FI_WILDP)) return NULL; if (!rw) { RWLOCK_EXIT(&ipf_nat); } - hv = NAT_HASH_FN(srcip, 0, ipf_nattable_sz); - hv = NAT_HASH_FN(dst.s_addr, hv, ipf_nattable_sz); + + hv = NAT_HASH_FN(dst.s_addr, srcip, ipf_nattable_sz); if (!rw) { WRITE_ENTER(&ipf_nat); } @@ -2096,8 +2172,6 @@ int rw; nflags = nat->nat_flags; if (ifp && ifp != nat->nat_ifp) continue; - if (!(nflags & IPN_TCPUDP)) - continue; if (!(nflags & FI_WILDP)) continue; if ((nat->nat_inip.s_addr != srcip) || @@ -2105,7 +2179,7 @@ int rw; continue; if (((nat->nat_inport == sport) || (nflags & FI_W_SPORT)) && ((nat->nat_oport == dport) || (nflags & FI_W_DPORT))) { - nat_tabmove(nat, ports); + nat_tabmove(fin, nat); break; } } @@ -2122,16 +2196,19 @@ int rw; nat_t *nat_lookupredir(np) register natlookup_t *np; { - u_32_t ports; nat_t *nat; + fr_info_t fi; + + bzero((char *)&fi, sizeof(fi)); + fi.fin_data[0] = np->nl_inport; + fi.fin_data[1] = np->nl_outport; - 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, 0))) { + if ((nat = nat_outlookup(&fi, np->nl_flags, 0, np->nl_inip, + np->nl_outip, 0))) { np->nl_realip = nat->nat_outip; np->nl_realport = nat->nat_outport; } @@ -2149,7 +2226,7 @@ ip_t *ip; if (ip->ip_v != 4) return 0; - if (np->in_p && ip->ip_p != np->in_p) + if (np->in_p && fin->fin_p != np->in_p) return 0; if (fin->fin_out) { if (!(np->in_redir & (NAT_MAP|NAT_MAPBLK))) @@ -2199,6 +2276,7 @@ fr_info_t *fin; u_int nflags = 0, hv, msk; struct ifnet *ifp; frentry_t *fr; + void *sifp; u_32_t iph; nat_t *nat; @@ -2206,15 +2284,17 @@ fr_info_t *fin; 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; + fr->fr_tif.fd_ifp && fr->fr_tif.fd_ifp != (void *)-1) { + sifp = fin->fin_ifp; + fin->fin_ifp = fr->fr_tif.fd_ifp; + } else + sifp = fin->fin_ifp; + ifp = fin->fin_ifp; if ((fin->fin_off == 0) && !(fin->fin_fl & FI_SHORT)) { - if (ip->ip_p == IPPROTO_TCP) + if (fin->fin_p == IPPROTO_TCP) nflags = IPN_TCP; - else if (ip->ip_p == IPPROTO_UDP) + else if (fin->fin_p == IPPROTO_UDP) nflags = IPN_UDP; if ((nflags & IPN_TCPUDP)) { tcp = (tcphdr_t *)fin->fin_dp; @@ -2223,27 +2303,28 @@ fr_info_t *fin; } } - ipa = ip->ip_src.s_addr; + ipa = fin->fin_saddr; READ_ENTER(&ipf_nat); - if ((ip->ip_p == IPPROTO_ICMP) && + if ((fin->fin_p == IPPROTO_ICMP) && (nat = nat_icmp(ip, fin, &nflags, NAT_OUTBOUND))) icmpset = 1; else if ((fin->fin_fl & FI_FRAG) && (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, 0))) { + else if ((nat = nat_outlookup(fin, nflags|FI_WILDP|FI_WILDA, + (u_int)fin->fin_p, fin->fin_src, + fin->fin_dst, 0))) { 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)) + 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); @@ -2252,13 +2333,15 @@ fr_info_t *fin; } } else { RWLOCK_EXIT(&ipf_nat); + + msk = 0xffffffff; + i = 32; + 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); @@ -2274,22 +2357,13 @@ maskloop: 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++; - break; - } + if (*np->in_plabel && !appr_ok(ip, tcp, np)) + continue; + nat = nat_new(fin, ip, np, NULL, + (u_int)nflags, NAT_OUTBOUND); + if (nat != NULL) { + np->in_hits++; + break; } } if ((np == NULL) && (i > 0)) { @@ -2311,7 +2385,14 @@ maskloop: if (natadd && (fin->fin_fl & FI_FRAG) && np) ipfr_nat_newfrag(ip, fin, 0, nat); MUTEX_ENTER(&nat->nat_lock); - nat->nat_age = fr_defnatage; + if (fin->fin_p != IPPROTO_TCP) { + if (np && np->in_age[1]) + nat->nat_age = np->in_age[1]; + else if (!icmpset && (fin->fin_p == IPPROTO_ICMP)) + nat->nat_age = fr_defnaticmpage; + else + nat->nat_age = fr_defnatage; + } nat->nat_bytes += ip->ip_len; nat->nat_pkts++; MUTEX_EXIT(&nat->nat_lock); @@ -2323,16 +2404,16 @@ maskloop: if (nflags == IPN_ICMPERR) { u_32_t s1, s2, sumd; - s1 = LONG_SUM(ntohl(ip->ip_src.s_addr)); + s1 = LONG_SUM(ntohl(fin->fin_saddr)); s2 = LONG_SUM(ntohl(nat->nat_outip.s_addr)); CALC_SUMD(s1, s2, sumd); if (nat->nat_dir == NAT_OUTBOUND) - fix_incksum(fin, &ip->ip_sum, sumd); - else fix_outcksum(fin, &ip->ip_sum, sumd); + else + fix_incksum(fin, &ip->ip_sum, sumd); } -#if SOLARIS || defined(__sgi) +#if (SOLARIS || defined(__sgi)) && defined(_KERNEL) else { if (nat->nat_dir == NAT_OUTBOUND) fix_outcksum(fin, &ip->ip_sum, nat->nat_ipsumd); @@ -2340,16 +2421,19 @@ maskloop: fix_incksum(fin, &ip->ip_sum, nat->nat_ipsumd); } #endif + /* + * Only change the packet contents, not what is filtered upon. + */ ip->ip_src = nat->nat_outip; if ((fin->fin_off == 0) && !(fin->fin_fl & FI_SHORT)) { - if ((nat->nat_outport != 0) && (nflags & IPN_TCPUDP)) { + if ((nat->nat_outport != 0) && (tcp != NULL)) { tcp->th_sport = nat->nat_outport; fin->fin_data[0] = ntohs(tcp->th_sport); } - if (ip->ip_p == IPPROTO_TCP) { + if (fin->fin_p == IPPROTO_TCP) { csump = &tcp->th_sum; MUTEX_ENTER(&nat->nat_lock); fr_tcp_age(&nat->nat_age, @@ -2369,25 +2453,24 @@ maskloop: if (nat->nat_age == fr_tcpclosed) nat->nat_age = fr_tcplastack; MUTEX_EXIT(&nat->nat_lock); - } else if (ip->ip_p == IPPROTO_UDP) { + } else if (fin->fin_p == IPPROTO_UDP) { udphdr_t *udp = (udphdr_t *)tcp; if (udp->uh_sum) csump = &udp->uh_sum; - } else if (ip->ip_p == IPPROTO_ICMP) { - if (!icmpset) - nat->nat_age = fr_defnaticmpage; } if (csump) { if (nat->nat_dir == NAT_OUTBOUND) - fix_outcksum(fin, csump, nat->nat_sumd[1]); + fix_outcksum(fin, csump, + nat->nat_sumd[1]); else - fix_incksum(fin, csump, nat->nat_sumd[1]); + fix_incksum(fin, csump, + nat->nat_sumd[1]); } } - if ((np->in_apr != NULL) && (np->in_dport == 0 || + if (np && (np->in_apr != NULL) && (np->in_dport == 0 || (tcp != NULL && dport == np->in_dport))) { i = appr_check(ip, fin, nat); if (i == 0) @@ -2396,9 +2479,11 @@ maskloop: i = 1; ATOMIC_INCL(nat_stats.ns_mapped[1]); RWLOCK_EXIT(&ipf_nat); /* READ */ + fin->fin_ifp = sifp; return i; } RWLOCK_EXIT(&ipf_nat); /* READ/WRITE */ + fin->fin_ifp = sifp; return 0; } @@ -2426,37 +2511,36 @@ fr_info_t *fin; return 0; if ((fin->fin_off == 0) && !(fin->fin_fl & FI_SHORT)) { - if (ip->ip_p == IPPROTO_TCP) + if (fin->fin_p == IPPROTO_TCP) nflags = IPN_TCP; - else if (ip->ip_p == IPPROTO_UDP) + else if (fin->fin_p == IPPROTO_UDP) nflags = IPN_UDP; if ((nflags & IPN_TCPUDP)) { tcp = (tcphdr_t *)fin->fin_dp; - dport = tcp->th_dport; sport = tcp->th_sport; + dport = tcp->th_dport; } } - in = ip->ip_dst; + in = fin->fin_dst; /* make sure the source address is to be redirected */ - src = ip->ip_src; + src = fin->fin_src; READ_ENTER(&ipf_nat); - if ((ip->ip_p == IPPROTO_ICMP) && + if ((fin->fin_p == IPPROTO_ICMP) && (nat = nat_icmp(ip, fin, &nflags, NAT_INBOUND))) icmpset = 1; else if ((fin->fin_fl & FI_FRAG) && (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, - 0))) { + else if ((nat = nat_inlookup(fin, nflags|FI_WILDP|FI_WILDA, + (u_int)fin->fin_p, fin->fin_src, in, 0))) { 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) && + if ((nat->nat_outport != dport) && (nflags & FI_W_SPORT)) nat->nat_outport = dport; nat->nat_flags &= ~(FI_W_SPORT|FI_W_DPORT); @@ -2465,19 +2549,21 @@ fr_info_t *fin; } } else { RWLOCK_EXIT(&ipf_nat); + + msk = 0xffffffff; + i = 32; + 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_p && (np->in_p != fin->fin_p)) || (np->in_flags && !(nflags & np->in_flags))) continue; if (np->in_flags & IPN_FILTER) { @@ -2485,11 +2571,10 @@ maskloop: continue; } else if ((in.s_addr & np->in_outmsk) != np->in_outip) continue; - if ((np->in_redir & NAT_REDIRECT) && - (!np->in_pmin || (np->in_flags & IPN_FILTER) || + if ((!np->in_pmin || (np->in_flags & IPN_FILTER) || ((ntohs(np->in_pmax) >= ntohs(dport)) && (ntohs(dport) >= ntohs(np->in_pmin))))) - if ((nat = nat_new(np, ip, fin, nflags, + if ((nat = nat_new(fin, ip, np, NULL, nflags, NAT_INBOUND))) { np->in_hits++; break; @@ -2515,8 +2600,8 @@ maskloop: fin->fin_fr = nat->nat_fr; if (natadd && (fin->fin_fl & FI_FRAG) && np) ipfr_nat_newfrag(ip, fin, 0, nat); - if ((np->in_apr != NULL) && (np->in_dport == 0 || - (tcp != NULL && sport == np->in_dport))) { + if (np && (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); @@ -2525,9 +2610,14 @@ maskloop: } MUTEX_ENTER(&nat->nat_lock); - if (nflags != IPN_ICMPERR) - nat->nat_age = fr_defnatage; - + if (fin->fin_p != IPPROTO_TCP) { + if (np && np->in_age[0]) + nat->nat_age = np->in_age[0]; + else if (!icmpset && (fin->fin_p == IPPROTO_ICMP)) + nat->nat_age = fr_defnaticmpage; + else + nat->nat_age = fr_defnatage; + } nat->nat_bytes += ip->ip_len; nat->nat_pkts++; MUTEX_EXIT(&nat->nat_lock); @@ -2538,7 +2628,7 @@ maskloop: * Fix up checksums, not by recalculating them, but * simply computing adjustments. */ -#if SOLARIS || defined(__sgi) +#if (SOLARIS || defined(__sgi)) && defined(_KERNEL) if (nat->nat_dir == NAT_OUTBOUND) fix_incksum(fin, &ip->ip_sum, nat->nat_ipsumd); else @@ -2546,12 +2636,12 @@ maskloop: #endif if ((fin->fin_off == 0) && !(fin->fin_fl & FI_SHORT)) { - if ((nat->nat_inport != 0) && (nflags & IPN_TCPUDP)) { + if ((nat->nat_inport != 0) && (tcp != NULL)) { tcp->th_dport = nat->nat_inport; fin->fin_data[1] = ntohs(tcp->th_dport); } - if (ip->ip_p == IPPROTO_TCP) { + if (fin->fin_p == IPPROTO_TCP) { csump = &tcp->th_sum; MUTEX_ENTER(&nat->nat_lock); fr_tcp_age(&nat->nat_age, @@ -2571,21 +2661,20 @@ maskloop: if (nat->nat_age == fr_tcpclosed) nat->nat_age = fr_tcplastack; MUTEX_EXIT(&nat->nat_lock); - } else if (ip->ip_p == IPPROTO_UDP) { + } else if (fin->fin_p == IPPROTO_UDP) { udphdr_t *udp = (udphdr_t *)tcp; if (udp->uh_sum) csump = &udp->uh_sum; - } else if (ip->ip_p == IPPROTO_ICMP) { - if (!icmpset) - nat->nat_age = fr_defnaticmpage; } if (csump) { if (nat->nat_dir == NAT_OUTBOUND) - fix_incksum(fin, csump, nat->nat_sumd[0]); + fix_incksum(fin, csump, + nat->nat_sumd[0]); else - fix_outcksum(fin, csump, nat->nat_sumd[0]); + fix_outcksum(fin, csump, + nat->nat_sumd[0]); } } ATOMIC_INCL(nat_stats.ns_mapped[0]); @@ -2760,3 +2849,13 @@ u_int type; (void) ipllog(IPL_LOGNAT, NULL, items, sizes, types, 1); } #endif + + +#if defined(__OpenBSD__) +void nat_ifdetach(ifp) +void *ifp; +{ + frsync(); + return; +} +#endif |