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.
This commit is contained in:
cinap_lenrek 2022-03-12 20:53:17 +00:00
parent c14ea9fdd1
commit d2a7d88662
15 changed files with 890 additions and 356 deletions

View file

@ -411,6 +411,9 @@ multicast route
.TP
.B p
point-to-point route
.TP
.B t
network address translation on source
.PD
.PP
The tag is an arbitrary, up to 4 character, string. It is normally used to
@ -442,7 +445,7 @@ with all subsequent routes added via this file descriptor.
.TP
.BI add\ "target mask nexthop tag interface source smask"
.TP
.BI add\ "target mask nexthop type tag interface source smask"
.BI add\ "target mask nexthop flags tag interface source smask"
Add the route to the table. If one already exists with the
same target and mask, replace it. The
.I interface
@ -461,7 +464,7 @@ IP address on the desired interface.
.TP
.BI remove\ "target mask nexthop tag interface source smask"
.TP
.BI remove\ "target mask nexthop type tag interface source smask"
.BI remove\ "target mask nexthop flags tag interface source smask"
Remove the matching route.
.
.SS "Address resolution

View file

@ -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";

View file

@ -99,6 +99,8 @@ struct Icmppriv
/* message counts */
ulong in[Maxtype+1];
ulong out[Maxtype+1];
Ipht ht;
};
static void icmpkick(void *x, Block*);
@ -192,9 +194,9 @@ ip4reply(Fs *f, uchar ip4[4])
uchar addr[IPaddrlen];
int i;
v4tov6(addr, ip4);
if(ipismulticast(addr))
if(isv4mcast(ip4))
return 0;
v4tov6(addr, ip4);
i = ipforme(f, addr);
return i == 0 || i == Runi;
}
@ -204,9 +206,9 @@ ip4me(Fs *f, uchar ip4[4])
{
uchar addr[IPaddrlen];
v4tov6(addr, ip4);
if(ipismulticast(addr))
if(isv4mcast(ip4))
return 0;
v4tov6(addr, ip4);
return ipforme(f, addr) == Runi;
}
@ -218,7 +220,7 @@ icmpttlexceeded(Fs *f, Ipifc *ifc, Block *bp)
uchar ia[IPv4addrlen];
p = (Icmp *)bp->rp;
if(!ip4reply(f, p->src) || !ipv4local(ifc, ia, 0, p->src))
if(isv4mcast(p->dst) || !ip4reply(f, p->src) || !ipv4local(ifc, ia, 0, p->src))
return;
netlog(f, Logicmp, "sending icmpttlexceeded %V -> src %V dst %V\n",
@ -249,7 +251,7 @@ icmpunreachable(Fs *f, Ipifc *ifc, Block *bp, int code, int seq)
uchar ia[IPv4addrlen];
p = (Icmp *)bp->rp;
if(!ip4reply(f, p->src))
if(isv4mcast(p->dst) || !ip4reply(f, p->src))
return;
if(ifc == nil){
@ -302,21 +304,43 @@ icmpcantfrag(Fs *f, Block *bp, int mtu)
static void
goticmpkt(Proto *icmp, Block *bp)
{
ushort recid;
uchar dst[IPaddrlen], src[IPaddrlen];
ushort recid;
Conv **c, *s;
Iphash *iph;
Icmp *p;
p = (Icmp *) bp->rp;
p = (Icmp *)bp->rp;
v4tov6(dst, p->dst);
v4tov6(src, p->src);
recid = nhgets(p->icmpid);
qlock(icmp);
iph = iphtlook(&((Icmppriv*)icmp->priv)->ht, src, recid, dst, recid);
if(iph != nil){
Translation *q;
int hop = p->ttl;
if(hop <= 1 || (q = transbackward(icmp, iph)) == nil)
goto raise;
memmove(p->dst, q->forward.raddr+IPv4off, IPv4addrlen);
hnputs_csum(p->icmpid, q->forward.rport, p->cksum);
/* only use route-hint when from original desination */
if(memcmp(p->src, q->forward.laddr+IPv4off, IPv4addrlen) != 0)
q = nil;
qunlock(icmp);
ipoput4(icmp->f, bp, 1, hop - 1, p->tos, q);
return;
}
for(c = icmp->conv; (s = *c) != nil; c++){
if(s->lport == recid)
if(ipcmp(s->laddr, dst) == 0 || ipcmp(s->raddr, src) == 0)
qpass(s->rq, copyblock(bp, blocklen(bp)));
}
raise:
qunlock(icmp);
freeblist(bp);
}
@ -404,31 +428,6 @@ icmpiput(Proto *icmp, Ipifc*, Block *bp)
ipriv->out[EchoReply]++;
ipoput4(icmp->f, r, 0, MAXTTL, DFLTTOS, nil);
break;
case Unreachable:
if(p->code >= nelem(unreachcode)) {
snprint(m2, sizeof m2, "unreachable %V -> %V code %d",
p->src, p->dst, p->code);
msg = m2;
} else
msg = unreachcode[p->code];
Advise:
bp->rp += ICMP_IPSIZE+ICMP_HDRSIZE;
if(BLEN(bp) < MinAdvise){
ipriv->stats[LenErrs]++;
goto raise;
}
p = (Icmp *)bp->rp;
if((nhgets(p->frag) & IP_FO) == 0){
pr = Fsrcvpcolx(icmp->f, p->proto);
if(pr != nil && pr->advise != nil) {
(*pr->advise)(pr, bp, msg);
return;
}
}
bp->rp -= ICMP_IPSIZE+ICMP_HDRSIZE;
goticmpkt(icmp, bp);
break;
case TimeExceed:
if(p->code == 0){
snprint(msg = m2, sizeof m2, "ttl exceeded at %V", p->src);
@ -436,29 +435,117 @@ icmpiput(Proto *icmp, Ipifc*, Block *bp)
}
goticmpkt(icmp, bp);
break;
case Unreachable:
if(p->code >= nelem(unreachcode)) {
snprint(m2, sizeof m2, "unreachable %V -> %V code %d",
p->src, p->dst, p->code);
msg = m2;
} else
msg = unreachcode[p->code];
Advise:
bp->rp += ICMP_IPSIZE+ICMP_HDRSIZE;
if(BLEN(bp) < MinAdvise){
ipriv->stats[LenErrs]++;
goto raise;
}
p = (Icmp *)bp->rp;
if(p->vihl == (IP_VER4|IP_HLEN4) /* advise() does not expect options */
&& (nhgets(p->frag) & IP_FO) == 0 /* first fragment */
&& ipcsum(&p->vihl) == 0){
pr = Fsrcvpcolx(icmp->f, p->proto);
if(pr != nil && pr->advise != nil) {
netlog(icmp->f, Logicmp, "advising %s!%V -> %V: %s\n", pr->name, p->src, p->dst, msg);
(*pr->advise)(pr, bp, msg);
return;
}
}
bp->rp -= ICMP_IPSIZE+ICMP_HDRSIZE;
/* wet floor */
default:
goticmpkt(icmp, bp);
break;
}
return;
raise:
freeblist(bp);
}
/*
* called from protocol advice handlers when the advice
* is actually for someone we source translate (ip4).
* the caller has fixed up the ip address and ports
* in the inner header, so we just restore the outer
* ip/icmp headers, recalculating icmp checksum
* and send the advice to ip4.
*/
void
icmpproxyadvice(Fs *f, Block *bp, uchar *ip4)
{
Icmp *p;
int hop;
/* inner header */
p = (Icmp *) bp->rp;
if(p->vihl != (IP_VER4|IP_HLEN4))
goto drop;
if(ipcsum(&p->vihl) != 0)
goto drop;
/* outer header */
bp->rp -= ICMP_IPSIZE+ICMP_HDRSIZE;
p = (Icmp *) bp->rp;
if(p->vihl != (IP_VER4|(ICMP_IPSIZE>>2)))
goto drop;
hop = p->ttl;
if(hop <= 1)
goto drop;
netlog(f, Logicmp|Logtrans, "proxying icmp advice from %V to %V->%V\n",
p->src, p->dst, ip4);
memmove(p->dst, ip4, IPv4addrlen);
/* recalculate ICMP checksum */
memset(p->cksum, 0, sizeof(p->cksum));
hnputs(p->cksum, ptclcsum(bp, ICMP_IPSIZE, blocklen(bp) - ICMP_IPSIZE));
ipoput4(f, bp, 1, hop - 1, p->tos, nil);
return;
drop:
freeblist(bp);
}
static void
icmpadvise(Proto *icmp, Block *bp, char *msg)
{
ushort recid;
uchar dst[IPaddrlen], src[IPaddrlen];
ushort recid;
Conv **c, *s;
Icmp *p;
Iphash *iph;
p = (Icmp *) bp->rp;
v4tov6(dst, p->dst);
v4tov6(src, p->src);
recid = nhgets(p->icmpid);
qlock(icmp);
iph = iphtlook(&((Icmppriv*)icmp->priv)->ht, dst, recid, src, recid);
if(iph != nil){
Translation *q;
if((q = transbackward(icmp, iph)) == nil)
goto raise;
hnputs_csum(p->src+0, nhgets(q->forward.raddr+IPv4off+0), p->ipcksum);
hnputs_csum(p->src+2, nhgets(q->forward.raddr+IPv4off+2), p->ipcksum);
hnputs_csum(p->icmpid, q->forward.rport, p->cksum);
qunlock(icmp);
icmpproxyadvice(icmp->f, bp, p->src);
return;
}
for(c = icmp->conv; (s = *c) != nil; c++){
if(s->lport == recid)
if(ipcmp(s->laddr, src) == 0)
@ -470,9 +557,38 @@ icmpadvise(Proto *icmp, Block *bp, char *msg)
break;
}
}
raise:
qunlock(icmp);
freeblist(bp);
}
static Block*
icmpforward(Proto *icmp, Block *bp, Route *r)
{
uchar da[IPaddrlen], sa[IPaddrlen];
ushort id;
Icmp *p;
Translation *q;
p = (Icmp*)(bp->rp);
v4tov6(sa, p->src);
v4tov6(da, p->dst);
id = nhgets(p->icmpid);
qlock(icmp);
q = transforward(icmp, &((Icmppriv*)icmp->priv)->ht, sa, id, da, id, r);
if(q == nil){
qunlock(icmp);
freeblist(bp);
return nil;
}
memmove(p->src, q->backward.laddr+IPv4off, IPv4addrlen);
hnputs_csum(p->icmpid, q->backward.lport, p->cksum);
qunlock(icmp);
return bp;
}
static int
icmpstats(Proto *icmp, char *buf, int len)
{
@ -511,6 +627,7 @@ icmpinit(Fs *fs)
icmp->stats = icmpstats;
icmp->ctl = nil;
icmp->advise = icmpadvise;
icmp->forward = icmpforward;
icmp->gc = nil;
icmp->ipproto = IP_ICMPPROTO;
icmp->nc = 128;

View file

@ -711,9 +711,8 @@ icmpiput6(Proto *icmp, Ipifc *ifc, Block *bp)
goto raise;
}
p = (IPICMP *)bp->rp;
/* get rid of fragment header if this is the first fragment */
if(p->proto == FH && BLEN(bp) >= MinAdvise+IP6FHDR && MinAdvise > IP6HDR){
if((p->vcf[0] & 0xF0) == IP_VER6 && p->proto == FH && BLEN(bp) >= MinAdvise+IP6FHDR && MinAdvise > IP6HDR){
Fraghdr6 *fh = (Fraghdr6*)(bp->rp + IP6HDR);
if((nhgets(fh->offsetRM) & ~7) == 0){ /* first fragment */
p->proto = fh->nexthdr;
@ -725,9 +724,10 @@ icmpiput6(Proto *icmp, Ipifc *ifc, Block *bp)
bp->rp -= IP6HDR;
}
}
if(p->proto != FH){
if((p->vcf[0] & 0xF0) == IP_VER6 && p->proto != FH){
pr = Fsrcvpcolx(icmp->f, p->proto);
if(pr != nil && pr->advise != nil) {
netlog(icmp->f, Logicmp, "advising %s!%I -> %I: %s\n", pr->name, p->src, p->dst, msg);
(*pr->advise)(pr, bp, msg);
return;
}

View file

@ -308,8 +308,8 @@ illocalclose(Conv *c)
ic = (Ilcb*)c->ptcl;
ic->state = Ilclosed;
iphtrem(&ipriv->ht, c);
ipmove(c->laddr, IPnoaddr);
c->lport = 0;
ipmove(c->laddr, IPnoaddr);
}
static void
@ -544,6 +544,7 @@ iliput(Proto *il, Ipifc*, Block *bp)
uchar laddr[IPaddrlen];
ushort sp, dp, csum;
int plen, illen;
Iphash *iph;
Conv *new, *s;
Ilpriv *ipriv;
@ -584,14 +585,14 @@ iliput(Proto *il, Ipifc*, Block *bp)
}
qlock(il);
s = iphtlook(&ipriv->ht, raddr, dp, laddr, sp);
if(s == nil){
iph = iphtlook(&ipriv->ht, raddr, dp, laddr, sp);
if(iph == nil){
if(ih->iltype == Ilsync)
ilreject(il->f, ih); /* no listener */
qunlock(il);
goto raise;
}
s = iphconv(iph);
ic = (Ilcb*)s->ptcl;
if(ic->state == Illistening){
if(ih->iltype != Ilsync){

View file

@ -252,7 +252,7 @@ free:
void
ipiput4(Fs *f, Ipifc *ifc, Block *bp)
{
int hl, len, hop, tos;
int hl, len, hop;
uchar v6dst[IPaddrlen];
ushort frag;
Ip4hdr *h;
@ -327,14 +327,42 @@ ipiput4(Fs *f, Ipifc *ifc, Block *bp)
/* don't forward if packet has timed out */
hop = h->ttl;
if(hop < 1) {
if(hop <= 1) {
ip->stats[InHdrErrors]++;
icmpttlexceeded(f, ifc, bp);
goto drop;
}
/* reassemble if the interface expects it */
if(nifc->reassemble){
if(r->type & Rtrans) {
p = Fsrcvpcolx(f, h->proto);
if(p == nil || p->forward == nil){
ip->stats[OutDiscards]++;
goto drop;
}
if(hl > IP4HDR) {
hl -= IP4HDR;
len -= hl;
bp->rp += hl;
memmove(bp->rp, h, IP4HDR);
h = (Ip4hdr*)bp->rp;
h->vihl = IP_VER4|IP_HLEN4;
hnputs(h->length, len);
}
frag = nhgets(h->frag);
if(frag & (IP_MF|IP_FO)) {
bp = ip4reassemble(ip, frag, bp);
if(bp == nil)
return;
}
bp = (*p->forward)(p, bp, r);
if(bp == nil)
return;
h = (Ip4hdr*)bp->rp;
} else if(nifc->reassemble) {
/* reassemble as the interface expects it */
frag = nhgets(h->frag);
if(frag & (IP_MF|IP_FO)) {
bp = ip4reassemble(ip, frag, bp);
@ -345,9 +373,7 @@ ipiput4(Fs *f, Ipifc *ifc, Block *bp)
}
ip->stats[ForwDatagrams]++;
tos = h->tos;
hop = h->ttl;
ipoput4(f, bp, 1, hop - 1, tos, &rh);
ipoput4(f, bp, 1, hop - 1, h->tos, &rh);
return;
}

View file

@ -22,6 +22,7 @@ typedef struct Arpent Arpent;
typedef struct Arp Arp;
typedef struct Route Route;
typedef struct Routehint Routehint;
typedef struct Translation Translation;
typedef struct Routerparams Routerparams;
typedef struct Hostparams Hostparams;
@ -177,6 +178,71 @@ struct Routehint
Arpent *a; /* last arp entry used */
};
/*
* hash table for 2 ip addresses + 2 ports
*/
enum
{
Nipht= 521, /* convenient prime */
IPmatchexact= 0, /* match on 4 tuple */
IPmatchany, /* *!* */
IPmatchport, /* *!port */
IPmatchaddr, /* addr!* */
IPmatchpa, /* addr!port */
};
struct Iphash
{
Iphash *nextiphash;
uchar trans; /* 0 = conv, 1 = foward, 2 = backward */
uchar match;
ushort lport; /* local port number */
ushort rport; /* remote port number */
uchar laddr[IPaddrlen]; /* local IP address */
uchar raddr[IPaddrlen]; /* remote IP address */
};
struct Ipht
{
Lock;
Iphash *tab[Nipht];
};
void iphtadd(Ipht*, Iphash*);
void iphtrem(Ipht*, Iphash*);
Iphash *iphtlook(Ipht *ht, uchar *sa, ushort sp, uchar *da, ushort dp);
/*
* NAT entry.
*
* This holds the 5 tuple as two Iphashes.
* The "forward" hash matches the the packets from
* the source that need to be translated and
* "backward" matches the packets coming back
* from the destination.
*/
struct Translation
{
Translation *next;
Translation **link;
ulong time;
Iphash forward;
#define iphforward(h) ((Translation*)((char*)(h) - (char*)&((Translation*)0)->forward))
Iphash backward;
#define iphbackward(h) ((Translation*)((char*)(h) - (char*)&((Translation*)0)->backward))
/* used for forwarding to the source */
Routehint;
};
Translation *transforward(Proto *p, Ipht *ht, uchar *sa, int sp, uchar *da, int dp, Route *r);
Translation *transbackward(Proto *p, Iphash *iph);
/*
* one per conversation directory
*/
@ -187,16 +253,15 @@ struct Conv
int x; /* conversation index */
Proto* p;
int restricted; /* remote port is restricted */
int ignoreadvice; /* don't terminate connection on icmp errors */
uint ttl; /* max time to live */
uint tos; /* type of service */
uchar restricted; /* remote port is restricted */
uchar ignoreadvice; /* don't terminate connection on icmp errors */
uchar ipversion;
uchar laddr[IPaddrlen]; /* local IP address */
uchar raddr[IPaddrlen]; /* remote IP address */
ushort lport; /* local port number */
ushort rport; /* remote port number */
Iphash;
#define iphconv(h) ((Conv*)((char*)(h) - (char*)&((Conv*)0)->Iphash))
char *owner; /* protections */
int perm;
@ -206,7 +271,6 @@ struct Conv
/* udp specific */
int headers; /* data src/dst headers in udp */
int reliable; /* true if reliable udp */
Conv* incall; /* calls waiting to be listened for */
Conv* next;
@ -351,34 +415,6 @@ struct Ipmulti
Ipmulti *next;
};
/*
* hash table for 2 ip addresses + 2 ports
*/
enum
{
Nipht= 521, /* convenient prime */
IPmatchexact= 0, /* match on 4 tuple */
IPmatchany, /* *!* */
IPmatchport, /* *!port */
IPmatchaddr, /* addr!* */
IPmatchpa, /* addr!port */
};
struct Iphash
{
Iphash *next;
Conv *c;
int match;
};
struct Ipht
{
Lock;
Iphash *tab[Nipht];
};
void iphtadd(Ipht*, Conv*);
void iphtrem(Ipht*, Conv*);
Conv* iphtlook(Ipht *ht, uchar *sa, ushort sp, uchar *da, ushort dp);
/*
* one per multiplexed protocol
*/
@ -412,9 +448,15 @@ struct Proto
Qid qid; /* qid for protocol directory */
ushort nextrport;
/* network address translation */
Translation* translations;
Block* (*forward)(Proto*, Block*, Route*);
void *priv;
};
int unusedlport(Proto *p);
/*
* one per IP protocol stack
@ -489,6 +531,7 @@ enum
Logrudpmsg= 1<<16,
Logesp= 1<<17,
Logtcpwin= 1<<18,
Logtrans= 1<<19,
};
void netloginit(Fs*);
@ -522,7 +565,9 @@ enum
Rbcast= (1<<4), /* a broadcast self address */
Rmulti= (1<<5), /* a multicast self address */
Rproxy= (1<<6), /* this route should be proxied */
Rsrc= (1<<7), /* source specific route */
Rtrans= (1<<7), /* this route translates source address (NAT) */
Rsrc= (1<<8), /* source specific route */
};
struct RouteTree
@ -533,7 +578,7 @@ struct RouteTree
Ipifc *ifc;
uchar ifcid; /* must match ifc->id */
uchar depth;
uchar type;
ushort type;
char tag[4];
int ref;
};
@ -641,10 +686,13 @@ extern int isv4(uchar*);
extern void v4tov6(uchar *v6, uchar *v4);
extern int v6tov4(uchar *v4, uchar *v6);
extern int eipfmt(Fmt*);
extern int ipismulticast(uchar *ip);
extern int convipvers(Conv *c);
extern void hnputs_csum(void *p, ushort v, uchar *pcsum);
#define ipmove(x, y) memmove(x, y, IPaddrlen)
#define ipcmp(x, y) ( (x)[IPaddrlen-1] != (y)[IPaddrlen-1] || memcmp(x, y, IPaddrlen) )
#define isv4mcast(ip4) ((ip4)[0] >= 0xe0 && (ip4)[0] < 0xf0)
extern uchar IPv4bcast[IPaddrlen];
extern uchar IPv4bcastobs[IPaddrlen];
@ -670,7 +718,6 @@ extern Medium* ipfindmedium(char *name);
extern void addipmedium(Medium *med);
extern void ipifcoput(Ipifc *ifc, Block *bp, int version, uchar *ip, Routehint *rh);
extern int ipforme(Fs*, uchar *addr);
extern int ipismulticast(uchar *ip);
extern Ipifc* findipifc(Fs*, uchar *local, uchar *remote, int type);
extern Ipifc* findipifcstr(Fs *f, char *s);
extern void findlocalip(Fs*, uchar *local, uchar *remote);
@ -694,6 +741,8 @@ extern void icmpnohost(Fs*, Ipifc*, Block*);
extern void icmpnoconv(Fs*, Block*);
extern void icmpcantfrag(Fs*, Block*, int);
extern void icmpttlexceeded(Fs*, Ipifc*, Block*);
extern void icmpproxyadvice(Fs *, Block*, uchar*);
extern ushort ipcsum(uchar*);
extern void ipiput4(Fs*, Ipifc*, Block*);
extern void ipiput6(Fs*, Ipifc*, Block*);

View file

@ -203,7 +203,6 @@ ipv62smcast(uchar *smcast, uchar *a)
smcast[15] = a[15];
}
/*
* parse a hex mac address
*/
@ -233,140 +232,23 @@ parsemac(uchar *to, char *from, int len)
}
/*
* hashing tcp, udp, ... connections
* return multicast version if any
*/
ulong
iphash(uchar *sa, ushort sp, uchar *da, ushort dp)
int
ipismulticast(uchar *ip)
{
return ((sa[IPaddrlen-1]<<24) ^ (sp << 16) ^ (da[IPaddrlen-1]<<8) ^ dp ) % Nipht;
}
void
iphtadd(Ipht *ht, Conv *c)
{
ulong hv;
Iphash *h;
hv = iphash(c->raddr, c->rport, c->laddr, c->lport);
h = smalloc(sizeof(*h));
if(ipcmp(c->raddr, IPnoaddr) != 0)
h->match = IPmatchexact;
else {
if(ipcmp(c->laddr, IPnoaddr) != 0){
if(c->lport == 0)
h->match = IPmatchaddr;
else
h->match = IPmatchpa;
} else {
if(c->lport == 0)
h->match = IPmatchany;
else
h->match = IPmatchport;
}
if(isv4(ip)){
if(isv4mcast(&ip[IPv4off]))
return V4;
}
h->c = c;
lock(ht);
h->next = ht->tab[hv];
ht->tab[hv] = h;
unlock(ht);
else if(isv6mcast(ip))
return V6;
return 0;
}
void
iphtrem(Ipht *ht, Conv *c)
{
ulong hv;
Iphash **l, *h;
hv = iphash(c->raddr, c->rport, c->laddr, c->lport);
lock(ht);
for(l = &ht->tab[hv]; (*l) != nil; l = &(*l)->next)
if((*l)->c == c){
h = *l;
(*l) = h->next;
free(h);
break;
}
unlock(ht);
}
/* look for a matching conversation with the following precedence
* connected && raddr,rport,laddr,lport
* announced && laddr,lport
* announced && *,lport
* announced && laddr,*
* announced && *,*
/*
* return ip version of a connection
*/
Conv*
iphtlook(Ipht *ht, uchar *sa, ushort sp, uchar *da, ushort dp)
{
ulong hv;
Iphash *h;
Conv *c;
/* exact 4 pair match (connection) */
hv = iphash(sa, sp, da, dp);
lock(ht);
for(h = ht->tab[hv]; h != nil; h = h->next){
if(h->match != IPmatchexact)
continue;
c = h->c;
if(sp == c->rport && dp == c->lport
&& ipcmp(sa, c->raddr) == 0 && ipcmp(da, c->laddr) == 0){
unlock(ht);
return c;
}
}
/* match local address and port */
hv = iphash(IPnoaddr, 0, da, dp);
for(h = ht->tab[hv]; h != nil; h = h->next){
if(h->match != IPmatchpa)
continue;
c = h->c;
if(dp == c->lport && ipcmp(da, c->laddr) == 0){
unlock(ht);
return c;
}
}
/* match just port */
hv = iphash(IPnoaddr, 0, IPnoaddr, dp);
for(h = ht->tab[hv]; h != nil; h = h->next){
if(h->match != IPmatchport)
continue;
c = h->c;
if(dp == c->lport){
unlock(ht);
return c;
}
}
/* match local address */
hv = iphash(IPnoaddr, 0, da, 0);
for(h = ht->tab[hv]; h != nil; h = h->next){
if(h->match != IPmatchaddr)
continue;
c = h->c;
if(ipcmp(da, c->laddr) == 0){
unlock(ht);
return c;
}
}
/* look for something that matches anything */
hv = iphash(IPnoaddr, 0, IPnoaddr, 0);
for(h = ht->tab[hv]; h != nil; h = h->next){
if(h->match != IPmatchany)
continue;
c = h->c;
unlock(ht);
return c;
}
unlock(ht);
return nil;
}
int
convipvers(Conv *c)
{
@ -375,3 +257,313 @@ convipvers(Conv *c)
else
return V6;
}
/*
* hashing tcp, udp, ... connections
*/
static ulong
iphash(uchar *sa, ushort sp, uchar *da, ushort dp)
{
return ((sa[IPaddrlen-1]<<24) ^ (sp << 16) ^ (da[IPaddrlen-1]<<8) ^ dp ) % Nipht;
}
void
iphtadd(Ipht *ht, Iphash *h)
{
ulong hv;
if(ipcmp(h->raddr, IPnoaddr) != 0)
h->match = IPmatchexact;
else {
if(ipcmp(h->laddr, IPnoaddr) != 0){
if(h->lport == 0)
h->match = IPmatchaddr;
else
h->match = IPmatchpa;
} else {
if(h->lport == 0)
h->match = IPmatchany;
else
h->match = IPmatchport;
}
}
lock(ht);
hv = iphash(h->raddr, h->rport, h->laddr, h->lport);
h->nextiphash = ht->tab[hv];
ht->tab[hv] = h;
unlock(ht);
}
void
iphtrem(Ipht *ht, Iphash *h)
{
ulong hv;
Iphash **l;
lock(ht);
hv = iphash(h->raddr, h->rport, h->laddr, h->lport);
for(l = &ht->tab[hv]; (*l) != nil; l = &(*l)->nextiphash)
if(*l == h){
(*l) = h->nextiphash;
h->nextiphash = nil;
break;
}
unlock(ht);
}
/* look for a matching iphash with the following precedence
* raddr,rport,laddr,lport
* laddr,lport
* *,lport
* laddr,*
* *,*
*/
Iphash*
iphtlook(Ipht *ht, uchar *sa, ushort sp, uchar *da, ushort dp)
{
ulong hv;
Iphash *h;
lock(ht);
/* exact 4 pair match (connection) */
hv = iphash(sa, sp, da, dp);
for(h = ht->tab[hv]; h != nil; h = h->nextiphash){
if(h->match != IPmatchexact)
continue;
if(sp == h->rport && dp == h->lport
&& ipcmp(sa, h->raddr) == 0 && ipcmp(da, h->laddr) == 0){
unlock(ht);
return h;
}
}
/* match local address and port */
hv = iphash(IPnoaddr, 0, da, dp);
for(h = ht->tab[hv]; h != nil; h = h->nextiphash){
if(h->match != IPmatchpa)
continue;
if(dp == h->lport && ipcmp(da, h->laddr) == 0){
unlock(ht);
return h;
}
}
/* match just port */
hv = iphash(IPnoaddr, 0, IPnoaddr, dp);
for(h = ht->tab[hv]; h != nil; h = h->nextiphash){
if(h->match != IPmatchport)
continue;
if(dp == h->lport){
unlock(ht);
return h;
}
}
/* match local address */
hv = iphash(IPnoaddr, 0, da, 0);
for(h = ht->tab[hv]; h != nil; h = h->nextiphash){
if(h->match != IPmatchaddr)
continue;
if(ipcmp(da, h->laddr) == 0){
unlock(ht);
return h;
}
}
/* look for something that matches anything */
hv = iphash(IPnoaddr, 0, IPnoaddr, 0);
for(h = ht->tab[hv]; h != nil; h = h->nextiphash){
if(h->match != IPmatchany)
continue;
unlock(ht);
return h;
}
unlock(ht);
return nil;
}
/*
* Move entry to front of Proto.translations
* and update the timestamp.
*
* Proto is locked.
*/
static Translation*
transupdate(Proto *p, Translation *q)
{
q->time = NOW;
/* unlink */
if(q->link != nil && (*q->link = q->next) != nil)
q->next->link = q->link;
/* link to front */
if((q->next = p->translations) != nil)
q->next->link = &q->next;
p->translations = q;
q->link = &p->translations;
return q;
}
/*
* Called with the 4-tuple (sa,sp,da,dp)
* that should be source translated,
* returning the translation.
*
* Proto is locked.
*/
Translation*
transforward(Proto *p, Ipht *ht, uchar *sa, int sp, uchar *da, int dp, Route *r)
{
uchar ia[IPaddrlen];
Routehint rh;
Translation *q;
Iphash *iph;
Ipifc *ifc;
int lport;
ulong now;
int num;
/* Translation already exists? */
iph = iphtlook(ht, sa, sp, da, dp);
if(iph != nil) {
if(iph->trans != 1)
return nil;
return transupdate(p, iphforward(iph));
}
/* Bad source address? */
if(ipismulticast(sa) || ipforme(p->f, sa) != 0){
netlog(p->f, Logtrans, "trans: bad source address: %s!%I!%d -> %I!%d\n",
p->name, sa, sp, da, dp);
return nil;
}
/* Bad forward route? */
if(r == nil || (ifc = r->ifc) == nil){
netlog(p->f, Logtrans, "trans: no forward route: %s!%I!%d -> %I!%d\n",
p->name, sa, sp, da, dp);
return nil;
}
/* Find a source address on the destination interface */
rlock(ifc);
memmove(ia, v4prefix, IPv4off);
if(!ipv4local(ifc, ia+IPv4off, 0, (r->type & (Rifc|Runi|Rbcast|Rmulti))? da+IPv4off: r->v4.gate)){
runlock(ifc);
netlog(p->f, Logtrans, "trans: no source ip: %s!%I!%d -> %I!%d\n",
p->name, sa, sp, da, dp);
return nil;
}
runlock(ifc);
/* Check backward route */
rh.a = nil;
rh.r = nil;
if(ipismulticast(da))
r = v4lookup(p->f, sa+IPv4off, ia+IPv4off, nil);
else
r = v4lookup(p->f, sa+IPv4off, da+IPv4off, &rh);
if(r == nil || (r->ifc == ifc && !ifc->reflect)){
netlog(p->f, Logtrans, "trans: bad backward route: %s!%I!%d <- %I <- %I!%d\n",
p->name, sa, sp, ia, da, dp);
return nil;
}
/* Find local port */
lport = unusedlport(p);
if(lport <= 0){
netlog(p->f, Logtrans, "trans: no local port: %s!%I!%d <- %I <- %I!%d\n",
p->name, sa, sp, ia, da, dp);
return nil;
}
/* Reuse expired entries */
num = 0;
now = NOW;
for(q = p->translations; q != nil; q = q->next) {
if(++num >= 1000 || (now - q->time) >= 5*60*1000){
netlog(p->f, Logtrans, "trans: removing %s!%I!%d -> %I!%d -> %I!%d\n",
p->name,
q->forward.raddr, q->forward.rport,
q->backward.laddr, q->backward.lport,
q->forward.laddr, q->forward.lport);
iphtrem(ht, &q->forward);
iphtrem(ht, &q->backward);
break;
}
}
if(q == nil){
q = malloc(sizeof(*q));
if(q == nil)
return nil;
q->link = nil;
}
/* Match what needs to be forwarded */
q->forward.trans = 1;
q->forward.lport = dp;
q->forward.rport = sp;
ipmove(q->forward.laddr, da);
ipmove(q->forward.raddr, sa);
/* Match what comes back to us */
q->backward.trans = 2;
q->backward.lport = lport;
ipmove(q->backward.laddr, ia);
if(p->ipproto == 1 || ipismulticast(da)){
q->backward.rport = 0;
ipmove(q->backward.raddr, IPnoaddr);
} else {
q->backward.rport = dp;
ipmove(q->backward.raddr, da);
}
memmove(&q->Routehint, &rh, sizeof(rh));
netlog(p->f, Logtrans, "trans: adding %s!%I!%d -> %I!%d -> %I!%d\n",
p->name,
q->forward.raddr, q->forward.rport,
q->backward.laddr, q->backward.lport,
q->forward.laddr, q->forward.lport);
iphtadd(ht, &q->forward);
iphtadd(ht, &q->backward);
return transupdate(p, q);
}
/*
* Check if backward translation is valid and
* update timestamp.
*
* Proto is locked.
*/
Translation*
transbackward(Proto *p, Iphash *iph)
{
if(iph == nil || iph->trans != 2)
return nil;
return transupdate(p, iphbackward(iph));
}
/*
* Checksum adjusting hnputs()
*/
void
hnputs_csum(void *p, ushort v, uchar *pcsum)
{
ulong csum;
assert((((uchar*)p - pcsum) & 1) == 0);
csum = nhgets(pcsum)^0xFFFF;
csum += nhgets(p)^0xFFFF;
csum += v;
hnputs(p, v);
while(v = csum >> 16)
csum = (csum & 0xFFFF) + v;
hnputs(pcsum, csum^0xFFFF);
}

View file

@ -1435,21 +1435,6 @@ ipproxyifc(Fs *f, Ipifc *ifc, uchar *ip)
return ipremoteonifc(ifc, ip) != nil;
}
/*
* return multicast version if any
*/
int
ipismulticast(uchar *ip)
{
if(isv4(ip)){
if(ip[IPv4off] >= 0xe0 && ip[IPv4off] < 0xf0)
return V4;
}
else if(ip[0] == 0xff)
return V6;
return 0;
}
/*
* add a multicast address to an interface.
*/

