shithub: 9ferno

Download patch

ref: d8b894afc0ff3c81fc750826ac0016250e4f81e4
parent: fa18daa2f56b306b151781523e877bd07d62f799
author: 9ferno <gophone2015@gmail.com>
date: Thu Aug 12 23:08:35 EDT 2021

imported 9legacy NAT patch

imported NAT patches from 9legacy http://9legacy.org/patch.html

http://9legacy.org/9legacy/patch/nat.diff
http://9legacy.org/9legacy/patch/nat-conf.diff

Should this patch be changed to use a pktmedium?

This is a NAT implementation for Plan 9 from Bell Labs.

* Introduction

This is a NAPT (Network Address Port Translation) implementation,
also known under the name "IP masquerade".

This is an early work, don't expect too much from it. Improvements
will come in the next future.

* Installation

First, apply the patches with the "apply" script:

/n/sources/contrib/djc/nat/apply

Then, add "nat" to you kernel configuration file, under
section dev/ip.

Finally, compile and install your kernel.

* Documentation

First, enable routing:

echo iprouting > /net/ipifc/clone

Then, enable NAT:

echo nat add <src> <mask> <dst> > /net/ipifc/<ifc>/ctl

Where:
 - <src> is the address of the source network or machine
   allowed to pass through the NAT
 - <mask> is the corresponding mask
 - <dst> is the address to be translated to, which must
   exist on the specified interface
 - <ifc> is your network physical interface number.

You can add or remove any NAT rule you want.

* Performance

The current implementation can handle up to 800 TCP connections
per second on a Soekris net5501-70, but the performance quickly
decrease as the table grows.

* Future

We plan to implement the following features in the next future:

 - improve performance
 - improve garbage collector
 - handling of TCP and IL connection states
 - IPv6 support
 - port forwarding (you can currently use trampoline(8) instead)
 - FTP proxy
 - statistics

* History

The work began in June 2010 and quickly evolved to the current state.
Erik Quanstrom offered his help in March 2011 with code review
and suggestions. We thank him much.

* Contact

David du Colombier <0intro@gmail.com>
With the help of Jean-Baptiste Campesato <camjelemon@gmail.com>

> But, I could not get the routing to work. Just want to check if you do
> not mind sharing the ip configuration that made the patch work.
>
> Thanks so much for the patch,

Personally, on my NAT gateway, I was running:

bind -a '#'l1 /net
ip/ipconfig ether /net/ether1 <dst> 255.255.255.0
echo iprouting > /net/ipifc/clone

Where #l1 (which provides /net/ether1) is the internal LAN
interface and <dst> is the public WAN address (on /net/ether0).

Then, you have to enable iprouting, so the packets can pass
through the NAT.

--- a/os/ip/devip.c	Tue Aug 17 16:28:42 2021
+++ b/os/ip/devip.c	Thu Aug 12 23:08:35 2021
@@ -59,7 +59,7 @@
 extern	void nullmediumlink(void);
 extern	void pktmediumlink(void);
 static	long ndbwrite(Fs*, char*, ulong, int);
-static	void	closeconv(Conv*);
+extern void    closeconv(Conv*);
 
 static int
 ip3gen(Chan *c, int i, Dir *dp)
@@ -550,7 +550,7 @@
 	return n;
 }
 
-static void
+extern void
 closeconv(Conv *cv)
 {
 	Conv *nc;
@@ -773,7 +773,7 @@
 /*
  *  pick a local port and set it
  */
-static void
+extern void
 setlport(Conv* c)
 {
 	Proto *p;
@@ -811,7 +811,7 @@
 				break;
 			}
 		}
-		if(!found)
+		if(found == 0)
 			break;
 	}
 	c->lport = (*pp)++;
--- a/os/ip/icmp.c	Tue Aug 17 16:28:42 2021
+++ b/os/ip/icmp.c	Thu Aug 12 23:08:35 2021
@@ -463,7 +463,13 @@
 	}
 	return p - buf;
 }
