diff options
author | cinap_lenrek <cinap_lenrek@felloff.net> | 2022-03-12 20:53:17 +0000 |
---|---|---|
committer | cinap_lenrek <cinap_lenrek@felloff.net> | 2022-03-12 20:53:17 +0000 |
commit | d2a7d886624c56673a6d7ba7d6a7958d2be5b867 (patch) | |
tree | 483c16a36a4fcb97f66708a0d11f1e43f6fcbddf /sys/src/9/ip/udp.c | |
parent | c14ea9fdd1521ff9322f9af71b801e016622c0cd (diff) |
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.
Diffstat (limited to 'sys/src/9/ip/udp.c')
-rw-r--r-- | sys/src/9/ip/udp.c | 126 |
1 files changed, 94 insertions, 32 deletions
diff --git a/sys/src/9/ip/udp.c b/sys/src/9/ip/udp.c index 5800c7e8c..651e616df 100644 --- a/sys/src/9/ip/udp.c +++ b/sys/src/9/ip/udp.c @@ -39,7 +39,7 @@ struct Udp4hdr uchar length[2]; /* packet length */ uchar id[2]; /* Identification */ uchar frag[2]; /* Fragment information */ - uchar Unused; + uchar ttl; /* Time to live */ uchar udpproto; /* Protocol */ uchar udpplen[2]; /* Header plus data length */ uchar udpsrc[IPv4addrlen]; /* Ip source */ @@ -91,7 +91,6 @@ struct Udppriv ulong lenerr; /* short packet */ }; -void (*etherprofiler)(char *name, int qlen); void udpkick(void *x, Block *bp); /* @@ -114,7 +113,6 @@ udpconnect(Conv *c, char **argv, int argc) Fsconnected(c, e); if(e != nil) return e; - iphtadd(&upriv->ht, c); return nil; } @@ -142,7 +140,6 @@ udpannounce(Conv *c, char** argv, int argc) return e; Fsconnected(c, nil); iphtadd(&upriv->ht, c); - return nil; } @@ -166,10 +163,10 @@ udpclose(Conv *c) qclose(c->rq); qclose(c->wq); qclose(c->eq); - ipmove(c->laddr, IPnoaddr); - ipmove(c->raddr, IPnoaddr); c->lport = 0; + ipmove(c->laddr, IPnoaddr); c->rport = 0; + ipmove(c->raddr, IPnoaddr); ucb = (Udpcb*)c->ptcl; ucb->headers = 0; @@ -238,7 +235,7 @@ udpkick(void *x, Block *bp) bp = padblock(bp, UDP4_IPHDR_SZ+UDP_UDPHDR_SZ); uh4 = (Udp4hdr *)(bp->rp); ptcllen = dlen + UDP_UDPHDR_SZ; - uh4->Unused = 0; + uh4->ttl = 0; uh4->udpproto = IP_UDPPROTO; uh4->frag[0] = 0; uh4->frag[1] = 0; @@ -319,6 +316,7 @@ udpiput(Proto *udp, Ipifc *ifc, Block *bp) int len; Udp4hdr *uh4; Udp6hdr *uh6; + Iphash *iph; Conv *c; Udpcb *ucb; uchar raddr[IPaddrlen], laddr[IPaddrlen]; @@ -334,14 +332,15 @@ udpiput(Proto *udp, Ipifc *ifc, Block *bp) upriv->ustats.udpInDatagrams++; uh4 = (Udp4hdr*)(bp->rp); + uh6 = (Udp6hdr*)(bp->rp); version = ((uh4->vihl&0xF0)==IP_VER6) ? V6 : V4; /* Put back pseudo header for checksum * (remember old values for icmpnoconv()) */ switch(version) { case V4: - ottl = uh4->Unused; - uh4->Unused = 0; + ottl = uh4->ttl; + uh4->ttl = 0; len = nhgets(uh4->udplen); olen = nhgets(uh4->udpplen); hnputs(uh4->udpplen, len); @@ -360,11 +359,10 @@ udpiput(Proto *udp, Ipifc *ifc, Block *bp) return; } } - uh4->Unused = ottl; + uh4->ttl = ottl; hnputs(uh4->udpplen, olen); break; case V6: - uh6 = (Udp6hdr*)(bp->rp); len = nhgets(uh6->udplen); oviclfl = nhgetl(uh6->viclfl); olen = nhgets(uh6->len); @@ -394,9 +392,8 @@ udpiput(Proto *udp, Ipifc *ifc, Block *bp) } qlock(udp); - - c = iphtlook(&upriv->ht, raddr, rport, laddr, lport); - if(c == nil){ + iph = iphtlook(&upriv->ht, raddr, rport, laddr, lport); + if(iph == nil){ /* no conversation found */ upriv->ustats.udpNoPorts++; qunlock(udp); @@ -417,6 +414,26 @@ udpiput(Proto *udp, Ipifc *ifc, Block *bp) freeblist(bp); return; } + if(iph->trans){ + Translation *q; + int hop = uh4->ttl; + if(hop <= 1 || (q = transbackward(udp, iph)) == nil){ + qunlock(udp); + freeblist(bp); + return; + } + hnputs_csum(uh4->udpdst+0, nhgets(q->forward.raddr+IPv4off+0), uh4->udpcksum); + hnputs_csum(uh4->udpdst+2, nhgets(q->forward.raddr+IPv4off+2), uh4->udpcksum); + hnputs_csum(uh4->udpdport, q->forward.rport, uh4->udpcksum); + + /* only use route-hint when from original desination */ + if(memcmp(uh4->udpsrc, q->forward.laddr+IPv4off, IPv4addrlen) != 0) + q = nil; + qunlock(udp); + ipoput4(f, bp, 1, hop - 1, uh4->tos, q); + return; + } + c = iphconv(iph); ucb = (Udpcb*)c->ptcl; if(c->state == Announced){ @@ -487,7 +504,6 @@ udpiput(Proto *udp, Ipifc *ifc, Block *bp) qpass(c->rq, concatblock(bp)); } qunlock(c); - } char* @@ -517,7 +533,8 @@ udpadvise(Proto *udp, Block *bp, char *msg) Udp6hdr *h6; uchar source[IPaddrlen], dest[IPaddrlen]; ushort psource, pdest; - Conv *s, **p; + Iphash *iph; + Conv *s; h4 = (Udp4hdr*)(bp->rp); h6 = (Udp6hdr*)(bp->rp); @@ -534,26 +551,70 @@ udpadvise(Proto *udp, Block *bp, char *msg) pdest = nhgets(h6->udpdport); } - /* Look for a connection */ + /* Look for a connection (source/dest reversed; this is the original packet we sent) */ qlock(udp); - for(p = udp->conv; (s = *p) != nil; p++) { - if(s->rport == pdest) - if(s->lport == psource) - if(ipcmp(s->raddr, dest) == 0) - if(ipcmp(s->laddr, source) == 0){ - if(s->ignoreadvice) - break; - qlock(s); - qunlock(udp); - qhangup(s->rq, msg); - qhangup(s->wq, msg); - qunlock(s); - freeblist(bp); - return; - } + iph = iphtlook(&((Udppriv*)udp->priv)->ht, dest, pdest, source, psource); + if(iph == nil) + goto raise; + if(iph->trans){ + Translation *q; + + if((q = transbackward(udp, iph)) == nil) + goto raise; + + /* h4->udpplen is the ip header checksum */ + hnputs_csum(h4->udpsrc+0, nhgets(q->forward.raddr+IPv4off+0), h4->udpplen); + hnputs_csum(h4->udpsrc+2, nhgets(q->forward.raddr+IPv4off+2), h4->udpplen); + + /* dont bother fixing udp checksum, packet is most likely truncated */ + hnputs(h4->udpsport, q->forward.rport); + qunlock(udp); + + icmpproxyadvice(udp->f, bp, h4->udpsrc); + return; } + s = iphconv(iph); + if(s->ignoreadvice) + goto raise; + qlock(s); qunlock(udp); + qhangup(s->rq, msg); + qhangup(s->wq, msg); + qunlock(s); freeblist(bp); + return; +raise: + qunlock(udp); + freeblist(bp); +} + +Block* +udpforward(Proto *udp, Block *bp, Route *r) +{ + uchar da[IPaddrlen], sa[IPaddrlen]; + ushort dp, sp; + Udp4hdr *uh4; + Translation *q; + + uh4 = (Udp4hdr*)(bp->rp); + v4tov6(sa, uh4->udpsrc); + v4tov6(da, uh4->udpdst); + dp = nhgets(uh4->udpdport); + sp = nhgets(uh4->udpsport); + + qlock(udp); + q = transforward(udp, &((Udppriv*)udp->priv)->ht, sa, sp, da, dp, r); + if(q == nil){ + qunlock(udp); + freeblist(bp); + return nil; + } + hnputs_csum(uh4->udpsrc+0, nhgets(q->backward.laddr+IPv4off+0), uh4->udpcksum); + hnputs_csum(uh4->udpsrc+2, nhgets(q->backward.laddr+IPv4off+2), uh4->udpcksum); + hnputs_csum(uh4->udpsport, q->backward.lport, uh4->udpcksum); + qunlock(udp); + + return bp; } int @@ -586,6 +647,7 @@ udpinit(Fs *fs) udp->close = udpclose; udp->rcv = udpiput; udp->advise = udpadvise; + udp->forward = udpforward; udp->stats = udpstats; udp->ipproto = IP_UDPPROTO; udp->nc = Nchans; |