View file

@ -875,6 +875,9 @@ parseroutetype(char *p)
case 'p':
if(((type ^= Rptpt) & Rptpt) != Rptpt) return -1;
break;
case 't':
if(((type ^= Rtrans) & Rtrans) != Rtrans) return -1;
break;
case '\0':
return type;
}
@ -900,6 +903,10 @@ routetype(int type, char p[8])
if(type & Rptpt)
*p++ = 'p';
if(type & Rtrans)
*p++ = 't';
*p = 0;
}

View file

@ -278,7 +278,7 @@ ipiput6(Fs *f, Ipifc *ifc, Block *bp)
/* don't forward if packet has timed out */
hop = h->ttl;
if(hop < 1) {
if(hop <= 1) {
ip->stats[InHdrErrors]++;
icmpttlexceeded6(f, ifc, bp);
goto drop;
@ -292,8 +292,7 @@ ipiput6(Fs *f, Ipifc *ifc, Block *bp)
ip->stats[ForwDatagrams]++;
h = (Ip6hdr*)bp->rp;
tos = (h->vcf[0]&0x0F)<<2 | (h->vcf[1]&0xF0)>>2;
hop = h->ttl;
ipoput6(f, bp, 1, hop-1, tos, &rh);
ipoput6(f, bp, 1, hop - 1, tos, &rh);
return;
}

View file

@ -51,6 +51,7 @@ static Netlogflag flags[] =
{ "udpmsg", Logudp|Logudpmsg, },
{ "ipmsg", Logip|Logipmsg, },
{ "esp", Logesp, },
{ "trans", Logtrans, },
{ nil, 0, },
};