-	
+
+int
+icmpgc(Proto *icmp)
+{
+	return natgc(icmp->ipproto);
+}
+
 void
 icmpinit(Fs *fs)
 {
@@ -481,7 +487,7 @@
 	icmp->stats = icmpstats;
 	icmp->ctl = nil;
 	icmp->advise = icmpadvise;
-	icmp->gc = nil;
+	icmp->gc = icmpgc;
 	icmp->ipproto = IP_ICMPPROTO;
 	icmp->nc = 128;
 	icmp->ptclsize = 0;
--- a/os/ip/il.c	Tue Aug 17 16:28:42 2021
+++ b/os/ip/il.c	Thu Aug 12 23:08:35 2021
@@ -1380,6 +1380,12 @@
 	}
 }
 
+int
+ilgc(Proto *il)
+{
+	return natgc(il->ipproto);
+}
+
 void
 ilinit(Fs *f)
 {
@@ -1400,7 +1406,7 @@
 	il->advise = iladvise;
 	il->stats = ilxstats;
 	il->inuse = ilinuse;
-	il->gc = nil;
+	il->gc = ilgc;
 	il->ipproto = IP_ILPROTO;
 	il->nc = scalednconv();
 	il->ptclsize = sizeof(Ilcb);
--- a/os/ip/ip.c	Tue Aug 17 16:28:42 2021
+++ b/os/ip/ip.c	Thu Aug 12 23:08:35 2021
@@ -7,7 +7,6 @@
 
 #include	"ip.h"
 
-typedef struct Ip4hdr		Ip4hdr;
 typedef struct IP		IP;
 typedef struct Fragment4	Fragment4;
 typedef struct Fragment6	Fragment6;
@@ -26,20 +25,6 @@
 
 #define BLKIPVER(xp)	(((Ip4hdr*)((xp)->rp))->vihl&0xF0)
 
-struct Ip4hdr
-{
-	uchar	vihl;		/* Version and header length */
-	uchar	tos;		/* Type of service */
-	uchar	length[2];	/* packet length */
-	uchar	id[2];		/* ip->identification */
-	uchar	frag[2];	/* Fragment information */
-	uchar	ttl;      	/* Time to live */
-	uchar	proto;		/* Protocol */
-	uchar	cksum[2];	/* Header checksum */
-	uchar	src[4];		/* IP source */
-	uchar	dst[4];		/* IP destination */
-};
-
 /* MIB II counters */
 enum
 {
@@ -295,6 +280,10 @@
 	if(ifc->m == nil)
 		goto raise;
 
+	/* Output NAT */
+	if(nato(bp, ifc, f) != 0)
+		goto raise;
+
 	/* If we dont need to fragment just send it */
 	medialen = ifc->maxtu - ifc->m->hsize;
 	if(len <= medialen) {
@@ -442,6 +431,9 @@
 	}
 
 	h = (Ip4hdr*)(bp->rp);
+
+	/* Input NAT */
+	nati(bp, ifc);
 
 	/* dump anything that whose header doesn't checksum */
 	if((bp->flag & Bipck) == 0 && ipcsum(&h->vihl)) {
--- a/os/ip/ip.h	Tue Aug 17 16:28:42 2021
+++ b/os/ip/ip.h	Thu Aug 12 23:08:35 2021
@@ -25,6 +25,9 @@
 typedef struct 	V6router	V6router;
 typedef struct	V6params	V6params;
 
+typedef struct Ip4hdr     Ip4hdr;
+typedef struct Nat	Nat;
+
 #pragma incomplete Arp
 #pragma	incomplete Ifclog
 #pragma incomplete Ipself
@@ -38,7 +41,7 @@
 	Maxproto=	20,
 	Nhash=		64,
 	Maxincall=	5,
-	Nchans=		256,
+	Nchans=		16383,
 	MAClen=		16,		/* longest mac address */
 
 	MAXTTL=		255,
@@ -70,6 +73,22 @@
 	Connected=	4,
 };
 
+/* on the wire packet header */
+struct Ip4hdr
+{
+	uchar	vihl;		/* Version and header length */
+	uchar	tos;		/* Type of service */
+	uchar	length[2];	/* packet length */
+	uchar	id[2];		/* ip->identification */
+	uchar	frag[2];	/* Fragment information */
+	uchar	ttl;      	/* Time to live */
+	uchar	proto;		/* Protocol */
+	uchar	cksum[2];	/* Header checksum */
+	uchar	src[4];		/* IP source */
+	uchar	dst[4];		/* IP destination */
+	uchar	data[1];	/* start of data */
+};
+
 /*
  *  one per conversation directory
  */
@@ -390,6 +409,7 @@
 char*	Fsstdannounce(Conv*, char**, int);
 char*	Fsstdbind(Conv*, char**, int);
 ulong	scalednconv(void);
+void	closeconv(Conv*);
 
 /* 
  *  logging
@@ -414,6 +434,7 @@
 	Logrudpmsg=	1<<16,
 	Logesp=		1<<17,
 	Logtcpwin=	1<<18,
+	Lognat=		1<<19,
 };
 
 void	netloginit(Fs*);
@@ -522,6 +543,7 @@
 };
 
 extern IPaux*	newipaux(char*, char*);
+extern void	setlport(Conv*);
 
 /*
  *  arp.c
@@ -570,6 +592,9 @@
 
 #define	ipmove(x, y) memmove(x, y, IPaddrlen)
 #define	ipcmp(x, y) ( (x)[IPaddrlen-1] != (y)[IPaddrlen-1] || memcmp(x, y, IPaddrlen) )
+ 
+#define	ip4move(x, y) memmove(x, y, IPv4addrlen)
+#define	ip4cmp(x, y) ( (x)[IPv4addrlen-1] != (y)[IPv4addrlen-1] || memcmp(x, y, IPv4addrlen) )
 
 extern uchar IPv4bcast[IPaddrlen];
 extern uchar IPv4bcastobs[IPaddrlen];
@@ -670,3 +695,15 @@
  *  global to all of the stack
  */
 extern void	(*igmpreportfn)(Ipifc*, uchar*);
+
+/*
+ * nat.c
+ */
+extern int	nato(Block*, Ipifc*, Fs*);
+extern void	nati(Block*, Ipifc*);
+extern int	natgc(uchar);
+
+extern int	addnataddr(uchar*, uchar*, Iplifc*);
+extern int	removenataddr(uchar*, uchar*, Iplifc*);
+extern void	shownataddr(void);
+extern void flushnataddr(void);
--- a/os/ip/ipifc.c	Tue Aug 17 16:28:42 2021
+++ b/os/ip/ipifc.c	Thu Aug 12 23:08:35 2021
@@ -784,6 +784,50 @@
 	return nil;
 }
 
+char*
+ipifcnat(Ipifc *ifc, char **argv, int argc)
+{
+	uchar src[IPaddrlen], mask[IPaddrlen], dst[IPaddrlen];
+	Iplifc *lifc;
+
+	if(argc == 2){
+		if((strcmp(argv[1], "show") == 0)){
+			shownataddr();
+			return nil;
+		}else if((strcmp(argv[1], "flush") == 0)){
+			flushnataddr();
+			return nil;
+		}else
+			return Ebadarg;
+	}
+
+	if(argc != 5)
+		return Ebadarg;
+
+	if (parseip(src, argv[2]) == -1)
+		return Ebadip;
+
+	if (parseipmask(mask, argv[3]) == -1)
+		return Ebadip;
+
+	if (parseip(dst, argv[4]) == -1)
+		return Ebadip;
+
+	if((lifc=iplocalonifc(ifc, dst)) == nil)
+		return Ebadip;
+
+	if(strcmp(argv[1], "add") == 0){
+		if(addnataddr(src, mask, lifc) != 0)
+			return Ebadarg;
+	}else if(strcmp(argv[1], "remove") == 0){
+		if(removenataddr(src, mask, lifc) != 0)
+			return Ebadarg;
+	}else
+		return Ebadarg;
+
+	return nil;
+}
+
 /*
  *  non-standard control messages.
  *  called with c locked.
@@ -830,6 +874,8 @@
 		return ipifcsendra6(ifc, argv, argc);
 	else if(strcmp(argv[0], "recvra6") == 0)
 		return ipifcrecvra6(ifc, argv, argc);
+	else if(strcmp(argv[0], "nat") == 0)
+		return ipifcnat(ifc, argv, argc);
 	return "unsupported ctl";
 }
 
--- a/os/ip/ipmux.c	Tue Aug 17 16:28:42 2021
+++ b/os/ip/ipmux.c	Thu Aug 12 23:08:35 2021
@@ -10,27 +10,11 @@
 
 typedef struct Ipmuxrock  Ipmuxrock;
 typedef struct Ipmux      Ipmux;
-typedef struct Ip4hdr     Ip4hdr;
 typedef struct Ip6hdr     Ip6hdr;
 
 enum
 {
 	IPHDR		= 20,		/* sizeof(Ip4hdr) */
-};
-
-struct Ip4hdr
-{
-	uchar	vihl;		/* Version and header length */
-	uchar	tos;		/* Type of service */
-	uchar	length[2];	/* packet length */
-	uchar	id[2];		/* ip->identification */
-	uchar	frag[2];	/* Fragment information */
-	uchar	ttl;		/* Time to live */
-	uchar	proto;		/* Protocol */
-	uchar	cksum[2];	/* Header checksum */
-	uchar	src[4];		/* IP source */
-	uchar	dst[4];		/* IP destination */
-	uchar	data[1];	/* start of data */
 };
 
 struct Ip6hdr
