summaryrefslogtreecommitdiff
path: root/sys/src/9/ip
diff options
context:
space:
mode:
authorcinap_lenrek <cinap_lenrek@felloff.net>2019-03-03 05:25:00 +0100
committercinap_lenrek <cinap_lenrek@felloff.net>2019-03-03 05:25:00 +0100
commit5b972a9aea9383fbb66142a6a9958e01f7028a89 (patch)
treee66a1ec299f3ed8a9dc7f0b39a9dea29e4cfbcde /sys/src/9/ip
parent0aac600fb3b659b5b9a2a8aaefb821774cf38bd2 (diff)
devip: fix ip fragmentation handling issues with header options
some protocols assume that Ip4hdr.length[] and Ip6hdr.ploadlen[] are valid and not out of range within the block but this has not been verified. also, the ipv4 and ipv6 headers can have variable length options, which was not considered in the fragmentation and reassembly code. to make this sane, ipiput4() and ipiput6() now verify that everything is in range and trims to block to the expected size before it does any further processing. now blocklen() and Ip4hdr.length[] are conistent. ipoput4() and ipoput6() are simpler now, as they can rely on blocklen() only, not having a special routing case. ip fragmentation reassembly has to consider that fragments could arrive with different ip header options, so we store the header+option size in new Ipfrag.hlen field. unfraglen() has to make sure not to run past the buffer, and hadle the case when it encounters multiple fragment headers.
Diffstat (limited to 'sys/src/9/ip')
-rw-r--r--sys/src/9/ip/ip.c322
-rw-r--r--sys/src/9/ip/ip.h5
-rw-r--r--sys/src/9/ip/ipaux.c4
-rw-r--r--sys/src/9/ip/ipv6.c328
4 files changed, 341 insertions, 318 deletions
diff --git a/sys/src/9/ip/ip.c b/sys/src/9/ip/ip.c
index 5be4bb5eb..a198e7dd7 100644
--- a/sys/src/9/ip/ip.c
+++ b/sys/src/9/ip/ip.c
@@ -7,8 +7,6 @@
#include "ip.h"
-#define BLKIPVER(xp) (((Ip4hdr*)((xp)->rp))->vihl&0xF0)
-
static char *statnames[] =
{
[Forwarding] "Forwarding",
@@ -32,41 +30,11 @@ static char *statnames[] =
[FragCreates] "FragCreates",
};
-#define BLKIP(xp) ((Ip4hdr*)((xp)->rp))
-/*
- * This sleazy macro relies on the media header size being
- * larger than sizeof(Ipfrag). ipreassemble checks this is true
- */
-#define BKFG(xp) ((Ipfrag*)((xp)->base))
-
-ushort ipcsum(uchar*);
-Block* ip4reassemble(IP*, int, Block*, Ip4hdr*);
-void ipfragfree4(IP*, Fragment4*);
-Fragment4* ipfragallo4(IP*);
-
-void
-ip_init_6(Fs *f)
-{
- v6params *v6p;
-
- v6p = smalloc(sizeof(v6params));
-
- v6p->rp.mflag = 0; /* default not managed */
- v6p->rp.oflag = 0;
- v6p->rp.maxraint = 600000; /* millisecs */
- v6p->rp.minraint = 200000;
- v6p->rp.linkmtu = 0; /* no mtu sent */
- v6p->rp.reachtime = 0;
- v6p->rp.rxmitra = 0;
- v6p->rp.ttl = MAXTTL;
- v6p->rp.routerlt = (3 * v6p->rp.maxraint) / 1000;
-
- v6p->hp.rxmithost = 1000; /* v6 RETRANS_TIMER */
+static Block* ip4reassemble(IP*, int, Block*);
+static void ipfragfree4(IP*, Fragment4*);
+static Fragment4* ipfragallo4(IP*);
- f->v6p = v6p;
-}
-
-void
+static void
initfrag(IP *ip, int size)
{
Fragment4 *fq4, *eq4;
@@ -124,41 +92,28 @@ ipoput4(Fs *f, Block *bp, int gating, int ttl, int tos, Routehint *rh)
ulong fragoff;
Block *xp, *nb;
Ip4hdr *eh, *feh;
- int lid, len, seglen, chunk, dlen, blklen, offset, medialen;
+ int lid, len, seglen, chunk, hlen, dlen, blklen, offset, medialen;
Route *r;
IP *ip;
int rv = 0;
ip = f->ip;
-
- /* Fill out the ip header */
- eh = (Ip4hdr*)(bp->rp);
-
ip->stats[OutRequests]++;
- /* Number of uchars in data and ip header to write */
+ /* Fill out the ip header */
+ eh = (Ip4hdr*)bp->rp;
+ assert(BLEN(bp) >= IP4HDR);
len = blocklen(bp);
-
- if(gating){
- chunk = nhgets(eh->length);
- if(chunk > len){
- ip->stats[OutDiscards]++;
- netlog(f, Logip, "short gated packet\n");
- goto free;
- }
- if(chunk < len)
- len = chunk;
- }
if(len >= IP_MAX){
ip->stats[OutDiscards]++;
- netlog(f, Logip, "exceeded ip max size %V\n", eh->dst);
+ netlog(f, Logip, "%V -> %V: exceeded ip max size: %d\n", eh->src, eh->dst, len);
goto free;
}
r = v4lookup(f, eh->dst, eh->src, rh);
if(r == nil || (ifc = r->ifc) == nil){
ip->stats[OutNoRoutes]++;
- netlog(f, Logip, "no interface %V -> %V\n", eh->src, eh->dst);
+ netlog(f, Logip, "%V -> %V: no interface\n", eh->src, eh->dst);
rv = -1;
goto free;
}
@@ -168,67 +123,65 @@ ipoput4(Fs *f, Block *bp, int gating, int ttl, int tos, Routehint *rh)
else
gate = r->v4.gate;
- if(!gating)
- eh->vihl = IP_VER4|IP_HLEN4;
- eh->ttl = ttl;
- if(!gating)
- eh->tos = tos;
-
if(!canrlock(ifc))
goto free;
if(waserror()){
runlock(ifc);
nexterror();
}
-
if(ifc->m == nil)
goto raise;
+ if(!gating){
+ eh->vihl = IP_VER4|IP_HLEN4;
+ eh->tos = tos;
+ }
+ eh->ttl = ttl;
+
/* If we dont need to fragment just send it */
medialen = ifc->maxtu - ifc->m->hsize;
if(len <= medialen) {
- if(!gating)
- hnputs(eh->id, incref(&ip->id4));
hnputs(eh->length, len);
if(!gating){
+ hnputs(eh->id, incref(&ip->id4));
eh->frag[0] = 0;
eh->frag[1] = 0;
}
eh->cksum[0] = 0;
eh->cksum[1] = 0;
hnputs(eh->cksum, ipcsum(&eh->vihl));
+
ipifcoput(ifc, bp, V4, gate);
runlock(ifc);
poperror();
return 0;
}
-if((eh->frag[0] & (IP_DF>>8)) && !gating) print("%V: DF set\n", eh->dst);
-
if(eh->frag[0] & (IP_DF>>8)){
ip->stats[FragFails]++;
ip->stats[OutDiscards]++;
icmpcantfrag(f, bp, medialen);
- netlog(f, Logip, "%V: eh->frag[0] & (IP_DF>>8)\n", eh->dst);
+ netlog(f, Logip, "%V -> %V: can't fragment with DF flag set\n", eh->src, eh->dst);
goto raise;
}
- seglen = (medialen - IP4HDR) & ~7;
+ hlen = (eh->vihl & 0xF)<<2;
+ seglen = (medialen - hlen) & ~7;
if(seglen < 8){
ip->stats[FragFails]++;
ip->stats[OutDiscards]++;
- netlog(f, Logip, "%V seglen < 8\n", eh->dst);
+ netlog(f, Logip, "%V -> %V: can't fragment with seglen < 8\n", eh->src, eh->dst);
goto raise;
}
- dlen = len - IP4HDR;
+ dlen = len - hlen;
xp = bp;
if(gating)
lid = nhgets(eh->id);
else
lid = incref(&ip->id4);
- offset = IP4HDR;
+ offset = hlen;
while(offset && offset >= BLEN(xp)) {
offset -= BLEN(xp);
xp = xp->next;
@@ -241,11 +194,11 @@ if((eh->frag[0] & (IP_DF>>8)) && !gating) print("%V: DF set\n", eh->dst);
fragoff = 0;
dlen += fragoff;
for(; fragoff < dlen; fragoff += seglen) {
- nb = allocb(IP4HDR+seglen);
- feh = (Ip4hdr*)(nb->rp);
+ nb = allocb(hlen+seglen);
+ feh = (Ip4hdr*)nb->rp;
- memmove(nb->wp, eh, IP4HDR);
- nb->wp += IP4HDR;
+ memmove(nb->wp, eh, hlen);
+ nb->wp += hlen;
if((fragoff + seglen) >= dlen) {
seglen = dlen - fragoff;
@@ -254,17 +207,17 @@ if((eh->frag[0] & (IP_DF>>8)) && !gating) print("%V: DF set\n", eh->dst);
else
hnputs(feh->frag, (fragoff>>3)|IP_MF);
- hnputs(feh->length, seglen + IP4HDR);
+ hnputs(feh->length, seglen + hlen);
hnputs(feh->id, lid);
/* Copy up the data area */
chunk = seglen;
while(chunk) {
- if(!xp) {
+ if(xp == nil) {
ip->stats[OutDiscards]++;
ip->stats[FragFails]++;
freeblist(nb);
- netlog(f, Logip, "!xp: chunk %d\n", chunk);
+ netlog(f, Logip, "xp == nil: chunk %d\n", chunk);
goto raise;
}
blklen = chunk;
@@ -281,6 +234,7 @@ if((eh->frag[0] & (IP_DF>>8)) && !gating) print("%V: DF set\n", eh->dst);
feh->cksum[0] = 0;
feh->cksum[1] = 0;
hnputs(feh->cksum, ipcsum(&feh->vihl));
+
ipifcoput(ifc, nb, V4, gate);
ip->stats[FragCreates]++;
}
@@ -296,16 +250,14 @@ free:
void
ipiput4(Fs *f, Ipifc *ifc, Block *bp)
{
- int hl;
- int hop, tos, proto, olen;
+ int hl, len, hop, tos, proto;
+ uchar v6dst[IPaddrlen];
+ ushort frag;
Ip4hdr *h;
Proto *p;
- ushort frag;
- int notforme;
- uchar *dp, v6dst[IPaddrlen];
IP *ip;
- if(BLKIPVER(bp) != IP_VER4) {
+ if((bp->rp[0]&0xF0) != IP_VER4) {
ipiput6(f, ifc, bp);
return;
}
@@ -329,39 +281,31 @@ ipiput4(Fs *f, Ipifc *ifc, Block *bp)
return;
}
- h = (Ip4hdr*)(bp->rp);
-
- /* dump anything that whose header doesn't checksum */
+ h = (Ip4hdr*)bp->rp;
+ hl = (h->vihl & 0xF)<<2;
+ if(hl < IP4HDR || hl > BLEN(bp)) {
+ ip->stats[InHdrErrors]++;
+ netlog(f, Logip, "%V -> %V: bad ip header length: %d\n", h->src, h->dst, hl);
+ goto drop;
+ }
if((bp->flag & Bipck) == 0 && ipcsum(&h->vihl)) {
ip->stats[InHdrErrors]++;
- netlog(f, Logip, "ip: checksum error %V\n", h->src);
+ netlog(f, Logip, "%V -> %V: bad ip header checksum\n", h->src, h->dst);
goto drop;
}
- v4tov6(v6dst, h->dst);
- notforme = ipforme(f, v6dst) == 0;
-
- /* Check header length and version */
- if((h->vihl&0x0F) != IP_HLEN4) {
- hl = (h->vihl&0xF)<<2;
- if(hl < (IP_HLEN4<<2)) {
- ip->stats[InHdrErrors]++;
- netlog(f, Logip, "ip: %V bad hivl %ux\n", h->src, h->vihl);
+ len = nhgets(h->length);
+ if(len < hl || (bp = trimblock(bp, 0, len)) == nil){
+ ip->stats[InHdrErrors]++;
+ netlog(f, Logip, "%V -> %V: bogus packet length: %d\n", h->src, h->dst, len);
+ if(bp != nil)
goto drop;
- }
- /* If this is not routed strip off the options */
- if(notforme == 0) {
- olen = nhgets(h->length);
- dp = bp->rp + (hl - (IP_HLEN4<<2));
- memmove(dp, h, IP_HLEN4<<2);
- bp->rp = dp;
- h = (Ip4hdr*)(bp->rp);
- h->vihl = (IP_VER4|IP_HLEN4);
- hnputs(h->length, olen-hl+(IP_HLEN4<<2));
- }
+ return;
}
+ h = (Ip4hdr*)bp->rp;
/* route */
- if(notforme) {
+ v4tov6(v6dst, h->dst);
+ if(!ipforme(f, v6dst)) {
Route *r;
Routehint rh;
Ipifc *nifc;
@@ -393,10 +337,10 @@ ipiput4(Fs *f, Ipifc *ifc, Block *bp)
h->tos = 0;
if(frag & IP_MF)
h->tos = 1;
- bp = ip4reassemble(ip, frag, bp, h);
+ bp = ip4reassemble(ip, frag, bp);
if(bp == nil)
return;
- h = (Ip4hdr*)(bp->rp);
+ h = (Ip4hdr*)bp->rp;
}
}
@@ -407,15 +351,26 @@ ipiput4(Fs *f, Ipifc *ifc, Block *bp)
return;
}
+ /* If this is not routed strip off the options */
+ 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_DF) {
h->tos = 0;
if(frag & IP_MF)
h->tos = 1;
- bp = ip4reassemble(ip, frag, bp, h);
+ bp = ip4reassemble(ip, frag, bp);
if(bp == nil)
return;
- h = (Ip4hdr*)(bp->rp);
+ h = (Ip4hdr*)bp->rp;
}
/* don't let any frag info go up the stack */
@@ -450,27 +405,30 @@ ipstats(Fs *f, char *buf, int len)
return p - buf;
}
-Block*
-ip4reassemble(IP *ip, int offset, Block *bp, Ip4hdr *ih)
+static Block*
+ip4reassemble(IP *ip, int offset, Block *bp)
{
int fend;
ushort id;
Fragment4 *f, *fnext;
+ Ip4hdr *ih;
ulong src, dst;
- Block *bl, **l, *last, *prev;
+ Ipfrag *fp, *fq;
+ Block *bl, **l, *prev;
int ovlap, len, fragsize, pktposn;
- src = nhgetl(ih->src);
- dst = nhgetl(ih->dst);
- id = nhgets(ih->id);
-
/*
* block lists are too hard, pullupblock into a single block
*/
- if(bp->next != nil){
+ if(bp->next != nil)
bp = pullupblock(bp, blocklen(bp));
- ih = (Ip4hdr*)(bp->rp);
- }
+
+ ih = (Ip4hdr*)bp->rp;
+ src = nhgetl(ih->src);
+ dst = nhgetl(ih->dst);
+ id = nhgets(ih->id);
+ len = nhgets(ih->length);
+ fragsize = len - ((ih->vihl&0xF)<<2);
qlock(&ip->fraglock4);
@@ -492,22 +450,24 @@ ip4reassemble(IP *ip, int offset, Block *bp, Ip4hdr *ih)
* and get rid of any fragments that might go
* with it.
*/
- if(!ih->tos && (offset & ~(IP_MF|IP_DF)) == 0) {
+ if(ih->tos == 0 && (offset & ~(IP_MF|IP_DF)) == 0) {
if(f != nil) {
- ipfragfree4(ip, f);
ip->stats[ReasmFails]++;
+ ipfragfree4(ip, f);
}
qunlock(&ip->fraglock4);
return bp;
}
- if(bp->base+IPFRAGSZ >= bp->rp){
+ if(bp->base+IPFRAGSZ > bp->rp){
bp = padblock(bp, IPFRAGSZ);
bp->rp += IPFRAGSZ;
}
- BKFG(bp)->foff = offset<<3;
- BKFG(bp)->flen = nhgets(ih->length)-IP4HDR;
+ fp = (Ipfrag*)bp->base;
+ fp->foff = (offset & 0x1fff)<<3;
+ fp->flen = fragsize;
+ fp->hlen = len - fragsize;
/* First fragment allocates a reassembly queue */
if(f == nil) {
@@ -518,8 +478,9 @@ ip4reassemble(IP *ip, int offset, Block *bp, Ip4hdr *ih)
f->blist = bp;
- qunlock(&ip->fraglock4);
ip->stats[ReasmReqds]++;
+ qunlock(&ip->fraglock4);
+
return nil;
}
@@ -529,7 +490,7 @@ ip4reassemble(IP *ip, int offset, Block *bp, Ip4hdr *ih)
prev = nil;
l = &f->blist;
bl = f->blist;
- while(bl != nil && BKFG(bp)->foff > BKFG(bl)->foff) {
+ while(bl != nil && fp->foff > ((Ipfrag*)bl->base)->foff) {
prev = bl;
l = &bl->next;
bl = bl->next;
@@ -537,14 +498,15 @@ ip4reassemble(IP *ip, int offset, Block *bp, Ip4hdr *ih)
/* Check overlap of a previous fragment - trim away as necessary */
if(prev != nil) {
- ovlap = BKFG(prev)->foff + BKFG(prev)->flen - BKFG(bp)->foff;
+ fq = (Ipfrag*)prev->base;
+ ovlap = fq->foff + fq->flen - fp->foff;
if(ovlap > 0) {
- if(ovlap >= BKFG(bp)->flen) {
- freeblist(bp);
+ if(ovlap >= fp->flen) {
qunlock(&ip->fraglock4);
+ freeb(bp);
return nil;
}
- BKFG(prev)->flen -= ovlap;
+ fq->flen -= ovlap;
}
}
@@ -555,24 +517,24 @@ ip4reassemble(IP *ip, int offset, Block *bp, Ip4hdr *ih)
/* Check to see if succeeding segments overlap */
if(bp->next != nil) {
l = &bp->next;
- fend = BKFG(bp)->foff + BKFG(bp)->flen;
+ fend = fp->foff + fp->flen;
/* Take completely covered segments out */
- while(*l != nil) {
- ovlap = fend - BKFG(*l)->foff;
+ while((bl = *l) != nil) {
+ fq = (Ipfrag*)bl->base;
+ ovlap = fend - fq->foff;
if(ovlap <= 0)
break;
- if(ovlap < BKFG(*l)->flen) {
- BKFG(*l)->flen -= ovlap;
- BKFG(*l)->foff += ovlap;
- /* move up ih hdrs */
- memmove((*l)->rp + ovlap, (*l)->rp, IP4HDR);
- (*l)->rp += ovlap;
+ if(ovlap < fq->flen) {
+ fq->flen -= ovlap;
+ fq->foff += ovlap;
+ /* move up ip header */
+ memmove(bl->rp + ovlap, bl->rp, fq->hlen);
+ bl->rp += ovlap;
break;
}
- last = (*l)->next;
- (*l)->next = nil;
- freeblist(*l);
- *l = last;
+ *l = bl->next;
+ bl->next = nil;
+ freeb(bl);
}
}
@@ -581,34 +543,50 @@ ip4reassemble(IP *ip, int offset, Block *bp, Ip4hdr *ih)
* without IP_MF set, we're done.
*/
pktposn = 0;
- for(bl = f->blist; bl != nil; bl = bl->next) {
- if(BKFG(bl)->foff != pktposn)
+ for(bl = f->blist; bl != nil; bl = bl->next, pktposn += fp->flen) {
+ fp = (Ipfrag*)bl->base;
+ if(fp->foff != pktposn)
break;
- if((BLKIP(bl)->frag[0]&(IP_MF>>8)) == 0) {
- bl = f->blist;
- len = nhgets(BLKIP(bl)->length);
- bl->wp = bl->rp + len;
-
- /* Pullup all the fragment headers and
- * return a complete packet
- */
- for(bl = bl->next; bl != nil; bl = bl->next) {
- fragsize = BKFG(bl)->flen;
- len += fragsize;
- bl->rp += IP4HDR;
- bl->wp = bl->rp + fragsize;
- }
- bl = f->blist;
- f->blist = nil;
+ ih = (Ip4hdr*)bl->rp;
+ if(ih->frag[0]&(IP_MF>>8))
+ continue;
+
+ bl = f->blist;
+ fq = (Ipfrag*)bl->base;
+ len = fq->hlen + fq->flen;
+ bl->wp = bl->rp + len;
+
+ /*
+ * Pullup all the fragment headers and
+ * return a complete packet
+ */
+ for(bl = bl->next; bl != nil && len < IP_MAX; bl = bl->next) {
+ fq = (Ipfrag*)bl->base;
+ fragsize = fq->flen;
+ len += fragsize;
+ bl->rp += fq->hlen;
+ bl->wp = bl->rp + fragsize;
+ }
+
+ if(len >= IP_MAX){
ipfragfree4(ip, f);
- ih = BLKIP(bl);
- hnputs(ih->length, len);
+ ip->stats[ReasmFails]++;
qunlock(&ip->fraglock4);
- ip->stats[ReasmOKs]++;
- return bl;
+ return nil;
}
- pktposn += BKFG(bl)->flen;
+
+ bl = f->blist;
+ f->blist = nil;
+ ipfragfree4(ip, f);
+
+ ih = (Ip4hdr*)bl->rp;
+ hnputs(ih->length, len);
+
+ ip->stats[ReasmOKs]++;
+ qunlock(&ip->fraglock4);
+
+ return bl;
}
qunlock(&ip->fraglock4);
return nil;
@@ -617,7 +595,7 @@ ip4reassemble(IP *ip, int offset, Block *bp, Ip4hdr *ih)
/*
* ipfragfree4 - Free a list of fragments - assume hold fraglock4
*/
-void
+static void
ipfragfree4(IP *ip, Fragment4 *frag)
{
Fragment4 *fl, **l;
@@ -646,7 +624,7 @@ ipfragfree4(IP *ip, Fragment4 *frag)
/*
* ipfragallo4 - allocate a reassembly queue - assume hold fraglock4
*/
-Fragment4 *
+static Fragment4*
ipfragallo4(IP *ip)
{
Fragment4 *f;
diff --git a/sys/src/9/ip/ip.h b/sys/src/9/ip/ip.h
index a1233ec46..017a8ad2f 100644
--- a/sys/src/9/ip/ip.h
+++ b/sys/src/9/ip/ip.h
@@ -57,7 +57,7 @@ enum
IP_HLEN4= 5, /* v4: Header length in words */
IP_DF= 0x4000, /* v4: Don't fragment */
IP_MF= 0x2000, /* v4: More fragments */
- IP4HDR= 20, /* sizeof(Ip4hdr) */
+ IP4HDR= IP_HLEN4<<2, /* sizeof(Ip4hdr) */
IP_MAX= 64*1024, /* Max. Internet packet size, v4 & v6 */
/* 2^Lroot trees in the root table */
@@ -123,9 +123,9 @@ struct Fragment6
struct Ipfrag
{
+ ushort hlen;
ushort foff;
ushort flen;
-
uchar payload[];
};
@@ -693,6 +693,7 @@ extern int ipstats(Fs*, char*, int);
extern ushort ptclbsum(uchar*, int);
extern ushort ptclcsum(Block*, int, int);
extern void ip_init(Fs*);
+extern void ip_init_6(Fs*);
/*
* bootp.c
diff --git a/sys/src/9/ip/ipaux.c b/sys/src/9/ip/ipaux.c
index e16249546..549df1f6e 100644
--- a/sys/src/9/ip/ipaux.c
+++ b/sys/src/9/ip/ipaux.c
@@ -151,7 +151,7 @@ ptclcsum(Block *bp, int offset, int len)
if(bp->next == nil) {
if(blocklen < len)
len = blocklen;
- return ~ptclbsum(addr, len) & 0xffff;
+ return ptclbsum(addr, len) ^ 0xffff;
}
losum = 0;
@@ -183,7 +183,7 @@ ptclcsum(Block *bp, int offset, int len)
while((csum = losum>>16) != 0)
losum = csum + (losum & 0xffff);
- return ~losum & 0xffff;
+ return losum ^ 0xffff;
}
enum
diff --git a/sys/src/9/ip/ipv6.c b/sys/src/9/ip/ipv6.c
index 6b6d4eeca..3c29a935f 100644
--- a/sys/src/9/ip/ipv6.c
+++ b/sys/src/9/ip/ipv6.c
@@ -13,19 +13,34 @@ enum
IP6FHDR = 8, /* sizeof(Fraghdr6) */
};
-#define IPV6CLASS(hdr) (((hdr)->vcf[0]&0x0F)<<2 | ((hdr)->vcf[1]&0xF0)>>2)
-#define BLKIPVER(xp) (((Ip6hdr*)((xp)->rp))->vcf[0] & 0xF0)
-/*
- * This sleazy macro is stolen shamelessly from ip.c, see comment there.
- */
-#define BKFG(xp) ((Ipfrag*)((xp)->base))
+static Block* ip6reassemble(IP*, int, Block*);
+static Fragment6* ipfragallo6(IP*);
+static void ipfragfree6(IP*, Fragment6*);
+static Block* procopts(Block *bp);
+static Block* procxtns(IP *ip, Block *bp, int doreasm);
+static int unfraglen(Block *bp, uchar *nexthdr, int setfh);
+
+void
+ip_init_6(Fs *f)
+{
+ v6params *v6p;
+
+ v6p = smalloc(sizeof(v6params));
-Block* ip6reassemble(IP*, int, Block*, Ip6hdr*);
-Fragment6* ipfragallo6(IP*);
-void ipfragfree6(IP*, Fragment6*);
-Block* procopts(Block *bp);
-static Block* procxtns(IP *ip, Block *bp, int doreasm);
-int unfraglen(Block *bp, uchar *nexthdr, int setfh);
+ v6p->rp.mflag = 0; /* default not managed */
+ v6p->rp.oflag = 0;
+ v6p->rp.maxraint = 600000; /* millisecs */
+ v6p->rp.minraint = 200000;
+ v6p->rp.linkmtu = 0; /* no mtu sent */
+ v6p->rp.reachtime = 0;
+ v6p->rp.rxmitra = 0;
+ v6p->rp.ttl = MAXTTL;
+ v6p->rp.routerlt = (3 * v6p->rp.maxraint) / 1000;
+
+ v6p->hp.rxmithost = 1000; /* v6 RETRANS_TIMER */
+
+ f->v6p = v6p;
+}
int
ipoput6(Fs *f, Block *bp, int gating, int ttl, int tos, Routehint *rh)
@@ -41,36 +56,22 @@ ipoput6(Fs *f, Block *bp, int gating, int ttl, int tos, Routehint *rh)
Route *r;
ip = f->ip;
-
- /* Fill out the ip header */
- eh = (Ip6hdr*)(bp->rp);
-
ip->stats[OutRequests]++;
- /* Number of uchars in data and ip header to write */
+ /* Fill out the ip header */
+ eh = (Ip6hdr*)bp->rp;
+ assert(BLEN(bp) >= IP6HDR);
len = blocklen(bp);
-
- if(gating){
- chunk = nhgets(eh->ploadlen);
- if(chunk > len){
- ip->stats[OutDiscards]++;
- netlog(f, Logip, "short gated packet\n");
- goto free;
- }
- if(chunk + IP6HDR < len)
- len = chunk + IP6HDR;
- }
-
if(len >= IP_MAX){
ip->stats[OutDiscards]++;
- netlog(f, Logip, "exceeded ip max size %I\n", eh->dst);
+ netlog(f, Logip, "%I -> %I: exceeded ip max size: %d\n", eh->src, eh->dst, len);
goto free;
}
r = v6lookup(f, eh->dst, eh->src, rh);
if(r == nil || (r->type & Rv4) != 0 || (ifc = r->ifc) == nil){
ip->stats[OutNoRoutes]++;
- netlog(f, Logip, "no interface %I -> %I\n", eh->src, eh->dst);
+ netlog(f, Logip, "%I -> %I: no interface\n", eh->src, eh->dst);
rv = -1;
goto free;
}
@@ -80,14 +81,6 @@ ipoput6(Fs *f, Block *bp, int gating, int ttl, int tos, Routehint *rh)
else
gate = r->v6.gate;
- if(!gating)
- eh->vcf[0] = IP_VER6;
- eh->ttl = ttl;
- if(!gating) {
- eh->vcf[0] |= tos >> 4;
- eh->vcf[1] = tos << 4;
- }
-
if(!canrlock(ifc))
goto free;
@@ -99,6 +92,13 @@ ipoput6(Fs *f, Block *bp, int gating, int ttl, int tos, Routehint *rh)
if(ifc->m == nil)
goto raise;
+ if(!gating){
+ eh->vcf[0] = IP_VER6;
+ eh->vcf[0] |= tos >> 4;
+ eh->vcf[1] = tos << 4;
+ }
+ eh->ttl = ttl;
+
/* If we dont need to fragment just send it */
medialen = ifc->maxtu - ifc->m->hsize;
if(len <= medialen) {
@@ -117,16 +117,16 @@ ipoput6(Fs *f, Block *bp, int gating, int ttl, int tos, Routehint *rh)
*/
ip->stats[OutDiscards]++;
icmppkttoobig6(f, ifc, bp);
- netlog(f, Logip, "%I: gated pkts not fragmented\n", eh->dst);
+ netlog(f, Logip, "%I -> %I: gated pkts not fragmented\n", eh->src, eh->dst);
goto raise;
}
/* start v6 fragmentation */
uflen = unfraglen(bp, &nexthdr, 1);
- if(uflen > medialen) {
+ if(uflen < 0 || uflen > medialen) {
ip->stats[FragFails]++;
ip->stats[OutDiscards]++;
- netlog(f, Logip, "%I: unfragmentable part too big\n", eh->dst);
+ netlog(f, Logip, "%I -> %I: unfragmentable part too big: %d\n", eh->src, eh->dst, uflen);
goto raise;
}
@@ -135,7 +135,7 @@ ipoput6(Fs *f, Block *bp, int gating, int ttl, int tos, Routehint *rh)
if(seglen < 8) {
ip->stats[FragFails]++;
ip->stats[OutDiscards]++;
- netlog(f, Logip, "%I: seglen < 8\n", eh->dst);
+ netlog(f, Logip, "%I -> %I: seglen < 8\n", eh->src, eh->dst);
goto raise;
}
@@ -175,11 +175,11 @@ ipoput6(Fs *f, Block *bp, int gating, int ttl, int tos, Routehint *rh)
/* Copy data */
chunk = seglen;
while (chunk) {
- if(!xp) {
+ if(xp == nil) {
ip->stats[OutDiscards]++;
ip->stats[FragFails]++;
freeblist(nb);
- netlog(f, Logip, "!xp: chunk in v6%d\n", chunk);
+ netlog(f, Logip, "xp == nil: chunk in v6%d\n", chunk);
goto raise;
}
blklen = chunk;
@@ -209,7 +209,7 @@ free:
void
ipiput6(Fs *f, Ipifc *ifc, Block *bp)
{
- int hl, hop, tos;
+ int hl, len, hop, tos;
uchar proto;
IP *ip;
Ip6hdr *h;
@@ -235,15 +235,22 @@ ipiput6(Fs *f, Ipifc *ifc, Block *bp)
}
/* Check header version */
- h = (Ip6hdr *)bp->rp;
- if(BLKIPVER(bp) != IP_VER6) {
+ h = (Ip6hdr*)bp->rp;
+ if((h->vcf[0] & 0xF0) != IP_VER6) {
ip->stats[InHdrErrors]++;
netlog(f, Logip, "ip: bad version %ux\n", (h->vcf[0]&0xF0)>>2);
goto drop;
}
+ len = IP6HDR + nhgets(h->ploadlen);
+ if((bp = trimblock(bp, 0, len)) == nil){
+ ip->stats[InHdrErrors]++;
+ netlog(f, Logip, "%I -> %I: bogus packet length: %d\n", h->src, h->dst, len);
+ return;
+ }
+ h = (Ip6hdr*)bp->rp;
/* route */
- if(ipforme(f, h->dst) == 0) {
+ if(!ipforme(f, h->dst)) {
Route *r;
Routehint rh;
Ipifc *nifc;
@@ -281,8 +288,8 @@ ipiput6(Fs *f, Ipifc *ifc, Block *bp)
return;
ip->stats[ForwDatagrams]++;
- h = (Ip6hdr *)bp->rp;
- tos = IPV6CLASS(h);
+ 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);
return;
@@ -293,7 +300,7 @@ ipiput6(Fs *f, Ipifc *ifc, Block *bp)
if(bp == nil)
return;
- h = (Ip6hdr *) (bp->rp);
+ h = (Ip6hdr*)bp->rp;
proto = h->proto;
p = Fsrcvpcol(f, proto);
if(p != nil && p->rcv != nil) {
@@ -311,7 +318,7 @@ drop:
/*
* ipfragfree6 - copied from ipfragfree4 - assume hold fraglock6
*/
-void
+static void
ipfragfree6(IP *ip, Fragment6 *frag)
{
Fragment6 *fl, **l;
@@ -339,7 +346,7 @@ ipfragfree6(IP *ip, Fragment6 *frag)
/*
* ipfragallo6 - copied from ipfragalloc4
*/
-Fragment6*
+static Fragment6*
ipfragallo6(IP *ip)
{
Fragment6 *f;
@@ -362,20 +369,22 @@ ipfragallo6(IP *ip)
static Block*
procxtns(IP *ip, Block *bp, int doreasm)
{
- int offset;
uchar proto;
- Ip6hdr *h;
+ int offset;
- h = (Ip6hdr *)bp->rp;
offset = unfraglen(bp, &proto, 0);
-
- if(proto == FH && doreasm != 0) {
- bp = ip6reassemble(ip, offset, bp, h);
+ if(offset >= 0 && proto == FH && doreasm != 0) {
+ bp = ip6reassemble(ip, offset, bp);
if(bp == nil)
return nil;
offset = unfraglen(bp, &proto, 0);
+ if(proto == FH)
+ offset = -1;
+ }
+ if(offset < 0){
+ freeblist(bp);
+ return nil;
}
-
if(proto == DOH || offset > IP6HDR)
bp = procopts(bp);
return bp;
@@ -386,63 +395,69 @@ procxtns(IP *ip, Block *bp, int doreasm)
* hop-by-hop & routing headers if present; *nexthdr is set to nexthdr value
* of the last header in the "Unfragmentable part"; if setfh != 0, nexthdr
* field of the last header in the "Unfragmentable part" is set to FH.
+ * returns -1 on error.
*/
-int
+static int
unfraglen(Block *bp, uchar *nexthdr, int setfh)
{
- uchar *p, *q;
- int ufl, hs;
+ uchar *e, *p, *q;
+ e = bp->wp;
p = bp->rp;
q = p+6; /* proto, = p+sizeof(Ip6hdr.vcf)+sizeof(Ip6hdr.ploadlen) */
*nexthdr = *q;
- ufl = IP6HDR;
- p += ufl;
-
- while (*nexthdr == HBH || *nexthdr == RH) {
- *nexthdr = *p;
- hs = ((int)*(p+1) + 1) * 8;
- ufl += hs;
+ p += IP6HDR;
+ while(*nexthdr == HBH || *nexthdr == RH){
+ if(p+2 > e)
+ return -1;
q = p;
- p += hs;
+ *nexthdr = *q;
+ p += ((int)p[1] + 1) * 8;
}
-
- if(*nexthdr == FH)
+ if(p > e)
+ return -1;
+ if(*nexthdr == FH){
+ if(setfh || p+IP6FHDR > e || *p == FH)
+ return -1;
*q = *p;
- if(setfh)
+ } else if(setfh)
*q = FH;
- return ufl;
+ return p - bp->rp;
}
-Block*
+static Block*
procopts(Block *bp)
{
return bp;
}
-Block*
-ip6reassemble(IP* ip, int uflen, Block* bp, Ip6hdr* ih)
+static Block*
+ip6reassemble(IP* ip, int uflen, Block* bp)
{
int fend, offset, ovlap, len, fragsize, pktposn;
uint id;
uchar src[IPaddrlen], dst[IPaddrlen];
- Block *bl, **l, *last, *prev;
+ Block *bl, **l, *prev;
Fraghdr6 *fraghdr;
Fragment6 *f, *fnext;
-
- fraghdr = (Fraghdr6 *)(bp->rp + uflen);
- memmove(src, ih->src, IPaddrlen);
- memmove(dst, ih->dst, IPaddrlen);
- id = nhgetl(fraghdr->id);
- offset = nhgets(fraghdr->offsetRM) & ~7;
+ Ipfrag *fp, *fq;
+ Ip6hdr* ih;
/*
* block lists are too hard, pullupblock into a single block
*/
- if(bp->next != nil){
+ if(bp->next != nil)
bp = pullupblock(bp, blocklen(bp));
- ih = (Ip6hdr *)bp->rp;
- }
+
+ ih = (Ip6hdr*)bp->rp;
+ fraghdr = (Fraghdr6*)(bp->rp + uflen);
+ id = nhgetl(fraghdr->id);
+ offset = nhgets(fraghdr->offsetRM);
+ len = nhgets(ih->ploadlen);
+ fragsize = (len + IP6HDR) - (uflen + IP6FHDR);
+
+ memmove(src, ih->src, IPaddrlen);
+ memmove(dst, ih->dst, IPaddrlen);
qlock(&ip->fraglock6);
@@ -451,7 +466,7 @@ ip6reassemble(IP* ip, int uflen, Block* bp, Ip6hdr* ih)
*/
for(f = ip->flisthead6; f != nil; f = fnext){
fnext = f->next;
- if(ipcmp(f->src, src)==0 && ipcmp(f->dst, dst)==0 && f->id == id)
+ if(ipcmp(f->src, src) == 0 && ipcmp(f->dst, dst) == 0 && f->id == id)
break;
if(f->age < NOW){
ip->stats[ReasmTimeout]++;
@@ -464,22 +479,30 @@ ip6reassemble(IP* ip, int uflen, Block* bp, Ip6hdr* ih)
* and get rid of any fragments that might go
* with it.
*/
- if(nhgets(fraghdr->offsetRM) == 0) { /* 1st frag is also last */
- if(f) {
- ipfragfree6(ip, f);
+ if(offset == 0) { /* 1st frag is also last */
+ if(f != nil) {
ip->stats[ReasmFails]++;
+ ipfragfree6(ip, f);
}
qunlock(&ip->fraglock6);
+
+ /* get rid of frag header */
+ memmove(bp->rp + IP6FHDR, bp->rp, uflen);
+ bp->rp += IP6FHDR;
+ hnputs(ih->ploadlen, len-IP6FHDR);
+
return bp;
}
- if(bp->base+IPFRAGSZ >= bp->rp){
+ if(bp->base+IPFRAGSZ > bp->rp){
bp = padblock(bp, IPFRAGSZ);
bp->rp += IPFRAGSZ;
}
- BKFG(bp)->foff = offset;
- BKFG(bp)->flen = nhgets(ih->ploadlen) + IP6HDR - uflen - IP6FHDR;
+ fp = (Ipfrag*)bp->base;
+ fp->hlen = uflen;
+ fp->foff = offset & ~7;
+ fp->flen = fragsize;
/* First fragment allocates a reassembly queue */
if(f == nil) {
@@ -490,8 +513,9 @@ ip6reassemble(IP* ip, int uflen, Block* bp, Ip6hdr* ih)
f->blist = bp;
- qunlock(&ip->fraglock6);
ip->stats[ReasmReqds]++;
+ qunlock(&ip->fraglock6);
+
return nil;
}
@@ -501,7 +525,7 @@ ip6reassemble(IP* ip, int uflen, Block* bp, Ip6hdr* ih)
prev = nil;
l = &f->blist;
bl = f->blist;
- while(bl != nil && BKFG(bp)->foff > BKFG(bl)->foff) {
+ while(bl != nil && fp->foff > ((Ipfrag*)bl->base)->foff) {
prev = bl;
l = &bl->next;
bl = bl->next;
@@ -509,14 +533,15 @@ ip6reassemble(IP* ip, int uflen, Block* bp, Ip6hdr* ih)
/* Check overlap of a previous fragment - trim away as necessary */
if(prev != nil) {
- ovlap = BKFG(prev)->foff + BKFG(prev)->flen - BKFG(bp)->foff;
+ fq = (Ipfrag*)prev->base;
+ ovlap = fq->foff + fq->flen - fp->foff;
if(ovlap > 0) {
- if(ovlap >= BKFG(bp)->flen) {
- freeblist(bp);
+ if(ovlap >= fp->flen) {
qunlock(&ip->fraglock6);
+ freeb(bp);
return nil;
}
- BKFG(prev)->flen -= ovlap;
+ fq->flen -= ovlap;
}
}
@@ -527,25 +552,25 @@ ip6reassemble(IP* ip, int uflen, Block* bp, Ip6hdr* ih)
/* Check to see if succeeding segments overlap */
if(bp->next != nil) {
l = &bp->next;
- fend = BKFG(bp)->foff + BKFG(bp)->flen;
+ fend = fp->foff + fp->flen;
/* Take completely covered segments out */
- while(*l != nil) {
- ovlap = fend - BKFG(*l)->foff;
+ while((bl = *l) != nil) {
+ fq = (Ipfrag*)bl->base;
+ ovlap = fend - fq->foff;
if(ovlap <= 0)
break;
- if(ovlap < BKFG(*l)->flen) {
- BKFG(*l)->flen -= ovlap;
- BKFG(*l)->foff += ovlap;
- /* move up ih hdrs */
- memmove((*l)->rp + ovlap, (*l)->rp, uflen);
- (*l)->rp += ovlap;
+ if(ovlap < fq->flen) {
+ fq->flen -= ovlap;
+ fq->foff += ovlap;
+ /* move up ip and frag header */
+ memmove(bl->rp + ovlap, bl->rp, fq->hlen + IP6FHDR);
+ bl->rp += ovlap;
break;
}
- last = (*l)->next;
- (*l)->next = nil;
- freeblist(*l);
- *l = last;
+ *l = bl->next;
+ bl->next = nil;
+ freeb(bl);
}
}
@@ -554,37 +579,56 @@ ip6reassemble(IP* ip, int uflen, Block* bp, Ip6hdr* ih)
* with the trailing bit of fraghdr->offsetRM[1] set, we're done.
*/
pktposn = 0;
- for(bl = f->blist; bl != nil && BKFG(bl)->foff == pktposn; bl = bl->next) {
- fraghdr = (Fraghdr6 *)(bl->rp + uflen);
- if((fraghdr->offsetRM[1] & 1) == 0) {
- bl = f->blist;
-
- /* get rid of frag header in first fragment */
- memmove(bl->rp + IP6FHDR, bl->rp, uflen);
- bl->rp += IP6FHDR;
- len = nhgets(((Ip6hdr*)bl->rp)->ploadlen) - IP6FHDR;
- bl->wp = bl->rp + len + IP6HDR;
- /*
- * Pullup all the fragment headers and
- * return a complete packet
- */
- for(bl = bl->next; bl != nil; bl = bl->next) {
- fragsize = BKFG(bl)->flen;
- len += fragsize;
- bl->rp += uflen + IP6FHDR;
- bl->wp = bl->rp + fragsize;
- }
+ for(bl = f->blist; bl != nil; bl = bl->next, pktposn += fp->flen) {
+ fp = (Ipfrag*)bl->base;
+ if(fp->foff != pktposn)
+ break;
+
+ fraghdr = (Fraghdr6*)(bl->rp + fp->hlen);
+ if(fraghdr->offsetRM[1] & 1)
+ continue;
+
+ bl = f->blist;
+ fq = (Ipfrag*)bl->base;
+
+ /* get rid of frag header in first fragment */
+ memmove(bl->rp + IP6FHDR, bl->rp, fq->hlen);
+ bl->rp += IP6FHDR;
+ len = fq->hlen + fq->flen;
+ bl->wp = bl->rp + len;
+
+ /*
+ * Pullup all the fragment headers and
+ * return a complete packet
+ */
+ for(bl = bl->next; bl != nil && len < IP_MAX; bl = bl->next) {
+ fq = (Ipfrag*)bl->base;
+ fragsize = fq->flen;
+ len += fragsize;
+ bl->rp += fq->hlen + IP6FHDR;
+ bl->wp = bl->rp + fragsize;
+ }
- bl = f->blist;
- f->blist = nil;
+ if(len >= IP_MAX){
ipfragfree6(ip, f);
- ih = (Ip6hdr*)bl->rp;
- hnputs(ih->ploadlen, len);
+
+ ip->stats[ReasmFails]++;
qunlock(&ip->fraglock6);
- ip->stats[ReasmOKs]++;
- return bl;
+
+ return nil;
}
- pktposn += BKFG(bl)->flen;
+
+ bl = f->blist;
+ f->blist = nil;
+ ipfragfree6(ip, f);
+
+ ih = (Ip6hdr*)bl->rp;
+ hnputs(ih->ploadlen, len-IP6HDR);
+
+ ip->stats[ReasmOKs]++;
+ qunlock(&ip->fraglock6);
+
+ return bl;
}
qunlock(&ip->fraglock6);
return nil;