View file

@ -220,9 +220,10 @@ rudpconnect(Conv *c, char **argv, int argc)
rudpstartackproc(c->p);
e = Fsstdconnect(c, argv, argc);
Fsconnected(c, e);
if(e != nil)
return e;
iphtadd(&upriv->ht, c);
return e;
return nil;
}
@ -256,7 +257,6 @@ rudpannounce(Conv *c, char** argv, int argc)
return e;
Fsconnected(c, nil);
iphtadd(&upriv->ht, c);
return nil;
}
@ -289,10 +289,11 @@ rudpclose(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->headers = 0;
ucb->randdrop = 0;
@ -460,11 +461,12 @@ rudpkick(void *x)
void
rudpiput(Proto *rudp, Ipifc *ifc, Block *bp)
{
int len, olen, ottl;
int len, olen;
Udphdr *uh;
Iphash *iph;
Conv *c;
Rudpcb *ucb;
uchar raddr[IPaddrlen], laddr[IPaddrlen];
uchar raddr[IPaddrlen], laddr[IPaddrlen], ottl;
ushort rport, lport;
Rudppriv *upriv;
Fs *f;
@ -503,9 +505,8 @@ rudpiput(Proto *rudp, Ipifc *ifc, Block *bp)
}
qlock(rudp);
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.rudpNoPorts++;
qunlock(rudp);
@ -517,6 +518,7 @@ rudpiput(Proto *rudp, Ipifc *ifc, Block *bp)
freeblist(bp);
return;
}
c = iphconv(iph);
ucb = (Rudpcb*)c->ptcl;
qlock(ucb);
qunlock(rudp);

