diff options
Diffstat (limited to 'contrib/ipfilter/ip_nat.c')
-rw-r--r-- | contrib/ipfilter/ip_nat.c | 122 |
1 files changed, 95 insertions, 27 deletions
diff --git a/contrib/ipfilter/ip_nat.c b/contrib/ipfilter/ip_nat.c index 5c10bc4..e0d5951 100644 --- a/contrib/ipfilter/ip_nat.c +++ b/contrib/ipfilter/ip_nat.c @@ -109,7 +109,7 @@ 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.63 2002/03/06 09:44:11 darrenr Exp $"; +static const char rcsid[] = "@(#)$Id: ip_nat.c,v 2.37.2.66 2002/04/23 14:58:27 darrenr Exp $"; #endif nat_t **nat_table[2] = { NULL, NULL }, @@ -425,7 +425,7 @@ caddr_t data; int mode; { register ipnat_t *nat, *nt, *n = NULL, **np = NULL; - int error = 0, ret, arg; + int error = 0, ret, arg, getlock; ipnat_t natd; u_32_t i, j; @@ -436,9 +436,15 @@ int mode; 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 */ + getlock = (mode & NAT_LOCKHELD) ? 0 : 1; + if ((cmd == SIOCADNAT) || (cmd == SIOCRMNAT)) { + if (mode & NAT_SYSSPACE) { + bcopy(data, (char *)&natd, sizeof(natd)); + error = 0; + } else { + error = IRCOPYPTR(data, (char *)&natd, sizeof(natd)); + } + } else if (cmd == SIOCIPFFL) { /* SIOCFLNAT & SIOCCNATL */ error = IRCOPY(data, (char *)&arg, sizeof(arg)); if (error) error = EFAULT; @@ -450,7 +456,8 @@ int mode; /* * For add/delete, look to see if the NAT entry is already present */ - WRITE_ENTER(&ipf_nat); + if (getlock == 1) + WRITE_ENTER(&ipf_nat); if ((cmd == SIOCADNAT) || (cmd == SIOCRMNAT)) { nat = &natd; nat->in_flags &= IPN_USERFLAGS; @@ -715,7 +722,8 @@ int mode; error = EINVAL; break; } - RWLOCK_EXIT(&ipf_nat); /* READ/WRITE */ + if (getlock == 1) + RWLOCK_EXIT(&ipf_nat); /* READ/WRITE */ done: if (nt) KFREE(nt); @@ -831,7 +839,7 @@ caddr_t data; return ENOMEM; bcopy((char *)&ipn, (char *)ipnn, sizeof(ipn)); - bcopy((char *)aps, ipnn->ipn_data, sizeof(*aps)); + bcopy((char *)aps, (char *)ipnn->ipn_data, sizeof(*aps)); if (aps->aps_data) { bcopy(aps->aps_data, ipnn->ipn_data + sizeof(*aps), aps->aps_psiz); @@ -1650,11 +1658,12 @@ int dir; { u_32_t sum1, sum2, sumd, sumd2 = 0; struct in_addr in; + int flags, dlen; icmphdr_t *icmp; udphdr_t *udp; + tcphdr_t *tcp; nat_t *nat; ip_t *oip; - int flags; if ((fin->fin_fl & FI_SHORT) || (fin->fin_off != 0)) return NULL; @@ -1673,6 +1682,13 @@ int dir; else if (oip->ip_p == IPPROTO_UDP) flags = IPN_UDP; udp = (udphdr_t *)((((char *)oip) + (oip->ip_hl << 2))); + dlen = ip->ip_len - ((char *)udp - (char *)ip); + /* + * XXX - what if this is bogus hl and we go off the end ? + * In this case, nat_icmplookup() will have returned NULL. + */ + tcp = (tcphdr_t *)udp; + /* * Need to adjust ICMP header to include the real IP#'s and * port #'s. Only apply a checksum change relative to the @@ -1695,8 +1711,6 @@ int dir; * change in the UDP and TCP checksums require yet another * adjustment of the ICMP checksum of the ICMP error message. * - * For the moment we forget about TCP, because that checksum is not - * in the first 8 bytes, so it will not be available in most cases. */ if (oip->ip_dst.s_addr == nat->nat_oip.s_addr) { @@ -1754,15 +1768,25 @@ int dir; sumd2 = sumd; } -#if 0 +#if 1 /* * 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) { + 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 } else { @@ -1813,15 +1837,25 @@ int dir; sumd2 = sumd; } -#if 0 +#if 1 /* * 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) { + 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 @@ -1829,14 +1863,6 @@ int dir; } if ((flags & IPN_TCPUDP) != 0) { - tcphdr_t *tcp; - - /* - * XXX - what if this is bogus hl and we go off the end ? - * In this case, nat_icmpinlookup() will have returned NULL. - */ - tcp = (tcphdr_t *)udp; - /* * Step 2 : * For offending TCP/UDP IP packets, translate the ports as @@ -1852,8 +1878,9 @@ int dir; * * To further complicate: the TCP checksum is not in the first * 8 bytes of the offending ip packet, so it most likely is not - * available (we might have to fix that if the encounter a - * device that returns more than 8 data bytes on icmp error) + * available. Some OSses like Solaris return enough bytes to + * 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) { @@ -1891,6 +1918,27 @@ int dir; CALC_SUMD(sum1, sum2, sumd); 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); + sum2 = ntohs(tcp->th_sum); + + /* + * Fix ICMP checksum to + * compensate TCP checksum + * adjustment. + */ + CALC_SUMD(sum1, sum2, sumd); + sumd2 += sumd; + } } } else { if (tcp->th_dport != nat->nat_outport) { @@ -1926,6 +1974,26 @@ int dir; CALC_SUMD(sum1, sum2, sumd); 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); + sum2 = ntohs(tcp->th_sum); + + /* + * Fix ICMP checksum to compensate + * UDP checksum adjustment. + */ + CALC_SUMD(sum1, sum2, sumd); + sumd2 += sumd; + } } } if (sumd2) { @@ -2435,7 +2503,7 @@ maskloop: csump = &tcp->th_sum; MUTEX_ENTER(&nat->nat_lock); fr_tcp_age(&nat->nat_age, - nat->nat_tcpstate, fin, 1); + nat->nat_tcpstate, fin, 1, 0); if (nat->nat_age < fr_defnaticmpage) nat->nat_age = fr_defnaticmpage; #ifdef LARGE_NAT @@ -2643,7 +2711,7 @@ maskloop: csump = &tcp->th_sum; MUTEX_ENTER(&nat->nat_lock); fr_tcp_age(&nat->nat_age, - nat->nat_tcpstate, fin, 0); + nat->nat_tcpstate, fin, 0, 0); if (nat->nat_age < fr_defnaticmpage) nat->nat_age = fr_defnaticmpage; #ifdef LARGE_NAT |