--- /dev/null	Mon Oct 18 12:05:45 2021
+++ b/os/ip/nat.c	Thu Aug 12 23:08:35 2021
@@ -0,0 +1,549 @@
+#include		"u.h"
+#include		"../port/lib.h"
+#include		"mem.h"
+#include		"dat.h"
+#include		"fns.h"
+#include		"../port/error.h"
+
+#include		"ip.h"
+
+typedef struct NatProto NatProto;
+typedef struct NatAddr NatAddr;
+
+/*
+ * NAT.
+ */
+struct Nat
+{
+	uchar	src[IPv4addrlen];	/* Source address */
+	uchar	sport[2];		/* Source port */
+	uchar	lport[2];		/* Local port */
+	uchar	proto;			/* Protocol */
+	long	time;			/* Time */
+	Conv	*conv;			/* Conversation */
+	Nat	*next;			/* Next node */
+};
+
+/*
+ * Protocol list.
+ */
+struct NatProto
+{
+	uchar	proto;			/* Protocol */
+	int	sport;			/* Source port offset */
+	int	dport;			/* Destination port offset */
+	int	cksum;			/* Checksum offset */
+	int	timeout;		/* Timeout */
+};
+
+/*
+ * Address list.
+ */
+struct NatAddr
+{
+	uchar	src[IPaddrlen];		/* Source address */
+	uchar	mask[IPaddrlen];	/* Source address mask */
+	uchar	net[IPaddrlen];		/* Source network address */
+	Iplifc	*dst;			/* Destination interface */
+	NatAddr	*next;			/* Next node */
+};
+
+static Nat *head = nil;
+static NatAddr *addrhead = nil;
+
+/*
+ * Timeouts for ICMP, TCP and UDP are respectively confirmed
+ * in RFC 5508, RFC 5382 and RFC 4787.
+ */
+static NatProto prototab[] =
+{
+	{ 1, 4, 4, 2, 60*1000 },		/* ICMP */
+	{ 6, 0, 2, 16, (2*60*60+4*60)*1000 },	/* TCP */
+	{ 17, 0, 2, 6, 2*60*1000 },		/* UDP */
+	{ 40, 6, 8, 0, 10*30*1000 },		/* IL */
+	{ 255, 0, 2, 6, 2*60*1000 },		/* RUDP */
+	{ 0 }
+};
+
+NatProto*	parseproto(uchar);
+void		natprepend(Nat*);
+Nat*		natexistout(uchar*, uchar, uchar*);
+Nat*		natexistin(uchar, uchar*);
+int		natdelete(uchar*, uchar, uchar*);
+int		natpurge(uchar);
+Nat*		natlport(Proto*, Ip4hdr*, uchar*);
+int		natgc(uchar);
+void		checksumadjust(uchar*, uchar*, int, uchar*, int);
+Iplifc*		natonifco(Ipifc*, Ip4hdr*);
+Iplifc*		natonifci(Ipifc*);
+void		nataddrprepend(NatAddr*);
+NatAddr*	nataddrexist(uchar*, uchar*, Iplifc*);
+int		addnataddr(uchar*, uchar*, Iplifc*);
+int		removenataddr(uchar*, uchar*, Iplifc*);
+void		shownataddr(void);
+void		flushnataddr(void);
+
+/*
+ * Return protocol attributes if known.
+ */
+NatProto*
+parseproto(uchar proto)
+{
+	NatProto *np;
+
+	for(np = prototab; np->proto; np++)
+		if(proto == np->proto)
+			return np;
+
+	return nil;
+}
+
+/*
+ * Output NAT.
+ * Return -1 if the packet must be NATed but the protocol is unknown.
+ */
+int
+nato(Block *b, Ipifc *ifc, Fs *f)
+{
+	Nat *n;		/* NAT table */
+	NatProto *np;	/* Protocol list */
+	Iplifc *lifc;	/* Logical interface */
+	Ip4hdr *h;	/* Source IPv4 header */
+	Proto *p;	/* New protocol */
+	uchar *laddr;	/* Local address on Iplifc */
+	uchar *sport;	/* Source port */
+	uchar *cksum;	/* Source checksum */
+
+	h = (Ip4hdr*)(b->rp);
+
+	/* Verify on which logical interface NAT is enabled,
+           and if this source address must be translated */
+	if((lifc=natonifco(ifc, h)) == nil)
+		return 0;
+
+	laddr = lifc->local+IPv4off;
+	p = Fsrcvpcolx(f, h->proto);
+
+	if(ip4cmp(h->src, laddr) != 0){
+		if((np=parseproto(h->proto)) != nil){
+			/* Protocol layer */
+			sport = (b->rp)+sizeof(Ip4hdr)+np->sport;
+			cksum = (b->rp)+sizeof(Ip4hdr)+np->cksum;
+			if((n = natlport(p, h, sport)) == nil)
+				return -1;
+			memmove(sport, n->lport, 2);
+			checksumadjust(cksum, n->sport, 2, n->lport, 2);
+			if(np->proto != 1)
+				/* ICMP checksum doesn't include IP header */
+				checksumadjust(cksum, n->src, IPv4addrlen,
+					laddr, IPv4addrlen);
+			/* IP layer */
+			ip4move(h->src, laddr);
+			checksumadjust(h->cksum, n->src, IPv4addrlen,
+				h->src, IPv4addrlen);
+			return 0;
+		}else{
+			netlog(f, Lognat, "nat: unknown protocol %d\n", h->proto);
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * Input NAT.
+ */
+void
+nati(Block *b, Ipifc *ifc)
+{
+	Nat *n;		/* NAT table */
+	NatProto *np;	/* Protocol list */
+	Ip4hdr *h;	/* Source IPv4 header */
+	uchar *lport;	/* Our local port, and dst port for the packet */
+	uchar *cksum;	/* Source checksum */
+
+	h = (Ip4hdr*)(b->rp);
+
+	/* Verify if NAT is enabled on this interface */
+	if(natonifci(ifc) == nil)
+		return;
+
+	if((np=parseproto(h->proto)) != nil){
+		lport = (b->rp)+sizeof(Ip4hdr)+np->dport;
+		if((n=natexistin(h->proto, lport)) != nil){
+			/* Protocol layer */
+			cksum = (b->rp)+sizeof(Ip4hdr)+np->cksum;
+			checksumadjust(cksum, lport, 2, n->sport, 2);
+			memmove(lport, n->sport, 2);
+			if(np->proto != 1)
+				/* ICMP checksum doesn't include IP header */
+		   		checksumadjust(cksum, h->dst, IPv4addrlen,
+					n->src, IPv4addrlen);
+			/* IP layer */
+			checksumadjust(h->cksum, h->dst, IPv4addrlen,
+				n->src, IPv4addrlen);
+			ip4move(h->dst, n->src);
+		}
+	}
+}
+
+/*
+ * Add Nat to Nat list.
+ */
+void
+natprepend(Nat *n)
+{
+	n->next = head;
+	head = n;
+}
+
+/*
+ * Return Nat if it exists in Nat list.
+ */
+Nat*
+natexistout(uchar *src, uchar proto, uchar *sport)
+{
+	Nat *c;		/* Current node */
+
+	for(c=head; c!=nil; c=c->next)
+		if(ip4cmp(src, c->src) == 0 &&
+			memcmp(sport, c->sport, 2) == 0 &&
+			proto == c->proto){
+			c->time = NOW;
+			return c;
+		}
+
+	return nil;
+}
+
+/*
+ * Return Nat if it exists in Nat list.
+ */
+Nat*
+natexistin(uchar proto, uchar *lport)
+{
+	Nat *c;		/* Current node */
+
+	for(c=head; c!=nil; c=c->next)
+		if(memcmp(lport, c->lport, 2) == 0 &&
+			proto == c->proto){
+			c->time = NOW;
+			return c;
+		}
+
+	return nil;
+}
+
+/*
+ * Delete Nat in Nat list.
+ * Return -1 if it doesn't exist.
+ */
+int
+natdelete(uchar src[IPv4addrlen], uchar proto, uchar sport[2])
+{
+	Nat *p;		/* Precedent node */
+	Nat *c;		/* Current node */
+
+	for(p=nil, c=head; c!=nil; p=c, c=c->next)
+		if(ip4cmp(src, c->src) == 0 &&
+			memcmp(sport, c->sport, 2) == 0 &&
+			proto == c->proto)
+			break;
+
+	if(c == nil)
+		return -1;
+
+	if(p == nil)
+		head = head->next;
+	else
+		p->next = c->next;
+
+	closeconv(c->conv);
+	free(c);
+
+	return 0;
+}
+
+/*
+ * Purge Nat list.
+ */
+int
+natpurge(uchar proto)
+{
+	Nat *c;		/* Current node */
+	int n;		/* Number of purged connections */
+
+	for(n = 0;; n++){
+		do{
+			if((c = head) == nil)
+				return n;
+			head = head->next;
+		}while(c->proto != proto);
+		closeconv(c->conv);
+		free(c);
+	}
+}
+
+/*
+ * Create a new Nat if necessary.
+ */
+Nat*
+natlport(Proto *p, Ip4hdr *h, uchar *sport)
+{
+	Nat *n;		/* New NAT node */
+	Conv *s;	/* New conversation */
+
+	if((n=natexistout(h->src, h->proto, sport)) == nil){
+		qlock(p);
+		s = Fsprotoclone(p, "network");
+		qunlock(p);
+		if(s == nil){
+			error(Enodev);
+			return nil;
+		}
+		setlport(s);
+		n = malloc(sizeof(Nat));
+		ip4move(n->src, h->src);
+		memmove(n->sport, sport, 2);
+		memmove(n->lport, &s->lport, 2);
+		n->proto = h->proto;
+		n->time = NOW;
+		n->conv = s;
+		natprepend(n);
+	}
+
+	return n;
+}
+
+/*
+ * Nat list garbage collector.
+ */
+int
+natgc(uchar proto){
+	Nat *p;		/* Precedent node */
+	Nat *c;		/* Current node */
+	NatProto *np;	/* Protocol list */
+	int n;		/* Number of garbage collected connections */
+
+	n = 0;
+	p = nil;
+	c = head;
+
+	np = parseproto(proto);
+
+	while(c != nil){
+		if(NOW - c->time > np->timeout){
+ 			if(p == nil){
+ 				head = head->next;
+				if(proto == c->proto)
+					n++;
+				closeconv(c->conv);
+				free(c);
+				p = nil;
+				c = head;
+ 			}else{
+ 				p->next = c->next;
+				if(proto == c->proto)
+					n++;
+				closeconv(c->conv);
+				free(c);
+ 				c = p->next;
+ 			}
+		}else{
+			p = c;
+			c = c->next;
+		}
+	}
+
+	if(n == 0)	/* Prevent Conv saturation */
+		n = natpurge(proto);
+
+	return n;
+}
+
+/*
+ * Function checksumadjust from RFC 3022.
+ */
+void
+checksumadjust(uchar *chksum, uchar *optr, int olen, uchar *nptr, int nlen)
+{
+	long x, old, new;
+
+	x=chksum[0]*256+chksum[1];
+	x=~x & 0xffff;
+	while(olen){
+		old=optr[0]*256+optr[1];
+		optr+=2;
+		x-=old & 0xffff;
+		if(x<=0){
+			x--;
+			x&=0xffff;
+		}
+		olen-=2;
+	}
+	while(nlen){
+		new=nptr[0]*256+nptr[1];
+		nptr+=2;
+		x+=new & 0xffff;
+		if(x & 0x10000){
+			x++;
+			x&=0xffff;
+		}
+		nlen-=2;
+	}
+	x=~x & 0xffff;
+	chksum[0]=x/256;
+	chksum[1]=x & 0xff;
+}
+
+/*
+ * Add NatAddr to NatAddr list.
+ */
+void
+nataddrprepend(NatAddr *na)
+{
+	na->next = addrhead;
+	addrhead = na;
+}
+
+/*
+ * Return NatAddr if it exists in NatAddr list.
+ */
+NatAddr*
+nataddrexist(uchar *src, uchar *mask, Iplifc *dst)
+{
+	NatAddr *c;	/* Current node */
+
+	for(c=addrhead; c!=nil; c=c->next)
+		if(ipcmp(src, c->src) == 0 &&
+			ipcmp(mask, c->mask) == 0 &&
+			dst == c->dst)
+			return c;
+
+	return nil;
+}
+
+/*
+ * Create a new NatAddr.
+ * Return -1 if it already exist.
+ */
+int
+addnataddr(uchar *src, uchar *mask, Iplifc *dst)
+{
+	NatAddr *na;		/* New address node */
+	uchar net[IPaddrlen];	/* Network address */
+
+	maskip(src, mask, net);
+
+	if(nataddrexist(src, mask, dst) != nil)
+		return -1;
+
+	na = malloc(sizeof(NatAddr));
+	ipmove(na->src, src);
+	ipmove(na->mask, mask);
+	ipmove(na->net, net);
+	na->dst = dst;
+
+	nataddrprepend(na);
+
+	return 0;
+}
+
+/*
+ * Remove a NatAddr.
+ * Return -1 if it doesn't exist.
+ */
+int
+removenataddr(uchar *src, uchar *mask, Iplifc *dst)
+{
+	NatAddr *c;	/* Current node */
+	NatAddr *p;	/* Precedent node */
+
+	for(p=nil, c=addrhead; c!=nil; p=c, c=c->next)
+		if(ipcmp(src, c->src) == 0 &&
+			ipcmp(mask, c->mask) == 0 &&
+			dst == c->dst)
+			break;
+
+	if(c == nil)
+		return -1;
+
+	if(p == nil)
+		addrhead = addrhead->next;
+	else
+		p->next = c->next;
+
+	return 0;
+}
+
+/*
+ * Display NatAddr list.
+ */
+void
+shownataddr(void)
+{
+	NatAddr *c;	/* Current node */
+
+	for(c=addrhead; c!=nil; c=c->next)
+		print("%I %V %I\n", c->src, c->mask+IPv4off, c->dst->local);
+}
+
+/*
+ * Flush NatAddr list.
+ */
+void
+flushnataddr(void)
+{
+	NatAddr *c;	/* Current node */
+
+	while((c=addrhead) != nil){
+		addrhead = addrhead->next;
+		free(c);
+	}
+}
+
+/*
+ * Return logical interface if NAT is enabled on this interface,
+ * and the source address must be translated.
+ */
+Iplifc*
+natonifco(Ipifc *ifc, Ip4hdr* h)
+{
+	NatAddr *na;		/* Address list */
+	Iplifc *lifc;		/* Logical interface */
+	uchar src[IPaddrlen];	/* Source address */
+	uchar net[IPaddrlen];	/* Source network address */
+
+	for(lifc=ifc->lifc; lifc!=nil; lifc=lifc->next)
+		for(na=addrhead; na; na=na->next)
+			if(lifc == na->dst){
+				/* NAT enabled on this logical interface */
+				v4tov6(src, h->src);
+				maskip(src, na->mask, net);
+				if(ipcmp(net, na->net) == 0)
+					/* Source address must be translated */
+					return lifc;
+			}
+
+	return nil;
+}
+
+/*
+ * Return logical interface if NAT is enabled on this interface.
+ */
+Iplifc*
+natonifci(Ipifc *ifc)
+{
+	NatAddr *na;		/* Address list */
+	Iplifc *lifc;		/* Logical interface */
+
+	for(lifc=ifc->lifc; lifc!=nil; lifc=lifc->next)
+		for(na=addrhead; na; na=na->next)
+			if(lifc == na->dst){
+				/* NAT enabled on this logical interface */
+				return lifc;
+			}
+
+	return nil;
+}
--- a/os/ip/rudp.c	Tue Aug 17 16:28:42 2021
+++ b/os/ip/rudp.c	Thu Aug 12 23:08:35 2021
@@ -701,6 +701,12 @@
 		upriv->orders);
 }
 
+int
+rudpgc(Proto *rudp)
+{
+	return natgc(rudp->ipproto);
+}
+
 void
 rudpinit(Fs *fs)
 {
@@ -719,6 +725,7 @@
 	rudp->rcv = rudpiput;
 	rudp->advise = rudpadvise;
 	rudp->stats = rudpstats;
+	rudp->gc = rudpgc;
 	rudp->ipproto = IP_UDPPROTO;
 	rudp->nc = 16;
 	rudp->ptclsize = sizeof(Rudpcb);
--- a/os/ip/tcp.c	Tue Aug 17 16:28:42 2021
+++ b/os/ip/tcp.c	Thu Aug 12 23:08:35 2021
@@ -3104,7 +3104,7 @@
 	Tcpctl *tcb;
 
 
-	n = 0;
+	n = natgc(tcp->ipproto);
 	ep = &tcp->conv[tcp->nc];
 	for(pp = tcp->conv; pp < ep; pp++) {
 		c = *pp;
--- a/os/ip/udp.c	Tue Aug 17 16:28:42 2021
+++ b/os/ip/udp.c	Thu Aug 12 23:08:35 2021
@@ -624,6 +624,12 @@
 		upriv->ustats.udpOutDatagrams);
 }
 
+int
+udpgc(Proto *udp)
+{
+	return natgc(udp->ipproto);
+}
+
 void
 udpinit(Fs *fs)
 {
@@ -641,6 +647,7 @@
 	udp->rcv = udpiput;
 	udp->advise = udpadvise;
 	udp->stats = udpstats;
+	udp->gc = udpgc;
 	udp->ipproto = IP_UDPPROTO;
 	udp->nc = Nchans;
 	udp->ptclsize = sizeof(Udpcb);
--- a/os/pc64/pc64	Tue Aug 17 16:28:42 2021
+++ b/os/pc64/pc64	Thu Aug 12 23:08:35 2021
@@ -15,7 +15,7 @@
 
 	ether		netif netaux ethermedium
 #	bridge		netif log
-	ip	bootp ip ipv6 ipaux iproute arp netlog ptclbsum iprouter plan9 nullmedium pktmedium
+	ip	bootp ip ipv6 ipaux iproute arp netlog ptclbsum iprouter plan9 nullmedium pktmedium nat
 
 	draw	screen vga vgax cga
 #	mouse		mouse