summaryrefslogtreecommitdiffstats
path: root/contrib/ipfilter/ip_nat.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/ipfilter/ip_nat.c')
-rw-r--r--contrib/ipfilter/ip_nat.c122
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
OpenPOWER on IntegriCloud