From 2a10e87cea7981243b71a597ec462a96d0a4ef82 Mon Sep 17 00:00:00 2001 From: David du Colombier <0intro@gmail.com> Date: Tue, 16 Mar 1999 00:00:00 +0000 Subject: [PATCH] Plan 9 from Bell Labs 1999-03-16 --- boot/boot.c | 6 +- boot/boot.h | 1 + ip/devip.c | 11 +- ip/esp.c | 645 +++++++++++++++++++++++++++++++++ ip/ip.h | 17 +- ip/ipaux.c | 20 -- ip/netlog.c | 13 +- pc/devether.c | 16 +- port/devbridge.c | 907 +++++++++++++++++++++++++++++++++++++++++++++++ port/devroot.c | 4 + port/log.c | 175 +++++++++ port/netif.c | 31 +- port/netif.h | 1 + port/parse.c | 44 +++ port/portdat.h | 33 ++ port/portfns.h | 6 + 16 files changed, 1877 insertions(+), 53 deletions(-) create mode 100644 ip/esp.c create mode 100644 port/devbridge.c create mode 100644 port/log.c create mode 100644 port/parse.c diff --git a/boot/boot.c b/boot/boot.c index 0d71ae2b422fd26546a2afca92bfefee5000ae2c..c755ee88641b7ada2f8869101fed5ca9a98aeabd 100644 --- a/boot/boot.c +++ b/boot/boot.c @@ -117,10 +117,14 @@ boot(int argc, char *argv[]) */ if(bind("/", "/", MREPL) < 0) fatal("bind /"); - if(mount(fd, "/", MAFTER|MCREATE, "") < 0) + if(mount(fd, "/root", MREPL|MCREATE, "") < 0) fatal("mount /"); + if(bind(rootdir, "/", MAFTER|MCREATE) < 0) + fatal("bind /"); close(fd); + setenv("rootdir", rootdir); + /* * if a local file server exists and it's not * running, start it and mount it onto /n/kfs diff --git a/boot/boot.h b/boot/boot.h index decf26b32fc3017f13c6be86277876a534b6f26c..1ca7b9e48676b136700b978749e2c9b2f3bd60dd 100644 --- a/boot/boot.h +++ b/boot/boot.h @@ -9,6 +9,7 @@ struct Method }; extern char* bootdisk; +extern char* rootdir; extern int (*cfs)(int); extern int cpuflag; extern char cputype[]; diff --git a/ip/devip.c b/ip/devip.c index f03fb89342c578f3fcbddeb63836a34975a3722a..c7606c4c145e3c9609643d9d3ec30261f37b2269 100644 --- a/ip/devip.c +++ b/ip/devip.c @@ -565,8 +565,13 @@ ipread(Chan *ch, void *a, long n, vlong off) return rv; case Qremote: buf = smalloc(Statelen); - c = f->p[PROTO(ch->qid)]->conv[CONV(ch->qid)]; - sprint(buf, "%I!%d\n", c->raddr, c->rport); + x = f->p[PROTO(ch->qid)]; + c = x->conv[CONV(ch->qid)]; + if(x->remote == nil) { + sprint(buf, "%I!%d\n", c->raddr, c->rport); + } else { + (*x->remote)(c, buf, Statelen-2); + } rv = readstr(offset, p, n, buf); free(buf); return rv; @@ -905,6 +910,8 @@ ipwrite(Chan* ch, char* a, long n, vlong) free(cb); nexterror(); } + if(cb->nf < 1) + error("short control request"); if(strcmp(cb->f[0], "connect") == 0) connectctlmsg(x, c, cb); else if(strcmp(cb->f[0], "announce") == 0) diff --git a/ip/esp.c b/ip/esp.c new file mode 100644 index 0000000000000000000000000000000000000000..1d86773cafeba716120880b1ace9ef709f2261fd --- /dev/null +++ b/ip/esp.c @@ -0,0 +1,645 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include "ip.h" + +#include "libcrypt.h" + +typedef struct Esphdr Esphdr; +typedef struct Esptail Esptail; +typedef struct Userhdr Userhdr; +typedef struct Esppriv Esppriv; +typedef struct Espcb Espcb; +typedef struct Algorithm Algorithm; + +#define DPRINT if(0)print + +enum +{ + IP_ESPPROTO = 50, + EsphdrSize = 28, // includes IP header + IphdrSize = 20, // options have been striped + EsptailSize = 2, // does not include pad or auth data + UserhdrSize = 4, // user visable header size - if enabled +}; + +struct Esphdr +{ + /* ip header */ + 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 espproto; /* Protocol */ + uchar espplen[2]; /* Header plus data length */ + uchar espsrc[4]; /* Ip source */ + uchar espdst[4]; /* Ip destination */ + + /* esp header */ + uchar espspi[4]; /* Security parameter index */ + uchar espseq[4]; /* Sequence number */ +}; + +struct Esptail +{ + uchar pad; + uchar nexthdr; +}; + +/* header as seen by the user */ +struct Userhdr +{ + uchar nexthdr; // next protocol + uchar unused[3]; +}; + +struct Esppriv +{ + ulong in; + ulong inerrors; +}; + +/* + * protocol specific part of Conv + */ +struct Espcb +{ + int incoming; + int header; // user user level header + ulong spi; + ulong seq; // last seq sent + ulong window; // for replay attacks + char *espalg; + void *espstate; // other state for esp + int espivlen; // in bytes + int espblklen; + int (*cipher)(Espcb*, uchar *buf, int len); + char *ahalg; + void *ahstate; // other state for esp + int ahlen; // auth data length in bytes + int ahblklen; + int (*auth)(Espcb*, uchar *buf, int len, uchar *hash); +}; + +struct Algorithm +{ + char *name; + int keylen; // in bits + void (*init)(Espcb*, char* name, uchar *key, int keylen); +}; + +static Conv* convlookup(Proto *esp, ulong spi); +static char *setalg(Espcb *ecb, char **f, int n, Algorithm *alg); +static void nullespinit(Espcb*, char*, uchar *key, int keylen); +static void nullahinit(Espcb*, char*, uchar *key, int keylen); +static void desespinit(Espcb *ecb, char *name, uchar *k, int n); + +static Algorithm espalg[] = +{ + "null", 0, nullespinit, + "des_56_cbc", 64, desespinit, + nil, 0, nil, +}; + +static Algorithm ahalg[] = +{ + "null", 0, nullahinit, + nil, 0, nil, +}; + +static char* +espconnect(Conv *c, char **argv, int argc) +{ + char *p, *pp; + char *e = nil; + ulong spi; + Espcb *ecb = (Espcb*)c->ptcl; + + qlock(c); + switch(argc) { + default: + e = "bad args to connect"; + break; + case 2: + p = strchr(argv[1], '!'); + if(p == nil){ + e = "malformed address"; + break; + } + *p++ = 0; + parseip(c->raddr, argv[1]); + findlocalip(c->p->f, c->laddr, c->raddr); + ecb->incoming = 0; + ecb->seq = 0; + if(strcmp(p, "*") == 0) { + qlock(c->p); + for(;;) { + spi = nrand(1<<16) + 256; + if(convlookup(c->p, spi) == nil) + break; + } + qunlock(c->p); + ecb->spi = spi; + ecb->incoming = 1; + qhangup(c->wq, nil); + } else { + spi = strtoul(p, &pp, 10); + if(pp == p) { + e = "malformed address"; + break; + } + ecb->spi = spi; + qhangup(c->rq, nil); + } + nullespinit(ecb, "null", nil, 0); + nullahinit(ecb, "null", nil, 0); + } + qunlock(c); + Fsconnected(c, e); + + return e; +} + + +static int +espstate(Conv *c, char *state, int n) +{ + return snprint(state, n, "%s", c->inuse?"Open\n":"Closed\n"); +} + +static void +espcreate(Conv *c) +{ + c->rq = qopen(64*1024, 1, 0, 0); + c->wq = qopen(64*1024, 0, 0, 0); +} + +static void +espclose(Conv *c) +{ + Espcb *ecb; + + qclose(c->rq); + qclose(c->wq); + qclose(c->eq); + ipmove(c->laddr, IPnoaddr); + ipmove(c->raddr, IPnoaddr); + + ecb = (Espcb*)c->ptcl; + free(ecb->espstate); + free(ecb->ahstate); + memset(ecb, 0, sizeof(Espcb)); + + qunlock(c); +} + +void +espkick(Conv *c, int) +{ + Esphdr *eh; + Esptail *et; + Userhdr *uh; + Espcb *ecb; + Block *bp; + int nexthdr; + int payload; + int pad; + int align; + uchar *auth; + + bp = qget(c->wq); + if(bp == nil) + return; + + qlock(c); + ecb = c->ptcl; + + if(ecb->header) { + /* make sure the message has a User header */ + bp = pullupblock(bp, UserhdrSize); + if(bp == nil) { + qunlock(c); + return; + } + uh = (Userhdr*)bp->rp; + nexthdr = uh->nexthdr; + bp->rp += UserhdrSize; + } else { + nexthdr = 0; // what should this be? + } + + payload = BLEN(bp) + ecb->espivlen; + + /* Make space to fit ip header */ + bp = padblock(bp, EsphdrSize + ecb->espivlen); + + align = 4; + if(ecb->espblklen > align) + align = ecb->espblklen; + if(align % ecb->ahblklen != 0) + panic("espkick: ahblklen is important after all"); + pad = (align-1) - (payload + EsptailSize-1)%align; + + /* + * Make space for tail + * this is done by calling padblock with a negative size + * Padblock does not change bp->wp! + */ + bp = padblock(bp, -(pad+EsptailSize+ecb->ahlen)); + bp->wp += pad+EsptailSize+ecb->ahlen; + + eh = (Esphdr *)(bp->rp); + et = (Esptail*)(bp->rp + EsphdrSize + payload + pad); + + // fill in tail + et->pad = pad; + et->nexthdr = nexthdr; + + ecb->cipher(ecb, bp->rp+EsphdrSize, payload+pad+EsptailSize); + auth = bp->rp + EsphdrSize + payload + pad + EsptailSize; + ecb->auth(ecb, bp->rp+IphdrSize, (EsphdrSize-IphdrSize)+payload+pad+EsptailSize, auth); + + // fill in head + hnputl(eh->espspi, ecb->spi); + hnputl(eh->espseq, ++ecb->seq); + v6tov4(eh->espsrc, c->laddr); + v6tov4(eh->espdst, c->raddr); + eh->espproto = IP_ESPPROTO; + eh->frag[0] = 0; + eh->frag[1] = 0; + + qunlock(c); +//print("esp: pass down: %uld\n", BLEN(bp)); + ipoput(c->p->f, bp, 0, c->ttl); +} + +void +espiput(Proto *esp, uchar*, Block *bp) +{ + Esphdr *eh; + Esptail *et; + Userhdr *uh; + Conv *c; + Espcb *ecb; + uchar raddr[IPaddrlen], laddr[IPaddrlen]; + Fs *f; + uchar *auth; + ulong spi; + int payload, nexthdr; + + f = esp->f; + + bp = pullupblock(bp, EsphdrSize+EsptailSize); + if(bp == nil) { + netlog(f, Logesp, "esp: short packet\n"); + return; + } + + eh = (Esphdr*)(bp->rp); + spi = nhgetl(eh->espspi); + v4tov6(raddr, eh->espsrc); + v4tov6(laddr, eh->espdst); + + qlock(esp); + /* Look for a conversation structure for this port */ + c = convlookup(esp, spi); + if(c == nil) { + qunlock(esp); + netlog(f, Logesp, "esp: no conv %I -> %I!%d\n", raddr, + laddr, spi); + icmpnoconv(f, bp); + freeblist(bp); + return; + } + + qlock(c); + qunlock(esp); + + ecb = c->ptcl; + // too hard to do decryption/authentication on block lists + if(bp->next) + bp = concatblock(bp); + + if(BLEN(bp) < EsphdrSize + ecb->espivlen + EsptailSize + ecb->ahlen) { + qunlock(esp); + netlog(f, Logesp, "esp: short block %I -> %I!%d\n", raddr, + laddr, spi); + freeb(bp); + return; + } + + eh = (Esphdr*)(bp->rp); + auth = bp->wp - ecb->ahlen; + if(!ecb->auth(ecb, eh->espspi, auth-eh->espspi, auth)) { + qunlock(esp); + netlog(f, Logesp, "esp: bad auth %I -> %I!%d\n", raddr, + laddr, spi); + freeb(bp); + return; + } + + payload = BLEN(bp)-EsphdrSize-ecb->ahlen; + if(payload<=0 || payload%4 != 0 || payload%ecb->espblklen!=0) { + qunlock(esp); + netlog(f, Logesp, "esp: bad length %I -> %I!%d payload=%d BLEN=%d\n", raddr, + laddr, spi, payload, BLEN(bp)); + freeb(bp); + return; + } + ecb->cipher(ecb, bp->rp+EsphdrSize, payload); + + payload -= EsptailSize; + et = (Esptail*)(bp->rp + EsphdrSize + payload); + payload -= et->pad + ecb->espivlen; + nexthdr = et->nexthdr; + if(payload <= 0) { + qunlock(esp); + netlog(f, Logesp, "esp: short packet after decrypt %I -> %I!%d\n", raddr, + laddr, spi); + freeb(bp); + return; + } + + // trim packet + bp->rp += EsphdrSize + ecb->espivlen; + bp->wp = bp->rp + payload; + if(ecb->header) { + // assume UserhdrSize < EsphdrSize + bp->rp -= UserhdrSize; + uh = (Userhdr*)bp->rp; + memset(uh, 0, UserhdrSize); + uh->nexthdr = nexthdr; + } + + if(qfull(c->rq)){ + netlog(f, Logesp, "esp: qfull %I -> %I.%uld\n", raddr, + laddr, spi); + freeblist(bp); + }else { +//print("esp: pass up: %uld\n", BLEN(bp)); + qpass(c->rq, bp); + } + + qunlock(c); +} + +char* +espctl(Conv *c, char **f, int n) +{ + Espcb *ecb = c->ptcl; + char *e = nil; + + qlock(c); + if(strcmp(f[0], "esp") == 0) + e = setalg(ecb, f, n, espalg); + else if(strcmp(f[0], "ah") == 0) + e = setalg(ecb, f, n, ahalg); + else if(strcmp(f[0], "header") == 0) + ecb->header = 1; + else if(strcmp(f[0], "noheader") == 0) + ecb->header = 0; + else + e = "unknown control request"; + qunlock(c); + return e; +} + +void +espadvise(Proto *esp, Block *bp, char *msg) +{ + Esphdr *h; + Conv *c; + ulong spi; + + h = (Esphdr*)(bp->rp); + + spi = nhgets(h->espspi); + qlock(esp); + c = convlookup(esp, spi); + if(c != nil) { + qhangup(c->rq, msg); + qhangup(c->wq, msg); + } + qunlock(esp); + freeblist(bp); +} + +int +espstats(Proto *esp, char *buf, int len) +{ + Esppriv *upriv; + + upriv = esp->priv; + return snprint(buf, len, "%lud %lud\n", + upriv->in, + upriv->inerrors); +} + +static int +esplocal(Conv *c, char *buf, int len) +{ + Espcb *ecb = c->ptcl; + int n; + + qlock(c); + if(ecb->incoming) + n = snprint(buf, len, "%I!%uld\n", c->laddr, ecb->spi); + else + n = snprint(buf, len, "%I\n", c->laddr); + qunlock(c); + return n; +} + +static int +espremote(Conv *c, char *buf, int len) +{ + Espcb *ecb = c->ptcl; + int n; + + qlock(c); + if(ecb->incoming) + n = snprint(buf, len, "%I\n", c->raddr); + else + n = snprint(buf, len, "%I!%uld\n", c->raddr, ecb->spi); + qunlock(c); + return n; +} + +// should have esp locked +static Conv* +convlookup(Proto *esp, ulong spi) +{ + Conv *c, **p; + Espcb *ecb; + + for(p=esp->conv; *p; p++){ + c = *p; + ecb = c->ptcl; + if(ecb->incoming && ecb->spi == spi) + return c; + } +} + +static char * +setalg(Espcb *ecb, char **f, int n, Algorithm *alg) +{ + uchar *key; + int i, nbyte, nchar; + int c; + + if(n < 2) + return "bad format"; + for(; alg->name; alg++) + if(strcmp(f[1], alg->name) == 0) + break; + if(alg->name == nil) + return "unknown algorithm"; + + if(n != 3) + return "bad format"; + nbyte = (alg->keylen + 7) >> 3; + nchar = strlen(f[2]); + for(i=0; i= '0' && c <= '9') + f[2][i] -= '0'; + else if(c >= 'a' && c <= 'f') + f[2][i] -= 'a'-10; + else if(c >= 'A' && c <= 'F') + f[2][i] -= 'A'-10; + else + return "bad character in key"; + } + key = smalloc(nbyte); + for(i=0; i>1] |= c; + } + + alg->init(ecb, alg->name, key, alg->keylen); + free(key); + return nil; +} + +static int +nullcipher(Espcb*, uchar*, int) +{ + return 1; +} + +static void +nullespinit(Espcb *ecb, char *name, uchar*, int) +{ + ecb->espalg = name; + ecb->espblklen = 1; + ecb->espivlen = 0; + ecb->cipher = nullcipher; +} + +static int +nullauth(Espcb*, uchar*, int, uchar*) +{ + return 1; +} + +static void +nullahinit(Espcb *ecb, char *name, uchar*, int) +{ + ecb->ahalg = name; + ecb->ahblklen = 1; + ecb->ahlen = 0; + ecb->auth = nullauth; +} + +static int +descipher(Espcb *ecb, uchar *p, int n) +{ + uchar tmp[8]; + uchar *pp, *tp, *ip, *eip, *ep; + DESstate *ds = ecb->espstate; + + ep = p + n; + if(ecb->incoming) { + memmove(ds->ivec, p, 8); + p += 8; + while(p < ep){ + memmove(tmp, p, 8); + block_cipher(ds->expanded, p, 1); + tp = tmp; + ip = ds->ivec; + for(eip = ip+8; ip < eip; ){ + *p++ ^= *ip; + *ip++ = *tp++; + } + } + } else { + memmove(p, ds->ivec, 8); + for(p += 8; p < ep; p += 8){ + pp = p; + ip = ds->ivec; + for(eip = ip+8; ip < eip; ) + *pp++ ^= *ip++; + block_cipher(ds->expanded, p, 0); + memmove(ds->ivec, p, 8); + } + } + return 1; +} + +static void +desespinit(Espcb *ecb, char *name, uchar *k, int n) +{ + uchar key[8]; + uchar ivec[8]; + int i; + + // bits to bytes + n = (n+7)>>3; + if(n > 8) + n = 8; + memset(key, 0, sizeof(key)); + memmove(key, k, n); + for(i=0; i<8; i++) + ivec[i] = nrand(256); + ecb->espalg = name; + ecb->espblklen = 8; + ecb->espivlen = 8; + ecb->cipher = descipher; + ecb->espstate = smalloc(sizeof(DESstate)); + setupDESstate(ecb->espstate, key, ivec); +} + +void +espinit(Fs *fs) +{ + Proto *esp; + + esp = smalloc(sizeof(Proto)); + esp->priv = smalloc(sizeof(Esppriv)); + esp->name = "esp"; + esp->kick = espkick; + esp->connect = espconnect; + esp->announce = nil; + esp->ctl = espctl; + esp->state = espstate; + esp->create = espcreate; + esp->close = espclose; + esp->rcv = espiput; + esp->advise = espadvise; + esp->stats = espstats; + esp->local = esplocal; + esp->remote = espremote; + esp->ipproto = IP_ESPPROTO; + esp->nc = Nchans; + esp->ptclsize = sizeof(Espcb); + + Fsproto(fs, esp); +} diff --git a/ip/ip.h b/ip/ip.h index d5dc298d13a6053f9737fc035fcc1ffc43c76972..2d07ad2a3a956468f8c9e4699295a3767ed6a3e7 100644 --- a/ip/ip.h +++ b/ip/ip.h @@ -11,7 +11,7 @@ typedef struct Ipmulti Ipmulti; typedef struct Ipmux Ipmux; typedef struct IProuter IProuter; typedef struct Ipifc Ipifc; -typedef struct Log Log; +typedef struct Netlog Netlog; typedef struct Ifclog Ifclog; typedef struct Medium Medium; typedef struct Proto Proto; @@ -212,6 +212,7 @@ struct Proto void (*advise)(Proto*, Block*, char*); int (*stats)(Proto*, char*, int); int (*local)(Conv*, char*, int); + int (*remote)(Conv*, char*, int); int (*inuse)(Conv*); Fs *f; /* file system this proto is part of */ @@ -258,7 +259,7 @@ struct Fs Route *v6root[1<>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); - if(n > 0 && cb->buf[n-1] == '\n') - n--; - cb->buf[n] = '\0'; - cb->nf = parsefields(cb->buf, cb->f, nelem(cb->f), " "); - return cb; -} - /* * parse a hex mac address */ diff --git a/ip/netlog.c b/ip/netlog.c index 19d0112d140dda0c23f96c5763937b1ca2c77984..473a6f82941874c65e2640a710acefcb9eb046be 100644 --- a/ip/netlog.c +++ b/ip/netlog.c @@ -13,7 +13,7 @@ enum { /* * action log */ -struct Log { +struct Netlog { Lock; int opens; char* buf; @@ -29,12 +29,12 @@ struct Log { Rendez; }; -typedef struct Logflag { +typedef struct Netlogflag { char* name; int mask; -} Logflag; +} Netlogflag; -static Logflag flags[] = +static Netlogflag flags[] = { { "ppp", Logppp, }, { "ip", Logip, }, @@ -49,6 +49,7 @@ static Logflag flags[] = { "tcpmsg", Logtcp|Logtcpmsg, }, { "udpmsg", Logudp|Logudpmsg, }, { "ipmsg", Logip|Logipmsg, }, + { "esp", Logesp, }, { nil, 0, }, }; @@ -57,7 +58,7 @@ static char Ebadnetctl[] = "unknown netlog ctl message"; void netloginit(Fs *f) { - f->alog = smalloc(sizeof(Log)); + f->alog = smalloc(sizeof(Netlog)); } void @@ -158,7 +159,7 @@ char* netlogctl(Fs *f, char* s, int len) { int i, n, set; - Logflag *fp; + Netlogflag *fp; char *fields[10], *p, buf[256]; if(len == 0) diff --git a/pc/devether.c b/pc/devether.c index 3b574de585b4ce888aa90cd216f513dbaefa3032..2d679c9246afdadc1d1c1e0e00437e7e2c1666b3 100644 --- a/pc/devether.c +++ b/pc/devether.c @@ -134,7 +134,7 @@ etheriq(Ether* ether, Block* bp, int freebp) { Etherpkt *pkt; ushort type; - int len; + int len, multi, forme; Netfile **ep, *f, **fp, *fx; Block *xbp; @@ -146,8 +146,9 @@ etheriq(Ether* ether, Block* bp, int freebp) fx = 0; ep = ðer->f[Ntypes]; + multi = pkt->d[0] & 1; /* check for valid multcast addresses */ - if((pkt->d[0] & 1) && memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) && ether->prom == 0){ + if(multi && memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) && ether->prom == 0){ if(!activemulti(ether, pkt->d, sizeof(pkt->d))){ if(freebp){ freeb(bp); @@ -157,6 +158,9 @@ etheriq(Ether* ether, Block* bp, int freebp) } } + // is it for me? + forme = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0; + /* * Multiplex the packet to all the connections which want it. * If the packet is not to be used subsequently (freebp != 0), @@ -164,7 +168,7 @@ etheriq(Ether* ether, Block* bp, int freebp) * saving a copy of the data (usual case hopefully). */ for(fp = ether->f; fp < ep; fp++){ - if((f = *fp) && (f->type == type || f->type < 0)){ + if((f = *fp) && (f->type == type || f->type < 0) && (forme || multi || f->prom)){ if(f->type > -2){ if(freebp && fx == 0) fx = f; @@ -197,7 +201,7 @@ etheriq(Ether* ether, Block* bp, int freebp) static int etheroq(Ether* ether, Block* bp) { - int len, loopback, s; + int len, loopback, s, mine; Etherpkt *pkt; ether->outpackets++; @@ -208,10 +212,14 @@ etheroq(Ether* ether, Block* bp) * in promiscuous mode. * If it's a loopback packet indicate to etheriq that the data isn't * needed and return, etheriq will pass-on or free the block. + * To enable bridging to work, only packets that were originated + * by this interface are feedback */ pkt = (Etherpkt*)bp->rp; len = BLEN(bp); loopback = (memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0); + mine = memcmp(pkt->s, ether->ea, sizeof(pkt->s)) == 0; + if(mine) if(loopback || memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) == 0 || ether->prom){ s = splhi(); etheriq(ether, bp, loopback); diff --git a/port/devbridge.c b/port/devbridge.c new file mode 100644 index 0000000000000000000000000000000000000000..4be66a3fec588ee01a458e2cd91c2536ead99e91 --- /dev/null +++ b/port/devbridge.c @@ -0,0 +1,907 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/netif.h" +#include "../port/error.h" + +typedef struct Bridge Bridge; +typedef struct Port Port; +typedef struct Centry Centry; + +enum +{ + Qtopdir= 1, /* top level directory */ + + Qbridgedir, /* bridge* directory */ + Qbctl, + Qstats, + Qcache, + Qlog, + + Qportdir, /* directory for a protocol */ + Qpctl, + Qlocal, + Qstatus, + + MaxQ, + + Maxbridge= 4, + Maxport= 16, // power of 2 + CacheHash= 257, // prime + CacheLook= 5, // how many cache entries to examine + CacheSize= (CacheHash+CacheLook-1), + CacheTimeout= 5*60, // timeout for cache entry in seconds + + Addrlen= 16, // must be long enough of IP addr and ether addr +}; + +Dirtab bridgedirtab[]={ + "ctl", {Qbctl}, 0, 0666, + "stats", {Qstats}, 0, 0444, + "cache", {Qcache}, 0, 0444, + "log", {Qlog}, 0, 0666, +}; + +Dirtab portdirtab[]={ + "ctl", {Qpctl}, 0, 0666, + "local", {Qlocal}, 0, 0444, + "status", {Qstatus}, 0, 0444, +}; + +enum { + Logcache= (1<<0), + Logmcast= (1<<1), +}; + +// types of interfaces +enum +{ + Tether, + Ttunnel, +}; + +static Logflag logflags[] = +{ + { "cache", Logcache, }, + { "multicast", Logmcast, }, + { nil, 0, }, +}; + +static Dirtab *dirtab[MaxQ]; + +#define TYPE(x) ((x).path & 0xff) +#define PORT(x) (((x).path >> 8)&(Maxport-1)) +#define QID(x, y) (((x)<<8) | (y)) + +struct Centry +{ + uchar d[Eaddrlen]; + int port; + long expire; // entry expires this number of seconds after bootime + long src; + long dst; +}; + +struct Bridge +{ + QLock; + int nport; + Port *port[Maxport]; + Centry cache[CacheSize]; + int hit; + int miss; + + Log; +}; + +struct Port +{ + int id; + Bridge *bridge; + int ref; + int closed; + + Chan *data[2]; // channel to data + + int mcast; // send multi cast packets + Proc *readp; // read proc + + // the following uniquely identifies the port + int type; + uchar addr[Addrlen]; + + // owner hash - avoids bind/unbind races + ulong ownhash; + + // various stats + int in; // number of packets read + int inmulti; // multicast or broadcast + int inunknown; // unknown address + int out; // number of packets read + int outmulti; // multicast or broadcast + int outunknown; // unknown address + int nentry; // number of cache entries for this port +}; + +static Bridge bridgetab[Maxbridge]; + +static int m2p[] = { + [OREAD] 4, + [OWRITE] 2, + [ORDWR] 6 +}; + +static int bridgegen(Chan *c, Dirtab*, int, int s, Dir *dp); +static void portbind(Bridge *b, int argc, char *argv[]); +static void portunbind(Bridge *b, int argc, char *argv[]); +static void etherread(void *a); +static char *cachedump(Bridge *b); +static void portfree(Port *port); +static void cacheflushport(Bridge *b, int port); + +extern ulong parseip(uchar*, char*); + +static void +bridgeinit(void) +{ + int i; + Dirtab *dt; + // setup dirtab with non directory entries + for(i=0; iqid)] = dt; + } + for(i=0; iqid)] = dt; + } +} + +static Chan* +bridgeattach(char* spec) +{ + Chan *c; + int dev; + + dev = atoi(spec); + if(dev<0 || dev >= Maxbridge) + error("bad specification"); + + c = devattach('B', spec); + c->qid = (Qid){QID(0, Qtopdir)|CHDIR, 0}; + c->dev = dev; + + return c; +} + +static int +bridgewalk(Chan *c, char *name) +{ + Path *op; + + if(strcmp(name, "..") == 0){ + switch(TYPE(c->qid)){ + case Qtopdir: + case Qbridgedir: + c->qid = (Qid){CHDIR|Qtopdir, 0}; + break; + case Qportdir: + c->qid = (Qid){CHDIR|Qbridgedir, 0}; + break; + default: + panic("bridgewalk %lux", c->qid.path); + } + op = c->path; + c->path = ptenter(&syspt, op, name); + decref(op); + return 1; + } + + return devwalk(c, name, 0, 0, bridgegen); +} + +static void +bridgestat(Chan* c, char* db) +{ + devstat(c, db, nil, 0, bridgegen); +} + +static Chan* +bridgeopen(Chan* c, int omode) +{ + int perm; + Bridge *b; + + omode &= 3; + perm = m2p[omode]; + USED(perm); + + b = bridgetab + c->dev; + USED(b); + + switch(TYPE(c->qid)) { + default: + break; + case Qtopdir: + case Qbridgedir: + case Qportdir: + case Qstatus: + case Qlocal: + case Qstats: + if(omode != OREAD) + error(Eperm); + break; + case Qlog: + logopen(b); + break; + case Qcache: + if(omode != OREAD) + error(Eperm); + c->aux = cachedump(b); + break; + } + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; +} + +static void +bridgeclose(Chan* c) +{ + Bridge *b = bridgetab + c->dev; + + switch(TYPE(c->qid)) { + case Qcache: + if(c->flag & COPEN) + free(c->aux); + break; + case Qlog: + if(c->flag & COPEN) + logclose(b); + break; + } +} + +static long +bridgeread(Chan *c, void *a, long n, vlong off) +{ + char buf[256]; + Bridge *b = bridgetab + c->dev; + Port *port; + int i; + + USED(off); + switch(TYPE(c->qid)) { + default: + error(Eperm); + case Qtopdir: + case Qbridgedir: + case Qportdir: + return devdirread(c, a, n, 0, 0, bridgegen); + case Qlog: + return logread(b, a, off, n); + case Qstatus: + qlock(b); + port = b->port[PORT(c->qid)]; + if(port == 0) + strcpy(buf, "unbound\n"); + else { + i = 0; + switch(port->type) { + default: panic("bridgeread: unknown port type: %d", port->type); + case Tether: + i += snprint(buf+i, sizeof(buf)-i, "ether %E: ", port->addr); + break; + case Ttunnel: + i += snprint(buf+i, sizeof(buf)-i, "tunnel %I: ", port->addr); + break; + } + i += snprint(buf+i, sizeof(buf)-i, "in=%d/%d/%d out=%d/%d/%d\n", + port->in, port->inmulti, port->inunknown, + port->out, port->outmulti, port->outunknown); + USED(i); + } + n = readstr(off, a, n, buf); + qunlock(b); + return n; + case Qcache: + n = readstr(off, a, n, c->aux); + return n; + } +} + +static long +bridgewrite(Chan *c, void *a, long n, vlong off) +{ + Bridge *b = bridgetab + c->dev; + Cmdbuf *cb; + char *arg0; + char *p; + + USED(off); + + switch(TYPE(c->qid)) { + default: + error(Eperm); + case Qbctl: + cb = parsecmd(a, n); + qlock(b); + if(waserror()) { + qunlock(b); + free(cb); + nexterror(); + } + if(cb->nf == 0) + error("short write"); + arg0 = cb->f[0]; + if(strcmp(arg0, "bind") == 0) + portbind(b, cb->nf-1, cb->f+1); + else if(strcmp(arg0, "unbind") == 0) + portunbind(b, cb->nf-1, cb->f+1); + else if(strcmp(arg0, "cacheflush") == 0) { + log(b, Logcache, "cache flush\n"); + memset(b->cache, 0, CacheSize*sizeof(Centry)); + } else + error("unknown control request"); + poperror(); + qunlock(b); + free(cb); + return n; + case Qlog: + cb = parsecmd(a, n); + p = logctl(b, cb->nf, cb->f, logflags); + free(cb); + if(p != nil) + error(p); + return n; + } +} + +static int +bridgegen(Chan *c, Dirtab*, int, int s, Dir *dp) +{ + Bridge *b = bridgetab + c->dev; + int type = TYPE(c->qid); + char buf[32]; + Dirtab *dt; + Qid qid; + + switch(type) { + default: + // non directory entries end up here + if(c->qid.path & CHDIR) + panic("bridgegen: unexpected directory"); + if(s != 0) + return -1; + dt = dirtab[TYPE(c->qid)]; + if(dt == nil) + panic("bridgegen: unknown type: %d", TYPE(c->qid)); + devdir(c, c->qid, dt->name, dt->length, eve, dt->perm, dp); + return 1; + case Qtopdir: + if(s != 0) + return -1; + sprint(buf, "bridge%ld", c->dev); + devdir(c, (Qid){QID(0,Qbridgedir)|CHDIR,0}, buf, 0, eve, 0555, dp); + return 1; + case Qbridgedir: + if(sqid, dt->name, dt->length, eve, dt->perm, dp); + return 1; + } + s -= nelem(bridgedirtab); + if(s >= b->nport) + return -1; + qid = (Qid){QID(s,Qportdir)|CHDIR, 0}; + snprint(buf, sizeof(buf), "%d", s); + devdir(c, qid, buf, 0, eve, 0555, dp); + return 1; + case Qportdir: + if(s>=nelem(portdirtab)) + return -1; + dt = portdirtab+s; + qid = (Qid){QID(PORT(c->qid),TYPE(dt->qid)),0}; + devdir(c, qid, dt->name, dt->length, eve, dt->perm, dp); + return 1; + } +} + +// also in netif.c +static int +parseaddr(uchar *to, char *from, int alen) +{ + char nip[4]; + char *p; + int i; + + p = from; + for(i = 0; i < alen; 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; +} + +// assumes b is locked +static void +portbind(Bridge *b, int argc, char *argv[]) +{ + Port *port; + char path[8*NAMELEN]; + char buf[100]; + char *dev, *dev2=nil, *p; + Chan *ctl; + int type=0, i, n; + char *usage = "usage: bind ether|tunnel addr ownhash dev [dev2]"; + uchar addr[Addrlen]; + ulong ownhash; + + memset(addr, 0, Addrlen); + if(argc < 4) + error(usage); + if(strcmp(argv[0], "ether") == 0) { + if(argc != 4) + error(usage); + type = Tether; + parseaddr(addr, argv[1], Eaddrlen); + } else if(strcmp(argv[0], "tunnel") == 0) { + if(argc != 5) + error(usage); + type = Ttunnel; + parseip(addr, argv[1]); + dev2 = argv[4]; + } else + error(usage); + ownhash = atoi(argv[2]); + dev = argv[3]; + for(i=0; inport; i++) { + port = b->port[i]; + if(port != nil) + if(port->type == type) + if(memcmp(port->addr, addr, Addrlen) == 0) + error("port in use"); + } + for(i=0; iport[i] == nil) + break; + if(i == Maxport) + error("no more ports"); + port = smalloc(sizeof(Port)); + port->ref = 1; + port->id = i; + port->ownhash = ownhash; + + if(waserror()) { + portfree(port); + nexterror(); + } + + port->type = type; + memmove(port->addr, addr, Addrlen); + switch(port->type) { + default: panic("portbind: unknown port type: %d", type); + case Tether: + snprint(path, sizeof(path), "%s/clone", dev); + ctl = namec(path, Aopen, ORDWR, 0); + if(waserror()) { + cclose(ctl); + nexterror(); + } + // check addr? + + // get directory name + n = devtab[ctl->type]->read(ctl, buf, sizeof(buf), 0); + buf[n] = 0; + for(p = buf; *p == ' '; p++) + ; + snprint(path, sizeof(path), "%s/%lud/data", dev, strtoul(p, 0, 0)); + + // setup connection to be promiscuous + snprint(buf, sizeof(buf), "connect -1"); + devtab[ctl->type]->write(ctl, buf, strlen(buf), 0); + snprint(buf, sizeof(buf), "promiscuous"); + devtab[ctl->type]->write(ctl, buf, strlen(buf), 0); + + // open data port + port->data[0] = namec(path, Aopen, ORDWR, 0); + // dup it + incref(port->data[0]); + port->data[1] = port->data[0]; + + poperror(); + cclose(ctl); + + break; + case Ttunnel: + port->data[0] = namec(dev, Aopen, OREAD, 0); + port->data[1] = namec(dev2, Aopen, OWRITE, 0); + break; + } + + poperror(); + + // commited to binding port + b->port[port->id] = port; + port->bridge = b; + if(b->nport <= port->id) + b->nport = port->id+1; + + // assumes kproc always succeeds + port->ref++; + kproc("etherread", etherread, port); // poperror must be next +} + +// assumes b is locked +static void +portunbind(Bridge *b, int argc, char *argv[]) +{ + Port *port=nil; + int type=0, i; + char *usage = "usage: unbind ether|tunnel addr [ownhash]"; + uchar addr[Addrlen]; + ulong ownhash; + + memset(addr, 0, Addrlen); + if(argc < 2 || argc > 3) + error(usage); + if(strcmp(argv[0], "ether") == 0) { + type = Tether; + parseaddr(addr, argv[1], Eaddrlen); + } else if(strcmp(argv[0], "tunnel") == 0) { + type = Ttunnel; + parseip(addr, argv[1]); + } else + error(usage); + if(argc == 3) + ownhash = atoi(argv[2]); + else + ownhash = 0; + for(i=0; inport; i++) { + port = b->port[i]; + if(port != nil) + if(port->type == type) + if(memcmp(port->addr, addr, Addrlen) == 0) + break; + } + if(i == b->nport) + error("port not found"); + if(ownhash != 0 && port->ownhash != 0 && ownhash != port->ownhash) + error("bad owner hash"); + + port->closed = 1; + b->port[i] = nil; // port is now unbound + cacheflushport(b, i); + + // try and stop reader + if(port->readp) + postnote(port->readp, 1, "unbind", 0); + portfree(port); +} + +// assumes b is locked +static Centry * +cachelookup(Bridge *b, uchar d[Eaddrlen]) +{ + int i; + uint h; + Centry *p; + long sec; + + // dont cache multicast or broadcast + if(d[0] & 1) + return 0; + + h = 0; + for(i=0; icache + h; + sec = TK2SEC(m->ticks); + for(i=0; id, Eaddrlen) == 0) { + p->dst++; + if(sec >= p->expire) { + log(b, Logcache, "expired cache entry: %E %d\n", + d, p->port); + return nil; + } + p->expire = sec + CacheTimeout; + return p; + } + } + log(b, Logcache, "cache miss: %E\n", d); + return nil; +} + +// assumes b is locked +static void +cacheupdate(Bridge *b, uchar d[Eaddrlen], int port) +{ + int i; + uint h; + Centry *p, *pp; + long sec; + + // dont cache multicast or broadcast + if(d[0] & 1) { + log(b, Logcache, "bad source address: %E\n", d); + return; + } + + h = 0; + for(i=0; icache + h; + pp = p; + sec = p->expire; + + // look for oldest entry + for(i=0; id, d, Eaddrlen) == 0) { + p->expire = TK2SEC(m->ticks) + CacheTimeout; + if(p->port != port) { + log(b, Logcache, "NIC changed port %d->%d: %E\n", + p->port, port, d); + p->port = port; + } + p->src++; + return; + } + if(p->expire < sec) { + sec = p->expire; + pp = p; + } + } + if(pp->expire != 0) + log(b, Logcache, "bumping from cache: %E %d\n", pp->d, pp->port); + pp->expire = TK2SEC(m->ticks) + CacheTimeout; + memmove(pp->d, d, Eaddrlen); + pp->port = port; + pp->src = 1; + pp->dst = 0; + log(b, Logcache, "adding to cache: %E %d\n", pp->d, pp->port); +} + +// assumes b is locked +static void +cacheflushport(Bridge *b, int port) +{ + Centry *ce; + int i; + + ce = b->cache; + for(i=0; iport != port) + continue; + memset(ce, 0, sizeof(Centry)); + } +} + +static char * +cachedump(Bridge *b) +{ + int i, n; + long sec, off; + char *buf, *p, *ep; + Centry *ce; + char c; + + qlock(b); + if(waserror()) { + qunlock(b); + nexterror(); + } + sec = TK2SEC(m->ticks); + n = 0; + for(i=0; icache[i].expire != 0) + n++; + + n *= 51; // change if print format is changed + n += 10; // some slop at the end + buf = malloc(n); + p = buf; + ep = buf + n; + ce = b->cache; + off = seconds() - sec; + for(i=0; iexpire == 0) + continue; + c = (sec < ce->expire)?'v':'e'; + p += snprint(p, ep-p, "%E %2d %10ld %10ld %10ld %c\n", ce->d, + ce->port, ce->src, ce->dst, ce->expire+off, c); + } + *p = 0; + poperror(); + qunlock(b); + + return buf; +} + + + +// assumes b is locked +static void +ethermultiwrite(Bridge *b, Block *bp, Port *port) +{ + Chan *c; + Block *bp2; + Etherpkt *ep; + int i, mcast, bcast; + static uchar bcastaddr[Eaddrlen] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + + if(waserror()) { + if(bp) + freeb(bp); + nexterror(); + } + + ep = (Etherpkt*)bp->rp; + mcast = ep->d[0] & 1; + if(mcast) + bcast = memcmp(ep->d, bcastaddr, Eaddrlen) == 0; + else + bcast = 0; + + c = nil; + for(i=0; inport; i++) { + if(i == port->id || b->port[i] == nil) + continue; + if(mcast && !bcast && !b->port[i]->mcast) + continue; + b->port[i]->out++; + if(mcast) + b->port[i]->outmulti++; + else + b->port[i]->outunknown++; + + // delay one so that the last write does not copy + if(c != nil) { + // can not use write since it changes scr addr + bp2 = copyblock(bp, blocklen(bp)); + devtab[c->type]->bwrite(c, bp2, 0); + } + c = b->port[i]->data[1]; + } + + // last write free block + if(c) { + bp2 = bp; bp = nil; USED(bp); + devtab[c->type]->bwrite(c, bp2, 0); + } else + freeb(bp); + + poperror(); +} + +/* + * process to read from the ethernet + */ +static void +etherread(void *a) +{ + Port *port = a, *oport; + Bridge *b = port->bridge; + Block *bp, *bp2; + Etherpkt *ep; + Centry *ce; + + + qlock(b); + port->readp = up; /* hide identity under a rock for unbind */ + + while(!port->closed){ + // release lock to read - error means it is time to quit + qunlock(b); + if(waserror()) { + qlock(b); + break; + } +//print("devbridge: etherread: reading\n"); + bp = devtab[port->data[0]->type]->bread(port->data[0], ETHERMAXTU, 0); +//print("devbridge: etherread: blocklen = %d\n", blocklen(bp)); + poperror(); + qlock(b); + + if(port->closed) + break; + if(waserror()) { + if(bp) + freeb(bp); +print("devbridge: etherread: %r\n"); + continue; + } + if(blocklen(bp) < ETHERMINTU) + error("short packet"); + port->in++; + ep = (Etherpkt*)bp->rp; + cacheupdate(b, ep->s, port->id); + + if(ep->d[0] & 1) { + log(b, Logmcast, "mulitcast: port=%d src=%E dst=%E type=%#.4ux\n", + port->id, ep->s, ep->d, (ep->type[0]<<8)|ep->type[1] ); + port->inmulti++; + bp2 = bp; bp = nil; + ethermultiwrite(b, bp2, port); + } else { + ce = cachelookup(b, ep->d); + if(ce == nil) { + b->miss++; + port->inunknown++; + bp2 = bp; bp = nil; + ethermultiwrite(b, bp2, port); + } else if (ce->port != port->id) { + b->hit++; + bp2 = bp; bp = nil; + oport = b->port[ce->port]; + oport->out++; + devtab[oport->data[1]->type]->bwrite(oport->data[1], bp2, 0); + } + } + + poperror(); + if(bp) + freeb(bp); + } +print("etherread: trying to exit\n"); + port->readp = nil; + portfree(port); + qunlock(b); + pexit("hangup", 1); +} + +// hold b lock +static void +portfree(Port *port) +{ + port->ref--; + if(port->ref < 0) + panic("portfree: bad ref"); + if(port->ref > 0) + return; + + if(port->data[0]) + cclose(port->data[0]); + if(port->data[1]) + cclose(port->data[1]); + memset(port, 0, sizeof(Port)); + free(port); +} + +Dev bridgedevtab = { + 'B', + "bridge", + + devreset, + bridgeinit, + bridgeattach, + devclone, + bridgewalk, + bridgestat, + bridgeopen, + devcreate, + bridgeclose, + bridgeread, + devbread, + bridgewrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/port/devroot.c b/port/devroot.c index f6f04c1d7a21eae487c03ec901e65a3679c75f30..d533eae91324a973c07ec4794247e860f2388d11 100644 --- a/port/devroot.c +++ b/port/devroot.c @@ -14,6 +14,9 @@ enum{ Qnet, Qnetalt, Qrecover, + Qroot, /* boot root */ + + // add new entries above this Qboot, /* readable files */ Nfiles=13, /* max root files */ @@ -30,6 +33,7 @@ Dirtab rootdir[Nfiles]={ "net", {Qnet|CHDIR}, 0, 0777, "net.alt", {Qnetalt|CHDIR}, 0, 0777, "recover", {Qrecover}, 0, 0777, + "root", {Qroot|CHDIR}, 0, 0777, }; static uchar *rootdata[Nfiles]; diff --git a/port/log.c b/port/log.c new file mode 100644 index 0000000000000000000000000000000000000000..0d68028b4779994b54624fb35653f4f2ffe1f6e5 --- /dev/null +++ b/port/log.c @@ -0,0 +1,175 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +static char Ebadlogctl[] = "unknown log ctl message"; + +enum { + Nlog = 4*1024, +}; + +void +logopen(Log *alog) +{ + lock(alog); + if(waserror()){ + unlock(alog); + nexterror(); + } + if(alog->opens == 0){ + if(alog->buf == nil) + alog->buf = malloc(Nlog); + alog->rptr = alog->buf; + alog->end = alog->buf + Nlog; + } + alog->opens++; + unlock(alog); + poperror(); +} + +void +logclose(Log *alog) +{ + lock(alog); + if(waserror()){ + unlock(alog); + nexterror(); + } + alog->opens--; + if(alog->opens == 0){ + free(alog->buf); + alog->buf = nil; + } + unlock(alog); + poperror(); +} + +static int +logready(void *a) +{ + Log *alog = a; + + return alog->len; +} + +long +logread(Log *alog, void *a, ulong, long n) +{ + int i, d; + char *p, *rptr; + + qlock(&alog->readq); + if(waserror()){ + qunlock(&alog->readq); + nexterror(); + } + + for(;;){ + lock(alog); + if(alog->len){ + if(n > alog->len) + n = alog->len; + d = 0; + rptr = alog->rptr; + alog->rptr += n; + if(alog->rptr >= alog->end){ + d = alog->rptr - alog->end; + alog->rptr = alog->buf + d; + } + alog->len -= n; + unlock(alog); + + i = n; + p = a; + if(d){ + memmove(p, rptr, d); + i -= d; + p += d; + rptr = alog->buf; + } + memmove(p, rptr, i); + break; + } + else + unlock(alog); + + sleep(&alog->readr, logready, alog); + } + + qunlock(&alog->readq); + poperror(); + + return n; +} + +char* +logctl(Log *alog, int argc, char *argv[], Logflag *flags) +{ + int i, n, set; + Logflag *fp; + + if(argc < 2) + return Ebadlogctl; + + if(strcmp("set", argv[0]) == 0) + set = 1; + else if(strcmp("clear", argv[0]) == 0) + set = 0; + else + return Ebadlogctl; + + for(i = 1; i < argc; i++){ + for(fp = flags; fp->name; fp++) + if(strcmp(fp->name, argv[i]) == 0) + break; + if(fp->name == nil) + continue; + if(set) + alog->logmask |= fp->mask; + else + alog->logmask &= ~fp->mask; + } + + return nil; +} + +void +log(Log *alog, int mask, char *fmt, ...) +{ + char buf[128], *t, *fp; + int i, n; + va_list arg; + + if(!(alog->logmask & mask)) + return; + + va_start(arg, fmt); + n = doprint(buf, buf+sizeof(buf), fmt, arg) - buf; + va_end(arg); + + if(alog->opens == 0) + return; + + lock(alog); + i = alog->len + n - Nlog; + if(i > 0){ + alog->len -= i; + alog->rptr += i; + if(alog->rptr >= alog->end) + alog->rptr = alog->buf + (alog->rptr - alog->end); + } + t = alog->rptr + alog->len; + fp = buf; + alog->len += n; + while(n-- > 0){ + if(t >= alog->end) + t = alog->buf + (t - alog->end); + *t++ = *fp++; + } + unlock(alog); + + wakeup(&alog->readr); +} diff --git a/port/netif.c b/port/netif.c index a9425e8d9f078c3920a1e5179f5aecfc9a0baff6..14bbb76cbfe02dab5c8c76540e5a5b742ee080f6 100644 --- a/port/netif.c +++ b/port/netif.c @@ -59,17 +59,26 @@ netifgen(Chan *c, void *vp, int, int i, Dir *dp) /* second level contains clone plus all the conversations */ t = NETTYPE(c->qid.path); if(t == N2ndqid || t == Ncloneqid){ - if(i == 0){ + switch(i) { + case 0: q.path = Ncloneqid; devdir(c, q, "clone", 0, eve, 0666, dp); - }else if(i <= nif->nfile){ - if(nif->f[i-1] == 0) + break; + case 1: + q.path = Naddrqid; + devdir(c, q, "addr", 0, eve, 0666, dp); + break; + default: + i -= 2; + if(i >= nif->nfile) + return -1; + if(nif->f[i] == 0) return 0; - q.path = CHDIR|NETQID(i-1, N3rdqid); - sprint(buf, "%d", i-1); + q.path = CHDIR|NETQID(i, N3rdqid); + sprint(buf, "%d", i); devdir(c, q, buf, 0, eve, 0555, dp); - }else - return -1; + break; + } return 1; } @@ -192,6 +201,14 @@ netifread(Netif *nif, Chan *c, void *a, long n, ulong offset) n = readstr(offset, a, n, p); free(p); return n; + case Naddrqid: + p = malloc(READSTR); + j = 0; + for(i = 0; i < nif->alen; i++) + j += snprint(p+j, READSTR-j, "%2.2ux", nif->addr[i]); + n = readstr(offset, a, n, p); + free(p); + return n; case Ntypeqid: f = nif->f[NETID(c->qid.path)]; return readnum(offset, a, n, f->type, NUMSIZE); diff --git a/port/netif.h b/port/netif.h index 567d49be594f8b700b0ba8cb17b63358970104b7..b7e21733d848dc2b30fa671390e1a66e611c8fef 100644 --- a/port/netif.h +++ b/port/netif.h @@ -9,6 +9,7 @@ enum Nmhash= 31, Ncloneqid= 1, + Naddrqid, N2ndqid, N3rdqid, Ndataqid, diff --git a/port/parse.c b/port/parse.c new file mode 100644 index 0000000000000000000000000000000000000000..b35c1151f547e1617e8a41e25c6041c11d9eddba --- /dev/null +++ b/port/parse.c @@ -0,0 +1,44 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +int +parsefields(char *lp, char **fields, int n, char *sep) +{ + int i; + + for(i=0; lp && *lp && i sizeof(cb->buf)-1) + n = sizeof(cb->buf)-1; + memmove(cb->buf, p, n); + if(n > 0 && cb->buf[n-1] == '\n') + n--; + cb->buf[n] = '\0'; + cb->nf = parsefields(cb->buf, cb->f, nelem(cb->f), " "); + return cb; +} + diff --git a/port/portdat.h b/port/portdat.h index 55736d7cad04aec4119add597ad8cd3de0a11d2f..2509e712e4d086a6e0f4f61c35fb81cc7ff5816e 100644 --- a/port/portdat.h +++ b/port/portdat.h @@ -1,6 +1,7 @@ typedef struct Alarms Alarms; typedef struct Block Block; typedef struct Chan Chan; +typedef struct Cmdbuf Cmdbuf; typedef struct Crypt Crypt; typedef struct Dev Dev; typedef struct Dirtab Dirtab; @@ -9,6 +10,8 @@ typedef struct Evalue Evalue; typedef struct Fgrp Fgrp; typedef struct Image Image; typedef struct List List; +typedef struct Log Log; +typedef struct Logflag Logflag; typedef struct Mntcache Mntcache; typedef struct Mount Mount; typedef struct Mntrpc Mntrpc; @@ -39,6 +42,7 @@ typedef struct Target Target; typedef struct Waitq Waitq; typedef int Devgen(Chan*, Dirtab*, int, int, Dir*); + #include #include @@ -716,6 +720,35 @@ enum LRESPROF = 3, }; +/* + * action log + */ +struct Log { + Lock; + int opens; + char* buf; + char *end; + char *rptr; + int len; + + int logmask; /* mask of things to debug */ + + QLock readq; + Rendez readr; +}; + +struct Logflag { + char* name; + int mask; +}; + +struct Cmdbuf +{ + char buf[64]; + char *f[16]; + int nf; +}; + extern int nsyscall; #pragma varargck argpos print 1 diff --git a/port/portfns.h b/port/portfns.h index 1fb78a2f5382e605eb01bd7a6277055c9359ba19..fcf0791ca6677c338a4c2be0d34a50ff49e4720c 100644 --- a/port/portfns.h +++ b/port/portfns.h @@ -142,6 +142,11 @@ void ksetenv(char*, char*); long latin1(uchar*, int); void lock(Lock*); void lockinit(void); +void logopen(Log*); +void logclose(Log*); +char* logctl(Log*, int, char**, Logflag*); +long logread(Log*, void*, ulong, long); +void log(Log*, int, char*, ...); Page* lookpage(Image*, ulong); void machinit(void); void* mallocz(ulong, int); @@ -179,6 +184,7 @@ void pagersummary(void); void panic(char*, ...); int parseether(uchar*, char*); int parsefields(char*, char**, int, char*); +Cmdbuf* parsecmd(char *a, int n); void pexit(char*, int); void pgrpcpy(Pgrp*, Pgrp*); void pgrpnote(ulong, char*, long, int);