diff options
author | Darren Reed <darrenr@FreeBSD.org> | 2004-06-21 22:26:10 +0000 |
---|---|---|
committer | Darren Reed <darrenr@FreeBSD.org> | 2004-06-21 22:26:10 +0000 |
commit | 866bef8856d9607d136d404359afae7b519a5bb6 (patch) | |
tree | 93b39b935e8d8e4087a28bff3155a4ee335822a2 /sys/contrib/ipfilter/netinet/ip_nat.c | |
parent | 46e22717ef19d69b4b74b02ddd23fc5fadb8713a (diff) | |
download | src-866bef8856d9607d136d404359afae7b519a5bb6.tar.gz src-866bef8856d9607d136d404359afae7b519a5bb6.zip |
Import ipfilter 3.4.35 (destinated for RELENG_4) to vendor branch
Notes
Notes:
svn path=/vendor-sys/ipfilter/dist/; revision=130882
Diffstat (limited to 'sys/contrib/ipfilter/netinet/ip_nat.c')
-rw-r--r-- | sys/contrib/ipfilter/netinet/ip_nat.c | 386 |
1 files changed, 177 insertions, 209 deletions
diff --git a/sys/contrib/ipfilter/netinet/ip_nat.c b/sys/contrib/ipfilter/netinet/ip_nat.c index d8c862220cea..41939334c901 100644 --- a/sys/contrib/ipfilter/netinet/ip_nat.c +++ b/sys/contrib/ipfilter/netinet/ip_nat.c @@ -109,12 +109,13 @@ extern struct ifnet vpnif; #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.37.2.74 2002/12/06 11:40:21 darrenr Exp $"; +static const char rcsid[] = "@(#)$Id: ip_nat.c,v 2.37.2.82 2004/05/30 17:56:52 darrenr Exp $"; #endif nat_t **nat_table[2] = { NULL, NULL }, *nat_instances = NULL; ipnat_t *nat_list = NULL; +u_int ipf_nattable_max = NAT_TABLE_MAX; u_int ipf_nattable_sz = NAT_TABLE_SZ; u_int ipf_natrules_sz = NAT_SIZE; u_int ipf_rdrrules_sz = RDR_SIZE; @@ -778,6 +779,8 @@ caddr_t data; if ((aps != NULL) && (aps->aps_data != 0)) { ng.ng_sz += sizeof(ap_session_t); ng.ng_sz += aps->aps_psiz; + if (aps->aps_psiz > 4) /* XXX - sizeof(ipn_data) */ + ng.ng_sz -= 4; } error = IWCOPY((caddr_t)&ng, data, sizeof(ng)); @@ -793,6 +796,7 @@ caddr_t data; nat_save_t ipn, *ipnp, *ipnn = NULL; register nat_t *n, *nat; ap_session_t *aps; + size_t dsz; int error; error = IRCOPY(data, (caddr_t)&ipnp, sizeof(ipnp)); @@ -824,7 +828,6 @@ caddr_t data; } 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; @@ -838,10 +841,13 @@ caddr_t data; sizeof(ipn.ipn_rule)); if ((aps = nat->nat_aps)) { - ipn.ipn_dsize = sizeof(*aps); + dsz = sizeof(*aps); if (aps->aps_data) - ipn.ipn_dsize += aps->aps_psiz; - KMALLOCS(ipnn, nat_save_t *, sizeof(*ipnn) + ipn.ipn_dsize); + dsz += aps->aps_psiz; + ipn.ipn_dsize = dsz; + if (dsz > sizeof(ipn.ipn_data)) + dsz -= sizeof(ipn.ipn_data); + KMALLOCS(ipnn, nat_save_t *, sizeof(*ipnn) + dsz); if (ipnn == NULL) return ENOMEM; bcopy((char *)&ipn, (char *)ipnn, sizeof(ipn)); @@ -850,14 +856,14 @@ caddr_t data; if (aps->aps_data) { bcopy(aps->aps_data, ipnn->ipn_data + sizeof(*aps), aps->aps_psiz); - ipnn->ipn_dsize += aps->aps_psiz; } error = IWCOPY((caddr_t)ipnn, ipnp, - sizeof(ipn) + ipn.ipn_dsize); + sizeof(ipn) + dsz); if (error) error = EFAULT; - KFREES(ipnn, sizeof(*ipnn) + ipn.ipn_dsize); + KFREES(ipnn, sizeof(*ipnn) + dsz); } else { + ipn.ipn_dsize = 0; error = IWCOPY((caddr_t)&ipn, ipnp, sizeof(ipn)); if (error) error = EFAULT; @@ -885,12 +891,12 @@ caddr_t data; return EFAULT; nat = NULL; if (ipn.ipn_dsize) { - KMALLOCS(ipnn, nat_save_t *, sizeof(ipn) + ipn.ipn_dsize); + KMALLOCS(ipnn, nat_save_t *, sizeof(*ipnn) + 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); + error = IRCOPY((caddr_t)ipnp + offsetof(nat_save_t, ipn_data), + (caddr_t)ipnn->ipn_data, ipn.ipn_dsize); if (error) { error = EFAULT; goto junkput; @@ -1065,7 +1071,7 @@ struct nat *natd; * If there's a fragment table entry too for this nat entry, then * dereference that as well. */ - ipfr_forget((void *)natd); + ipfr_forgetnat((void *)natd); aps_free(natd->nat_aps); nat_stats.ns_inuse--; KFREE(natd); @@ -1163,6 +1169,11 @@ int direction; qif_t *qf = fin->fin_qif; #endif + if (nat_stats.ns_inuse >= ipf_nattable_max) { + nat_stats.ns_memfail++; + return NULL; + } + nflags = flags & np->in_flags; if (flags & IPN_TCPUDP) { tcp = (tcphdr_t *)fin->fin_dp; @@ -1174,6 +1185,17 @@ int direction; KMALLOC(nat, nat_t *); if (nat == NULL) { nat_stats.ns_memfail++; + /* + * Try to automatically tune the max # of entries in the + * table allowed to be less than what will cause kmem_alloc() + * to fail and try to eliminate panics due to out of memory + * conditions arising. + */ + if (ipf_nattable_max > ipf_nattable_sz) { + ipf_nattable_max = nat_stats.ns_inuse - 100; + printf("ipf_nattable_max reduced to %d\n", + ipf_nattable_max); + } return NULL; } @@ -1430,7 +1452,7 @@ int direction; CALC_SUMD(sum1, sum2, sumd); nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); #if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) - if ((flags & IPN_TCPUDP) && dohwcksum && + 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)); @@ -1682,6 +1704,7 @@ int dir; return NULL; flags = 0; + sumd2 = 0; *nflags = IPN_ICMPERR; icmp = (icmphdr_t *)fin->fin_dp; oip = (ip_t *)&icmp->icmp_ip; @@ -1735,137 +1758,75 @@ int dir; CALC_SUMD(sum1, sum2, sumd); - if (nat->nat_dir == NAT_OUTBOUND) { - /* - * Fix IP checksum of the offending IP packet to adjust for - * the change in the IP address. - * - * Normally, you would expect that the ICMP checksum of the - * ICMP error message needs to be adjusted as well for the - * IP address change in oip. - * However, this is a NOP, because the ICMP checksum is - * calculated over the complete ICMP packet, which includes the - * changed oip IP addresses and oip->ip_sum. However, these - * two changes cancel each other out (if the delta for - * the IP address is x, then the delta for ip_sum is minus x), - * so no change in the icmp_cksum is necessary. - * - * Be careful that nat_dir refers to the direction of the - * offending IP packet (oip), not to its ICMP response (icmp) - */ - fix_datacksum(&oip->ip_sum, sumd); + /* + * Fix IP checksum of the offending IP packet to adjust for + * the change in the IP address. + * + * Normally, you would expect that the ICMP checksum of the + * ICMP error message needs to be adjusted as well for the + * IP address change in oip. + * However, this is a NOP, because the ICMP checksum is + * calculated over the complete ICMP packet, which includes the + * changed oip IP addresses and oip->ip_sum. However, these + * two changes cancel each other out (if the delta for + * the IP address is x, then the delta for ip_sum is minus x), + * so no change in the icmp_cksum is necessary. + * + * Be careful that nat_dir refers to the direction of the + * offending IP packet (oip), not to its ICMP response (icmp) + */ + fix_datacksum(&oip->ip_sum, sumd); + /* Fix icmp cksum : IP Addr + Cksum */ + /* + * Fix UDP pseudo header checksum to compensate for the + * IP address change. + */ + if ((oip->ip_p == IPPROTO_UDP) && (dlen >= 8) && udp->uh_sum) { /* - * Fix UDP pseudo header checksum to compensate for the - * IP address change. + * The UDP checksum is optional, only adjust it + * if it has been set. */ - if (oip->ip_p == IPPROTO_UDP && udp->uh_sum) { - /* - * The UDP checksum is optional, only adjust it - * if it has been set. - */ - sum1 = ntohs(udp->uh_sum); - fix_datacksum(&udp->uh_sum, sumd); - sum2 = ntohs(udp->uh_sum); - - /* - * Fix ICMP checksum to compensate the UDP - * checksum adjustment. - */ - CALC_SUMD(sum1, sum2, sumd); - sumd2 = sumd; - } + sum1 = ntohs(udp->uh_sum); + fix_datacksum(&udp->uh_sum, sumd); + sum2 = ntohs(udp->uh_sum); /* - * Fix TCP pseudo header checksum to compensate for the - * IP address change. Before we can do the change, we - * must make sure that oip is sufficient large to hold - * the TCP checksum (normally it does not!). + * Fix ICMP checksum to compensate the UDP + * checksum adjustment. */ - if (oip->ip_p == IPPROTO_TCP && dlen >= 18) { - - sum1 = ntohs(tcp->th_sum); - fix_datacksum(&tcp->th_sum, sumd); - sum2 = ntohs(tcp->th_sum); - - /* - * Fix ICMP checksum to compensate the TCP - * checksum adjustment. - */ - CALC_SUMD(sum1, sum2, sumd); - sumd2 = sumd; - } - } else { + sumd2 = sumd << 1; + CALC_SUMD(sum1, sum2, sumd); + sumd2 += sumd; + } - /* - * Fix IP checksum of the offending IP packet to adjust for - * the change in the IP address. - * - * Normally, you would expect that the ICMP checksum of the - * ICMP error message needs to be adjusted as well for the - * IP address change in oip. - * However, this is a NOP, because the ICMP checksum is - * calculated over the complete ICMP packet, which includes the - * changed oip IP addresses and oip->ip_sum. However, these - * two changes cancel each other out (if the delta for - * the IP address is x, then the delta for ip_sum is minus x), - * so no change in the icmp_cksum is necessary. - * - * Be careful that nat_dir refers to the direction of the - * offending IP packet (oip), not to its ICMP response (icmp) - */ - fix_datacksum(&oip->ip_sum, sumd); + /* + * Fix TCP pseudo header checksum to compensate for the + * IP address change. Before we can do the change, we + * must make sure that oip is sufficient large to hold + * the TCP checksum (normally it does not!). + */ + else if ((oip->ip_p == IPPROTO_TCP) && (dlen >= 18)) { + sum1 = ntohs(tcp->th_sum); + fix_datacksum(&tcp->th_sum, sumd); + sum2 = ntohs(tcp->th_sum); -/* XXX FV : without having looked at Solaris source code, it seems unlikely - * that SOLARIS would compensate this in the kernel (a body of an IP packet - * in the data section of an ICMP packet). I have the feeling that this should - * be unconditional, but I'm not in a position to check. - */ -#if !SOLARIS && !defined(__sgi) /* - * Fix UDP pseudo header checksum to compensate for the - * IP address change. + * Fix ICMP checksum to compensate the TCP + * checksum adjustment. */ - if (oip->ip_p == IPPROTO_UDP && udp->uh_sum) { - /* - * The UDP checksum is optional, only adjust it - * if it has been set - */ - sum1 = ntohs(udp->uh_sum); - fix_datacksum(&udp->uh_sum, sumd); - sum2 = ntohs(udp->uh_sum); - - /* - * Fix ICMP checksum to compensate the UDP - * checksum adjustment. - */ - CALC_SUMD(sum1, sum2, sumd); - sumd2 = sumd; - } - - /* - * Fix TCP pseudo header checksum to compensate for the - * IP address change. Before we can do the change, we - * must make sure that oip is sufficient large to hold - * the TCP checksum (normally it does not!). - */ - if (oip->ip_p == IPPROTO_TCP && dlen >= 18) { - - sum1 = ntohs(tcp->th_sum); - fix_datacksum(&tcp->th_sum, sumd); - sum2 = ntohs(tcp->th_sum); - - /* - * Fix ICMP checksum to compensate the TCP - * checksum adjustment. - */ - CALC_SUMD(sum1, sum2, sumd); - sumd2 = sumd; - } -#endif + sumd2 = sumd << 1; + CALC_SUMD(sum1, sum2, sumd); + sumd2 += sumd; + } else { + sumd2 = (sumd >> 16); + if (nat->nat_dir == NAT_OUTBOUND) + sumd2 = ~sumd2; + else + sumd2 = ~sumd2 + 1; } - if ((flags & IPN_TCPUDP) != 0) { + if (((flags & IPN_TCPUDP) != 0) && (dlen >= 4)) { /* * Step 2 : * For offending TCP/UDP IP packets, translate the ports as @@ -1885,17 +1846,14 @@ int dir; * include the TCP checksum. So we have to check if the * ip->ip_len actually holds the TCP checksum of the oip! */ - if (nat->nat_oport == tcp->th_dport) { if (tcp->th_sport != nat->nat_inport) { /* * Fix ICMP checksum to compensate port * adjustment. */ - sum1 = ntohs(tcp->th_sport); - sum2 = ntohs(nat->nat_inport); - CALC_SUMD(sum1, sum2, sumd); - sumd2 += sumd; + sum1 = ntohs(nat->nat_inport); + sum2 = ntohs(tcp->th_sport); tcp->th_sport = nat->nat_inport; /* @@ -1907,16 +1865,18 @@ int dir; * The UDP checksum is optional, only adjust * it if it has been set. */ - if (oip->ip_p == IPPROTO_UDP && udp->uh_sum) { + if ((oip->ip_p == IPPROTO_UDP) && + (dlen >= 8) && udp->uh_sum) { + sumd = sum1 - sum2; + sumd2 += sumd; sum1 = ntohs(udp->uh_sum); fix_datacksum(&udp->uh_sum, sumd); sum2 = ntohs(udp->uh_sum); /* - * Fix ICMP checksum to - * compensate UDP checksum - * adjustment. + * Fix ICMP checksum to compensate + * UDP checksum adjustment. */ CALC_SUMD(sum1, sum2, sumd); sumd2 += sumd; @@ -1928,63 +1888,73 @@ int dir; * packet flows the other direction compared to * the ICMP message. */ - if (oip->ip_p == IPPROTO_TCP && dlen >= 18) { - - sum1 = ntohs(tcp->th_sum); - fix_datacksum(&tcp->th_sum, sumd); - sum2 = ntohs(tcp->th_sum); - - /* - * Fix ICMP checksum to - * compensate TCP checksum - * adjustment. - */ - CALC_SUMD(sum1, sum2, sumd); - sumd2 += sumd; + if (oip->ip_p == IPPROTO_TCP) { + if (dlen >= 18) { + sumd = sum1 - sum2; + sumd2 += sumd; + + sum1 = ntohs(tcp->th_sum); + fix_datacksum(&tcp->th_sum, + sumd); + sum2 = ntohs(tcp->th_sum); + + /* + * Fix ICMP checksum to + * compensate TCP checksum + * adjustment. + */ + CALC_SUMD(sum1, sum2, sumd); + sumd2 += sumd; + } else { + sumd = sum2 - sum1 + 1; + sumd2 += sumd; + } } } - } else { - if (tcp->th_dport != nat->nat_outport) { - /* - * Fix ICMP checksum to compensate port - * adjustment. - */ - sum1 = ntohs(tcp->th_dport); - sum2 = ntohs(nat->nat_outport); - CALC_SUMD(sum1, sum2, sumd); + } else if (tcp->th_dport != nat->nat_outport) { + /* + * Fix ICMP checksum to compensate port + * adjustment. + */ + sum1 = ntohs(nat->nat_outport); + sum2 = ntohs(tcp->th_dport); + tcp->th_dport = nat->nat_outport; + + /* + * Fix udp checksum to compensate port + * adjustment. NOTE : the offending IP + * packet flows the other direction compared + * to the ICMP message. + * + * The UDP checksum is optional, only adjust + * it if it has been set. + */ + if ((oip->ip_p == IPPROTO_UDP) && + (dlen >= 8) && udp->uh_sum) { + sumd = sum1 - sum2; sumd2 += sumd; - tcp->th_dport = nat->nat_outport; + + sum1 = ntohs(udp->uh_sum); + fix_datacksum(&udp->uh_sum, sumd); + sum2 = ntohs(udp->uh_sum); /* - * Fix udp checksum to compensate port - * adjustment. NOTE : the offending IP - * packet flows the other direction compared - * to the ICMP message. - * - * The UDP checksum is optional, only adjust - * it if it has been set. + * Fix ICMP checksum to compensate + * UDP checksum adjustment. */ - if (oip->ip_p == IPPROTO_UDP && udp->uh_sum) { - - sum1 = ntohs(udp->uh_sum); - fix_datacksum(&udp->uh_sum, sumd); - sum2 = ntohs(udp->uh_sum); + CALC_SUMD(sum1, sum2, sumd); + } - /* - * Fix ICMP checksum to compensate - * UDP checksum adjustment. - */ - CALC_SUMD(sum1, sum2, sumd); + /* + * Fix tcp checksum (if present) to compensate + * port adjustment. NOTE : the offending IP + * packet flows the other direction compared to + * the ICMP message. + */ + if (oip->ip_p == IPPROTO_TCP) { + if (dlen >= 18) { + sumd = sum1 - sum2; sumd2 += sumd; - } - - /* - * Fix tcp checksum (if present) to compensate - * port adjustment. NOTE : the offending IP - * packet flows the other direction compared to - * the ICMP message. - */ - if (oip->ip_p == IPPROTO_TCP && dlen >= 18) { sum1 = ntohs(tcp->th_sum); fix_datacksum(&tcp->th_sum, sumd); @@ -1995,18 +1965,18 @@ int dir; * UDP checksum adjustment. */ CALC_SUMD(sum1, sum2, sumd); - sumd2 += sumd; + } else { + sumd = sum2 - sum1; + if (nat->nat_dir == NAT_OUTBOUND) + sumd++; } } + sumd2 += sumd; } if (sumd2) { sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); - if (nat->nat_dir == NAT_OUTBOUND) { - fix_outcksum(fin, &icmp->icmp_cksum, sumd2); - } else { - fix_incksum(fin, &icmp->icmp_cksum, sumd2); - } + fix_incksum(fin, &icmp->icmp_cksum, sumd2); } } if (oip->ip_p == IPPROTO_ICMP) @@ -2476,13 +2446,9 @@ maskloop: 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_outcksum(fin, &ip->ip_sum, sumd); - else - fix_incksum(fin, &ip->ip_sum, sumd); + fix_outcksum(fin, &ip->ip_sum, sumd); } -#if (SOLARIS || defined(__sgi)) && defined(_KERNEL) +#if (SOLARIS || defined(__sgi)) || !defined(_KERNEL) else { if (nat->nat_dir == NAT_OUTBOUND) fix_outcksum(fin, &ip->ip_sum, nat->nat_ipsumd); @@ -2510,7 +2476,8 @@ maskloop: if (nat->nat_age < fr_defnaticmpage) nat->nat_age = fr_defnaticmpage; #ifdef LARGE_NAT - else if (nat->nat_age > fr_defnatage) + else if ((!np || !np->in_age[1]) && + (nat->nat_age > fr_defnatage)) nat->nat_age = fr_defnatage; #endif /* @@ -2703,19 +2670,19 @@ maskloop: 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)) && defined(_KERNEL) if (nat->nat_dir == NAT_OUTBOUND) fix_incksum(fin, &ip->ip_sum, nat->nat_ipsumd); else fix_outcksum(fin, &ip->ip_sum, nat->nat_ipsumd); -#endif + + ip->ip_dst = nat->nat_inip; + fin->fin_fi.fi_daddr = nat->nat_inip.s_addr; + if ((fin->fin_off == 0) && !(fin->fin_fl & FI_SHORT)) { if ((nat->nat_inport != 0) && (tcp != NULL)) { @@ -2731,7 +2698,8 @@ maskloop: if (nat->nat_age < fr_defnaticmpage) nat->nat_age = fr_defnaticmpage; #ifdef LARGE_NAT - else if (nat->nat_age > fr_defnatage) + else if ((!np || !np->in_age[0]) && + (nat->nat_age > fr_defnatage)) nat->nat_age = fr_defnatage; #endif /* @@ -2983,7 +2951,7 @@ u_short *csump; if (&cp[1] >= ep) break; advance = cp[1]; - if (&cp[advance] >= ep) + if (&cp[advance] > ep) break; switch (opt) { case TCPOPT_MAXSEG: |