From b044a1ea8abe3fbfca8c413c7326d79f9890a8f7 Mon Sep 17 00:00:00 2001 From: David du Colombier <0intro@gmail.com> Date: Tue, 7 May 2002 00:00:00 +0000 Subject: [PATCH] Plan 9 from Bell Labs 2002-05-07 --- ip/arp.c | 448 +++++++++++++++++---- ip/devip.c | 131 +++---- ip/esp.c | 7 +- ip/ethermedium.c | 251 +++++++++--- ip/gre.c | 3 +- ip/icmp.c | 57 +-- ip/icmp6.c | 923 ++++++++++++++++++++++++++++++++++++++++++++ ip/igmp.c | 5 +- ip/il.c | 13 +- ip/ip.c | 408 ++++++++++---------- ip/ip.h | 98 ++++- ip/ipaux.c | 194 +++++++++- ip/ipifc.c | 697 ++++++++++++++++++++++++++++++--- ip/ipmux.c | 113 ++++-- ip/iproute.c | 84 ++-- ip/ipv6.c | 746 +++++++++++++++++++++++++++++++++++ ip/ipv6.h | 182 +++++++++ ip/loopbackmedium.c | 2 +- ip/netdevmedium.c | 2 +- ip/netlog.c | 2 +- ip/pktmedium.c | 2 +- ip/rudp.c | 4 +- ip/tcp.c | 643 +++++++++++++++++++++--------- ip/tripmedium.c | 2 +- ip/udp.c | 298 ++++++++++---- port/devnmouse.c | 2 - 26 files changed, 4395 insertions(+), 922 deletions(-) create mode 100644 ip/icmp6.c create mode 100644 ip/ipv6.c create mode 100644 ip/ipv6.h diff --git a/ip/arp.c b/ip/arp.c index 6f23306f993ea9780c29a4cef19bfbca136883fc..433529f36775d5f4dfd7a204440d9f2488e5f6b4 100644 --- a/ip/arp.c +++ b/ip/arp.c @@ -6,6 +6,7 @@ #include "../port/error.h" #include "ip.h" +#include "ipv6.h" /* * address resolution tables @@ -27,12 +28,6 @@ char *arpstate[] = "WAIT", }; -enum -{ - CMadd, - CMflush, -}; - /* * one per Fs */ @@ -42,24 +37,27 @@ struct Arp Fs *f; Arpent *hash[NHASH]; Arpent cache[NCACHE]; -}; - -static -Cmdtab arpcmd[] = -{ - CMadd, "add", 0, - CMflush, "flush", 1, + Arpent *rxmt; + Proc *rxmitp; /* neib sol re-transmit proc */ + Rendez rxmtq; + Block *dropf, *dropl; }; char *Ebadarp = "bad arp"; #define haship(s) ((s)[IPaddrlen-1]%NHASH) +extern int ReTransTimer = RETRANS_TIMER; +static void rxmitproc(void *v); + void arpinit(Fs *f) { f->arp = smalloc(sizeof(Arp)); f->arp->f = f; + f->arp->rxmt = nil; + f->arp->dropf = f->arp->dropl = nil; + kproc("rxmitproc", rxmitproc, f->arp); } static Arpent* @@ -83,6 +81,7 @@ newarp(Arp *arp, uchar *ip, Medium *m) /* dump waiting packets */ xp = a->hold; a->hold = nil; + while(xp){ next = xp->list; freeblist(xp); @@ -92,7 +91,74 @@ newarp(Arp *arp, uchar *ip, Medium *m) /* take out of current chain */ l = &arp->hash[haship(a->ip)]; for(f = *l; f; f = f->hash){ - if(f == a) { + if(f == a){ + *l = a->hash; + break; + } + l = &f->hash; + } + + /* insert into new chain */ + l = &arp->hash[haship(ip)]; + a->hash = *l; + *l = a; + memmove(a->ip, ip, sizeof(a->ip)); + a->used = msec; + a->time = 0; + a->type = m; + + return a; +} + +static Arpent* +newarp6(Arp *arp, uchar *ip, Ipifc *ifc, int addrxt) +{ + uint t; + Block *next, *xp; + Arpent *a, *e, *f, **l; + Medium *m = ifc->m; + int empty; + + /* find oldest entry */ + e = &arp->cache[NCACHE]; + a = arp->cache; + t = a->used; + for(f = a; f < e; f++){ + if(f->used < t){ + t = f->used; + a = f; + } + } + + /* dump waiting packets */ + xp = a->hold; + a->hold = nil; + + if(isv4(a->ip)){ + while(xp){ + next = xp->list; + freeblist(xp); + xp = next; + } + } + else { // queue icmp unreachable for rxmitproc later on, w/o arp lock + if(xp){ + if(arp->dropl == nil) + arp->dropf = xp; + else + arp->dropl->list = xp; + + for(next = xp->list; next; next = next->list) + xp = next; + arp->dropl = xp; + wakeup(&arp->rxmtq); + } + } + + /* take out of current chain */ + l = &arp->hash[haship(a->ip)]; + for(f = *l; f; f = f->hash){ + if(f == a){ *l = a->hash; break; } @@ -103,36 +169,107 @@ newarp(Arp *arp, uchar *ip, Medium *m) l = &arp->hash[haship(ip)]; a->hash = *l; *l = a; + memmove(a->ip, ip, sizeof(a->ip)); a->used = msec; a->time = 0; a->type = m; + a->rxtat = msec + ReTransTimer; + a->rxtsrem = MAX_MULTICAST_SOLICIT; + a->ifc = ifc; + a->ifcid = ifc->ifcid; + + /* put to the end of re-transmit chain; addrxt is 0 when isv4(a->ip) */ + if(!ipismulticast(a->ip) && addrxt){ + l = &arp->rxmt; + empty = (*l==nil); + + for(f = *l; f; f = f->nextrxt){ + if(f == a){ + *l = a->nextrxt; + break; + } + l = &f->nextrxt; + } + for(f = *l; f; f = f->nextrxt){ + l = &f->nextrxt; + } + *l = a; + if(empty) + wakeup(&arp->rxmtq); + } + + a->nextrxt = nil; + return a; } +/* called with arp qlocked */ + +void +cleanarpent(Arp *arp, Arpent *a) +{ + Arpent *f, **l; + + a->used = 0; + a->time = 0; + a->type = 0; + a->state = 0; + + /* take out of current chain */ + l = &arp->hash[haship(a->ip)]; + for(f = *l; f; f = f->hash){ + if(f == a){ + *l = a->hash; + break; + } + l = &f->hash; + } + + /* take out of re-transmit chain */ + l = &arp->rxmt; + for(f = *l; f; f = f->nextrxt){ + if(f == a){ + *l = a->nextrxt; + break; + } + l = &f->nextrxt; + } + a->nextrxt = nil; + a->hash = nil; + a->hold = nil; + a->last = nil; + a->ifc = nil; +} + Arpent* -arpget(Arp *arp, Block *bp, int version, Medium *type, uchar *ip, uchar *mac) +arpget(Arp *arp, Block *bp, int version, Ipifc *ifc, uchar *ip, uchar *mac) { int hash; Arpent *a; + Medium *type = ifc->m; uchar v6ip[IPaddrlen]; - if(version == V4) { + if(version == V4){ v4tov6(v6ip, ip); ip = v6ip; } qlock(arp); hash = haship(ip); - for(a = arp->hash[hash]; a; a = a->hash) { + for(a = arp->hash[hash]; a; a = a->hash){ if(memcmp(ip, a->ip, sizeof(a->ip)) == 0) if(type == a->type) break; } if(a == nil){ - a = newarp(arp, ip, type); + //a = newarp6(arp, ip, ifc, (version != V4)); + if(version == V4) + a = newarp(arp, ip, type); + else + a = newarp6(arp, ip, ifc, 1); a->state = AWAIT; } a->used = msec; @@ -169,6 +306,18 @@ Block* arpresolve(Arp *arp, Arpent *a, Medium *type, uchar *mac) { Block *bp; + Arpent *f, **l; + + if(!isv4(a->ip)){ + l = &arp->rxmt; + for(f = *l; f; f = f->nextrxt){ + if(f == a){ + *l = a->nextrxt; + break; + } + l = &f->nextrxt; + } + } memmove(a->mac, mac, type->maclen); a->type = type; @@ -186,7 +335,7 @@ arpenter(Fs *fs, int version, uchar *ip, uchar *mac, int n, int refresh) { Arp *arp; Route *r; - Arpent *a; + Arpent *a, *f, **l; Ipifc *ifc; Medium *type; Block *bp, *next; @@ -194,12 +343,12 @@ arpenter(Fs *fs, int version, uchar *ip, uchar *mac, int n, int refresh) arp = fs->arp; - if(n != 6) { + if(n != 6){ // print("arp: len = %d\n", n); return; } - if(version == V4) { + if(version == V4){ r = v4lookup(fs, ip); v4tov6(v6ip, ip); ip = v6ip; @@ -207,7 +356,7 @@ arpenter(Fs *fs, int version, uchar *ip, uchar *mac, int n, int refresh) else r = v6lookup(fs, ip); - if(r == nil) { + if(r == nil){ // print("arp: no route for entry\n"); return; } @@ -216,19 +365,35 @@ arpenter(Fs *fs, int version, uchar *ip, uchar *mac, int n, int refresh) type = ifc->m; qlock(arp); - for(a = arp->hash[haship(ip)]; a; a = a->hash) { + for(a = arp->hash[haship(ip)]; a; a = a->hash){ if(a->type != type || (a->state != AWAIT && a->state != AOK)) continue; - if(ipcmp(a->ip, ip) == 0) { + if(ipcmp(a->ip, ip) == 0){ a->state = AOK; memmove(a->mac, mac, type->maclen); + + if(version == V6){ + /* take out of re-transmit chain */ + l = &arp->rxmt; + for(f = *l; f; f = f->nextrxt){ + if(f == a){ + *l = a->nextrxt; + break; + } + l = &f->nextrxt; + } + } + + a->hold = nil; + a->ifc = ifc; + a->ifcid = ifc->ifcid; bp = a->hold; a->hold = nil; if(version == V4) ip += IPv4off; qunlock(arp); - while(bp) { + while(bp){ next = bp->list; if(ifc != nil){ if(waserror()){ @@ -252,7 +417,12 @@ arpenter(Fs *fs, int version, uchar *ip, uchar *mac, int n, int refresh) } if(refresh == 0){ - a = newarp(arp, ip, type); + //a = newarp6(arp, ip, ifc, 0); + if(version == 4) + a = newarp(arp, ip, type); + else + a = newarp6(arp, ip, ifc, 0); + a->state = AOK; a->type = type; memmove(a->mac, mac, type->maclen); @@ -269,40 +439,63 @@ arpwrite(Fs *fs, char *s, int len) Arp *arp; Block *bp; Arpent *a; - Cmdbuf *cb; - Cmdtab *ct; Medium *m; + char *f[4], buf[256]; uchar ip[IPaddrlen], mac[MAClen]; arp = fs->arp; - cb = parsecmd(s, len); - if(waserror()){ - free(cb); - nexterror(); - } - - ct = lookupcmd(cb, arpcmd, nelem(arpcmd)); - - switch(ct->index){ - case CMadd: - switch(cb->nf) { + if(len == 0) + error(Ebadarp); + if(len >= sizeof(buf)) + len = sizeof(buf)-1; + strncpy(buf, s, len); + buf[len] = 0; + if(len > 0 && buf[len-1] == '\n') + buf[len-1] = 0; + + n = getfields(buf, f, 4, 1, " "); + if(strcmp(f[0], "flush") == 0){ + qlock(arp); + for(a = arp->cache; a < &arp->cache[NCACHE]; a++){ + memset(a->ip, 0, sizeof(a->ip)); + memset(a->mac, 0, sizeof(a->mac)); + a->hash = nil; + a->state = 0; + a->used = 0; + while(a->hold != nil){ + bp = a->hold->list; + freeblist(a->hold); + a->hold = bp; + } + } + memset(arp->hash, 0, sizeof(arp->hash)); +// clear all pkts on these lists (rxmt, dropf/l) + arp->rxmt = nil; + arp->dropf = nil; + arp->dropl = nil; + qunlock(arp); + } else if(strcmp(f[0], "add") == 0){ + switch(n){ default: - cmderror(cb, Ecmdargs); + error(Ebadarg); case 3: - parseip(ip, cb->f[1]); - r = v4lookup(fs, ip+IPv4off); + parseip(ip, f[1]); + if(isv4(ip)) + r = v4lookup(fs, ip+IPv4off); + else + r = v6lookup(fs, ip); if(r == nil) - error("destination unreachable"); + error("Destination unreachable"); m = r->ifc->m; - n = parsemac(mac, cb->f[2], m->maclen); + n = parsemac(mac, f[2], m->maclen); break; case 4: - m = ipfindmedium(cb->f[1]); + m = ipfindmedium(f[1]); if(m == nil) error(Ebadarp); - parseip(ip, cb->f[2]); - n = parsemac(mac, cb->f[3], m->maclen); + parseip(ip, f[2]); + n = parsemac(mac, f[3], m->maclen); break; } @@ -310,38 +503,18 @@ arpwrite(Fs *fs, char *s, int len) error(Ebadarp); m->ares(fs, V6, ip, mac, n, 0); - break; - - case CMflush: - qlock(arp); - for(a = arp->cache; a < &arp->cache[NCACHE]; a++){ - memset(a->ip, 0, sizeof(a->ip)); - memset(a->mac, 0, sizeof(a->mac)); - a->hash = nil; - a->state = 0; - a->used = 0; - while(a->hold != nil) { - bp = a->hold->list; - freeblist(a->hold); - a->hold = bp; - } - } - memset(arp->hash, 0, sizeof(arp->hash)); - qunlock(arp); - break; - } + } else + error(Ebadarp); - poperror(); - free(cb); return len; } enum { - Alinelen= 66, + Alinelen= 90, }; -char *aformat = "%-6.6s %-8.8s %-16.16I %-32.32s\n"; +char *aformat = "%-6.6s %-8.8s %-40.40I %-32.32s\n"; static void convmac(char *p, uchar *mac, int n) @@ -380,3 +553,136 @@ arpread(Arp *arp, char *p, ulong offset, int len) return n; } + +extern int +rxmitsols(Arp *arp) +{ + uint sflag; + Block *next, *xp; + Arpent *a, *b, **l; + Fs *f; + uchar ipsrc[IPaddrlen]; + Ipifc *ifc = nil; + long nrxt; + + qlock(arp); + f = arp->f; + + a = arp->rxmt; + if(a==nil){ + nrxt = 0; + goto dodrops; //return nrxt; + } + nrxt = a->rxtat - msec; + if(nrxt > 3*ReTransTimer/4) + goto dodrops; //return nrxt; + + for(; a; a = a->nextrxt){ + ifc = a->ifc; + assert(ifc != nil); + if((a->rxtsrem <= 0) || !(canrlock(ifc)) || (a->ifcid != ifc->ifcid)){ + xp = a->hold; + a->hold = nil; + + if(xp){ + if(arp->dropl == nil) + arp->dropf = xp; + else + arp->dropl->list = xp; + } + + cleanarpent(arp, a); + } + else + break; + } + + /* need to unlock arp, else will deadlock when icmpns + * wants to lock arp later. + */ + + qunlock(arp); + + if(a == nil) + goto dodrops; // return 0; + + if(sflag = ipv6anylocal(ifc, ipsrc)) + icmpns(f, ipsrc, sflag, a->ip, TARG_MULTI, ifc->mac); + + runlock(ifc); + + /* grab lock on arp again */ + + qlock(arp); + + /* put to the end of re-transmit chain */ + l = &arp->rxmt; + for(b = *l; b; b = b->nextrxt){ + if(b == a){ + *l = a->nextrxt; + break; + } + l = &b->nextrxt; + } + for(b = *l; b; b = b->nextrxt){ + l = &b->nextrxt; + } + *l = a; + a->rxtsrem--; + a->nextrxt = nil; + a->time = msec; + a->rxtat = msec + ReTransTimer; + + a = arp->rxmt; + if(a==nil) + nrxt = 0; + else + nrxt = a->rxtat - msec; + +dodrops: + xp = arp->dropf; + arp->dropf = nil; + arp->dropl = nil; + qunlock(arp); + + for(; xp; xp = next){ + next = xp->list; + icmphostunr(f, ifc, xp, icmp6_adr_unreach, 1); + } + + return nrxt; + +} + +static int +rxready(void *v) +{ + Arp *arp = (Arp *) v; + int x; + + x = ((arp->rxmt != nil) || (arp->dropf != nil)); + + return x; +} + +static void +rxmitproc(void *v) +{ + Arp *arp = v; + long wakeupat; + + arp->rxmitp = up; + print("arp rxmitproc started\n"); + if(waserror()){ + arp->rxmitp = 0; + pexit("hangup", 1); + } + for(;;){ + wakeupat = rxmitsols(arp); + if(wakeupat == 0) + sleep(&arp->rxmtq, rxready, v); + else if(wakeupat > ReTransTimer/4) + tsleep(&arp->rxmtq, return0, 0, wakeupat); + } +} + diff --git a/ip/devip.c b/ip/devip.c index 36e2f4e48bfb9010d8f5d722e05b2481c48b7804..18b9f338e0e48671254673b148fa4a945be508a2 100644 --- a/ip/devip.c +++ b/ip/devip.c @@ -15,6 +15,7 @@ enum Qndb, Qiproute, Qipselftab, + Qipgate6, Qlog, Qprotodir, /* directory for a protocol */ @@ -54,30 +55,6 @@ QLock fslock; Fs *ipfs[Nfs]; /* attached fs's */ Queue *qlog; -enum -{ - CMaddmulti, - CMannounce, - CMbind, - CMconnect, - CMremmulti, - CMtos, - CMttl, - CMwildcard, -}; - -Cmdtab ipctlmsg[] = -{ - CMaddmulti, "addmulti", 0, - CMannounce, "announce", 0, - CMbind, "bind", 0, - CMconnect, "connect", 0, - CMremmulti, "remmulti", 0, - CMtos, "tos", 0, - CMttl, "ttl", 0, - CMwildcard, "*", 0, -}; - extern void nullmediumlink(void); extern void pktmediumlink(void); long ndbwrite(Fs *f, char *a, ulong off, int n); @@ -166,7 +143,6 @@ ip1gen(Chan *c, int i, Dir *dp) case Qndb: p = "ndb"; len = strlen(f->ndb); - q.vers = f->ndbvers; break; case Qiproute: p = "iproute"; @@ -175,13 +151,15 @@ ip1gen(Chan *c, int i, Dir *dp) p = "ipselftab"; prot = 0444; break; + case Qipgate6: + p = "ipgate6"; + prot = 0444; + break; case Qlog: p = "log"; break; } devdir(c, q, p, len, network, prot, dp); - if(i == Qndb) - dp->mtime = f->ndbmtime; return 1; } @@ -217,6 +195,7 @@ ipgen(Chan *c, char*, Dirtab*, int, int s, Dir *dp) case Qlog: case Qiproute: case Qipselftab: + case Qipgate6: return ip1gen(c, TYPE(c->qid), dp); case Qprotodir: if(s == DEVDOTDOT){ @@ -346,6 +325,7 @@ ipwalk(Chan* c, Chan *nc, char **name, int nname) return w; } + static int ipstat(Chan* c, uchar* db, int n) { @@ -384,12 +364,10 @@ ipopen(Chan* c, int omode) default: break; case Qndb: - if((omode & (OWRITE|OTRUNC)) && !iseve()) + if(omode & (OWRITE|OTRUNC) && !iseve()) error(Eperm); - if((omode & (OWRITE|OTRUNC)) == (OWRITE|OTRUNC)){ + if((omode & (OWRITE|OTRUNC)) == (OWRITE|OTRUNC)) f->ndb[0] = 0; - f->ndbvers++; - } break; case Qlog: netlogopen(f); @@ -405,6 +383,7 @@ ipopen(Chan* c, int omode) case Qstats: case Qbootp: case Qipselftab: + case Qipgate6: if(omode != OREAD) error(Eperm); break; @@ -497,14 +476,25 @@ ipopen(Chan* c, int omode) c->mode = openmode(omode); c->flag |= COPEN; c->offset = 0; - c->iounit = qiomaxatomic; return c; } +static void +ipcreate(Chan*, char*, int, ulong) +{ + error(Eperm); +} + +static void +ipremove(Chan*) +{ + error(Eperm); +} + static int ipwstat(Chan *c, uchar *dp, int n) { - Dir *d; + Dir d; Conv *cv; Fs *f; Proto *p; @@ -519,26 +509,16 @@ ipwstat(Chan *c, uchar *dp, int n) break; } - d = smalloc(n + sizeof(Dir)); - if(waserror()){ - free(d); - nexterror(); + n = convM2D(dp, n, &d, nil); + if(n > 0){ + p = f->p[PROTO(c->qid)]; + cv = p->conv[CONV(c->qid)]; + if(!iseve() && strcmp(ATTACHER(c), cv->owner) != 0) + error(Eperm); + if(d.uid[0]) + kstrdup(&cv->owner, d.uid); + cv->perm = d.mode & 0777; } - n = convM2D(dp, n, d, nil); - if(n == 0) - error(Eshortstat); - - p = f->p[PROTO(c->qid)]; - cv = p->conv[CONV(c->qid)]; - if(!iseve() && strcmp(ATTACHER(c), cv->owner) != 0) - error(Eperm); - if(!emptystr(d->uid)) - kstrdup(&cv->owner, d->uid); - if(d->mode != ~0UL) - cv->perm = d->mode & 0777; - - poperror(); - free(d); return n; } @@ -631,6 +611,8 @@ ipread(Chan *ch, void *a, long n, vlong off) return routeread(f, a, offset, n); case Qipselftab: return ipselftabread(f, a, offset, n); + case Qipgate6: + return ipgateread6(f, a, offset, n); case Qlog: return netlogread(f, a, offset, n); case Qctl: @@ -749,6 +731,7 @@ setluniqueport(Conv* c, int lport) return nil; } + /* * pick a local port and set it */ @@ -1037,7 +1020,6 @@ ipwrite(Chan* ch, void *v, long n, vlong off) Proto *x; char *p; Cmdbuf *cb; - Cmdtab *ct; uchar ia[IPaddrlen], ma[IPaddrlen]; Fs *f; char *a; @@ -1080,26 +1062,21 @@ ipwrite(Chan* ch, void *v, long n, vlong off) free(cb); nexterror(); } - ct = lookupcmd(cb, ipctlmsg, nelem(ipctlmsg)); - switch(ct->index){ - case CMconnect: + if(cb->nf < 1) + error("short control request"); + if(strcmp(cb->f[0], "connect") == 0) connectctlmsg(x, c, cb); - break; - case CMannounce: + else if(strcmp(cb->f[0], "announce") == 0) announcectlmsg(x, c, cb); - break; - case CMbind: + else if(strcmp(cb->f[0], "bind") == 0) bindctlmsg(x, c, cb); - break; - case CMttl: + else if(strcmp(cb->f[0], "ttl") == 0) ttlctlmsg(c, cb); - break; - case CMtos: + else if(strcmp(cb->f[0], "tos") == 0) tosctlmsg(c, cb); - break; - case CMaddmulti: - if(cb->nf != 2 && cb->nf != 3) - cmderror(cb, Ecmdargs); + else if(strcmp(cb->f[0], "addmulti") == 0){ + if(cb->nf < 2) + error("addmulti needs interface address"); if(cb->nf == 2){ if(!ipismulticast(c->raddr)) error("addmulti for a non multicast address"); @@ -1112,23 +1089,19 @@ ipwrite(Chan* ch, void *v, long n, vlong off) parseip(ia, cb->f[1]); ipifcaddmulti(c, ma, ia); } - break; - case CMremmulti: + } else if(strcmp(cb->f[0], "remmulti") == 0){ if(cb->nf < 2) error("remmulti needs interface address"); if(!ipismulticast(c->raddr)) error("remmulti for a non multicast address"); parseip(ia, cb->f[1]); ipifcremmulti(c, c->raddr, ia); - break; - case CMwildcard: - if(x->ctl == nil) - cmderror(cb, "unknown control request"); + } else if(x->ctl != nil) { p = x->ctl(c, cb->f, cb->nf); if(p != nil) error(p); - } - + } else + error("unknown control request"); qunlock(c); free(cb); poperror(); @@ -1175,13 +1148,13 @@ Dev ipdevtab = { ipwalk, ipstat, ipopen, - devcreate, + ipcreate, ipclose, ipread, ipbread, ipwrite, ipbwrite, - devremove, + ipremove, ipwstat, }; @@ -1375,8 +1348,6 @@ ndbwrite(Fs *f, char *a, ulong off, int n) error(Eio); memmove(f->ndb+off, a, n); f->ndb[off+n] = 0; - f->ndbvers++; - f->ndbmtime = seconds(); return n; } diff --git a/ip/esp.c b/ip/esp.c index 1b5643f5dcbc97802bac170f00e0b7082af4b387..ffd14054d8562fd93d467ca98e823b1bced77979 100644 --- a/ip/esp.c +++ b/ip/esp.c @@ -23,7 +23,7 @@ enum { IP_ESPPROTO = 50, EsphdrSize = 28, // includes IP header - IphdrSize = 20, // options have been stripped + IphdrSize = 20, // options have been striped EsptailSize = 2, // does not include pad or auth data UserhdrSize = 4, // user visable header size - if enabled }; @@ -285,6 +285,7 @@ espkick(Conv *c) auth = bp->rp + EsphdrSize + payload + pad + EsptailSize; // fill in head + eh->vihl = IP_VER4; hnputl(eh->espspi, ecb->spi); hnputl(eh->espseq, ++ecb->seq); v6tov4(eh->espsrc, c->laddr); @@ -296,8 +297,8 @@ espkick(Conv *c) ecb->auth(ecb, bp->rp+IphdrSize, (EsphdrSize-IphdrSize)+payload+pad+EsptailSize, auth); qunlock(c); -//print("esp: pass down: %uld\n", BLEN(bp)); - ipoput(c->p->f, bp, 0, c->ttl, c->tos); + //print("esp: pass down: %uld\n", BLEN(bp)); + ipoput4(c->p->f, bp, 0, c->ttl, c->tos); } void diff --git a/ip/ethermedium.c b/ip/ethermedium.c index e6b4ee5c2261aad574f1d84b2cd446de4ba39554..dcf9493705dd47c560acd43c8fd4dcd52a142731 100644 --- a/ip/ethermedium.c +++ b/ip/ethermedium.c @@ -6,6 +6,7 @@ #include "../port/error.h" #include "ip.h" +#include "ipv6.h" typedef struct Etherhdr Etherhdr; struct Etherhdr @@ -15,7 +16,8 @@ struct Etherhdr uchar t[2]; }; -static void etherread(void *a); +static void etherread4(void *a); +static void etherread6(void *a); static void etherbind(Ipifc *ifc, int argc, char **argv); static void etherunbind(Ipifc *ifc); static void etherbwrite(Ipifc *ifc, Block *bp, int version, uchar *ip); @@ -26,6 +28,8 @@ static void sendarp(Ipifc *ifc, Arpent *a); static void sendgarp(Ipifc *ifc, uchar*); static int multicastea(uchar *ea, uchar *ip); static void recvarpproc(void*); +static void resolveaddr6(Ipifc *ifc, Arpent *a); +static void etherpref2addr(uchar *pref, uchar *ea); Medium ethermedium = { @@ -41,6 +45,7 @@ Medium ethermedium = .remmulti= etherremmulti, .ares= arpenter, .areg= sendgarp, +.pref2addr= etherpref2addr, }; Medium gbemedium = @@ -57,6 +62,7 @@ Medium gbemedium = .remmulti= etherremmulti, .ares= arpenter, .areg= sendgarp, +.pref2addr= etherpref2addr, }; typedef struct Etherrock Etherrock; @@ -64,10 +70,13 @@ struct Etherrock { Fs *f; /* file system we belong to */ Proc *arpp; /* arp process */ - Proc *readp; /* reading process */ - Chan *mchan; /* Data channel */ + Proc *read4p; /* reading process (v4)*/ + Proc *read6p; /* reading process (v6)*/ + Chan *mchan4; /* Data channel for v4 */ Chan *achan; /* Arp channel */ - Chan *cchan; /* Control channel */ + Chan *cchan4; /* Control channel for v4 */ + Chan *mchan6; /* Data channel for v6 */ + Chan *cchan6; /* Control channel for v6 */ }; /* @@ -76,10 +85,12 @@ struct Etherrock enum { ETARP = 0x0806, - ETIP = 0x0800, + ETIP4 = 0x0800, + ETIP6 = 0x86DD, ARPREQUEST = 1, ARPREPLY = 2, }; + typedef struct Etherarp Etherarp; struct Etherarp { @@ -97,7 +108,6 @@ struct Etherarp uchar tpa[4]; }; - /* * called to bind an IP ifc to an ethernet device * called with ifc wlock'd @@ -105,9 +115,9 @@ struct Etherarp static void etherbind(Ipifc *ifc, int argc, char **argv) { - Chan *mchan, *cchan, *achan; - char addr[Maxpath]; - char dir[Maxpath]; + Chan *mchan4, *cchan4, *achan, *mchan6, *cchan6; + char addr[Maxpath]; //char addr[2*KNAMELEN]; + char dir[Maxpath]; //char dir[2*KNAMELEN]; char *buf; int n; char *ptr; @@ -116,15 +126,19 @@ etherbind(Ipifc *ifc, int argc, char **argv) if(argc < 2) error(Ebadarg); - mchan = cchan = achan = nil; + mchan4 = cchan4 = achan = mchan6 = cchan6 = nil; buf = nil; if(waserror()){ - if(mchan != nil) - cclose(mchan); - if(cchan != nil) - cclose(cchan); + if(mchan4 != nil) + cclose(mchan4); + if(cchan4 != nil) + cclose(cchan4); if(achan != nil) cclose(achan); + if(mchan6 != nil) + cclose(mchan6); + if(cchan6 != nil) + cclose(cchan6); if(buf != nil) free(buf); nexterror(); @@ -137,7 +151,7 @@ etherbind(Ipifc *ifc, int argc, char **argv) * this device. */ snprint(addr, sizeof(addr), "%s!0x800", argv[2]); - mchan = chandial(addr, nil, dir, &cchan); + mchan4 = chandial(addr, nil, dir, &cchan4); /* * get mac address @@ -162,18 +176,30 @@ etherbind(Ipifc *ifc, int argc, char **argv) snprint(addr, sizeof(addr), "%s!0x806", argv[2]); achan = chandial(addr, nil, nil, nil); + /* + * open ip conversation + * + * the dial will fail if the type is already open on + * this device. + */ + snprint(addr, sizeof(addr), "%s!0x86DD", argv[2]); + mchan6 = chandial(addr, nil, dir, &cchan6); + er = smalloc(sizeof(*er)); - er->mchan = mchan; - er->cchan = cchan; + er->mchan4 = mchan4; + er->cchan4 = cchan4; er->achan = achan; + er->mchan6 = mchan6; + er->cchan6 = cchan6; er->f = ifc->conv->p->f; ifc->arg = er; free(buf); poperror(); - kproc("etherread", etherread, ifc); + kproc("etherread4", etherread4, ifc); kproc("recvarpproc", recvarpproc, ifc); + kproc("etherread6", etherread6, ifc); } /* @@ -184,21 +210,27 @@ etherunbind(Ipifc *ifc) { Etherrock *er = ifc->arg; - if(er->readp) - postnote(er->readp, 1, "unbind", 0); + if(er->read4p) + postnote(er->read4p, 1, "unbind", 0); + if(er->read6p) + postnote(er->read6p, 1, "unbind", 0); if(er->arpp) postnote(er->arpp, 1, "unbind", 0); /* wait for readers to die */ - while(er->arpp != 0 || er->readp != 0) + while(er->arpp != 0 || er->read4p != 0 || er->read6p != 0) tsleep(&up->sleep, return0, 0, 300); - if(er->mchan != nil) - cclose(er->mchan); + if(er->mchan4 != nil) + cclose(er->mchan4); if(er->achan != nil) cclose(er->achan); - if(er->cchan != nil) - cclose(er->cchan); + if(er->cchan4 != nil) + cclose(er->cchan4); + if(er->mchan6 != nil) + cclose(er->mchan6); + if(er->cchan6 != nil) + cclose(er->cchan6); free(er); } @@ -215,12 +247,15 @@ etherbwrite(Ipifc *ifc, Block *bp, int version, uchar *ip) Etherrock *er = ifc->arg; /* get mac address of destination */ - a = arpget(er->f->arp, bp, version, ifc->m, ip, mac); + a = arpget(er->f->arp, bp, version, ifc, ip, mac); if(a){ /* check for broadcast or multicast */ bp = multicastarp(er->f, a, ifc->m, mac); - if(bp == nil){ - sendarp(ifc, a); + if(bp==nil){ + if(version == V4) + sendarp(ifc, a); + else + resolveaddr6(ifc, a); return; } } @@ -236,26 +271,67 @@ etherbwrite(Ipifc *ifc, Block *bp, int version, uchar *ip) /* copy in mac addresses and ether type */ memmove(eh->s, ifc->mac, sizeof(eh->s)); memmove(eh->d, mac, sizeof(eh->d)); - switch(version){ + + switch(version){ case V4: eh->t[0] = 0x08; eh->t[1] = 0x00; + devtab[er->mchan4->type]->bwrite(er->mchan4, bp, 0); break; case V6: eh->t[0] = 0x86; eh->t[1] = 0xDD; + devtab[er->mchan6->type]->bwrite(er->mchan6, bp, 0); break; } - - devtab[er->mchan->type]->bwrite(er->mchan, bp, 0); ifc->out++; } + /* * process to read from the ethernet */ static void -etherread(void *a) +etherread4(void *a) +{ + Ipifc *ifc; + Block *bp; + Etherrock *er; + + ifc = a; + er = ifc->arg; + er->read4p = up; /* hide identity under a rock for unbind */ + if(waserror()){ + er->read4p = 0; + pexit("hangup", 1); + } + for(;;){ + bp = devtab[er->mchan4->type]->bread(er->mchan4, ifc->maxmtu, 0); + if(!canrlock(ifc)){ + freeb(bp); + continue; + } + if(waserror()){ + runlock(ifc); + nexterror(); + } + ifc->in++; + bp->rp += ifc->m->hsize; + if(ifc->lifc == nil) + freeb(bp); + else + ipiput4(er->f, ifc, bp); + runlock(ifc); + poperror(); + } +} + + +/* + * process to read from the ethernet, IPv6 + */ +static void +etherread6(void *a) { Ipifc *ifc; Block *bp; @@ -263,13 +339,13 @@ etherread(void *a) ifc = a; er = ifc->arg; - er->readp = up; /* hide identity under a rock for unbind */ + er->read6p = up; /* hide identity under a rock for unbind */ if(waserror()){ - er->readp = 0; + er->read6p = 0; pexit("hangup", 1); } for(;;){ - bp = devtab[er->mchan->type]->bread(er->mchan, ifc->maxmtu, 0); + bp = devtab[er->mchan6->type]->bread(er->mchan6, ifc->maxmtu, 0); if(!canrlock(ifc)){ freeb(bp); continue; @@ -283,7 +359,7 @@ etherread(void *a) if(ifc->lifc == nil) freeb(bp); else - ipiput(er->f, ifc, bp); + ipiput6(er->f, ifc, bp); runlock(ifc); poperror(); } @@ -295,10 +371,14 @@ etheraddmulti(Ipifc *ifc, uchar *a, uchar *) uchar mac[6]; char buf[64]; Etherrock *er = ifc->arg; + int type; - multicastea(mac, a); + type = multicastea(mac, a); sprint(buf, "addmulti %E", mac); - devtab[er->cchan->type]->write(er->cchan, buf, strlen(buf), 0); + if(type == V4) + devtab[er->cchan4->type]->write(er->cchan4, buf, strlen(buf), 0); + else if(type == V6) + devtab[er->cchan6->type]->write(er->cchan6, buf, strlen(buf), 0); } static void @@ -307,10 +387,14 @@ etherremmulti(Ipifc *ifc, uchar *a, uchar *) uchar mac[6]; char buf[64]; Etherrock *er = ifc->arg; + int type; - multicastea(mac, a); + type = multicastea(mac, a); sprint(buf, "remmulti %E", mac); - devtab[er->cchan->type]->write(er->cchan, buf, strlen(buf), 0); + if(type == V4) + devtab[er->cchan4->type]->write(er->cchan4, buf, strlen(buf), 0); + else if(type == V6) + devtab[er->cchan6->type]->write(er->cchan6, buf, strlen(buf), 0); } /* @@ -357,7 +441,7 @@ sendarp(Ipifc *ifc, Arpent *a) hnputs(e->type, ETARP); hnputs(e->hrd, 1); - hnputs(e->pro, ETIP); + hnputs(e->pro, ETIP4); e->hln = sizeof(e->sha); e->pln = sizeof(e->spa); hnputs(e->op, ARPREQUEST); @@ -368,6 +452,43 @@ sendarp(Ipifc *ifc, Arpent *a) print("arp: send: %r\n"); } +static void +resolveaddr6(Ipifc *ifc, Arpent *a) +{ + int sflag; + Block *bp; + Etherrock *er = ifc->arg; + uchar ipsrc[IPaddrlen]; + + /* don't do anything if it's been less than a second since the last */ + if(msec - a->time < ReTransTimer){ + arprelease(er->f->arp, a); + return; + } + + /* remove all but the last message */ + while((bp = a->hold) != nil){ + if(bp == a->last) + break; + a->hold = bp->list; + freeblist(bp); + } + + /* try to keep it around for a second more */ + a->time = msec; + a->rxtat = msec + ReTransTimer; + if(a->rxtsrem <= 0) { + arprelease(er->f->arp, a); + return; + } + + a->rxtsrem--; + arprelease(er->f->arp, a); + + if(sflag = ipv6anylocal(ifc, ipsrc)) + icmpns(er->f, ipsrc, sflag, a->ip, TARG_MULTI, ifc->mac); +} + /* * send a gratuitous arp to refresh arp caches */ @@ -397,7 +518,7 @@ sendgarp(Ipifc *ifc, uchar *ip) hnputs(e->type, ETARP); hnputs(e->hrd, 1); - hnputs(e->pro, ETIP); + hnputs(e->pro, ETIP4); e->hln = sizeof(e->sha); e->pln = sizeof(e->spa); hnputs(e->op, ARPREQUEST); @@ -429,13 +550,6 @@ recvarp(Ipifc *ifc) break; case ARPREPLY: - if(iplocalonifc(ifc, ip) || ipproxyifc(er->f, ifc, ip)){ - if(memcmp(e->sha, ifc->mac, sizeof(e->sha)) != 0){ - print("arprep: 0x%E/0x%E also has ip addr %V\n", - e->s, e->sha, e->spa); - break; - } - } arpenter(er->f, V4, e->spa, e->sha, sizeof(e->sha), 0); break; @@ -447,11 +561,8 @@ recvarp(Ipifc *ifc) /* check for someone that think's they're me */ v4tov6(ip, e->spa); if(iplocalonifc(ifc, ip) || ipproxyifc(er->f, ifc, ip)){ - if(memcmp(e->sha, ifc->mac, sizeof(e->sha)) != 0){ - print("arpreq: 0x%E/0x%E also has ip addr %V\n", - e->s, e->sha, e->spa); - break; - } + if(memcmp(e->sha, ifc->mac, sizeof(e->sha)) != 0) + print("arp: 0x%E also has ip addr %V\n", e->sha, e->spa); } else { if(memcmp(e->sha, ifc->mac, sizeof(e->sha)) == 0){ print("arp: %V also has ether addr %E\n", e->spa, e->sha); @@ -468,8 +579,6 @@ recvarp(Ipifc *ifc) if(!ipproxyifc(er->f, ifc, ip)) break; -/* print("arp: rem %I %E (for %I)\n", e->spa, e->sha, e->tpa); /**/ - n = sizeof(Etherarp); if(n < ifc->minmtu) n = ifc->minmtu; @@ -478,7 +587,7 @@ recvarp(Ipifc *ifc) memset(r, 0, sizeof(Etherarp)); hnputs(r->type, ETARP); hnputs(r->hrd, 1); - hnputs(r->pro, ETIP); + hnputs(r->pro, ETIP4); r->hln = sizeof(r->sha); r->pln = sizeof(r->spa); hnputs(r->op, ARPREPLY); @@ -526,14 +635,14 @@ multicastea(uchar *ea, uchar *ip) ea[4] = ip[14]; ea[5] = ip[15]; break; - case V6: - ea[0] = 0x33; - ea[1] = 0x33; - ea[2] = ip[12]; + case V6: + ea[0] = 0x33; + ea[1] = 0x33; + ea[2] = ip[12]; ea[3] = ip[13]; - ea[4] = ip[14]; - ea[5] = ip[15]; - break; + ea[4] = ip[14]; + ea[5] = ip[15]; + break; } return x; } @@ -573,3 +682,17 @@ ethermediumlink(void) addipmedium(ðermedium); addipmedium(&gbemedium); } + + +static void +etherpref2addr(uchar *pref, uchar *ea) +{ + pref[8] = ea[0] | 0x2; + pref[9] = ea[1]; + pref[10] = ea[2]; + pref[11] = 0xFF; + pref[12] = 0xFE; + pref[13] = ea[3]; + pref[14] = ea[4]; + pref[15] = ea[5]; +} diff --git a/ip/gre.c b/ip/gre.c index b53004bf270bf15880127574daac61007704865c..1226f27a14581ae89197d70a85d764f8b3b16d9a 100644 --- a/ip/gre.c +++ b/ip/gre.c @@ -140,6 +140,7 @@ grekick(Conv *c) return; ghp = (GREhdr *)(bp->rp); + ghp->vihl = IP_VER4; v4tov6(raddr, ghp->dst); if(ipcmp(raddr, v4prefix) == 0) @@ -156,7 +157,7 @@ grekick(Conv *c) ghp->frag[0] = 0; ghp->frag[1] = 0; - ipoput(c->p->f, bp, 0, c->ttl, c->tos); + ipoput4(c->p->f, bp, 0, c->ttl, c->tos); } static void diff --git a/ip/icmp.c b/ip/icmp.c index 92d69515208246f4955c2608bd1194dd8cb4c397..9c0d2cfacf7525a5e118e10de8831fea8a144374 100644 --- a/ip/icmp.c +++ b/ip/icmp.c @@ -28,7 +28,6 @@ typedef struct Icmp { enum { /* Packet Types */ EchoReply = 0, - UnreachableV6 = 1, Unreachable = 3, SrcQuench = 4, Redirect = 5, @@ -52,7 +51,6 @@ enum char *icmpnames[Maxtype+1] = { -[UnreachableV6] "UnreachableV6", [EchoReply] "EchoReply", [Unreachable] "Unreachable", [SrcQuench] "SrcQuench", @@ -106,7 +104,7 @@ struct Icmppriv ulong out[Maxtype+1]; }; -static char* +extern char* icmpconnect(Conv *c, char **argv, int argc) { char *e; @@ -117,21 +115,21 @@ icmpconnect(Conv *c, char **argv, int argc) return e; } -static int +extern int icmpstate(Conv *c, char *state, int n) { USED(c); return snprint(state, n, "%s", "Datagram"); } -static void +extern void icmpcreate(Conv *c) { c->rq = qopen(64*1024, 1, 0, c); c->wq = qopen(64*1024, 0, 0, 0); } -static char* +extern char* icmpannounce(Conv *c, char **argv, int argc) { char *e; @@ -144,7 +142,7 @@ icmpannounce(Conv *c, char **argv, int argc) return nil; } -static void +extern void icmpclose(Conv *c) { qclose(c->rq); @@ -154,7 +152,7 @@ icmpclose(Conv *c) c->lport = 0; } -static void +extern void icmpkick(Conv *c) { Icmp *p; @@ -170,6 +168,7 @@ icmpkick(Conv *c) return; } p = (Icmp *)(bp->rp); + p->vihl = IP_VER4; ipriv = c->p->priv; if(p->type <= Maxtype) ipriv->out[p->type]++; @@ -181,23 +180,24 @@ icmpkick(Conv *c) memset(p->cksum, 0, sizeof(p->cksum)); hnputs(p->cksum, ptclcsum(bp, ICMP_IPSIZE, blocklen(bp) - ICMP_IPSIZE)); ipriv->stats[OutMsgs]++; - ipoput(c->p->f, bp, 0, c->ttl, c->tos); + ipoput4(c->p->f, bp, 0, c->ttl, c->tos); } extern void -icmpttlexceeded(Fs *f, Ipifc *ifc, Block *bp) +icmpttlexceeded(Fs *f, uchar *ia, Block *bp) { Block *nbp; Icmp *p, *np; p = (Icmp *)bp->rp; + p->vihl = IP_VER4; netlog(f, Logicmp, "sending icmpttlexceeded -> %V\n", p->src); nbp = allocb(ICMP_IPSIZE + ICMP_HDRSIZE + ICMP_IPSIZE + 8); nbp->wp += ICMP_IPSIZE + ICMP_HDRSIZE + ICMP_IPSIZE + 8; np = (Icmp *)nbp->rp; memmove(np->dst, p->src, sizeof(np->dst)); - v6tov4(np->src, ifc->lifc->local); + v6tov4(np->src, ia); memmove(np->data, bp->rp, ICMP_IPSIZE + 8); np->type = TimeExceed; np->code = 0; @@ -206,7 +206,7 @@ icmpttlexceeded(Fs *f, Ipifc *ifc, Block *bp) hnputs(np->seq, 0); memset(np->cksum, 0, sizeof(np->cksum)); hnputs(np->cksum, ptclcsum(nbp, ICMP_IPSIZE, blocklen(nbp) - ICMP_IPSIZE)); - ipoput(f, nbp, 0, MAXTTL, DFLTTOS); + ipoput4(f, nbp, 0, MAXTTL, DFLTTOS); } @@ -219,6 +219,7 @@ icmpnoconv(Fs *f, Block *bp) uchar addr[IPaddrlen]; p = (Icmp *)bp->rp; + p->vihl = IP_VER4; /* only do this for unicast sources and destinations */ v4tov6(addr, p->dst); @@ -244,7 +245,7 @@ icmpnoconv(Fs *f, Block *bp) hnputs(np->seq, 0); memset(np->cksum, 0, sizeof(np->cksum)); hnputs(np->cksum, ptclcsum(nbp, ICMP_IPSIZE, blocklen(nbp) - ICMP_IPSIZE)); - ipoput(f, nbp, 0, MAXTTL, DFLTTOS); + ipoput4(f, nbp, 0, MAXTTL, DFLTTOS); } static void @@ -279,6 +280,7 @@ mkechoreply(Block *bp) uchar ip[4]; q = (Icmp *)bp->rp; + q->vihl = IP_VER4; memmove(ip, q->src, sizeof(q->dst)); memmove(q->src, q->dst, sizeof(q->src)); memmove(q->dst, ip, sizeof(q->dst)); @@ -295,7 +297,7 @@ static char *unreachcode[] = [1] "host unreachable", [2] "protocol unreachable", [3] "port unreachable", -[4] "unfragmentable", +[4] "fragmentation needed and DF set", [5] "source route failed", }; @@ -309,9 +311,6 @@ icmpiput(Proto *icmp, Ipifc*, Block *bp) char *msg; char m2[128]; Icmppriv *ipriv; - int code; - ushort x; - uchar dst[IPaddrlen]; ipriv = icmp->priv; @@ -346,27 +345,13 @@ icmpiput(Proto *icmp, Ipifc*, Block *bp) case EchoRequest: r = mkechoreply(bp); ipriv->out[EchoReply]++; - ipoput(icmp->f, r, 0, MAXTTL, DFLTTOS); + ipoput4(icmp->f, r, 0, MAXTTL, DFLTTOS); break; case Unreachable: - code = p->code; - x = 0; - switch(code){ - default: + if(p->code > 5 || p->code < 0) msg = unreachcode[1]; - break; - case 5: - x = nhgets(p->seq); - msg = unreachcode[p->code]; - break; - case 0: - case 1: - case 2: - case 3: - case 4: + else msg = unreachcode[p->code]; - break; - } bp->rp += ICMP_IPSIZE+ICMP_HDRSIZE; if(blocklen(bp) < MinAdvise){ @@ -374,10 +359,6 @@ icmpiput(Proto *icmp, Ipifc*, Block *bp) goto raise; } p = (Icmp *)bp->rp; - if(code == 5){ - v4tov6(dst, p->dst); - update_mtucache(dst, x); - } pr = Fsrcvpcolx(icmp->f, p->proto); if(pr != nil && pr->advise != nil) { (*pr->advise)(pr, bp, msg); diff --git a/ip/icmp6.c b/ip/icmp6.c new file mode 100644 index 0000000000000000000000000000000000000000..b578779f7d7482f07abb7fde813f42da7b67f15f --- /dev/null +++ b/ip/icmp6.c @@ -0,0 +1,923 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "ip.h" +#include "ipv6.h" + +typedef struct ICMPpkt ICMPpkt; +typedef struct IPICMP IPICMP; +typedef struct Ndpkt Ndpkt; +typedef struct NdiscC NdiscC; + +struct ICMPpkt { + uchar type; + uchar code; + uchar cksum[2]; + uchar icmpid[2]; + uchar seq[2]; +}; + +struct IPICMP { + Ip6hdr; + ICMPpkt; +}; + +struct NdiscC +{ + IPICMP; + uchar target[IPaddrlen]; +}; + +struct Ndpkt +{ + NdiscC; + uchar otype; + uchar olen; // length in units of 8 octets(incl type, code), + // 1 for IEEE 802 addresses + uchar lnaddr[6]; // link-layer address +}; + +enum { + // ICMPv6 types + EchoReply = 0, + UnreachableV6 = 1, + PacketTooBigV6 = 2, + TimeExceedV6 = 3, + SrcQuench = 4, + ParamProblemV6 = 4, + Redirect = 5, + EchoRequest = 8, + TimeExceed = 11, + InParmProblem = 12, + Timestamp = 13, + TimestampReply = 14, + InfoRequest = 15, + InfoReply = 16, + AddrMaskRequest = 17, + AddrMaskReply = 18, + EchoRequestV6 = 128, + EchoReplyV6 = 129, + RouterSolicit = 133, + RouterAdvert = 134, + NbrSolicit = 135, + NbrAdvert = 136, + RedirectV6 = 137, + + Maxtype6 = 137, +}; + +char *icmpnames6[Maxtype6+1] = +{ +[EchoReply] "EchoReply", +[UnreachableV6] "UnreachableV6", +[PacketTooBigV6] "PacketTooBigV6", +[TimeExceedV6] "TimeExceedV6", +[SrcQuench] "SrcQuench", +[Redirect] "Redirect", +[EchoRequest] "EchoRequest", +[TimeExceed] "TimeExceed", +[InParmProblem] "InParmProblem", +[Timestamp] "Timestamp", +[TimestampReply] "TimestampReply", +[InfoRequest] "InfoRequest", +[InfoReply] "InfoReply", +[AddrMaskRequest] "AddrMaskRequest", +[AddrMaskReply] "AddrMaskReply", +[EchoRequestV6] "EchoRequestV6", +[EchoReplyV6] "EchoReplyV6", +[RouterSolicit] "RouterSolicit", +[RouterAdvert] "RouterAdvert", +[NbrSolicit] "NbrSolicit", +[NbrAdvert] "NbrAdvert", +[RedirectV6] "RedirectV6", +}; + +enum +{ + InMsgs6, + InErrors6, + OutMsgs6, + CsumErrs6, + LenErrs6, + HlenErrs6, + HoplimErrs6, + IcmpCodeErrs6, + TargetErrs6, + OptlenErrs6, + AddrmxpErrs6, + RouterAddrErrs6, + + Nstats6, +}; + +static char *statnames6[Nstats6] = +{ +[InMsgs6] "InMsgs", +[InErrors6] "InErrors", +[OutMsgs6] "OutMsgs", +[CsumErrs6] "CsumErrs", +[LenErrs6] "LenErrs", +[HlenErrs6] "HlenErrs", +[HoplimErrs6] "HoplimErrs", +[IcmpCodeErrs6] "IcmpCodeErrs", +[TargetErrs6] "TargetErrs", +[OptlenErrs6] "OptlenErrs", +[AddrmxpErrs6] "AddrmxpErrs", +[RouterAddrErrs6] "RouterAddrErrs", +}; + +typedef struct Icmppriv6 +{ + ulong stats[Nstats6]; + + /* message counts */ + ulong in[Maxtype6+1]; + ulong out[Maxtype6+1]; +} Icmppriv6; + +typedef struct Icmpcb6 +{ + QLock; + uchar headers; +} Icmpcb6; + +static char *unreachcode[] = +{ +[icmp6_no_route] "no route to destination", +[icmp6_ad_prohib] "comm with destination administratively prohibited", +[icmp6_unassigned] "icmp unreachable: unassigned error code (2)", +[icmp6_adr_unreach] "address unreachable", +[icmp6_port_unreach] "port unreachable", +[icmp6_unkn_code] "icmp unreachable: unknown code", +}; + +enum { + ICMP_USEAD6 = 40, +}; + +enum { + Oflag = 1<<5, + Sflag = 1<<6, + Rflag = 1<<7, +}; + +enum { + slladd = 1, + tlladd = 2, + prfinfo = 3, + redhdr = 4, + mtuopt = 5, +}; + +static void +set_cksum(Block *bp) +{ + IPICMP *p = (IPICMP *)(bp->rp); + + hnputl(p->vcf, 0); // borrow IP header as pseudoheader + hnputs(p->ploadlen, blocklen(bp)-IPV6HDR_LEN); + p->proto = 0; + p->ttl = ICMPv6; // ttl gets set later + hnputs(p->cksum, 0); + hnputs(p->cksum, ptclcsum(bp, 0, blocklen(bp))); + p->proto = ICMPv6; +} + +static Block * +newIPICMP(int packetlen) +{ + Block *nbp; + nbp = allocb(packetlen); + nbp->wp += packetlen; + memset(nbp->rp, 0, packetlen); + return nbp; +} + + +char* +icmpconnect6(Conv *c, char **argv, int argc) +{ + char *e; + + e = Fsstdconnect(c, argv, argc); + Fsconnected(c, e); + + return e; +} + +void +icmpadvise6(Proto *icmp, Block *bp, char *msg) +{ + Conv **c, *s; + IPICMP *p; + ushort recid; + + p = (IPICMP *) bp->rp; + recid = nhgets(p->icmpid); + + for(c = icmp->conv; *c; c++) { + s = *c; + if(s->lport == recid) + if(ipcmp(s->raddr, p->dst) == 0){ + qhangup(s->rq, msg); + qhangup(s->wq, msg); + break; + } + } + freeblist(bp); +} + +void +icmpkick6(Conv *c) +{ + IPICMP *p; + Block *bp; + uchar laddr[IPaddrlen], raddr[IPaddrlen]; + Icmppriv6 *ipriv = c->p->priv; + Icmpcb6 *icb = (Icmpcb6*)c->ptcl; + + bp = qget(c->wq); + if(bp == nil) + return; + + if(icb->headers==6) { + /* get user specified addresses */ +//print("icmpkick6: headers\n"); + bp = pullupblock(bp, ICMP_USEAD6); + if(bp == nil) + return; + bp->rp += 8; + ipmove(laddr, bp->rp); + bp->rp += IPaddrlen; + ipmove(raddr, bp->rp); + bp->rp += IPaddrlen; + bp = padblock(bp, sizeof(Ip6hdr)); + } + + if(blocklen(bp) < sizeof(IPICMP)){ + freeblist(bp); + return; + } + p = (IPICMP *)(bp->rp); + if(icb->headers == 6) { + ipmove(p->dst, raddr); + ipmove(p->src, laddr); + } else { + ipmove(p->dst, c->raddr); + ipmove(p->src, c->laddr); + hnputs(p->icmpid, c->lport); + } + + set_cksum(bp); + p->vcf[0] = 0x06 << 4; + if(p->type <= Maxtype6) + ipriv->out[p->type]++; +//print("icmpkick6: src %I dst %I\n", p->src, p->dst); + ipoput6(c->p->f, bp, 0, c->ttl, c->tos); +} + +char* +icmpctl6(Conv *c, char **argv, int argc) +{ + Icmpcb6 *icb; + + icb = (Icmpcb6*) c->ptcl; + + if(argc==1) { + if(strcmp(argv[0], "headers")==0) { + icb->headers = 6; + return nil; + } + } + return "unknown control request"; +} + +static void +goticmpkt6(Proto *icmp, Block *bp, int muxkey) +{ + Conv **c, *s; + IPICMP *p = (IPICMP *)bp->rp; + ushort recid; + uchar *addr; + + if(muxkey == 0) { + recid = nhgets(p->icmpid); + addr = p->src; + } + else { + recid = muxkey; + addr = p->dst; + } + + for(c = icmp->conv; *c; c++){ + s = *c; + if(s->lport == recid && ipcmp(s->raddr, addr) == 0){ + bp = concatblock(bp); + if(bp != nil) + qpass(s->rq, bp); + return; + } + } + + freeblist(bp); +} + +static Block * +mkechoreply6(Block *bp) +{ + IPICMP *p = (IPICMP *)(bp->rp); + uchar addr[IPaddrlen]; + + ipmove(addr, p->src); + ipmove(p->src, p->dst); + ipmove(p->dst, addr); + p->type = EchoReplyV6; + set_cksum(bp); + return bp; +} + +/* + * sends out an ICMPv6 neighbor solicitation + * suni == SRC_UNSPEC or SRC_UNI, + * tuni == TARG_MULTI => multicast for address resolution, + * and tflag == TARG_UNI => neighbor reachability. + */ + +extern void +icmpns(Fs *f, uchar* src, int suni, uchar* targ, int tuni, uchar* mac) +{ + Block *nbp; + Ndpkt *np; + Proto *icmp = f->t2p[ICMPv6]; + Icmppriv6 *ipriv = icmp->priv; + + + nbp = newIPICMP(sizeof(Ndpkt)); + np = (Ndpkt*) nbp->rp; + + + if(suni == SRC_UNSPEC) + memmove(np->src, v6Unspecified, IPaddrlen); + else + memmove(np->src, src, IPaddrlen); + + if(tuni == TARG_UNI) + memmove(np->dst, targ, IPaddrlen); + else + ipv62smcast(np->dst, targ); + + np->type = NbrSolicit; + np->code = 0; + memmove(np->target, targ, IPaddrlen); + if(suni != SRC_UNSPEC) { + np->otype = SRC_LLADDRESS; + np->olen = 1; /* 1+1+6 = 8 = 1 8-octet */ + memmove(np->lnaddr, mac, sizeof(np->lnaddr)); + } + else { + int r = sizeof(Ndpkt)-sizeof(NdiscC); + nbp->wp -= r; + } + + set_cksum(nbp); + np = (Ndpkt*) nbp->rp; + np->ttl = HOP_LIMIT; + np->vcf[0] = 0x06 << 4; + ipriv->out[NbrSolicit]++; + netlog(f, Logicmp, "sending neighbor solicitation %I\n", targ); + ipoput6(f, nbp, 0, MAXTTL, DFLTTOS); +} + +/* + * sends out an ICMPv6 neighbor advertisement. pktflags == RSO flags. + */ +extern void +icmpna(Fs *f, uchar* src, uchar* dst, uchar* targ, uchar* mac, uchar flags) +{ + Block *nbp; + Ndpkt *np; + Proto *icmp = f->t2p[ICMPv6]; + Icmppriv6 *ipriv = icmp->priv; + + nbp = newIPICMP(sizeof(Ndpkt)); + np = (Ndpkt*) nbp->rp; + + memmove(np->src, src, IPaddrlen); + memmove(np->dst, dst, IPaddrlen); + + np->type = NbrAdvert; + np->code = 0; + np->icmpid[0] = flags; + memmove(np->target, targ, IPaddrlen); + + np->otype = TARGET_LLADDRESS; + np->olen = 1; + memmove(np->lnaddr, mac, sizeof(np->lnaddr)); + + set_cksum(nbp); + np = (Ndpkt*) nbp->rp; + np->ttl = HOP_LIMIT; + np->vcf[0] = 0x06 << 4; + ipriv->out[NbrAdvert]++; + netlog(f, Logicmp, "sending neighbor advertisement %I\n", src); + ipoput6(f, nbp, 0, MAXTTL, DFLTTOS); +} + +extern void +icmphostunr(Fs *f, Ipifc *ifc, Block *bp, int code, int free) +{ + Block *nbp; + IPICMP *np; + Ip6hdr *p; + int osz = BLEN(bp); + int sz = MIN(sizeof(IPICMP) + osz, v6MINTU); + Proto *icmp = f->t2p[ICMPv6]; + Icmppriv6 *ipriv = icmp->priv; + + p = (Ip6hdr *) bp->rp; + + if(isv6mcast(p->src)) + goto clean; + + nbp = newIPICMP(sz); + np = (IPICMP *) nbp->rp; + + rlock(ifc); + if(ipv6anylocal(ifc, np->src)) { + netlog(f, Logicmp, "send icmphostunr -> s%I d%I\n", p->src, p->dst); + } + else { + netlog(f, Logicmp, "icmphostunr fail -> s%I d%I\n", p->src, p->dst); + freeblist(nbp); + if(free) + goto clean; + else + return; + } + + memmove(np->dst, p->src, IPaddrlen); + np->type = UnreachableV6; + np->code = code; + memmove(nbp->rp + sizeof(IPICMP), bp->rp, sz - sizeof(IPICMP)); + set_cksum(nbp); + np->ttl = HOP_LIMIT; + np->vcf[0] = 0x06 << 4; + ipriv->out[UnreachableV6]++; + + if(free) + ipiput6(f, ifc, nbp); + else { + ipoput6(f, nbp, 0, MAXTTL, DFLTTOS); + return; + } + +clean: + runlock(ifc); + freeblist(bp); +} + +extern void +icmpttlexceeded6(Fs *f, Ipifc *ifc, Block *bp) +{ + Block *nbp; + IPICMP *np; + Ip6hdr *p; + int osz = BLEN(bp); + int sz = MIN(sizeof(IPICMP) + osz, v6MINTU); + Proto *icmp = f->t2p[ICMPv6]; + Icmppriv6 *ipriv = icmp->priv; + + p = (Ip6hdr *) bp->rp; + + if(isv6mcast(p->src)) + return; + + nbp = newIPICMP(sz); + np = (IPICMP *) nbp->rp; + + if(ipv6anylocal(ifc, np->src)) { + netlog(f, Logicmp, "send icmpttlexceeded6 -> s%I d%I\n", p->src, p->dst); + } + else { + netlog(f, Logicmp, "icmpttlexceeded6 fail -> s%I d%I\n", p->src, p->dst); + return; + } + + memmove(np->dst, p->src, IPaddrlen); + np->type = TimeExceedV6; + np->code = 0; + memmove(nbp->rp + sizeof(IPICMP), bp->rp, sz - sizeof(IPICMP)); + set_cksum(nbp); + np->ttl = HOP_LIMIT; + np->vcf[0] = 0x06 << 4; + ipriv->out[TimeExceedV6]++; + ipoput6(f, nbp, 0, MAXTTL, DFLTTOS); +} + +extern void +icmppkttoobig6(Fs *f, Ipifc *ifc, Block *bp) +{ + Block *nbp; + IPICMP *np; + Ip6hdr *p; + int osz = BLEN(bp); + int sz = MIN(sizeof(IPICMP) + osz, v6MINTU); + Proto *icmp = f->t2p[ICMPv6]; + Icmppriv6 *ipriv = icmp->priv; + + p = (Ip6hdr *) bp->rp; + + if(isv6mcast(p->src)) + return; + + nbp = newIPICMP(sz); + np = (IPICMP *) nbp->rp; + + if(ipv6anylocal(ifc, np->src)) { + netlog(f, Logicmp, "send icmppkttoobig6 -> s%I d%I\n", p->src, p->dst); + } + else { + netlog(f, Logicmp, "icmppkttoobig6 fail -> s%I d%I\n", p->src, p->dst); + return; + } + + memmove(np->dst, p->src, IPaddrlen); + np->type = PacketTooBigV6; + np->code = 0; + hnputl(np->icmpid, ifc->maxmtu - ifc->m->hsize); + memmove(nbp->rp + sizeof(IPICMP), bp->rp, sz - sizeof(IPICMP)); + set_cksum(nbp); + np->ttl = HOP_LIMIT; + np->vcf[0] = 0x06 << 4; + ipriv->out[PacketTooBigV6]++; + ipoput6(f, nbp, 0, MAXTTL, DFLTTOS); +} + +/* + * RFC 2461, pages 39-40, pages 57-58. + */ +static int +valid(Proto *icmp, Ipifc *ifc, Block *bp, Icmppriv6 *ipriv) { + int sz, osz, unsp, n, ttl, iplen; + int pktsz = BLEN(bp); + uchar *packet = bp->rp; + IPICMP *p = (IPICMP *) packet; + Ndpkt *np; + + USED(ifc); + n = blocklen(bp); + if(n < sizeof(IPICMP)) { + ipriv->stats[HlenErrs6]++; + netlog(icmp->f, Logicmp, "icmp hlen %d\n", n); + goto err; + } + + iplen = nhgets(p->ploadlen); + if(iplen > n-IPV6HDR_LEN || (iplen % 1)) { + ipriv->stats[LenErrs6]++; + netlog(icmp->f, Logicmp, "icmp length %d\n", iplen); + goto err; + } + + // Rather than construct explicit pseudoheader, overwrite IPv6 header + if(p->proto != ICMPv6) { + // This code assumes no extension headers!!! + netlog(icmp->f, Logicmp, "icmp error: extension header\n"); + goto err; + } + memset(packet, 0, 4); + ttl = p->ttl; + p->ttl = p->proto; + p->proto = 0; + if(ptclcsum(bp, 0, iplen + IPV6HDR_LEN)) { + ipriv->stats[CsumErrs6]++; + netlog(icmp->f, Logicmp, "icmp checksum error\n"); + goto err; + } + p->proto = p->ttl; + p->ttl = ttl; + + /* additional tests for some pkt types */ + if( (p->type == NbrSolicit) || + (p->type == NbrAdvert) || + (p->type == RouterAdvert) || + (p->type == RouterSolicit) || + (p->type == RedirectV6) ) { + + if(p->ttl != HOP_LIMIT) { + ipriv->stats[HoplimErrs6]++; + goto err; + } + if(p->code != 0) { + ipriv->stats[IcmpCodeErrs6]++; + goto err; + } + + switch (p->type) { + case NbrSolicit: + case NbrAdvert: + np = (Ndpkt*) p; + if(isv6mcast(np->target)) { + ipriv->stats[TargetErrs6]++; + goto err; + } + if(optexsts(np) && (np->olen == 0)) { + ipriv->stats[OptlenErrs6]++; + goto err; + } + + if(p->type == NbrSolicit) { + if(ipcmp(np->src, v6Unspecified) == 0) { + if(!issmcast(np->dst) || optexsts(np)) { + ipriv->stats[AddrmxpErrs6]++; + goto err; + } + } + } + + if(p->type == NbrAdvert) { + if((isv6mcast(np->dst))&&(nhgets(np->icmpid) & Sflag)){ + ipriv->stats[AddrmxpErrs6]++; + goto err; + } + } + break; + + case RouterAdvert: + if(pktsz - sizeof(Ip6hdr) < 16) { + ipriv->stats[HlenErrs6]++; + goto err; + } + if(!islinklocal(p->src)) { + ipriv->stats[RouterAddrErrs6]++; + goto err; + } + sz = sizeof(IPICMP) + 8; + while ((sz+1) < pktsz) { + osz = *(packet+sz+1); + if(osz <= 0) { + ipriv->stats[OptlenErrs6]++; + goto err; + } + sz += 8*osz; + } + break; + + case RouterSolicit: + if(pktsz - sizeof(Ip6hdr) < 8) { + ipriv->stats[HlenErrs6]++; + goto err; + } + unsp = (ipcmp(p->src, v6Unspecified) == 0); + sz = sizeof(IPICMP) + 8; + while ((sz+1) < pktsz) { + osz = *(packet+sz+1); + if((osz <= 0) || + (unsp && (*(packet+sz) == slladd)) ) { + ipriv->stats[OptlenErrs6]++; + goto err; + } + sz += 8*osz; + } + break; + + case RedirectV6: + //to be filled in + break; + + default: + goto err; + } + } + + return 1; + +err: + ipriv->stats[InErrors6]++; + return 0; +} + +static int +targettype(Fs *f, Ipifc *ifc, uchar *target) +{ + Iplifc *lifc; + int t; + + rlock(ifc); + if(ipproxyifc(f, ifc, target)) { + runlock(ifc); + return t_uniproxy; + } + + for(lifc = ifc->lifc; lifc; lifc = lifc->next) { + if(ipcmp(lifc->local, target) == 0) { + t = (lifc->tentative) ? t_unitent : t_unirany; + runlock(ifc); + return t; + } + } + + runlock(ifc); + return 0; +} + +static void +icmpiput6(Proto *icmp, Ipifc *ipifc, Block *bp) +{ + uchar *packet = bp->rp; + IPICMP *p = (IPICMP *)packet; + Icmppriv6 *ipriv = icmp->priv; + Block *r; + Proto *pr; + char *msg, m2[128]; + Ndpkt* np; + uchar pktflags; + uchar lsrc[IPaddrlen]; + int refresh = 1; + Iplifc *lifc; + + if(!valid(icmp, ipifc, bp, ipriv)) + goto raise; + + if(p->type <= Maxtype6) + ipriv->in[p->type]++; + + switch(p->type) { + case EchoRequestV6: + r = mkechoreply6(bp); + ipriv->out[EchoReply]++; + ipoput6(icmp->f, r, 0, MAXTTL, DFLTTOS); + break; + + case UnreachableV6: + if(p->code > 4 || p->code < 0) + msg = unreachcode[icmp6_unkn_code]; + else + msg = unreachcode[p->code]; + + bp->rp += sizeof(IPICMP); + if(blocklen(bp) < 8){ + ipriv->stats[LenErrs6]++; + goto raise; + } + p = (IPICMP *)bp->rp; + pr = Fsrcvpcolx(icmp->f, p->proto); + if(pr != nil && pr->advise != nil) { + (*pr->advise)(pr, bp, msg); + return; + } + + bp->rp -= sizeof(IPICMP); + goticmpkt6(icmp, bp, 0); + break; + + case TimeExceedV6: + if(p->code == 0){ + sprint(m2, "ttl exceeded at %I", p->src); + + bp->rp += sizeof(IPICMP); + if(blocklen(bp) < 8){ + ipriv->stats[LenErrs6]++; + goto raise; + } + p = (IPICMP *)bp->rp; + pr = Fsrcvpcolx(icmp->f, p->proto); + if(pr != nil && pr->advise != nil) { + (*pr->advise)(pr, bp, m2); + return; + } + bp->rp -= sizeof(IPICMP); + } + + goticmpkt6(icmp, bp, 0); + break; + + case RouterAdvert: + case RouterSolicit: + /* using lsrc as a temp, munge hdr for goticmp6 + memmove(lsrc, p->src, IPaddrlen); + memmove(p->src, p->dst, IPaddrlen); + memmove(p->dst, lsrc, IPaddrlen); */ + + goticmpkt6(icmp, bp, p->type); + break; + + case NbrSolicit: + np = (Ndpkt*) p; + pktflags = 0; + switch (targettype(icmp->f, ipifc, np->target)) { + case t_unirany: + pktflags |= Oflag; + /* fall through */ + + case t_uniproxy: + if(ipcmp(np->src, v6Unspecified) != 0) { + arpenter(icmp->f, V6, np->src, np->lnaddr, 8*np->olen-2, 0); + pktflags |= Sflag; + } + if(ipv6local(ipifc, lsrc)) { + icmpna(icmp->f, lsrc, + (ipcmp(np->src, v6Unspecified)==0)?v6allnodesL:np->src, + np->target, ipifc->mac, pktflags); + } + else + freeblist(bp); + break; + + case t_unitent: + /* not clear what needs to be done. send up + * an icmp mesg saying don't use this address? */ + + default: + freeblist(bp); + } + + ipriv->out[NbrSolicit]++; + break; + + case NbrAdvert: + np = (Ndpkt*) p; + + /* if the target address matches one of the local interface + * address and the local interface address has tentative bit set, + * then insert into ARP table. this is so the duplication address + * detection part of ipconfig can discover duplication through + * the arp table + */ + lifc = iplocalonifc(ipifc, np->target); + if(lifc && lifc->tentative) + refresh = 0; + arpenter(icmp->f, V6, np->target, np->lnaddr, 8*np->olen-2, refresh); + freeblist(bp); + break; + + case PacketTooBigV6: + + default: + goticmpkt6(icmp, bp, 0); + break; + } + return; + +raise: + freeblist(bp); + +} + +int +icmpstats6(Proto *icmp6, char *buf, int len) +{ + Icmppriv6 *priv; + char *p, *e; + int i; + + priv = icmp6->priv; + p = buf; + e = p+len; + for(i = 0; i < Nstats6; i++) + p = seprint(p, e, "%s: %lud\n", statnames6[i], priv->stats[i]); + for(i = 0; i <= Maxtype6; i++){ + if(icmpnames6[i]) + p = seprint(p, e, "%s: %lud %lud\n", icmpnames6[i], priv->in[i], priv->out[i]); +/* else + p = seprint(p, e, "%d: %lud %lud\n", i, priv->in[i], priv->out[i]); +*/ + } + return p - buf; +} + + +// need to import from icmp.c +extern int icmpstate(Conv *c, char *state, int n); +extern char* icmpannounce(Conv *c, char **argv, int argc); +extern void icmpcreate(Conv *c); +extern void icmpclose(Conv *c); + +void +icmp6init(Fs *fs) +{ + Proto *icmp6 = smalloc(sizeof(Proto)); + + icmp6->priv = smalloc(sizeof(Icmppriv6)); + icmp6->name = "icmpv6"; + icmp6->kick = icmpkick6; + icmp6->connect = icmpconnect6; + icmp6->announce = icmpannounce; + icmp6->state = icmpstate; + icmp6->create = icmpcreate; + icmp6->close = icmpclose; + icmp6->rcv = icmpiput6; + icmp6->stats = icmpstats6; + icmp6->ctl = icmpctl6; + icmp6->advise = icmpadvise6; + icmp6->gc = nil; + icmp6->ipproto = ICMPv6; + icmp6->nc = 16; + icmp6->ptclsize = sizeof(Icmpcb6); + + Fsproto(fs, icmp6); +} + diff --git a/ip/igmp.c b/ip/igmp.c index 10f58993bdda0c6e1a64ace4268eb362604ee19d..f1365dac3e99f874b4ef1eafbc1d8959af55736b 100644 --- a/ip/igmp.c +++ b/ip/igmp.c @@ -86,6 +86,7 @@ igmpsendreport(Media *m, byte *addr) if(bp == nil) return; p = (IGMPpkt*)bp->wp; + p->vihl = IP_VER4; bp->wp += sizeof(IGMPpkt); memset(bp->rp, 0, sizeof(IGMPpkt)); hnputl(p->src, Mediagetaddr(m)); @@ -96,7 +97,7 @@ igmpsendreport(Media *m, byte *addr) hnputs(p->igmpcksum, ptclcsum(bp, IGMP_IPHDRSIZE, IGMP_HDRSIZE)); netlog(Logigmp, "igmpreport %I\n", p->group); stats.outreports++; - ipoput(bp, 0, 1, DFLTTOS); /* TTL of 1 */ + ipoput4(bp, 0, 1, DFLTTOS); /* TTL of 1 */ } static int @@ -166,7 +167,7 @@ igmpproc(void *a) } void -igmpiput(Media *m, Ipifc*, Block *bp) +igmpiput(Media *m, Ipifc *, Block *bp) { int n; IGMPpkt *ghp; diff --git a/ip/il.c b/ip/il.c index 1ccef41ba19beb848bf485603933f5e31587cd74..ba051512cd5da7eed9a3017b1e9478d54a10873d 100644 --- a/ip/il.c +++ b/ip/il.c @@ -367,6 +367,7 @@ ilkick(Conv *c) /* Make space to fit il & ip */ bp = padblock(bp, IL_IPSIZE+IL_HDRSIZE); ih = (Ilhdr *)(bp->rp); + ih->vihl = IP_VER4; /* Ip fields */ ih->frag[0] = 0; @@ -408,7 +409,7 @@ ilkick(Conv *c) if(later(msec, ic->timeout, nil)) ilsettimeout(ic); - ipoput(f, bp, 0, c->ttl, c->tos); + ipoput4(f, bp, 0, c->ttl, c->tos); priv->stats[OutMsgs]++; } @@ -803,6 +804,7 @@ ilrexmit(Ilcb *ic) return; h = (Ilhdr*)nb->rp; + h->vihl = IP_VER4; h->iltype = Ildataquery; hnputl(h->ilack, ic->recvd); @@ -819,7 +821,7 @@ ilrexmit(Ilcb *ic) ilbackoff(ic); - ipoput(c->p->f, nb, 0, ic->conv->ttl, ic->conv->tos); + ipoput4(c->p->f, nb, 0, ic->conv->ttl, ic->conv->tos); /* statistics */ ic->rxtot++; @@ -979,6 +981,7 @@ ilsendctl(Conv *ipc, Ilhdr *inih, int type, ulong id, ulong ack, int ilspec) bp->wp += IL_IPSIZE+IL_HDRSIZE; ih = (Ilhdr *)(bp->rp); + ih->vihl = IP_VER4; /* Ip fields */ ih->proto = IP_ILPROTO; @@ -1015,6 +1018,7 @@ ilsendctl(Conv *ipc, Ilhdr *inih, int type, ulong id, ulong ack, int ilspec) if(ilcksum) hnputs(ih->ilsum, ptclcsum(bp, IL_IPSIZE, IL_HDRSIZE)); + if(ipc==nil) panic("ipc is nil caller is %.8lux", getcallerpc(&ipc)); if(ipc->p==nil) @@ -1024,7 +1028,7 @@ if(ipc->p==nil) iltype[ih->iltype], nhgetl(ih->ilid), nhgetl(ih->ilack), nhgets(ih->ilsrc), nhgets(ih->ildst)); - ipoput(ipc->p->f, bp, 0, ttl, tos); + ipoput4(ipc->p->f, bp, 0, ttl, tos); } void @@ -1037,6 +1041,7 @@ ilreject(Fs *f, Ilhdr *inih) bp->wp += IL_IPSIZE+IL_HDRSIZE; ih = (Ilhdr *)(bp->rp); + ih->vihl = IP_VER4; /* Ip fields */ ih->proto = IP_ILPROTO; @@ -1057,7 +1062,7 @@ ilreject(Fs *f, Ilhdr *inih) if(ilcksum) hnputs(ih->ilsum, ptclcsum(bp, IL_IPSIZE, IL_HDRSIZE)); - ipoput(f, bp, 0, MAXTTL, DFLTTOS); + ipoput4(f, bp, 0, MAXTTL, DFLTTOS); } void diff --git a/ip/ip.c b/ip/ip.c index bba409fc7b475177df38d9e9f49ac9545b8f563e..097467d983558bc58b47c09dedfc070a68eb7538 100644 --- a/ip/ip.c +++ b/ip/ip.c @@ -5,52 +5,41 @@ #include "fns.h" #include "../port/error.h" -#include "../ip/ip.h" +#include "ip.h" -typedef struct Iphdr Iphdr; -typedef struct Fragment Fragment; -typedef struct Ipfrag Ipfrag; +typedef struct Ip4hdr Ip4hdr; +typedef struct IP IP; +typedef struct Fragment4 Fragment4; +typedef struct Fragment6 Fragment6; +typedef struct Ipfrag Ipfrag; enum { - IPHDR = 20, /* sizeof(Iphdr) */ - IP_VER = 0x40, /* Using IP version 4 */ - IP_HLEN = 0x05, /* Header length in words */ + IP4HDR = 20, /* sizeof(Ip4hdr) */ + IP6HDR = 40, /* sizeof(Ip6hdr) */ + IP_HLEN4 = 0x05, /* Header length in words */ IP_DF = 0x4000, /* Don't fragment */ IP_MF = 0x2000, /* More fragments */ + IP6FHDR = 8, /* sizeof(Fraghdr6) */ IP_MAX = (32*1024), /* Maximum Internet packet size */ }; -struct Iphdr +#define BLKIPVER(xp) (((Ip4hdr*)((xp)->rp))->vihl&0xF0) + +struct Ip4hdr { uchar vihl; /* Version and header length */ uchar tos; /* Type of service */ uchar length[2]; /* packet length */ uchar id[2]; /* ip->identification */ uchar frag[2]; /* Fragment information */ - uchar ttl; /* Time to live */ + uchar ttl; /* Time to live */ uchar proto; /* Protocol */ uchar cksum[2]; /* Header checksum */ uchar src[4]; /* IP source */ uchar dst[4]; /* IP destination */ }; -struct Fragment -{ - Block* blist; - Fragment* next; - ulong src; - ulong dst; - ushort id; - ulong age; -}; - -struct Ipfrag -{ - ushort foff; - ushort flen; -}; - /* MIB II counters */ enum { @@ -77,6 +66,50 @@ enum Nstats, }; +struct Fragment4 +{ + Block* blist; + Fragment4* next; + ulong src; + ulong dst; + ushort id; + ulong age; +}; + +struct Fragment6 +{ + Block* blist; + Fragment6* next; + uchar src[IPaddrlen]; + uchar dst[IPaddrlen]; + uint id; + ulong age; +}; + +struct Ipfrag +{ + ushort foff; + ushort flen; +}; + +/* an instance of IP */ +struct IP +{ + ulong stats[Nstats]; + + QLock fraglock4; + Fragment4* flisthead4; + Fragment4* fragfree4; + Ref id4; + + QLock fraglock6; + Fragment6* flisthead6; + Fragment6* fragfree6; + Ref id6; + + int iprouting; /* true if we route like a gateway */ +}; + static char *statnames[] = { [Forwarding] "Forwarding", @@ -100,20 +133,7 @@ static char *statnames[] = [FragCreates] "FragCreates", }; -/* an instance of IP */ -struct IP -{ - ulong stats[Nstats]; - - QLock fraglock; - Fragment* flisthead; - Fragment* fragfree; - - Ref id; - int iprouting; /* true if we route like a gateway */ -}; - -#define BLKIP(xp) ((Iphdr*)((xp)->rp)) +#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 @@ -121,9 +141,62 @@ struct IP #define BKFG(xp) ((Ipfrag*)((xp)->base)) ushort ipcsum(uchar*); -Block* ipreassemble(IP*, int, Block*, Iphdr*); -void ipfragfree(IP*, Fragment*); -Fragment* ipfragallo(IP*); +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) @@ -133,6 +206,8 @@ ip_init(Fs *f) ip = smalloc(sizeof(IP)); initfrag(ip, 100); f->ip = ip; + + ip_init_6(f); } void @@ -146,13 +221,13 @@ iprouting(Fs *f, int on) } void -ipoput(Fs *f, Block *bp, int gating, int ttl, int tos) +ipoput4(Fs *f, Block *bp, int gating, int ttl, int tos) { Ipifc *ifc; uchar *gate; ulong fragoff; Block *xp, *nb; - Iphdr *eh, *feh; + Ip4hdr *eh, *feh; int lid, len, seglen, chunk, dlen, blklen, offset, medialen; Route *r, *sr; IP *ip; @@ -160,7 +235,7 @@ ipoput(Fs *f, Block *bp, int gating, int ttl, int tos) ip = f->ip; /* Fill out the ip header */ - eh = (Iphdr*)(bp->rp); + eh = (Ip4hdr*)(bp->rp); ip->stats[OutRequests]++; @@ -204,7 +279,7 @@ ipoput(Fs *f, Block *bp, int gating, int ttl, int tos) gate = r->v4.gate; if(!gating) - eh->vihl = IP_VER|IP_HLEN; + eh->vihl = IP_VER4|IP_HLEN4; eh->ttl = ttl; if(!gating) eh->tos = tos; @@ -222,7 +297,7 @@ ipoput(Fs *f, Block *bp, int gating, int ttl, int tos) medialen = ifc->maxmtu - ifc->m->hsize; if(len <= medialen) { if(!gating) - hnputs(eh->id, incref(&ip->id)); + hnputs(eh->id, incref(&ip->id4)); hnputs(eh->length, len); if(!gating){ eh->frag[0] = 0; @@ -231,8 +306,6 @@ ipoput(Fs *f, Block *bp, int gating, int ttl, int tos) eh->cksum[0] = 0; eh->cksum[1] = 0; hnputs(eh->cksum, ipcsum(&eh->vihl)); - -/* print("ipoput %V->%V via %V\n", eh->src, eh->dst, gate); /**/ ifc->m->bwrite(ifc, bp, V4, gate); runlock(ifc); poperror(); @@ -246,7 +319,7 @@ ipoput(Fs *f, Block *bp, int gating, int ttl, int tos) goto raise; } - seglen = (medialen - IPHDR) & ~7; + seglen = (medialen - IP4HDR) & ~7; if(seglen < 8){ ip->stats[FragFails]++; ip->stats[OutDiscards]++; @@ -254,14 +327,14 @@ ipoput(Fs *f, Block *bp, int gating, int ttl, int tos) goto raise; } - dlen = len - IPHDR; + dlen = len - IP4HDR; xp = bp; if(gating) lid = nhgets(eh->id); else - lid = incref(&ip->id); + lid = incref(&ip->id4); - offset = IPHDR; + offset = IP4HDR; while(xp != nil && offset && offset >= BLEN(xp)) { offset -= BLEN(xp); xp = xp->next; @@ -274,11 +347,11 @@ ipoput(Fs *f, Block *bp, int gating, int ttl, int tos) fragoff = 0; dlen += fragoff; for(; fragoff < dlen; fragoff += seglen) { - nb = allocb(IPHDR+seglen); - feh = (Iphdr*)(nb->rp); + nb = allocb(IP4HDR+seglen); + feh = (Ip4hdr*)(nb->rp); - memmove(nb->wp, eh, IPHDR); - nb->wp += IPHDR; + memmove(nb->wp, eh, IP4HDR); + nb->wp += IP4HDR; if((fragoff + seglen) >= dlen) { seglen = dlen - fragoff; @@ -287,7 +360,7 @@ ipoput(Fs *f, Block *bp, int gating, int ttl, int tos) else hnputs(feh->frag, (fragoff>>3)|IP_MF); - hnputs(feh->length, seglen + IPHDR); + hnputs(feh->length, seglen + IP4HDR); hnputs(feh->id, lid); /* Copy up the data area */ @@ -326,43 +399,26 @@ free: } void -initfrag(IP *ip, int size) -{ - Fragment *fq, *eq; - - ip->fragfree = (Fragment*)malloc(sizeof(Fragment) * size); - if(ip->fragfree == nil) - panic("initfrag"); - - eq = &ip->fragfree[size]; - for(fq = ip->fragfree; fq < eq; fq++) - fq->next = fq+1; - - ip->fragfree[size-1].next = nil; -} - -#define DBG(x) if((logmask & Logipmsg) && (iponly == 0 || x == iponly))netlog - -void -ipiput(Fs *f, Ipifc *ifc, Block *bp) +ipiput4(Fs *f, Ipifc *ifc, Block *bp) { int hl; - Iphdr *h; + int hop, tos, proto, olen; + Ip4hdr *h; Proto *p; ushort frag; int notforme; uchar *dp, v6dst[IPaddrlen]; IP *ip; Route *r, *sr; - int olen; + + if(BLKIPVER(bp) != IP_VER4) { + ipiput6(f, ifc, bp); + return; + } ip = f->ip; ip->stats[InReceives]++; -// h = (Iphdr *)(bp->rp); -// DBG(nhgetl(h->src))(Logipmsg, "ipiput %V %V len %d proto %d\n", -// h->src, h->dst, BLEN(bp), h->proto); - /* * Ensure we have all the header info in the first * block. Make life easier for other protocols by @@ -370,15 +426,16 @@ ipiput(Fs *f, Ipifc *ifc, Block *bp) */ if(BLEN(bp) < 64) { hl = blocklen(bp); - if(hl < IPHDR) - hl = IPHDR; + if(hl < IP4HDR) + hl = IP4HDR; if(hl > 64) hl = 64; bp = pullupblock(bp, hl); if(bp == nil) return; } - h = (Iphdr *)(bp->rp); + + h = (Ip4hdr*)(bp->rp); /* dump anything that whose header doesn't checksum */ if(ipcsum(&h->vihl)) { @@ -387,28 +444,27 @@ ipiput(Fs *f, Ipifc *ifc, Block *bp) freeblist(bp); return; } - v4tov6(v6dst, h->dst); notforme = ipforme(f, v6dst) == 0; /* Check header length and version */ - if(h->vihl != (IP_VER|IP_HLEN)) { + if((h->vihl&0x0F) != IP_HLEN4) { hl = (h->vihl&0xF)<<2; - if((h->vihl&0xF0) != IP_VER || hl < (IP_HLEN<<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 this is not routed strip off the options */ if(notforme == 0) { olen = nhgets(h->length); - dp = bp->rp + (hl - (IP_HLEN<<2)); - memmove(dp, h, IP_HLEN<<2); + dp = bp->rp + (hl - (IP_HLEN4<<2)); + memmove(dp, h, IP_HLEN4<<2); bp->rp = dp; - h = (Iphdr *)(bp->rp); - h->vihl = (IP_VER|IP_HLEN); - hnputs(h->length, olen-hl+(IP_HLEN<<2)); + h = (Ip4hdr*)(bp->rp); + h->vihl = (IP_VER4|IP_HLEN4); + hnputs(h->length, olen-hl+(IP_HLEN4<<2)); } } @@ -418,10 +474,10 @@ ipiput(Fs *f, Ipifc *ifc, Block *bp) freeb(bp); return; } - /* don't forward to source's network */ sr = v4lookup(f, h->src); r = v4lookup(f, h->dst); + if(r == nil || sr == r){ ip->stats[OutDiscards]++; freeblist(bp); @@ -429,13 +485,14 @@ ipiput(Fs *f, Ipifc *ifc, Block *bp) } /* don't forward if packet has timed out */ - if(h->ttl <= 1){ + hop = h->ttl; + if(hop < 1) { ip->stats[InHdrErrors]++; - icmpttlexceeded(f, ifc, bp); + icmpttlexceeded(f, ifc->lifc->local, bp); freeblist(bp); return; } - + /* reassemble if the interface expects it */ if(r->ifc->reassemble){ frag = nhgets(h->frag); @@ -443,32 +500,33 @@ ipiput(Fs *f, Ipifc *ifc, Block *bp) h->tos = 0; if(frag & IP_MF) h->tos = 1; - bp = ipreassemble(ip, frag, bp, h); + bp = ip4reassemble(ip, frag, bp, h); if(bp == nil) return; - h = (Iphdr *)(bp->rp); + h = (Ip4hdr*)(bp->rp); } } ip->stats[ForwDatagrams]++; - ipoput(f, bp, 1, h->ttl - 1, h->tos); - + tos = h->tos; + hop = h->ttl; + ipoput4(f, bp, 1, hop - 1, tos); return; } - /* reassemble */ frag = nhgets(h->frag); if(frag) { h->tos = 0; if(frag & IP_MF) h->tos = 1; - bp = ipreassemble(ip, frag, bp, h); + bp = ip4reassemble(ip, frag, bp, h); if(bp == nil) return; - h = (Iphdr *)(bp->rp); + h = (Ip4hdr*)(bp->rp); } - p = Fsrcvpcol(f, h->proto); + proto = h->proto; + p = Fsrcvpcol(f, proto); if(p != nil && p->rcv != nil) { ip->stats[InDelivers]++; (*p->rcv)(p, ifc, bp); @@ -497,11 +555,11 @@ ipstats(Fs *f, char *buf, int len) } Block* -ipreassemble(IP *ip, int offset, Block *bp, Iphdr *ih) +ip4reassemble(IP *ip, int offset, Block *bp, Ip4hdr *ih) { int fend; ushort id; - Fragment *f, *fnext; + Fragment4 *f, *fnext; ulong src, dst; Block *bl, **l, *last, *prev; int ovlap, len, fragsize, pktposn; @@ -515,21 +573,21 @@ ipreassemble(IP *ip, int offset, Block *bp, Iphdr *ih) */ if(bp->next){ bp = pullupblock(bp, blocklen(bp)); - ih = (Iphdr *)(bp->rp); + ih = (Ip4hdr*)(bp->rp); } - qlock(&ip->fraglock); + qlock(&ip->fraglock4); /* * find a reassembly queue for this fragment */ - for(f = ip->flisthead; f; f = fnext){ - fnext = f->next; /* because ipfragfree changes the list */ + 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 < msec){ ip->stats[ReasmTimeout]++; - ipfragfree(ip, f); + ipfragfree4(ip, f); } } @@ -540,10 +598,10 @@ ipreassemble(IP *ip, int offset, Block *bp, Iphdr *ih) */ if(!ih->tos && (offset & ~(IP_MF|IP_DF)) == 0) { if(f != nil) { - ipfragfree(ip, f); + ipfragfree4(ip, f); ip->stats[ReasmFails]++; } - qunlock(&ip->fraglock); + qunlock(&ip->fraglock4); return bp; } @@ -553,18 +611,18 @@ ipreassemble(IP *ip, int offset, Block *bp, Iphdr *ih) } BKFG(bp)->foff = offset<<3; - BKFG(bp)->flen = nhgets(ih->length)-IPHDR; + BKFG(bp)->flen = nhgets(ih->length)-IP4HDR; /* First fragment allocates a reassembly queue */ if(f == nil) { - f = ipfragallo(ip); + f = ipfragallo4(ip); f->id = id; f->src = src; f->dst = dst; f->blist = bp; - qunlock(&ip->fraglock); + qunlock(&ip->fraglock4); ip->stats[ReasmReqds]++; return nil; } @@ -587,7 +645,7 @@ ipreassemble(IP *ip, int offset, Block *bp, Iphdr *ih) if(ovlap > 0) { if(ovlap >= BKFG(bp)->flen) { freeblist(bp); - qunlock(&ip->fraglock); + qunlock(&ip->fraglock4); return nil; } BKFG(prev)->flen -= ovlap; @@ -611,7 +669,7 @@ ipreassemble(IP *ip, int offset, Block *bp, Iphdr *ih) BKFG(*l)->flen -= ovlap; BKFG(*l)->foff += ovlap; /* move up ih hdrs */ - memmove((*l)->rp + ovlap, (*l)->rp, IPHDR); + memmove((*l)->rp + ovlap, (*l)->rp, IP4HDR); (*l)->rp += ovlap; break; } @@ -641,32 +699,32 @@ ipreassemble(IP *ip, int offset, Block *bp, Iphdr *ih) for(bl = bl->next; bl; bl = bl->next) { fragsize = BKFG(bl)->flen; len += fragsize; - bl->rp += IPHDR; + bl->rp += IP4HDR; bl->wp = bl->rp + fragsize; } bl = f->blist; f->blist = nil; - ipfragfree(ip, f); + ipfragfree4(ip, f); ih = BLKIP(bl); hnputs(ih->length, len); - qunlock(&ip->fraglock); + qunlock(&ip->fraglock4); ip->stats[ReasmOKs]++; return bl; } pktposn += BKFG(bl)->flen; } - qunlock(&ip->fraglock); + qunlock(&ip->fraglock4); return nil; } /* - * ipfragfree - Free a list of fragments - assume hold fraglock + * ipfragfree4 - Free a list of fragments - assume hold fraglock4 */ void -ipfragfree(IP *ip, Fragment *frag) +ipfragfree4(IP *ip, Fragment4 *frag) { - Fragment *fl, **l; + Fragment4 *fl, **l; if(frag->blist) freeblist(frag->blist); @@ -675,7 +733,7 @@ ipfragfree(IP *ip, Fragment *frag) frag->id = 0; frag->blist = nil; - l = &ip->flisthead; + l = &ip->flisthead4; for(fl = *l; fl; fl = fl->next) { if(fl == frag) { *l = frag->next; @@ -684,29 +742,29 @@ ipfragfree(IP *ip, Fragment *frag) l = &fl->next; } - frag->next = ip->fragfree; - ip->fragfree = frag; + frag->next = ip->fragfree4; + ip->fragfree4 = frag; } /* - * ipfragallo - allocate a reassembly queue - assume hold fraglock + * ipfragallo4 - allocate a reassembly queue - assume hold fraglock4 */ -Fragment * -ipfragallo(IP *ip) +Fragment4 * +ipfragallo4(IP *ip) { - Fragment *f; + Fragment4 *f; - while(ip->fragfree == nil) { + while(ip->fragfree4 == nil) { /* free last entry on fraglist */ - for(f = ip->flisthead; f->next; f = f->next) + for(f = ip->flisthead4; f->next; f = f->next) ; - ipfragfree(ip, f); + ipfragfree4(ip, f); } - f = ip->fragfree; - ip->fragfree = f->next; - f->next = ip->flisthead; - ip->flisthead = f; + f = ip->fragfree4; + ip->fragfree4 = f->next; + f->next = ip->flisthead4; + ip->flisthead4 = f; f->age = msec + 30000; return f; @@ -733,70 +791,4 @@ ipcsum(uchar *addr) return (sum^0xffff); } -enum -{ - Nmtucache= 128, -}; - -typedef struct MTUcache MTUcache; - -struct MTUcache -{ - uchar ip[IPaddrlen]; - ulong mtu; - ulong ms; -}; - -static struct { - Lock; - MTUcache c[Nmtucache]; -} mc; - -void -update_mtucache(uchar *ip, ulong mtu) -{ - MTUcache *oldest, *p; - - if(mtu < 512) - return; - - lock(&mc); - oldest = mc.c; - for(p = mc.c; p < &mc.c[Nmtucache]; p++){ - if(ipcmp(ip, p->ip) == 0){ - p->mtu = mtu; - p->ms = msec; - break; - } - if(p->ms < oldest->ms) - oldest = p; - } - if(p == &mc.c[Nmtucache]){ - ipmove(oldest->ip, ip); - oldest->mtu = mtu; - oldest->ms = msec; - } - unlock(&mc); -} - -ulong -restrict_mtu(uchar *ip, ulong mtu) -{ - MTUcache *p; - lock(&mc); - for(p = mc.c; p < &mc.c[Nmtucache]; p++){ - if(p->ms + 1000*10*60 < msec){ - memset(p->ip, 0, sizeof(p->ip)); - p->ms = 0; - } - if(ipcmp(ip, p->ip) == 0){ - if(p->mtu < mtu) - mtu = p->mtu; - break; - } - } - unlock(&mc); - - return mtu; -} diff --git a/ip/ip.h b/ip/ip.h index 700ba77242f094e6b7176125cf86d7dd2f44a544..d72b9f2e7964bbb8739eadd665efc93a77e3797c 100644 --- a/ip/ip.h +++ b/ip/ip.h @@ -24,6 +24,11 @@ typedef struct Arpent Arpent; typedef struct Arp Arp; typedef struct Route Route; +typedef struct Routerparams Routerparams; +typedef struct Hostparams Hostparams; +typedef struct v6router v6router; +typedef struct v6params v6params; + enum { Addrlen= 64, @@ -44,9 +49,11 @@ enum /* ip versions */ V4= 4, V6= 6, + IP_VER4= 0x40, + IP_VER6= 0x60, /* 2^Lroot trees in the root table */ - Lroot = 10, + Lroot= 10, Maxpath = 64, }; @@ -78,7 +85,7 @@ struct Conv uint ttl; /* max time to live */ uint tos; /* type of service */ - char *owner ; /* protections */ + char *owner; /* protections */ int perm; int inuse; /* opens of listen/data/ctl */ int length; @@ -138,6 +145,9 @@ struct Medium void (*ares)(Fs*, int, uchar*, uchar*, int, int); /* resolve */ void (*areg)(Ipifc*, uchar*); /* register */ + /* v6 address generation */ + void (*pref2addr)(uchar *pref, uchar *ea); + int unbindonclose; /* if non-zero, unbind on last close */ }; @@ -148,6 +158,12 @@ struct Iplifc uchar mask[IPaddrlen]; uchar remote[IPaddrlen]; uchar net[IPaddrlen]; + uchar tentative; /* =1 => v6 dup disc on, =0 => confirmed unique */ + uchar onlink; /* =1 => onlink, =0 offlink. */ + uchar autoflag; /* v6 autonomous flag */ + long validlt; /* v6 valid lifetime */ + long preflt; /* v6 preferred lifetime */ + long origint; /* time when addr was added */ Iplink *link; /* addresses linked to this lifc */ Iplifc *next; }; @@ -164,6 +180,25 @@ struct Iplink int ref; }; +/* rfc 2461, pp.40--43. */ + +/* default values, one per stack */ +struct Routerparams { + int mflag; + int oflag; + int maxraint; + int minraint; + int linkmtu; + int reachtime; + int rxmitra; + int ttl; + int routerlt; +}; + +struct Hostparams { + int rxmithost; +}; + struct Ipifc { RWlock; @@ -189,6 +224,11 @@ struct Ipifc ulong in, out; /* message statistics */ ulong inerr, outerr; /* ... */ + + uchar sendra6; /* == 1 => send router advs on this ifc */ + uchar recvra6; /* == 1 => recv router advs on this ifc */ + Routerparams rp; /* router parameters as in RFC 2461, pp.40--43. + used only if node is router */ }; /* @@ -267,6 +307,7 @@ struct Proto void *priv; }; + /* * one per IP protocol stack */ @@ -284,6 +325,7 @@ struct Fs IP *ip; Ipselftab *self; Arp *arp; + v6params *v6p; Route *v4root[1<= 0. */ +}; + + int Fsconnected(Conv*, char*); Conv* Fsnewcall(Conv*, uchar*, ushort, uchar*, ushort); int Fspcolstats(char*, int); @@ -310,7 +370,6 @@ char* Fsstdannounce(Conv*, char**, int); char* Fsstdbind(Conv*, char**, int); ulong scalednconv(void); void closeconv(Conv*); - /* * logging */ @@ -449,19 +508,24 @@ struct Arpent { uchar ip[IPaddrlen]; uchar mac[MAClen]; - Medium *type; /* media type */ + Medium *type; /* media type */ Arpent* hash; Block* hold; Block* last; uint time; uint used; uchar state; + Arpent *nextrxt; /* re-transmit chain */ + uint rxtat; + uchar rxtsrem; + Ipifc *ifc; + uchar ifcid; /* must match ifc->id */ }; extern void arpinit(Fs*); extern int arpread(Arp*, char*, ulong, int); extern int arpwrite(Fs*, char*, int); -extern Arpent* arpget(Arp*, Block *bp, int version, Medium *type, uchar *ip, uchar *h); +extern Arpent* arpget(Arp*, Block *bp, int version, Ipifc *ifc, uchar *ip, uchar *h); extern void arprelease(Arp*, Arpent *a); extern Block* arpresolve(Arp*, Arpent *a, Medium *type, uchar *mac); extern void arpenter(Fs*, int version, uchar *ip, uchar *mac, int len, int norefresh); @@ -509,6 +573,7 @@ extern Medium tripmedium; extern Medium* ipfindmedium(char *name); extern void addipmedium(Medium *med); extern int ipforme(Fs*, uchar *addr); +extern int iptentative(Fs*, uchar *addr); extern int ipisbm(uchar *); extern int ipismulticast(uchar *); extern Ipifc* findipifc(Fs*, uchar *remote, int type); @@ -516,6 +581,7 @@ extern void findprimaryip(Fs*, uchar*); extern void findlocalip(Fs*, uchar *local, uchar *remote); extern int ipv4local(Ipifc *ifc, uchar *addr); extern int ipv6local(Ipifc *ifc, uchar *addr); +extern int ipv6anylocal(Ipifc *ifc, uchar *addr); extern Iplifc* iplocalonifc(Ipifc *ifc, uchar *ip); extern int ipproxyifc(Fs *f, Ipifc *ifc, uchar *ip); extern int ipismulticast(uchar *ip); @@ -528,27 +594,30 @@ extern void ipifcremroute(Fs*, int, uchar*, uchar*); extern void ipifcremmulti(Conv *c, uchar *ma, uchar *ia); extern void ipifcaddmulti(Conv *c, uchar *ma, uchar *ia); extern char* ipifcrem(Ipifc *ifc, char **argv, int argc, int dolock); -extern char* ipifcadd(Ipifc *ifc, char **argv, int argc); +extern char* ipifcadd(Ipifc *ifc, char **argv, int argc, int tentative, Iplifc *lifcp); extern long ipselftabread(Fs*, char *a, ulong offset, int n); - +extern char* ipifcaddgate6(Fs *f, Ipifc *ifc, char**argv, int argc); +extern char* ipifcaddpref6(Ipifc *ifc, char**argv, int argc); +extern void ipsendra6(Fs *f, int on); +extern long ipgateread6(Fs *f, char *cp, ulong offset, int n); /* * ip.c */ extern void iprouting(Fs*, int); extern void closeifcconv(Ifcconv*); extern void icmpnoconv(Fs*, Block*); -extern void icmpttlexceeded(Fs*, Ipifc*, Block*); -extern void initfrag(IP*, int); +extern void icmpttlexceeded(Fs*, uchar*, Block*); extern ushort ipcsum(uchar*); -extern void ipiput(Fs*, Ipifc*, Block*); -extern void ipoput(Fs*, Block*, int, int, int); +extern void ipiput4(Fs*, Ipifc*, Block*); +extern void ipiput6(Fs*, Ipifc*, Block*); +extern void ipoput4(Fs*, Block*, int, int, int); +extern void ipoput6(Fs*, Block*, int, int, int); extern int ipstats(Fs*, char*, int); extern ushort ptclbsum(uchar*, int); extern ushort ptclcsum(Block*, int, int); extern void ip_init(Fs*); extern void update_mtucache(uchar*, ulong); extern ulong restrict_mtu(uchar*, ulong); - /* * bootp.c */ @@ -573,3 +642,4 @@ extern Chan* chandial(char*, char*, char*, Chan**); extern int debug; extern Fs fs; extern void (*igmpreportfn)(Ipifc*, uchar*); + diff --git a/ip/ipaux.c b/ip/ipaux.c index 05431c86dbe111af51dc8b3121e8c063cb445817..ae8baf293c40e3c8c90bb677195b7a1afe551af6 100644 --- a/ip/ipaux.c +++ b/ip/ipaux.c @@ -5,6 +5,7 @@ #include "fns.h" #include "../port/error.h" #include "ip.h" +#include "ipv6.h" /* * well known IP addresses @@ -33,6 +34,7 @@ uchar IPallbits[IPaddrlen] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + uchar IPnoaddr[IPaddrlen]; /* @@ -46,6 +48,151 @@ uchar v4prefix[IPaddrlen] = { }; +char *v6hdrtypes[Maxhdrtype] = +{ + [HBH] "HopbyHop", + [ICMP] "ICMP", + [IGMP] "IGMP", + [GGP] "GGP", + [IPINIP] "IP", + [ST] "ST", + [TCP] "TCP", + [UDP] "UDP", + [ISO_TP4] "ISO_TP4", + [RH] "Routinghdr", + [FH] "Fraghdr", + [IDRP] "IDRP", + [RSVP] "RSVP", + [AH] "Authhdr", + [ESP] "ESP", + [ICMPv6] "ICMPv6", + [NNH] "Nonexthdr", + [ISO_IP] "ISO_IP", + [IGRP] "IGRP", + [OSPF] "OSPF", +}; + +/* + * well known IPv6 addresses + */ +uchar v6Unspecified[IPaddrlen] = { + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 +}; +uchar v6loopback[IPaddrlen] = { + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0x01 +}; +uchar v6linklocal[IPaddrlen] = { + 0xfe, 0x80, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 +}; +uchar v6linklocalmask[IPaddrlen] = { + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0, 0, 0, 0, + 0, 0, 0, 0 +}; +int v6linklocalprefix = 8; +uchar v6sitelocal[IPaddrlen] = { + 0xfe, 0xc0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 +}; +uchar v6sitelocalmask[IPaddrlen] = { + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0, 0, 0, 0, + 0, 0, 0, 0 +}; +int v6sitelocalprefix = 6; +uchar v6glunicast[IPaddrlen] = { + 0x08, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 +}; +uchar v6multicast[IPaddrlen] = { + 0xff, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 +}; +uchar v6multicastmask[IPaddrlen] = { + 0xff, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 +}; +int v6multicastprefix = 1; +uchar v6allnodesN[IPaddrlen] = { + 0xff, 0x01, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0x01 +}; +uchar v6allnodesNmask[IPaddrlen] = { + 0xff, 0xff, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 +}; +int v6allnodesprefix = 2; +uchar v6allnodesL[IPaddrlen] = { + 0xff, 0x02, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0x01 +}; +uchar v6allnodesLmask[IPaddrlen] = { + 0xff, 0xff, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 +}; +int v6allnodesLprefix = 2; +uchar v6allroutersN[IPaddrlen] = { + 0xff, 0x01, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0x02 +}; +uchar v6allroutersL[IPaddrlen] = { + 0xff, 0x02, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0x02 +}; +uchar v6allroutersS[IPaddrlen] = { + 0xff, 0x05, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0x02 +}; +uchar v6solicitednode[IPaddrlen] = { + 0xff, 0x02, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0x01, + 0xff, 0, 0, 0 +}; +uchar v6solicitednodemask[IPaddrlen] = { + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0x0, 0x0, 0x0 +}; +int v6solicitednodeprefix = 13; + + + + ushort ptclcsum(Block *bp, int offset, int len) { @@ -135,16 +282,13 @@ eipfmt(Fmt *f) switch(f->r) { case 'E': /* Ethernet address */ p = va_arg(f->args, uchar*); - snprint(buf, sizeof buf, efmt, p[0], p[1], p[2], p[3], p[4], p[5]); - return fmtstrcpy(f, buf); + return fmtprint(f, efmt, p[0], p[1], p[2], p[3], p[4], p[5]); case 'I': /* Ip address */ p = va_arg(f->args, uchar*); common: - if(memcmp(p, v4prefix, 12) == 0){ - snprint(buf, sizeof buf, ifmt, p[12], p[13], p[14], p[15]); - return fmtstrcpy(f, buf); - } + if(memcmp(p, v4prefix, 12) == 0) + return fmtprint(f, ifmt, p[12], p[13], p[14], p[15]); /* find longest elision */ eln = eli = -1; @@ -182,8 +326,7 @@ common: case 'V': /* v4 ip address */ p = va_arg(f->args, uchar*); - snprint(buf, sizeof buf, ifmt, p[0], p[1], p[2], p[3]); - return fmtstrcpy(f, buf); + return fmtprint(f, ifmt, p[0], p[1], p[2], p[3]); case 'M': /* ip mask */ p = va_arg(f->args, uchar*); @@ -203,10 +346,10 @@ common: n = 8*16; /* got one, use /xx format */ - snprint(buf, sizeof buf, "/%d", n); - return fmtstrcpy(f, buf); + return fmtprint(f, "/%d", n); + } - return fmtstrcpy(f, "(eipfmt)"); + return fmtprint(f, "(eipfmt)"); } #define CLASS(p) ((*(uchar*)(p))>>6) @@ -230,7 +373,7 @@ v4parseip(uchar *to, char *from) to[3] = to[2]; to[2] = to[1]; to[1] = 0; - } else if (i == 2){ + } else if(i == 2){ to[3] = to[1]; to[1] = 0; } @@ -395,9 +538,34 @@ uchar classmask[4][16] = { uchar* defmask(uchar *ip) { - return classmask[ip[IPv4off]>>6]; + if(isv4(ip)) + return classmask[ip[IPv4off]>>6]; + else { + if(ipcmp(ip, v6loopback) == 0) + return IPallbits; + else if(memcmp(ip, v6linklocal, v6linklocalprefix) == 0) + return v6linklocalmask; + else if(memcmp(ip, v6sitelocal, v6sitelocalprefix) == 0) + return v6sitelocalmask; + else if(memcmp(ip, v6solicitednode, v6solicitednodeprefix) == 0) + return v6solicitednodemask; + else if(memcmp(ip, v6multicast, v6multicastprefix) == 0) + return v6multicastmask; + return IPallbits; + } } +void +ipv62smcast(uchar *smcast, uchar *a) +{ + assert(IPaddrlen == 16); + memmove(smcast, v6solicitednode, IPaddrlen); + smcast[13] = a[13]; + smcast[14] = a[14]; + smcast[15] = a[15]; +} + + /* * parse a hex mac address */ diff --git a/ip/ipifc.c b/ip/ipifc.c index 618c0d7b68e574a8a76e81cbe97b60316a6cc270..a7e8767a7481e1a7427fc7c1b4a3a6fa307f5180 100644 --- a/ip/ipifc.c +++ b/ip/ipifc.c @@ -6,6 +6,7 @@ #include "../port/error.h" #include "ip.h" +#include "ipv6.h" #define DPRINT if(0)print @@ -138,6 +139,17 @@ ipifcbind(Conv *c, char **argv, int argc) ifc->maxmtu = ifc->m->maxmtu; if(ifc->m->unbindonclose == 0) ifc->conv->inuse++; + + ifc->rp.mflag = 0; // default not managed + ifc->rp.oflag = 0; + ifc->rp.maxraint = 600000; // millisecs + ifc->rp.minraint = 200000; + ifc->rp.linkmtu = 0; // no mtu sent + ifc->rp.reachtime = 0; + ifc->rp.rxmitra = 0; + ifc->rp.ttl = MAXTTL; + ifc->rp.routerlt = 3*(ifc->rp.maxraint); + ifc->ifcid++; wunlock(ifc); @@ -199,6 +211,13 @@ ipifcunbind(Ipifc *ifc) return nil; } + + +char *sfixedformat = "device %-12s maxmtu %-5d sendra %-1d recvra %-1d mflag %-1d oflag %-1d maxraint %-10d minraint %-10d linkmtu %-10d reachtime %-10d rxmitra %-10d ttl %-10d routerlt %-10d pktin %-7lud pktout %-7lud errin %-7lud errout %-7lud\n"; + +char *slineformat = " %-40.40I %-10.10M %-40.40I %-12lud %-12lud\n"; + + static int ipifcstate(Conv *c, char *state, int n) { @@ -208,14 +227,18 @@ ipifcstate(Conv *c, char *state, int n) ifc = (Ipifc*)c->ptcl; - m = snprint(state, n, "%-12s %-5d", ifc->dev, ifc->maxmtu); + m = snprint(state, n, sfixedformat, + ifc->dev, ifc->maxmtu, ifc->sendra6, ifc->recvra6, + ifc->rp.mflag, ifc->rp.oflag, ifc->rp.maxraint, + ifc->rp.minraint, ifc->rp.linkmtu, ifc->rp.reachtime, + ifc->rp.rxmitra, ifc->rp.ttl, ifc->rp.routerlt, + ifc->in, ifc->out, ifc->inerr, ifc->outerr); rlock(ifc); for(lifc = ifc->lifc; lifc && n > m; lifc = lifc->next) - m += snprint(state+m, n - m, - " %-20.20I %-20.20M %-20.20I %-7lud %-7lud %-7lud %-7lud\n", - lifc->local, lifc->mask, lifc->remote, - ifc->in, ifc->out, ifc->inerr, ifc->outerr); + m += snprint(state+m, n - m, slineformat, + lifc->local, lifc->mask, lifc->remote, + lifc->validlt, lifc->preflt); if(ifc->lifc == nil) m += snprint(state+m, n - m, "\n"); runlock(ifc); @@ -236,9 +259,9 @@ ipifclocal(Conv *c, char *state, int n) rlock(ifc); for(lifc = ifc->lifc; lifc; lifc = lifc->next){ - m += snprint(state+m, n - m, "%-20.20I ->", lifc->local); + m += snprint(state+m, n - m, "%-40.40I ->", lifc->local); for(link = lifc->link; link; link = link->lifclink) - m += snprint(state+m, n - m, " %-20.20I", link->self->a); + m += snprint(state+m, n - m, " %-40.40I", link->self->a); m += snprint(state+m, n - m, "\n"); } runlock(ifc); @@ -331,13 +354,14 @@ ipifcsetmtu(Ipifc *ifc, char **argv, int argc) * add an address to an interface. */ char* -ipifcadd(Ipifc *ifc, char **argv, int argc) +ipifcadd(Ipifc *ifc, char **argv, int argc, int tentative, Iplifc *lifcp) { uchar ip[IPaddrlen], mask[IPaddrlen], rem[IPaddrlen]; uchar bcast[IPaddrlen], net[IPaddrlen]; Iplifc *lifc, **l; int i, type, mtu; Fs *f; + int sendnbrdisc = 0; if(ifc->m == nil) return "ipifc not yet bound to device"; @@ -380,13 +404,25 @@ ipifcadd(Ipifc *ifc, char **argv, int argc) return Ebadarg; break; } - + if(isv4(ip)) + tentative = 0; wlock(ifc); /* ignore if this is already a local address for this ifc */ - for(lifc = ifc->lifc; lifc; lifc = lifc->next) - if(ipcmp(lifc->local, ip) == 0) + for(lifc = ifc->lifc; lifc; lifc = lifc->next) { + if(ipcmp(lifc->local, ip) == 0) { + if(lifc->tentative != tentative) + lifc->tentative = tentative; + if(lifcp != nil) { + lifc->onlink = lifcp->onlink; + lifc->autoflag = lifcp->autoflag; + lifc->validlt = lifcp->validlt; + lifc->preflt = lifcp->preflt; + lifc->origint = lifcp->origint; + } goto out; + } + } /* add the address to the list of logical ifc's for this ifc */ lifc = smalloc(sizeof(Iplifc)); @@ -394,18 +430,37 @@ ipifcadd(Ipifc *ifc, char **argv, int argc) ipmove(lifc->mask, mask); ipmove(lifc->remote, rem); ipmove(lifc->net, net); + lifc->tentative = tentative; + if(lifcp != nil) { + lifc->onlink = lifcp->onlink; + lifc->autoflag = lifcp->autoflag; + lifc->validlt = lifcp->validlt; + lifc->preflt = lifcp->preflt; + lifc->origint = lifcp->origint; + } + else { // default values + lifc->onlink = 1; + lifc->autoflag = 1; + lifc->validlt = 0xffffffff; + lifc->preflt = 0xffffffff; + lifc->origint = msec / 10^3; + } lifc->next = nil; + for(l = &ifc->lifc; *l; l = &(*l)->next) ; *l = lifc; - /* add a route for the local network */ + /* check for point-to-point interface */ + if(ipcmp(ip, v6loopback)) /* skip v6 loopback, it's a special address */ if(ipcmp(mask, IPallbits) == 0){ /* point to point networks are a hack */ if(ipcmp(ip, rem) == 0) findprimaryip(f, lifc->local); type |= Rptpt; } + + /* add local routes */ if(isv4(ip)) v4addroute(f, tifc, rem+IPv4off, mask+IPv4off, rem+IPv4off, type); else @@ -418,36 +473,63 @@ ipifcadd(Ipifc *ifc, char **argv, int argc) goto out; } - /* add subnet directed broadcast addresses to the self cache */ - for(i = 0; i < IPaddrlen; i++) - bcast[i] = (ip[i] & mask[i]) | ~mask[i]; - addselfcache(f, ifc, lifc, bcast, Rbcast); + if(isv4(ip) || ipcmp(ip, IPnoaddr) == 0) { + /* add subnet directed broadcast address to the self cache */ + for(i = 0; i < IPaddrlen; i++) + bcast[i] = (ip[i] & mask[i]) | ~mask[i]; + addselfcache(f, ifc, lifc, bcast, Rbcast); + + /* add subnet directed network address to the self cache */ + for(i = 0; i < IPaddrlen; i++) + bcast[i] = (ip[i] & mask[i]) & mask[i]; + addselfcache(f, ifc, lifc, bcast, Rbcast); - /* add old subnet directed broadcast addresses to the self cache */ - for(i = 0; i < IPaddrlen; i++) - bcast[i] = (ip[i] & mask[i]) & mask[i]; - addselfcache(f, ifc, lifc, bcast, Rbcast); + /* add network directed broadcast address to the self cache */ + memmove(mask, defmask(ip), IPaddrlen); + for(i = 0; i < IPaddrlen; i++) + bcast[i] = (ip[i] & mask[i]) | ~mask[i]; + addselfcache(f, ifc, lifc, bcast, Rbcast); - /* add network directed broadcast addresses to the self cache */ - memmove(mask, defmask(ip), IPaddrlen); - for(i = 0; i < IPaddrlen; i++) - bcast[i] = (ip[i] & mask[i]) | ~mask[i]; - addselfcache(f, ifc, lifc, bcast, Rbcast); + /* add network directed network address to the self cache */ + memmove(mask, defmask(ip), IPaddrlen); + for(i = 0; i < IPaddrlen; i++) + bcast[i] = (ip[i] & mask[i]) & mask[i]; + addselfcache(f, ifc, lifc, bcast, Rbcast); + + addselfcache(f, ifc, lifc, IPv4bcast, Rbcast); + } + else { + if(ipcmp(ip, v6loopback) == 0) { + /* add node-local mcast address */ + addselfcache(f, ifc, lifc, v6allnodesN, Rmulti); + + /* add route for all node multicast */ + v6addroute(f, tifc, v6allnodesN, v6allnodesNmask, v6allnodesN, Rmulti); + } - /* add old network directed broadcast addresses to the self cache */ - memmove(mask, defmask(ip), IPaddrlen); - for(i = 0; i < IPaddrlen; i++) - bcast[i] = (ip[i] & mask[i]) & mask[i]; - addselfcache(f, ifc, lifc, bcast, Rbcast); + else if(memcmp(ip, v6linklocal, v6linklocalprefix) == 0) { + /* add link-local mcast address */ + addselfcache(f, ifc, lifc, v6allnodesL, Rmulti); + + /* add route for all link multicast */ + v6addroute(f, tifc, v6allnodesL, v6allnodesLmask, v6allnodesL, Rmulti); + + /* add solicited-node multicast address */ + ipv62smcast(bcast, ip); + addselfcache(f, ifc, lifc, bcast, Rmulti); + } - addselfcache(f, ifc, lifc, IPv4bcast, Rbcast); + sendnbrdisc = 1; + } /* register the address on this network for address resolution */ - if(ifc->m->areg != nil) + if(isv4(ip) && ifc->m->areg != nil) (*ifc->m->areg)(ifc, ip); out: wunlock(ifc); + if(tentative && sendnbrdisc) + icmpns(f, 0, SRC_UNSPEC, ip, TARG_MULTI, ifc->mac); return nil; } @@ -505,8 +587,6 @@ ipifcrem(Ipifc *ifc, char **argv, int argc, int dolock) return "address not on this interface"; } - ifc->ifcid++; - /* disassociate any addresses */ while(lifc->link) remselfcache(f, ifc, lifc, lifc->link->self->a); @@ -514,8 +594,15 @@ ipifcrem(Ipifc *ifc, char **argv, int argc, int dolock) /* remove the route for this logical interface */ if(isv4(ip)) v4delroute(f, lifc->remote+IPv4off, lifc->mask+IPv4off, 1); - else + else { v6delroute(f, lifc->remote, lifc->mask, 1); + if(ipcmp(ip, v6loopback) == 0) + /* remove route for all node multicast */ + v6delroute(f, v6allnodesN, v6allnodesNmask, 1); + else if(memcmp(ip, v6linklocal, v6linklocalprefix) == 0) + /* remove route for all link multicast */ + v6delroute(f, v6allnodesL, v6allnodesLmask, 1); + } free(lifc); @@ -525,7 +612,7 @@ ipifcrem(Ipifc *ifc, char **argv, int argc, int dolock) } /* - * distrbute routes to active interfaces like the + * distribute routes to active interfaces like the * TRIP linecards */ void @@ -606,7 +693,7 @@ ipifcconnect(Conv* c, char **argv, int argc) wunlock(ifc); poperror(); - err = ipifcadd(ifc, argv, argc); + err = ipifcadd(ifc, argv, argc, 0, nil); if(err) return err; @@ -615,6 +702,81 @@ ipifcconnect(Conv* c, char **argv, int argc) return nil; } +char* +ipifcsetpar6(Ipifc *ifc, char **argv, int argc) +{ + int i, argsleft, vmax = ifc->rp.maxraint, vmin = ifc->rp.minraint; + + argsleft = argc - 1; + i = 1; + + if(argsleft % 2 != 0) + return Ebadarg; + + while (argsleft > 1) { + if(strcmp(argv[i],"recvra")==0) + ifc->recvra6 = (atoi(argv[i+1]) != 0); + else if(strcmp(argv[i],"sendra")==0) + ifc->sendra6 = (atoi(argv[i+1]) != 0); + else if(strcmp(argv[i],"mflag")==0) + ifc->rp.mflag = (atoi(argv[i+1]) != 0); + else if(strcmp(argv[i],"oflag")==0) + ifc->rp.oflag = (atoi(argv[i+1]) != 0); + else if(strcmp(argv[i],"maxraint")==0) + ifc->rp.maxraint = atoi(argv[i+1]); + else if(strcmp(argv[i],"minraint")==0) + ifc->rp.minraint = atoi(argv[i+1]); + else if(strcmp(argv[i],"linkmtu")==0) + ifc->rp.linkmtu = atoi(argv[i+1]); + else if(strcmp(argv[i],"reachtime")==0) + ifc->rp.reachtime = atoi(argv[i+1]); + else if(strcmp(argv[i],"rxmitra")==0) + ifc->rp.rxmitra = atoi(argv[i+1]); + else if(strcmp(argv[i],"ttl")==0) + ifc->rp.ttl = atoi(argv[i+1]); + else if(strcmp(argv[i],"routerlt")==0) + ifc->rp.routerlt = atoi(argv[i+1]); + else + return Ebadarg; + + argsleft -= 2; + i += 2; + } + + // consistency check + if(ifc->rp.maxraint < ifc->rp.minraint) { + ifc->rp.maxraint = vmax; + ifc->rp.minraint = vmin; + return Ebadarg; + } + + return nil; +} + +char* +ipifcsendra6(Ipifc *ifc, char **argv, int argc) +{ + int i; + + i = 0; + if(argc > 1) + i = atoi(argv[1]); + ifc->sendra6 = (i!=0); + return nil; +} + +char* +ipifcrecvra6(Ipifc *ifc, char **argv, int argc) +{ + int i; + + i = 0; + if(argc > 1) + i = atoi(argv[1]); + ifc->recvra6 = (i!=0); + return nil; +} + /* * non-standard control messages. * called with c->car locked. @@ -627,7 +789,9 @@ ipifcctl(Conv* c, char**argv, int argc) ifc = (Ipifc*)c->ptcl; if(strcmp(argv[0], "add") == 0) - return ipifcadd(ifc, argv, argc); + return ipifcadd(ifc, argv, argc, 0, nil); + else if(strcmp(argv[0], "try") == 0) + return ipifcadd(ifc, argv, argc, 1, nil); else if(strcmp(argv[0], "remove") == 0) return ipifcrem(ifc, argv, argc, 1); else if(strcmp(argv[0], "unbind") == 0) @@ -641,13 +805,24 @@ ipifcctl(Conv* c, char**argv, int argc) else if(strcmp(argv[0], "reassemble") == 0){ ifc->reassemble = 1; return nil; - } else if(strcmp(argv[0], "iprouting") == 0){ + } + else if(strcmp(argv[0], "iprouting") == 0){ i = 1; if(argc > 1) i = atoi(argv[1]); iprouting(c->p->f, i); return nil; } + else if(strcmp(argv[0], "gate6") == 0) + return ipifcaddgate6(c->p->f, ifc, argv, argc); + else if(strcmp(argv[0], "addpref6") == 0) + return ipifcaddpref6(ifc, argv, argc); + else if(strcmp(argv[0], "setpar6") == 0) + return ipifcsetpar6(ifc, argv, argc); + else if(strcmp(argv[0], "sendra6") == 0) + return ipifcsendra6(ifc, argv, argc); + else if(strcmp(argv[0], "recvra6") == 0) + return ipifcrecvra6(ifc, argv, argc); return "unsupported ctl"; } @@ -882,6 +1057,10 @@ out: } static char *stformat = "%-32.32I %2.2d %4.4s\n"; +enum +{ + Nstformat= 41, +}; long ipselftabread(Fs *f, char *cp, ulong offset, int n) @@ -911,6 +1090,19 @@ ipselftabread(Fs *f, char *cp, ulong offset, int n) return m; } +int +iptentative(Fs *f, uchar *addr) +{ + Ipself *p; + + p = f->self->hash[hashipa(addr)]; + for(; p; p = p->next){ + if(ipcmp(addr, p->a) == 0) { + return p->link->lifc->tentative; + } + } + return 0; +} /* * returns @@ -950,14 +1142,16 @@ findipifc(Fs *f, uchar *remote, int type) uchar gnet[IPaddrlen]; uchar xmask[IPaddrlen]; - x = nil; + x = nil; memset(xmask, 0, IPaddrlen); /* find most specific match */ e = &f->ipifc->conv[f->ipifc->nc]; for(cp = f->ipifc->conv; cp < e; cp++){ if(*cp == 0) continue; + ifc = (Ipifc*)(*cp)->ptcl; + for(lifc = ifc->lifc; lifc; lifc = lifc->next){ maskip(remote, lifc->mask, gnet); if(ipcmp(gnet, lifc->net) == 0){ @@ -985,8 +1179,65 @@ findipifc(Fs *f, uchar *remote, int type) return nil; } +enum { + unknownv6, + multicastv6, + unspecifiedv6, + linklocalv6, + sitelocalv6, + globalv6, +}; + +int +v6addrtype(uchar *addr) +{ + if(isv6global(addr)) + return globalv6; + if(islinklocal(addr)) + return linklocalv6; + if(isv6mcast(addr)) + return multicastv6; + if(issitelocal(addr)) + return sitelocalv6; + return unknownv6; +} + +#define v6addrcurr(lifc) (( (lifc)->origint + (lifc)->preflt >= (msec/10^3) ) || ( (lifc)->preflt == 0xffffffff )) + +void +findprimaryip6(Fs *f, uchar *local) +{ + Conv **cp, **e; + Ipifc *ifc; + Iplifc *lifc; + int atype, atypel; + + ipmove(local, v6Unspecified); + atype = unspecifiedv6; + + /* find "best" (global > sitelocal > link local > unspecified) + * local address; address must be current */ + + e = &f->ipifc->conv[f->ipifc->nc]; + for(cp = f->ipifc->conv; cp < e; cp++){ + if(*cp == 0) + continue; + ifc = (Ipifc*)(*cp)->ptcl; + for(lifc = ifc->lifc; lifc; lifc = lifc->next){ + atypel = v6addrtype(lifc->local); + if(atypel > atype) + if(v6addrcurr(lifc)) { + ipmove(local, lifc->local); + atype = atypel; + if(atype == globalv6) + return; + } + } + } +} + /* - * returns first ip address configured + * returns first ip address configured */ void findprimaryip(Fs *f, uchar *local) @@ -1020,28 +1271,57 @@ findlocalip(Fs *f, uchar *local, uchar *remote) Route *r; uchar gate[IPaddrlen]; uchar gnet[IPaddrlen]; + int version; + int atype = unspecifiedv6, atypel = unknownv6; + USED(atype); + USED(atypel); qlock(f->ipifc); r = v6lookup(f, remote); + version = (memcmp(remote, v4prefix, IPv4off) == 0) ? 4 : 6; if(r != nil){ ifc = r->ifc; if(r->type & Rv4) v4tov6(gate, r->v4.gate); - else + else { ipmove(gate, r->v6.gate); + ipmove(local, v6Unspecified); + } /* find ifc address closest to the gateway to use */ - for(lifc = ifc->lifc; lifc; lifc = lifc->next){ - maskip(gate, lifc->mask, gnet); - if(ipcmp(gnet, lifc->net) == 0){ - ipmove(local, lifc->local); - goto out; + if(version == 4) { + for(lifc = ifc->lifc; lifc; lifc = lifc->next){ + maskip(gate, lifc->mask, gnet); + if(ipcmp(gnet, lifc->net) == 0){ + ipmove(local, lifc->local); + goto out; + } + } + } + else { + for(lifc = ifc->lifc; lifc; lifc = lifc->next){ + atypel = v6addrtype(lifc->local); + maskip(gate, lifc->mask, gnet); + if(ipcmp(gnet, lifc->net) == 0) + if(atypel > atype) + if(v6addrcurr(lifc)) { + ipmove(local, lifc->local); + atype = atypel; + if(atype == globalv6) + break; + } } + if(atype > unspecifiedv6) + goto out; } } - findprimaryip(f, local); + if(version == 4) + findprimaryip(f, local); + else + findprimaryip6(f, local); + out: qunlock(f->ipifc); @@ -1072,6 +1352,20 @@ ipv6local(Ipifc *ifc, uchar *addr) { Iplifc *lifc; + for(lifc = ifc->lifc; lifc; lifc = lifc->next){ + if(!isv4(lifc->local) && !(lifc->tentative)){ + ipmove(addr, lifc->local); + return 1; + } + } + return 0; +} + +int +ipv6anylocal(Ipifc *ifc, uchar *addr) +{ + Iplifc *lifc; + for(lifc = ifc->lifc; lifc; lifc = lifc->next){ if(!isv4(lifc->local)){ ipmove(addr, lifc->local); @@ -1269,26 +1563,303 @@ ipifcregisterproxy(Fs *f, Ipifc *ifc, uchar *ip) /* register the address on any network that will proxy for us */ e = &f->ipifc->conv[f->ipifc->nc]; - for(cp = f->ipifc->conv; cp < e; cp++){ - if(*cp == nil) - continue; - nifc = (Ipifc*)(*cp)->ptcl; - if(nifc == ifc) - continue; - rlock(nifc); - m = nifc->m; - if(m == nil || m->areg == nil){ + if(!isv4(ip)) { // V6 + for(cp = f->ipifc->conv; cp < e; cp++){ + if(*cp == nil) + continue; + nifc = (Ipifc*)(*cp)->ptcl; + if(nifc == ifc) + continue; + + rlock(nifc); + m = nifc->m; + if(m == nil || m->addmulti == nil) { + runlock(nifc); + continue; + } + for(lifc = nifc->lifc; lifc; lifc = lifc->next){ + maskip(ip, lifc->mask, net); + if(ipcmp(net, lifc->remote) == 0) { /* add solicited-node multicast address */ + ipv62smcast(net, ip); + addselfcache(f, nifc, lifc, net, Rmulti); + arpenter(f, V6, ip, nifc->mac, 6, 0); + //(*m->addmulti)(nifc, net, ip); + break; + } + } + runlock(nifc); + } + return; + } + else { // V4 + for(cp = f->ipifc->conv; cp < e; cp++){ + if(*cp == nil) + continue; + nifc = (Ipifc*)(*cp)->ptcl; + if(nifc == ifc) + continue; + + rlock(nifc); + m = nifc->m; + if(m == nil || m->areg == nil){ + runlock(nifc); + continue; + } + for(lifc = nifc->lifc; lifc; lifc = lifc->next){ + maskip(ip, lifc->mask, net); + if(ipcmp(net, lifc->remote) == 0){ + (*m->areg)(nifc, ip); + break; + } + } runlock(nifc); - continue; } - for(lifc = nifc->lifc; lifc; lifc = lifc->next){ - maskip(ip, lifc->mask, net); - if(ipcmp(net, lifc->remote) == 0){ - (*m->areg)(nifc, ip); + } +} + + +// added for new v6 mesg types +static void +adddefroute6(Fs *f, uchar *gate, int force) +{ + Route *r; + + r = v6lookup(f, v6Unspecified); + if(r!=nil) + if(!(force) && (strcmp(r->tag,"ra")!=0)) // route entries generated + return; // by all other means take + // precedence over router annc + + v6delroute(f, v6Unspecified, v6Unspecified, 1); + v6addroute(f, "ra", v6Unspecified, v6Unspecified, gate, 0); +} + +enum +{ + Ngates = 3, +}; + +char* +ipifcaddgate6(Fs *f, Ipifc *ifc, char**argv, int argc) +{ + v6router *r = f->v6p->v6rlist; + uchar routeraddr[IPaddrlen]; + int mflag = f->v6p->rp.mflag; + int oflag = f->v6p->rp.oflag; + int reachtime = f->v6p->rp.reachtime; + int rxmitra = f->v6p->rp.rxmitra; + int ttl = MAXTTL; + int routerlt = f->v6p->rp.routerlt; + int force; // force == 1 forces argv[1] to + // be the default router. + int i, j; + int found = 0; + + if((argc<3)||(argc>9)) + return Ebadarg; + if( (parseip(routeraddr, argv[1])!=6) || !(islinklocal(routeraddr)) ) + return Ebadarg; + + force = (atoi(argv[2])!=0); + + switch(argc){ + case 9: + rxmitra = atoi(argv[8]); + /* fall through */ + case 8: + reachtime = atoi(argv[7]); + /* fall through */ + case 7: + routerlt = atoi(argv[6]); + /* fall through */ + case 6: + oflag = atoi(argv[5]); + /* fall through */ + case 5: + mflag = atoi(argv[4]); + /* fall through */ + case 4: + ttl = atoi(argv[3]); + /* fall through */ + } + + if((force) && (routerlt < 0)) + return Ebadarg; + if((ttl < 0) || (255 < ttl)) + return Ebadarg; + + j = -1; + for(i=0; iv6p->cdrouter; + f->v6p->cdrouter = -1; + } + } + + // assert((found && (j>=0))||(!found && routerlt>0)); + if(routerlt > 0) { + memset(&r[j], 0, sizeof(v6router)); + r[j].inuse = 1; + r[j].ifc = ifc; + r[j].ifcid = ifc->ifcid; + ipmove(r[j].routeraddr, routeraddr); + r[j].ltorigin = msec / 10^3; + r[j].rp.mflag = (mflag!=0); + r[j].rp.oflag = (oflag!=0); + r[j].rp.rxmitra = rxmitra; + r[j].rp.reachtime = reachtime; + r[j].rp.routerlt = routerlt; + r[j].rp.ttl = ttl; + + if((f->v6p->cdrouter < 0) || (force==1)) { + f->v6p->cdrouter = j; + adddefroute6(f, routeraddr, force); + } + } + else if(j >= 0) { // remove router + r[j].inuse = 0; + if(f->v6p->cdrouter==j) { + f->v6p->cdrouter = -1; + v6delroute(f, v6Unspecified, v6Unspecified, 1); + for(i=0; iv6p->cdrouter = i; + adddefroute6(f, r[i].routeraddr, 1); + break; + } + } + } + } + return nil; +} + +char* +ipifcaddpref6(Ipifc *ifc, char**argv, int argc) +{ + uchar onlink = 1; + uchar autoflag = 1; + long validlt = 0xffffffff; + long preflt = 0xffffffff; + long origint = msec / 10^3; + uchar prefix[IPaddrlen]; + int plen = 64; + Iplifc *lifc; + char addr[40]; + char *params[3]; + + switch(argc) { + case 7: + preflt = atoi(argv[6]); + /* fall through */ + case 6: + validlt = atoi(argv[5]); + /* fall through */ + case 5: + autoflag = atoi(argv[4]); + /* fall through */ + case 4: + onlink = atoi(argv[3]); + /* fall through */ + case 3: + plen = atoi(argv[2]); + case 2: + break; + default: + return Ebadarg; + } + + if((parseip(prefix, argv[1])!=6) || + (validlt < preflt) || + (plen > 64) || + (islinklocal(prefix)) + ) + return Ebadarg; + + lifc = smalloc(sizeof(Iplifc)); + lifc->onlink = (onlink!=0); + lifc->autoflag = (autoflag!=0); + lifc->validlt = validlt; + lifc->preflt = preflt; + lifc->origint = origint; + + if(ifc->m->pref2addr!=nil) + ifc->m->pref2addr(prefix, ifc->mac); + else + return Ebadarg; + + sprint(addr, "%I", prefix); + params[0] = "add"; + params[1] = addr; + params[2] = "/64"; + + return ipifcadd(ifc, params, 3, 0, lifc); +} + +static char *gateformat = "%-1.1s %-40.40I %20ld %10d %-40.40s\n"; +enum +{ + Ngateformat= 116, +}; + +static char *rtrstat[] = +{ +[0] "*", +[1] "-", +}; + +/* line corresponding to current default router, if in f->v6p->v6rlist, + starts with a "*", others with a "-". */ + +long +ipgateread6(Fs *f, char *cp, ulong offset, int n) +{ + v6router *r = f->v6p->v6rlist; + int i, j, k, l; + long m; + + + if(offset % Ngateformat) + return 0; + + offset = offset/Ngateformat; + n = n/Ngateformat; + + i = f->v6p->cdrouter; + k = i; + if((i<0)||(i>2)) + i = 0; + m = 0; + for(j = 0; (n > 0) && (j < Ngates) ; j++){ + if(offset > 0){ + offset--; + i = (i+1) % Ngates; + continue; + } + n--; + if(r[i].inuse) { + l = (i==k) ? 0 : 1; + m += sprint(cp + m, gateformat, + rtrstat[l], r[i].routeraddr, r[i].ltorigin, + r[i].rp.routerlt, r[i].ifc->dev); + } + i = (i+1) % Ngates; } + return m; } diff --git a/ip/ipmux.c b/ip/ipmux.c index 021d9bc83f5b10847c8e28fe3046de423a1b4bf2..29ba08ce90c415d6bf77d6f24747a167ae83c190 100644 --- a/ip/ipmux.c +++ b/ip/ipmux.c @@ -6,19 +6,19 @@ #include "../port/error.h" #include "ip.h" - #define DPRINT if(0)print -typedef struct Iphdr Iphdr; -typedef struct Ipmuxrock Ipmuxrock; -typedef struct Ipmux Ipmux; +typedef struct Ipmuxrock Ipmuxrock; +typedef struct Ipmux Ipmux; +typedef struct Ip4hdr Ip4hdr; +typedef struct Ip6hdr Ip6hdr; enum { - IPHDR = 20, /* sizeof(Iphdr) */ + IPHDR = 20, /* sizeof(Ip4hdr) */ }; -struct Iphdr +struct Ip4hdr { uchar vihl; /* Version and header length */ uchar tos; /* Type of service */ @@ -32,12 +32,24 @@ struct Iphdr uchar dst[4]; /* IP destination */ uchar data[1]; /* start of data */ }; -Iphdr *ipoff = 0; +Ip4hdr *ipoff = 0; + +struct Ip6hdr +{ + uchar vcf[4]; /* version, class label, and flow label */ + uchar ploadlen[2]; /* payload length */ + uchar proto; /* next header, i.e. proto */ + uchar ttl; /* hop limit, i.e. ttl */ + uchar src[16]; /* IP source */ + uchar dst[16]; /* IP destination */ +}; + enum { Tproto, Tdata, + Tiph, Tdst, Tsrc, Tifc, @@ -57,6 +69,7 @@ char *ftname[] = { [Tproto] "proto", [Tdata] "data", +[Tiph] "iph", [Tdst] "dst", [Tsrc] "src", [Tifc] "ifc", @@ -69,12 +82,13 @@ struct Ipmux { Ipmux *yes; Ipmux *no; - uchar type; /* type of field (Txxxx) */ - uchar ctype; /* tupe of comparison (Cxxxx) */ - uchar len; /* length in bytes of item to compare */ - uchar n; /* number of items val points to */ - short off; /* offset of comparison */ - short eoff; /* end offset of comparison */ + uchar type; /* type of field (Txxxx) */ + uchar ctype; /* tupe of comparison (Cxxxx) */ + uchar len; /* length in bytes of item to compare */ + uchar n; /* number of items val points to */ + short off; /* offset of comparison */ + short eoff; /* end offset of comparison */ + uchar skiphdr; /* should offset start after ip header */ uchar *val; uchar *mask; uchar *e; /* val + n*len */ @@ -148,9 +162,15 @@ parseop(char **pp) len = 1; p += 5; } - else if(strncmp(p, "data", 4) == 0){ - type = Tdata; - p += 4; + else if(strncmp(p, "data", 4) == 0 || strncmp(p, "iph", 3) == 0){ + if(strncmp(p, "data", 4) == 0) { + type = Tdata; + p += 4; + } + else { + type = Tiph; + p += 3; + } p = skipwhite(p); if(*p != '[') return nil; @@ -173,9 +193,8 @@ parseop(char **pp) return nil; p++; len = end - off + 1; - off += (ulong)(ipoff->data); } - else + else return nil; f = smalloc(sizeof(*f)); @@ -186,6 +205,10 @@ parseop(char **pp) f->mask = nil; f->n = 1; f->ref = 1; + if(type == Tdata) + f->skiphdr = 1; + else + f->skiphdr = 0; return f; } @@ -250,6 +273,7 @@ parsemux(char *p) v4parseip(f->mask, mask); break; case Tdata: + case Tiph: f->mask = smalloc(f->len); parseval(f->mask, mask, f->len); break; @@ -278,6 +302,7 @@ parsemux(char *p) break; case Tproto: case Tdata: + case Tiph: parseval(v, vals[n], f->len); break; } @@ -333,7 +358,8 @@ ipmuxcmp(Ipmux *a, Ipmux *b) return n; /* compare offsets, call earlier ones more specific */ - n = a->off - b->off; + n = (a->off+((int)a->skiphdr)*(ulong)ipoff->data) - + (b->off+((int)b->skiphdr)*(ulong)ipoff->data); if(n != 0) return n; @@ -629,29 +655,35 @@ static void ipmuxkick(Conv *c) { Block *bp; - Iphdr *ih; + struct Ip6hdr *ih6; bp = qget(c->wq); if(bp == nil) return; - - ih = (Iphdr*)(bp->rp); - - ipoput(c->p->f, bp, 0, ih->ttl, ih->tos); + else { + Ip4hdr *ih4 = (Ip4hdr*)(bp->rp); + if((ih4->vihl)&0xF0 != 0x60) + ipoput4(c->p->f, bp, 0, ih4->ttl, ih4->tos); + else { + ih6 = (struct Ip6hdr*)ih4; + ipoput6(c->p->f, bp, 0, ih6->ttl, 0); + } + } } static void ipmuxiput(Proto *p, Ipifc *ifc, Block *bp) { - int len; + int len, hl; Fs *f = p->f; uchar *m, *h, *v, *e, *ve, *hp; Conv *c; Ipmux *mux; - Iphdr *ip; - uchar *ia; + Ip4hdr *ip; + Ip6hdr *ip6; - ia = ifc->lifc->local; + ip = (Ip4hdr*)bp->rp; + hl = (ip->vihl&0x0F)<<2; if(p->priv == nil) goto nomatch; @@ -668,7 +700,7 @@ ipmuxiput(Proto *p, Ipifc *ifc, Block *bp) mux = mux->no; continue; } - hp = h + mux->off; + hp = h + mux->off + ((int)mux->skiphdr)*hl; switch(mux->ctype){ case Cbyte: if(*mux->val == *hp) @@ -695,11 +727,11 @@ ipmuxiput(Proto *p, Ipifc *ifc, Block *bp) goto yes; break; case Cifc: - if(*((ulong*)mux->val) == *(ulong*)(ia + IPv4off)) + if(*((ulong*)mux->val) == *(ulong*)(ifc->lifc->local + IPv4off)) goto yes; break; case Cmifc: - if((*(ulong*)(ia + IPv4off) & (*((ulong*)mux->mask))) == *((ulong*)mux->val)) + if((*(ulong*)(ifc->lifc->local + IPv4off) & (*((ulong*)mux->mask))) == *((ulong*)mux->val)) goto yes; break; default: @@ -727,19 +759,24 @@ yes: if(c != nil){ /* tack on interface address */ bp = padblock(bp, IPaddrlen); - ipmove(bp->rp, ia); + ipmove(bp->rp, ifc->lifc->local); bp = concatblock(bp); if(bp != nil) - if (qpass(c->rq, bp) < 0) + if(qpass(c->rq, bp) < 0) print("Q"); return; } nomatch: /* doesn't match any filter, hand it to the specific protocol handler */ - ip = (Iphdr*)bp->rp; - p = f->t2p[ip->proto]; - if(p) + ip = (Ip4hdr*)bp->rp; + if((ip->vihl&0xF0)==0x40) { + p = f->t2p[ip->proto]; + } else { + ip6 = (Ip6hdr*)bp->rp; + p = f->t2p[ip6->proto]; + } + if(p && p->rcv) (*p->rcv)(p, ifc, bp); else freeblist(bp); @@ -759,7 +796,9 @@ ipmuxsprint(Ipmux *mux, int level, char *buf, int len) n += snprint(buf+n, len-n, "\n"); return n; } - n += snprint(buf+n, len-n, "h[%d:%d]&", mux->off, mux->off+mux->len-1); + n += snprint(buf+n, len-n, "h[%d:%d]&", + mux->off+((int)mux->skiphdr)*((int)ipoff->data), + mux->off+(((int)mux->skiphdr)*((int)ipoff->data))+mux->len-1); for(i = 0; i < mux->len; i++) n += snprint(buf+n, len - n, "%2.2ux", mux->mask[i]); n += snprint(buf+n, len-n, "="); diff --git a/ip/iproute.c b/ip/iproute.c index e757cc0e66ec9e3da7760ea72458376663e904e2..238a1e2741a959ab09889b252d8d2cffd112d3b5 100644 --- a/ip/iproute.c +++ b/ip/iproute.c @@ -16,22 +16,6 @@ Route* v4freelist; Route* v6freelist; RWlock routelock; -enum -{ - RWadd, - RWflush, - RWremove, - RWtag, -}; - -static -Cmdtab routecmd[] = { - RWadd, "add", 4, - RWflush, "flush", 2, - RWremove, "remove", 3, - RWtag, "tag", 2, -}; - static void freeroute(Route *r) { @@ -337,6 +321,7 @@ v4addroute(Fs *f, char *tag, uchar *a, uchar *mask, uchar *gate, int type) } #define V6H(a) (((a)[IPllen-1] & 0x07ffffff)>>(32-Lroot-5)) +#define ISDFLT(a, mask, tag) ((ipcmp((a),v6Unspecified)==0) && (ipcmp((mask),v6Unspecified)==0) && (strcmp((tag), "ra")!=0)) void v6addroute(Fs *f, char *tag, uchar *a, uchar *mask, uchar *gate, int type) @@ -346,6 +331,12 @@ v6addroute(Fs *f, char *tag, uchar *a, uchar *mask, uchar *gate, int type) ulong x, y; int h, eh; + /* + if(ISDFLT(a, mask, tag)) + f->v6p->cdrouter = -1; + */ + + for(h = 0; h < IPllen; h++){ x = nhgetl(a+4*h); y = nhgetl(mask+4*h); @@ -528,8 +519,8 @@ v6lookup(Fs *f, uchar *a) ulong x, y; uchar gate[IPaddrlen]; - if(memcmp(a, v4prefix, 12) == 0){ - q = v4lookup(f, a+12); + if(memcmp(a, v4prefix, IPv4off) == 0){ + q = v4lookup(f, a+IPv4off); if(q != nil) return q; } @@ -568,10 +559,8 @@ next: ; if(q && (q->ifc == nil || q->ifcid != q->ifc->ifcid)){ if(q->type & Rifc) { - hnputl(gate, q->v6.gate[0]); - hnputl(gate+4, q->v6.gate[1]); - hnputl(gate+8, q->v6.gate[2]); - hnputl(gate+12, q->v6.gate[3]); + for(h = 0; h < IPllen; h++) + hnputl(gate+4*h, q->v6.address[h]); q->ifc = findipifc(f, gate, q->type); } else q->ifc = findipifc(f, q->v6.gate, q->type); @@ -606,10 +595,10 @@ routetype(int type, char *p) enum { - Rlinelen= 89, + Rlinelen= 137, }; -char *rformat = "%-24.24I %-24.24M %-24.24I %4.4s %4.4s %3s\n"; +char *rformat = "%-40.40I %-40.40M %-40.40I %4.4s %4.4s %3s\n"; void convroute(Route *r, uchar *addr, uchar *mask, uchar *gate, char *t, int *nifc) @@ -779,7 +768,6 @@ routewrite(Fs *f, Chan *c, char *p, int n) int h, changed; char *tag; Cmdbuf *cb; - Cmdtab *ct; uchar addr[IPaddrlen]; uchar mask[IPaddrlen]; uchar gate[IPaddrlen]; @@ -791,25 +779,7 @@ routewrite(Fs *f, Chan *c, char *p, int n) nexterror(); } - ct = lookupcmd(cb, routecmd, nelem(routecmd)); - - switch(ct->index){ - case RWadd: - parseip(addr, cb->f[1]); - parseipmask(mask, cb->f[2]); - parseip(gate, cb->f[3]); - tag = "none"; - if(c != nil){ - a = c->aux; - tag = a->tag; - } - if(memcmp(addr, v4prefix, IPv4off) == 0) - v4addroute(f, tag, addr+IPv4off, mask+IPv4off, gate+IPv4off, 0); - else - v6addroute(f, tag, addr, mask, gate, 0); - break; - - case RWflush: + if(strcmp(cb->f[0], "flush") == 0){ tag = cb->f[1]; for(h = 0; h < nelem(f->v4root); h++) for(changed = 1; changed;){ @@ -823,18 +793,34 @@ routewrite(Fs *f, Chan *c, char *p, int n) changed = routeflush(f, f->v6root[h], tag); wunlock(&routelock); } - break; - - case RWremove: + } else if(strcmp(cb->f[0], "remove") == 0){ + if(cb->nf < 3) + error(Ebadarg); parseip(addr, cb->f[1]); parseipmask(mask, cb->f[2]); if(memcmp(addr, v4prefix, IPv4off) == 0) v4delroute(f, addr+IPv4off, mask+IPv4off, 1); else v6delroute(f, addr, mask, 1); - break; + } else if(strcmp(cb->f[0], "add") == 0){ + if(cb->nf < 4) + error(Ebadarg); + parseip(addr, cb->f[1]); + parseipmask(mask, cb->f[2]); + parseip(gate, cb->f[3]); + tag = "none"; + if(c != nil){ + a = c->aux; + tag = a->tag; + } + if(memcmp(addr, v4prefix, IPv4off) == 0) + v4addroute(f, tag, addr+IPv4off, mask+IPv4off, gate+IPv4off, 0); + else + v6addroute(f, tag, addr, mask, gate, 0); + } else if(strcmp(cb->f[0], "tag") == 0) { + if(cb->nf < 2) + error(Ebadarg); - case RWtag: a = c->aux; na = newipaux(a->owner, cb->f[1]); c->aux = na; diff --git a/ip/ipv6.c b/ip/ipv6.c new file mode 100644 index 0000000000000000000000000000000000000000..2baeb283f5387a6a99d59b0e402cebbf129918e6 --- /dev/null +++ b/ip/ipv6.c @@ -0,0 +1,746 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include "ip.h" +#include "ipv6.h" + +enum +{ + IP4HDR = 20, /* sizeof(Ip4hdr) */ + IP6HDR = 40, /* sizeof(Ip6hdr) */ + IP_HLEN4 = 0x05, /* Header length in words */ + IP_DF = 0x4000, /* Don't fragment */ + IP_MF = 0x2000, /* More fragments */ + IP6FHDR = 8, /* sizeof(Fraghdr6) */ + IP_MAX = (32*1024), /* Maximum Internet packet size */ +}; + +#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)) + +typedef struct IP IP; +typedef struct Fragment4 Fragment4; +typedef struct Fragment6 Fragment6; +typedef struct Ipfrag Ipfrag; + +Block* ip6reassemble(IP*, int, Block*, Ip6hdr*); +void ipfragfree6(IP*, Fragment6*); +Fragment6* ipfragallo6(IP*); +static Block* procxtns(IP *ip, Block *bp, int doreasm); +int unfraglen(Block *bp, uchar *nexthdr, int setfh); +Block* procopts(Block *bp); + +/* MIB II counters */ +enum +{ + Forwarding, + DefaultTTL, + InReceives, + InHdrErrors, + InAddrErrors, + ForwDatagrams, + InUnknownProtos, + InDiscards, + InDelivers, + OutRequests, + OutDiscards, + OutNoRoutes, + ReasmTimeout, + ReasmReqds, + ReasmOKs, + ReasmFails, + FragOKs, + FragFails, + FragCreates, + + Nstats, +}; + +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", +}; + +struct Fragment4 +{ + Block* blist; + Fragment4* next; + ulong src; + ulong dst; + ushort id; + ulong age; +}; + +struct Fragment6 +{ + Block* blist; + Fragment6* next; + uchar src[IPaddrlen]; + uchar dst[IPaddrlen]; + uint id; + ulong age; +}; + +struct Ipfrag +{ + ushort foff; + ushort flen; +}; + +/* an instance of IP */ +struct IP +{ + ulong stats[Nstats]; + + QLock fraglock4; + Fragment4* flisthead4; + Fragment4* fragfree4; + Ref id4; + + QLock fraglock6; + Fragment6* flisthead6; + Fragment6* fragfree6; + Ref id6; + + int iprouting; /* true if we route like a gateway */ +}; + +void +ipoput6(Fs *f, Block *bp, int gating, int ttl, int tos) +{ + int tentative; + Ipifc *ifc; + uchar *gate, nexthdr; + Ip6hdr *eh; + int medialen, len, chunk, uflen, flen, seglen, lid, offset, fragoff, morefrags, blklen; + Route *r, *sr; + Fraghdr6 fraghdr; + Block *xp, *nb; + IP *ip; + + + 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 */ + len = blocklen(bp); + + tentative = iptentative(f, eh->src); + if(tentative){ + netlog(f, Logip, "reject tx of packet with tentative src address\n"); + goto free; + } + + if(gating){ + chunk = nhgets(eh->ploadlen); + if(chunk > len){ + ip->stats[OutDiscards]++; + netlog(f, Logip, "short gated packet\n"); + goto free; + } + if(chunk + IPV6HDR_LEN < len) + len = chunk + IPV6HDR_LEN; + } + + if(len >= IP_MAX){ + print("len > IP_MAX, free\n"); + ip->stats[OutDiscards]++; + netlog(f, Logip, "exceeded ip max size %I\n", eh->dst); + goto free; + } + + r = v6lookup(f, eh->dst); + if(r == nil){ + print("no route for %I, src %I free\n", eh->dst, eh->src); + ip->stats[OutNoRoutes]++; + netlog(f, Logip, "no interface %I\n", eh->dst); + goto free; + } + + ifc = r->ifc; + if(r->type & (Rifc|Runi)) + gate = eh->dst; + else + if(r->type & (Rbcast|Rmulti)) { + gate = eh->dst; + sr = v6lookup(f, eh->src); + if(sr != nil && (sr->type & Runi)) + ifc = sr->ifc; + } + 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; + } + + if(waserror()){ + runlock(ifc); + nexterror(); + } + + if(ifc->m == nil) { + goto raise; + } + + /* If we dont need to fragment just send it */ + medialen = ifc->maxmtu - ifc->m->hsize; + if(len <= medialen) { + hnputs(eh->ploadlen, len-IPV6HDR_LEN); + ifc->m->bwrite(ifc, bp, V6, gate); + runlock(ifc); + poperror(); + return; + } + + if(gating) + if(ifc->reassemble <= 0) { + + /* v6 intermediate nodes are not supposed to fragment pkts; + we fragment if ifc->reassemble is turned on; an exception + needed for nat. + */ + + ip->stats[OutDiscards]++; + icmppkttoobig6(f, ifc, bp); + netlog(f, Logip, "%I: gated pkts not fragmented\n", eh->dst); + goto raise; + } + + /* start v6 fragmentation */ + uflen = unfraglen(bp, &nexthdr, 1); + if(uflen > medialen) { + ip->stats[FragFails]++; + ip->stats[OutDiscards]++; + netlog(f, Logip, "%I: unfragmentable part too big\n", eh->dst); + goto raise; + } + + flen = len - uflen; + seglen = (medialen - (uflen + IP6FHDR)) & ~7; + if(seglen < 8) { + ip->stats[FragFails]++; + ip->stats[OutDiscards]++; + netlog(f, Logip, "%I: seglen < 8\n", eh->dst); + goto raise; + } + + lid = incref(&ip->id6); + fraghdr.nexthdr = nexthdr; + fraghdr.res = 0; + hnputl(fraghdr.id, lid); + + xp = bp; + offset = uflen; + while (xp != nil && offset && offset >= BLEN(xp)) { + offset -= BLEN(xp); + xp = xp->next; + } + xp->rp += offset; + + fragoff = 0; + morefrags = 1; + + for(; fragoff < flen; fragoff += seglen) { + nb = allocb(uflen + IP6FHDR + seglen); + + if(fragoff + seglen >= flen) { + seglen = flen - fragoff; + morefrags = 0; + } + + hnputs(eh->ploadlen, seglen+IP6FHDR); + memmove(nb->wp, eh, uflen); + nb->wp += uflen; + + hnputs(fraghdr.offsetRM, fragoff); // last 3 bits must be 0 + fraghdr.offsetRM[1] |= morefrags; + memmove(nb->wp, &fraghdr, IP6FHDR); + nb->wp += IP6FHDR; + + /* Copy data */ + chunk = seglen; + while (chunk) { + if(!xp) { + ip->stats[OutDiscards]++; + ip->stats[FragFails]++; + freeblist(nb); + netlog(f, Logip, "!xp: chunk in v6%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; + } + + ifc->m->bwrite(ifc, nb, V6, gate); + ip->stats[FragCreates]++; + } + ip->stats[FragOKs]++; + +raise: + runlock(ifc); + poperror(); +free: + freeblist(bp); + +} + +void +ipiput6(Fs *f, Ipifc *ifc, Block *bp) +{ + int hl; + int hop, tos; + uchar proto; + Ip6hdr *h; + Proto *p; + int notforme; + int tentative; + uchar v6dst[IPaddrlen]; + IP *ip; + Route *r, *sr; + + 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 < IP6HDR) + hl = IP6HDR; + if(hl > 64) + hl = 64; + bp = pullupblock(bp, hl); + if(bp == nil) + return; + } + + h = (Ip6hdr *)(bp->rp); + + memmove(&v6dst[0], &(h->dst)[0], IPaddrlen); + notforme = ipforme(f, v6dst) == 0; + tentative = iptentative(f, v6dst); + + if(tentative && (h->proto != ICMPv6)) { + print("tentative addr, drop\n"); + freeblist(bp); + return; + } + + /* Check header version */ + if(BLKIPVER(bp) != IP_VER6) { + ip->stats[InHdrErrors]++; + netlog(f, Logip, "ip: bad version %ux\n", (h->vcf[0]&0xF0)>>2); + freeblist(bp); + return; + } + + /* route */ + if(notforme) { + if(!ip->iprouting){ + freeb(bp); + return; + } + /* don't forward to source's network */ + sr = v6lookup(f, h->src); + r = v6lookup(f, h->dst); + + if(r == nil || sr == r){ + ip->stats[OutDiscards]++; + freeblist(bp); + return; + } + + /* don't forward if packet has timed out */ + hop = h->ttl; + if(hop < 1) { + ip->stats[InHdrErrors]++; + icmpttlexceeded6(f, ifc, bp); + freeblist(bp); + return; + } + + /* process headers & reassemble if the interface expects it */ + bp = procxtns(ip, bp, r->ifc->reassemble); + + if(bp == nil) + return; + + ip->stats[ForwDatagrams]++; + h = (Ip6hdr *) (bp->rp); + tos = IPV6CLASS(h); + hop = h->ttl; + ipoput6(f, bp, 1, hop-1, tos); + return; + } + + /* reassemble & process headers if needed */ + bp = procxtns(ip, bp, 1); + + if(bp == nil) + return; + + h = (Ip6hdr *) (bp->rp); + 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); +} + +/* + * ipfragfree6 - copied from ipfragfree4 - assume hold fraglock6 + */ +void +ipfragfree6(IP *ip, Fragment6 *frag) +{ + Fragment6 *fl, **l; + + if(frag->blist) + freeblist(frag->blist); + + memset(frag->src, 0, IPaddrlen); + frag->id = 0; + frag->blist = nil; + + l = &ip->flisthead6; + for(fl = *l; fl; fl = fl->next) { + if(fl == frag) { + *l = frag->next; + break; + } + l = &fl->next; + } + + frag->next = ip->fragfree6; + ip->fragfree6 = frag; + +} + +/* + * ipfragallo6 - copied from ipfragalloc4 + */ +Fragment6* +ipfragallo6(IP *ip) +{ + Fragment6 *f; + + while(ip->fragfree6 == nil) { + /* free last entry on fraglist */ + for(f = ip->flisthead6; f->next; f = f->next) + ; + ipfragfree6(ip, f); + } + f = ip->fragfree6; + ip->fragfree6 = f->next; + f->next = ip->flisthead6; + ip->flisthead6 = f; + f->age = msec + 30000; + + return f; +} + +static Block* +procxtns(IP *ip, Block *bp, int doreasm) { + + int offset; + uchar proto; + Ip6hdr *h; + + h = (Ip6hdr *) (bp->rp); + offset = unfraglen(bp, &proto, 0); + + if((proto == FH) && (doreasm != 0)) { + bp = ip6reassemble(ip, offset, bp, h); + if(bp == nil) + return nil; + offset = unfraglen(bp, &proto, 0); + } + + if(proto == DOH || offset > IP6HDR) + bp = procopts(bp); + + return bp; +} + + +/* returns length of "Unfragmentable part", i.e., sum of lengths of ipv6 hdr, + * 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. + */ +int +unfraglen(Block *bp, uchar *nexthdr, int setfh) +{ + uchar *p, *q; + int ufl, hs; + + p = bp->rp; + q = p+6; /* proto, = p+sizeof(Ip6hdr.vcf)+sizeof(Ip6hdr.ploadlen) */ + *nexthdr = *q; + ufl = IP6HDR; + p += ufl; + + for(;;) { + if(*nexthdr == HBH || *nexthdr == RH) { + *nexthdr = *p; + hs = ((int)*(p+1) + 1) * 8; + ufl += hs; + q = p; + p += hs; + } + else + break; + } + + if(*nexthdr == FH) + *q = *p; + + if(setfh) + *q = FH; + + return ufl; +} + +Block* +procopts(Block *bp) +{ + return bp; +} + +Block* +ip6reassemble(IP* ip, int uflen, Block* bp, Ip6hdr* ih) +{ + + int fend, offset; + uint id; + Fragment6 *f, *fnext; + Fraghdr6 *fraghdr; + uchar src[IPaddrlen], dst[IPaddrlen]; + Block *bl, **l, *last, *prev; + int ovlap, len, fragsize, pktposn; + + 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){ + bp = pullupblock(bp, blocklen(bp)); + ih = (Ip6hdr *)(bp->rp); + } + + + qlock(&ip->fraglock6); + + /* + * find a reassembly queue for this fragment + */ + for(f = ip->flisthead6; f; f = fnext){ + fnext = f->next; + if(ipcmp(f->src, src)==0 && ipcmp(f->dst, dst)==0 && f->id == id) + break; + if(f->age < msec){ + ip->stats[ReasmTimeout]++; + ipfragfree6(ip, f); + } + } + + + /* + * if this isn't a fragmented packet, accept it + * and get rid of any fragments that might go + * with it. + */ + if(nhgets(fraghdr->offsetRM)==0) { // first frag is also the last + if(f != nil) { + ipfragfree6(ip, f); + ip->stats[ReasmFails]++; + } + qunlock(&ip->fraglock6); + return bp; + } + + if(bp->base+sizeof(Ipfrag) >= bp->rp){ + bp = padblock(bp, sizeof(Ipfrag)); + bp->rp += sizeof(Ipfrag); + } + + BKFG(bp)->foff = offset; + BKFG(bp)->flen = nhgets(ih->ploadlen) + IP6HDR - uflen - IP6FHDR; + + /* First fragment allocates a reassembly queue */ + if(f == nil) { + f = ipfragallo6(ip); + f->id = id; + memmove(f->src, src, IPaddrlen); + memmove(f->dst, dst, IPaddrlen); + + f->blist = bp; + + qunlock(&ip->fraglock6); + 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->fraglock6); + 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, uflen); + (*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 + * with the trailing bit of fraghdr->offsetRM[1] set, we're done. + */ + pktposn = 0; + for(bl = f->blist; bl; bl = bl->next) { + if(BKFG(bl)->foff != pktposn) + break; + + 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; bl = bl->next) { + fragsize = BKFG(bl)->flen; + len += fragsize; + bl->rp += uflen + IP6FHDR; + bl->wp = bl->rp + fragsize; + } + + bl = f->blist; + f->blist = nil; + ipfragfree6(ip, f); + ih = (Ip6hdr*)(bl->rp); + hnputs(ih->ploadlen, len); + qunlock(&ip->fraglock6); + ip->stats[ReasmOKs]++; + return bl; + } + pktposn += BKFG(bl)->flen; + } + qunlock(&ip->fraglock6); + return nil; +} + diff --git a/ip/ipv6.h b/ip/ipv6.h new file mode 100644 index 0000000000000000000000000000000000000000..fb0087875b8cdda0604a9805bfedf95bdab4568d --- /dev/null +++ b/ip/ipv6.h @@ -0,0 +1,182 @@ +#define MIN(a, b) ((a) <= (b) ? (a) : (b)) +#define isv6mcast(addr) ((addr)[0] == 0xff) +#define islinklocal(addr) ( ((addr)[0] == 0xfe) && ((addr)[1] == 0x80) ) +#define issitelocal(addr) ( ((addr)[0] == 0xfe) && ((addr)[1] == 0xc0) ) +#define isv6global(addr) ( (addr)[0] == 0x2 ) +#define optexsts(np) (nhgets((np)->ploadlen) > 24) +#define issmcast(addr) (memcmp((addr), v6solicitednode, 13) == 0) + +/* from RFC 2460 */ + +typedef struct Ip6hdr Ip6hdr; +typedef struct Opthdr Opthdr; +typedef struct Routinghdr Routinghdr; +typedef struct Fraghdr6 Fraghdr6; + +struct Ip6hdr { + uchar vcf[4]; // version:4, traffic class:8, flow label:20 + uchar ploadlen[2]; // payload length: packet length - 40 + uchar proto; // next header type + uchar ttl; // hop limit + uchar src[IPaddrlen]; + uchar dst[IPaddrlen]; +}; + +struct Opthdr { + uchar nexthdr; + uchar len; +}; + +struct Routinghdr { + uchar nexthdr; + uchar len; + uchar rtetype; + uchar segrem; +}; + +struct Fraghdr6 { + uchar nexthdr; + uchar res; + uchar offsetRM[2]; // Offset, Res, M flag + uchar id[4]; +}; + + +enum { /* Header Types */ + HBH = 0, //? + ICMP = 1, + IGMP = 2, + GGP = 3, + IPINIP = 4, + ST = 5, + TCP = 6, + UDP = 17, + ISO_TP4 = 29, + RH = 43, + FH = 44, + IDRP = 45, + RSVP = 46, + AH = 51, + ESP = 52, + ICMPv6 = 58, + NNH = 59, + DOH = 60, + ISO_IP = 80, + IGRP = 88, + OSPF = 89, + + Maxhdrtype = 256, +}; + + +enum { + // multicast flgs and scop + + well_known_flg = 0, + transient_flg = 1, + + node_local_scop = 1, + link_local_scop = 2, + site_local_scop = 5, + org_local_scop = 8, + global_scop = 14, + + // various prefix lengths + + SOLN_PREF_LEN = 13, + + // icmpv6 unreach codes + icmp6_no_route = 0, + icmp6_ad_prohib = 1, + icmp6_unassigned = 2, + icmp6_adr_unreach = 3, + icmp6_port_unreach = 4, + icmp6_unkn_code = 5, + + // various flags & constants + + v6MINTU = 1280, + HOP_LIMIT = 255, + ETHERHDR_LEN = 14, + IPV6HDR_LEN = 40, + IPV4HDR_LEN = 20, + + // option types + + SRC_LLADDRESS = 1, + TARGET_LLADDRESS = 2, + PREFIX_INFO = 3, + REDIR_HEADER = 4, + MTU_OPTION = 5, + + SRC_UNSPEC = 0, + SRC_UNI = 1, + TARG_UNI = 2, + TARG_MULTI = 3, + + t_unitent = 1, + t_uniproxy = 2, + t_unirany = 3, + + // Router constants (all times in milliseconds) + + MAX_INITIAL_RTR_ADVERT_INTERVAL = 16000, + MAX_INITIAL_RTR_ADVERTISEMENTS = 3, + MAX_FINAL_RTR_ADVERTISEMENTS = 3, + MIN_DELAY_BETWEEN_RAS = 3000, + MAX_RA_DELAY_TIME = 500, + + // Host constants + + MAX_RTR_SOLICITATION_DELAY = 1000, + RTR_SOLICITATION_INTERVAL = 4000, + MAX_RTR_SOLICITATIONS = 3, + + // Node constants + + MAX_MULTICAST_SOLICIT = 3, + MAX_UNICAST_SOLICIT = 3, + MAX_ANYCAST_DELAY_TIME = 1000, + MAX_NEIGHBOR_ADVERTISEMENT = 3, + REACHABLE_TIME = 30000, + RETRANS_TIMER = 1000, + DELAY_FIRST_PROBE_TIME = 5000, + +}; + +extern void ipv62smcast(uchar *, uchar *); +extern void icmpns(Fs *f, uchar* src, int suni, uchar* targ, int tuni, uchar* mac); +extern void icmpna(Fs *f, uchar* src, uchar* dst, uchar* targ, uchar* mac, uchar flags); +extern void icmpttlexceeded6(Fs *f, Ipifc *ifc, Block *bp); +extern void icmppkttoobig6(Fs *f, Ipifc *ifc, Block *bp); +extern void icmphostunr(Fs *f, Ipifc *ifc, Block *bp, int code, int free); + +extern uchar v6allnodesN[IPaddrlen]; +extern uchar v6allnodesL[IPaddrlen]; +extern uchar v6allroutersN[IPaddrlen]; +extern uchar v6allroutersL[IPaddrlen]; +extern uchar v6allnodesNmask[IPaddrlen]; +extern uchar v6allnodesLmask[IPaddrlen]; +extern uchar v6allroutersS[IPaddrlen]; +extern uchar v6solicitednode[IPaddrlen]; +extern uchar v6solicitednodemask[IPaddrlen]; +extern uchar v6Unspecified[IPaddrlen]; +extern uchar v6loopback[IPaddrlen]; +extern uchar v6loopbackmask[IPaddrlen]; +extern uchar v6linklocal[IPaddrlen]; +extern uchar v6linklocalmask[IPaddrlen]; +extern uchar v6sitelocal[IPaddrlen]; +extern uchar v6sitelocalmask[IPaddrlen]; +extern uchar v6glunicast[IPaddrlen]; +extern uchar v6multicast[IPaddrlen]; +extern uchar v6multicastmask[IPaddrlen]; + +extern int v6linklocalprefix; +extern int v6sitelocalprefix; +extern int v6loopbackprefix; +extern int v6multicastprefix; +extern int v6solicitednodeprefix; +extern int v6allnodesNprefix; +extern int v6allnodesLprefix; + +extern int ReTransTimer; diff --git a/ip/loopbackmedium.c b/ip/loopbackmedium.c index b0f2fb9971e8b79a7ccfb6a0e87e953185bef6f9..b1b4bb17a8e65b1203fb0021f02983c636e5409f 100644 --- a/ip/loopbackmedium.c +++ b/ip/loopbackmedium.c @@ -94,7 +94,7 @@ loopbackread(void *a) if(ifc->lifc == nil) freeb(bp); else - ipiput(lb->f, ifc, bp); + ipiput4(lb->f, ifc, bp); runlock(ifc); poperror(); } diff --git a/ip/netdevmedium.c b/ip/netdevmedium.c index 5dd90a0588821771ef0cee4c8002a628491bf181..c13dd1b625b435f6a63af22cc709470d26f48f88 100644 --- a/ip/netdevmedium.c +++ b/ip/netdevmedium.c @@ -140,7 +140,7 @@ ZZZ is this a good idea? if(ifc->lifc == nil) freeb(bp); else - ipiput(er->f, ifc, bp); + ipiput4(er->f, ifc, bp); runlock(ifc); poperror(); } diff --git a/ip/netlog.c b/ip/netlog.c index 3ae36f3425e266325a8c23f18b545e1ae071f754..90da1774000df2567ebc4c22a99d6573e1f079c1 100644 --- a/ip/netlog.c +++ b/ip/netlog.c @@ -207,7 +207,7 @@ netlogctl(Fs *f, char* s, int n) cmderror(cb, "unknown ip control message"); } - for(i = 1; i < cb->nf; i++){ + for(i = 1; i < n; i++){ for(fp = flags; fp->name; fp++) if(strcmp(fp->name, cb->f[i]) == 0) break; diff --git a/ip/pktmedium.c b/ip/pktmedium.c index 3432ad42d021458bf1e5aa4e4cce2455586d6e7b..6f74f3f0310b4aacb2c79b83d5232e1127638f2e 100644 --- a/ip/pktmedium.c +++ b/ip/pktmedium.c @@ -64,7 +64,7 @@ pktin(Fs *f, Ipifc *ifc, Block *bp) if(ifc->lifc == nil) freeb(bp); else - ipiput(f, ifc, bp); + ipiput4(f, ifc, bp); } void diff --git a/ip/rudp.c b/ip/rudp.c index f6daa50f2bceb9814f7277cb87d3bcbfef325519..c9ad2ef7dd580705d46e9d43ca2d35f6adadeced 100644 --- a/ip/rudp.c +++ b/ip/rudp.c @@ -321,7 +321,7 @@ doipoput(Conv *c, Fs *f, Block *bp, int x, int ttl, int tos) if(ucb->randdrop && nrand(100) < ucb->randdrop) freeblist(bp); else - ipoput(f, bp, x, ttl, tos); + ipoput4(f, bp, x, ttl, tos); } int @@ -402,6 +402,7 @@ rudpkick(Conv *c) return; uh = (Udphdr *)(bp->rp); + uh->vihl = IP_VER4; rh = (Rudphdr*)uh; @@ -987,6 +988,7 @@ relsendack(Conv *c, Reliable *r, int hangup) bp->wp += UDP_IPHDR + UDP_RHDRSIZE; f = c->p->f; uh = (Udphdr *)(bp->rp); + uh->vihl = IP_VER4; rh = (Rudphdr*)uh; ptcllen = (UDP_RHDRSIZE-UDP_PHDRSIZE); diff --git a/ip/tcp.c b/ip/tcp.c index d4c9375c634a0a1fe386eaef48a19e3decced60e..5dab83c0dae7500426c908328fe392ccf217f8f4 100644 --- a/ip/tcp.c +++ b/ip/tcp.c @@ -11,13 +11,21 @@ enum { QMAX = 64*1024-1, IP_TCPPROTO = 6, - TCP_IPLEN = 8, - TCP_PHDRSIZE = 12, - TCP_HDRSIZE = 20, - TCP_TCBPHDRSZ = 40, - TCP_PKT = TCP_IPLEN+TCP_PHDRSIZE, + + TCP4_IPLEN = 8, + TCP4_PHDRSIZE = 12, + TCP4_HDRSIZE = 20, + TCP4_TCBPHDRSZ = 40, + TCP4_PKT = TCP4_IPLEN+TCP4_PHDRSIZE, + + TCP6_IPLEN = 0, + TCP6_PHDRSIZE = 40, + TCP6_HDRSIZE = 20, + TCP6_TCBPHDRSZ = 60, + TCP6_PKT = TCP6_IPLEN+TCP6_PHDRSIZE, + TcptimerOFF = 0, - TcptimerON = 1, + TcptimerON = 1, TcptimerDONE = 2, MAX_TIME = (1<<20), /* Forever */ TCP_ACK = 200, /* Timed ack sequence in ms */ @@ -37,6 +45,7 @@ enum MSL2 = 10, MSPTICK = 50, /* Milliseconds per timer tick */ DEF_MSS = 1460, /* Default mean segment */ + DEF_MSS6 = 1280, /* Default mean segment (min) for v6 */ DEF_RTT = 500, /* Default round trip */ DEF_KAT = 30000, /* Default time ms) between keep alives */ TCP_LISTEN = 0, /* Listen connection */ @@ -86,8 +95,8 @@ struct Tcptimer void *arg; }; -typedef struct Tcphdr Tcphdr; -struct Tcphdr +typedef struct Tcp4hdr Tcp4hdr; +struct Tcp4hdr { uchar vihl; /* Version and header length */ uchar tos; /* Type of service */ @@ -112,6 +121,29 @@ struct Tcphdr uchar tcpmss[2]; }; +typedef struct Tcp6hdr Tcp6hdr; +struct Tcp6hdr +{ + uchar vcf[4]; + uchar ploadlen[2]; + uchar proto; + uchar ttl; + uchar tcpsrc[IPaddrlen]; + uchar tcpdst[IPaddrlen]; + uchar tcpsport[2]; + uchar tcpdport[2]; + uchar tcpseq[4]; + uchar tcpack[4]; + uchar tcpflag[2]; + uchar tcpwin[2]; + uchar tcpcksum[2]; + uchar tcpurg[2]; + /* Options segment */ + uchar tcpopt[2]; + uchar tcpmss[2]; +}; + + typedef struct Tcp Tcp; struct Tcp { @@ -123,13 +155,13 @@ struct Tcp ushort wnd; ushort urg; ushort mss; - ushort len; /* size of data */ + ushort len; /* size of data */ }; typedef struct Reseq Reseq; struct Reseq { - Reseq *next; + Reseq *next; Tcp seg; Block *bp; ushort length; @@ -148,14 +180,13 @@ struct Tcpctl ulong una; /* Unacked data pointer */ ulong nxt; /* Next sequence expected */ ulong ptr; /* Data pointer */ - ushort wnd; /* Tcp send window */ + ushort wnd; /* Tcp send window */ ulong urg; /* Urgent data pointer */ ulong wl2; - - /* to implement tahoe and reno TCP */ - ulong dupacks; /* number of duplicate acks rcvd */ - int recovery; /* loss recovery flag */ - ulong rxt; /* right window marker for recovery */ + /* to implement tahoe and reno TCP */ + ulong dupacks; /* number of duplicate acks rcvd */ + int recovery; /* loss recovery flag */ + ulong rxt; /* right window marker for recovery */ } snd; struct { ulong nxt; /* Receive pointer to next uchar slot */ @@ -190,7 +221,10 @@ struct Tcpctl int nochecksum; /* non-zero means don't send checksums */ int flgcnt; /* 1 when we're waiting for a SYN/FIN ACK */ - Tcphdr protohdr; /* prototype header */ + union { + Tcp4hdr tcp4hdr; + Tcp6hdr tcp6hdr; + } protohdr; /* prototype header */ }; int tcp_irtt = DEF_RTT; /* Initial guess at round trip time */ @@ -241,7 +275,7 @@ static char *statnames[] = typedef struct Tcppriv Tcppriv; struct Tcppriv { - Tcptimer *timers; /* List of active timers */ + Tcptimer *timers; /* List of active timers */ QLock tl; /* Protect timer list */ Rendez tcpr; /* used by tcpackproc */ @@ -642,22 +676,28 @@ tcpmtu(Conv *s) { Ipifc *ifc; int mtu; + int version = (isv4(s->raddr) && isv4(s->laddr)) ? 4 : 6; mtu = 0; ifc = findipifc(s->p->f, s->raddr, 0); - if(ifc != nil) - mtu = ifc->maxmtu - ifc->m->hsize - (TCP_PKT + TCP_HDRSIZE); - if(mtu < 4) - mtu = DEF_MSS; - return restrict_mtu(s->raddr, mtu); + if(ifc != nil) { + if(version == 4) + mtu = ifc->maxmtu - ifc->m->hsize - (TCP4_PKT + TCP4_HDRSIZE); + else + mtu = ifc->maxmtu - ifc->m->hsize - (TCP6_PKT + TCP6_HDRSIZE); + } + + if(mtu < 32) { + mtu = 1280; + } + + return mtu; } void inittcpctl(Conv *s, int mode) { Tcpctl *tcb; - Tcphdr *h; - tcb = (Tcpctl*)s->ptcl; memset(tcb, 0, sizeof(Tcpctl)); @@ -682,13 +722,26 @@ inittcpctl(Conv *s, int mode) if(mode != TCP_LISTEN) if(ipcmp(s->laddr, IPnoaddr) == 0) findlocalip(s->p->f, s->laddr, s->raddr); - h = &tcb->protohdr; - memset(h, 0, sizeof(*h)); - h->proto = IP_TCPPROTO; - hnputs(h->tcpsport, s->lport); - hnputs(h->tcpdport, s->rport); - v6tov4(h->tcpsrc, s->laddr); - v6tov4(h->tcpdst, s->raddr); + +// if(isv4(s->raddr)) { + if(memcmp(s->raddr, v4prefix, IPv4off) == 0 && + memcmp(s->laddr, v4prefix, IPv4off) == 0) { + Tcp4hdr* h4 = &tcb->protohdr.tcp4hdr; + memset(h4, 0, sizeof(*h4)); + h4->proto = IP_TCPPROTO; + hnputs(h4->tcpsport, s->lport); + hnputs(h4->tcpdport, s->rport); + v6tov4(h4->tcpsrc, s->laddr); + v6tov4(h4->tcpdst, s->raddr); + } else { + Tcp6hdr* h6 = &tcb->protohdr.tcp6hdr; + memset(h6, 0, sizeof(*h6)); + h6->proto = IP_TCPPROTO; + hnputs(h6->tcpsport, s->lport); + hnputs(h6->tcpdport, s->rport); + ipmove(h6->tcpsrc, s->laddr); + ipmove(h6->tcpdst, s->raddr); + } tcb->mss = tcb->cwind = tcpmtu(s); } @@ -763,37 +816,41 @@ tcpflag(ushort flag) } Block * -htontcp(Tcp *tcph, Block *data, Tcphdr *ph, Tcpctl *tcb) +htontcp6(Tcp *tcph, Block *data, Tcp6hdr *ph, Tcpctl *tcb) { int dlen; - Tcphdr *h; + Tcp6hdr *h; ushort csum; ushort hdrlen; - hdrlen = TCP_HDRSIZE; + hdrlen = TCP6_HDRSIZE; if(tcph->mss) hdrlen += MSS_LENGTH; if(data) { dlen = blocklen(data); - data = padblock(data, hdrlen + TCP_PKT); + data = padblock(data, hdrlen + TCP6_PKT); if(data == nil) return nil; } else { dlen = 0; - data = allocb(hdrlen + TCP_PKT + 64); /* the 64 pad is to meet mintu's */ + data = allocb(hdrlen + TCP6_PKT + 64); /* the 64 pad is to meet mintu's */ if(data == nil) return nil; - data->wp += hdrlen + TCP_PKT; + data->wp += hdrlen + TCP6_PKT; } /* copy in pseudo ip header plus port numbers */ - h = (Tcphdr *)(data->rp); - memmove(h, ph, TCP_TCBPHDRSZ); + h = (Tcp6hdr *)(data->rp); + memmove(h, ph, TCP6_TCBPHDRSZ); + + /* compose pseudo tcp header, do cksum calculation */ + hnputl(h->vcf, hdrlen + dlen); + h->ploadlen[0] = h->ploadlen[1] = h->proto = 0; + h->ttl = ph->proto; /* copy in variable bits */ - hnputs(h->tcplen, hdrlen + dlen); hnputl(h->tcpseq, tcph->seq); hnputl(h->tcpack, tcph->ack); hnputs(h->tcpflag, (hdrlen<<10) | tcph->flags); @@ -808,39 +865,92 @@ htontcp(Tcp *tcph, Block *data, Tcphdr *ph, Tcpctl *tcb) if(tcb != nil && tcb->nochecksum){ h->tcpcksum[0] = h->tcpcksum[1] = 0; } else { - csum = ptclcsum(data, TCP_IPLEN, hdrlen+dlen+TCP_PHDRSIZE); + csum = ptclcsum(data, TCP6_IPLEN, hdrlen+dlen+TCP6_PHDRSIZE); hnputs(h->tcpcksum, csum); } -/* netlog(f, Logtcpmsg, "%d > %d s %l8.8ux a %8.8lux %s w %.4ux l %d\n", - tcph->source, tcph->dest, - tcph->seq, tcph->ack, tcpflag((hdrlen<<10)|tcph->flags), - tcph->wnd, dlen); */ + /* move from pseudo header back to normal ip header */ + memset(h->vcf, 0, 4); + h->vcf[0] = IP_VER6; + hnputs(h->ploadlen, hdrlen+dlen); + h->proto = ph->proto; + + return data; +} + +Block * +htontcp4(Tcp *tcph, Block *data, Tcp4hdr *ph, Tcpctl *tcb) +{ + int dlen; + Tcp4hdr *h; + ushort csum; + ushort hdrlen; + + hdrlen = TCP4_HDRSIZE; + if(tcph->mss) + hdrlen += MSS_LENGTH; + + if(data) { + dlen = blocklen(data); + data = padblock(data, hdrlen + TCP4_PKT); + if(data == nil) + return nil; + } + else { + dlen = 0; + data = allocb(hdrlen + TCP4_PKT + 64); /* the 64 pad is to meet mintu's */ + if(data == nil) + return nil; + data->wp += hdrlen + TCP4_PKT; + } + + /* copy in pseudo ip header plus port numbers */ + h = (Tcp4hdr *)(data->rp); + memmove(h, ph, TCP4_TCBPHDRSZ); + + /* copy in variable bits */ + hnputs(h->tcplen, hdrlen + dlen); + hnputl(h->tcpseq, tcph->seq); + hnputl(h->tcpack, tcph->ack); + hnputs(h->tcpflag, (hdrlen<<10) | tcph->flags); + hnputs(h->tcpwin, tcph->wnd); + hnputs(h->tcpurg, tcph->urg); + + if(tcph->mss != 0){ + h->tcpopt[0] = MSSOPT; + h->tcpopt[1] = MSS_LENGTH; + hnputs(h->tcpmss, tcph->mss); + } + if(tcb != nil && tcb->nochecksum){ + h->tcpcksum[0] = h->tcpcksum[1] = 0; + } else { + csum = ptclcsum(data, TCP4_IPLEN, hdrlen+dlen+TCP4_PHDRSIZE); + hnputs(h->tcpcksum, csum); + } return data; } int -ntohtcp(Tcp *tcph, Block **bpp) +ntohtcp6(Tcp *tcph, Block **bpp) { - Tcphdr *h; + Tcp6hdr *h; uchar *optr; ushort hdrlen; ushort optlen; int n; - *bpp = pullupblock(*bpp, TCP_PKT+TCP_HDRSIZE); + *bpp = pullupblock(*bpp, TCP6_PKT+TCP6_HDRSIZE); if(*bpp == nil) return -1; - h = (Tcphdr *)((*bpp)->rp); + h = (Tcp6hdr *)((*bpp)->rp); tcph->source = nhgets(h->tcpsport); tcph->dest = nhgets(h->tcpdport); tcph->seq = nhgetl(h->tcpseq); tcph->ack = nhgetl(h->tcpack); - hdrlen = (h->tcpflag[0] & 0xf0)>>2; - if(hdrlen < TCP_HDRSIZE) { + if(hdrlen < TCP6_HDRSIZE) { freeblist(*bpp); return -1; } @@ -849,19 +959,72 @@ ntohtcp(Tcp *tcph, Block **bpp) tcph->wnd = nhgets(h->tcpwin); tcph->urg = nhgets(h->tcpurg); tcph->mss = 0; - tcph->len = nhgets(h->length) - (hdrlen + TCP_PKT); + tcph->len = nhgets(h->ploadlen) - hdrlen; - *bpp = pullupblock(*bpp, hdrlen+TCP_PKT); + *bpp = pullupblock(*bpp, hdrlen+TCP6_PKT); if(*bpp == nil) return -1; -/* netlog(Logtcpmsg, "%d > %d s %l8.8ux a %8.8lux %s w %.4ux l %d\n", - tcph->source, tcph->dest, - tcph->seq, tcph->ack, tcpflag((hdrlen<<10)|tcph->flags), - tcph->wnd, nhgets(h->length)-hdrlen-TCP_PKT); */ + optr = h->tcpopt; + n = hdrlen - TCP6_HDRSIZE; + while(n > 0 && *optr != EOLOPT) { + if(*optr == NOOPOPT) { + n--; + optr++; + continue; + } + optlen = optr[1]; + if(optlen < 2 || optlen > n) + break; + switch(*optr) { + case MSSOPT: + if(optlen == MSS_LENGTH) + tcph->mss = nhgets(optr+2); + break; + } + n -= optlen; + optr += optlen; + } + return hdrlen; +} + +int +ntohtcp4(Tcp *tcph, Block **bpp) +{ + Tcp4hdr *h; + uchar *optr; + ushort hdrlen; + ushort optlen; + int n; + + *bpp = pullupblock(*bpp, TCP4_PKT+TCP4_HDRSIZE); + if(*bpp == nil) + return -1; + + h = (Tcp4hdr *)((*bpp)->rp); + tcph->source = nhgets(h->tcpsport); + tcph->dest = nhgets(h->tcpdport); + tcph->seq = nhgetl(h->tcpseq); + tcph->ack = nhgetl(h->tcpack); + + hdrlen = (h->tcpflag[0] & 0xf0)>>2; + if(hdrlen < TCP4_HDRSIZE) { + freeblist(*bpp); + return -1; + } + + tcph->flags = h->tcpflag[1]; + tcph->wnd = nhgets(h->tcpwin); + tcph->urg = nhgets(h->tcpurg); + tcph->mss = 0; + tcph->len = nhgets(h->length) - (hdrlen + TCP4_PKT); + + *bpp = pullupblock(*bpp, hdrlen+TCP4_PKT); + if(*bpp == nil) + return -1; optr = h->tcpopt; - n = hdrlen - TCP_HDRSIZE; + n = hdrlen - TCP4_HDRSIZE; while(n > 0 && *optr != EOLOPT) { if(*optr == NOOPOPT) { n--; @@ -871,7 +1034,6 @@ ntohtcp(Tcp *tcph, Block **bpp) optlen = optr[1]; if(optlen < 2 || optlen > n) break; -if(0) print("tcpopt %d %d\n", *optr, optlen); switch(*optr) { case MSSOPT: if(optlen == MSS_LENGTH) @@ -899,17 +1061,14 @@ tcpsndsyn(Tcpctl *tcb) tcb->sndsyntime = msec; } - -/* - * called with v4 (4 byte) addresses - */ void -sndrst(Proto *tcp, uchar *source, uchar *dest, ushort length, Tcp *seg) +sndrst(Proto *tcp, uchar *source, uchar *dest, ushort length, Tcp *seg, int version) { - Tcphdr ph; Block *hbp; uchar rflags; Tcppriv *tpriv; + Tcp4hdr ph4; + Tcp6hdr ph6; tpriv = tcp->priv; @@ -917,13 +1076,26 @@ sndrst(Proto *tcp, uchar *source, uchar *dest, ushort length, Tcp *seg) return; /* make pseudo header */ - memset(&ph, 0, sizeof(ph)); - v6tov4(ph.tcpsrc, dest); - v6tov4(ph.tcpdst, source); - ph.proto = IP_TCPPROTO; - hnputs(ph.tcplen, TCP_HDRSIZE); - hnputs(ph.tcpsport, seg->dest); - hnputs(ph.tcpdport, seg->source); + if(version == 4) { + memset(&ph4, 0, sizeof(ph4)); + ph4.vihl = IP_VER4; + v6tov4(ph4.tcpsrc, dest); + v6tov4(ph4.tcpdst, source); + ph4.proto = IP_TCPPROTO; + hnputs(ph4.tcplen, TCP4_HDRSIZE); + hnputs(ph4.tcpsport, seg->dest); + hnputs(ph4.tcpdport, seg->source); + } + else { + memset(&ph6, 0, sizeof(ph6)); + ph6.vcf[0] = IP_VER6; + ipmove(ph6.tcpsrc, dest); + ipmove(ph6.tcpdst, source); + ph6.proto = IP_TCPPROTO; + hnputs(ph6.ploadlen, TCP6_HDRSIZE); + hnputs(ph6.tcpsport, seg->dest); + hnputs(ph6.tcpdport, seg->source); + } tpriv->stats[OutRsts]++; rflags = RST; @@ -947,11 +1119,18 @@ sndrst(Proto *tcp, uchar *source, uchar *dest, ushort length, Tcp *seg) seg->wnd = 0; seg->urg = 0; seg->mss = 0; - hbp = htontcp(seg, nil, &ph, nil); - if(hbp == nil) - return; - - ipoput(tcp->f, hbp, 0, MAXTTL, DFLTTOS); + if(version == 4) { + hbp = htontcp4(seg, nil, &ph4, nil); + if(hbp == nil) + return; + ipoput4(tcp->f, hbp, 0, MAXTTL, DFLTTOS); + } + else { + hbp = htontcp6(seg, nil, &ph6, nil); + if(hbp == nil) + return; + ipoput6(tcp->f, hbp, 0, MAXTTL, DFLTTOS); + } } /* @@ -963,13 +1142,13 @@ tcphangup(Conv *s) { Tcp seg; Tcpctl *tcb; - Tcphdr ph; Block *hbp; tcb = (Tcpctl*)s->ptcl; if(waserror()) return commonerror(); if(s->raddr != 0) { + int version = isv4(s->raddr) ? 4 : 6; seg.flags = RST | ACK; seg.ack = tcb->rcv.nxt; seg.seq = tcb->snd.ptr; @@ -977,9 +1156,16 @@ tcphangup(Conv *s) seg.urg = 0; seg.mss = 0; tcb->last_ack = tcb->rcv.nxt; - hnputs(ph.tcplen, TCP_HDRSIZE); - hbp = htontcp(&seg, nil, &tcb->protohdr, tcb); - ipoput(s->p->f, hbp, 0, s->ttl, s->tos); + if(version == 4) { + tcb->protohdr.tcp4hdr.vihl = IP_VER4; + hbp = htontcp4(&seg, nil, &tcb->protohdr.tcp4hdr, tcb); + ipoput4(s->p->f, hbp, 0, s->ttl, s->tos); + } + else { + tcb->protohdr.tcp6hdr.vcf[0] = IP_VER6; + hbp = htontcp6(&seg, nil, &tcb->protohdr.tcp6hdr, tcb); + ipoput6(s->p->f, hbp, 0, s->ttl, s->tos); + } } localclose(s, nil); poperror(); @@ -991,7 +1177,6 @@ tcpincoming(Conv *s, Tcp *segp, uchar *src, uchar *dst) { Conv *new; Tcpctl *tcb; - Tcphdr *h; Tcppriv *tpriv; new = Fsnewcall(s, src, segp->source, dst, segp->dest); @@ -1010,13 +1195,24 @@ tcpincoming(Conv *s, Tcp *segp, uchar *src, uchar *dst) tcb->rtt_timer.arg = new; tcb->rtt_timer.state = TcptimerOFF; - h = &tcb->protohdr; - memset(h, 0, sizeof(*h)); - h->proto = IP_TCPPROTO; - hnputs(h->tcpsport, new->lport); - hnputs(h->tcpdport, new->rport); - v6tov4(h->tcpsrc, dst); - v6tov4(h->tcpdst, src); + if(isv4(src)) { + Tcp4hdr *h = &tcb->protohdr.tcp4hdr; + memset(h, 0, sizeof(*h)); + h->proto = IP_TCPPROTO; + hnputs(h->tcpsport, new->lport); + hnputs(h->tcpdport, new->rport); + v6tov4(h->tcpsrc, dst); + v6tov4(h->tcpdst, src); + } + else { + Tcp6hdr *h = &tcb->protohdr.tcp6hdr; + memset(h, 0, sizeof(*h)); + h->proto = IP_TCPPROTO; + hnputs(h->tcpsport, new->lport); + hnputs(h->tcpdport, new->rport); + ipmove(h->tcpsrc, dst); + ipmove(h->tcpdst, src); + } tpriv = new->p->priv; iphtadd(&tpriv->ht, new); @@ -1195,7 +1391,7 @@ update(Conv *s, Tcp *seg) if(rtt == 0) rtt = 1; /* otherwise all close systems will rexmit in 0 time */ rtt *= MSPTICK; - if (tcb->srtt == 0) { + if(tcb->srtt == 0) { tcb->srtt = rtt << LOGAGAIN; tcb->mdev = rtt << LOGDGAIN; } else { @@ -1237,7 +1433,8 @@ void tcpiput(Proto *tcp, Ipifc*, Block *bp) { Tcp seg; - Tcphdr *h; + Tcp4hdr *h4; + Tcp6hdr *h6; int hdrlen; Tcpctl *tcb; ushort length; @@ -1245,45 +1442,92 @@ tcpiput(Proto *tcp, Ipifc*, Block *bp) Conv *s; Fs *f; Tcppriv *tpriv; + int version; f = tcp->f; tpriv = tcp->priv; tpriv->stats[InSegs]++; - h = (Tcphdr*)(bp->rp); - - length = nhgets(h->length); - v4tov6(dest, h->tcpdst); - v4tov6(source, h->tcpsrc); + h4 = (Tcp4hdr*)(bp->rp); + h6 = (Tcp6hdr*)(bp->rp); + + if((h4->vihl&0xF0)==IP_VER4) { + version = 4; + length = nhgets(h4->length); + v4tov6(dest, h4->tcpdst); + v4tov6(source, h4->tcpsrc); + + h4->Unused = 0; + hnputs(h4->tcplen, length-TCP4_PKT); + if((h4->tcpcksum[0] || h4->tcpcksum[1]) && + ptclcsum(bp, TCP4_IPLEN, length-TCP4_IPLEN)) { + tpriv->stats[CsumErrs]++; + tpriv->stats[InErrs]++; + netlog(f, Logtcp, "bad tcp proto cksum\n"); + freeblist(bp); + return; + } - h->Unused = 0; - hnputs(h->tcplen, length-TCP_PKT); - if((h->tcpcksum[0] || h->tcpcksum[1]) && - ptclcsum(bp, TCP_IPLEN, length-TCP_IPLEN)) { - tpriv->stats[CsumErrs]++; - tpriv->stats[InErrs]++; - netlog(f, Logtcp, "bad tcp proto cksum\n"); - freeblist(bp); - return; - } + hdrlen = ntohtcp4(&seg, &bp); + if(hdrlen < 0){ + tpriv->stats[HlenErrs]++; + tpriv->stats[InErrs]++; + netlog(f, Logtcp, "bad tcp hdr len\n"); + return; + } - hdrlen = ntohtcp(&seg, &bp); - if(hdrlen < 0){ - tpriv->stats[HlenErrs]++; - tpriv->stats[InErrs]++; - 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; + } } + else { + int ttl = h6->ttl; + int proto = h6->proto; + + version = 6; + length = nhgets(h6->ploadlen); + ipmove(dest, h6->tcpdst); + ipmove(source, h6->tcpsrc); + + h6->ploadlen[0] = h6->ploadlen[1] = h6->proto = 0; + h6->ttl = proto; + hnputl(h6->vcf, length); + if((h6->tcpcksum[0] || h6->tcpcksum[1]) && + ptclcsum(bp, TCP6_IPLEN, length+TCP6_PHDRSIZE)) { + tpriv->stats[CsumErrs]++; + tpriv->stats[InErrs]++; + netlog(f, Logtcp, "bad tcp proto cksum\n"); + freeblist(bp); + return; + } + h6->ttl = ttl; + h6->proto = proto; + hnputs(h6->ploadlen, length); + + hdrlen = ntohtcp6(&seg, &bp); + if(hdrlen < 0){ + tpriv->stats[HlenErrs]++; + tpriv->stats[InErrs]++; + netlog(f, Logtcp, "bad tcp hdr len\n"); + return; + } - /* trim the packet to the size claimed by the datagram */ - length -= hdrlen+TCP_PKT; - bp = trimblock(bp, hdrlen+TCP_PKT, length); - if(bp == nil){ - tpriv->stats[LenErrs]++; - tpriv->stats[InErrs]++; - netlog(f, Logtcp, "tcp len < 0 after trim\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, "tcp len < 0 after trim\n"); + return; + } } /* lock protocol while searching for a conversation */ @@ -1294,7 +1538,7 @@ tcpiput(Proto *tcp, Ipifc*, Block *bp) if(s == nil){ reset: qunlock(tcp); - sndrst(tcp, source, dest, length, &seg); + sndrst(tcp, source, dest, length, &seg, version); freeblist(bp); return; } @@ -1334,7 +1578,7 @@ reset: switch(tcb->state) { case Closed: - sndrst(tcp, source, dest, length, &seg); + sndrst(tcp, source, dest, length, &seg, version); goto raise; case Listen: if(seg.flags & SYN) { @@ -1349,7 +1593,7 @@ reset: case Syn_sent: if(seg.flags & ACK) { if(!seq_within(seg.ack, tcb->iss+1, tcb->snd.nxt)) { - sndrst(tcp, source, dest, length, &seg); + sndrst(tcp, source, dest, length, &seg, version); goto raise; } } @@ -1413,7 +1657,7 @@ reset: /* Cannot accept so answer with a rst */ if(length && tcb->state == Closed) { - sndrst(tcp, source, dest, length, &seg); + sndrst(tcp, source, dest, length, &seg, version); goto raise; } @@ -1448,7 +1692,7 @@ reset: switch(tcb->state) { case Syn_received: if(!seq_within(seg.ack, tcb->snd.una+1, tcb->snd.nxt)){ - sndrst(tcp, source, dest, length, &seg); + sndrst(tcp, source, dest, length, &seg, version); goto raise; } update(s, &seg); @@ -1556,7 +1800,7 @@ reset: /* no process to read the data, send a reset */ if(bp != nil) freeblist(bp); - sndrst(tcp, source, dest, length, &seg); + sndrst(tcp, source, dest, length, &seg, version); qunlock(s); poperror(); return; @@ -1649,6 +1893,15 @@ tcpoutput(Conv *s) ulong ssize, dsize, usable, sent; Fs *f; Tcppriv *tpriv; + //int version = isv4(s->raddr) ? 4 : 6; + int version; + + if( (memcmp(s->raddr, v4prefix, IPv4off) == 0 && + memcmp(s->laddr, v4prefix, IPv4off) == 0) + || ipcmp(s->raddr, IPnoaddr) == 0) + version = 4; + else + version = 6; f = s->p->f; tpriv = s->p->priv; @@ -1681,7 +1934,7 @@ tcpoutput(Conv *s) */ if(tcb->snd.wnd == 0){ if(sent != 0) { - if ((tcb->flags&FORCE) == 0) + if((tcb->flags&FORCE) == 0) break; tcb->snd.ptr = tcb->snd.una; } @@ -1755,8 +2008,6 @@ tcpoutput(Conv *s) seg.flags |= FIN; dsize--; } -/* netlog(f, Logtcp, "qcopy: dlen %d blen %d sndcnt %d qlen %d sent %d rp[0] %d\n", - dsize, BLEN(bp), sndcnt, qlen(s->wq), sent, bp->rp[0]); */ } if(sent+dsize == sndcnt) @@ -1782,10 +2033,21 @@ tcpoutput(Conv *s) tcb->snd.nxt = tcb->snd.ptr; /* Build header, link data and compute cksum */ - hbp = htontcp(&seg, bp, &tcb->protohdr, tcb); - if(hbp == nil) { - freeblist(bp); - return; + if(version == 4) { + tcb->protohdr.tcp4hdr.vihl = IP_VER4; + hbp = htontcp4(&seg, bp, &tcb->protohdr.tcp4hdr, tcb); + if(hbp == nil) { + freeblist(bp); + return; + } + } + else { + tcb->protohdr.tcp6hdr.vcf[0] = IP_VER6; + hbp = htontcp6(&seg, bp, &tcb->protohdr.tcp6hdr, tcb); + if(hbp == nil) { + freeblist(bp); + return; + } } /* Start the transmission timers if there is new data and we @@ -1814,7 +2076,10 @@ tcpoutput(Conv *s) qlock(s); nexterror(); } - ipoput(f, hbp, 0, s->ttl, s->tos); + if(version == 4) + ipoput4(f, hbp, 0, s->ttl, s->tos); + else + ipoput6(f, hbp, 0, s->ttl, s->tos); qlock(s); poperror(); } @@ -1832,7 +2097,6 @@ tcpsendka(Conv *s) tcb = (Tcpctl*)s->ptcl; - dbp = nil; seg.urg = 0; seg.source = s->lport; @@ -1850,14 +2114,26 @@ tcpsendka(Conv *s) dbp->wp++; } - /* Build header, link data and compute cksum */ - hbp = htontcp(&seg, dbp, &tcb->protohdr, tcb); - if(hbp == nil) { - freeblist(dbp); - return; + if(isv4(s->raddr)) { + /* Build header, link data and compute cksum */ + tcb->protohdr.tcp4hdr.vihl = IP_VER4; + hbp = htontcp4(&seg, dbp, &tcb->protohdr.tcp4hdr, tcb); + if(hbp == nil) { + freeblist(dbp); + return; + } + ipoput4(s->p->f, hbp, 0, s->ttl, s->tos); + } + else { + /* Build header, link data and compute cksum */ + tcb->protohdr.tcp6hdr.vcf[0] = IP_VER6; + hbp = htontcp6(&seg, dbp, &tcb->protohdr.tcp6hdr, tcb); + if(hbp == nil) { + freeblist(dbp); + return; + } + ipoput6(s->p->f, hbp, 0, s->ttl, s->tos); } - - ipoput(s->p->f, hbp, 0, s->ttl, s->tos); } /* @@ -2133,7 +2409,7 @@ tcptrim(Tcpctl *tcb, Tcp *seg, Block **bp, ushort *length) seg->flags &= ~SYN; seg->seq++; - if (seg->urg > 1) + if(seg->urg > 1) seg->urg--; else seg->flags &= ~URG; @@ -2144,7 +2420,7 @@ tcptrim(Tcpctl *tcb, Tcp *seg, Block **bp, ushort *length) seg->seq += dupcnt; *length -= dupcnt; - if (seg->urg > dupcnt) + if(seg->urg > dupcnt) seg->urg -= dupcnt; else { seg->flags &= ~URG; @@ -2167,61 +2443,50 @@ tcptrim(Tcpctl *tcb, Tcp *seg, Block **bp, ushort *length) void tcpadvise(Proto *tcp, Block *bp, char *msg) { - Tcphdr *h; + Tcp4hdr *h4; + Tcp6hdr *h6; Tcpctl *tcb; uchar source[IPaddrlen]; uchar dest[IPaddrlen]; ushort psource, pdest; Conv *s, **p; - h = (Tcphdr*)(bp->rp); + h4 = (Tcp4hdr*)(bp->rp); + h6 = (Tcp6hdr*)(bp->rp); - v4tov6(dest, h->tcpdst); - v4tov6(source, h->tcpsrc); - psource = nhgets(h->tcpsport); - pdest = nhgets(h->tcpdport); + if((h4->vihl&0xF0)==IP_VER4) { + v4tov6(dest, h4->tcpdst); + v4tov6(source, h4->tcpsrc); + psource = nhgets(h4->tcpsport); + pdest = nhgets(h4->tcpdport); + } + else { + ipmove(dest, h6->tcpdst); + ipmove(source, h6->tcpsrc); + psource = nhgets(h6->tcpsport); + pdest = nhgets(h6->tcpdport); + } /* Look for a connection */ qlock(tcp); - if(strcmp(msg, "unfragmentable") == 0){ - for(p = tcp->conv; *p; p++) { - s = *p; - tcb = (Tcpctl*)s->ptcl; - if(tcb->state != Closed) - if(ipcmp(s->raddr, dest) == 0) - if(ipcmp(s->laddr, source) == 0){ - qlock(s); - qunlock(tcp); - switch(tcb->state){ - case Syn_sent: - localclose(s, msg); - break; - } - qunlock(s); - freeblist(bp); - return; - } - } - } else { - for(p = tcp->conv; *p; p++) { - s = *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){ - qlock(s); - qunlock(tcp); - switch(tcb->state){ - case Syn_sent: - localclose(s, msg); - break; - } - qunlock(s); - freeblist(bp); - return; + for(p = tcp->conv; *p; p++) { + s = *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){ + qlock(s); + qunlock(tcp); + switch(tcb->state){ + case Syn_sent: + localclose(s, msg); + break; } + qunlock(s); + freeblist(bp); + return; } } qunlock(tcp); diff --git a/ip/tripmedium.c b/ip/tripmedium.c index eb2baa1c065a78312dbe3605b08b5b1bbcbc88cf..aece203046932629533c20f63ffccebf60e978bc 100644 --- a/ip/tripmedium.c +++ b/ip/tripmedium.c @@ -142,7 +142,7 @@ tripread(void *a) for(;;) { bp = devtab[er->mchan->type]->bread(er->mchan, ifc->maxmtu, 0); ifc->in++; - ipiput(er->fs, ifc, bp); + ipiput4(er->fs, ifc, bp); } pexit("hangup", 1); diff --git a/ip/udp.c b/ip/udp.c index 40d38df92413e6132788b33a874bdfe33babd8e2..a13e98790c7730eda15e849f25d7076c4b346f04 100644 --- a/ip/udp.c +++ b/ip/udp.c @@ -6,14 +6,22 @@ #include "../port/error.h" #include "ip.h" +#include "ipv6.h" + #define DPRINT if(0)print enum { - UDP_PHDRSIZE = 12, - UDP_HDRSIZE = 20, - UDP_IPHDR = 8, + UDP_UDPHDR_SZ = 8, + + UDP4_PHDR_OFF = 8, + UDP4_PHDR_SZ = 12, + UDP4_IPHDR_SZ = 20, + UDP6_IPHDR_SZ = 40, + UDP6_PHDR_SZ = 40, + UDP6_PHDR_OFF = 0, + IP_UDPPROTO = 17, UDP_USEAD6 = 36, UDP_USEAD4 = 12, @@ -23,8 +31,8 @@ enum Udpmaxxmit = 10, }; -typedef struct Udphdr Udphdr; -struct Udphdr +typedef struct Udp4hdr Udp4hdr; +struct Udp4hdr { /* ip header */ uchar vihl; /* Version and header length */ @@ -35,8 +43,24 @@ struct Udphdr uchar Unused; uchar udpproto; /* Protocol */ uchar udpplen[2]; /* Header plus data length */ - uchar udpsrc[4]; /* Ip source */ - uchar udpdst[4]; /* Ip destination */ + uchar udpsrc[IPv4addrlen]; /* Ip source */ + uchar udpdst[IPv4addrlen]; /* Ip destination */ + + /* udp header */ + uchar udpsport[2]; /* Source port */ + uchar udpdport[2]; /* Destination port */ + uchar udplen[2]; /* data length */ + uchar udpcksum[2]; /* Checksum */ +}; + +typedef struct Udp6hdr Udp6hdr; +struct Udp6hdr { + uchar viclfl[4]; + uchar len[2]; + uchar nextheader; + uchar hoplimit; + uchar udpsrc[IPaddrlen]; + uchar udpdst[IPaddrlen]; /* udp header */ uchar udpsport[2]; /* Source port */ @@ -68,6 +92,8 @@ struct Udppriv ulong lenerr; /* short packet */ }; +void (*etherprofiler)(char *name, int qlen); + /* * protocol specific part of Conv */ @@ -142,12 +168,15 @@ udpclose(Conv *c) ucb = (Udpcb*)c->ptcl; ucb->headers = 0; + + qunlock(c); } void udpkick(Conv *c) { - Udphdr *uh; + Udp4hdr *uh4; + Udp6hdr *uh6; ushort rport; uchar laddr[IPaddrlen], raddr[IPaddrlen]; Block *bp; @@ -155,6 +184,7 @@ udpkick(Conv *c) int dlen, ptcllen; Udppriv *upriv; Fs *f; + int version; upriv = c->p->priv; f = c->p->f; @@ -179,7 +209,7 @@ udpkick(Conv *c) if(ipforme(f, laddr) != Runi) findlocalip(f, laddr, raddr); rport = nhgets(bp->rp); - bp->rp += 2+2; /* Igonore local port */ + bp->rp += 2+2; /* Ignore local port */ break; case 4: bp = pullupblock(bp, UDP_USEAD4); @@ -199,45 +229,97 @@ udpkick(Conv *c) break; } - dlen = blocklen(bp); + if(ucb->headers == 6) { + if(memcmp(laddr, v4prefix, IPv4off) == 0 || + ipcmp(laddr, IPnoaddr) == 0) + version = 4; + else + version = 6; + } + else if(ucb->headers == 4) + version = 4; + else { + if( (memcmp(c->raddr, v4prefix, IPv4off) == 0 && + memcmp(c->laddr, v4prefix, IPv4off) == 0) + || ipcmp(c->raddr, IPnoaddr) == 0) + version = 4; + else + version = 6; + } + dlen = blocklen(bp); /* Make space to fit udp & ip header */ - bp = padblock(bp, UDP_IPHDR+UDP_HDRSIZE); + if(version == 4) + bp = padblock(bp, UDP4_IPHDR_SZ+UDP_UDPHDR_SZ); + else + bp = padblock(bp, UDP6_IPHDR_SZ+UDP_UDPHDR_SZ); + if(bp == nil) return; - uh = (Udphdr *)(bp->rp); - - ptcllen = dlen + (UDP_HDRSIZE-UDP_PHDRSIZE); - uh->Unused = 0; - uh->udpproto = IP_UDPPROTO; - uh->frag[0] = 0; - uh->frag[1] = 0; - hnputs(uh->udpplen, ptcllen); - switch(ucb->headers){ - case 4: - case 6: - v6tov4(uh->udpdst, raddr); - hnputs(uh->udpdport, rport); - v6tov4(uh->udpsrc, laddr); - break; - default: - v6tov4(uh->udpdst, c->raddr); - hnputs(uh->udpdport, c->rport); - if(ipcmp(c->laddr, IPnoaddr) == 0) - findlocalip(f, c->laddr, c->raddr); - v6tov4(uh->udpsrc, c->laddr); - break; + /* fill in pseudo header and compute checksum */ + if(version == 4) + { + uh4 = (Udp4hdr *)(bp->rp); + ptcllen = dlen + UDP_UDPHDR_SZ; + uh4->Unused = 0; + uh4->udpproto = IP_UDPPROTO; + uh4->frag[0] = 0; + uh4->frag[1] = 0; + hnputs(uh4->udpplen, ptcllen); + if(ucb->headers == 4 || ucb->headers == 6) { + v6tov4(uh4->udpdst, raddr); + hnputs(uh4->udpdport, rport); + v6tov4(uh4->udpsrc, laddr); + } else { + v6tov4(uh4->udpdst, c->raddr); + hnputs(uh4->udpdport, c->rport); + if(ipcmp(c->laddr, IPnoaddr) == 0) + findlocalip(f, c->laddr, c->raddr); + v6tov4(uh4->udpsrc, c->laddr); + } + hnputs(uh4->udpsport, c->lport); + hnputs(uh4->udplen, ptcllen); + uh4->udpcksum[0] = 0; + uh4->udpcksum[1] = 0; + hnputs(uh4->udpcksum, + ptclcsum(bp, UDP4_PHDR_OFF, dlen+UDP_UDPHDR_SZ+UDP4_PHDR_SZ)); + uh4->vihl = IP_VER4; + ipoput4(f, bp, 0, c->ttl, c->tos); + } + else { + // using the v6 ip header to create pseudo header + // first then reset it to the normal ip header + uh6 = (Udp6hdr *)(bp->rp); + memset(uh6, 0, 8); + ptcllen = dlen + UDP_UDPHDR_SZ; + hnputl(uh6->viclfl, ptcllen); + uh6->hoplimit = IP_UDPPROTO; + if(ucb->headers == 6) { + ipmove(uh6->udpdst, raddr); + hnputs(uh6->udpdport, rport); + ipmove(uh6->udpsrc, laddr); + } else { + ipmove(uh6->udpdst, c->raddr); + hnputs(uh6->udpdport, c->rport); + if(ipcmp(c->laddr, IPnoaddr) == 0) + findlocalip(f, c->laddr, c->raddr); + ipmove(uh6->udpsrc, c->laddr); + } + hnputs(uh6->udpsport, c->lport); + hnputs(uh6->udplen, ptcllen); + uh6->udpcksum[0] = 0; + uh6->udpcksum[1] = 0; + hnputs(uh6->udpcksum, + ptclcsum(bp, UDP6_PHDR_OFF, dlen+UDP_UDPHDR_SZ+UDP6_PHDR_SZ)); + memset(uh6, 0, 8); + uh6->viclfl[0] = IP_VER6; + hnputs(uh6->len, ptcllen); + uh6->nextheader = IP_UDPPROTO; + + ipoput6(f, bp, 0, c->ttl, c->tos); } - hnputs(uh->udpsport, c->lport); - hnputs(uh->udplen, ptcllen); - uh->udpcksum[0] = 0; - uh->udpcksum[1] = 0; - - hnputs(uh->udpcksum, ptclcsum(bp, UDP_IPHDR, dlen+UDP_HDRSIZE)); - upriv->ustats.udpOutDatagrams++; - ipoput(f, bp, 0, c->ttl, c->tos); } Conv* @@ -253,42 +335,76 @@ udpincoming(Conv *c, uchar *raddr, ushort rport, uchar *laddr, ushort lport) void udpiput(Proto *udp, Ipifc *ifc, Block *bp) { - int len, olen, ottl; - Udphdr *uh; + int len; + Udp4hdr *uh4; + Udp6hdr *uh6; Conv *c; Udpcb *ucb; uchar raddr[IPaddrlen], laddr[IPaddrlen]; ushort rport, lport; Udppriv *upriv; Fs *f; + int version; upriv = udp->priv; f = udp->f; - upriv->ustats.udpInDatagrams++; - uh = (Udphdr*)(bp->rp); - - /* Put back pseudo header for checksum (remember old values for icmpnoconv()) */ - ottl = uh->Unused; - uh->Unused = 0; - len = nhgets(uh->udplen); - olen = nhgets(uh->udpplen); - hnputs(uh->udpplen, len); - - v4tov6(raddr, uh->udpsrc); - v4tov6(laddr, uh->udpdst); - lport = nhgets(uh->udpdport); - rport = nhgets(uh->udpsport); - - if(nhgets(uh->udpcksum)) { - if(ptclcsum(bp, UDP_IPHDR, len+UDP_PHDRSIZE)) { + uh4 = (Udp4hdr*)(bp->rp); + version = ((uh4->vihl&0xF0)==IP_VER6) ? 6 : 4; + + /* Put back pseudo header for checksum + * (remember old values for icmpnoconv()) */ + if(version == 4) { + int ottl, olen; + ottl = uh4->Unused; + uh4->Unused = 0; + len = nhgets(uh4->udplen); + olen = nhgets(uh4->udpplen); + hnputs(uh4->udpplen, len); + + v4tov6(raddr, uh4->udpsrc); + v4tov6(laddr, uh4->udpdst); + lport = nhgets(uh4->udpdport); + rport = nhgets(uh4->udpsport); + + if(nhgets(uh4->udpcksum)) { + if(ptclcsum(bp, UDP4_PHDR_OFF, len+UDP4_PHDR_SZ)) { + upriv->ustats.udpInErrors++; + netlog(f, Logudp, "udp: checksum error %I\n", raddr); + DPRINT("udp: checksum error %I\n", raddr); + freeblist(bp); + return; + } + } + uh4->Unused = ottl; + hnputs(uh4->udpplen, olen); + } + else { + int ottl, oviclfl, olen; + uh6 = (Udp6hdr*)(bp->rp); + len = nhgets(uh6->udplen); + oviclfl = nhgetl(uh6->viclfl); + olen = nhgets(uh6->len); + ottl = uh6->hoplimit; + ipmove(raddr, uh6->udpsrc); + ipmove(laddr, uh6->udpdst); + lport = nhgets(uh6->udpdport); + rport = nhgets(uh6->udpsport); + memset(uh6, 0, 8); + hnputl(uh6->viclfl, len); + uh6->hoplimit = IP_UDPPROTO; + if(ptclcsum(bp, UDP6_PHDR_OFF, len+UDP6_PHDR_SZ)) { upriv->ustats.udpInErrors++; netlog(f, Logudp, "udp: checksum error %I\n", raddr); DPRINT("udp: checksum error %I\n", raddr); freeblist(bp); return; } + hnputl(uh6->viclfl, oviclfl); + hnputs(uh6->len, olen); + uh6->nextheader = IP_UDPPROTO; + uh6->hoplimit = ottl; } qlock(udp); @@ -299,10 +415,15 @@ udpiput(Proto *udp, Ipifc *ifc, Block *bp) upriv->ustats.udpNoPorts++; qunlock(udp); netlog(f, Logudp, "udp: no conv %I!%d -> %I!%d\n", raddr, rport, - laddr, lport); - uh->Unused = ottl; - hnputs(uh->udpplen, olen); - icmpnoconv(f, bp); + laddr, lport); + + if(version == 4) + icmpnoconv(f, bp); + else { +print("udpiput: no conv %I!%d -> %I!%d\n", raddr, rport, laddr, lport); + icmphostunr(f, ifc, bp, icmp6_port_unreach, 0); + } + freeblist(bp); return; } @@ -311,8 +432,12 @@ udpiput(Proto *udp, Ipifc *ifc, Block *bp) if(c->state == Announced){ if(ucb->headers == 0){ /* create a new conversation */ - if(ipforme(f, laddr) != Runi) - v4tov6(laddr, ifc->lifc->local); + if(ipforme(f, laddr) != Runi) { + if(version == 4) + v4tov6(laddr, ifc->lifc->local); + else + ipmove(laddr, ifc->lifc->local); + } c = Fsnewcall(c, raddr, rport, laddr, lport); if(c == nil){ qunlock(udp); @@ -330,18 +455,21 @@ udpiput(Proto *udp, Ipifc *ifc, Block *bp) /* * Trim the packet down to data size */ - len -= (UDP_HDRSIZE-UDP_PHDRSIZE); - bp = trimblock(bp, UDP_IPHDR+UDP_HDRSIZE, len); + len -= UDP_UDPHDR_SZ; + if(version == 4) + bp = trimblock(bp, UDP4_IPHDR_SZ+UDP_UDPHDR_SZ, len); + else + bp = trimblock(bp, UDP6_IPHDR_SZ+UDP_UDPHDR_SZ, len); if(bp == nil){ qunlock(c); netlog(f, Logudp, "udp: len err %I.%d -> %I.%d\n", raddr, rport, - laddr, lport); + laddr, lport); upriv->lenerr++; return; } netlog(f, Logudpmsg, "udp: %I.%d -> %I.%d l %d\n", raddr, rport, - laddr, lport, len); + laddr, lport, len); switch(ucb->headers){ case 6: @@ -374,7 +502,7 @@ udpiput(Proto *udp, Ipifc *ifc, Block *bp) if(qfull(c->rq)){ qunlock(c); netlog(f, Logudp, "udp: qfull %I.%d -> %I.%d\n", raddr, rport, - laddr, lport); + laddr, lport); freeblist(bp); return; } @@ -405,17 +533,30 @@ udpctl(Conv *c, char **f, int n) void udpadvise(Proto *udp, Block *bp, char *msg) { - Udphdr *h; + Udp4hdr *h4; + Udp6hdr *h6; uchar source[IPaddrlen], dest[IPaddrlen]; ushort psource, pdest; Conv *s, **p; + int version; + + h4 = (Udp4hdr*)(bp->rp); + version = ((h4->vihl&0xF0)==IP_VER6) ? 6 : 4; + + if(version == 4) { + v4tov6(dest, h4->udpdst); + v4tov6(source, h4->udpsrc); + psource = nhgets(h4->udpsport); + pdest = nhgets(h4->udpdport); + } else { + h6 = (Udp6hdr*)(bp->rp); + ipmove(dest, h6->udpdst); + ipmove(source, h6->udpsrc); + psource = nhgets(h6->udpsport); + pdest = nhgets(h6->udpdport); + } - h = (Udphdr*)(bp->rp); - - v4tov6(dest, h->udpdst); - v4tov6(source, h->udpsrc); - psource = nhgets(h->udpsport); - pdest = nhgets(h->udpdport); +print("udpadvise: looking for s %I sp %d d %I dp %d\n", source, (int)psource, dest, (int)pdest); /* Look for a connection */ qlock(udp); @@ -427,6 +568,7 @@ udpadvise(Proto *udp, Block *bp, char *msg) if(ipcmp(s->laddr, source) == 0){ qlock(s); qunlock(udp); +print("udpadvise: found, hanging up\n"); qhangup(s->rq, msg); qhangup(s->wq, msg); qunlock(s); diff --git a/port/devnmouse.c b/port/devnmouse.c index 25b9e21885fed2430d2b0c8e40361322541365a3..ff1bf07c04861f23a7e10f247bfa4142e4b62394 100644 --- a/port/devnmouse.c +++ b/port/devnmouse.c @@ -32,8 +32,6 @@ enum{ Qdir, Qcursor, Qmousepoint, - Qmousein, - Qmousectl, }; static Dirtab mousedir[]={