--- /n/sources/plan9/sys/src/9/ip/icmp.c Mon Jan 11 21:25:07 2010
+++ /sys/src/9/ip/icmp.c Sun Jul 31 00:00:00 2011
@@ -465,6 +465,12 @@
}
return p - buf;
}
+
+int
+icmpgc(Proto *icmp)
+{
+ return natgc(icmp->ipproto);
+}
void
icmpinit(Fs *fs)
@@ -483,9 +489,9 @@
icmp->stats = icmpstats;
icmp->ctl = nil;
icmp->advise = icmpadvise;
- icmp->gc = nil;
+ icmp->gc = icmpgc;
icmp->ipproto = IP_ICMPPROTO;
- icmp->nc = 128;
+ icmp->nc = Nchans;
icmp->ptclsize = 0;
Fsproto(fs, icmp);
--- /sys/src/9/ip/il.c Sun Jul 30 00:00:00 2011
+++ /sys/src/9/ip/il.c Sun Jul 31 00:00:00 2011
@@ -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);
--- /n/sources/plan9/sys/src/9/ip/ip.c Fri May 6 19:31:29 2011
+++ /sys/src/9/ip/ip.c Sun Jul 31 00:00:00 2011
@@ -192,6 +192,10 @@
if(ifc->m == nil)
goto raise;
+ /* Ouput NAT */
+ if(nato(bp, ifc, f) != 0)
+ goto raise;
+
/* If we dont need to fragment just send it */
if(c && c->maxfragsize && c->maxfragsize < ifc->maxtu)
medialen = c->maxfragsize - ifc->m->hsize;
@@ -344,6 +348,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)) {
--- /n/sources/plan9/sys/src/9/ip/ip.h Wed Nov 25 04:19:33 2009
+++ /sys/src/9/ip/ip.h Sun Jul 31 00:00:00 2011
@@ -27,6 +27,8 @@
typedef struct v6router v6router;
typedef struct v6params v6params;
+typedef struct Nat Nat;
+
#pragma incomplete Arp
#pragma incomplete Ipself
#pragma incomplete Ipselftab
@@ -39,7 +41,7 @@
Maxproto= 20,
Nhash= 64,
Maxincall= 10,
- Nchans= 1024,
+ Nchans= 16383,
MAClen= 16, /* longest mac address */
MAXTTL= 255,
@@ -501,6 +503,7 @@
Logrudpmsg= 1<<16,
Logesp= 1<<17,
Logtcpwin= 1<<18,
+ Lognat= 1<<19,
};
void netloginit(Fs*);
@@ -611,6 +614,7 @@
};
extern IPaux* newipaux(char*, char*);
+extern char* setlport(Conv*);
/*
* arp.c
@@ -660,6 +664,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];
extern uchar IPv4allsys[IPaddrlen];
@@ -744,3 +751,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);
--- /n/sources/plan9/sys/src/9/ip/ipifc.c Wed Aug 4 19:58:07 2010
+++ /sys/src/9/ip/ipifc.c Sun Jul 31 00:00:00 2011
@@ -747,6 +747,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->car locked.
@@ -787,6 +831,8 @@
return ipifcadd6(ifc, argv, argc);
else if(strcmp(argv[0], "ra6") == 0)
return ipifcra6(ifc, argv, argc);
+ else if(strcmp(argv[0], "nat") == 0)
+ return ipifcnat(ifc, argv, argc);
return "unsupported ctl";
}
--- /sys/src/9/ip/nat.c Thu Jan 1 00:00:00 1970
+++ /sys/src/9/ip/nat.c Sun Jul 31 00:00:00 2011
@@ -0,0 +1,550 @@
+#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;
+ }
+ if(setlport(s) == nil){
+ 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);
+ }else
+ return nil;
+ }
+
+ 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;
+}
--- /n/sources/plan9/sys/src/9/ip/rudp.c Thu Jul 9 03:34:23 2009
+++ /sys/src/9/ip/rudp.c Sun Jul 31 00:00:00 2011
@@ -671,6 +671,12 @@
upriv->orders);
}
+int
+rudpgc(Proto *rudp)
+{
+ return natgc(rudp->ipproto);
+}
+
void
rudpinit(Fs *fs)
{
@@ -689,6 +695,7 @@
rudp->rcv = rudpiput;
rudp->advise = rudpadvise;
rudp->stats = rudpstats;
+ rudp->gc = rudpgc;
rudp->ipproto = IP_UDPPROTO;
rudp->nc = 32;
rudp->ptclsize = sizeof(Rudpcb);
--- /n/sources/plan9/sys/src/9/ip/tcp.c Fri Mar 18 18:24:20 2011
+++ /sys/src/9/ip/tcp.c Sun Jul 31 00:00:00 2011
@@ -3146,7 +3146,7 @@
Tcpctl *tcb;
- n = 0;
+ n = natgc(tcp->ipproto);
ep = &tcp->conv[tcp->nc];
for(pp = tcp->conv; pp < ep; pp++) {
c = *pp;
--- /n/sources/plan9/sys/src/9/ip/udp.c Wed Nov 25 04:16:56 2009
+++ /sys/src/9/ip/udp.c Sun Jul 31 00:00:00 2011
@@ -595,6 +595,12 @@
upriv->ustats.udpOutDatagrams);
}
+int
+udpgc(Proto *udp)
+{
+ return natgc(udp->ipproto);
+}
+
void
udpinit(Fs *fs)
{
@@ -612,6 +618,7 @@
udp->rcv = udpiput;
udp->advise = udpadvise;
udp->stats = udpstats;
+ udp->gc = udpgc;
udp->ipproto = IP_UDPPROTO;
udp->nc = Nchans;
udp->ptclsize = sizeof(Udpcb);
|