ref: 5b972a9aea9383fbb66142a6a9958e01f7028a89
parent: 0aac600fb3b659b5b9a2a8aaefb821774cf38bd2
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Sun Mar 3 00:25:00 EST 2019
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.
--- 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 @@
[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))
+static Block* ip4reassemble(IP*, int, Block*);
+static void ipfragfree4(IP*, Fragment4*);
+static Fragment4* ipfragallo4(IP*);
-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 */
-
- f->v6p = v6p;
-}
-
-void
+static void
initfrag(IP *ip, int size)
{
Fragment4 *fq4, *eq4;
@@ -124,34 +92,21 @@
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;
}
@@ -158,7 +113,7 @@
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,12 +123,6 @@
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()){
@@ -180,17 +129,21 @@
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;
}
@@ -197,6 +150,7 @@
eh->cksum[0] = 0;
eh->cksum[1] = 0;
hnputs(eh->cksum, ipcsum(&eh->vihl));
+
ipifcoput(ifc, bp, V4, gate);
runlock(ifc);
poperror();
@@ -203,25 +157,24 @@
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);
@@ -228,7 +181,7 @@
else
lid = incref(&ip->id4);
- offset = IP4HDR;
+ offset = hlen;
while(offset && offset >= BLEN(xp)) {
offset -= BLEN(xp);
xp = xp->next;
@@ -241,11 +194,11 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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,28 +405,31 @@
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 @@
* 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 @@
f->blist = bp;
- qunlock(&ip->fraglock4);
ip->stats[ReasmReqds]++;
+ qunlock(&ip->fraglock4);
+
return nil;
}
@@ -529,7 +490,7 @@
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 @@
/* 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 @@
/* 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 @@
* 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;
- }
+ ih = (Ip4hdr*)bl->rp;
+ if(ih->frag[0]&(IP_MF>>8))
+ continue;
- bl = f->blist;
- f->blist = nil;
+ 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 @@
/*
* ipfragfree4 - Free a list of fragments - assume hold fraglock4
*/
-void
+static void
ipfragfree4(IP *ip, Fragment4 *frag)
{
Fragment4 *fl, **l;
@@ -646,7 +624,7 @@
/*
* ipfragallo4 - allocate a reassembly queue - assume hold fraglock4
*/
-Fragment4 *
+static Fragment4*
ipfragallo4(IP *ip)
{
Fragment4 *f;
--- a/sys/src/9/ip/ip.h
+++ b/sys/src/9/ip/ip.h
@@ -57,7 +57,7 @@
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 Ipfrag
{
+ ushort hlen;
ushort foff;
ushort flen;
-
uchar payload[];
};
@@ -693,6 +693,7 @@
extern ushort ptclbsum(uchar*, int);
extern ushort ptclcsum(Block*, int, int);
extern void ip_init(Fs*);
+extern void ip_init_6(Fs*);
/*
* bootp.c
--- a/sys/src/9/ip/ipaux.c
+++ b/sys/src/9/ip/ipaux.c
@@ -151,7 +151,7 @@
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 @@
while((csum = losum>>16) != 0)
losum = csum + (losum & 0xffff);
- return ~losum & 0xffff;
+ return losum ^ 0xffff;
}
enum
--- a/sys/src/9/ip/ipv6.c
+++ b/sys/src/9/ip/ipv6.c
@@ -13,20 +13,35 @@
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);
-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);
+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 */
+
+ f->v6p = v6p;
+}
+
int
ipoput6(Fs *f, Block *bp, int gating, int ttl, int tos, Routehint *rh)
{
@@ -41,29 +56,15 @@
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;
}
@@ -70,7 +71,7 @@
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 @@
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 @@
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 @@
*/
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 @@
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 @@
/* 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 @@
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 @@
}
/* 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 @@
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 @@
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 @@
/*
* ipfragfree6 - copied from ipfragfree4 - assume hold fraglock6
*/
-void
+static void
ipfragfree6(IP *ip, Fragment6 *frag)
{
Fragment6 *fl, **l;
@@ -339,7 +346,7 @@
/*
* ipfragallo6 - copied from ipfragalloc4
*/
-Fragment6*
+static Fragment6*
ipfragallo6(IP *ip)
{
Fragment6 *f;
@@ -362,20 +369,22 @@
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,64 +395,70 @@
* 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;
+ Ipfrag *fp, *fq;
+ Ip6hdr* ih;
- fraghdr = (Fraghdr6 *)(bp->rp + uflen);
- memmove(src, ih->src, IPaddrlen);
- memmove(dst, ih->dst, IPaddrlen);
- id = nhgetl(fraghdr->id);
- offset = nhgets(fraghdr->offsetRM) & ~7;
-
/*
* 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 @@
*/
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 @@
* 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 @@
f->blist = bp;
- qunlock(&ip->fraglock6);
ip->stats[ReasmReqds]++;
+ qunlock(&ip->fraglock6);
+
return nil;
}
@@ -501,7 +525,7 @@
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 @@
/* 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 @@
/* 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 @@
* 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;
+ for(bl = f->blist; bl != nil; bl = bl->next, pktposn += fp->flen) {
+ fp = (Ipfrag*)bl->base;
+ if(fp->foff != pktposn)
+ break;
- /* 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;
- }
+ fraghdr = (Fraghdr6*)(bl->rp + fp->hlen);
+ if(fraghdr->offsetRM[1] & 1)
+ continue;
- bl = f->blist;
- f->blist = nil;
+ 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;
+ }
+
+ 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;