View file

@ -126,7 +126,7 @@ struct Tcp4hdr
uchar length[2]; /* packet length */
uchar id[2]; /* Identification */
uchar frag[2]; /* Fragment information */
uchar Unused;
uchar ttl;
uchar proto;
uchar tcplen[2];
uchar tcpsrc[4];
@ -1814,9 +1814,7 @@ tcpincoming(Conv *s, Tcp *segp, uchar *src, uchar *dst, uchar version)
}
tcpsetstate(new, Established);
iphtadd(&tpriv->ht, new);
return new;
}
@ -2068,10 +2066,11 @@ tcpiput(Proto *tcp, Ipifc*, Block *bp)
Tcp seg;
Tcp4hdr *h4;
Tcp6hdr *h6;
int hdrlen;
Tcpctl *tcb;
ushort length, csum;
int hdrlen;
ushort length;
uchar source[IPaddrlen], dest[IPaddrlen];
Iphash *iph;
Conv *s;
Fs *f;
Tcppriv *tpriv;
@ -2087,21 +2086,32 @@ tcpiput(Proto *tcp, Ipifc*, Block *bp)
h6 = (Tcp6hdr*)(bp->rp);
if((h4->vihl&0xF0)==IP_VER4) {
int ttl = h4->ttl;
version = V4;
length = nhgets(h4->length);
if(length < TCP4_PKT){
tpriv->stats[HlenErrs]++;
tpriv->stats[InErrs]++;
netlog(f, Logtcp, "bad tcp len\n");
freeblist(bp);
return;
}
length -= TCP4_PKT;
v4tov6(dest, h4->tcpdst);
v4tov6(source, h4->tcpsrc);
h4->Unused = 0;
hnputs(h4->tcplen, length-TCP4_PKT);
if(!(bp->flag & Btcpck) && (h4->tcpcksum[0] || h4->tcpcksum[1]) &&
ptclcsum(bp, TCP4_IPLEN, length-TCP4_IPLEN)) {
h4->ttl = 0;
hnputs(h4->tcplen, length);
if(!(bp->flag & Btcpck) && (h4->tcpcksum[0] || h4->tcpcksum[1])
&& ptclcsum(bp, TCP4_IPLEN, length + TCP4_PKT - TCP4_IPLEN)) {
tpriv->stats[CsumErrs]++;
tpriv->stats[InErrs]++;
netlog(f, Logtcp, "bad tcp proto cksum\n");
freeblist(bp);
return;
}
h4->ttl = ttl;
hdrlen = ntohtcp4(&seg, &bp);
if(hdrlen < 0){
@ -2110,16 +2120,8 @@ tcpiput(Proto *tcp, Ipifc*, Block *bp)
netlog(f, Logtcp, "bad tcp hdr len\n");
return;
}
/* trim the packet to the size claimed by the datagram */
length -= hdrlen+TCP4_PKT;
bp = trimblock(bp, hdrlen+TCP4_PKT, length);
if(bp == nil){
tpriv->stats[LenErrs]++;
tpriv->stats[InErrs]++;
netlog(f, Logtcp, "tcp len < 0 after trim\n");
return;
}
length -= hdrlen;
hdrlen += TCP4_PKT;
}
else {
int ttl = h6->ttl;
@ -2133,13 +2135,13 @@ tcpiput(Proto *tcp, Ipifc*, Block *bp)
h6->ploadlen[0] = h6->ploadlen[1] = h6->proto = 0;
h6->ttl = proto;
hnputl(h6->vcf, length);
if((h6->tcpcksum[0] || h6->tcpcksum[1]) &&
(csum = ptclcsum(bp, TCP6_IPLEN, length+TCP6_PHDRSIZE)) != 0) {
if((h6->tcpcksum[0] || h6->tcpcksum[1])
&& ptclcsum(bp, TCP6_IPLEN, length+TCP6_PHDRSIZE) != 0) {
tpriv->stats[CsumErrs]++;
tpriv->stats[InErrs]++;
netlog(f, Logtcp,
"bad tcpv6 proto cksum: got %#ux, computed %#ux\n",
h6->tcpcksum[0]<<8 | h6->tcpcksum[1], csum);
"bad tcpv6 proto cksum: got %#ux\n",
h6->tcpcksum[0]<<8 | h6->tcpcksum[1]);
freeblist(bp);
return;
}
@ -2154,24 +2156,16 @@ tcpiput(Proto *tcp, Ipifc*, Block *bp)
netlog(f, Logtcp, "bad tcpv6 hdr len\n");
return;
}
/* trim the packet to the size claimed by the datagram */
length -= hdrlen;
bp = trimblock(bp, hdrlen+TCP6_PKT, length);
if(bp == nil){
tpriv->stats[LenErrs]++;
tpriv->stats[InErrs]++;
netlog(f, Logtcp, "tcpv6 len < 0 after trim\n");
return;
}
hdrlen += TCP6_PKT;
}
/* lock protocol while searching for a conversation */
qlock(tcp);
/* Look for a matching conversation */
s = iphtlook(&tpriv->ht, source, seg.source, dest, seg.dest);
if(s == nil){
iph = iphtlook(&tpriv->ht, source, seg.source, dest, seg.dest);
if(iph == nil){
netlog(f, Logtcp, "iphtlook(src %I!%d, dst %I!%d) failed\n",
source, seg.source, dest, seg.dest);
reset:
@ -2180,6 +2174,30 @@ reset:
sndrst(tcp, source, dest, length, &seg, version, "no conversation", nil);
return;
}
if(iph->trans){
Translation *q;
int hop = h4->ttl;
if(hop <= 1 || (q = transbackward(tcp, iph)) == nil)
goto reset;
hnputs_csum(h4->tcpdst+0, nhgets(q->forward.raddr+IPv4off+0), h4->tcpcksum);
hnputs_csum(h4->tcpdst+2, nhgets(q->forward.raddr+IPv4off+2), h4->tcpcksum);
hnputs_csum(h4->tcpdport, q->forward.rport, h4->tcpcksum);
qunlock(tcp);
ipoput4(f, bp, 1, hop - 1, h4->tos, q);
return;
}
s = iphconv(iph);
/* trim off ip and tcp headers */
bp = trimblock(bp, hdrlen, length);
if(bp == nil){
tpriv->stats[LenErrs]++;
tpriv->stats[InErrs]++;
netlog(f, Logtcp, "tcp bad length after header trim off\n");
qunlock(tcp);
return;
}
/* if it's a listener, look for the right flags and get a new conv */
tcb = (Tcpctl*)s->ptcl;
@ -3200,11 +3218,12 @@ tcpadvise(Proto *tcp, Block *bp, char *msg)
{
Tcp4hdr *h4;
Tcp6hdr *h6;
Tcpctl *tcb;
uchar source[IPaddrlen];
uchar dest[IPaddrlen];
ushort psource, pdest;
Conv *s, **p;
Iphash *iph;
Tcpctl *tcb;
Conv *s;
h4 = (Tcp4hdr*)(bp->rp);
h6 = (Tcp6hdr*)(bp->rp);
@ -3221,33 +3240,73 @@ tcpadvise(Proto *tcp, Block *bp, char *msg)
pdest = nhgets(h6->tcpdport);
}
/* Look for a connection */
/* Look for a connection (source/dest reversed; this is the original packet we sent) */
qlock(tcp);
for(p = tcp->conv; (s = *p) != nil; p++) {
tcb = (Tcpctl*)s->ptcl;
if(s->rport == pdest)
if(s->lport == psource)
if(tcb->state != Closed)
if(ipcmp(s->raddr, dest) == 0)
if(ipcmp(s->laddr, source) == 0){
if(s->ignoreadvice)
break;
qlock(s);
qunlock(tcp);
switch(tcb->state){
case Syn_sent:
localclose(s, msg);
break;
}
qunlock(s);
freeblist(bp);
return;
}
iph = iphtlook(&((Tcppriv*)tcp->priv)->ht, dest, pdest, source, psource);
if(iph == nil)
goto raise;
if(iph->trans){
Translation *q;
if((q = transbackward(tcp, iph)) == nil)
goto raise;
/* h4->tcplen is the ip header checksum */
hnputs_csum(h4->tcpsrc+0, nhgets(q->forward.raddr+IPv4off+0), h4->tcplen);
hnputs_csum(h4->tcpsrc+2, nhgets(q->forward.raddr+IPv4off+2), h4->tcplen);
/* dont bother fixing tcp checksum, packet is most likely truncated */
hnputs(h4->tcpsport, q->forward.rport);
qunlock(tcp);
icmpproxyadvice(tcp->f, bp, h4->tcpsrc);
return;
}
s = iphconv(iph);
if(s->ignoreadvice || s->state == Closed)
goto raise;
qlock(s);
qunlock(tcp);
tcb = (Tcpctl*)s->ptcl;
if(tcb->state == Syn_sent)
localclose(s, msg);
qunlock(s);
freeblist(bp);
return;
raise:
qunlock(tcp);
freeblist(bp);
}
static Block*
tcpforward(Proto *tcp, Block *bp, Route *r)
{
uchar da[IPaddrlen], sa[IPaddrlen];
ushort dp, sp;
Tcp4hdr *h4;
Translation *q;
h4 = (Tcp4hdr*)(bp->rp);
v4tov6(da, h4->tcpdst);
v4tov6(sa, h4->tcpsrc);
dp = nhgets(h4->tcpdport);
sp = nhgets(h4->tcpsport);
qlock(tcp);
q = transforward(tcp, &((Tcppriv*)tcp->priv)->ht, sa, sp, da, dp, r);
if(q == nil){
qunlock(tcp);
freeblist(bp);
return nil;
}
hnputs_csum(h4->tcpsrc+0, nhgets(q->backward.laddr+IPv4off+0), h4->tcpcksum);
hnputs_csum(h4->tcpsrc+2, nhgets(q->backward.laddr+IPv4off+2), h4->tcpcksum);
hnputs_csum(h4->tcpsport, q->backward.lport, h4->tcpcksum);
qunlock(tcp);
return bp;
}
static char*
tcpporthogdefensectl(char *val)
{
@ -3371,6 +3430,7 @@ tcpinit(Fs *fs)
tcp->close = tcpclose;
tcp->rcv = tcpiput;
tcp->advise = tcpadvise;
tcp->forward = tcpforward;
tcp->stats = tcpstats;
tcp->inuse = tcpinuse;
tcp->gc = tcpgc;

View file

@ -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,28 +551,72 @@ 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
udpstats(Proto *udp, char *buf, int len)
{
@ -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;