summaryrefslogtreecommitdiff
path: root/sys/src/9/ip/ip.c
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/9/ip/ip.c
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/9/ip/ip.c')
-rwxr-xr-xsys/src/9/ip/ip.c706
1 files changed, 706 insertions, 0 deletions
diff --git a/sys/src/9/ip/ip.c b/sys/src/9/ip/ip.c
new file mode 100755
index 000000000..19aef70a4
--- /dev/null
+++ b/sys/src/9/ip/ip.c
@@ -0,0 +1,706 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+#include "ip.h"
+
+#define BLKIPVER(xp) (((Ip4hdr*)((xp)->rp))->vihl&0xF0)
+
+static char *statnames[] =
+{
+[Forwarding] "Forwarding",
+[DefaultTTL] "DefaultTTL",
+[InReceives] "InReceives",
+[InHdrErrors] "InHdrErrors",
+[InAddrErrors] "InAddrErrors",
+[ForwDatagrams] "ForwDatagrams",
+[InUnknownProtos] "InUnknownProtos",
+[InDiscards] "InDiscards",
+[InDelivers] "InDelivers",
+[OutRequests] "OutRequests",
+[OutDiscards] "OutDiscards",
+[OutNoRoutes] "OutNoRoutes",
+[ReasmTimeout] "ReasmTimeout",
+[ReasmReqds] "ReasmReqds",
+[ReasmOKs] "ReasmOKs",
+[ReasmFails] "ReasmFails",
+[FragOKs] "FragOKs",
+[FragFails] "FragFails",
+[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;
+
+ v6p->hp.rxmithost = 1000; /* v6 RETRANS_TIMER */
+
+ v6p->cdrouter = -1;
+
+ f->v6p = v6p;
+}
+
+void
+initfrag(IP *ip, int size)
+{
+ Fragment4 *fq4, *eq4;
+ Fragment6 *fq6, *eq6;
+
+ ip->fragfree4 = (Fragment4*)malloc(sizeof(Fragment4) * size);
+ if(ip->fragfree4 == nil)
+ panic("initfrag");
+
+ eq4 = &ip->fragfree4[size];
+ for(fq4 = ip->fragfree4; fq4 < eq4; fq4++)
+ fq4->next = fq4+1;
+
+ ip->fragfree4[size-1].next = nil;
+
+ ip->fragfree6 = (Fragment6*)malloc(sizeof(Fragment6) * size);
+ if(ip->fragfree6 == nil)
+ panic("initfrag");
+
+ eq6 = &ip->fragfree6[size];
+ for(fq6 = ip->fragfree6; fq6 < eq6; fq6++)
+ fq6->next = fq6+1;
+
+ ip->fragfree6[size-1].next = nil;
+}
+
+void
+ip_init(Fs *f)
+{
+ IP *ip;
+
+ ip = smalloc(sizeof(IP));
+ initfrag(ip, 100);
+ f->ip = ip;
+
+ ip_init_6(f);
+}
+
+void
+iprouting(Fs *f, int on)
+{
+ f->ip->iprouting = on;
+ if(f->ip->iprouting==0)
+ f->ip->stats[Forwarding] = 2;
+ else
+ f->ip->stats[Forwarding] = 1;
+}
+
+int
+ipoput4(Fs *f, Block *bp, int gating, int ttl, int tos, Conv *c)
+{
+ Ipifc *ifc;
+ uchar *gate;
+ ulong fragoff;
+ Block *xp, *nb;
+ Ip4hdr *eh, *feh;
+ int lid, len, seglen, chunk, dlen, blklen, offset, medialen;
+ Route *r, *sr;
+ 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 */
+ 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);
+ goto free;
+ }
+
+ r = v4lookup(f, eh->dst, c);
+ if(r == nil){
+ ip->stats[OutNoRoutes]++;
+ netlog(f, Logip, "no interface %V\n", eh->dst);
+ rv = -1;
+ goto free;
+ }
+
+ ifc = r->ifc;
+ if(r->type & (Rifc|Runi))
+ gate = eh->dst;
+ else
+ if(r->type & (Rbcast|Rmulti)) {
+ gate = eh->dst;
+ sr = v4lookup(f, eh->src, nil);
+ if(sr != nil && (sr->type & Runi))
+ ifc = sr->ifc;
+ }
+ 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 we dont need to fragment just send it */
+ if(c && c->maxfragsize && c->maxfragsize < ifc->maxtu)
+ medialen = c->maxfragsize - ifc->m->hsize;
+ else
+ medialen = ifc->maxtu - ifc->m->hsize;
+ if(len <= medialen) {
+ if(!gating)
+ hnputs(eh->id, incref(&ip->id4));
+ hnputs(eh->length, len);
+ if(!gating){
+ eh->frag[0] = 0;
+ eh->frag[1] = 0;
+ }
+ eh->cksum[0] = 0;
+ eh->cksum[1] = 0;
+ hnputs(eh->cksum, ipcsum(&eh->vihl));
+ assert(bp->next == nil);
+ ifc->m->bwrite(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);
+ goto raise;
+ }
+
+ seglen = (medialen - IP4HDR) & ~7;
+ if(seglen < 8){
+ ip->stats[FragFails]++;
+ ip->stats[OutDiscards]++;
+ netlog(f, Logip, "%V seglen < 8\n", eh->dst);
+ goto raise;
+ }
+
+ dlen = len - IP4HDR;
+ xp = bp;
+ if(gating)
+ lid = nhgets(eh->id);
+ else
+ lid = incref(&ip->id4);
+
+ offset = IP4HDR;
+ while(xp != nil && offset && offset >= BLEN(xp)) {
+ offset -= BLEN(xp);
+ xp = xp->next;
+ }
+ xp->rp += offset;
+
+ if(gating)
+ fragoff = nhgets(eh->frag)<<3;
+ else
+ fragoff = 0;
+ dlen += fragoff;
+ for(; fragoff < dlen; fragoff += seglen) {
+ nb = allocb(IP4HDR+seglen);
+ feh = (Ip4hdr*)(nb->rp);
+
+ memmove(nb->wp, eh, IP4HDR);
+ nb->wp += IP4HDR;
+
+ if((fragoff + seglen) >= dlen) {
+ seglen = dlen - fragoff;
+ hnputs(feh->frag, fragoff>>3);
+ }
+ else
+ hnputs(feh->frag, (fragoff>>3)|IP_MF);
+
+ hnputs(feh->length, seglen + IP4HDR);
+ hnputs(feh->id, lid);
+
+ /* Copy up the data area */
+ chunk = seglen;
+ while(chunk) {
+ if(!xp) {
+ ip->stats[OutDiscards]++;
+ ip->stats[FragFails]++;
+ freeblist(nb);
+ netlog(f, 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;
+ }
+
+ feh->cksum[0] = 0;
+ feh->cksum[1] = 0;
+ hnputs(feh->cksum, ipcsum(&feh->vihl));
+ ifc->m->bwrite(ifc, nb, V4, gate);
+ ip->stats[FragCreates]++;
+ }
+ ip->stats[FragOKs]++;
+raise:
+ runlock(ifc);
+ poperror();
+free:
+ freeblist(bp);
+ return rv;
+}
+
+void
+ipiput4(Fs *f, Ipifc *ifc, Block *bp)
+{
+ int hl;
+ int hop, tos, proto, olen;
+ Ip4hdr *h;
+ Proto *p;
+ ushort frag;
+ int notforme;
+ uchar *dp, v6dst[IPaddrlen];
+ IP *ip;
+ Route *r;
+ Conv conv;
+
+ if(BLKIPVER(bp) != IP_VER4) {
+ ipiput6(f, ifc, bp);
+ return;
+ }
+
+ ip = f->ip;
+ ip->stats[InReceives]++;
+
+ /*
+ * Ensure we have all the header info in the first
+ * block. Make life easier for other protocols by
+ * collecting up to the first 64 bytes in the first block.
+ */
+ if(BLEN(bp) < 64) {
+ hl = blocklen(bp);
+ if(hl < IP4HDR)
+ hl = IP4HDR;
+ if(hl > 64)
+ hl = 64;
+ bp = pullupblock(bp, hl);
+ if(bp == nil)
+ return;
+ }
+
+ h = (Ip4hdr*)(bp->rp);
+
+ /* dump anything that whose header doesn't checksum */
+ if((bp->flag & Bipck) == 0 && ipcsum(&h->vihl)) {
+ ip->stats[InHdrErrors]++;
+ netlog(f, Logip, "ip: checksum error %V\n", h->src);
+ freeblist(bp);
+ return;
+ }
+ 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);
+ freeblist(bp);
+ return;
+ }
+ /* 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));
+ }
+ }
+
+ /* route */
+ if(notforme) {
+ if(!ip->iprouting){
+ freeblist(bp);
+ return;
+ }
+
+ /* don't forward to source's network */
+ memmove(&conv, ifc->conv, sizeof conv);
+ conv.r = nil;
+ r = v4lookup(f, h->dst, &conv);
+ if(r == nil || r->ifc == ifc){
+ ip->stats[OutDiscards]++;
+ freeblist(bp);
+ return;
+ }
+
+ /* don't forward if packet has timed out */
+ hop = h->ttl;
+ if(hop < 1) {
+ ip->stats[InHdrErrors]++;
+ icmpttlexceeded(f, ifc->lifc->local, bp);
+ freeblist(bp);
+ return;
+ }
+
+ /* reassemble if the interface expects it */
+if(r->ifc == nil) panic("nil route rfc");
+ if(r->ifc->reassemble){
+ frag = nhgets(h->frag);
+ if(frag) {
+ h->tos = 0;
+ if(frag & IP_MF)
+ h->tos = 1;
+ bp = ip4reassemble(ip, frag, bp, h);
+ if(bp == nil)
+ return;
+ h = (Ip4hdr*)(bp->rp);
+ }
+ }
+
+ ip->stats[ForwDatagrams]++;
+ tos = h->tos;
+ hop = h->ttl;
+ ipoput4(f, bp, 1, hop - 1, tos, &conv);
+ return;
+ }
+
+ frag = nhgets(h->frag);
+ if(frag) {
+ h->tos = 0;
+ if(frag & IP_MF)
+ h->tos = 1;
+ bp = ip4reassemble(ip, frag, bp, h);
+ if(bp == nil)
+ return;
+ h = (Ip4hdr*)(bp->rp);
+ }
+
+ /* don't let any frag info go up the stack */
+ h->frag[0] = 0;
+ h->frag[1] = 0;
+
+ proto = h->proto;
+ p = Fsrcvpcol(f, proto);
+ if(p != nil && p->rcv != nil) {
+ ip->stats[InDelivers]++;
+ (*p->rcv)(p, ifc, bp);
+ return;
+ }
+ ip->stats[InDiscards]++;
+ ip->stats[InUnknownProtos]++;
+ freeblist(bp);
+}
+
+int
+ipstats(Fs *f, char *buf, int len)
+{
+ IP *ip;
+ char *p, *e;
+ int i;
+
+ ip = f->ip;
+ ip->stats[DefaultTTL] = MAXTTL;
+
+ p = buf;
+ e = p+len;
+ for(i = 0; i < Nipstats; i++)
+ p = seprint(p, e, "%s: %llud\n", statnames[i], ip->stats[i]);
+ return p - buf;
+}
+
+Block*
+ip4reassemble(IP *ip, int offset, Block *bp, Ip4hdr *ih)
+{
+ int fend;
+ ushort id;
+ Fragment4 *f, *fnext;
+ ulong src, dst;
+ Block *bl, **l, *last, *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){
+ bp = pullupblock(bp, blocklen(bp));
+ ih = (Ip4hdr*)(bp->rp);
+ }
+
+ qlock(&ip->fraglock4);
+
+ /*
+ * find a reassembly queue for this fragment
+ */
+ for(f = ip->flisthead4; f; f = fnext){
+ fnext = f->next; /* because ipfragfree4 changes the list */
+ if(f->src == src && f->dst == dst && f->id == id)
+ break;
+ if(f->age < NOW){
+ ip->stats[ReasmTimeout]++;
+ ipfragfree4(ip, f);
+ }
+ }
+
+ /*
+ * if this isn't a fragmented packet, accept it
+ * and get rid of any fragments that might go
+ * with it.
+ */
+ if(!ih->tos && (offset & ~(IP_MF|IP_DF)) == 0) {
+ if(f != nil) {
+ ipfragfree4(ip, f);
+ ip->stats[ReasmFails]++;
+ }
+ qunlock(&ip->fraglock4);
+ return bp;
+ }
+
+ 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;
+
+ /* First fragment allocates a reassembly queue */
+ if(f == nil) {
+ f = ipfragallo4(ip);
+ f->id = id;
+ f->src = src;
+ f->dst = dst;
+
+ f->blist = bp;
+
+ qunlock(&ip->fraglock4);
+ ip->stats[ReasmReqds]++;
+ 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);
+ qunlock(&ip->fraglock4);
+ 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 ih hdrs */
+ memmove((*l)->rp + ovlap, (*l)->rp, IP4HDR);
+ (*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 += IP4HDR;
+ bl->wp = bl->rp + fragsize;
+ }
+
+ bl = f->blist;
+ f->blist = nil;
+ ipfragfree4(ip, f);
+ ih = BLKIP(bl);
+ hnputs(ih->length, len);
+ qunlock(&ip->fraglock4);
+ ip->stats[ReasmOKs]++;
+ return bl;
+ }
+ pktposn += BKFG(bl)->flen;
+ }
+ qunlock(&ip->fraglock4);
+ return nil;
+}
+
+/*
+ * ipfragfree4 - Free a list of fragments - assume hold fraglock4
+ */
+void
+ipfragfree4(IP *ip, Fragment4 *frag)
+{
+ Fragment4 *fl, **l;
+
+ if(frag->blist)
+ freeblist(frag->blist);
+
+ frag->src = 0;
+ frag->id = 0;
+ frag->blist = nil;
+
+ l = &ip->flisthead4;
+ for(fl = *l; fl; fl = fl->next) {
+ if(fl == frag) {
+ *l = frag->next;
+ break;
+ }
+ l = &fl->next;
+ }
+
+ frag->next = ip->fragfree4;
+ ip->fragfree4 = frag;
+
+}
+
+/*
+ * ipfragallo4 - allocate a reassembly queue - assume hold fraglock4
+ */
+Fragment4 *
+ipfragallo4(IP *ip)
+{
+ Fragment4 *f;
+
+ while(ip->fragfree4 == nil) {
+ /* free last entry on fraglist */
+ for(f = ip->flisthead4; f->next; f = f->next)
+ ;
+ ipfragfree4(ip, f);
+ }
+ f = ip->fragfree4;
+ ip->fragfree4 = f->next;
+ f->next = ip->flisthead4;
+ ip->flisthead4 = f;
+ f->age = NOW + 30000;
+
+ return f;
+}
+
+ushort
+ipcsum(uchar *addr)
+{
+ 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);
+}