From d2a7d886624c56673a6d7ba7d6a7958d2be5b867 Mon Sep 17 00:00:00 2001 From: cinap_lenrek Date: Sat, 12 Mar 2022 20:53:17 +0000 Subject: devip: implement network address translation routes This adds a new route "t"-flag that enables network address translation, replacing the source address (and local port) of a forwarded packet to one of the outgoing interface. The state for a translation is kept in a new Translation structure, which contains two Iphash entries, so it can be inserted into the per protocol 4-tuple hash table, requiering no extra lookups. Translations have a low overhead (~200 bytes on amd64), so we can have many of them. They get reused after 5 minutes of inactivity or when the per protocol limit of 1000 entries is reached (then the one with longest inactivity is reused). The protocol needs to export a "forward" function that is responsible for modifying the forwarded packet, and then handle translations in its input function for iphash hits with Iphash.trans != 0. This patch also fixes a few minor things found during development: - Include the Iphash in the Conv structure, avoiding estra malloc - Fix ttl exceeded check (ttl < 1 -> ttl <= 1) - Router should not reply with ttl exceeded for multicast flows - Extra checks for icmp advice to avoid protocol confusions. --- sys/src/9/ip/devip.c | 70 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 50 insertions(+), 20 deletions(-) (limited to 'sys/src/9/ip/devip.c') diff --git a/sys/src/9/ip/devip.c b/sys/src/9/ip/devip.c index 403faa083..cb6be1d1c 100644 --- a/sys/src/9/ip/devip.c +++ b/sys/src/9/ip/devip.c @@ -737,6 +737,7 @@ setladdr(Conv* c) char* setluniqueport(Conv* c, int lport) { + Translation *q; Proto *p; Conv *xp; int x; @@ -754,14 +755,22 @@ setluniqueport(Conv* c, int lport) && xp->lport == lport && xp->rport == c->rport && ipcmp(xp->raddr, c->raddr) == 0 - && ipcmp(xp->laddr, c->laddr) == 0){ - qunlock(p); - return "address in use"; - } + && ipcmp(xp->laddr, c->laddr) == 0) + goto Inuse; + } + for(q = p->translations; q != nil; q = q->next){ + if(q->backward.lport == lport + && q->backward.rport == c->rport + && ipcmp(q->backward.raddr, c->raddr) == 0 + && ipcmp(q->backward.laddr, c->laddr) == 0) + goto Inuse; } c->lport = lport; qunlock(p); return nil; +Inuse: + qunlock(p); + return "address in use"; } /* @@ -770,18 +779,51 @@ setluniqueport(Conv* c, int lport) static int lportinuse(Proto *p, ushort lport) { + Translation *q; int x; for(x = 0; x < p->nc && p->conv[x]; x++) if(p->conv[x]->lport == lport) return 1; + for(q = p->translations; q != nil; q = q->next) + if(q->backward.lport == lport) + return 1; return 0; } +/* + * find a unused loacal port for a protocol. + * + * p needs to be locked + */ +int +unusedlport(Proto *p) +{ + ushort port; + int i; + + /* + * Unrestricted ports are chosen randomly + * between 2^15 and 2^16. There are at most + * 4*Nchan = 4096 ports in use at any given time, + * so even in the worst case, a random probe has a + * 1 - 4096/2^15 = 87% chance of success. + * If 64 successive probes fail, there is a bug somewhere + * (or a once in 10^58 event has happened, but that's + * less likely than a venti collision). + */ + for(i=0; i<64; i++){ + port = (1<<15) + nrand(1<<15); + if(!lportinuse(p, port)) + return port; + } + return -1; +} + /* * pick a local port and set it */ -char * +static char * setlport(Conv* c) { Proto *p; @@ -799,21 +841,9 @@ setlport(Conv* c) goto chosen; } }else{ - /* - * Unrestricted ports are chosen randomly - * between 2^15 and 2^16. There are at most - * 4*Nchan = 4096 ports in use at any given time, - * so even in the worst case, a random probe has a - * 1 - 4096/2^15 = 87% chance of success. - * If 64 successive probes fail, there is a bug somewhere - * (or a once in 10^58 event has happened, but that's - * less likely than a venti collision). - */ - for(i=0; i<64; i++){ - port = (1<<15) + nrand(1<<15); - if(!lportinuse(p, port)) - goto chosen; - } + port = unusedlport(p); + if(port > 0) + goto chosen; } qunlock(p); return "no ports available"; -- cgit v1.2.3