From 01c8a087da9151c754a485d64bea5dd0cfaf4cdf Mon Sep 17 00:00:00 2001 From: David du Colombier <0intro@gmail.com> Date: Fri, 6 Mar 1998 00:00:00 +0000 Subject: [PATCH] Plan 9 from Bell Labs 1998-03-06 --- ip/arp.c | 314 +++++++++++++ ip/devip.c | 555 ++++++++++++---------- ip/eipconvtest.c | 152 ++++++ ip/ethermedium.c | 523 +++++++++++++++++++++ ip/gre.c | 78 ++-- ip/icmp.c | 86 ++-- ip/il.c | 143 +++--- ip/ip.c | 116 ++--- ip/ip.h | 412 ++++++++++++----- ip/ipaux.c | 343 +++++++++++--- ip/ipifc.c | 1156 ++++++++++++++++++++++++++++++++++++++++++++++ ip/iproute.c | 792 +++++++++++++++++++++++++++++++ ip/netlog.c | 18 +- ip/nullmedium.c | 46 ++ ip/pktmedium.c | 75 +++ ip/ptclbsum.c | 4 +- ip/tcp.c | 252 +++++----- ip/udp.c | 159 +++---- port/portdat.h | 1 + 19 files changed, 4386 insertions(+), 839 deletions(-) create mode 100644 ip/arp.c create mode 100644 ip/eipconvtest.c create mode 100644 ip/ethermedium.c create mode 100644 ip/ipifc.c create mode 100644 ip/iproute.c create mode 100644 ip/nullmedium.c create mode 100644 ip/pktmedium.c diff --git a/ip/arp.c b/ip/arp.c new file mode 100644 index 0000000000000000000000000000000000000000..74fb26e845cb044a3067f54fb8e224925b060bf3 --- /dev/null +++ b/ip/arp.c @@ -0,0 +1,314 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include "ip.h" + +/* + * address resolution tables + */ + +enum +{ + NHASH = (1<<6), + NCACHE = 256, + + AOK = 1, + AWAIT = 2, +}; + +char *arpstate[] = +{ + "UNUSED", + "OK", + "WAIT", +}; + +typedef struct Arpcache Arpcache; +struct Arpcache +{ + QLock; + Arpent* hash[NHASH]; + Arpent cache[NCACHE]; +}; + +Arpcache arp; + +char *Ebadarp = "bad arp"; + +#define haship(s) ((s)[IPaddrlen-1]%NHASH) + +static Arpent* +newarp(uchar *ip, Medium *m) +{ + uint t; + Block *next, *xp; + Arpent *a, *e, *f, **l; + + /* 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; + while(xp){ + next = xp->list; + freeblist(xp); + xp = next; + } + + /* 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; + } + + /* 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; +} + +Arpent* +arpget(Block *bp, int version, Medium *type, uchar *ip, uchar *mac) +{ + int hash; + Arpent *a; + uchar v6ip[IPaddrlen]; + + if(version == V4) { + v4tov6(v6ip, ip); + ip = v6ip; + } + + qlock(&arp); + hash = haship(ip); + 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(ip, type); + a->state = AWAIT; + } + a->used = msec; + if(a->state == AWAIT){ + if(a->hold) + a->last->list = bp; + else + a->hold = bp; + a->last = bp; + bp->list = nil; + return a; /* return with arp qlocked */ + } + + memmove(mac, a->mac, a->type->maclen); + qunlock(&arp); + return nil; +} + +/* + * called with arp locked + */ +void +arprelease(Arpent*) +{ + qunlock(&arp); +} + +/* + * called with arp locked + */ +Block* +arpresolve(Arpent *a, Medium *type, uchar *mac) +{ + Block *bp; + + memmove(a->mac, mac, type->maclen); + a->type = type; + a->state = AOK; + a->used = msec; + bp = a->hold; + a->hold = nil; + qunlock(&arp); + + return bp; +} + +void +arpenter(Ipifc *ifc, int version, uchar *ip, uchar *mac, Medium *type, int refresh) +{ + Arpent *a; + Block *bp, *next; + uchar v6ip[IPaddrlen]; + + if(version == V4){ + v4tov6(v6ip, ip); + ip = v6ip; + } + + qlock(&arp); + 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) { + a->state = AOK; + memmove(a->mac, mac, type->maclen); + bp = a->hold; + a->hold = nil; + if(version == V4) + ip += IPv4off; + qunlock(&arp); + while(bp) { + next = bp->list; + if(ifc != nil){ + if(waserror()){ + runlock(ifc); + nexterror(); + } + rlock(ifc); + if(ifc->m != nil) + ifc->m->bwrite(ifc, bp, version, ip); + else + freeb(bp); + runlock(ifc); + poperror(); + } else + freeb(bp); + bp = next; + } + a->used = msec; + return; + } + } + + /* if nil, we're adding a new entry */ + if(refresh == 0){ + a = newarp(ip, type); + a->state = AOK; + a->type = type; + memmove(a->mac, mac, type->maclen); + } + + qunlock(&arp); +} + +int +arpwrite(char *s, int len) +{ + int n; + Block *bp; + Arpent *a; + char *f[4], buf[256]; + uchar ip[], mac[MAClen]; + Medium *m; + + 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 = parsefields(buf, f, 4, " "); + 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)); + qunlock(&arp); + } else if(strcmp(f[0], "add") == 0){ + if(n != 4) + error(Ebadarp); + m = ipfindmedium(f[1]); + if(m == nil) + error(Ebadarp); + parseip(ip, f[2]); + parsemac(mac, f[3], m->maclen); + arpenter(nil, V6, ip, mac, m, 0); + } else + error(Ebadarp); + + return len; +} + +enum +{ + Alinelen= 66, +}; + +char *aformat = "%-6.6s %-8.8s %-16.16I %-32.32s\n"; + +static void +convmac(char *p, uchar *mac, int n) +{ + while(n-- > 0) + p += sprint(p, "%2.2ux", *mac++); +} + +int +arpread(char *p, ulong offset, int len) +{ + Arpent *a; + int n; + char mac[2*MAClen+1]; + + if(offset % Alinelen) + return 0; + + offset = offset/Alinelen; + len = len/Alinelen; + + n = 0; + for(a = arp.cache; len > 0 && a < &arp.cache[NCACHE]; a++){ + if(a->state == 0) + continue; + if(offset > 0){ + offset--; + continue; + } + len--; + qlock(&arp); + convmac(mac, a->mac, a->type->maclen); + n += sprint(p+n, aformat, a->type->name, arpstate[a->state], a->ip, mac); + qunlock(&arp); + } + + return n; +} diff --git a/ip/devip.c b/ip/devip.c index 311a9251c031e1817c3305cf0bed39d8b70bdf34..6fbec42314d29c1b32b83b593843dfca5fad1372 100644 --- a/ip/devip.c +++ b/ip/devip.c @@ -14,13 +14,14 @@ enum Qtopdir= 1, /* top level directory */ Qtopbase, Qarp= Qtopbase, - Qipifc, Qiproute, + Qiprouter, Qlog, Qprotodir, /* directory for a protocol */ Qprotobase, Qclone= Qprotobase, + Qstats, Qconvdir, /* directory for a conversation */ Qconvbase, @@ -79,7 +80,7 @@ ip3gen(Chan *c, int i, Dir *dp) q = (Qid){QID(PROTO(c->qid), CONV(c->qid), Qstatus), 0}; break; } - devdir(c, q, p, 0, cv->owner, 0444, dp); + devdir(c, q, p, 0, cv->owner, 0666, dp); return 1; } @@ -91,7 +92,11 @@ ip2gen(Chan *c, int i, Dir *dp) switch(i) { case Qclone: q = (Qid){QID(PROTO(c->qid), 0, Qclone), 0}; - devdir(c, q, "clone", 0, network, 0444, dp); + devdir(c, q, "clone", 0, network, 0666, dp); + return 1; + case Qstats: + q = (Qid){QID(PROTO(c->qid), 0, Qstats), 0}; + devdir(c, q, "stats", 0, network, 0444, dp); return 1; } return -1; @@ -110,20 +115,20 @@ ip1gen(Chan *c, int i, Dir *dp) p = "arp"; q = (Qid){QID(0, 0, Qarp), 0}; break; - case Qipifc: - p = "ipifc"; - q = (Qid){QID(0, 0, Qipifc), 0}; - break; case Qiproute: p = "iproute"; q = (Qid){QID(0, 0, Qiproute), 0}; break; + case Qiprouter: + p = "iprouter"; + q = (Qid){QID(0, 0, Qiprouter), 0}; + break; case Qlog: p = "log"; q = (Qid){QID(0, 0, Qlog), 0}; break; } - devdir(c, q, p, 0, network, 0444, dp); + devdir(c, q, p, 0, network, 0666, dp); return 1; } @@ -146,9 +151,9 @@ ipgen(Chan *c, Dirtab*, int, int s, Dir *dp) s -= fs.np; return ip1gen(c, s+Qtopbase, dp); case Qarp: - case Qipifc: case Qlog: case Qiproute: + case Qiprouter: return ip1gen(c, TYPE(c->qid), dp); case Qprotodir: if(s < fs.p[PROTO(c->qid)]->ac) { @@ -161,6 +166,7 @@ ipgen(Chan *c, Dirtab*, int, int s, Dir *dp) s -= fs.p[PROTO(c->qid)]->ac; return ip2gen(c, s+Qprotobase, dp); case Qclone: + case Qstats: return ip2gen(c, TYPE(c->qid), dp); case Qconvdir: return ip3gen(c, s+Qconvbase, dp); @@ -193,6 +199,8 @@ ipinit(void) fmtinstall('i', eipconv); fmtinstall('I', eipconv); fmtinstall('E', eipconv); + fmtinstall('V', eipconv); + fmtinstall('M', eipconv); } static Chan* @@ -217,25 +225,24 @@ ipwalk(Chan* c, char* name) { Path *op; - if(strcmp(name, "..") == 0){ - switch(TYPE(c->qid)){ - case Qtopdir: - case Qprotodir: - c->qid = (Qid){QID(0, 0, Qtopdir)|CHDIR, 0}; - break; - case Qconvdir: - c->qid = (Qid){QID(PROTO(c->qid), 0, Qprotodir)|CHDIR, 0}; - break; - default: - panic("ipwalk %lux", c->qid.path); - } - op = c->path; - c->path = ptenter(&syspt, op, name); - decref(op); - return 1; - } + if(strcmp(name, "..") != 0) + return devwalk(c, name, nil, 0, ipgen); - return devwalk(c, name, nil, 0, ipgen); + switch(TYPE(c->qid)){ + case Qtopdir: + case Qprotodir: + c->qid = (Qid){QID(0, 0, Qtopdir)|CHDIR, 0}; + break; + case Qconvdir: + c->qid = (Qid){QID(PROTO(c->qid), 0, Qprotodir)|CHDIR, 0}; + break; + default: + panic("ipwalk %lux", c->qid.path); + } + op = c->path; + c->path = ptenter(&syspt, op, name); + decref(op); + return 1; } static void @@ -272,18 +279,22 @@ ipopen(Chan* c, int omode) switch(TYPE(c->qid)) { default: break; - case Qipifc: - c->aux = newifcconv(); - break; case Qlog: netlogopen(); break; + case Qiprouter: + iprouteropen(); + break; + case Qiproute: + memmove(c->tag, "none", sizeof(c->tag)); + break; case Qtopdir: case Qprotodir: case Qconvdir: case Qstatus: case Qremote: case Qlocal: + case Qstats: if(omode != OREAD) error(Eperm); break; @@ -300,12 +311,12 @@ ipopen(Chan* c, int omode) case Qctl: case Qerr: p = fs.p[PROTO(c->qid)]; - lock(p); + qlock(p); cv = p->conv[CONV(c->qid)]; lock(cv); if(waserror()) { unlock(cv); - unlock(p); + qunlock(p); nexterror(); } if((perm & (cv->perm>>6)) != perm) { @@ -321,7 +332,7 @@ ipopen(Chan* c, int omode) cv->perm = 0660; } unlock(cv); - unlock(p); + qunlock(p); poperror(); break; case Qlisten: @@ -381,8 +392,10 @@ static void closeconv(Conv *cv) { Conv *nc; + Ipmulti *mp; lock(cv); + if(--cv->inuse > 0) { unlock(cv); return; @@ -397,6 +410,9 @@ closeconv(Conv *cv) strcpy(cv->owner, network); cv->perm = 0660; + while((mp = cv->multi) != nil) + ipifcremmulti(cv, mp->ma, mp->ia); + /* The close routine will unlock the conv */ cv->p->close(cv); } @@ -407,13 +423,13 @@ ipclose(Chan* c) switch(TYPE(c->qid)) { default: break; - case Qipifc: + case Qlog: if(c->flag & COPEN) - closeifcconv(c->aux); - c->aux = nil; + netlogclose(); break; - case Qlog: - netlogclose(); + case Qiprouter: + if(c->flag & COPEN) + iprouterclose(); break; case Qdata: case Qctl: @@ -423,13 +439,18 @@ ipclose(Chan* c) } } +enum +{ + Statelen= 1024, +}; + static long ipread(Chan *ch, void *a, long n, ulong offset) { Conv *c; Proto *x; - byte ip[4]; - char buf[256], *p, *statename; + char *buf, *p; + long m, rv; p = a; switch(TYPE(ch->qid)) { @@ -441,41 +462,61 @@ ipread(Chan *ch, void *a, long n, ulong offset) return devdirread(ch, a, n, 0, 0, ipgen); case Qarp: return arpread(a, offset, n); - case Qipifc: - return Mediaifcread(a, offset, n); case Qiproute: return routeread(a, offset, n); + case Qiprouter: + return iprouterread(a, n); case Qlog: return netlogread(a, offset, n); case Qctl: + buf = smalloc(16); sprint(buf, "%d", CONV(ch->qid)); - return readstr(offset, p, n, buf); + rv = readstr(offset, p, n, buf); + free(buf); + return rv; case Qremote: + buf = smalloc(Statelen); c = fs.p[PROTO(ch->qid)]->conv[CONV(ch->qid)]; - hnputl(ip, c->raddr); - sprint(buf, "%I!%d\n", ip, c->rport); - return readstr(offset, p, n, buf); + sprint(buf, "%I!%d\n", c->raddr, c->rport); + rv = readstr(offset, p, n, buf); + free(buf); + return rv; case Qlocal: - c = fs.p[PROTO(ch->qid)]->conv[CONV(ch->qid)]; - if(media != nil && c->laddr == 0) - hnputl(ip, Mediagetaddr(media)); - else - hnputl(ip, c->laddr); - sprint(buf, "%I!%d\n", ip, c->lport); - return readstr(offset, p, n, buf); + buf = smalloc(Statelen); + x = fs.p[PROTO(ch->qid)]; + c = x->conv[CONV(ch->qid)]; + if(x->local == nil) { + sprint(buf, "%I!%d\n", c->laddr, c->lport); + } else { + (*x->local)(c, buf, Statelen-2); + } + rv = readstr(offset, p, n, buf); + free(buf); + return rv; case Qstatus: + buf = smalloc(Statelen); x = fs.p[PROTO(ch->qid)]; c = x->conv[CONV(ch->qid)]; - x->state(&statename, c); - sprint(buf, "%s/%d %d %s \n", c->p->name, c->x, c->inuse, statename); - return readstr(offset, p, n, buf); + m = sprint(buf, "%s/%d %d ", c->p->name, c->x, c->inuse); + (*x->state)(c, buf, Statelen-m-2); + rv = readstr(offset, p, n, buf); + free(buf); + return rv; case Qdata: c = fs.p[PROTO(ch->qid)]->conv[CONV(ch->qid)]; return qread(c->rq, a, n); - case Qerr: c = fs.p[PROTO(ch->qid)]->conv[CONV(ch->qid)]; return qread(c->eq, a, n); + case Qstats: + x = fs.p[PROTO(ch->qid)]; + if(x->stats == nil) + error("stats not implemented"); + buf = smalloc(Statelen); + (*x->stats)(buf, Statelen); + rv = readstr(offset, p, n, buf); + free(buf); + return rv; } } @@ -485,15 +526,18 @@ ipbread(Chan* c, long n, ulong offset) return devbread(c, n, offset); } +/* + * set local address to be that of the ifc closest to remote address + */ static void setladdr(Conv* c) { - byte rem[4]; - - hnputl(rem, c->raddr); - c->laddr = Mediagetsrc(rem); + findlocalip(c->laddr, c->raddr); } +/* + * pick a local port and set it + */ static void setlport(Conv* c) { @@ -506,7 +550,7 @@ setlport(Conv* c) pp = &p->nextrport; else pp = &p->nextport; - lock(p); + qlock(p); for(;;(*pp)++){ /* * Fsproto initialises p->nextport to 0 and the restricted @@ -536,14 +580,21 @@ setlport(Conv* c) break; } c->lport = (*pp)++; - unlock(p); + qunlock(p); } +/* + * set a local address and port from a string of the form + * address!port + * if address is missing and not announcing, pick one + * if address is missing and announcing, leave address as zero (i.e. matches anything) + * if port is 0, pick a free one + * if port is '*', leave port as zero (i.e. matches anything) + */ static void -setladdrport(Conv* c, char* str, int nodefault) +setladdrport(Conv* c, char* str, int announcing) { char *p; - uchar addr[IPaddrlen]; /* * ignore restricted part if it exists. it's @@ -552,19 +603,16 @@ setladdrport(Conv* c, char* str, int nodefault) p = strchr(str, '!'); if(p == nil || strcmp(p, "!r") == 0) { p = str; - if(nodefault) + memset(c->laddr, 0, sizeof(c->laddr)); + if(!announcing) setladdr(c); - else - c->laddr = 0; - } - else { + } else { *p++ = 0; - parseip(addr, str); - c->laddr = nhgetl(addr); + parseip(c->laddr, str); } - if(*p == '*') - c->lport = 0; - else { + + c->lport = 0; + if(*p != '*'){ c->lport = atoi(p); if(c->lport == 0) setlport(c); @@ -575,14 +623,12 @@ static char* setraddrport(Conv* c, char* str) { char *p; - uchar addr[IPaddrlen]; p = strchr(str, '!'); if(p == nil) return "malformed address"; *p++ = 0; - parseip(addr, str); - c->raddr = nhgetl(addr); + parseip(c->raddr, str); c->rport = atoi(p); p = strchr(p, '!'); if(p){ @@ -592,44 +638,163 @@ setraddrport(Conv* c, char* str) return nil; } +/* + * called by protocol connect routine to set addresses + */ +char* +Fsstdconnect(Conv *c, char *argv[], int argc) +{ + char *p; + + switch(argc) { + default: + return "bad args to connect"; + case 2: + p = setraddrport(c, argv[1]); + if(p != nil) + return p; + setladdr(c); + setlport(c); + break; + case 3: + p = setraddrport(c, argv[1]); + if(p != nil) + return p; + setladdrport(c, argv[2], 0); + break; + } + return nil; +} +/* + * initiate connection and sleep till its set up + */ static int connected(void* a) { return ((Conv*)a)->state == Connected; } +static void +connectctlmsg(Proto *x, Conv *c, Cmdbuf *cb) +{ + char *p; + + c->state = Connecting; + c->cerr[0] = '\0'; + if(x->connect == nil) + error("connect not supported"); + p = x->connect(c, cb->f, cb->nf); + if(p != nil) + error(p); + sleep(&c->cr, connected, c); + if(c->cerr[0] != '\0') + error(c->cerr); +} + +/* + * called by protocol announce routine to set addresses + */ +char* +Fsstdannounce(Conv* c, char* argv[], int argc) +{ + switch(argc){ + default: + return "bad args to announce"; + case 2: + setladdrport(c, argv[1], 1); + break; + } + return nil; +} +/* + * initiate announcement and sleep till its set up + */ static int announced(void* a) { return ((Conv*)a)->state == Announced; } +static void +announcectlmsg(Proto *x, Conv *c, Cmdbuf *cb) +{ + char *p; + + c->state = Announcing; + c->cerr[0] = '\0'; + if(x->announce == nil) + error("announce not supported"); + p = x->announce(c, cb->f, cb->nf); + if(p != nil) + error(p); + sleep(&c->cr, announced, c); + if(c->cerr[0] != '\0') + error(c->cerr); +} + +/* + * called by protocol bide routine to set addresses + */ +char* +Fsstdbind(Conv* c, char* argv[], int argc) +{ + switch(argc){ + default: + return "bad args to bind"; + case 2: + setladdrport(c, argv[1], 0); + break; + } + return nil; +} + +static void +bindctlmsg(Proto *x, Conv *c, Cmdbuf *cb) +{ + char *p; + + if(x->bind == nil) + p = Fsstdbind(c, cb->f, cb->nf); + else + p = x->bind(c, cb->f, cb->nf); + if(p != nil) + error(p); +} + +static void +ttlctlmsg(Conv *c, Cmdbuf *cb) +{ + if(cb->nf < 2) + c->ttl = MAXTTL; + else + c->ttl = atoi(cb->f[1]); +} static long ipwrite(Chan* ch, char* a, long n, ulong) { Conv *c; Proto *x; - int nfield; - char *p, *fields[10], buf[128]; + char *p; + Cmdbuf *cb; + uchar ia[IPaddrlen]; switch(TYPE(ch->qid)){ default: error(Eperm); + case Qdata: + x = fs.p[PROTO(ch->qid)]; + c = x->conv[CONV(ch->qid)]; + + if(c->wq == nil) + error(Eperm); + + qwrite(c->wq, a, n); + x->kick(c, n); + break; case Qarp: - p = arpwrite(a, n); - if(p != nil) - error(p); - return n; - case Qipifc: - p = Mediaifcwrite(ch->aux, a, n); - if(p != nil) - error(p); - return n; + return arpwrite(a, n); case Qiproute: - p = routewrite(a, n); - if(p != nil) - error(p); - return n; + return routewrite(ch, a, n); case Qlog: p = netlogctl(a, n); if(p != nil) @@ -638,90 +803,48 @@ ipwrite(Chan* ch, char* a, long n, ulong) case Qctl: x = fs.p[PROTO(ch->qid)]; c = x->conv[CONV(ch->qid)]; - if(n > sizeof(buf)-1) - n = sizeof(buf)-1; - memmove(buf, a, n); - buf[n] = '\0'; - nfield = parsefields(buf, fields, 10, " "); - if(strcmp(fields[0], "connect") == 0){ - if(canqlock(&c->car) == 0) - error("connect/announce in progress"); - if(waserror()) { - qunlock(&c->car); - nexterror(); - } - c->state = Connecting; - c->cerr[0] = '\0'; - p = x->connect(c, fields, nfield); - if(p != nil) - error(p); - sleep(&c->cr, connected, c); - if(c->cerr[0] != '\0') - error(c->cerr); - qunlock(&c->car); - poperror(); - return n; - } - if(strcmp(fields[0], "announce") == 0){ - if(canqlock(&c->car) == 0) - error("connect/announce in progress"); - if(waserror()) { - qunlock(&c->car); - nexterror(); - } - c->state = Announcing; - c->cerr[0] = '\0'; - p = x->announce(c, fields, nfield); - if(p != nil) - error(p); - sleep(&c->cr, announced, c); - if(c->cerr[0] != '\0') - error(c->cerr); - qunlock(&c->car); - poperror(); - return n; + cb = parsecmd(a, n); + + if(canqlock(&c->car) == 0){ + free(cb); + error("connect/announce in progress"); } - if(strcmp(fields[0], "bind") == 0){ - if(canqlock(&c->car) == 0) - error("connect/announce in progress"); - if(waserror()) { - qunlock(&c->car); - nexterror(); - } - switch(nfield){ - default: - error("bad args to bind"); - case 2: - setladdr(c); - c->lport = atoi(fields[1]); - if(c->lport == 0) - setlport(c); - break; - } + if(waserror()) { qunlock(&c->car); - poperror(); - return n; - } - if(strcmp(fields[0], "ttl") == 0){ - if(nfield < 2) - c->ttl = MAXTTL; - else - c->ttl = atoi(fields[1]); - return n; + free(cb); + nexterror(); } - if(x->ctl != nil) { - p = x->ctl(c, fields, nfield); + if(strcmp(cb->f[0], "connect") == 0) + connectctlmsg(x, c, cb); + else if(strcmp(cb->f[0], "announce") == 0) + announcectlmsg(x, c, cb); + else if(strcmp(cb->f[0], "bind") == 0) + bindctlmsg(x, c, cb); + else if(strcmp(cb->f[0], "ttl") == 0) + ttlctlmsg(c, cb); + else if(strcmp(cb->f[0], "addmulti") == 0){ + if(cb->nf < 2) + error("addmulti needs interface address"); + if(!ipismulticast(c->raddr)) + error("addmulti for a non multicast address"); + parseip(ia, cb->f[1]); + ipifcaddmulti(c, c->raddr, ia); + } 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); + } else if(x->ctl != nil) { + p = x->ctl(c, cb->f, cb->nf); if(p != nil) error(p); - return n; - } - error("unknown control request"); - case Qdata: - x = fs.p[PROTO(ch->qid)]; - c = x->conv[CONV(ch->qid)]; - - qwrite(c->wq, a, n); - x->kick(c, n); + } else + error("unknown control request"); + qunlock(&c->car); + free(cb); + poperror(); } return n; } @@ -782,7 +905,7 @@ Fsproto(Fs *fs, Proto *p) * built in */ int -Fsbuiltinproto(Fs* fs, byte proto) +Fsbuiltinproto(Fs* fs, uchar proto) { return fs->t2p[proto] != nil; } @@ -790,14 +913,12 @@ Fsbuiltinproto(Fs* fs, byte proto) Conv* Fsprotoclone(Proto *p, char *user) { - char *junk; - int unused; Conv *c, **pp, **ep; c = nil; - lock(p); + qlock(p); if(waserror()) { - unlock(p); + qunlock(p); nexterror(); } ep = &p->conv[p->nc]; @@ -812,7 +933,6 @@ Fsprotoclone(Proto *p, char *user) c->x = pp - p->conv; c->ptcl = malloc(p->ptclsize); if(c->ptcl == nil) { - unlock(c); free(c); error(Enomem); } @@ -823,15 +943,18 @@ Fsprotoclone(Proto *p, char *user) break; } if(canlock(c)){ - unused = p->state(&junk, c); - if(c->inuse == 0 && unused) + /* + * make sure both processes and protocol + * are done with this Conv + */ + if(c->inuse == 0 && (p->inuse == nil || (*p->inuse)(c) == 0)) break; unlock(c); } } if(pp >= ep) { - unlock(p); + qunlock(p); poperror(); return nil; } @@ -840,8 +963,8 @@ Fsprotoclone(Proto *p, char *user) strcpy(c->owner, user); c->perm = 0660; c->state = 0; - c->laddr = 0; - c->raddr = 0; + ipmove(c->laddr, IPnoaddr); + ipmove(c->raddr, IPnoaddr); c->lport = 0; c->rport = 0; c->restricted = 0; @@ -851,7 +974,7 @@ Fsprotoclone(Proto *p, char *user) qreopen(c->eq); unlock(c); - unlock(p); + qunlock(p); poperror(); return c; } @@ -878,13 +1001,13 @@ Fsconnected(Fs*, Conv* c, char* msg) } Proto* -Fsrcvpcol(Fs* fs, byte proto) +Fsrcvpcol(Fs* fs, uchar proto) { return fs->t2p[proto]; } Conv* -Fsnewcall(Fs*, Conv *c, Ipaddr raddr, ushort rport, Ipaddr laddr, ushort lport) +Fsnewcall(Fs*, Conv *c, uchar *raddr, ushort rport, uchar *laddr, ushort lport) { Conv *nc; Conv **l; @@ -905,9 +1028,9 @@ Fsnewcall(Fs*, Conv *c, Ipaddr raddr, ushort rport, Ipaddr laddr, ushort lport) unlock(c); return nil; } - nc->raddr = raddr; + ipmove(nc->raddr, raddr); nc->rport = rport; - nc->laddr = laddr; + ipmove(nc->laddr, laddr); nc->lport = lport; nc->next = nil; *l = nc; @@ -917,59 +1040,3 @@ Fsnewcall(Fs*, Conv *c, Ipaddr raddr, ushort rport, Ipaddr laddr, ushort lport) return nc; } - -int -Fspcolstats(char *buf, int len) -{ - Proto **p; - int n; - - n = 0; - for(p = fs.p; *p; p++){ - n += snprint(buf + n, len - n, - "%s: csum %d hlen %d len %d order %d rexmit %d\n", - (*p)->name, (*p)->csumerr, (*p)->hlenerr, (*p)->lenerr, - (*p)->order, (*p)->rexmit); - if((*p)->stats) - n += (*(*p)->stats)(buf + n, len - n); - } - return n; -} - -char* -Fsstdconnect(Conv *c, char *argv[], int argc) -{ - char *p; - - switch(argc) { - default: - return "bad args to connect"; - case 2: - p = setraddrport(c, argv[1]); - if(p != nil) - return p; - setladdr(c); - setlport(c); - break; - case 3: - p = setraddrport(c, argv[1]); - if(p != nil) - return p; - setladdrport(c, argv[2], 1); - break; - } - return nil; -} - -char* -Fsstdannounce(Conv* c, char* argv[], int argc) -{ - switch(argc){ - default: - return "bad args to announce"; - case 2: - setladdrport(c, argv[1], 0); - break; - } - return nil; -} diff --git a/ip/eipconvtest.c b/ip/eipconvtest.c new file mode 100644 index 0000000000000000000000000000000000000000..06b0f9b50dddd014aa85ef0d13942eea10d78023 --- /dev/null +++ b/ip/eipconvtest.c @@ -0,0 +1,152 @@ +#include +#include + +enum +{ + Isprefix= 16, +}; + +uchar prefixvals[256] = +{ +[0x00] 0 | Isprefix, +[0x80] 1 | Isprefix, +[0xC0] 2 | Isprefix, +[0xE0] 3 | Isprefix, +[0xF0] 4 | Isprefix, +[0xF8] 5 | Isprefix, +[0xFC] 6 | Isprefix, +[0xFE] 7 | Isprefix, +[0xFF] 8 | Isprefix, +}; + +uchar v4prefix[16] = { + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0xff, 0xff, + 0, 0, 0, 0 +}; + +void +hnputl(void *p, ulong v) +{ + uchar *a; + + a = p; + a[0] = v>>24; + a[1] = v>>16; + a[2] = v>>8; + a[3] = v; +} + +int +eipconv(va_list *arg, Fconv *f) +{ + char buf[8*5]; + static char *efmt = "%.2lux%.2lux%.2lux%.2lux%.2lux%.2lux"; + static char *ifmt = "%d.%d.%d.%d"; + uchar *p, ip[16]; + ulong *lp; + ushort s; + int i, j, n, eln, eli; + + switch(f->chr) { + case 'E': /* Ethernet address */ + p = va_arg(*arg, uchar*); + sprint(buf, efmt, p[0], p[1], p[2], p[3], p[4], p[5]); + break; + case 'I': /* Ip address */ + p = va_arg(*arg, uchar*); +common: + if(memcmp(p, v4prefix, 12) == 0) + sprint(buf, ifmt, p[12], p[13], p[14], p[15]); + else { + /* find longest elision */ + eln = eli = -1; + for(i = 0; i < 16; i += 2){ + for(j = i; j < 16; j += 2) + if(p[j] != 0 || p[j+1] != 0) + break; + if(j > i && j - i > eln){ + eli = i; + eln = j - i; + } + } + + /* print with possible elision */ + n = 0; + for(i = 0; i < 16; i += 2){ + if(i == eli){ + n += sprint(buf+n, "::"); + i += eln; + if(i >= 16) + break; + } else if(i != 0) + n += sprint(buf+n, ":"); + s = (p[i]<<8) + p[i+1]; + n += sprint(buf+n, "%ux", s); + } + } + break; + case 'i': /* v6 address as 4 longs */ + lp = va_arg(*arg, ulong*); + for(i = 0; i < 4; i++) + hnputl(ip+4*i, *lp++); + p = ip; + goto common; + case 'V': /* v4 ip address */ + p = va_arg(*arg, uchar*); + sprint(buf, ifmt, p[0], p[1], p[2], p[3]); + break; + case 'M': /* ip mask */ + p = va_arg(*arg, uchar*); + + /* look for a prefix mask */ + for(i = 0; i < 16; i++) + if(p[i] != 0xff) + break; + if(i < 16){ + if((prefixvals[p[i]] & Isprefix) == 0) + goto common; + for(j = i+1; j < 16; j++) + if(p[j] != 0) + goto common; + n = 8*i + (prefixvals[p[i]] & ~Isprefix); + } else + n = 8*16; + + /* got one, use /xx format */ + sprint(buf, "/%d", n); + break; + default: + strcpy(buf, "(eipconv)"); + } + strconv(buf, f); + return sizeof(uchar*); +} + +uchar testvec[11][16] = +{ + { 0,0,0,0, 0,0,0,0, 0,0,0xff,0xff, 1,3,4,5, }, + { 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, }, + { 0xff,0xff,0x80,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, }, + { 0xff,0xff,0xff,0xc0, 0,0,0,0, 0,0,0,0, 0,0,0,0, }, + { 0xff,0xff,0xff,0xff, 0xe0,0,0,0, 0,0,0,0, 0,0,0,0, }, + { 0xff,0xff,0xff,0xff, 0xff,0xf0,0,0, 0,0,0,0, 0,0,0,0, }, + { 0xff,0xff,0xff,0xff, 0xff,0xff,0xf8,0, 0,0,0,0, 0,0,0,0, }, + { 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, }, + { 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, }, + { 0,0,0,0, 0,0x11,0,0, 0,0,0,0, 0,0,0,0, }, + { 0,0,0,0x11, 0,0,0,0, 0,0,0,0, 0,0,0,0x12, }, +}; + +void +main(void) +{ + int i; + + fmtinstall('I', eipconv); + fmtinstall('M', eipconv); + for(i = 0; i < 11; i++) + print("%I\n%M\n", testvec[i], testvec[i]); + exits(0); +} diff --git a/ip/ethermedium.c b/ip/ethermedium.c new file mode 100644 index 0000000000000000000000000000000000000000..741f40480f103b6887c6debce4bb6a4b052fa09f --- /dev/null +++ b/ip/ethermedium.c @@ -0,0 +1,523 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include "ip.h" +#include "kernel.h" + +typedef struct Etherhdr Etherhdr; +struct Etherhdr +{ + uchar d[6]; + uchar s[6]; + uchar t[2]; +}; + +static void etherread(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); +static void etheraddmulti(Ipifc *ifc, uchar *a, uchar *ia); +static void etherremmulti(Ipifc *ifc, uchar *a, uchar *ia); +static Block* multicastarp(Arpent *a, uchar *mac); +static void sendarp(Ipifc *ifc, Arpent *a); +static int multicastea(uchar *ea, uchar *ip); +static void recvarpproc(Ipifc *ifc); + +Medium ethermedium = +{ + "ether", + 14, + 60, + 1514, + 6, + etherbind, + etherunbind, + etherbwrite, + etheraddmulti, + etherremmulti, + nil, /* pktin */ + nil, /* addroute */ + nil, /* remroute */ + nil, /* flushroute */ + nil, /* joinmulti */ + nil, /* leavemulti */ +}; + +typedef struct Etherrock Etherrock; +struct Etherrock +{ + Proc *arpp; /* arp process */ + Proc *readp; /* reading process */ + Chan *mchan; /* Data channel */ + Chan *achan; /* Arp channel */ + Chan *cchan; /* Control channel */ +}; + +/* + * ethernet arp request + */ +enum +{ + ETARP = 0x0806, + ETIP = 0x0800, + ARPREQUEST = 1, + ARPREPLY = 2, +}; +typedef struct Etherarp Etherarp; +struct Etherarp +{ + uchar d[6]; + uchar s[6]; + uchar type[2]; + uchar hrd[2]; + uchar pro[2]; + uchar hln; + uchar pln; + uchar op[2]; + uchar sha[6]; + uchar spa[4]; + uchar tha[6]; + uchar tpa[4]; +}; + + +/* + * called to bind an IP ifc to an ethernet device + * called with ifc wlock'd + */ +static void +etherbind(Ipifc *ifc, int argc, char **argv) +{ + Chan *mchan, *cchan, *achan; + char addr[2*NAMELEN]; + char dir[2*NAMELEN]; + char *buf; + int fd, cfd, n; + char *ptr; + Etherrock *er; + + if(argc < 2) + error(Ebadarg); + + mchan = cchan = achan = nil; + buf = nil; + if(waserror()){ + if(mchan != nil) + cclose(mchan); + if(cchan != nil) + cclose(cchan); + if(achan != nil) + cclose(achan); + if(buf != nil) + free(buf); + nexterror(); + } + + /* + * open ip conversation + * + * the dial will fail if the type is already open on + * this device. + */ + snprint(addr, sizeof(addr), "%s!0x800", argv[2]); + fd = kdial(addr, nil, dir, &cfd); + if(fd < 0) + error("dial 0x800 failed"); + mchan = fdtochan(fd, ORDWR, 0, 1); + cchan = fdtochan(cfd, ORDWR, 0, 1); + kclose(fd); + kclose(cfd); + + /* + * get mac address + */ + snprint(addr, sizeof(addr), "%s/stats", dir); + fd = kopen(addr, OREAD); + if(fd < 0) + error("can't read ether stats"); + + buf = smalloc(512); + n = kread(fd, buf, 511); + kclose(fd); + if(n <= 0) + error(Eio); + buf[n] = 0; + + ptr = strstr(buf, "addr: "); + if(!ptr) + error(Eio); + ptr += 6; + + parsemac(ifc->mac, ptr, 6); + + /* + * open arp conversation + */ + snprint(addr, sizeof(addr), "%s!0x806", argv[2]); + fd = kdial(addr, nil, nil, nil); + if(fd < 0) + error("dial 0x806 failed"); + achan = fdtochan(fd, ORDWR, 0, 1); + kclose(fd); + + er = smalloc(sizeof(*er)); + er->mchan = mchan; + er->cchan = cchan; + er->achan = achan; + ifc->arg = er; + + free(buf); + poperror(); + + kproc("etherread", etherread, ifc); + kproc("recvarpproc", recvarpproc, ifc); +} + +/* + * called with ifc wlock'd + */ +static void +etherunbind(Ipifc *ifc) +{ + Etherrock *er = ifc->arg; + + if(er->readp) + postnote(er->readp, 1, "unbind", 0); + if(er->arpp) + postnote(er->arpp, 1, "unbind", 0); + + /* wait for readers to die */ + while(er->arpp != 0 || er->readp != 0) + tsleep(&up->sleep, return0, 0, 300); + + if(er->mchan != nil) + cclose(er->mchan); + if(er->achan != nil) + cclose(er->achan); + if(er->cchan != nil) + cclose(er->cchan); + + free(er); +} + +/* + * called by ipoput with a single block to write + */ +static void +etherbwrite(Ipifc *ifc, Block *bp, int version, uchar *ip) +{ + Etherhdr *eh; + Arpent *a; + uchar mac[6]; + Etherrock *er = ifc->arg; + + if(waserror()) { + print("etherbwrite failed\n"); + ipifccheckout(ifc); + return; + } + if(ipifccheckin(ifc, ðermedium) < 0){ + freeb(bp); + poperror(); + return; + } + + /* get mac address of destination */ + a = arpget(bp, version, ðermedium, ip, mac); + if(a){ + /* check for broadcast or multicast */ + bp = multicastarp(a, mac); + if(bp == nil){ + sendarp(ifc, a); + goto out; + } + } + + /* make it a single block with space for the ether header */ + bp = padblock(bp, ifc->m->hsize); + if(bp->next) + bp = concatblock(bp); + if(BLEN(bp) < ifc->minmtu) + bp = adjustblock(bp, ifc->minmtu); + eh = (Etherhdr*)bp->rp; + + /* copy in mac addresses and ether type */ + memmove(eh->s, ifc->mac, sizeof(eh->s)); + memmove(eh->d, mac, sizeof(eh->d)); + switch(version){ + case V4: + eh->t[0] = 0x08; + eh->t[1] = 0x00; + break; + case V6: + eh->t[0] = 0x86; + eh->t[1] = 0xDD; + break; + } + + devtab[er->mchan->type]->bwrite(er->mchan, bp, 0); + ifc->out++; + +out: + ipifccheckout(ifc); + poperror(); +} + +/* + * process to read from the ethernet + */ +static void +etherread(void *a) +{ + Ipifc *ifc; + Block *bp; + Etherrock *er; + + ifc = a; + er = ifc->arg; + er->readp = up; /* hide identity under a rock for unbind */ + if(waserror()){ + er->readp = 0; + pexit("hangup", 1); + } + for(;;){ + bp = devtab[er->mchan->type]->bread(er->mchan, ifc->maxmtu, 0); + ifc->in++; + bp->rp += ifc->m->hsize; + if(ifc->lifc == nil) + freeb(bp); + else + ipiput(ifc->lifc->local, bp); + } +} + +static void +etheraddmulti(Ipifc *ifc, uchar *a, uchar *) +{ + uchar mac[6]; + char buf[64]; + Etherrock *er = ifc->arg; + + multicastea(mac, a); + sprint(buf, "addmulti %E", mac); + devtab[er->cchan->type]->write(er->cchan, buf, strlen(buf), 0); +} + +static void +etherremmulti(Ipifc *ifc, uchar *a, uchar *) +{ + uchar mac[6]; + char buf[64]; + Etherrock *er = ifc->arg; + + multicastea(mac, a); + sprint(buf, "remmulti %E", mac); + devtab[er->cchan->type]->write(er->cchan, buf, strlen(buf), 0); +} + +/* + * send an ethernet arp + * (only v4, v6 uses the neighbor discovery, rfc1970) + */ +static void +sendarp(Ipifc *ifc, Arpent *a) +{ + int n; + Block *bp; + Etherarp *e; + Etherrock *er = ifc->arg; + + /* don't do anything if it's been less than a second since the last */ + if(msec - a->time < 1000){ + arprelease(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; + arprelease(a); + + n = sizeof(Etherarp); + if(n < a->type->minmtu) + n = a->type->minmtu; + bp = allocb(n); + memset(bp->rp, 0, n); + e = (Etherarp*)bp->rp; + memmove(e->tpa, a->ip+IPv4off, sizeof(e->tpa)); + ipv4local(ifc, e->spa); + memmove(e->sha, ifc->mac, sizeof(e->sha)); + memset(e->d, 0xff, sizeof(e->d)); /* ethernet broadcast */ + memmove(e->s, ifc->mac, sizeof(e->s)); + + hnputs(e->type, ETARP); + hnputs(e->hrd, 1); + hnputs(e->pro, ETIP); + e->hln = sizeof(e->sha); + e->pln = sizeof(e->spa); + hnputs(e->op, ARPREQUEST); + bp->wp += n; + + n = devtab[er->achan->type]->bwrite(er->achan, bp, 0); + if(n < 0) + print("arp: send: %r\n"); +} + +static void +recvarp(Ipifc *ifc) +{ + int n; + Block *ebp, *rbp; + Etherarp *e, *r; + uchar ip[IPaddrlen]; + Etherrock *er = ifc->arg; + + ebp = devtab[er->achan->type]->bread(er->achan, ifc->maxmtu, 0); + if(ebp == nil) { + print("arp: rcv: %r\n"); + return; + } + + e = (Etherarp*)ebp->rp; + switch(nhgets(e->op)) { + default: + break; + + case ARPREPLY: + arpenter(ifc, V4, e->spa, e->sha, ðermedium, 0); + break; + + case ARPREQUEST: + /* don't answer arps till we know who we are */ + if(ifc->lifc == 0) + break; + + /* check for someone that think's they're me */ + v4tov6(ip, e->spa); + if(iplocalonifc(ifc, ip)){ + 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); + break; + } + } + + /* refresh what we know about sender */ + arpenter(ifc, V4, e->spa, e->sha, ðermedium, 1); + + /* answer only requests for our address or systems we're proxying for */ + v4tov6(ip, e->tpa); + if(!iplocalonifc(ifc, ip)) + if(ipproxyifc(ifc, ip) == 0) + 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; + rbp = allocb(n); + r = (Etherarp*)rbp->rp; + memset(r, 0, sizeof(Etherarp)); + hnputs(r->type, ETARP); + hnputs(r->hrd, 1); + hnputs(r->pro, ETIP); + r->hln = sizeof(r->sha); + r->pln = sizeof(r->spa); + hnputs(r->op, ARPREPLY); + memmove(r->tha, e->sha, sizeof(r->tha)); + memmove(r->tpa, e->spa, sizeof(r->tpa)); + memmove(r->sha, ifc->mac, sizeof(r->sha)); + memmove(r->spa, e->tpa, sizeof(r->spa)); + memmove(r->d, e->sha, sizeof(r->d)); + memmove(r->s, ifc->mac, sizeof(r->s)); + rbp->wp += n; + + n = devtab[er->achan->type]->bwrite(er->achan, rbp, 0); + if(n < 0) + print("arp: write: %r\n"); + } + freeb(ebp); +} + +static void +recvarpproc(Ipifc *ifc) +{ + Etherrock *er = ifc->arg; + + er->arpp = up; + if(waserror()){ + er->arpp = 0; + pexit("hangup", 1); + } + for(;;) + recvarp(ifc); +} + +static int +multicastea(uchar *ea, uchar *ip) +{ + int x; + + switch(x = ipismulticast(ip)){ + case V4: + ea[0] = 0x01; + ea[1] = 0x00; + ea[2] = 0x5e; + ea[3] = ip[13] & 0x7f; + ea[4] = ip[14]; + ea[5] = ip[15]; + break; + 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; + } + return x; +} + +/* + * fill in an arp entry for broadcast or multicast + * addresses + */ +static Block* +multicastarp(Arpent *a, uchar *mac) +{ + /* is it broadcast? */ + switch(ipforme(a->ip)){ + case Runi: + return nil; + case Rbcast: + memset(mac, 0xff, 6); + return arpresolve(a, ðermedium, mac); + default: + break; + } + + /* if multicast, fill in mac */ + switch(multicastea(mac, a->ip)){ + case V4: + case V6: + return arpresolve(a, ðermedium, mac); + } + + /* let arp take care of it */ + return nil; +} diff --git a/ip/gre.c b/ip/gre.c index 4542c8a7c6360f8c0ce09ca43c919aefade81cc5..065a492f224db7fb298e5faf5ab05bee8c91173e 100644 --- a/ip/gre.c +++ b/ip/gre.c @@ -23,20 +23,20 @@ enum typedef struct GREhdr { /* ip header */ - byte vihl; /* Version and header length */ - byte tos; /* Type of service */ - byte len[2]; /* packet length (including headers) */ - byte id[2]; /* Identification */ - byte frag[2]; /* Fragment information */ - byte Unused; - byte proto; /* Protocol */ - byte cksum[2]; /* checksum */ - byte src[4]; /* Ip source */ - byte dst[4]; /* Ip destination */ + uchar vihl; /* Version and header length */ + uchar tos; /* Type of service */ + uchar len[2]; /* packet length (including headers) */ + uchar id[2]; /* Identification */ + uchar frag[2]; /* Fragment information */ + uchar Unused; + uchar proto; /* Protocol */ + uchar cksum[2]; /* checksum */ + uchar src[4]; /* Ip source */ + uchar dst[4]; /* Ip destination */ /* gre header */ - byte flags[2]; - byte eproto[2]; /* encapsulation protocol */ + uchar flags[2]; + uchar eproto[2]; /* encapsulation protocol */ } GREhdr; Proto gre; @@ -56,7 +56,7 @@ greconnect(Conv *c, char **argv, int argc) /* make sure noone's already connected to this other sys */ p = c->p; - lock(p); + qlock(p); ecp = &p->conv[p->nc]; for(cp = p->conv; cp < ecp; cp++){ tc = *cp; @@ -66,12 +66,12 @@ greconnect(Conv *c, char **argv, int argc) continue; if(tc->rport == c->rport && tc->raddr == c->raddr){ err = "already connected to that addr/proto"; - c->rport = 0; - c->raddr = 0; + ipmove(c->laddr, IPnoaddr); + ipmove(c->raddr, IPnoaddr); break; } } - unlock(p); + qunlock(p); if(err != nil) return err; @@ -81,11 +81,10 @@ greconnect(Conv *c, char **argv, int argc) } static int -grestate(char **msg, Conv *c) +grestate(Conv *c, char *state, int n) { USED(c); - *msg = "Datagram"; - return 1; + return snprint(state, n, "%s", "Datagram"); } static void @@ -107,8 +106,8 @@ greclose(Conv *c) qclose(c->rq); qclose(c->wq); qclose(c->eq); - c->laddr = 0; - c->raddr = 0; + ipmove(c->laddr, IPnoaddr); + ipmove(c->raddr, IPnoaddr); c->lport = 0; c->rport = 0; @@ -122,7 +121,7 @@ grekick(Conv *c, int l) { GREhdr *ghp; Block *bp; - ulong raddr, laddr; + uchar laddr[IPaddrlen], raddr[IPaddrlen]; USED(l); @@ -142,16 +141,17 @@ grekick(Conv *c, int l) ghp = (GREhdr *)(bp->rp); - raddr = nhgetl(ghp->dst); - laddr = nhgetl(ghp->src); - if(raddr == 0) - raddr = c->raddr; - if(laddr == 0 || Mediaforme(ghp->src) <= 0) - laddr = c->laddr; + v4tov6(raddr, ghp->dst); + if(ipcmp(raddr, v4prefix) == 0) + memmove(ghp->dst, c->raddr + IPv4off, IPv4addrlen); + v4tov6(laddr, ghp->src); + if(ipcmp(laddr, v4prefix) == 0){ + if(ipcmp(c->laddr, IPnoaddr) == 0) + findlocalip(c->laddr, raddr); /* pick interface closest to dest */ + memmove(ghp->src, c->laddr + IPv4off, IPv4addrlen); + } ghp->proto = IP_GREPROTO; - hnputl(ghp->dst, raddr); - hnputl(ghp->src, laddr); hnputs(ghp->eproto, c->rport); ghp->frag[0] = 0; ghp->frag[1] = 0; @@ -160,19 +160,18 @@ grekick(Conv *c, int l) } static void -greiput(Media *m, Block *bp) +greiput(uchar*, Block *bp) { int len; GREhdr *ghp; - Ipaddr addr; Conv *c, **p; ushort eproto; + uchar raddr[IPaddrlen]; - USED(m); ghp = (GREhdr*)(bp->rp); + v4tov6(raddr, ghp->src); eproto = nhgets(ghp->eproto); - addr = nhgetl(ghp->src); /* Look for a conversation structure for this port and address */ c = nil; @@ -180,7 +179,7 @@ greiput(Media *m, Block *bp) c = *p; if(c->inuse == 0) continue; - if(c->raddr == addr && c->rport == eproto) + if(c->rport == eproto && ipcmp(c->raddr, raddr) == 0) break; } @@ -216,6 +215,14 @@ greiput(Media *m, Block *bp) } } +int +grestats(char *buf, int len) +{ + return snprint(buf, len, + "gre: csum %d hlen %d len %d order %d rexmit %d\n", + gre.csumerr, gre.hlenerr, gre.lenerr, gre.order, gre.rexmit); +} + void greinit(Fs *fs) { @@ -229,6 +236,7 @@ greinit(Fs *fs) gre.rcv = greiput; gre.ctl = nil; gre.advise = nil; + gre.stats = grestats; gre.ipproto = IP_GREPROTO; gre.nc = 64; gre.ptclsize = 0; diff --git a/ip/icmp.c b/ip/icmp.c index 89746927bab4a538aea4f9fa5c32c0f8ce07f751..623547145816805b2ef0cbe8a8e4514a097b5d9b 100644 --- a/ip/icmp.c +++ b/ip/icmp.c @@ -8,22 +8,22 @@ #include "ip.h" typedef struct Icmp { - byte vihl; /* Version and header length */ - byte tos; /* Type of service */ - byte length[2]; /* packet length */ - byte id[2]; /* Identification */ - byte frag[2]; /* Fragment information */ - byte ttl; /* Time to live */ - byte proto; /* Protocol */ - byte ipcksum[2]; /* Header checksum */ - byte src[4]; /* Ip source */ - byte dst[4]; /* Ip destination */ - byte type; - byte code; - byte cksum[2]; - byte icmpid[2]; - byte seq[2]; - byte data[1]; + uchar vihl; /* Version and header length */ + uchar tos; /* Type of service */ + uchar length[2]; /* packet length */ + uchar id[2]; /* Identification */ + uchar frag[2]; /* Fragment information */ + uchar ttl; /* Time to live */ + uchar proto; /* Protocol */ + uchar ipcksum[2]; /* Header checksum */ + uchar src[4]; /* Ip source */ + uchar dst[4]; /* Ip destination */ + uchar type; + uchar code; + uchar cksum[2]; + uchar icmpid[2]; + uchar seq[2]; + uchar data[1]; } Icmp; enum { /* Packet Types */ @@ -67,11 +67,10 @@ icmpconnect(Conv *c, char **argv, int argc) } static int -icmpstate(char **s, Conv *c) +icmpstate(Conv *c, char *state, int n) { USED(c); - *s = "Datagram"; - return 1; + return snprint(state, n, "%s", "Datagram"); } static void @@ -99,7 +98,8 @@ icmpclose(Conv *c) { qclose(c->rq); qclose(c->wq); - c->laddr = 0; + ipmove(c->laddr, IPnoaddr); + ipmove(c->raddr, IPnoaddr); c->lport = 0; unlock(c); } @@ -122,8 +122,8 @@ icmpkick(Conv *c, int l) p = (Icmp *)(bp->rp); if(p->type <= Maxtype) stats.out[p->type]++; - hnputl(p->dst, c->raddr); - hnputl(p->src, c->laddr); + v6tov4(p->dst, c->raddr); + v6tov4(p->src, c->laddr); p->proto = IP_ICMPPROTO; hnputs(p->icmpid, c->lport); memset(p->cksum, 0, sizeof(p->cksum)); @@ -137,11 +137,8 @@ icmpnoconv(Block *bp) Block *nbp; Icmp *p, *np; - /* don't bother if we haven't gotten any links up yet */ - if(Mediabooting()) - return; - p = (Icmp *)bp->rp; + netlog(Logicmp, "sending icmpnoconv -> %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; @@ -164,20 +161,17 @@ goticmpkt(Block *bp) { Conv **c, *s; Icmp *p; - Ipaddr dst; + uchar dst[IPaddrlen]; ushort recid; p = (Icmp *) bp->rp; - dst = nhgetl(p->src); + v4tov6(dst, p->src); recid = nhgets(p->icmpid); -netlog(Logicmp, "goticmpkt from %i to %d\n", dst, recid); for(c = icmp.conv; *c; c++) { s = *c; -netlog(Logicmp, "conv %i %d %i %d\n", s->laddr, s->lport, s->raddr, s->rport); - if(s->lport == recid && s->raddr == dst){ - if(bp->next) - bp = concatblock(bp); + if(s->lport == recid) + if(ipcmp(s->raddr, dst) == 0){ qpass(s->rq, bp); return; } @@ -189,7 +183,7 @@ static Block * mkechoreply(Block *bp) { Icmp *q; - byte ip[4]; + uchar ip[4]; q = (Icmp *)bp->rp; memmove(ip, q->src, sizeof(q->dst)); @@ -198,6 +192,7 @@ mkechoreply(Block *bp) q->type = EchoReply; memset(q->cksum, 0, sizeof(q->cksum)); hnputs(q->cksum, ptclcsum(bp, ICMP_IPSIZE, blocklen(bp) - ICMP_IPSIZE)); + return bp; } @@ -212,7 +207,7 @@ static char *unreachcode[] = }; static void -icmpiput(Media *m, Block *bp) +icmpiput(uchar*, Block *bp) { int n, iplen; Icmp *p; @@ -221,21 +216,23 @@ icmpiput(Media *m, Block *bp) char *msg; char m2[128]; - USED(m); p = (Icmp *)bp->rp; netlog(Logicmp, "icmpiput %d %d\n", p->type, p->code); n = blocklen(bp); if(n < ICMP_IPSIZE+ICMP_HDRSIZE){ icmp.hlenerr++; + netlog(Logicmp, "icmp hlen %d\n", n); goto raise; } iplen = nhgets(p->length); if(iplen > n || (iplen % 1)){ icmp.lenerr++; + netlog(Logicmp, "icmp length %d\n", iplen); goto raise; } if(ptclcsum(bp, ICMP_IPSIZE, iplen - ICMP_IPSIZE)){ icmp.csumerr++; + netlog(Logicmp, "icmp checksum error\n"); goto raise; } if(p->type <= Maxtype) @@ -269,7 +266,7 @@ icmpiput(Media *m, Block *bp) break; case TimeExceed: if(p->code == 0){ - sprint(m2, "ttl exceeded at %I", p->src); + sprint(m2, "ttl exceeded at %V", p->src); bp->rp += ICMP_IPSIZE+ICMP_HDRSIZE; if(blocklen(bp) < 8){ @@ -301,16 +298,17 @@ icmpadvise(Block *bp, char *msg) { Conv **c, *s; Icmp *p; - Ipaddr dst; + uchar dst[IPaddrlen]; ushort recid; p = (Icmp *) bp->rp; - dst = nhgetl(p->dst); + v4tov6(dst, p->dst); recid = nhgets(p->icmpid); for(c = icmp.conv; *c; c++) { s = *c; - if(s->lport == recid && s->raddr == dst){ + if(s->lport == recid) + if(ipcmp(s->raddr, dst) == 0){ qhangup(s->rq, msg); qhangup(s->wq, msg); break; @@ -324,10 +322,14 @@ icmpstats(char *buf, int len) { int i, n; - n = snprint(buf, len, "\trcvd "); + + n = snprint(buf, len, + "icmp: csum %d hlen %d len %d order %d rexmit %d\n", + icmp.csumerr, icmp.hlenerr, icmp.lenerr, icmp.order, icmp.rexmit); + n += snprint(buf+n, len-n, "\trcvd "); for(i = 0; i < Maxtype && len > n; i++) n += snprint(buf+n, len-n, " %d", stats.in[i]); - n += snprint(buf+n, len - n, "\n\tsent "); + n += snprint(buf+n, len-n, "\n\tsent "); for(i = 0; i < Maxtype && len > n; i++) n += snprint(buf+n, len-n, " %d", stats.out[i]); if(n < len) diff --git a/ip/il.c b/ip/il.c index a48eb886234726deadb12ff60a040bed16fbffb1..e947a990aedd086a53f944748ec86f2229f8f299 100644 --- a/ip/il.c +++ b/ip/il.c @@ -63,7 +63,7 @@ struct Ilcb /* Control block */ QLock ackq; /* Unacknowledged queue */ Block *unacked; Block *unackedtail; - ulong unackedbytes; + ulong unackeduchars; QLock outo; /* Out of order packet queue */ Block *outoforder; ulong next; /* Id of next to send */ @@ -77,7 +77,7 @@ struct Ilcb /* Control block */ int querytime; /* Query timer */ int deathtime; /* Time to kill connection */ int delay; /* Average of the fixed rtt delay */ - int rate; /* Average byte rate */ + int rate; /* Average uchar rate */ int mdev; /* Mean deviation of rtt */ int maxrtt; /* largest rtt seen */ ulong rttack; /* The ack we are waiting for */ @@ -87,7 +87,6 @@ struct Ilcb /* Control block */ int rexmit; /* number of retransmits */ ulong qt[Nqt+1]; /* state table for query messages */ int qtx; /* ... index into qt */ - char buf[128]; }; enum @@ -102,24 +101,24 @@ enum typedef struct Ilhdr Ilhdr; struct Ilhdr { - byte vihl; /* Version and header length */ - byte tos; /* Type of service */ - byte length[2]; /* packet length */ - byte id[2]; /* Identification */ - byte frag[2]; /* Fragment information */ - byte ttl; /* Time to live */ - byte proto; /* Protocol */ - byte cksum[2]; /* Header checksum */ - byte src[4]; /* Ip source */ - byte dst[4]; /* Ip destination */ - byte ilsum[2]; /* Checksum including header */ - byte illen[2]; /* Packet length */ - byte iltype; /* Packet type */ - byte ilspec; /* Special */ - byte ilsrc[2]; /* Src port */ - byte ildst[2]; /* Dst port */ - byte ilid[4]; /* Sequence id */ - byte ilack[4]; /* Acked sequence */ + uchar vihl; /* Version and header length */ + uchar tos; /* Type of service */ + uchar length[2]; /* packet length */ + uchar id[2]; /* Identification */ + uchar frag[2]; /* Fragment information */ + uchar ttl; /* Time to live */ + uchar proto; /* Protocol */ + uchar cksum[2]; /* Header checksum */ + uchar src[4]; /* Ip source */ + uchar dst[4]; /* Ip destination */ + uchar ilsum[2]; /* Checksum including header */ + uchar illen[2]; /* Packet length */ + uchar iltype; /* Packet type */ + uchar ilspec; /* Special */ + uchar ilsrc[2]; /* Src port */ + uchar ildst[2]; /* Dst port */ + uchar ilid[4]; /* Sequence id */ + uchar ilack[4]; /* Acked sequence */ }; static struct Ilstats @@ -167,7 +166,7 @@ void iltimers(Ilcb*); char* ilstart(Conv*, int, int); void ilackproc(); void iloutoforder(Conv*, Ilhdr*, Block*); -void iliput(Media*, Block*); +void iliput(uchar*, Block*); void iladvise(Block*, char*); int ilnextqt(Ilcb*); @@ -189,23 +188,26 @@ ilconnect(Conv *c, char **argv, int argc) return ilstart(c, IL_CONNECT, 20); } -int -ilstate(char **msg, Conv *c) +static int +ilstate(Conv *c, char *state, int n) { Ilcb *ic; - int isclose; ic = (Ilcb*)(c->ptcl); + return snprint(state, n, "%14.14s del %5.5d Br %5.5d md %5.5d una %5.5d rex %5.5d max %5.5d", + ilstates[ic->state], + ic->delay>>LogAGain, ic->rate>>LogAGain, ic->mdev>>LogDGain, + ic->unackeduchars, ic->rexmit, ic->maxrtt); +} - isclose = 0; - if(ic->state == Ilclosed) - isclose = 1; +static int +ilinuse(Conv *c) +{ + Ilcb *ic; + + ic = (Ilcb*)(c->ptcl); + return ic->state != Ilclosed; - sprint(ic->buf, "%s del %d Br %d md %d una %d rex %d max %d", ilstates[ic->state], - ic->delay>>LogAGain, ic->rate>>LogAGain, ic->mdev>>LogDGain, - ic->unackedbytes, ic->rexmit, ic->maxrtt); - *msg = ic->buf; - return isclose; } /* called with c locked */ @@ -248,7 +250,7 @@ ilclose(Conv *c) break; case Illistening: ic->state = Ilclosed; - c->laddr = 0; + ipmove(c->laddr, IPnoaddr); c->lport = 0; break; } @@ -291,8 +293,8 @@ ilkick(Conv *c, int l) /* Ip fields */ ih->frag[0] = 0; ih->frag[1] = 0; - hnputl(ih->dst, c->raddr); - hnputl(ih->src, c->laddr); + v6tov4(ih->dst, c->raddr); + v6tov4(ih->src, c->laddr); ih->proto = IP_ILPROTO; /* Il fields */ @@ -338,7 +340,13 @@ ilcreate(Conv *c) int ilxstats(char *buf, int len) { - return snprint(buf, len, "\tdupp %d dupb %d\n", ilstats.dup, ilstats.dupb); + int n; + + n = snprint(buf, len, + "il: csum %d hlen %d len %d order %d rexmit %d", + il.csumerr, il.hlenerr, il.lenerr, il.order, il.rexmit); + n += snprint(buf+n, len-n, " dupp %d dupb %d\n", + ilstats.dup, ilstats.dupb); } void @@ -355,6 +363,7 @@ ilinit(Fs *fs) il.ctl = nil; il.advise = iladvise; il.stats = ilxstats; + il.inuse = ilinuse; il.ipproto = IP_ILPROTO; il.nc = Nchans; il.ptclsize = sizeof(Ilcb); @@ -383,7 +392,7 @@ ilackq(Ilcb *ic, Block *bp) } ic->unackedtail = np; np->list = nil; - ic->unackedbytes += n; + ic->unackeduchars += n; } static @@ -450,24 +459,24 @@ ilackto(Ilcb *ic, ulong ackto) bp = ic->unacked; ic->unacked = bp->list; bp->list = nil; - ic->unackedbytes -= blocklen(bp); + ic->unackeduchars -= blocklen(bp); freeblist(bp); } qunlock(&ic->ackq); } void -iliput(Media *m, Block *bp) +iliput(uchar*, Block *bp) { char *st; Ilcb *ic; Ilhdr *ih; - Ipaddr dst; + uchar raddr[IPaddrlen]; + uchar laddr[IPaddrlen]; ushort sp, dp, csum; int plen, illen; Conv *s, **p, *new, *spec, *gen; - USED(m); ih = (Ilhdr *)bp->rp; plen = blocklen(bp); if(plen < IL_IPSIZE+IL_HDRSIZE){ @@ -485,7 +494,7 @@ iliput(Media *m, Block *bp) sp = nhgets(ih->ildst); dp = nhgets(ih->ilsrc); - dst = nhgetl(ih->src); + v4tov6(raddr, ih->src); if(ilcksum && (csum = ptclcsum(bp, IL_IPSIZE, illen)) != 0) { if(ih->iltype < 0 || ih->iltype > Ilclose) @@ -493,14 +502,16 @@ iliput(Media *m, Block *bp) else st = iltype[ih->iltype]; il.csumerr++; - netlog(Logil, "il: cksum %ux %ux, pkt(%s id %lud ack %lud %i/%d->%d)\n", - csum, st, nhgetl(ih->ilid), nhgetl(ih->ilack), dst, sp, dp); + netlog(Logil, "il: cksum %ux %ux, pkt(%s id %lud ack %lud %I/%d->%d)\n", + csum, st, nhgetl(ih->ilid), nhgetl(ih->ilack), raddr, sp, dp); goto raise; } for(p = il.conv; *p; p++) { s = *p; - if(s->lport == sp && s->rport == dp && s->raddr == dst) { + if(s->lport == sp) + if(s->rport == dp) + if(ipcmp(s->raddr, raddr) == 0) { ilprocess(s, ih, bp); return; } @@ -511,8 +522,8 @@ iliput(Media *m, Block *bp) st = "?"; else st = iltype[ih->iltype]; - netlog(Logil, "il: no channel, pkt(%s id %lud ack %lud %i/%ud->%ud)\n", - st, nhgetl(ih->ilid), nhgetl(ih->ilack), dst, sp, dp); + netlog(Logil, "il: no channel, pkt(%s id %lud ack %lud %I/%ud->%ud)\n", + st, nhgetl(ih->ilid), nhgetl(ih->ilack), raddr, sp, dp); goto raise; } @@ -524,7 +535,7 @@ iliput(Media *m, Block *bp) if(ic->state != Illistening) continue; - if(s->rport == 0 && s->raddr == 0) { + if(s->rport == 0 && ipcmp(s->raddr, IPnoaddr) == 0) { if(s->lport == sp) { spec = s; break; @@ -542,9 +553,10 @@ iliput(Media *m, Block *bp) else goto raise; - new = Fsnewcall(&fs, s, nhgetl(ih->src), dp, nhgetl(ih->dst), sp); + v4tov6(laddr, ih->dst); + new = Fsnewcall(&fs, s, raddr, dp, laddr, sp); if(new == nil){ - netlog(Logil, "il: bad newcall %i/%ud->%ud\n", dst, sp, dp); + netlog(Logil, "il: bad newcall %I/%ud->%ud\n", raddr, sp, dp); ilsendctl(nil, ih, Ilclose, 0, nhgetl(ih->ilid), 0); goto raise; } @@ -797,7 +809,7 @@ ilhangup(Conv *s, char *msg) Ilcb *ic; int callout; - netlog(Logil, "il: hangup! %i %d/%d: %s\n", s->raddr, s->lport, s->rport, msg?msg:"no reason"); + netlog(Logil, "il: hangup! %I %d/%d: %s\n", s->raddr, s->lport, s->rport, msg?msg:"no reason"); ic = (Ilcb*)s->ptcl; callout = ic->state == Ilsyncer; @@ -859,7 +871,7 @@ void iloutoforder(Conv *s, Ilhdr *h, Block *bp) { Ilcb *ic; - byte *lid; + uchar *lid; Block *f, **l; ulong id, newid; @@ -932,8 +944,8 @@ ilsendctl(Conv *ipc, Ilhdr *inih, int type, ulong id, ulong ack, int ilspec) ttl = MAXTTL; } else { - hnputl(ih->dst, ipc->raddr); - hnputl(ih->src, ipc->laddr); + v6tov4(ih->dst, ipc->raddr); + v6tov4(ih->src, ipc->laddr); hnputs(ih->ilsrc, ipc->lport); hnputs(ih->ildst, ipc->rport); hnputl(ih->ilid, id); @@ -1015,9 +1027,7 @@ loop: break; } if(ic->timeout >= ic->fasttime) { - ilsendctl(p, nil, Ilquery, ic->next, ic->recvd, ilnextqt(ic)); - ic->querytime = Querytime; - ic->timeout = 0; + ilrexmit(ic); ilbackoff(ic); } if(ic->timeout >= ic->slowtime) { @@ -1053,7 +1063,7 @@ ilstart(Conv *c, int type, int window) ic->unacked = nil; ic->outoforder = nil; - ic->unackedbytes = 0; + ic->unackeduchars = 0; ic->delay = Iltickms<mdev = Iltickms<rate = DefByteRate<timeout = 0; - pt = (ic->delay>>LogAGain) + ic->unackedbytes/(ic->rate>>LogAGain) + (ic->mdev>>(LogDGain-1)); - ic->fasttime = pt + Iltickms - 1; + pt = (ic->delay>>LogAGain) + ic->unackeduchars/(ic->rate>>LogAGain) + ic->mdev; + ic->fasttime = Acktime + pt + Iltickms - 1; if(ic->fasttime > Fasttime) ic->fasttime = Fasttime; ic->slowtime = (Slowtime/Seconds)*pt; @@ -1125,22 +1135,23 @@ iladvise(Block *bp, char *msg) { Ilhdr *h; Ilcb *ic; - Ipaddr source, dest; + uchar source[IPaddrlen], dest[IPaddrlen]; ushort psource; Conv *s, **p; h = (Ilhdr*)(bp->rp); - dest = nhgetl(h->dst); - source = nhgetl(h->src); + v4tov6(dest, h->dst); + v4tov6(source, h->src); psource = nhgets(h->ilsrc); /* Look for a connection, unfortunately the destination port is missing */ for(p = il.conv; *p; p++) { s = *p; - if(s->laddr == source && s->lport == psource) - if(s->raddr == dest){ + if(s->lport == psource) + if(ipcmp(s->laddr, source) == 0) + if(ipcmp(s->raddr, dest) == 0){ ic = (Ilcb*)s->ptcl; switch(ic->state){ case Ilsyncer: diff --git a/ip/ip.c b/ip/ip.c index 3c2e7cd04e5c803ae2f38e04347a0e1b89c5e846..2761a102eaa8223d32848283ceeb98cb5276c860 100644 --- a/ip/ip.c +++ b/ip/ip.c @@ -18,29 +18,29 @@ enum IP_HLEN = 0x05, /* Header length in characters */ IP_DF = 0x4000, /* Don't fragment */ IP_MF = 0x2000, /* More fragments */ - IP_MAX = (64*1024), /* Maximum Internet packet size */ + IP_MAX = (32*1024), /* Maximum Internet packet size */ }; struct Iphdr { - byte vihl; /* Version and header length */ - byte tos; /* Type of service */ - byte length[2]; /* packet length */ - byte id[2]; /* Identification */ - byte frag[2]; /* Fragment information */ - byte ttl; /* Time to live */ - byte proto; /* Protocol */ - byte cksum[2]; /* Header checksum */ - byte src[4]; /* Ip source */ - byte dst[4]; /* Ip destination */ + uchar vihl; /* Version and header length */ + uchar tos; /* Type of service */ + uchar length[2]; /* packet length */ + uchar id[2]; /* Identification */ + uchar frag[2]; /* Fragment information */ + uchar ttl; /* Time to live */ + uchar proto; /* Protocol */ + uchar cksum[2]; /* Header checksum */ + uchar src[4]; /* Ip source */ + uchar dst[4]; /* Ip destination */ }; struct Fragment { Block* blist; Fragment* next; - Ipaddr src; - Ipaddr dst; + ulong src; + ulong dst; ushort id; ulong age; }; @@ -57,8 +57,8 @@ Fragment* fragfree; ulong Id; int iprouting; /* true if we route like a gateway */ ulong ipcsumerr; -ulong ipin, ippin; /* bytes, packets in */ -ulong ipout, ippout; /* bytes, packets out */ +ulong ipin, ippin; /* uchars, packets in */ +ulong ipout, ippout; /* uchars, packets out */ #define BLKIP(xp) ((Iphdr*)((xp)->rp)) /* @@ -73,7 +73,7 @@ static struct Stats ulong droppedfrag; } stats; -ushort ipcsum(byte*); +ushort ipcsum(uchar*); Block* ipreassemble(int, Block*, Iphdr*); void ipfragfree(Fragment*); Fragment* ipfragallo(void); @@ -81,17 +81,18 @@ Fragment* ipfragallo(void); void ipoput(Block *bp, int gating, int ttl) { - Media *m; - byte gate[4]; + Ipifc *ifc; + uchar *gate; ushort fragoff; Block *xp, *nb; Iphdr *eh, *feh; int lid, len, seglen, chunk, dlen, blklen, offset, medialen; + Route *r; /* Fill out the ip header */ eh = (Iphdr *)(bp->rp); - /* Number of bytes in data and ip header to write */ + /* Number of uchars in data and ip header to write */ len = blocklen(bp); ipout += len; ippout++; @@ -104,30 +105,30 @@ ipoput(Block *bp, int gating, int ttl) if(chunk < len) len = chunk; } - if(len >= IP_MAX) { - netlog(Logip, "exceeded ip max size %I\n", eh->dst); + if(len >= IP_MAX){ + netlog(Logip, "exceeded ip max size %V\n", eh->dst); goto raise; } - if(isbmcast(eh->dst)){ - m = Mediaroute(eh->src, nil); - memmove(gate, eh->dst, IPaddrlen); - } else - m = Mediaroute(eh->dst, gate); - if(m == nil){ + r = v4lookup(eh->dst); + if(r == nil){ stats.noroute++; - netlog(Logip, "no interface %I\n", eh->dst); + netlog(Logip, "no interface %V\n", eh->dst); goto raise; } - + if(r->type & (Rifc|Runi|Rbcast|Rmulti)) + gate = eh->dst; + else + gate = r->v4.gate; if(!gating){ eh->vihl = IP_VER|IP_HLEN; eh->tos = 0; eh->ttl = ttl; } + ifc = r->ifc; /* If we dont need to fragment just send it */ - medialen = m->maxmtu-m->hsize; + medialen = ifc->m->maxmtu - ifc->m->hsize; if(len <= medialen) { if(!gating) hnputs(eh->id, Id++); @@ -138,18 +139,19 @@ ipoput(Block *bp, int gating, int ttl) eh->cksum[1] = 0; hnputs(eh->cksum, ipcsum(&eh->vihl)); - Mediawrite(m, bp, gate); +/*print("ipoput %V->%V via %V\n", eh->src, eh->dst, gate);*/ + ifc->m->bwrite(ifc, bp, V4, gate); return; } if(eh->frag[0] & (IP_DF>>8)){ - netlog(Logip, "%I: eh->frag[0] & (IP_DF>>8)\n", eh->dst); + netlog(Logip, "%V: eh->frag[0] & (IP_DF>>8)\n", eh->dst); goto raise; } seglen = (medialen - IPHDR) & ~7; if(seglen < 8){ - netlog(Logip, "%I seglen < 8\n", eh->dst); + netlog(Logip, "%V seglen < 8\n", eh->dst); goto raise; } @@ -206,7 +208,7 @@ ipoput(Block *bp, int gating, int ttl) feh->cksum[0] = 0; feh->cksum[1] = 0; hnputs(feh->cksum, ipcsum(&feh->vihl)); - Mediawrite(m, nb, gate); + ifc->m->bwrite(ifc, nb, V4, gate); } raise: @@ -218,7 +220,7 @@ initfrag(int size) { Fragment *fq, *eq; - fragfree = malloc(sizeof(Fragment) * size); + fragfree = (Fragment*)malloc(sizeof(Fragment) * size); if(fragfree == nil) panic("initfrag"); @@ -234,12 +236,13 @@ void (*ipextprotoiput)(Block*); //#define DBG(x) if((logmask & Logipmsg) && (iponly == 0 || x == iponly))netlog void -ipiput(Media *m, Block *bp) +ipiput(uchar *ia, Block *bp) { Iphdr *h; Proto *p; ushort frag; int notforme; + uchar v6dst[IPaddrlen]; // h = (Iphdr *)(bp->rp); // DBG(nhgetl(h->src))(Logipmsg, "ipiput %I %I len %d proto %d\n", h->src, h->dst, BLEN(bp), h->proto); @@ -252,14 +255,7 @@ ipiput(Media *m, Block *bp) } h = (Iphdr *)(bp->rp); - /* Look to see if its for me before we waste time checksumming it */ - notforme = Mediaforme(h->dst) == 0; - if(notforme && !iprouting) { - netlog(Logip, "ip: pkt not for me\n"); - freeblist(bp); - return; - } - + /* dump anything that whose header doesn't checksum */ if(ipcsum(&h->vihl)) { ipcsumerr++; netlog(Logip, "ip: checksum error %I\n", h->src); @@ -267,6 +263,21 @@ ipiput(Media *m, Block *bp) return; } + /* route */ + v4tov6(v6dst, h->dst); + notforme = ipforme(v6dst) == 0; + if(notforme) { + if(iprouting) { + /* gate */ + if(h->ttl <= 1) + freeblist(bp); + else + ipoput(bp, 1, h->ttl - 1); + } else + useriprouter(ia, bp); + return; + } + /* Check header length and version */ if(h->vihl != (IP_VER|IP_HLEN)) { netlog(Logip, "ip: %I bad hivl %ux\n", h->src, h->vihl); @@ -287,20 +298,9 @@ ipiput(Media *m, Block *bp) ipin += blocklen(bp); ippin++; - if(iprouting) { - /* gate */ - if(notforme){ - if(h->ttl == 0) - freeblist(bp); - else - ipoput(bp, 1, h->ttl - 1); - return; - } - } - p = Fsrcvpcol(&fs, h->proto); if(p != nil && p->rcv != nil) - (*p->rcv)(m, bp); + (*p->rcv)(ia, bp); else if(ipextprotoiput != nil) ipextprotoiput(bp); else @@ -325,7 +325,7 @@ ipreassemble(int offset, Block *bp, Iphdr *ip) int fend; ushort id; Fragment *f, *fnext; - Ipaddr src, dst; + ulong src, dst; Block *bl, **l, *last, *prev; int ovlap, len, fragsize, pktposn; @@ -532,7 +532,7 @@ ipfragallo(void) } ushort -ipcsum(byte *addr) +ipcsum(uchar *addr) { int len; ulong sum; diff --git a/ip/ip.h b/ip/ip.h index 15b6c90297b6d2f222dc4f2b8c399b3938e75be9..265926eb01bf5f54090933a8a5a17e5a7d59e864 100644 --- a/ip/ip.h +++ b/ip/ip.h @@ -1,15 +1,19 @@ -typedef ulong Ipaddr; -typedef uchar byte; typedef struct Conv Conv; typedef struct Fs Fs; typedef union Hwaddr Hwaddr; typedef struct Ifcconv Ifcconv; +typedef struct Ipself Ipself; +typedef struct Iplink Iplink; +typedef struct Iplifc Iplifc; +typedef struct Ipmulti Ipmulti; typedef struct Iproute Iproute; -typedef struct Media Media; -typedef struct Multicast Multicast; +typedef struct Ipifc Ipifc; +typedef struct Medium Medium; typedef struct Proto Proto; typedef struct Pstate Pstate; typedef struct Tcpc Tcpc; +typedef struct Arpent Arpent; +typedef struct Route Route; enum { @@ -18,14 +22,18 @@ enum Nhash= 64, Maxincall= 5, Nchans= 256, + MAClen= 16, /* longest mac address */ MAXTTL= 255, - IPaddrlen= 4, - Ipbcast= 0xffffffff, /* ip broadcast address */ - Ipbcastobs= 0, /* obsolete (but still used) ip broadcast addr */ - Ipallsys= 0xe0000001, /* multicast for all systems */ - Ipallrouter= 0xe0000002, /* multicast for all routers */ + IPaddrlen= 16, + IPv4addrlen= 4, + IPv4off= 12, + IPllen= 4, + + /* ip versions */ + V4= 4, + V6= 6, }; enum @@ -46,8 +54,8 @@ struct Conv int x; /* conversation index */ Proto* p; - Ipaddr laddr; /* local IP address */ - Ipaddr raddr; /* remote IP address */ + uchar laddr[IPaddrlen]; /* local IP address */ + uchar raddr[IPaddrlen]; /* remote IP address */ int restricted; /* remote port is restricted */ ushort lport; /* local port number */ ushort rport; /* remote port number */ @@ -77,86 +85,104 @@ struct Conv QLock listenq; Rendez listenr; + Ipmulti *multi; /* multicast bindings for this interface */ + void* ptcl; /* protocol specific stuff */ }; -union Hwaddr +struct Medium { - byte ether[6]; + char *name; + int hsize; /* medium header size */ + int minmtu; /* default min mtu */ + int maxmtu; /* default max mtu */ + int maclen; /* mac address length */ + void (*bind)(Ipifc*, int, char**); + void (*unbind)(Ipifc*); + void (*bwrite)(Ipifc *ifc, Block *b, int version, uchar *ip); + + /* for arming interfaces to receive multicast */ + void (*addmulti)(Ipifc *ifc, uchar *a, uchar *ia); + void (*remmulti)(Ipifc *ifc, uchar *a, uchar *ia); + + /* process packets written to 'data' */ + void (*pktin)(Ipifc *ifc, Block *bp); + + /* routes for router boards */ + void (*addroute)(Ipifc *ifc, int, uchar*, uchar*, uchar*, int); + void (*remroute)(Ipifc *ifc, int, uchar*, uchar*); + void (*flushroutes)(Ipifc *ifc); + + /* for routing multicast groups */ + void (*joinmulti)(Ipifc *ifc, uchar *a, uchar *ia, uchar **iap); + void (*leavemulti)(Ipifc *ifc, uchar *a, uchar *ia); }; -enum +/* logical interface associated with a physical one */ +struct Iplifc { - METHER, /* Media types */ - MFDDI, - MPACKET, + uchar local[IPaddrlen]; + uchar mask[IPaddrlen]; + uchar remote[IPaddrlen]; + uchar net[IPaddrlen]; + Iplink *link; /* addresses linked to this lifc */ + Iplifc *next; }; -/* one per multicast address per medium */ -struct Multicast +/* binding twixt Ipself and Ipifc */ +struct Iplink { - Ipaddr addr; + Ipself *local; + Iplifc *lifc; + Iplink *locallink; /* next link for this local address */ + Iplink *lifclink; /* next link for this ifc */ + ulong expire; + Iplink *next; /* free list */ int ref; - int timeout; - Multicast *next; }; -struct Media +struct Ipifc { - int type; /* Media type */ - Chan* mchan; /* Data channel */ - Chan* achan; /* Arp channel */ - Chan* cchan; /* Control channel */ - char* dev; /* device mfd points to */ - Ipaddr myip[5]; - Ipaddr mymask; - Ipaddr mynetmask; - Ipaddr remip; /* Address of remote side */ - byte netmyip[4]; /* In Network byte order */ - int arping; /* true if we mus arp */ + RWlock; + + Conv *conv; /* link to its conversation structure */ + char dev[64]; /* device we're attached to */ + Medium *m; /* Media pointer */ int maxmtu; /* Maximum transfer unit */ int minmtu; /* Minumum tranfer unit */ - int hsize; /* Media header size */ - Hwaddr; + void *arg; /* medium specific */ + + /* these are used so that we can unbind on the fly */ + Lock idlock; + uchar ifcid; /* incremented each 'bind/unbind/add/remove' */ + int ref; /* number of proc's using this ipifc */ + Rendez wait; /* where unbinder waits for ref == 0 */ + int unbinding; + + uchar mac[MAClen]; /* MAC address */ + + Iplifc *lifc; /* logical interfaces on this physical one */ + ulong in, out; /* message statistics */ ulong inerr, outerr; /* ... */ - int inuse; - Conv *c; /* for packet interface */ - - QLock mlock; /* lock for changing *multi */ - Multicast *multi; /* list of multicast addresses we're listening to */ - int mactive; /* number of active multicast addresses */ +}; - Media* link; +/* + * one per multicast-lifc pair used by a Conv + */ +struct Ipmulti +{ + uchar ma[IPaddrlen]; + uchar ia[IPaddrlen]; + Ipmulti *next; }; -int Mediabooting(void); -int Mediaforme(byte*); -int Mediaforpt2pt(byte*); -Ipaddr Mediagetsrc(byte*); -void Mediaclose(Media*); -char* Mediaopen(int, char*, Conv*, Ipaddr, Ipaddr, Ipaddr, int, Media**); -Media* Mediaroute(byte*, byte*); -void Mediasetaddr(Media*, Ipaddr, Ipaddr); -void Mediasetraddr(Media*, Ipaddr); -Ipaddr Mediagetaddr(Media*); -Ipaddr Mediagetraddr(Media*); -void Mediawrite(Media*, Block*, byte*); -int Mediaifcread(char*, ulong, int); -char* Mediaifcwrite(Ifcconv*, char*, int); -void Mediaresolver(Media*); -void Mediaread(Media*); -int Mediaarp(Media*, Block*, byte*, Hwaddr*); -Media* Mediafind(Iproute*); -Multicast* Mediacopymulti(Media*); -void Mediamulticastadd(Media*, Ifcconv*, Ipaddr); -void Mediamulticastrem(Media*, Ipaddr); /* * one per multiplexed protocol */ struct Proto { - Lock; + QLock; char* name; /* protocol name */ int x; /* protocol index */ int ipproto; /* ip protocol type */ @@ -164,13 +190,16 @@ struct Proto void (*kick)(Conv*, int); char* (*connect)(Conv*, char**, int); char* (*announce)(Conv*, char**, int); - int (*state)(char**, Conv*); + char* (*bind)(Conv*, char**, int); + int (*state)(Conv*, char*, int); void (*create)(Conv*); void (*close)(Conv*); - void (*rcv)(Media*, Block*); + void (*rcv)(uchar*, Block*); char* (*ctl)(Conv*, char**, int); void (*advise)(Block*, char*); int (*stats)(char*, int); + int (*local)(Conv*, char*, int); + int (*inuse)(Conv*); Conv **conv; /* array of conversations */ int ptclsize; /* size of per protocol ctl block */ @@ -196,16 +225,19 @@ struct Fs Proto* t2p[256]; /* vector of all ip protocol handlers */ }; int Fsconnected(Fs*, Conv*, char*); -Conv* Fsnewcall(Fs*, Conv*, Ipaddr, ushort, Ipaddr, ushort); +Conv* Fsnewcall(Fs*, Conv*, uchar*, ushort, uchar*, ushort); int Fspcolstats(char*, int); int Fsproto(Fs*, Proto*); -int Fsbuiltinproto(Fs*, byte); +int Fsbuiltinproto(Fs*, uchar); Conv* Fsprotoclone(Proto*, char*); -Proto* Fsrcvpcol(Fs*, byte); +Proto* Fsrcvpcol(Fs*, uchar); char* Fsstdconnect(Conv*, char**, int); char* Fsstdannounce(Conv*, char**, int); +char* Fsstdbind(Conv*, char**, int); -/* log flags */ +/* + * logging + */ enum { Logip= 1<<1, @@ -225,7 +257,8 @@ enum }; extern int logmask; /* mask of things to debug */ -extern Ipaddr iponly; /* ip address to print debugging for */ +extern uchar iponly[IPaddrlen]; /* ip address to print debugging for */ +extern int iponlyset; void netlogopen(void); void netlogclose(void); @@ -233,51 +266,200 @@ char* netlogctl(char*, int); long netlogread(void*, ulong, long); void netlog(int, char*, ...); -#define msec TK2MS(MACHP(0)->ticks) +/* + * iproute.c + */ +typedef struct RouteTree RouteTree; +typedef struct Routewalk Routewalk; +typedef struct V4route V4route; +typedef struct V6route V6route; -/* Globals */ -extern int debug; -extern Fs fs; -extern Media* media; -extern int iprouting; /* true if routing turned on */ -extern void (*igmpreportfn)(Media*, byte*); - -int arpread(byte*, ulong, int); -char* arpwrite(char*, int); -void closeifcconv(Ifcconv*); -Ipaddr defmask(Ipaddr); -int eipconv(va_list*, Fconv*); -int equivip(byte*, byte*); -void fatal(byte*, ...); -void hnputl(byte*, ulong); -void hnputs(byte*, ushort); -void icmpnoconv(Block*); -void initfrag(int); -ushort ipcsum(byte*); -void (*ipextprotoiput)(Block*); -Ipaddr ipgetsrc(byte*); -void ipiput(Media*, Block*); -void ipoput(Block*, int, int); -int ipstats(char*, int); -int ismcast(byte*); -int isbmcast(byte*); -byte* logctl(byte*); -void maskip(byte*, byte*, byte*); -Ifcconv* newifcconv(void); -ulong nhgetl(byte*); -ushort nhgets(byte*); -void (*pktifcrcv)(Conv*, Block*); -ushort ptclbsum(byte*, int); -ushort ptclcsum(Block*, int, int); -int pullblock(Block**, int); -Block* pullupblock(Block*, int); -char* routeadd(Ipaddr, Ipaddr, Ipaddr, Media *m); -void routedelete(ulong, ulong, Media *m); -int routeread(byte*, ulong, int); -char* routewrite(char*, int); +enum +{ + + /* type bits */ + Rv4= (1<<0), /* this is a version 4 route */ + Rifc= (1<<1), /* this route is a directly connected interface */ + Rptpt= (1<<2), /* this route is a pt to pt interface */ + Runi= (1<<3), /* a unicast self address */ + Rbcast= (1<<4), /* a broadcast self address */ + Rmulti= (1<<5), /* a multicast self address */ +}; + +struct Routewalk { + int n; + int o; + int h; + char* p; + void* state; + void (*walk)(Route*, Routewalk*); +}; + +struct RouteTree +{ + Route* right; + Route* left; + Route* mid; + uchar depth; + uchar type; + uchar ifcid; /* must match ifc->id */ + Ipifc *ifc; + char tag[4]; +}; + +struct V4route +{ + ulong address; + ulong endaddress; + uchar gate[IPv4addrlen]; +}; + +struct V6route +{ + ulong address[IPllen]; + ulong endaddress[IPllen]; + uchar gate[IPaddrlen]; +}; + +struct Route +{ + RouteTree; + + union { + V6route v6; + V4route v4; + }; +}; +extern void v4addroute(char *tag, uchar *a, uchar *mask, uchar *gate, int type); +extern void v6addroute(char *tag, uchar *a, uchar *mask, uchar *gate, int type); +extern void v4delroute(uchar *a, uchar *mask); +extern void v6delroute(uchar *a, uchar *mask); +extern Route* v4lookup(uchar *a); +extern Route* v6lookup(uchar *a); +extern long routeread(char*, ulong, int); +extern long routewrite(Chan*, char*, int); + +/* + * arp.c + */ +struct Arpent +{ + uchar ip[IPaddrlen]; + uchar mac[MAClen]; + Medium *type; /* media type */ + Arpent* hash; + Block* hold; + Block* last; + uint time; + uint used; + uchar state; +}; + +extern int arpread(char*, ulong, int); +extern int arpwrite(char*, int); +extern Arpent* arpget(Block *bp, int version, Medium *type, uchar *ip, uchar *h); +extern void arprelease(Arpent *a); +extern Block* arpresolve(Arpent *a, Medium *type, uchar *mac); +extern void arpenter(Ipifc *ifc, int version, uchar *ip, uchar *mac, Medium *type, int norefresh); /* * ipaux.c */ -int myetheraddr(uchar*, char*); -ulong parseip(uchar*, char*); + +typedef struct Cmdbuf Cmdbuf; +struct Cmdbuf +{ + char buf[64]; + char *f[16]; + int nf; +}; + +extern int myetheraddr(uchar*, char*); +extern ulong parseip(uchar*, char*); +extern ulong parseipmask(uchar*, char*); +extern void maskip(uchar *from, uchar *mask, uchar *to); +extern int parsemac(uchar *to, char *from, int len); +extern uchar* defmask(uchar*); +extern int isv4(uchar*); +extern void v4tov6(uchar *v6, uchar *v4); +extern int v6tov4(uchar *v4, uchar *v6); +extern Cmdbuf* parsecmd(char *a, int n); +extern int eipconv(va_list *arg, Fconv *f); + +#define ipcmp(x, y) memcmp(x, y, IPaddrlen) +#define ipmove(x, y) memmove(x, y, IPaddrlen) + +extern uchar IPv4bcast[IPaddrlen]; +extern uchar IPv4bcastobs[IPaddrlen]; +extern uchar IPv4allsys[IPaddrlen]; +extern uchar IPv4allrouter[IPaddrlen]; +extern uchar IPnoaddr[IPaddrlen]; +extern uchar v4prefix[IPaddrlen]; +extern uchar IPallbits[IPaddrlen]; + +#define msec TK2MS(MACHP(0)->ticks) + +/* + * media + */ +extern Medium ethermedium; +extern Medium nullmedium; +extern Medium pktmedium; +extern Proto ipifc; + +/* + * ipifc.c + */ +extern Medium* ipfindmedium(char *name); +extern int ipforme(uchar *addr); +extern int ipismulticast(uchar *); +extern Ipifc* findipifc(uchar *remote, int type); +extern void findlocalip(uchar *local, uchar *remote); +extern int ipv4local(Ipifc *ifc, uchar *addr); +extern int ipv6local(Ipifc *ifc, uchar *addr); +extern Iplifc* iplocalonifc(Ipifc *ifc, uchar *ip); +extern int ipproxyifc(Ipifc *ifc, uchar *ip); +extern int ipismulticast(uchar *ip); +extern int ipisbooting(void); +extern int ipifccheckin(Ipifc *ifc, Medium *med); +extern void ipifccheckout(Ipifc *ifc); +extern int ipifcgrab(Ipifc *ifc); +extern void ipifcaddroute(int, uchar*, uchar*, uchar*, int); +extern void ipifcremroute(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); + +/* + * ip.c + */ +extern void closeifcconv(Ifcconv*); +extern void icmpnoconv(Block*); +extern void initfrag(int); +extern ushort ipcsum(uchar*); +extern void (*ipextprotoiput)(Block*); +extern void ipiput(uchar*, Block*); +extern void ipoput(Block*, int, int); +extern int ipstats(char*, int); +extern uchar* logctl(uchar*); +extern Ifcconv* newifcconv(void); +extern void (*pktifcrcv)(Conv*, Block*); +extern ushort ptclbsum(uchar*, int); +extern ushort ptclcsum(Block*, int, int); + +/* + * iprouter.c + */ +void useriprouter(uchar*, Block*); +void iprouteropen(void); +void iprouterclose(void); +long iprouterread(void*, int); + +/* + * global to all of the stack + */ +extern int debug; +extern Fs fs; +extern int iprouting; /* true if routing turned on */ +extern void (*igmpreportfn)(Ipifc*, uchar*); diff --git a/ip/ipaux.c b/ip/ipaux.c index 33a22d39841bf78d2a181ef89f94cfa64da006b6..e3e06b81d8066f49b59b8bd4ff95a82194d65deb 100644 --- a/ip/ipaux.c +++ b/ip/ipaux.c @@ -7,10 +7,50 @@ #include "kernel.h" #include "ip.h" +/* + * well known IP addresses + */ +uchar IPv4bcast[IPaddrlen] = { + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff +}; +uchar IPv4allsys[IPaddrlen] = { + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0xff, 0xff, + 0xe0, 0, 0, 0x01 +}; +uchar IPv4allrouter[IPaddrlen] = { + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0xff, 0xff, + 0xe0, 0, 0, 0x02 +}; +uchar IPallbits[IPaddrlen] = { + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff +}; +uchar IPnoaddr[IPaddrlen]; + +/* + * prefix of all v4 addresses + */ +uchar v4prefix[IPaddrlen] = { + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0xff, 0xff, + 0, 0, 0, 0 +}; + + ushort ptclcsum(Block *bp, int offset, int len) { - byte *addr; + uchar *addr; ulong losum, hisum; ushort csum; int odd, blocklen, x; @@ -64,65 +104,34 @@ ptclcsum(Block *bp, int offset, int len) return ~losum & 0xffff; } -Ipaddr -defmask(Ipaddr ip) +enum { - switch(ip>>30){ - default: - return 0xff000000; - case 2: - return 0xffff0000; - case 3: - return 0xffffff00; - } -} + Isprefix= 16, +}; -int -myetheraddr(uchar *to, char *dev) +uchar prefixvals[256] = { - int n, fd; - char buf[256], *ptr; - - /* Make one exist */ - if(*dev == '/' || *dev == '#') - sprint(buf, "%s/clone", dev); - else - sprint(buf, "/net/%s/clone", dev); - - fd = kopen(buf, ORDWR); - if(fd >= 0) - kclose(fd); - - if(*dev == '/' || *dev == '#') - sprint(buf, "%s/0/stats", dev); - else - sprint(buf, "/net/%s/0/stats", dev); - fd = kopen(buf, OREAD); - if(fd < 0) - return -1; - - n = kread(fd, buf, sizeof(buf)-1); - kclose(fd); - if(n <= 0) - return -1; - buf[n] = 0; - - ptr = strstr(buf, "addr: "); - if(!ptr) - return -1; - ptr += 6; - - parseether(to, ptr); - return 0; -} +[0x00] 0 | Isprefix, +[0x80] 1 | Isprefix, +[0xC0] 2 | Isprefix, +[0xE0] 3 | Isprefix, +[0xF0] 4 | Isprefix, +[0xF8] 5 | Isprefix, +[0xFC] 6 | Isprefix, +[0xFE] 7 | Isprefix, +[0xFF] 8 | Isprefix, +}; int eipconv(va_list *arg, Fconv *f) { - char buf[64]; + char buf[8*5]; static char *efmt = "%.2lux%.2lux%.2lux%.2lux%.2lux%.2lux"; static char *ifmt = "%d.%d.%d.%d"; - uchar *p, ip[4]; + uchar *p, ip[16]; + ulong *lp; + ushort s; + int i, j, n, eln, eli; switch(f->chr) { case 'E': /* Ethernet address */ @@ -131,11 +140,66 @@ eipconv(va_list *arg, Fconv *f) break; case 'I': /* Ip address */ p = va_arg(*arg, uchar*); +common: + if(memcmp(p, v4prefix, 12) == 0) + sprint(buf, ifmt, p[12], p[13], p[14], p[15]); + else { + /* find longest elision */ + eln = eli = -1; + for(i = 0; i < 16; i += 2){ + for(j = i; j < 16; j += 2) + if(p[j] != 0 || p[j+1] != 0) + break; + if(j > i && j - i > eln){ + eli = i; + eln = j - i; + } + } + + /* print with possible elision */ + n = 0; + for(i = 0; i < 16; i += 2){ + if(i == eli){ + n += sprint(buf+n, "::"); + i += eln; + if(i >= 16) + break; + } else if(i != 0) + n += sprint(buf+n, ":"); + s = (p[i]<<8) + p[i+1]; + n += sprint(buf+n, "%ux", s); + } + } + break; + case 'i': /* v6 address as 4 longs */ + lp = va_arg(*arg, ulong*); + for(i = 0; i < 4; i++) + hnputl(ip+4*i, *lp++); + p = ip; + goto common; + case 'V': /* v4 ip address */ + p = va_arg(*arg, uchar*); sprint(buf, ifmt, p[0], p[1], p[2], p[3]); break; - case 'i': - hnputl(ip, va_arg(*arg, ulong)); - sprint(buf, ifmt, ip[0], ip[1], ip[2], ip[3]); + case 'M': /* ip mask */ + p = va_arg(*arg, uchar*); + + /* look for a prefix mask */ + for(i = 0; i < 16; i++) + if(p[i] != 0xff) + break; + if(i < 16){ + if((prefixvals[p[i]] & Isprefix) == 0) + goto common; + for(j = i+1; j < 16; j++) + if(p[j] != 0) + goto common; + n = 8*i + (prefixvals[p[i]] & ~Isprefix); + } else + n = 8*16; + + /* got one, use /xx format */ + sprint(buf, "/%d", n); break; default: strcpy(buf, "(eipconv)"); @@ -146,21 +210,20 @@ eipconv(va_list *arg, Fconv *f) #define CLASS(p) ((*(uchar*)(p))>>6) -ulong -parseip(uchar *to, char *from) +static char* +v4parseip(uchar *to, char *from) { int i; char *p; p = from; - memset(to, 0, 4); for(i = 0; i < 4 && *p; i++){ to[i] = strtoul(p, &p, 0); if(*p == '.') p++; } switch(CLASS(to)){ - case 0: /* class A - 1 byte net */ + case 0: /* class A - 1 uchar net */ case 1: if(i == 3){ to[3] = to[2]; @@ -171,12 +234,172 @@ parseip(uchar *to, char *from) to[1] = 0; } break; - case 2: /* class B - 2 byte net */ + case 2: /* class B - 2 uchar net */ if(i == 3){ to[3] = to[2]; to[2] = 0; } break; } - return nhgetl(to); + return p; +} + +int +isv4(uchar *ip) +{ + return memcmp(ip, v4prefix, IPv4off) == 0; +} + +void +v4tov6(uchar *v6, uchar *v4) +{ + memmove(v6, v4prefix, IPv4off); + memmove(v6 + IPv4off, v4, IPv4addrlen); +} + +int +v6tov4(uchar *v4, uchar *v6) +{ + if(memcmp(v6, v4prefix, IPv4off) != 0) + return -1; + memmove(v4, v6 + IPv4off, IPv4addrlen); + return 0; +} + +ulong +parseip(uchar *to, char *from) +{ + int i, elipsis = 0, v4 = 1; + ulong x; + char *p, *op; + + memset(to, 0, IPaddrlen); + p = from; + for(i = 0; i < 16 && *p; i+=2){ + op = p; + x = strtoul(p, &p, 16); + if(*p == '.' || (*p == 0 && i == 0)){ + p = v4parseip(to+i, op); + i += 2; + } else { + to[i] = x>>8; + to[i+1] = x; + } + if(*p == ':'){ + v4 = 0; + if(*++p == ':'){ + elipsis = i+2; + p++; + } + } + } + if(i < 16){ + memmove(&to[elipsis+16-i], &to[elipsis], i-elipsis); + memset(&to[elipsis], 0, 16-i); + } + if(v4){ + to[10] = to[11] = 0xff; + return nhgetl(to+12); + } else + return 6; +} + +/* + * hack to allow ip v4 masks to be entered in the old + * style + */ +ulong +parseipmask(uchar *to, char *from) +{ + ulong x; + int i; + uchar *p; + + if(*from == '/'){ + /* as a number of prefix bits */ + i = atoi(from+1); + if(i < 0) + i = 0; + if(i > 128) + i = 128; + memset(to, 0, IPaddrlen); + for(p = to; i >= 8; i -= 8) + *p++ = 0xff; + if(i > 0) + *p = ~((1<<(8-i))-1); + x = nhgetl(to+IPv4off); + } else { + /* as a straight bit mask */ + x = parseip(to, from); + if(memcmp(to, v4prefix, IPv4off) == 0) + memset(to, 0xff, IPv4off); + } + return x; +} + +void +maskip(uchar *from, uchar *mask, uchar *to) +{ + int i; + + for(i = 0; i < IPaddrlen; i++) + to[i] = from[i] & mask[i]; +} + +uchar classmask[4][16] = { + 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0x00,0x00,0x00, + 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0x00,0x00,0x00, + 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0x00,0x00, + 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0x00, +}; + +uchar* +defmask(uchar *ip) +{ + return classmask[ip[IPv4off]>>6]; +} + +/* + * parse a command written to a device + */ +Cmdbuf* +parsecmd(char *p, int n) +{ + Cmdbuf *cb; + + cb = smalloc(sizeof(*cb)); + + if(n > sizeof(cb->buf)-1) + n = sizeof(cb->buf)-1; + memmove(cb->buf, p, n); + cb->buf[n] = '\0'; + cb->nf = parsefields(cb->buf, cb->f, nelem(cb->f), " "); + return cb; +} + +/* + * parse a hex mac address + */ +int +parsemac(uchar *to, char *from, int len) +{ + char nip[4]; + char *p; + int i; + + p = from; + memset(to, 0, len); + for(i = 0; i < len; i++){ + if(*p == 0) + return -1; + nip[0] = *p++; + if(*p == 0) + return -1; + nip[1] = *p++; + nip[2] = 0; + to[i] = strtoul(nip, 0, 16); + if(*p == ':') + p++; + } + return 0; } diff --git a/ip/ipifc.c b/ip/ipifc.c new file mode 100644 index 0000000000000000000000000000000000000000..7a694dc1f2e51cc1bd318362ba5faeccff69075d --- /dev/null +++ b/ip/ipifc.c @@ -0,0 +1,1156 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include "ip.h" + +#define DPRINT if(0)print + +enum { + Maxmedia = 16, + Nself = Maxmedia*5, + NHASH = (1<<6), + NCACHE = 256, + QMAX = 64*1024-1, +}; + + Proto ipifc; +extern Fs fs; + +Medium *media[] = +{ + ðermedium, + &nullmedium, + 0 +}; + +/* + * cache of local addresses (addresses we answer to) + */ +typedef struct Ipself Ipself; +struct Ipself +{ + uchar a[IPaddrlen]; + Ipself *hnext; /* next address in the hash table */ + Iplink *link; /* binding twixt Ipself and Ipifc */ + ulong expire; + uchar type; /* type of address */ + int ref; + Ipself *next; /* free list */ +}; + +typedef struct Ipselftab Ipselftab; +struct Ipselftab +{ + QLock; + int inited; + int acceptall; /* true if an interface has the null address */ + Ipself *hash[NHASH]; /* hash chains */ +}; +Ipselftab selftab; + +/* + * Multicast addresses are chained onto a Chan so that + * we can remove them when the Chan is closed. + */ +typedef struct Ipmcast Ipmcast; +struct Ipmcast +{ + Ipmcast *next; + uchar ma[IPaddrlen]; /* multicast address */ + uchar ia[IPaddrlen]; /* interface address */ +}; + +/* quick hash for ip addresses */ +#define hashipa(a) ( ( ((a)[IPaddrlen-2]<<8) | (a)[IPaddrlen-1] )%NHASH ) + +static char tifc[] = "ifc "; + +static void addselfcache(Ipifc *ifc, Iplifc *lifc, uchar *a, int type); +static void remselfcache(Ipifc *ifc, Iplifc *lifc, uchar *a); +static char* ipifcjoinmulti(Ipifc *ifc, char **argv, int argc); +static char* ipifcleavemulti(Ipifc *ifc, char **argv, int argc); + +/* + * find the medium with this name + */ +Medium* +ipfindmedium(char *name) +{ + Medium **mp; + + for(mp = media; *mp != nil; mp++) + if(strcmp((*mp)->name, name) == 0) + break; + return *mp; +} + +/* + * attach a device (or pkt driver) to the interface. + * called with c->car locked + */ +static char* +ipifcbind(Conv *c, char **argv, int argc) +{ + Ipifc *ifc; + Medium *m; + + if(argc < 2) + return Ebadarg; + + ifc = (Ipifc*)c->ptcl; + + /* bind the device to the interface */ + m = ipfindmedium(argv[1]); + if(m == nil) + return "unknown interface type"; + + wlock(ifc); + if(ifc->m != nil){ + wunlock(ifc); + return "interface already bound"; + } + if(waserror()){ + wunlock(ifc); + nexterror(); + } + + (*m->bind)(ifc, argc, argv); + if(argc > 2) + strncpy(ifc->dev, argv[2], sizeof(ifc->dev)); + else + sprint(ifc->dev, "%s%d", m->name, c->x); + ifc->dev[sizeof(ifc->dev)-1] = 0; + ifc->m = m; + ifc->minmtu = ifc->m->minmtu; + ifc->maxmtu = ifc->m->maxmtu; + ifc->ifcid++; + + wunlock(ifc); + poperror(); + + return nil; +} + +/* + * detach a device from an interface, close the interface + */ +static char* +ipifcunbind(Ipifc *ifc) +{ + char *av[4]; + char ip[32]; + char mask[32]; + + if(waserror()){ + wunlock(ifc); + nexterror(); + } + wlock(ifc); + + if(ipifcgrab(ifc) == 0); + goto out; + + /* hangup queues to stop queuing of packets */ + qhangup(ifc->conv->rq, "unbind"); + qhangup(ifc->conv->wq, "unbind"); + + /* dissociate routes */ + ifc->ifcid++; + + /* disassociate logical interfaces */ + av[0] = "remove"; + av[1] = ip; + av[2] = mask; + av[3] = 0; + while(ifc->lifc){ + sprint(ip, "%I", ifc->lifc->local); + sprint(mask, "%M", ifc->lifc->mask); + ipifcrem(ifc, av, 3, 0); + } + + /* disassociate device */ + (*ifc->m->unbind)(ifc); + memset(ifc->dev, 0, sizeof(ifc->dev)); + ifc->arg = nil; + ifc->m = &nullmedium; + ifc->unbinding = 0; + +out: + wunlock(ifc); + poperror(); + return nil; +} + +static int +ipifcstate(Conv *c, char *state, int n) +{ + Ipifc *ifc; + Iplifc *lifc; + int m; + + ifc = (Ipifc*)c->ptcl; + + m = snprint(state, n, "%-12.12s %-5d", ifc->dev, ifc->maxmtu); + + rlock(ifc); + for(lifc = ifc->lifc; lifc; lifc = lifc->next) + m += snprint(state+m, n - m, + " %-20.20I %-20.20M %-20.20I %-7d %-7d %-7d %-7d", + lifc->local, lifc->mask, lifc->remote, + ifc->in, ifc->out, ifc->inerr, ifc->outerr); + m += snprint(state+m, n - m, "\n"); + runlock(ifc); + return m; +} + +static int +ipifclocal(Conv *c, char *state, int n) +{ + Ipifc *ifc; + Iplifc *lifc; + Iplink *link; + int m; + + ifc = (Ipifc*)c->ptcl; + + m = 0; + + rlock(ifc); + for(lifc = ifc->lifc; lifc; lifc = lifc->next){ + m += snprint(state+m, n - m, "%-20.20I ->", lifc->local); + for(link = lifc->link; link; link = link->lifclink) + m += snprint(state+m, n - m, " %-20.20I", link->local->a); + m += snprint(state+m, n - m, "\n"); + } + runlock(ifc); + return m; +} + +static int +ipifcinuse(Conv *c) +{ + Ipifc *ifc; + + ifc = (Ipifc*)c->ptcl; + return ifc->m != nil; +} + +/* + * called when a process writes to an interface's 'data' + */ +static void +ipifckick(Conv *c, int) +{ + Block *bp; + Ipifc *ifc; + + bp = qget(c->wq); + if(bp == nil) + return; + + ifc = (Ipifc*)c->ptcl; + if(ifc->m == nil || ifc->m->pktin == nil) + freeb(bp); + else + (*ifc->m->pktin)(ifc, bp); +} + +/* + * we'll have to have a kick routine at + * some point to deal with these + */ +static void +ipifccreate(Conv *c) +{ + Ipifc *ifc; + + c->rq = qopen(QMAX, 0, 0, 0); + c->wq = qopen(QMAX, 0, 0, 0); + ifc = (Ipifc*)c->ptcl; + ifc->conv = c; +} + +/* + * called after last close of ipifc data or ctl + * called with c locked, we must unlock + */ +static void +ipifcclose(Conv *c) +{ + /* + * nothing to do since conversation stays open + * till the device is unbound. + */ + unlock(c); +} + +/* + * add an address to an interface. + */ +char* +ipifcadd(Ipifc *ifc, char **argv, int argc) +{ + uchar ip[IPaddrlen], mask[IPaddrlen], rem[IPaddrlen]; + uchar bcast[IPaddrlen], net[IPaddrlen]; + Iplifc *lifc, **l; + int i, type, mtu; + + memset(ip, 0, IPaddrlen); + memset(mask, 0, IPaddrlen); + memset(rem, 0, IPaddrlen); + switch(argc){ + case 5: + mtu = strtoul(argv[4], 0, 0); + if(mtu >= ifc->m->minmtu && mtu <= ifc->m->maxmtu) + ifc->maxmtu = mtu; + /* fall through */ + case 4: + parseip(ip, argv[1]); + parseipmask(mask, argv[2]); + parseip(rem, argv[3]); + maskip(rem, mask, net); + break; + case 3: + parseip(ip, argv[1]); + parseipmask(mask, argv[2]); + maskip(ip, mask, rem); + maskip(rem, mask, net); + break; + case 2: + parseip(ip, argv[1]); + memmove(mask, defmask(ip), IPaddrlen); + maskip(ip, mask, rem); + maskip(rem, mask, net); + break; + default: + return Ebadarg; + break; + } + + if(waserror()){ + wunlock(ifc); + panic("ipifcadd"); + } + 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) + goto out; + + /* add the address to the list of logical ifc's for this ifc */ + lifc = smalloc(sizeof(Iplifc)); + ipmove(lifc->local, ip); + ipmove(lifc->mask, mask); + ipmove(lifc->remote, rem); + ipmove(lifc->net, net); + lifc->next = nil; + for(l = &ifc->lifc; *l; l = &(*l)->next) + ; + *l = lifc; + + /* add a route for the local network */ + type = Rifc; + if(ipcmp(mask, IPallbits) == 0) + type |= Rptpt; + if(isv4(ip)) + v4addroute(tifc, rem+IPv4off, mask+IPv4off, ip+IPv4off, type); + else + v6addroute(tifc, ip, mask, rem, type); + + addselfcache(ifc, lifc, ip, Runi); + + /* add subnet directed broadcast addresses to the self cache */ + for(i = 0; i < IPaddrlen; i++) + bcast[i] = (ip[i] & mask[i]) | ~mask[i]; + addselfcache(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(ifc, lifc, bcast, Rbcast); + + addselfcache(ifc, lifc, IPv4bcast, Rbcast); + +out: + wunlock(ifc); + poperror(); + return nil; +} + +/* + * remove an address from an interface. + * called with c->car locked + */ +char* +ipifcrem(Ipifc *ifc, char **argv, int argc, int dolock) +{ + uchar ip[IPaddrlen]; + uchar mask[IPaddrlen]; + Iplifc *lifc, **l; + + if(argc < 3) + return Ebadarg; + + parseip(ip, argv[1]); + parseipmask(mask, argv[2]); + + if(dolock){ + if(waserror()){ + wunlock(ifc); + nexterror(); + } + wlock(ifc); + } + + /* find address on this interface and remove from chain */ + lifc = nil; + for(l = &ifc->lifc; *l; l = &(*l)->next) + if(memcmp(ip, (*l)->local, IPaddrlen) == 0) + if(memcmp(mask, (*l)->mask, IPaddrlen) == 0){ + lifc = *l; + *l = lifc->next; + break; + } + + if(lifc == nil) + return "address not on this interface"; + + /* disassociate any addresses */ + while(lifc->link) + remselfcache(ifc, lifc, lifc->link->local->a); + + /* remove the route for this logical interface */ + if(isv4(ip)) + v4delroute(lifc->remote+IPv4off, lifc->mask+IPv4off); + else + v6delroute(lifc->remote, lifc->mask); + + free(lifc); + +out: + if(dolock){ + wunlock(ifc); + poperror(); + } + return nil; +} + +/* + * distrbute routes to active interfaces like the + * TRIP linecards + */ +void +ipifcaddroute(int vers, uchar *addr, uchar *mask, uchar *gate, int type) +{ + Medium *m; + Conv **cp; + Ipifc *ifc; + + for(cp = ipifc.conv; cp < &ipifc.conv[ipifc.nc]; cp++){ + if(*cp != nil) { + ifc = (Ipifc*)(*cp)->ptcl; + m = ifc->m; + if(m->addroute != nil) + m->addroute(ifc, vers, addr, mask, gate, type); + } + } +} + +void +ipifcremroute(int vers, uchar *addr, uchar *mask) +{ + Medium *m; + Conv **cp; + Ipifc *ifc; + + for(cp = ipifc.conv; cp < &ipifc.conv[ipifc.nc]; cp++){ + if(*cp != nil) { + ifc = (Ipifc*)(*cp)->ptcl; + m = ifc->m; + if(m->remroute != nil) + m->remroute(ifc, vers, addr, mask); + } + } +} + +/* + * associate an address with the interface. This wipes out any previous + * addresses. This is a macro that means, remove all the old interfaces + * and add a new one. + */ +static char* +ipifcconnect(Conv* c, char **argv, int argc) +{ + char *err; + Ipifc *ifc; + char *av[4]; + char ip[80], mask[80]; + + ifc = (Ipifc*)c->ptcl; + + if(ifc->m == nil) + return "ipifc not yet bound to device"; + + av[0] = "remove"; + av[1] = ip; + av[2] = mask; + av[3] = 0; + if(waserror()){ + wunlock(ifc); + nexterror(); + } + wlock(ifc); + while(ifc->lifc){ + sprint(ip, "%I", ifc->lifc->local); + sprint(mask, "%I", ifc->lifc->mask); + ipifcrem(ifc, av, 3, 0); + } + wunlock(ifc); + poperror(); + + err = ipifcadd(ifc, argv, argc); + if(err) + return err; + + Fsconnected(&fs, c, nil); + + return nil; +} + +/* + * non-standard control messages. + * called with c->car locked. + */ +static char* +ipifcctl(Conv* c, char**argv, int argc) +{ + Ipifc *ifc; + + ifc = (Ipifc*)c->ptcl; + if(strcmp(argv[0], "add") == 0) + return ipifcadd(ifc, argv, argc); + else if(strcmp(argv[0], "remove") == 0) + return ipifcrem(ifc, argv, argc, 1); + else if(strcmp(argv[0], "unbind") == 0) + return ipifcunbind(ifc); + else if(strcmp(argv[0], "joinmulti") == 0) + return ipifcjoinmulti(ifc, argv, argc); + else if(strcmp(argv[0], "leavemulti") == 0) + return ipifcleavemulti(ifc, argv, argc); + return "unsupported ctl"; +} + +void +ipifcinit(Fs *fs) +{ + ipifc.name = "ipifc"; + ipifc.kick = ipifckick; + ipifc.connect = ipifcconnect; + ipifc.announce = nil; + ipifc.bind = ipifcbind; + ipifc.state = ipifcstate; + ipifc.create = ipifccreate; + ipifc.close = ipifcclose; + ipifc.rcv = nil; + ipifc.ctl = ipifcctl; + ipifc.advise = nil; + ipifc.stats = ipstats; + ipifc.inuse = ipifcinuse; + ipifc.local = ipifclocal; + ipifc.ipproto = -1; + ipifc.nc = Maxmedia; + ipifc.ptclsize = sizeof(Ipifc); + + Fsproto(fs, &ipifc); +} + +/* + * add to self routing cache + * called with c->car locked + */ +static void +addselfcache(Ipifc *ifc, Iplifc *lifc, uchar *a, int type) +{ + Ipself *p; + Iplink *lp; + int h; + + qlock(&selftab); + + /* see if the address already exists */ + h = hashipa(a); + for(p = selftab.hash[h]; p; p = p->next) + if(memcmp(a, p->a, IPaddrlen) == 0) + break; + + /* allocate a local address and add to hash chain */ + if(p == nil){ + p = smalloc(sizeof(*p)); + ipmove(p->a, a); + p->type = type; + p->next = selftab.hash[h]; + selftab.hash[h] = p; + + /* if the null address, accept all packets */ + if(ipcmp(a, v4prefix) == 0 || ipcmp(a, IPnoaddr) == 0) + selftab.acceptall = 1; + } + + /* look for a link for this lifc */ + for(lp = p->link; lp; lp = lp->locallink) + if(lp->lifc == lifc) + break; + + /* allocate a lifc-to-local link and link to both */ + if(lp == nil){ + lp = smalloc(sizeof(*lp)); + lp->ref = 1; + lp->lifc = lifc; + lp->local = p; + lp->locallink = p->link; + p->link = lp; + lp->lifclink = lifc->link; + lifc->link = lp; + + /* add to routing table */ + if(isv4(a)) + v4addroute(tifc, a+IPv4off, IPallbits+IPv4off, a+IPv4off, type); + else + v6addroute(tifc, a, IPallbits, a, type); + + if((type & Rmulti) && ifc->m->addmulti != nil) + (*ifc->m->addmulti)(ifc, a, lifc->local); + } else { + lp->ref++; + } + + qunlock(&selftab); +} + +/* + * These structures are unlinked from their chains while + * other threads may be using them. To avoid excessive locking, + * just put them aside for a while before freeing them. + * called with &selftab locked + */ +static Iplink *freeiplink; +static Ipself *freeipself; + +static void +iplinkfree(Iplink *p) +{ + Iplink **l, *np; + ulong now = msec; + + l = &freeiplink; + for(np = *l; np; np = *l){ + if(np->expire > now){ + *l = np->next; + free(np); + continue; + } + l = &np->next; + } + p->expire = now + 5000; /* give other threads 5 secs to get out */ + p->next = nil; + *l = p; +} +static void +ipselffree(Ipself *p) +{ + Ipself **l, *np; + ulong now = msec; + + l = &freeipself; + for(np = *l; np; np = *l){ + if(np->expire > now){ + *l = np->next; + free(np); + continue; + } + l = &np->next; + } + p->expire = now + 5000; /* give other threads 5 secs to get out */ + p->next = nil; + *l = p; +} + +/* + * Decrement reference for this address on this link. + * Unlink from selftab if this is the last ref. + * called with c->car locked + */ +static void +remselfcache(Ipifc *ifc, Iplifc *lifc, uchar *a) +{ + Ipself *p, **l; + Iplink *lp, *llp, **ill, **lll; + + qlock(&selftab); + + /* find the unique selftab entry */ + l = &selftab.hash[hashipa(a)]; + for(p = *l; p; p = *l){ + if(ipcmp(p->a, a) == 0) + break; + l = &p->next; + } + + if(p == nil) + goto out; + + /* + * walk down links from an ifc looking for one + * that matches the selftab entry + */ + ill = &lifc->link; + for(lp = *ill; lp; lp = *ill){ + if(lp->local == p) + break; + ill = &lp->lifclink; + } + + if(lp == nil) + goto out; + + /* + * walk down the links from the selftab looking for + * the one we just found + */ + lll = &p->link; + for(llp = *lll; llp; llp = *lll){ + if(llp == lp) + break; + lll = &lp->locallink; + } + + if(llp == nil) + panic("remselfcache"); + + if(--(llp->ref) != 0) + goto out; + + if((p->type & Rmulti) && ifc->m->remmulti != nil) + (*ifc->m->remmulti)(ifc, a, lifc->local); + + /* ref == 0, remove from both chains and free the link */ + *ill = lp->lifclink; + *lll = llp->locallink; + iplinkfree(lp); + + /* remove from routing table */ + if(isv4(a)) + v4delroute(a+IPv4off, IPallbits+IPv4off); + else + v6delroute(a, IPallbits); + + if(p->link != nil) + goto out; + + /* no more links, remove from hash and free */ + *l = p->next; + ipselffree(p); + + /* if IPnoaddr, forget */ + if(ipcmp(a, v4prefix) == 0 || ipcmp(a, IPnoaddr) == 0) + selftab.acceptall = 0; + +out: + qunlock(&selftab); +} + +static void +dumpselftab(void) +{ + int i, count; + Ipself *p; + + qlock(&selftab); + for(i = 0; i < NHASH; i++){ + p = selftab.hash[i]; + if(p == nil) + continue; + count = 0; + for(; p != nil && count++ < 6; p = p->next) + print("(%i %d %lux)", p->a, p->type, p); + print("\n"); + } + qunlock(&selftab); +} + + +/* + * returns + * 0 - no match + * Runi + * Rbcast + * Rmcast + */ +int +ipforme(uchar *addr) +{ + Ipself *p; + int count; + + p = selftab.hash[hashipa(addr)]; + count = 0; + for(; p; p = p->next){ + if(count++ > 1000){ /* check for loops */ + dumpselftab(); + break; + } + if(ipcmp(addr, p->a) == 0) + return p->type; + } + + /* hack to say accept anything */ + if(selftab.acceptall) + return Runi; + + return 0; +} + +/* + * find the ifc on same net as the remote system. If none, + * return nil. + */ +Ipifc* +findipifc(uchar *remote, int type) +{ + Ipifc *ifc; + Iplifc *lifc; + Conv **cp; + uchar gnet[IPaddrlen]; + + for(cp = ipifc.conv; cp < &ipifc.conv[ipifc.nc]; 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){ + qunlock(&ipifc); + return ifc; + } + } + } + + /* for now for broadcast and mutlicast, just use first interface */ + if(type & (Rbcast|Rmulti)){ + for(cp = ipifc.conv; cp < &ipifc.conv[ipifc.nc]; cp++){ + if(*cp == 0) + continue; + ifc = (Ipifc*)(*cp)->ptcl; + if(ifc->lifc != nil) + return ifc; + } + } + + return nil; +} + +/* + * find the local address 'closest' to the remote system, copy it to + * local and return the ifc for that address + */ +void +findlocalip(uchar *local, uchar *remote) +{ + Ipifc *ifc; + Iplifc *lifc; + Conv **cp; + Route *r; + uchar gate[IPaddrlen]; + uchar gnet[IPaddrlen]; + + qlock(&ipifc); + r = v6lookup(remote); + + if(r != nil){ + ifc = r->ifc; + if(r->type & Rv4) + v4tov6(gate, r->v4.gate); + else + ipmove(gate, r->v6.gate); + + if(r->type & Rifc){ + ipmove(local, gate); + goto out; + } + + /* 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; + } + } + } + + /* no match, choose first ifc local address */ + for(cp = ipifc.conv; cp < &ipifc.conv[ipifc.nc]; cp++){ + if(*cp == 0) + continue; + ifc = (Ipifc*)(*cp)->ptcl; + for(lifc = ifc->lifc; lifc; lifc = lifc->next){ + ipmove(local, lifc->local); + goto out; + } + } + +out: + qunlock(&ipifc); +} + +/* + * return first v4 address associated with an interface + */ +int +ipv4local(Ipifc *ifc, uchar *addr) +{ + Iplifc *lifc; + + for(lifc = ifc->lifc; lifc; lifc = lifc->next){ + if(isv4(lifc->local)){ + memmove(addr, lifc->local+IPv4off, IPv4addrlen); + return 1; + } + } + return 0; +} + +/* + * return first v6 address associated with an interface + */ +int +ipv6local(Ipifc *ifc, uchar *addr) +{ + Iplifc *lifc; + + for(lifc = ifc->lifc; lifc; lifc = lifc->next){ + if(!isv4(lifc->local)){ + ipmove(addr, lifc->local); + return 1; + } + } + return 0; +} + +/* + * see if this address is bound to the interface + */ +Iplifc* +iplocalonifc(Ipifc *ifc, uchar *ip) +{ + Iplifc *lifc; + + for(lifc = ifc->lifc; lifc; lifc = lifc->next) + if(ipcmp(ip, lifc->local) == 0) + return lifc; + return nil; +} + + +/* + * See if we're proxying for this address on this interface + */ +int +ipproxyifc(Ipifc *ifc, uchar *ip) +{ + Route *r; + uchar net[IPaddrlen]; + Iplifc *lifc; + + /* see if this is a direct connected pt to pt address */ + r = v6lookup(ip); + if(r == nil) + return 0; + if((r->type & Rifc) == 0) + return 0; + if((r->type & Rptpt) == 0) + return 0; + + /* see if this is on the right interface */ + for(lifc = ifc->lifc; lifc; lifc = lifc->next){ + maskip(ip, lifc->mask, net); + if(ipcmp(net, lifc->remote) == 0) + return 1; + } + + return 0; +} + +/* + * return multicast version if any + */ +int +ipismulticast(uchar *ip) +{ + if(isv4(ip)){ + if(ip[IPv4off] >= 0xe0 && ip[IPv4off] < 0xf0) + return V4; + } else { + if(ip[0] == 0xff) + return V6; + } + return 0; +} + +/* + * used to allow on the fly unbinds, return -1 if interface unusable + */ +int +ipifccheckin(Ipifc *ifc, Medium *med) +{ + int rv; + + lock(&ifc->idlock); + if(ifc->unbinding || ifc->m != med) + rv = -1; + else + rv = ++(ifc->ref); + if(ifc->ref < 0) panic("ipifccheckin"); + unlock(&ifc->idlock); + return rv; +} + +void +ipifccheckout(Ipifc *ifc) +{ + lock(&ifc->idlock); + if(--(ifc->ref) == 0) + if(ifc->unbinding) + wakeup(&ifc->wait); + if(ifc->ref < 0) panic("ipifccheckin"); + unlock(&ifc->idlock); +} + +static int +allout(void *x) +{ + Ipifc *ifc = x; + + return ifc->ref == 0; +} + +int +ipifcgrab(Ipifc *ifc) +{ + lock(&ifc->idlock); + if(ifc->unbinding){ + unlock(&ifc->idlock); + return 0; + } + ifc->unbinding = 1; /* after this ref can only go down */ + unlock(&ifc->idlock); + + sleep(&ifc->wait, allout, ifc); + + return 1; +} + +/* + * add a multicast address to an interface, called with c->car locked + */ +void +ipifcaddmulti(Conv *c, uchar *ma, uchar *ia) +{ + Ipifc *ifc; + Iplifc *lifc; + Conv **p; + Ipmulti *multi, **l; + + for(l = &c->multi; *l; l = &(*l)->next) + if(ipcmp(ma, (*l)->ma) == 0) + if(ipcmp(ia, (*l)->ia) == 0) + return; /* it's already there */ + + multi = *l = smalloc(sizeof(*multi)); + ipmove(multi->ma, ma); + ipmove(multi->ia, ia); + multi->next = nil; + + for(p = ipifc.conv; *p; p++){ + if((*p)->inuse == 0) + continue; + ifc = (Ipifc*)(*p)->ptcl; + if(waserror()){ + wunlock(ifc); + nexterror(); + } + wlock(ifc); + for(lifc = ifc->lifc; lifc; lifc = lifc->next) + if(ipcmp(ia, lifc->local) == 0) + addselfcache(ifc, lifc, ma, Rmulti); + wunlock(ifc); + poperror(); + } +} + + +/* + * remove a multicast address from an interface, called with c->car locked + */ +void +ipifcremmulti(Conv *c, uchar *ma, uchar *ia) +{ + Ipmulti *multi, **l; + Iplifc *lifc; + Conv **p; + Ipifc *ifc; + + for(l = &c->multi; *l; l = &(*l)->next) + if(ipcmp(ma, (*l)->ma) == 0) + if(ipcmp(ia, (*l)->ia) == 0) + break; + + multi = *l; + if(multi == nil) + return; /* we don't have it open */ + + *l = multi->next; + + for(p = ipifc.conv; *p; p++){ + if((*p)->inuse == 0) + continue; + + ifc = (Ipifc*)(*p)->ptcl; + if(waserror()){ + wunlock(ifc); + nexterror(); + } + wlock(ifc); + for(lifc = ifc->lifc; lifc; lifc = lifc->next) + if(ipcmp(ia, lifc->local) == 0) + remselfcache(ifc, lifc, ma); + wunlock(ifc); + poperror(); + } + + free(multi); +} + +/* + * make lifc's join and leave multicast groups + */ +static char* +ipifcjoinmulti(Ipifc *ifc, char **argv, int argc) +{ + USED(ifc, argv, argc); + return nil; +} + +static char* +ipifcleavemulti(Ipifc *ifc, char **argv, int argc) +{ + USED(ifc, argv, argc); + return nil; +} + diff --git a/ip/iproute.c b/ip/iproute.c new file mode 100644 index 0000000000000000000000000000000000000000..e801a7767ad4d37791d4ea63e2cc0149289dbd47 --- /dev/null +++ b/ip/iproute.c @@ -0,0 +1,792 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include "ip.h" + +enum +{ + Lroot = 10, +}; + +Route *v4root[1<left = nil; + r->right = nil; + if(r->type & Rv4) + l = &v4freelist; + else + l = &v6freelist; + r->mid = *l; + *l = r; +} + +static Route* +allocroute(int type) +{ + Route *r; + int n; + Route **l; + + if(type & Rv4){ + n = sizeof(RouteTree) + sizeof(V4route); + l = &v4freelist; + } else { + n = sizeof(RouteTree) + sizeof(V6route); + l = &v6freelist; + } + + r = *l; + if(r != nil){ + *l = r->mid; + } else { + r = malloc(n); + if(r == nil) + panic("out of routing nodes"); + } + memset(r, 0, n); + r->type = type; + r->ifc = nil; + + return r; +} + +static void +addqueue(Route **q, Route *r) +{ + Route *l; + + if(r == nil) + return; + + l = allocroute(r->type); + l->mid = *q; + *q = l; + l->left = r; +} + +/* + * compare 2 v6 addresses + */ +static int +lcmp(ulong *a, ulong *b) +{ + int i; + + for(i = 0; i < IPllen; i++){ + if(a[i] > b[i]) + return 1; + if(a[i] < b[i]) + return -1; + } + return 0; +} + +/* + * compare 2 v4 or v6 ranges + */ +enum +{ + Rpreceeds, + Rfollows, + Requals, + Rcontains, + Rcontained, +}; + +static int +rangecompare(Route *a, Route *b) +{ + if(a->type & Rv4){ + if(a->v4.endaddress < b->v4.address) + return Rpreceeds; + + if(a->v4.address > b->v4.endaddress) + return Rfollows; + + if(a->v4.address <= b->v4.address + && a->v4.endaddress >= b->v4.endaddress){ + if(a->v4.address == b->v4.address + && a->v4.endaddress == b->v4.endaddress) + return Requals; + return Rcontains; + } + return Rcontained; + } + + if(lcmp(a->v6.endaddress, b->v6.address) < 0) + return Rpreceeds; + + if(lcmp(a->v6.address, b->v6.endaddress) > 0) + return Rfollows; + + if(lcmp(a->v6.address, b->v6.address) <= 0 + && lcmp(a->v6.endaddress, b->v6.endaddress) >= 0){ + if(lcmp(a->v6.address, b->v6.address) == 0 + && lcmp(a->v6.endaddress, b->v6.endaddress) == 0) + return Requals; + return Rcontains; + } + + return Rcontained; +} + +static void +copygate(Route *old, Route *new) +{ + if(new->type & Rv4) + memmove(old->v4.gate, new->v4.gate, IPv4addrlen); + else + memmove(old->v6.gate, new->v6.gate, IPaddrlen); +} + +/* + * walk down a tree adding nodes back in + */ +static void +walkadd(Route **root, Route *p) +{ + Route *l, *r; + + l = p->left; + r = p->right; + p->left = 0; + p->right = 0; + addnode(root, p); + if(l) + walkadd(root, l); + if(r) + walkadd(root, r); +} + +/* + * calculate depth + */ +static void +calcd(Route *p) +{ + Route *q; + int d; + + if(p) { + d = 0; + q = p->left; + if(q) + d = q->depth; + q = p->right; + if(q && q->depth > d) + d = q->depth; + q = p->mid; + if(q && q->depth > d) + d = q->depth; + p->depth = d+1; + } +} + +/* + * balance the tree at the current node + */ +static void +balancetree(Route **cur) +{ + Route *p, *l, *r; + int dl, dr; + + /* + * if left and right are + * too out of balance, + * rotate tree node + */ + p = *cur; + dl = 0; if(l = p->left) dl = l->depth; + dr = 0; if(r = p->right) dr = r->depth; + + if(dl > dr+1) { + p->left = l->right; + l->right = p; + *cur = l; + calcd(p); + calcd(l); + } else + if(dr > dl+1) { + p->right = r->left; + r->left = p; + *cur = r; + calcd(p); + calcd(r); + } else + calcd(p); +} + +/* + * add a new node to the tree + */ +static void +addnode(Route **cur, Route *new) +{ + Route *p; + + p = *cur; + if(p == 0) { + *cur = new; + new->depth = 1; + return; + } + + switch(rangecompare(new, p)){ + case Rpreceeds: + addnode(&p->left, new); + break; + case Rfollows: + addnode(&p->right, new); + break; + case Rcontains: + /* + * if new node is superset + * of tree node, + * replace tree node and + * queue tree node to be + * merged into root. + */ + *cur = new; + new->depth = 1; + addqueue(&queue, p); + break; + case Requals: + copygate(p, new); + freeroute(new); + break; + case Rcontained: + addnode(&p->mid, new); + break; + } + + balancetree(cur); +} + +#define V4H(a) ((a&0x07ffffff)>>(32-Lroot-5)) + +void +v4addroute(char *tag, uchar *a, uchar *mask, uchar *gate, int type) +{ + Route *p; + ulong sa; + ulong m; + ulong ea; + int h, eh; + + m = nhgetl(mask); + sa = nhgetl(a) & m; + ea = sa | ~m; + + eh = V4H(ea); + for(h=V4H(sa); h<=eh; h++) { + p = allocroute(Rv4 | type); + p->v4.address = sa; + p->v4.endaddress = ea; + memmove(p->v4.gate, gate, sizeof(p->v4.gate)); + memmove(p->tag, tag, sizeof(p->tag)); + + wlock(&routelock); + addnode(&v4root[h], p); + while(p = queue) { + queue = p->mid; + walkadd(&v4root[h], p->left); + freeroute(p); + } + wunlock(&routelock); + } + + ipifcaddroute(Rv4, a, mask, gate, type); +} + +#define V6H(a) (((a)[IPllen-1] & 0x07ffffff)>>(32-Lroot-5)) + +void +v6addroute(char *tag, uchar *a, uchar *mask, uchar *gate, int type) +{ + Route *p; + ulong sa[IPllen], ea[IPllen]; + ulong x, y; + int h, eh; + + for(h = 0; h < IPllen; h++){ + x = nhgetl(a+4*h); + y = nhgetl(mask+4*h); + sa[h] = x & y; + ea[h] = x | ~y; + } + + eh = V6H(ea); + for(h = V6H(sa); h <= eh; h++) { + p = allocroute(type); + memmove(p->v6.address, sa, IPaddrlen); + memmove(p->v6.endaddress, ea, IPaddrlen); + memmove(p->v6.gate, gate, IPaddrlen); + memmove(p->tag, tag, sizeof(p->tag)); + + wlock(&routelock); + addnode(&v6root[h], p); + while(p = queue) { + queue = p->mid; + walkadd(&v6root[h], p->left); + freeroute(p); + } + wunlock(&routelock); + } + + ipifcaddroute(0, a, mask, gate, type); +} + +Route** +looknode(Route **cur, Route *r) +{ + Route *p; + + for(;;){ + p = *cur; + if(p == 0) + return 0; + + switch(rangecompare(r, p)){ + case Rcontains: + return 0; + case Rpreceeds: + cur = &p->left; + break; + case Rfollows: + cur = &p->right; + break; + case Rcontained: + cur = &p->mid; + break; + case Requals: + return cur; + } + } +} + +void +v4delroute(uchar *a, uchar *mask) +{ + Route **r, *p; + Route rt; + int h, eh; + ulong m; + + m = nhgetl(mask); + rt.v4.address = nhgetl(a) & m; + rt.v4.endaddress = rt.v4.address | ~m; + rt.type = Rv4; + + eh = V4H(rt.v4.endaddress); + for(h=V4H(rt.v4.address); h<=eh; h++) { + wlock(&routelock); + r = looknode(&v4root[h], &rt); + if(r) { + p = *r; + *r = 0; + addqueue(&queue, p->left); + addqueue(&queue, p->mid); + addqueue(&queue, p->right); + freeroute(p); + while(p = queue) { + queue = p->mid; + walkadd(&v4root[h], p->left); + freeroute(p); + } + } + wunlock(&routelock); + } + + ipifcremroute(Rv4, a, mask); +} + +void +v6delroute(uchar *a, uchar *mask) +{ + Route **r, *p; + Route rt; + int h, eh; + ulong x, y; + + for(h = 0; h < IPllen; h++){ + x = nhgetl(a+4*h); + y = nhgetl(mask+4*h); + rt.v6.address[h] = x & y; + rt.v6.endaddress[h] = x | ~y; + } + rt.type = 0; + + eh = V6H(rt.v6.endaddress); + for(h=V6H(rt.v6.address); h<=eh; h++) { + wlock(&routelock); + r = looknode(&v6root[h], &rt); + if(r) { + p = *r; + *r = 0; + addqueue(&queue, p->left); + addqueue(&queue, p->mid); + addqueue(&queue, p->right); + freeroute(p); + while(p = queue) { + queue = p->mid; + walkadd(&v6root[h], p->left); + freeroute(p); + } + } + wunlock(&routelock); + } + + ipifcremroute(0, a, mask); +} + +Route* +v4lookup(uchar *a) +{ + Route *p, *q; + ulong la; + uchar gate[IPaddrlen]; + + la = nhgetl(a); + q = nil; + for(p=v4root[V4H(la)]; p;) + if(la >= p->v4.address) { + if(la <= p->v4.endaddress) { + q = p; + p = p->mid; + } else + p = p->right; + } else + p = p->left; + + if(q && (q->ifc == nil || q->ifcid != q->ifc->ifcid)){ + v4tov6(gate, q->v4.gate); + q->ifc = findipifc(gate, q->type); + if(q->ifc == nil) + return nil; + q->ifcid = q->ifc->ifcid; + } + + return q; +} + +Route* +v6lookup(uchar *a) +{ + Route *p, *q; + ulong la[IPllen]; + int h; + ulong x, y; + + if(memcmp(a, v4prefix, 12) == 0){ + q = v4lookup(a+12); + if(q != nil) + return q; + } + + for(h = 0; h < IPllen; h++) + la[h] = nhgetl(a+4*h); + + q = 0; + for(p=v6root[V6H(la)]; p;){ + for(h = 0; h < IPllen; h++){ + x = la[h]; + y = p->v6.address[h]; + if(x == y) + continue; + if(x < y){ + p = p->left; + goto next; + } + break; + } + for(h = 0; h < IPllen; h++){ + x = la[h]; + y = p->v6.endaddress[h]; + if(x == y) + continue; + if(x > y){ + p = p->right; + goto next; + } + break; + } + q = p; + p = p->mid; +next: ; + } + + if(q && q->ifc == nil){ + q->ifc = findipifc(q->v6.gate, q->type); + if(q->ifc == nil) + return nil; + if(q->ifcid != q->ifc->ifcid) + return nil; + } + + return q; +} + +enum +{ + Rlinelen= 89, +}; + +char *rformat = "%-24.24I %-24.24M %-24.24I %4.4s %4.4s %3s\n"; + +void +convroute(Route *r, uchar *addr, uchar *mask, uchar *gate, char *t, int *nifc) +{ + int i; + char *p = t; + + if(r->type & Rv4){ + memmove(addr, v4prefix, IPv4off); + hnputl(addr+IPv4off, r->v4.address); + memset(mask, 0xff, IPv4off); + hnputl(mask+IPv4off, ~(r->v4.endaddress ^ r->v4.address)); + memmove(gate, v4prefix, IPv4off); + memmove(gate+IPv4off, r->v4.gate, IPv4addrlen); + } else { + for(i = 0; i < IPllen; i++){ + hnputl(addr + 4*i, r->v6.address[i]); + hnputl(mask + 4*i, ~(r->v6.endaddress[i] ^ r->v6.address[i])); + } + memmove(gate, r->v6.gate, IPaddrlen); + } + + memset(t, ' ', 4); + t[4] = 0; + if(r->type & Rv4) + *p++ = '4'; + else + *p++ = '6'; + if(r->type & Rifc) + *p++ = 'i'; + if(r->type & Runi) + *p++ = 'u'; + else if(r->type & Rbcast) + *p++ = 'b'; + else if(r->type & Rmulti) + *p++ = 'm'; + if(r->type & Rptpt) + *p = 'p'; + + if(r->ifc) + *nifc = r->ifc->conv->x; + else + *nifc = -1; +} + +/* + * this code is not in rr to reduce stack size + */ +static void +sprintroute(Route *r, Routewalk *rw) +{ + int nifc; + char t[5], *iname, ifbuf[5]; + uchar addr[IPaddrlen], mask[IPaddrlen], gate[IPaddrlen]; + + if(rw->o >= 0) { + convroute(r, addr, mask, gate, t, &nifc); + iname = "-"; + if(nifc != -1) { + iname = ifbuf; + sprint(ifbuf, "%d", nifc); + } + sprint(rw->p, rformat, addr, mask, gate, t, r->tag, iname); + rw->p += Rlinelen; + } + rw->o++; +} + +/* + * recurse descending tree, applying the function in Routewalk + */ +static int +rr(Route *r, Routewalk *rw) +{ + int h; + + if(rw->n <= rw->o) + return 0; + if(r == nil) + return 1; + + if(rr(r->left, rw) == 0) + return 0; + + if(r->type & Rv4) + h = V4H(r->v4.address); + else + h = V6H(r->v6.address); + + if(h == rw->h) + rw->walk(r, rw); + + if(rr(r->mid, rw) == 0) + return 0; + + return rr(r->right, rw); +} + +void +ipwalkroutes(Routewalk *rw) +{ + rlock(&routelock); + if(rw->n > rw->o) { + for(rw->h = 0; rw->h < nelem(v4root); rw->h++) + if(rr(v4root[rw->h], rw) == 0) + break; + } + if(rw->n > rw->o) { + for(rw->h = 0; rw->h < nelem(v6root); rw->h++) + if(rr(v6root[rw->h], rw) == 0) + break; + } + runlock(&routelock); +} + +long +routeread(char *p, ulong offset, int n) +{ + Routewalk rw; + + if(offset % Rlinelen) + return 0; + + rw.p = p; + rw.n = n/Rlinelen; + rw.o = -(offset/Rlinelen); + rw.walk = sprintroute; + + ipwalkroutes(&rw); + + if(rw.o < 0) + rw.o = 0; + + return rw.o*Rlinelen; +} + +/* + * this code is not in routeflush to reduce stack size + */ +void +delroute(Route *r) +{ + uchar addr[IPaddrlen]; + uchar mask[IPaddrlen]; + uchar gate[IPaddrlen]; + char t[5]; + int nifc; + + convroute(r, addr, mask, gate, t, &nifc); + if(r->type & Rv4) + v4delroute(addr+IPv4off, mask+IPv4off); + else + v6delroute(addr, mask); +} + +/* + * recurse until one route is deleted + * returns 0 if nothing is deleted, 1 otherwise + */ +int +routeflush(Route *r) +{ + if(r == nil) + return 0; + if(routeflush(r->mid)) + return 1; + if(routeflush(r->left)) + return 1; + if(routeflush(r->right)) + return 1; + if((r->type & Rifc) == 0){ + delroute(r); + return 1; + } + return 0; +} + +long +routewrite(Chan *c, char *p, int n) +{ + int h; + char *tag; + Cmdbuf *cb; + uchar addr[IPaddrlen]; + uchar mask[IPaddrlen]; + uchar gate[IPaddrlen]; + + cb = parsecmd(p, n); + if(waserror()){ + free(cb); + nexterror(); + } + + if(strcmp(cb->f[0], "flush") == 0){ + wlock(&routelock); + for(h = 0; h < nelem(v4root); h++) + while(routeflush(v4root[h]) == 1) + ; + for(h = 0; h < nelem(v6root); h++) + while(routeflush(v6root[h]) == 1) + ; + wunlock(&routelock); + } 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(addr+IPv4off, mask+IPv4off); + else + v6delroute(addr, mask); + } 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) + tag = c->tag; + if(memcmp(addr, v4prefix, IPv4off) == 0) + v4addroute(tag, addr+IPv4off, mask+IPv4off, gate+IPv4off, 0); + else + v6addroute(tag, addr, mask, gate, 0); + } else if(strcmp(cb->f[0], "tag") == 0) { + if(cb->nf < 2) + error(Ebadarg); + + h = strlen(cb->f[1]); + if(h > sizeof(c->tag)) + h = sizeof(c->tag); + strncpy(c->tag, cb->f[1], h); + if(h < 4) + memset(c->tag+h, ' ', sizeof(c->tag)-h); + } + + poperror(); + free(cb); + return n; +} diff --git a/ip/netlog.c b/ip/netlog.c index e8e4e4d85d64a5f89def9109f8906a52d3ffa73b..41f6605c23eb9672265ea847b8792daf7b28448a 100644 --- a/ip/netlog.c +++ b/ip/netlog.c @@ -7,7 +7,8 @@ #include "../ip/ip.h" int logmask; /* mask of things to debug */ -Ipaddr iponly; /* ip address to print debugging for */ +uchar iponly[IPaddrlen]; /* ip address to print debugging for */ +int iponlyset; enum { Nlog = 4*1024, @@ -62,11 +63,8 @@ netlogopen(void) nexterror(); } if(alog.opens == 0){ - if(alog.buf == nil){ + if(alog.buf == nil) alog.buf = malloc(Nlog); - if(alog.buf == nil) - error(Enomem); - } alog.rptr = alog.buf; alog.end = alog.buf + Nlog; } @@ -175,7 +173,11 @@ netlogctl(char* s, int len) else if(strcmp("clear", fields[0]) == 0) set = 0; else if(strcmp("only", fields[0]) == 0){ - iponly = parseip(addr, fields[1]); + parseip(iponly, fields[1]); + if(ipcmp(iponly, IPnoaddr) == 0) + iponlyset = 0; + else + iponlyset = 1; return nil; } else return Ebadnetctl; @@ -206,13 +208,15 @@ netlog(int mask, char *fmt, ...) int i, n; va_list arg; - if(alog.opens == 0 || !(logmask & mask)) + if(!(logmask & mask)) return; va_start(arg, fmt); n = doprint(buf, buf+sizeof(buf), fmt, arg) - buf; va_end(arg); +print("%s", buf); + if(alog.opens == 0) return; diff --git a/ip/nullmedium.c b/ip/nullmedium.c new file mode 100644 index 0000000000000000000000000000000000000000..c4acd3ff5c95f3909c7622db434c5200d235952b --- /dev/null +++ b/ip/nullmedium.c @@ -0,0 +1,46 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include "ip.h" +#include "kernel.h" + +static void +nullbind(Ipifc*, int, char**) +{ + error("can't bind null device"); +} + +static void +nullunbind(Ipifc*) +{ +} + +static void +nullbwrite(Ipifc*, Block*, int, uchar*) +{ + error("nullbwrite"); +} + +Medium nullmedium = +{ + "null", + 0, /* medium header size */ + 0, /* default min mtu */ + 0, /* default max mtu */ + 0, /* mac address length */ + nullbind, + nullunbind, + nullbwrite, + nil, /* addmulti */ + nil, /* remmulti */ + nil, /* pktin */ + nil, /* addroute */ + nil, /* remroute */ + nil, /* flushroute */ + nil, /* joinmulti */ + nil, /* leave multi */ +}; diff --git a/ip/pktmedium.c b/ip/pktmedium.c new file mode 100644 index 0000000000000000000000000000000000000000..6a2500bdb7fa4f9dfdb2e0b555ee5fa3a3db9fd3 --- /dev/null +++ b/ip/pktmedium.c @@ -0,0 +1,75 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include "ip.h" +#include "kernel.h" + + +static void pktbind(Ipifc *ifc, int argc, char **argv); +static void pktunbind(Ipifc *ifc); +static void pktbwrite(Ipifc *ifc, Block *bp, int version, uchar *ip); +static void pktin(Ipifc *ifc, Block *bp); + +Medium pktmedium = +{ + "pkt", + 14, + 60, + 1514, + 6, + pktbind, + pktunbind, + pktbwrite, + nil, /* addmulti */ + nil, /* remmulti */ + pktin, + nil, /* addroute */ + nil, /* remroute */ + nil, /* flushroute */ + nil, /* joinmulti */ + nil, /* leave multi */ +}; + +/* + * called to bind an IP ifc to an ethernet device + * called with ifc wlock'd + */ +static void +pktbind(Ipifc*, int, char**) +{ +} + +/* + * called with ifc wlock'd + */ +static void +pktunbind(Ipifc*) +{ +} + +/* + * called by ipoput with a single packet to write + */ +static void +pktbwrite(Ipifc *ifc, Block *bp, int, uchar*) +{ + /* enqueue onto the conversation's rq */ + bp = concatblock(bp); + qpass(ifc->conv->rq, bp); +} + +/* + * called when someone write's to 'data' with ifc rlocked + */ +static void +pktin(Ipifc *ifc, Block *bp) +{ + if(ifc->lifc == nil) + freeb(bp); + else + ipiput(ifc->lifc->local, bp); +} diff --git a/ip/ptclbsum.c b/ip/ptclbsum.c index dd660c8ea78874652f3ad7bdb375b68ba2468367..8f822e5f54279739ddf68fcadc117faa1e9a7be7 100644 --- a/ip/ptclbsum.c +++ b/ip/ptclbsum.c @@ -8,11 +8,11 @@ #include "ip.h" static short endian = 1; -static byte* aendian = (byte*)&endian; +static uchar* aendian = (uchar*)&endian; #define LITTLE *aendian ushort -ptclbsum(byte *addr, int len) +ptclbsum(uchar *addr, int len) { ulong losum, hisum, mdsum, x; ulong t1, t2; diff --git a/ip/tcp.c b/ip/tcp.c index fb457b96b964826a872723c9a6806dd88158fb4d..ed2b5c305e7fbadb66b5cfcdc5b410a4b45b764b 100644 --- a/ip/tcp.c +++ b/ip/tcp.c @@ -14,6 +14,7 @@ enum TCP_IPLEN = 8, TCP_PHDRSIZE = 12, TCP_HDRSIZE = 20, + TCP_TCBPHDRSZ = 40, TCP_PKT = TCP_IPLEN+TCP_PHDRSIZE, TimerOFF = 0, TimerON = 1, @@ -85,27 +86,27 @@ struct Timer typedef struct Tcphdr Tcphdr; struct Tcphdr { - byte vihl; /* Version and header length */ - byte tos; /* Type of service */ - byte length[2]; /* packet length */ - byte id[2]; /* Identification */ - byte frag[2]; /* Fragment information */ - byte Unused; - byte proto; - byte tcplen[2]; - byte tcpsrc[4]; - byte tcpdst[4]; - byte tcpsport[2]; - byte tcpdport[2]; - byte tcpseq[4]; - byte tcpack[4]; - byte tcpflag[2]; - byte tcpwin[2]; - byte tcpcksum[2]; - byte tcpurg[2]; + uchar vihl; /* Version and header length */ + uchar tos; /* Type of service */ + uchar length[2]; /* packet length */ + uchar id[2]; /* Identification */ + uchar frag[2]; /* Fragment information */ + uchar Unused; + uchar proto; + uchar tcplen[2]; + uchar tcpsrc[4]; + uchar tcpdst[4]; + 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 */ - byte tcpopt[2]; - byte tcpmss[2]; + uchar tcpopt[2]; + uchar tcpmss[2]; }; typedef struct Tcp Tcp; @@ -115,7 +116,7 @@ struct Tcp ushort dest; ulong seq; ulong ack; - byte flags; + uchar flags; ushort wnd; ushort urg; ushort mss; @@ -134,9 +135,9 @@ typedef struct Tcpctl Tcpctl; struct Tcpctl { QLock; - byte state; /* Connection state */ - byte type; /* Listening or active connection */ - byte code; /* Icmp code */ + uchar state; /* Connection state */ + uchar type; /* Listening or active connection */ + uchar code; /* Icmp code */ struct { ulong una; /* Unacked data pointer */ ulong nxt; /* Next sequence expected */ @@ -147,7 +148,7 @@ struct Tcpctl ulong wl2; } snd; struct { - ulong nxt; /* Receive pointer to next byte slot */ + ulong nxt; /* Receive pointer to next uchar slot */ ushort wnd; /* Receive window incoming */ ulong urg; /* Urgent pointer */ int blocked; @@ -162,8 +163,8 @@ struct Tcpctl ushort window; /* Recevive window */ int max_snd; /* Max send */ ulong last_ack; /* Last acknowledege received */ - byte backoff; /* Exponential backoff counter */ - byte flags; /* State flags */ + uchar backoff; /* Exponential backoff counter */ + uchar flags; /* State flags */ ulong sndcnt; /* Amount of data in send queue */ Reseq *reseq; /* Resequencing queue */ Timer timer; /* Activity timer */ @@ -175,10 +176,11 @@ struct Tcpctl int kacounter; /* count down for keep alive */ int f2counter; /* count down for finwait2 state */ uint sndsyntime; /* time syn sent */ - char ascstate[128]; + + Tcphdr protohdr; /* prototype header */ }; -#define DBG(x) if((logmask & Logtcpmsg) && (iponly == 0 || x == iponly))netlog +#define DBG(x) if((logmask & Logtcpmsg) && (iponlyset == 0 || memcmp(x, iponly+12, 4) == 0))netlog Proto tcp; int tcp_irtt = DEF_RTT; /* Initial guess at round trip time */ @@ -189,14 +191,14 @@ QLock tl; /* Protect timer list */ static struct Tcpstats { ulong dup; /* (partially) duplicated packets */ - ulong dupb; /* duplicated bytes */ + ulong dupb; /* duplicated uchars */ } tstats; void addreseq(Tcpctl*, Tcp*, Block*, ushort); void getreseq(Tcpctl*, Tcp*, Block**, ushort*); void localclose(Conv*, char*); void procsyn(Conv*, Tcp*); -void tcpiput(Media*, Block*); +void tcpiput(uchar*, Block*); void tcpoutput(Conv*); int tcptrim(Tcpctl*, Tcp*, Block**, ushort*); void tcpstart(Conv*, int, ushort); @@ -206,10 +208,10 @@ void tcprcvwin(Conv*); void tcpacktimer(Conv*); void -tcpsetstate(Conv *s, byte newstate) +tcpsetstate(Conv *s, uchar newstate) { Tcpctl *tcb; - byte oldstate; + uchar oldstate; tcb = (Tcpctl*)s->ptcl; @@ -231,7 +233,7 @@ tcpsetstate(Conv *s, byte newstate) qclose(s->eq); s->lport = 0; /* This connection is toast */ s->rport = 0; - s->raddr = 0; + ipmove(s->raddr, IPnoaddr); case Close_wait: /* Remote closes */ qhangup(s->rq, nil); @@ -255,29 +257,26 @@ tcpconnect(Conv *c, char **argv, int argc) return nil; } -int -tcpstate(char **msg, Conv *c) +static int +tcpstate(Conv *c, char *state, int n) { Tcpctl *s; - int isclose; s = (Tcpctl*)(c->ptcl); - isclose = 0; - if(s->state == Closed) - isclose = 1; - -/* snprint(s->ascstate, sizeof(s->ascstate), - "tcnt %d tnxt %lux tuna %lux twnd %d rack %lux rnxt %lux rwnd %d", - s->sndcnt, s->snd.nxt, s->snd.una, s->snd.wnd, - s->last_ack, s->rcv.nxt, s->rcv.wnd);*/ - snprint(s->ascstate, sizeof(s->ascstate), + return snprint(state, n, "%s srtt %d mdev %d timer.start %d timer.count %d\n", tcpstates[s->state], s->srtt, s->mdev, s->timer.start, s->timer.count); +} - *msg = s->ascstate; - return isclose; +static int +tcpinuse(Conv *c) +{ + Tcpctl *s; + + s = (Tcpctl*)(c->ptcl); + return s->state != Closed; } static char* @@ -533,6 +532,7 @@ void inittcpctl(Conv *s) { Tcpctl *tcb; + Tcphdr *h; tcb = (Tcpctl*)s->ptcl; @@ -550,21 +550,30 @@ inittcpctl(Conv *s) tcb->acktimer.start = TCP_ACK / MSPTICK; tcb->acktimer.func = tcpacktimer; tcb->acktimer.arg = s; + + /* create a prototype(pseudo) header */ + if(ipcmp(s->laddr, IPnoaddr) == 0) + findlocalip(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); } /* mtu (- TCP + IP hdr len) of 1st hop */ int tcpmtu(Conv *s) { - Media *m; - byte dst[4], dummy[4]; + Ipifc *ifc; int mtu; mtu = 0; - hnputl(dst, s->raddr); - m = Mediaroute(dst, dummy); - if(m != nil) - mtu = m->maxmtu - m->hsize - (TCP_PKT + TCP_HDRSIZE); + ifc = findipifc(s->raddr, 0); + if(ifc != nil) + mtu = ifc->maxmtu - ifc->m->hsize - (TCP_PKT + TCP_HDRSIZE); if(mtu < 4) mtu = DEF_MSS; return mtu; @@ -647,23 +656,16 @@ htontcp(Tcp *tcph, Block *data, Tcphdr *ph) data->wp += hdrlen + TCP_PKT; } - - memmove(data->rp, ph, TCP_PKT); - + /* copy in pseudo ip header plus port numbers */ h = (Tcphdr *)(data->rp); - h->proto = IP_TCPPROTO; - h->frag[0] = 0; - h->frag[1] = 0; + memmove(h, ph, TCP_TCBPHDRSZ); + + /* copy in variable bits */ hnputs(h->tcplen, hdrlen + dlen); - hnputs(h->tcpsport, tcph->source); - hnputs(h->tcpdport, tcph->dest); hnputl(h->tcpseq, tcph->seq); hnputl(h->tcpack, tcph->ack); hnputs(h->tcpflag, (hdrlen<<10) | tcph->flags); hnputs(h->tcpwin, tcph->wnd); - h->tcpcksum[0] = 0; - h->tcpcksum[1] = 0; - h->Unused = 0; hnputs(h->tcpurg, tcph->urg); if(tcph->mss != 0){ @@ -674,7 +676,7 @@ htontcp(Tcp *tcph, Block *data, Tcphdr *ph) csum = ptclcsum(data, TCP_IPLEN, hdrlen+dlen+TCP_PHDRSIZE); hnputs(h->tcpcksum, csum); - DBG(nhgetl(h->tcpdst))(Logtcpmsg, "%d > %d s %l8.8ux a %8.8lux %s w %.4ux l %d\n", + DBG(h->tcpdst)(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); @@ -686,7 +688,7 @@ int ntohtcp(Tcp *tcph, Block **bpp) { Tcphdr *h; - byte *optr; + uchar *optr; ushort hdrlen; ushort i, optlen; @@ -715,7 +717,7 @@ ntohtcp(Tcp *tcph, Block **bpp) if(*bpp == nil) return -1; - DBG(nhgetl(h->tcpsrc))(Logtcpmsg, "%d > %d s %l8.8ux a %8.8lux %s w %.4ux l %d\n", + DBG(h->tcpsrc)(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); @@ -754,27 +756,28 @@ tcpsndsyn(Tcpctl *tcb) tcb->sndsyntime = msec; } + +/* + * called with v4 (4 byte) addresses + */ void -sndrst(Ipaddr source, Ipaddr dest, ushort length, Tcp *seg) +sndrst(uchar *source, uchar *dest, ushort length, Tcp *seg) { - ushort tmp; Tcphdr ph; Block *hbp; - byte rflags; + uchar rflags; if(seg->flags & RST) return; - - hnputl(ph.tcpsrc, dest); - hnputl(ph.tcpdst, source); + /* 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); - - /* Swap port numbers */ - tmp = seg->dest; - seg->dest = seg->source; - seg->source = tmp; + hnputs(ph.tcpsport, seg->dest); + hnputs(ph.tcpdport, seg->source); rflags = RST; @@ -811,7 +814,6 @@ char* tcphangup(Conv *s) { Tcp seg; - byte dst[4]; Tcpctl *tcb; Tcphdr ph; Block *hbp; @@ -823,8 +825,6 @@ tcphangup(Conv *s) } qlock(tcb); if(s->raddr != 0) { - seg.source = s->lport; - seg.dest = s->rport; seg.flags = RST | ACK; seg.ack = tcb->rcv.nxt; seg.seq = tcb->snd.ptr; @@ -832,15 +832,8 @@ tcphangup(Conv *s) seg.urg = 0; seg.mss = 0; tcb->last_ack = tcb->rcv.nxt; - if(s->laddr == 0) { - hnputl(dst, s->raddr); - s->laddr = Mediagetsrc(dst); - } - hnputl(ph.tcpsrc, s->laddr); - hnputl(ph.tcpdst, s->raddr); - ph.proto = IP_TCPPROTO; hnputs(ph.tcplen, TCP_HDRSIZE); - hbp = htontcp(&seg, nil, &ph); + hbp = htontcp(&seg, nil, &tcb->protohdr); ipoput(hbp, 0, s->ttl); } localclose(s, nil); @@ -850,10 +843,11 @@ tcphangup(Conv *s) } Conv* -tcpincoming(Conv *s, Tcp *segp, Ipaddr src, Ipaddr dst) +tcpincoming(Conv *s, Tcp *segp, uchar *src, uchar *dst) { Conv *new; Tcpctl *tcb; + Tcphdr *h; new = Fsnewcall(&fs, s, src, segp->source, dst, segp->dest); if(new == nil) @@ -867,6 +861,14 @@ tcpincoming(Conv *s, Tcp *segp, Ipaddr src, Ipaddr dst) tcb->acktimer.arg = new; tcb->acktimer.state = TimerOFF; + 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); + return new; } @@ -1030,21 +1032,20 @@ update(Conv *s, Tcp *seg) } void -tcpiput(Media *m, Block *bp) +tcpiput(uchar*, Block *bp) { Tcp seg; Tcphdr *h; int hdrlen; Tcpctl *tcb; ushort length; - Ipaddr source, dest; + uchar source[IPaddrlen], dest[IPaddrlen]; Conv *spec, *gen, *s, **p; - USED(m); h = (Tcphdr*)(bp->rp); - dest = nhgetl(h->tcpdst); - source = nhgetl(h->tcpsrc); + v4tov6(dest, h->tcpdst); + v4tov6(source, h->tcpsrc); length = nhgets(h->length); h->Unused = 0; @@ -1076,8 +1077,9 @@ tcpiput(Media *m, Block *bp) /* Look for a connection. failing that look for a listener. */ for(p = tcp.conv; *p; p++) { s = *p; - if(s->rport == seg.source && - s->lport == seg.dest && s->raddr == source) + if(s->rport == seg.source) + if(s->lport == seg.dest) + if(ipcmp(s->raddr, source) == 0) break; } s = *p; @@ -1116,7 +1118,7 @@ tcpiput(Media *m, Block *bp) continue; if(tcb->state != Listen) continue; - if(s->rport == 0 && s->raddr == 0) { + if(s->rport == 0 && ipcmp(s->raddr, IPnoaddr) == 0) { if(s->lport == seg.dest){ spec = s; break; @@ -1414,7 +1416,6 @@ tcpoutput(Conv *s) int x; Tcp seg; int msgs; - Tcphdr ph; Tcpctl *tcb; Block *hbp, *bp; int sndcnt, n, first; @@ -1547,17 +1548,8 @@ tcpoutput(Conv *s) if(seq_gt(tcb->snd.ptr,tcb->snd.nxt)) tcb->snd.nxt = tcb->snd.ptr; - /* Fill in fields of pseudo IP header */ - hnputl(ph.tcpdst, s->raddr); - if(s->laddr == 0) - s->laddr = Mediagetsrc(ph.tcpdst); - - hnputl(ph.tcpsrc, s->laddr); - hnputs(ph.tcpsport, s->lport); - hnputs(ph.tcpdport, s->rport); - /* Build header, link data and compute cksum */ - hbp = htontcp(&seg, bp, &ph); + hbp = htontcp(&seg, bp, &tcb->protohdr); if(hbp == nil) { freeblist(bp); return; @@ -1588,13 +1580,12 @@ tcpoutput(Conv *s) } /* - * the BSD convention (hack?) for keep alives. resend last byte acked. + * the BSD convention (hack?) for keep alives. resend last uchar acked. */ void tcpkeepalive(Conv *s) { Tcp seg; - Tcphdr ph; Tcpctl *tcb; Block *hbp,*dbp; @@ -1618,16 +1609,8 @@ tcpkeepalive(Conv *s) dbp->wp++; } - /* Fill in fields of pseudo IP header */ - hnputl(ph.tcpdst, s->raddr); - if(s->laddr == 0) - s->laddr = Mediagetsrc(ph.tcpdst); - hnputl(ph.tcpsrc, s->laddr); - hnputs(ph.tcpsport, s->lport); - hnputs(ph.tcpdport, s->rport); - /* Build header, link data and compute cksum */ - hbp = htontcp(&seg, dbp, &ph); + hbp = htontcp(&seg, dbp, &tcb->protohdr); if(hbp == nil) { freeblist(dbp); return; @@ -1792,7 +1775,7 @@ tcptrim(Tcpctl *tcb, Tcp *seg, Block **bp, ushort *length) { ushort len; Block *nbp; - byte accept; + uchar accept; int dupcnt, excess; accept = 0; @@ -1866,22 +1849,25 @@ tcpadvise(Block *bp, char *msg) { Tcphdr *h; Tcpctl *tcb; - Ipaddr source, dest; + uchar source[IPaddrlen]; + uchar dest[IPaddrlen]; ushort psource, pdest; Conv *s, **p; h = (Tcphdr*)(bp->rp); - dest = nhgetl(h->tcpdst); - source = nhgetl(h->tcpsrc); + v4tov6(dest, h->tcpdst); + v4tov6(source, h->tcpsrc); psource = nhgets(h->tcpsport); pdest = nhgets(h->tcpdport); /* Look for a connection */ for(p = tcp.conv; *p; p++) { s = *p; - if(s->rport == pdest && s->lport == psource) - if(s->raddr == dest && s->laddr == source){ + if(s->rport == pdest) + if(s->lport == psource) + if(ipcmp(s->raddr, dest) == 0) + if(ipcmp(s->laddr, source) == 0){ tcb = (Tcpctl*)s->ptcl; qlock(tcb); switch(tcb->state){ @@ -1896,6 +1882,7 @@ tcpadvise(Block *bp, char *msg) freeblist(bp); } +/* called with c->car qlocked */ char* tcpctl(Conv* c, char** f, int n) { @@ -1907,7 +1894,14 @@ tcpctl(Conv* c, char** f, int n) int tcpstats(char *buf, int len) { - return snprint(buf, len, "\tdupp %d dupb %d\n", tstats.dup, tstats.dupb); + int n; + + n = snprint(buf, len, + "tcp: csum %d hlen %d len %d order %d rexmit %d", + tcp.csumerr, tcp.hlenerr, tcp.lenerr, tcp.order, tcp.rexmit); + n += snprint(buf+n, len-n, " dupp %d dupb %d\n", + tstats.dup, tstats.dupb); + return n; } void @@ -1924,6 +1918,7 @@ tcpinit(Fs *fs) tcp.rcv = tcpiput; tcp.advise = tcpadvise; tcp.stats = tcpstats; + tcp.inuse = tcpinuse; tcp.ipproto = IP_TCPPROTO; tcp.nc = Nchans; tcp.ptclsize = sizeof(Tcpctl); @@ -1932,3 +1927,4 @@ tcpinit(Fs *fs) Fsproto(fs, &tcp); } + diff --git a/ip/udp.c b/ip/udp.c index 57e34a1485efb2dfc341034027c78c4a494c2371..e53a495b214e0a00b45f173da3b15b191174e6d4 100644 --- a/ip/udp.c +++ b/ip/udp.c @@ -15,7 +15,7 @@ enum UDP_HDRSIZE = 20, UDP_IPHDR = 8, IP_UDPPROTO = 17, - UDP_USEAD = 12, + UDP_USEAD = 36, Udprxms = 200, Udptickms = 100, @@ -26,22 +26,22 @@ typedef struct Udphdr Udphdr; struct Udphdr { /* ip header */ - byte vihl; /* Version and header length */ - byte tos; /* Type of service */ - byte length[2]; /* packet length */ - byte id[2]; /* Identification */ - byte frag[2]; /* Fragment information */ - byte Unused; - byte udpproto; /* Protocol */ - byte udpplen[2]; /* Header plus data length */ - byte udpsrc[4]; /* Ip source */ - byte udpdst[4]; /* Ip destination */ + uchar vihl; /* Version and header length */ + uchar tos; /* Type of service */ + uchar length[2]; /* packet length */ + uchar id[2]; /* Identification */ + uchar frag[2]; /* Fragment information */ + uchar Unused; + uchar udpproto; /* Protocol */ + uchar udpplen[2]; /* Header plus data length */ + uchar udpsrc[4]; /* Ip source */ + uchar udpdst[4]; /* Ip destination */ /* udp header */ - byte udpsport[2]; /* Source port */ - byte udpdport[2]; /* Destination port */ - byte udplen[2]; /* data length */ - byte udpcksum[2]; /* Checksum */ + uchar udpsport[2]; /* Source port */ + uchar udpdport[2]; /* Destination port */ + uchar udplen[2]; /* data length */ + uchar udpcksum[2]; /* Checksum */ }; /* @@ -51,7 +51,7 @@ typedef struct Udpcb Udpcb; struct Udpcb { QLock; - byte headers; + uchar headers; }; Proto udp; @@ -71,12 +71,11 @@ udpconnect(Conv *c, char **argv, int argc) return e; } -int -udpstate(char **s, Conv *c) +static int +udpstate(Conv *c, char *state, int n) { USED(c); - *s = "Datagram"; - return 1; + return snprint(state, n, "%s", "Datagram"); } static char* @@ -107,8 +106,8 @@ udpclose(Conv *c) qclose(c->rq); qclose(c->wq); qclose(c->eq); - c->laddr = 0; - c->raddr = 0; + ipmove(c->laddr, IPnoaddr); + ipmove(c->raddr, IPnoaddr); c->lport = 0; c->rport = 0; @@ -119,17 +118,16 @@ udpclose(Conv *c) } void -udpkick(Conv *c, int l) +udpkick(Conv *c, int) { Udphdr *uh; ushort rport; - Ipaddr laddr, raddr; + uchar laddr[IPaddrlen], raddr[IPaddrlen]; Block *bp; Udpcb *ucb; int dlen, ptcllen; - USED(l); - + netlog(Logudp, "udp: kick\n"); bp = qget(c->wq); if(bp == nil) return; @@ -138,24 +136,20 @@ udpkick(Conv *c, int l) if(ucb->headers) { /* get user specified addresses */ bp = pullupblock(bp, UDP_USEAD); - if(bp == nil) { - freeblist(bp); + if(bp == nil) return; - } - raddr = nhgetl(bp->rp); - bp->rp += 4; - laddr = nhgetl(bp->rp); - if(laddr != 0 && Mediaforme(bp->rp) <= 0) - laddr = 0; - bp->rp += 4; + ipmove(raddr, bp->rp); + bp->rp += IPaddrlen; + ipmove(laddr, bp->rp); + bp->rp += IPaddrlen; + if(ipforme(laddr) != Runi) + findlocalip(laddr, raddr); /* pick interface closest to dest */ rport = nhgets(bp->rp); bp->rp += 2; /* ignore local port number */ bp->rp += 2; } else { - raddr = 0; rport = 0; - laddr = 0; } dlen = blocklen(bp); @@ -174,16 +168,15 @@ udpkick(Conv *c, int l) uh->frag[1] = 0; hnputs(uh->udpplen, ptcllen); if(ucb->headers) { - hnputl(uh->udpdst, raddr); + v6tov4(uh->udpdst, raddr); hnputs(uh->udpdport, rport); - if(laddr) - hnputl(uh->udpsrc, laddr); - else - hnputl(uh->udpsrc, Mediagetsrc(uh->udpdst)); + v6tov4(uh->udpsrc, laddr); } else { - hnputl(uh->udpdst, c->raddr); + v6tov4(uh->udpdst, c->raddr); hnputs(uh->udpdport, c->rport); - hnputl(uh->udpsrc, c->laddr); + if(ipcmp(c->laddr, IPnoaddr) == 0) + findlocalip(c->laddr, c->raddr); /* pick interface closest to dest */ + v6tov4(uh->udpsrc, c->laddr); } hnputs(uh->udpsport, c->lport); hnputs(uh->udplen, ptcllen); @@ -196,18 +189,15 @@ udpkick(Conv *c, int l) } void -udpiput(Media *m, Block *bp) +udpiput(uchar *ia, Block *bp) { int len, olen, ottl; Udphdr *uh; Conv *c, **p; Udpcb *ucb; - Ipaddr raddr, laddr; + uchar raddr[IPaddrlen], laddr[IPaddrlen]; ushort rport, lport; - uchar dst[IPaddrlen]; - uchar src[IPaddrlen]; - USED(m); uh = (Udphdr*)(bp->rp); /* Put back pseudo header for checksum (remember old values for icmpnoconv()) */ @@ -217,17 +207,15 @@ udpiput(Media *m, Block *bp) olen = nhgets(uh->udpplen); hnputs(uh->udpplen, len); - raddr = nhgetl(uh->udpsrc); - laddr = nhgetl(uh->udpdst); + v4tov6(raddr, uh->udpsrc); + v4tov6(laddr, uh->udpdst); lport = nhgets(uh->udpdport); rport = nhgets(uh->udpsport); - memmove(src, uh->udpsrc, IPaddrlen); - memmove(dst, uh->udpdst, IPaddrlen); if(udpsum && nhgets(uh->udpcksum)) { if(ptclcsum(bp, UDP_IPHDR, len+UDP_PHDRSIZE)) { udp.csumerr++; - netlog(Logudp, "udp: checksum error %I\n", src); + netlog(Logudp, "udp: checksum error %I\n", raddr); freeblist(bp); return; } @@ -244,10 +232,10 @@ udpiput(Media *m, Block *bp) } if(*p == nil) { - netlog(Logudp, "udp: no conv %I.%d -> %I.%d\n", src, rport, - dst, lport); + netlog(Logudp, "udp: no conv %I.%d -> %I.%d\n", raddr, rport, + laddr, lport); /* don't complain about broadcasts... */ - if(Mediaforme(dst) > 0){ + if(ipforme(raddr) == 0){ uh->Unused = ottl; hnputs(uh->udpplen, olen); icmpnoconv(bp); @@ -262,51 +250,47 @@ udpiput(Media *m, Block *bp) len -= (UDP_HDRSIZE-UDP_PHDRSIZE); bp = trimblock(bp, UDP_IPHDR+UDP_HDRSIZE, len); if(bp == nil){ - netlog(Logudp, "udp: len err %I.%d -> %I.%d\n", src, rport, - dst, lport); + netlog(Logudp, "udp: len err %I.%d -> %I.%d\n", raddr, rport, + laddr, lport); udp.lenerr++; return; } - netlog(Logudpmsg, "udp: %I.%d -> %I.%d l %d\n", src, rport, - dst, lport, len); + netlog(Logudpmsg, "udp: %I.%d -> %I.%d l %d\n", raddr, rport, + laddr, lport, len); ucb = (Udpcb*)c->ptcl; if(ucb->headers) { /* pass the src address */ bp = padblock(bp, UDP_USEAD); - hnputl(bp->rp, raddr); - if(Mediaforme(dst) > 0) - hnputl(bp->rp+4, laddr); - else if(m != 0) - hnputl(bp->rp+4, m->myip[0]); - else if(media != 0) - hnputl(bp->rp+4, media->myip[0]); + ipmove(bp->rp, raddr); + if(ipforme(laddr) == Runi) + ipmove(bp->rp+IPaddrlen, laddr); else - hnputl(bp->rp+4, laddr); - hnputs(bp->rp+8, rport); - hnputs(bp->rp+10, lport); + ipmove(bp->rp+IPaddrlen, ia); + hnputs(bp->rp+2*IPaddrlen, rport); + hnputs(bp->rp+2*IPaddrlen+2, lport); } else { /* connection oriented udp */ if(c->raddr == 0){ /* save the src address in the conversation */ - c->raddr = raddr; + ipmove(c->raddr, raddr); c->rport = rport; /* reply with the same ip address (if not broadcast) */ - if(Mediaforme(dst) > 0) - c->laddr = laddr; + if(ipforme(laddr) == Runi) + ipmove(c->laddr, laddr); else - c->laddr = m->myip[0]; + ipmove(c->laddr, ia); } } if(bp->next) bp = concatblock(bp); if(qfull(c->rq)){ - netlog(Logudp, "udp: qfull %I.%d -> %I.%d\n", src, rport, - dst, lport); + netlog(Logudp, "udp: qfull %I.%d -> %I.%d\n", raddr, rport, + laddr, lport); freeblist(bp); }else qpass(c->rq, bp); @@ -333,22 +317,24 @@ void udpadvise(Block *bp, char *msg) { Udphdr *h; - Ipaddr source, dest; + uchar source[IPaddrlen], dest[IPaddrlen]; ushort psource, pdest; Conv *s, **p; h = (Udphdr*)(bp->rp); - dest = nhgetl(h->udpdst); - source = nhgetl(h->udpsrc); + v4tov6(dest, h->udpdst); + v4tov6(source, h->udpsrc); psource = nhgets(h->udpsport); pdest = nhgets(h->udpdport); /* Look for a connection */ for(p = udp.conv; *p; p++) { s = *p; - if(s->rport == pdest && s->lport == psource) - if(s->raddr == dest && s->laddr == source){ + if(s->rport == pdest) + if(s->lport == psource) + if(ipcmp(s->raddr, dest) == 0) + if(ipcmp(s->laddr, source) == 0){ qhangup(s->rq, msg); qhangup(s->wq, msg); break; @@ -357,6 +343,14 @@ udpadvise(Block *bp, char *msg) freeblist(bp); } +int +udpstats(char *buf, int len) +{ + return snprint(buf, len, + "udp: csum %d hlen %d len %d order %d rexmit %d\n", + udp.csumerr, udp.hlenerr, udp.lenerr, udp.order, udp.rexmit); +} + void udpinit(Fs *fs) { @@ -370,6 +364,7 @@ udpinit(Fs *fs) udp.close = udpclose; udp.rcv = udpiput; udp.advise = udpadvise; + udp.stats = udpstats; udp.ipproto = IP_UDPPROTO; udp.nc = 16; udp.ptclsize = sizeof(Udpcb); diff --git a/port/portdat.h b/port/portdat.h index bd0bf933c78b346ef26a5c08d21b6a946baf5ccb..4a49a0c0646855fb9780fc2b1b6eef996180ef3e 100644 --- a/port/portdat.h +++ b/port/portdat.h @@ -158,6 +158,7 @@ struct Chan Qid pgrpid; /* for #p/notepg */ Mnt* mntptr; /* for devmnt */ ulong mid; /* for ns in devproc */ + char tag[4]; /* for iproute */ }; Chan* mchan; /* channel to mounted server */ Qid mqid; /* qid of root of mount point */