summaryrefslogtreecommitdiff
path: root/sys/netinet/ip_nat.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/netinet/ip_nat.c')
-rw-r--r--sys/netinet/ip_nat.c712
1 files changed, 416 insertions, 296 deletions
diff --git a/sys/netinet/ip_nat.c b/sys/netinet/ip_nat.c
index a3a91659022..17416db0c86 100644
--- a/sys/netinet/ip_nat.c
+++ b/sys/netinet/ip_nat.c
@@ -1,30 +1,18 @@
+/* $OpenBSD: ip_nat.c,v 1.8 1997/02/11 22:23:25 kstailey Exp $ */
/*
- * (C)opyright 1995 by Darren Reed.
+ * (C)opyright 1995-1996 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)
- *
- * Things still screwed:
- * 1) You can't specify a mapping to a class D address. By default, it
- * always adds 1 to that address. As a result, when a packet comes back,
- * the rule won't be matched. (e.g. outgoing address = 199.165.219.2,
- * whereas the rule says outgoing address = 199.165.219.1/32. Because
- * ADNATS always adds one, and there really isn't any provision for
- * only using 1 address (the in_space stuff is broke), there isn't any
- * easy solution)
- * 2) There needs to be a way to flush the NATs table completely. Either
- * an ioctl, or an easy way of doing it from ipnat.c.
- *
- * Missing from RFC 1631: ICMP header checksum recalculations.
+ * Added redirect stuff and a LOT of bug fixes. (mcn@EnGarde.com)
*
*/
#if 0
-#ifndef lint
+#if !defined(lint) && defined(LIBC_SCCS)
static char sccsid[] = "@(#)ip_nat.c 1.11 6/5/96 (C) 1995 Darren Reed";
-static char rcsid[] = "$OpenBSD: ip_nat.c,v 1.7 1997/01/18 08:29:21 downsj Exp $";
+static char rcsid[] = "Id: ip_nat.c,v 2.0.1.10 1997/02/08 06:38:49 darrenr Exp";
#endif
#endif
@@ -36,20 +24,15 @@ static char rcsid[] = "$OpenBSD: ip_nat.c,v 1.7 1997/01/18 08:29:21 downsj Exp $
#include <sys/errno.h>
#include <sys/types.h>
#include <sys/param.h>
-#if defined(_KERNEL) || defined(KERNEL)
-#include <sys/systm.h>
-#endif
#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/uio.h>
#include <sys/protosw.h>
#include <sys/socket.h>
+#ifdef _KERNEL
+# include <sys/systm.h>
+#endif
#if !defined(__SVR4) && !defined(__svr4__)
-# if defined(__OpenBSD__)
-# include <sys/dirent.h>
-# else
-# include <sys/dir.h>
-# endif
# include <sys/mbuf.h>
#else
# include <sys/byteorder.h>
@@ -66,25 +49,34 @@ static char rcsid[] = "$OpenBSD: ip_nat.c,v 1.7 1997/01/18 08:29:21 downsj Exp $
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
+
+#ifdef RFC1825
+#include <vpn/md5.h>
+#include <vpn/ipsec.h>
+extern struct ifnet vpnif;
+#endif
+
#include <netinet/ip_var.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <netinet/tcpip.h>
#include <netinet/ip_icmp.h>
-#include <sys/syslog.h>
#include "ip_fil_compat.h"
#include "ip_fil.h"
#include "ip_nat.h"
+#include "ip_state.h"
#ifndef MIN
#define MIN(a,b) (((a)<(b))?(a):(b))
#endif
-nat_t *nat_table[2][NAT_SIZE];
+nat_t *nat_table[2][NAT_SIZE], *nat_instances = NULL;
ipnat_t *nat_list = NULL;
-u_long nat_inuse = 0;
+u_long nat_inuse = 0,
+ fr_defnatage = 1200;
natstat_t nat_stats;
#if SOLARIS
# ifndef _KERNEL
+#define bzero(a,b) memset(a,0,b)
#define bcmp(a,b,c) memcpy(a,b,c)
#define bcopy(a,b,c) memmove(b,a,c)
# else
@@ -92,9 +84,57 @@ extern kmutex_t ipf_nat;
# endif
#endif
-int flush_nattable __P((void));
-int clear_natlist __P((void));
-nat_t *nat_new __P((ipnat_t *, ip_t *, int, u_short, int));
+static int flush_nattable __P((void)), clear_natlist __P((void));
+static void nattable_sync __P((void)), nat_delete __P((struct nat *));
+void fix_incksum __P((u_short *, u_long));
+void fix_outcksum __P((u_short *, u_long));
+nat_t *nat_new __P((ipnat_t *, ip_t *, fr_info_t *, u_short, int));
+nat_t *nat_lookupmapip __P((register int, struct in_addr, u_short,
+ struct in_addr, u_short));
+
+void
+fix_outcksum(sp, n)
+ u_short *sp;
+ u_long n;
+{
+ register u_short sumshort;
+ register u_long sum1;
+
+#ifdef sparc
+ sum1 = (~(*sp)) & 0xffff;
+#else
+ sum1 = (~ntohs(*sp)) & 0xffff;
+#endif
+ 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)
+ u_short *sp;
+ u_long n;
+{
+ register u_short sumshort;
+ register u_long sum1;
+
+#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.
@@ -125,7 +165,7 @@ nat_ioctl(data, cmd, mode)
caddr_t data;
int cmd, mode;
{
- register ipnat_t *nat, *n, **np;
+ register ipnat_t *nat, *n = NULL, **np = NULL;
ipnat_t natd;
int error = 0, ret;
@@ -134,10 +174,10 @@ nat_ioctl(data, cmd, mode)
*/
MUTEX_ENTER(&ipf_nat);
if ((cmd == SIOCADNAT) || (cmd == SIOCRMNAT)) {
- IRCOPY(data, &natd, sizeof(natd));
+ IRCOPY(data, (char *)&natd, sizeof(natd));
nat = &natd;
for (np = &nat_list; (n = *np); np = &n->in_next)
- if (!bcmp((char *)&nat->in_port, (char *)&n->in_port,
+ if (!bcmp((char *)&nat->in_flags, (char *)&n->in_flags,
IPN_CMPSIZ))
break;
}
@@ -160,14 +200,25 @@ nat_ioctl(data, cmd, mode)
IRCOPY((char *)data, (char *)n, sizeof(*n));
n->in_ifp = (void *)GETUNIT(n->in_ifname);
n->in_next = *np;
+ n->in_use = 0;
n->in_space = ~(0xffffffff & ntohl(n->in_outmsk));
- n->in_space -= 2; /* lose 2: broadcast + network address */
- if (n->in_inmsk != 0xffffffff)
+ if (n->in_space) /* lose 2: broadcast + network address */
+ n->in_space -= 2;
+ else
+ n->in_space = 1; /* single IP# mapping */
+ if (n->in_outmsk != 0xffffffff)
n->in_nip = ntohl(n->in_outip) + 1;
else
n->in_nip = ntohl(n->in_outip);
- if (n->in_redir == NAT_MAP)
+ 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));
+ }
/* Otherwise, these fields are preset */
*np = n;
break;
@@ -181,10 +232,13 @@ nat_ioctl(data, cmd, mode)
break;
}
*np = n->in_next;
+
KFREE(n);
+ nattable_sync();
break;
case SIOCGNATS :
- nat_stats.ns_table = (nat_t ***)nat_table;
+ 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_inuse = nat_inuse;
IWCOPY((char *)&nat_stats, (char *)data, sizeof(nat_stats));
@@ -192,14 +246,12 @@ nat_ioctl(data, cmd, mode)
case SIOCGNATL :
{
natlookup_t nl;
- nat_t *na;
IRCOPY((char *)data, (char *)&nl, sizeof(nl));
- if ((na = nat_lookupredir(&nl))) {
- nl.nl_inip = na->nat_outip;
- nl.nl_inport = na->nat_outport;
+
+ if (nat_lookupredir(&nl))
IWCOPY((char *)&nl, (char *)data, sizeof(nl));
- } else
+ else
error = ESRCH;
break;
}
@@ -225,36 +277,90 @@ nat_ioctl(data, cmd, mode)
}
+static void
+nat_delete(natd)
+ struct nat *natd;
+{
+ register struct nat **natp, *nat;
+
+ for (natp = natd->nat_hstart[0]; (nat = *natp);
+ natp = &nat->nat_hnext[0])
+ if (nat == natd) {
+ *natp = nat->nat_hnext[0];
+ break;
+ }
+
+ for (natp = natd->nat_hstart[1]; (nat = *natp);
+ natp = &nat->nat_hnext[1])
+ if (nat == natd) {
+ *natp = nat->nat_hnext[1];
+ break;
+ }
+
+ if (natd->nat_ptr) {
+ natd->nat_ptr->in_space++;
+ natd->nat_ptr->in_use--;
+ }
+ KFREE(natd);
+ nat_inuse--;
+}
+
+
/*
* flush_nattable - clear the NAT table of all mapping entries.
*/
-int
+static int
flush_nattable()
{
- nat_t *nat, **natp;
- int i, j = 0;
+ register nat_t *nat, **natp;
+ register int j = 0;
+
+ /*
+ * Everything will be deleted, so lets just make it the deletions
+ * quicker.
+ */
+ bzero((char *)nat_table[0], sizeof(nat_table[0]));
+ bzero((char *)nat_table[1], sizeof(nat_table[1]));
- for (natp = &nat_table[0][0], i = NAT_SIZE - 1; i >= 0; i--, natp++)
- while ((nat = *natp)) {
- *natp = nat->nat_next;
- KFREE((caddr_t)nat);
- j++;
- }
+ for (natp = &nat_instances; (nat = *natp); ) {
+ *natp = nat->nat_next;
+ nat_delete(nat);
+ j++;
+ }
- for (natp = &nat_table[1][0], i = NAT_SIZE - 1; i >= 0; i--, natp++)
- while ((nat = *natp)) {
- *natp = nat->nat_next;
- KFREE((caddr_t)nat);
- j++;
- }
return j;
}
/*
+ * I know this is O(N*M), but it can't be avoided.
+ */
+static void
+nattable_sync()
+{
+ register nat_t *nat;
+ register ipnat_t *np;
+ int i;
+
+ for (i = NAT_SIZE - 1; i >= 0; i--)
+ for (nat = nat_instances; nat; nat = nat->nat_next) {
+ for (np = nat_list; np; np = np->in_next)
+ if (nat->nat_ptr == np)
+ break;
+ /*
+ * XXX - is it better to remove this if ? works the
+ * same if it is just "nat->nat_ptr = np".
+ */
+ if (!np)
+ nat->nat_ptr = NULL;
+ }
+}
+
+
+/*
* clear_natlist - delete all entries in the active NAT mapping list.
*/
-int
+static int
clear_natlist()
{
register ipnat_t *n, **np;
@@ -264,6 +370,8 @@ clear_natlist()
*np = n->in_next;
KFREE(n);
}
+
+ nattable_sync();
return i;
}
@@ -272,21 +380,23 @@ clear_natlist()
* Create a new NAT table entry.
*/
nat_t *
-nat_new(np, ip, hlen, flags, direction)
+nat_new(np, ip, fin, flags, direction)
ipnat_t *np;
ip_t *ip;
- int hlen;
+ fr_info_t *fin;
u_short flags;
int direction;
{
register u_long sum1, sum2, sumd;
u_short port = 0, sport = 0, dport = 0, nport = 0;
struct in_addr in;
- tcphdr_t *tcp;
+ tcphdr_t *tcp = NULL;
nat_t *nat, **natp;
+ u_short nflags;
- if (flags) {
- tcp = (tcphdr_t *)((char *)ip + hlen);
+ nflags = flags & np->in_flags;
+ if (flags & IPN_TCPUDP) {
+ tcp = (tcphdr_t *)fin->fin_dp;
sport = tcp->th_sport;
dport = tcp->th_dport;
}
@@ -295,6 +405,8 @@ nat_new(np, ip, hlen, flags, direction)
if (!(nat = (nat_t *)KMALLOC(sizeof(*nat))))
return NULL;
+ bzero((char *)nat, sizeof(*nat));
+
/*
* Search the current table for a match.
*/
@@ -304,8 +416,9 @@ nat_new(np, ip, hlen, flags, direction)
* record, then create a new port
*/
do {
+ port = 0;
in.s_addr = np->in_nip;
- if (np->in_flags & IPN_TCPUDP) {
+ if (nflags & IPN_TCPUDP) {
port = htons(np->in_pnext++);
if (np->in_pnext >= ntohs(np->in_pmax)) {
np->in_pnext = ntohs(np->in_pmin);
@@ -313,46 +426,32 @@ nat_new(np, ip, hlen, flags, direction)
if (np->in_outmsk != 0xffffffff)
np->in_nip++;
}
- } else {
+ } else if (np->in_outmsk != 0xffffffff) {
np->in_space--;
- if (np->in_outmsk != 0xffffffff)
- np->in_nip++;
+ np->in_nip++;
}
+
+ if (!port && (flags & IPN_TCPUDP))
+ port = sport;
if ((np->in_nip & ntohl(np->in_outmsk)) >
- ntohl(np->in_outip))
+ ntohl(np->in_outip))
np->in_nip = ntohl(np->in_outip) + 1;
- } while (nat_lookupinip(in, sport));
+ } while (nat_inlookup(flags, ip->ip_dst, dport, in, port));
/* Setup the NAT table */
- nat->nat_use = 0;
nat->nat_inip = ip->ip_src;
nat->nat_outip.s_addr = htonl(in.s_addr);
+ nat->nat_oip = ip->ip_dst;
sum1 = (ntohl(ip->ip_src.s_addr) & 0xffff) +
(ntohl(ip->ip_src.s_addr) >> 16) + ntohs(sport);
- /* Do it twice */
- sum1 = (sum1 & 0xffff) + (sum1 >> 16);
- sum1 = (sum1 & 0xffff) + (sum1 >> 16);
-
sum2 = (in.s_addr & 0xffff) + (in.s_addr >> 16) + ntohs(port);
- /* Do it twice */
- sum2 = (sum2 & 0xffff) + (sum2 >> 16);
- sum2 = (sum2 & 0xffff) + (sum2 >> 16);
-
- if (sum1 > sum2)
- sum2--; /* Because ~1 == -2, We really need ~1 == -1 */
- sumd = sum2 - sum1;
- sumd = (sumd & 0xffff) + (sumd >> 16);
- nat->nat_sumd = (sumd & 0xffff) + (sumd >> 16);
-
- if (sport) {
+ if (flags & IPN_TCPUDP) {
nat->nat_inport = sport;
nat->nat_outport = port;
- } else {
- nat->nat_inport = 0;
- nat->nat_outport = 0;
+ nat->nat_oport = dport;
}
} else {
@@ -363,9 +462,9 @@ nat_new(np, ip, hlen, flags, direction)
* internal port.
*/
in.s_addr = ntohl(np->in_inip);
- nport = np->in_pnext;
+ if (!(nport = np->in_pnext))
+ nport = dport;
- nat->nat_use = 0;
nat->nat_inip.s_addr = htonl(in.s_addr);
nat->nat_outip = ip->ip_dst;
nat->nat_oip = ip->ip_src;
@@ -373,51 +472,75 @@ nat_new(np, ip, hlen, flags, direction)
sum1 = (ntohl(ip->ip_dst.s_addr) & 0xffff) +
(ntohl(ip->ip_dst.s_addr) >> 16) + ntohs(dport);
+ sum2 = (in.s_addr & 0xffff) + (in.s_addr >> 16) + ntohs(nport);
+
+ if (flags & IPN_TCPUDP) {
+ nat->nat_inport = nport;
+ nat->nat_outport = dport;
+ nat->nat_oport = sport;
+ }
+ }
+
+ /* Do it twice */
+ sum1 = (sum1 & 0xffff) + (sum1 >> 16);
+ sum1 = (sum1 & 0xffff) + (sum1 >> 16);
+
+ /* Do it twice */
+ sum2 = (sum2 & 0xffff) + (sum2 >> 16);
+ sum2 = (sum2 & 0xffff) + (sum2 >> 16);
+
+ if (sum1 > sum2)
+ sum2--; /* Because ~1 == -2, We really need ~1 == -1 */
+ sumd = sum2 - sum1;
+ sumd = (sumd & 0xffff) + (sumd >> 16);
+ nat->nat_sumd = (sumd & 0xffff) + (sumd >> 16);
+
+ if ((flags & IPN_TCPUDP) && ((sport != port) || (dport != nport))) {
+ if (direction == NAT_OUTBOUND)
+ sum1 = (ntohl(ip->ip_src.s_addr) & 0xffff) +
+ (ntohl(ip->ip_src.s_addr) >> 16);
+ else
+ sum1 = (ntohl(ip->ip_dst.s_addr) & 0xffff) +
+ (ntohl(ip->ip_dst.s_addr) >> 16);
+
+ sum2 = (in.s_addr & 0xffff) + (in.s_addr >> 16);
+
/* Do it twice */
sum1 = (sum1 & 0xffff) + (sum1 >> 16);
sum1 = (sum1 & 0xffff) + (sum1 >> 16);
- sum2 = (in.s_addr & 0xffff) + (in.s_addr >> 16) + ntohs(nport);
-
/* Do it twice */
sum2 = (sum2 & 0xffff) + (sum2 >> 16);
sum2 = (sum2 & 0xffff) + (sum2 >> 16);
- if (sum2 > sum1)
- sum1--; /* Because ~1 == -2, We really need ~1 == -1 */
- sumd = (sum1 - sum2);
+ if (sum1 > sum2)
+ sum2--; /* Because ~1 == -2, We really need ~1 == -1 */
+ sumd = sum2 - sum1;
sumd = (sumd & 0xffff) + (sumd >> 16);
- nat->nat_sumd = (sumd & 0xffff) + (sumd >> 16);
-
- if (dport) {
- nat->nat_inport = nport;
- nat->nat_outport = dport;
- nat->nat_oport = sport;
- } else {
- nat->nat_inport = 0;
- nat->nat_outport = 0;
- }
- }
+ nat->nat_ipsumd = (sumd & 0xffff) + (sumd >> 16);
+ } else
+ nat->nat_ipsumd = nat->nat_sumd;
in.s_addr = htonl(in.s_addr);
+ nat->nat_next = nat_instances;
+ nat_instances = nat;
natp = &nat_table[0][nat->nat_inip.s_addr % NAT_SIZE];
- nat->nat_next = *natp;
+ nat->nat_hstart[0] = natp;
+ nat->nat_hnext[0] = *natp;
*natp = nat;
- nat->nat_use++;
natp = &nat_table[1][nat->nat_outip.s_addr % NAT_SIZE];
- nat->nat_next = *natp;
+ nat->nat_hstart[1] = natp;
+ nat->nat_hnext[1] = *natp;
*natp = nat;
- nat->nat_use++;
- if (direction == NAT_REDIRECT) {
- ip->ip_src = in;
- if (flags)
+ nat->nat_ptr = np;
+ np->in_use++;
+ if (direction == NAT_OUTBOUND) {
+ if (flags & IPN_TCPUDP)
tcp->th_sport = htons(port);
} else {
- ip->ip_dst = in;
- if (flags)
+ if (flags & IPN_TCPUDP)
tcp->th_dport = htons(nport);
}
-
nat_stats.ns_added++;
nat_inuse++;
return nat;
@@ -428,76 +551,113 @@ nat_new(np, ip, hlen, flags, direction)
* 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_lookupredir(np)
- natlookup_t *np;
+nat_inlookup(flags, src, sport, mapdst, mapdport)
+ register int flags;
+ struct in_addr src, mapdst;
+ u_short sport, mapdport;
{
- nat_t *nat;
+ register nat_t *nat;
- nat = nat_table[0][np->nl_inip.s_addr % NAT_SIZE];
- for (; nat; nat = nat->nat_next)
- if ((nat->nat_inip.s_addr == np->nl_inip.s_addr) &&
- (nat->nat_oip.s_addr == np->nl_outip.s_addr) &&
- (np->nl_inport == nat->nat_inport) &&
- (np->nl_outport == nat->nat_oport))
+ flags &= IPN_TCPUDP;
+
+ nat = nat_table[1][mapdst.s_addr % NAT_SIZE];
+ for (; nat; nat = nat->nat_hnext[1])
+ if (nat->nat_oip.s_addr == src.s_addr &&
+ nat->nat_outip.s_addr == mapdst.s_addr &&
+ (!flags || (nat->nat_oport == sport &&
+ nat->nat_outport == mapdport)))
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_lookupinip(ipaddr, sport)
- struct in_addr ipaddr;
- u_short sport;
+nat_outlookup(flags, src, sport, dst, dport)
+ register int flags;
+ struct in_addr src, dst;
+ u_short sport, dport;
{
- nat_t *nat;
+ register nat_t *nat;
- nat = nat_table[0][ipaddr.s_addr % NAT_SIZE];
+ flags &= IPN_TCPUDP;
- for (; nat; nat = nat->nat_next)
- if (nat->nat_inip.s_addr == ipaddr.s_addr) {
- if (nat->nat_inport && (sport != nat->nat_inport))
- continue;
+ nat = nat_table[0][src.s_addr % NAT_SIZE];
+ for (; nat; nat = nat->nat_hnext[0])
+ if (nat->nat_inip.s_addr == src.s_addr &&
+ nat->nat_oip.s_addr == dst.s_addr &&
+ (!flags || (nat->nat_inport == sport &&
+ nat->nat_oport == dport)))
return nat;
- }
return NULL;
}
+/*
+ * Lookup a nat entry based on the mapped source ip address/port and
+ * real destination address/port. We use this lookup when sending a packet
+ * out, we're looking for a table entry, based on the source address.
+ */
nat_t *
-nat_lookupoutip(np, ip, tcp)
- register ipnat_t *np;
- ip_t *ip;
- tcphdr_t *tcp;
+nat_lookupmapip(flags, mapsrc, mapsport, dst, dport)
+ register int flags;
+ struct in_addr mapsrc, dst;
+ u_short mapsport, dport;
{
- struct in_addr ipaddr;
- u_short port = tcp->th_dport;
- nat_t *nat;
+ register nat_t *nat;
- ipaddr.s_addr = ip->ip_dst.s_addr;
- nat = nat_table[1][ipaddr.s_addr % NAT_SIZE];
+ flags &= IPN_TCPUDP;
- if (np->in_redir == NAT_MAP) {
- for (; nat; nat = nat->nat_next)
- if (nat->nat_outip.s_addr == ipaddr.s_addr &&
- (!nat->nat_outport || (port == nat->nat_outport)))
- return nat;
- } else
- for (; nat; nat = nat->nat_next)
- if (nat->nat_outip.s_addr == ipaddr.s_addr &&
- nat->nat_oip.s_addr == ip->ip_src.s_addr &&
- port == nat->nat_outport &&
- tcp->th_sport == nat->nat_oport)
- return nat;
+ nat = nat_table[1][mapsrc.s_addr % NAT_SIZE];
+ for (; nat; nat = nat->nat_hnext[0])
+ if (nat->nat_outip.s_addr == mapsrc.s_addr &&
+ nat->nat_oip.s_addr == dst.s_addr &&
+ (!flags || (nat->nat_outport == mapsport &&
+ nat->nat_oport == dport)))
+ return nat;
return NULL;
}
/*
+ * Lookup the NAT tables to search for a matching redirect
+ */
+nat_t *
+nat_lookupredir(np)
+ register natlookup_t *np;
+{
+ nat_t *nat;
+
+ /*
+ * 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(IPN_TCPUDP, np->nl_inip, np->nl_inport,
+ np->nl_outip, np->nl_outport))) {
+ np->nl_inip = nat->nat_outip;
+ np->nl_inport = nat->nat_outport;
+ }
+ return nat;
+}
+
+
+/*
* Packets going out on the external interface go through this.
* Here, the source address requires alteration, if anything.
*/
-void
+int
ip_natout(ip, hlen, fin)
ip_t *ip;
int hlen;
@@ -505,21 +665,28 @@ ip_natout(ip, hlen, fin)
{
register ipnat_t *np;
register u_long ipa;
- register u_long sum1;
- tcphdr_t *tcp;
+ tcphdr_t *tcp = NULL;
nat_t *nat;
- u_short nflags = 0, sport = 0;
- struct ifnet *ifp = fin->fin_ifp;
+ u_short nflags = 0, sport = 0, dport = 0, *csump = NULL;
+ struct ifnet *ifp;
+ frentry_t *fr;
+
+ 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 & 0x1fff) && !(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) {
- tcp = (tcphdr_t *)fin->fin_dp;
- sport = tcp->th_sport;
+ if (nflags) {
+ tcp = (tcphdr_t *)fin->fin_dp;
+ sport = tcp->th_sport;
+ dport = tcp->th_dport;
+ }
}
ipa = ip->ip_src.s_addr;
@@ -529,13 +696,14 @@ ip_natout(ip, hlen, fin)
if ((np->in_ifp == ifp) && np->in_space &&
(!np->in_flags || (np->in_flags & nflags)) &&
((ipa & np->in_inmsk) == np->in_inip) &&
- (np->in_redir == NAT_MAP ||
- np->in_pnext == sport)) {
+ ((np->in_redir == NAT_MAP) ||
+ (np->in_pnext == sport))) {
/*
* If there is no current entry in the nat table for
* this IP#, create one for it.
*/
- if (!(nat = nat_lookupinip(ip->ip_src, sport))) {
+ if (!(nat = nat_outlookup(nflags, ip->ip_src, sport,
+ ip->ip_dst, dport))) {
if (np->in_redir == NAT_REDIRECT)
continue;
/*
@@ -544,64 +712,60 @@ ip_natout(ip, hlen, fin)
* Redirections are only for incoming
* connections.
*/
- if (!(nat = nat_new(np, ip, hlen,
- nflags & np->in_flags,
+ if (!(nat = nat_new(np, ip, fin, nflags,
NAT_OUTBOUND)))
break;
- } else
- ip->ip_src = nat->nat_outip;
+ }
+ ip->ip_src = nat->nat_outip;
- nat->nat_age = 1200; /* 5 mins */
+ nat->nat_age = fr_defnatage; /* 5 mins */
/*
* Fix up checksums, not by recalculating them, but
* simply computing adjustments.
*/
+#if SOLARIS
+ if (np->in_redir == NAT_MAP)
+ fix_outcksum(&ip->ip_sum, nat->nat_ipsumd);
+ else
+ fix_incksum(&ip->ip_sum, nat->nat_ipsumd);
+#endif
+
if (nflags && !(ip->ip_off & 0x1fff) &&
!(fin->fin_fi.fi_fl & FI_SHORT)) {
- u_short *sp;
- u_short sumshort;
if (nat->nat_outport)
tcp->th_sport = nat->nat_outport;
if (ip->ip_p == IPPROTO_TCP) {
- sp = &tcp->th_sum;
-
- sum1 = (~ntohs(*sp)) & 0xffff;
-
- sum1 += nat->nat_sumd;
-
- sum1 = (sum1 >> 16) + (sum1 & 0xffff);
- /* Again */
- sum1 = (sum1 >> 16) + (sum1 & 0xffff);
- sumshort = ~(u_short)sum1;
- *sp = htons(sumshort);
-
+ csump = &tcp->th_sum;
+ set_tcp_age(&nat->nat_age,
+ nat->nat_state, ip, fin,1);
} else if (ip->ip_p == IPPROTO_UDP) {
udphdr_t *udp = (udphdr_t *)tcp;
- sp = &udp->uh_sum;
-
- if (udp->uh_sum) {
- sum1 = (~ntohs(*sp)) & 0xffff;
- sum1 += nat->nat_sumd;
- sum1 = (sum1 >> 16) +
- (sum1 & 0xffff);
- /* Again */
- sum1 = (sum1 >> 16) +
- (sum1 & 0xffff);
- sumshort = ~(u_short)sum1;
- *sp = htons(sumshort);
- }
+ if (udp->uh_sum)
+ csump = &udp->uh_sum;
+ } else if (ip->ip_p == IPPROTO_ICMP) {
+ icmphdr_t *ic = (icmphdr_t *)tcp;
+
+ csump = &ic->icmp_cksum;
+ }
+ if (csump) {
+ if (np->in_redir == NAT_MAP)
+ fix_outcksum(csump,
+ nat->nat_sumd);
+ else
+ fix_incksum(csump,
+ nat->nat_sumd);
}
}
nat_stats.ns_mapped[1]++;
MUTEX_EXIT(&ipf_nat);
- return;
+ return 1;
}
MUTEX_EXIT(&ipf_nat);
- return;
+ return 0;
}
@@ -609,7 +773,7 @@ ip_natout(ip, hlen, fin)
* Packets coming in from the external interface go through this.
* Here, the destination address requires alteration, if anything.
*/
-void
+int
ip_natin(ip, hlen, fin)
ip_t *ip;
int hlen;
@@ -617,10 +781,9 @@ ip_natin(ip, hlen, fin)
{
register ipnat_t *np;
register struct in_addr in;
- register u_long sum1;
struct ifnet *ifp = fin->fin_ifp;
- tcphdr_t *tcp;
- u_short port = 0, nflags;
+ tcphdr_t *tcp = NULL;
+ u_short sport = 0, dport = 0, nflags = 0, *csump = NULL;
nat_t *nat;
if (!(ip->ip_off & 0x1fff) && !(fin->fin_fi.fi_fl & FI_SHORT)) {
@@ -628,10 +791,11 @@ ip_natin(ip, hlen, fin)
nflags = IPN_TCP;
else if (ip->ip_p == IPPROTO_UDP)
nflags = IPN_UDP;
- }
- if (nflags) {
- tcp = (tcphdr_t *)((char *)ip + hlen);
- port = tcp->th_dport;
+ if (nflags) {
+ tcp = (tcphdr_t *)((char *)ip + hlen);
+ dport = tcp->th_dport;
+ sport = tcp->th_sport;
+ }
}
in = ip->ip_dst;
@@ -641,8 +805,9 @@ ip_natin(ip, hlen, fin)
if ((np->in_ifp == ifp) &&
(!np->in_flags || (nflags & np->in_flags)) &&
((in.s_addr & np->in_outmsk) == np->in_outip) &&
- (np->in_redir == NAT_MAP || np->in_pmin == port)) {
- if (!(nat = nat_lookupoutip(np, ip, tcp))) {
+ (np->in_redir == NAT_MAP || np->in_pmin == dport)) {
+ if (!(nat = nat_inlookup(nflags, ip->ip_src, sport,
+ ip->ip_dst, dport))) {
if (np->in_redir == NAT_MAP)
continue;
else {
@@ -652,63 +817,61 @@ ip_natin(ip, hlen, fin)
* nat_new. Otherwise, if it's just a
* mapping, do a continue;
*/
- nflags &= np->in_flags;
- if (!(nat = nat_new(np, ip, hlen,
+ if (!(nat = nat_new(np, ip, fin,
nflags,
NAT_INBOUND)))
break;
}
}
- nat->nat_age = 1200;
-
ip->ip_dst = nat->nat_inip;
+ nat->nat_age = fr_defnatage;
+
/*
* Fix up checksums, not by recalculating them, but
* simply computing adjustments.
*/
+#if SOLARIS
+ if (np->in_redir == NAT_MAP)
+ fix_incksum(&ip->ip_sum, nat->nat_ipsumd);
+ else
+ fix_outcksum(&ip->ip_sum, nat->nat_ipsumd);
+#endif
if (nflags && !(ip->ip_off & 0x1fff) &&
!(fin->fin_fi.fi_fl & FI_SHORT)) {
- u_short *sp;
- u_short sumshort;
if (nat->nat_inport)
tcp->th_dport = nat->nat_inport;
if (ip->ip_p == IPPROTO_TCP) {
- sp = &tcp->th_sum;
-
- sum1 = (~ntohs(*sp)) & 0xffff;
- sum1 += ~nat->nat_sumd & 0xffff;
- sum1 = (sum1 >> 16) + (sum1 & 0xffff);
- /* Again */
- sum1 = (sum1 >> 16) + (sum1 & 0xffff);
- sumshort = ~(u_short)sum1;
- *sp = htons(sumshort);
+ csump = &tcp->th_sum;
+ set_tcp_age(&nat->nat_age,
+ nat->nat_state, ip, fin,0);
} else if (ip->ip_p == IPPROTO_UDP) {
udphdr_t *udp = (udphdr_t *)tcp;
- sp = &udp->uh_sum;
-
- if (udp->uh_sum) {
- sum1 = (~ntohs(*sp)) & 0xffff;
- sum1+= ~nat->nat_sumd & 0xffff;
- sum1 = (sum1 >> 16) +
- (sum1 & 0xffff);
- /* Again */
- sum1 = (sum1 >> 16) +
- (sum1 & 0xffff);
- sumshort = ~(u_short)sum1;
- *sp = htons(sumshort);
- }
+ if (udp->uh_sum)
+ csump = &udp->uh_sum;
+ } else if (ip->ip_p == IPPROTO_ICMP) {
+ icmphdr_t *ic = (icmphdr_t *)tcp;
+
+ csump = &ic->icmp_cksum;
+ }
+ if (csump) {
+ if (np->in_redir == NAT_MAP)
+ fix_incksum(csump,
+ nat->nat_sumd);
+ else
+ fix_outcksum(csump,
+ nat->nat_sumd);
}
}
nat_stats.ns_mapped[0]++;
MUTEX_EXIT(&ipf_nat);
- return;
+ return 1;
}
MUTEX_EXIT(&ipf_nat);
- return;
+ return 0;
}
@@ -718,28 +881,12 @@ ip_natin(ip, hlen, fin)
void
ip_natunload()
{
- register struct nat *nat, **natp;
- register struct ipnat *ipn, **ipnp;
- register int i;
MUTEX_ENTER(&ipf_nat);
- for (i = 0; i < NAT_SIZE; i++)
- for (natp = &nat_table[0][i]; (nat = *natp); ) {
- *natp = nat->nat_next;
- if (!--nat->nat_use)
- KFREE(nat);
- }
- for (i = 0; i < NAT_SIZE; i++)
- for (natp = &nat_table[1][i]; (nat = *natp); ) {
- *natp = nat->nat_next;
- if (!--nat->nat_use)
- KFREE(nat);
- }
- for (ipnp = &nat_list; (ipn = *ipnp); ) {
- *ipnp = ipn->in_next;
- KFREE(ipn);
- }
+ (void) clear_natlist();
+ (void) flush_nattable();
+
MUTEX_EXIT(&ipf_nat);
}
@@ -752,41 +899,14 @@ void
ip_natexpire()
{
register struct nat *nat, **natp;
- register int i;
MUTEX_ENTER(&ipf_nat);
- for (i = 0; i < NAT_SIZE; i++)
- for (natp = &nat_table[0][i]; (nat = *natp); ) {
- if (nat->nat_age > 0)
- nat->nat_age--;
- if (!nat->nat_use || !nat->nat_age) {
- *natp = nat->nat_next;
- if (nat->nat_use)
- nat->nat_use--;
- if (!nat->nat_use) {
- KFREE(nat);
- nat_stats.ns_expire++;
- nat_inuse--;
- }
- } else
- natp = &nat->nat_next;
- }
-
- for (i = 0; i < NAT_SIZE; i++)
- for (natp = &nat_table[1][i]; (nat = *natp); ) {
- if (nat->nat_age > 0)
- nat->nat_age--;
- if (!nat->nat_use || !nat->nat_age) {
- *natp = nat->nat_next;
- if (nat->nat_use)
- nat->nat_use--;
- if (!nat->nat_use) {
- KFREE(nat);
- nat_stats.ns_expire++;
- nat_inuse--;
- }
- } else
- natp = &nat->nat_next;
- }
+ for (natp = &nat_instances; (nat = *natp); natp = &nat->nat_next) {
+ if (--nat->nat_age)
+ continue;
+ *natp = nat->nat_next;
+ nat_delete(nat);
+ nat_stats.ns_expire++;
+ }
MUTEX_EXIT(&ipf_nat);
}