| plan 9 kernel history: overview | file list | diff list |
1998/0307/ip/ip.c (diff list | history)
| ip/ip.c on 1997/0327 | ||
| 1997/0327 | #include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "../port/error.h" #include "ip.h" typedef struct Iphdr Iphdr; typedef struct Fragment Fragment; typedef struct Ipfrag Ipfrag; enum { IPHDR = 20, /* sizeof(Iphdr) */ IP_VER = 0x40, /* Using IP version 4 */ IP_HLEN = 0x05, /* Header length in characters */ IP_DF = 0x4000, /* Don't fragment */ IP_MF = 0x2000, /* More fragments */ | |
| 1998/0306 | IP_MAX = (32*1024), /* Maximum Internet packet size */ | |
| 1997/0327 | }; struct Iphdr { | |
| 1998/0306 | uchar vihl; /* Version and header length */ uchar tos; /* Type of service */ uchar length[2]; /* packet length */ uchar id[2]; /* Identification */ uchar frag[2]; /* Fragment information */ uchar ttl; /* Time to live */ uchar proto; /* Protocol */ uchar cksum[2]; /* Header checksum */ uchar src[4]; /* Ip source */ uchar dst[4]; /* Ip destination */ | |
| 1997/0327 | }; struct Fragment { Block* blist; Fragment* next; | |
| 1998/0306 | ulong src; ulong dst; | |
| 1997/0327 | ushort id; ulong age; }; struct Ipfrag { ushort foff; ushort flen; }; | |
| 1997/0529 | QLock fraglock; | |
| 1997/0327 | Fragment* flisthead; Fragment* fragfree; ulong Id; int iprouting; /* true if we route like a gateway */ ulong ipcsumerr; | |
| 1998/0306 | ulong ipin, ippin; /* uchars, packets in */ ulong ipout, ippout; /* uchars, packets out */ | |
| 1997/0327 | #define BLKIP(xp) ((Iphdr*)((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)) | |
| 1997/0916 | static struct Stats { ulong noroute; ulong droppedfrag; } stats; | |
| 1998/0306 | ushort ipcsum(uchar*); | |
| 1997/0327 | Block* ipreassemble(int, Block*, Iphdr*); | |
| 1997/0529 | void ipfragfree(Fragment*); | |
| 1997/0327 | Fragment* ipfragallo(void); void ipoput(Block *bp, int gating, int ttl) { | |
| 1998/0306 | Ipifc *ifc; uchar *gate; | |
| 1997/0327 | ushort fragoff; Block *xp, *nb; Iphdr *eh, *feh; int lid, len, seglen, chunk, dlen, blklen, offset, medialen; | |
| 1998/0306 | Route *r; | |
| 1997/0327 | /* Fill out the ip header */ eh = (Iphdr *)(bp->rp); | |
| 1998/0306 | /* Number of uchars in data and ip header to write */ | |
| 1997/0327 | len = blocklen(bp); ipout += len; ippout++; if(gating){ chunk = nhgets(eh->length); if(chunk > len){ netlog(Logip, "short gated packet\n"); goto raise; } if(chunk < len) len = chunk; } | |
| 1998/0306 | if(len >= IP_MAX){ netlog(Logip, "exceeded ip max size %V\n", eh->dst); | |
| 1997/0327 | goto raise; } | |
| 1998/0306 | r = v4lookup(eh->dst); if(r == nil){ | |
| 1997/0916 | stats.noroute++; | |
| 1998/0306 | netlog(Logip, "no interface %V\n", eh->dst); | |
| 1997/0327 | goto raise; } | |
| 1998/0306 | if(r->type & (Rifc|Runi|Rbcast|Rmulti)) gate = eh->dst; else gate = r->v4.gate; | |
| 1997/0327 | if(!gating){ eh->vihl = IP_VER|IP_HLEN; eh->tos = 0; eh->ttl = ttl; } | |
| 1998/0306 | ifc = r->ifc; | |
| 1997/0327 | ||
| 1998/0307 | if(waserror()){ runlock(ifc); nexterror(); } rlock(ifc); if(ifc->m == nil) goto raise; | |
| 1997/0327 | /* If we dont need to fragment just send it */ | |
| 1998/0306 | medialen = ifc->m->maxmtu - ifc->m->hsize; | |
| 1997/0327 | if(len <= medialen) { if(!gating) hnputs(eh->id, Id++); hnputs(eh->length, len); eh->frag[0] = 0; eh->frag[1] = 0; eh->cksum[0] = 0; eh->cksum[1] = 0; hnputs(eh->cksum, ipcsum(&eh->vihl)); | |
| 1998/0306 | /*print("ipoput %V->%V via %V\n", eh->src, eh->dst, gate);*/ ifc->m->bwrite(ifc, bp, V4, gate); | |
| 1998/0307 | runlock(ifc); poperror(); | |
| 1997/0327 | return; } if(eh->frag[0] & (IP_DF>>8)){ | |
| 1998/0306 | netlog(Logip, "%V: eh->frag[0] & (IP_DF>>8)\n", eh->dst); | |
| 1997/0327 | goto raise; } seglen = (medialen - IPHDR) & ~7; if(seglen < 8){ | |
| 1998/0306 | netlog(Logip, "%V seglen < 8\n", eh->dst); | |
| 1997/0327 | goto raise; } dlen = len - IPHDR; xp = bp; if(gating) lid = nhgets(eh->id); else lid = Id++; offset = IPHDR; while(xp != nil && offset && offset >= BLEN(xp)) { offset -= BLEN(xp); xp = xp->next; } xp->rp += offset; for(fragoff = 0; fragoff < dlen; fragoff += seglen) { nb = allocb(IPHDR+seglen); feh = (Iphdr*)(nb->rp); memmove(nb->wp, eh, IPHDR); nb->wp += IPHDR; if((fragoff + seglen) >= dlen) { seglen = dlen - fragoff; hnputs(feh->frag, fragoff>>3); } else hnputs(feh->frag, (fragoff>>3)|IP_MF); hnputs(feh->length, seglen + IPHDR); hnputs(feh->id, lid); /* Copy up the data area */ chunk = seglen; while(chunk) { if(!xp) { freeblist(nb); netlog(Logip, "!xp: chunk %d\n", chunk); goto raise; } blklen = chunk; if(BLEN(xp) < chunk) blklen = BLEN(xp); memmove(nb->wp, xp->rp, blklen); nb->wp += blklen; xp->rp += blklen; chunk -= blklen; if(xp->rp == xp->wp) xp = xp->next; | |
| 1998/0307 | } | |
| 1997/0327 | feh->cksum[0] = 0; feh->cksum[1] = 0; hnputs(feh->cksum, ipcsum(&feh->vihl)); | |
| 1998/0306 | ifc->m->bwrite(ifc, nb, V4, gate); | |
| 1997/0327 | } raise: | |
| 1998/0307 | runlock(ifc); poperror(); | |
| 1997/0327 | freeblist(bp); } void initfrag(int size) { Fragment *fq, *eq; | |
| 1998/0306 | fragfree = (Fragment*)malloc(sizeof(Fragment) * size); | |
| 1997/0327 | if(fragfree == nil) panic("initfrag"); eq = &fragfree[size]; for(fq = fragfree; fq < eq; fq++) fq->next = fq+1; fragfree[size-1].next = nil; } void (*ipextprotoiput)(Block*); | |
| 1997/0806 | //#define DBG(x) if((logmask & Logipmsg) && (iponly == 0 || x == iponly))netlog | |
| 1997/0327 | void | |
| 1998/0306 | ipiput(uchar *ia, Block *bp) | |
| 1997/0327 | { Iphdr *h; Proto *p; ushort frag; int notforme; | |
| 1998/0306 | uchar v6dst[IPaddrlen]; | |
| 1997/0327 | ||
| 1997/0806 | // h = (Iphdr *)(bp->rp); // DBG(nhgetl(h->src))(Logipmsg, "ipiput %I %I len %d proto %d\n", h->src, h->dst, BLEN(bp), h->proto); | |
| 1997/0327 | /* Ensure we have enough data to process */ if(BLEN(bp) < IPHDR) { bp = pullupblock(bp, IPHDR); if(bp == nil) return; } h = (Iphdr *)(bp->rp); | |
| 1998/0306 | /* dump anything that whose header doesn't checksum */ | |
| 1997/0327 | if(ipcsum(&h->vihl)) { ipcsumerr++; netlog(Logip, "ip: checksum error %I\n", h->src); freeblist(bp); return; } | |
| 1998/0306 | /* route */ v4tov6(v6dst, h->dst); notforme = ipforme(v6dst) == 0; if(notforme) { if(iprouting) { /* gate */ if(h->ttl <= 1) freeblist(bp); else ipoput(bp, 1, h->ttl - 1); } else useriprouter(ia, bp); return; } | |
| 1997/0327 | /* Check header length and version */ if(h->vihl != (IP_VER|IP_HLEN)) { netlog(Logip, "ip: %I bad hivl %ux\n", h->src, h->vihl); freeblist(bp); return; } frag = nhgets(h->frag); if(frag) { h->tos = 0; if(frag & IP_MF) h->tos = 1; bp = ipreassemble(frag, bp, h); if(bp == nil) return; h = (Iphdr *)(bp->rp); } ipin += blocklen(bp); ippin++; p = Fsrcvpcol(&fs, h->proto); if(p != nil && p->rcv != nil) | |
| 1998/0306 | (*p->rcv)(ia, bp); | |
| 1997/0327 | else if(ipextprotoiput != nil) ipextprotoiput(bp); else freeblist(bp); } int ipstats(char *buf, int len) { int n; | |
| 1997/0808 | n = snprint(buf, len, "ip: csum %lud inb %lud outb %lud inp %lud outp %lud\n", | |
| 1997/0327 | ipcsumerr, ipin, ipout, ippin, ippout); | |
| 1997/0916 | n += snprint(buf+n, len - n, "\tnoroute %lud droppedfrag %lud\n", stats.noroute, stats.droppedfrag); | |
| 1997/0327 | return n; } Block* ipreassemble(int offset, Block *bp, Iphdr *ip) { int fend; ushort id; Fragment *f, *fnext; | |
| 1998/0306 | ulong src, dst; | |
| 1997/0327 | Block *bl, **l, *last, *prev; int ovlap, len, fragsize, pktposn; src = nhgetl(ip->src); dst = nhgetl(ip->dst); id = nhgets(ip->id); /* * block lists are too hard, pullupblock into a single block */ if(bp->next){ bp = pullupblock(bp, blocklen(bp)); ip = (Iphdr *)(bp->rp); } | |
| 1997/0529 | qlock(&fraglock); | |
| 1997/0327 | /* * find a reassembly queue for this fragment */ for(f = flisthead; f; f = fnext){ fnext = f->next; /* because ipfragfree changes the list */ if(f->src == src && f->dst == dst && f->id == id) break; | |
| 1997/0916 | if(f->age < msec){ stats.droppedfrag++; | |
| 1997/0529 | ipfragfree(f); | |
| 1997/0916 | } | |
| 1997/0327 | } /* * if this isn't a fragmented packet, accept it * and get rid of any fragments that might go * with it. */ if(!ip->tos && (offset & ~(IP_MF|IP_DF)) == 0) { | |
| 1997/0529 | if(f != nil) ipfragfree(f); qunlock(&fraglock); | |
| 1997/0327 | return bp; } | |
| 1997/0504 | if(bp->base+sizeof(Ipfrag) >= bp->rp){ bp = padblock(bp, sizeof(Ipfrag)); bp->rp += sizeof(Ipfrag); } | |
| 1997/0327 | BKFG(bp)->foff = offset<<3; BKFG(bp)->flen = nhgets(ip->length)-IPHDR; /* First fragment allocates a reassembly queue */ if(f == nil) { f = ipfragallo(); f->id = id; f->src = src; f->dst = dst; f->blist = bp; | |
| 1997/0529 | qunlock(&fraglock); | |
| 1997/0327 | return nil; } /* * find the new fragment's position in the queue */ prev = nil; l = &f->blist; bl = f->blist; while(bl != nil && BKFG(bp)->foff > BKFG(bl)->foff) { prev = bl; l = &bl->next; bl = bl->next; } /* Check overlap of a previous fragment - trim away as necessary */ if(prev) { ovlap = BKFG(prev)->foff + BKFG(prev)->flen - BKFG(bp)->foff; if(ovlap > 0) { if(ovlap >= BKFG(bp)->flen) { freeblist(bp); | |
| 1997/0529 | qunlock(&fraglock); | |
| 1997/0327 | return nil; } BKFG(prev)->flen -= ovlap; } } /* Link onto assembly queue */ bp->next = *l; *l = bp; /* Check to see if succeeding segments overlap */ if(bp->next) { l = &bp->next; fend = BKFG(bp)->foff + BKFG(bp)->flen; /* Take completely covered segments out */ while(*l) { ovlap = fend - BKFG(*l)->foff; if(ovlap <= 0) break; if(ovlap < BKFG(*l)->flen) { BKFG(*l)->flen -= ovlap; BKFG(*l)->foff += ovlap; /* move up ip hdrs */ memmove((*l)->rp + ovlap, (*l)->rp, IPHDR); (*l)->rp += ovlap; break; } last = (*l)->next; (*l)->next = nil; freeblist(*l); *l = last; } } /* * look for a complete packet. if we get to a fragment * without IP_MF set, we're done. */ pktposn = 0; for(bl = f->blist; bl; bl = bl->next) { if(BKFG(bl)->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; bl = bl->next) { fragsize = BKFG(bl)->flen; len += fragsize; bl->rp += IPHDR; bl->wp = bl->rp + fragsize; } bl = f->blist; f->blist = nil; | |
| 1997/0529 | ipfragfree(f); | |
| 1997/0327 | ip = BLKIP(bl); hnputs(ip->length, len); | |
| 1997/0529 | qunlock(&fraglock); | |
| 1997/0327 | return bl; } pktposn += BKFG(bl)->flen; } | |
| 1997/0529 | qunlock(&fraglock); | |
| 1997/0327 | return nil; } /* | |
| 1997/0529 | * ipfragfree - Free a list of fragments - assume hold fraglock | |
| 1997/0327 | */ void | |
| 1997/0529 | ipfragfree(Fragment *frag) | |
| 1997/0327 | { Fragment *fl, **l; if(frag->blist) freeblist(frag->blist); frag->src = 0; frag->id = 0; frag->blist = nil; l = &flisthead; for(fl = *l; fl; fl = fl->next) { if(fl == frag) { *l = frag->next; break; } l = &fl->next; } frag->next = fragfree; fragfree = frag; } /* | |
| 1997/0529 | * ipfragallo - allocate a reassembly queue - assume hold fraglock | |
| 1997/0327 | */ Fragment * ipfragallo(void) { Fragment *f; while(fragfree == nil) { | |
| 1997/0529 | /* free last entry on fraglist */ for(f = flisthead; f->next; f = f->next) ; ipfragfree(f); | |
| 1997/0327 | } f = fragfree; fragfree = f->next; f->next = flisthead; flisthead = f; f->age = msec + 30000; return f; } ushort | |
| 1998/0306 | ipcsum(uchar *addr) | |
| 1997/0327 | { int len; ulong sum; sum = 0; len = (addr[0]&0xf)<<2; while(len > 0) { sum += addr[0]<<8 | addr[1] ; len -= 2; addr += 2; } sum = (sum & 0xffff) + (sum >> 16); sum = (sum & 0xffff) + (sum >> 16); return (sum^0xffff); } | |