/* * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ /* * IPv4 Ethernet bridge */#include "u.h"#include "../port/lib.h"#include "mem.h"#include "dat.h"#include "fns.h"#include "../ip/ip.h"#include "../port/netif.h"#include "../port/error.h" typedef struct Bridge Bridge;typedef struct Port Port;typedef struct Centry Centry;typedef struct Iphdr Iphdr;typedef struct Tcphdr Tcphdr; 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= 128, // 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 TcpMssMax = 1300, // max desirable Tcp MSS value TunnelMtu = 1400,}; static Dirtab bridgedirtab[]={ "ctl", {Qbctl}, 0, 0666, "stats", {Qstats}, 0, 0444, "cache", {Qcache}, 0, 0444, "log", {Qlog}, 0, 0666,}; static Dirtab portdirtab[]={ "ctl", {Qpctl}, 0, 0666, "local", {Qlocal}, 0, 0444, "status", {Qstatus}, 0, 0444,}; enum { Logcache= (1<<0), Logmcast= (1<<1),}; // types of interfacesenum{ Tether, Ttun,}; static Logflag logflags[] ={ { "cache", Logcache, }, { "multicast", Logmcast, }, { nil, 0, },}; static Dirtab *dirtab[MaxQ]; #define TYPE(x) (((ulong)(x).path) & 0xff)#define PORT(x) ((((ulong)(x).path) >> 8)&(Maxport-1))#define QID(x, y) (((x)<<8) | (y)) struct Centry{ uchar d[Eaddrlen]; int port; long expire; // entry expires this many seconds after bootime long src; long dst;}; struct Bridge{ QLock; int nport; Port *port[Maxport]; Centry cache[CacheSize]; ulong hit; ulong miss; ulong copy; long delay0; // constant microsecond delay per packet long delayn; // microsecond delay per byte int tcpmss; // modify tcpmss value Log;}; struct Port{ int id; Bridge *bridge; int ref; int closed; Chan *data[2]; // channel to data Proc *readp; // read proc // the following uniquely identifies the port int type; char name[KNAMELEN]; // 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 outfrag; // fragmented the packet int nentry; // number of cache entries for this port}; enum { IP_TCPPROTO = 6, EOLOPT = 0, NOOPOPT = 1, MSSOPT = 2, MSS_LENGTH = 4, /* Mean segment size */ SYN = 0x02, /* Pkt. is synchronise */ IPHDR = 20, /* sizeof(Iphdr) */}; struct Iphdr{ uchar vihl; /* Version and header length */ uchar tos; /* Type of service */ uchar length[2]; /* packet length */ uchar id[2]; /* ip->identification */ uchar frag[2]; /* Fragment information */ uchar ttl; /* Time to live */ uchar proto; /* Protocol */ uchar cksum[2]; /* Header checksum */ uchar src[4]; /* IP source */ uchar dst[4]; /* IP destination */}; struct Tcphdr{ uchar sport[2]; uchar dport[2]; uchar seq[4]; uchar ack[4]; uchar flag[2]; uchar win[2]; uchar cksum[2]; uchar urg[2];}; static Bridge bridgetab[Maxbridge]; static int m2p[] = { [OREAD] 4, [OWRITE] 2, [ORDWR] 6}; static int bridgegen(Chan *c, char*, 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);static void etherwrite(Port *port, Block *bp); static voidbridgeinit(void){ int i; Dirtab *dt; // setup dirtab with non directory entries for(i=0; i<nelem(bridgedirtab); i++) { dt = bridgedirtab + i; dirtab[TYPE(dt->qid)] = dt; } for(i=0; i<nelem(portdirtab); i++) { dt = portdirtab + i; dirtab[TYPE(dt->qid)] = 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); mkqid(&c->qid, QID(0, Qtopdir), 0, QTDIR); c->dev = dev; return c;} static Walkqid*bridgewalk(Chan *c, Chan *nc, char **name, int nname){ return devwalk(c, nc, name, nname, (Dirtab*)0, 0, bridgegen);} static intbridgestat(Chan* c, uchar* db, int n){ return devstat(c, db, n, (Dirtab *)0, 0L, 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 Qlog: logopen(b); break; case Qcache: c->aux = cachedump(b); break; } c->mode = openmode(omode); c->flag |= COPEN; c->offset = 0; return c;} static voidbridgeclose(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 longbridgeread(Chan *c, void *a, long n, vlong off){ char buf[256]; Bridge *b = bridgetab + c->dev; Port *port; int i, ingood, outgood; 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 %s: ", port->name); break; case Ttun: i += snprint(buf+i, sizeof(buf)-i, "tunnel %s: ", port->name); break; } ingood = port->in - port->inmulti - port->inunknown; outgood = port->out - port->outmulti - port->outunknown; i += snprint(buf+i, sizeof(buf)-i, "in=%d(%d:%d:%d) out=%d(%d:%d:%d:%d)\n", port->in, ingood, port->inmulti, port->inunknown, port->out, outgood, port->outmulti, port->outunknown, port->outfrag); USED(i); } n = readstr(off, a, n, buf); qunlock(b); return n; case Qbctl: snprint(buf, sizeof(buf), "%s tcpmss\ndelay %ld %ld\n", b->tcpmss ? "set" : "clear", b->delay0, b->delayn); n = readstr(off, a, n, buf); return n; case Qcache: n = readstr(off, a, n, c->aux); return n; case Qstats: snprint(buf, sizeof(buf), "hit=%uld miss=%uld copy=%uld\n", b->hit, b->miss, b->copy); n = readstr(off, a, n, buf); return n; }} static voidbridgeoption(Bridge *b, char *option, int value){ if(strcmp(option, "tcpmss") == 0) b->tcpmss = value; else error("unknown bridge option");} static longbridgewrite(Chan *c, void *a, long n, vlong off){ Bridge *b = bridgetab + c->dev; Cmdbuf *cb; char *arg0, *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 if(strcmp(arg0, "set") == 0) { if(cb->nf != 2) error("usage: set option"); bridgeoption(b, cb->f[1], 1); } else if(strcmp(arg0, "clear") == 0) { if(cb->nf != 2) error("usage: clear option"); bridgeoption(b, cb->f[1], 0); } else if(strcmp(arg0, "delay") == 0) { if(cb->nf != 3) error("usage: delay delay0 delayn"); b->delay0 = strtol(cb->f[1], nil, 10); b->delayn = strtol(cb->f[2], nil, 10); } 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 intbridgegen(Chan *c, char *, Dirtab*, int, int s, Dir *dp){ Bridge *b = bridgetab + c->dev; int type = TYPE(c->qid); Dirtab *dt; Qid qid; if(s == DEVDOTDOT){ switch(TYPE(c->qid)){ case Qtopdir: case Qbridgedir: snprint(up->genbuf, sizeof(up->genbuf), "#B%ld", c->dev); mkqid(&qid, Qtopdir, 0, QTDIR); devdir(c, qid, up->genbuf, 0, eve, 0555, dp); break; case Qportdir: snprint(up->genbuf, sizeof(up->genbuf), "bridge%ld", c->dev); mkqid(&qid, Qbridgedir, 0, QTDIR); devdir(c, qid, up->genbuf, 0, eve, 0555, dp); break; default: panic("bridgewalk %llux", c->qid.path); } return 1; } switch(type) { default: /* non-directory entries end up here */ if(c->qid.type & QTDIR) panic("bridgegen: unexpected directory"); if(s != 0) return -1; dt = dirtab[TYPE(c->qid)]; if(dt == nil) panic("bridgegen: unknown type: %lud", TYPE(c->qid)); devdir(c, c->qid, dt->name, dt->length, eve, dt->perm, dp); return 1; case Qtopdir: if(s != 0) return -1; snprint(up->genbuf, sizeof(up->genbuf), "bridge%ld", c->dev); mkqid(&qid, QID(0, Qbridgedir), 0, QTDIR); devdir(c, qid, up->genbuf, 0, eve, 0555, dp); return 1; case Qbridgedir: if(s<nelem(bridgedirtab)) { dt = bridgedirtab+s; devdir(c, dt->qid, dt->name, dt->length, eve, dt->perm, dp); return 1; } s -= nelem(bridgedirtab); if(s >= b->nport) return -1; mkqid(&qid, QID(s, Qportdir), 0, QTDIR); snprint(up->genbuf, sizeof(up->genbuf), "%d", s); devdir(c, qid, up->genbuf, 0, eve, 0555, dp); return 1; case Qportdir: if(s>=nelem(portdirtab)) return -1; dt = portdirtab+s; mkqid(&qid, QID(PORT(c->qid),TYPE(dt->qid)), 0, QTFILE); devdir(c, qid, dt->name, dt->length, eve, dt->perm, dp); return 1; }} // parse mac address; also in netif.cstatic intparseaddr(uchar *to, char *from, int alen){ char nip[4]; char *p; int i; Lines 1–500 of 1206. Load more
/* * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ /* * IPv4 Ethernet bridge */ #include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "../ip/ip.h" #include "../port/netif.h" #include "../port/error.h" typedef struct Bridge Bridge; typedef struct Port Port; typedef struct Centry Centry; typedef struct Iphdr Iphdr; typedef struct Tcphdr Tcphdr; 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= 128, // 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 TcpMssMax = 1300, // max desirable Tcp MSS value TunnelMtu = 1400, }; static Dirtab bridgedirtab[]={ "ctl", {Qbctl}, 0, 0666, "stats", {Qstats}, 0, 0444, "cache", {Qcache}, 0, 0444, "log", {Qlog}, 0, 0666, }; static 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, Ttun, }; static Logflag logflags[] = { { "cache", Logcache, }, { "multicast", Logmcast, }, { nil, 0, }, }; static Dirtab *dirtab[MaxQ]; #define TYPE(x) (((ulong)(x).path) & 0xff) #define PORT(x) ((((ulong)(x).path) >> 8)&(Maxport-1)) #define QID(x, y) (((x)<<8) | (y)) struct Centry { uchar d[Eaddrlen]; int port; long expire; // entry expires this many seconds after bootime long src; long dst; }; struct Bridge { QLock; int nport; Port *port[Maxport]; Centry cache[CacheSize]; ulong hit; ulong miss; ulong copy; long delay0; // constant microsecond delay per packet long delayn; // microsecond delay per byte int tcpmss; // modify tcpmss value Log; }; struct Port { int id; Bridge *bridge; int ref; int closed; Chan *data[2]; // channel to data Proc *readp; // read proc // the following uniquely identifies the port int type; char name[KNAMELEN]; // 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 outfrag; // fragmented the packet int nentry; // number of cache entries for this port }; enum { IP_TCPPROTO = 6, EOLOPT = 0, NOOPOPT = 1, MSSOPT = 2, MSS_LENGTH = 4, /* Mean segment size */ SYN = 0x02, /* Pkt. is synchronise */ IPHDR = 20, /* sizeof(Iphdr) */ }; struct Iphdr { uchar vihl; /* Version and header length */ uchar tos; /* Type of service */ uchar length[2]; /* packet length */ uchar id[2]; /* ip->identification */ uchar frag[2]; /* Fragment information */ uchar ttl; /* Time to live */ uchar proto; /* Protocol */ uchar cksum[2]; /* Header checksum */ uchar src[4]; /* IP source */ uchar dst[4]; /* IP destination */ }; struct Tcphdr { uchar sport[2]; uchar dport[2]; uchar seq[4]; uchar ack[4]; uchar flag[2]; uchar win[2]; uchar cksum[2]; uchar urg[2]; }; static Bridge bridgetab[Maxbridge]; static int m2p[] = { [OREAD] 4, [OWRITE] 2, [ORDWR] 6 }; static int bridgegen(Chan *c, char*, 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); static void etherwrite(Port *port, Block *bp); static void bridgeinit(void) { int i; Dirtab *dt; // setup dirtab with non directory entries for(i=0; i<nelem(bridgedirtab); i++) { dt = bridgedirtab + i; dirtab[TYPE(dt->qid)] = dt; } for(i=0; i<nelem(portdirtab); i++) { dt = portdirtab + i; dirtab[TYPE(dt->qid)] = 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); mkqid(&c->qid, QID(0, Qtopdir), 0, QTDIR); c->dev = dev; return c; } static Walkqid* bridgewalk(Chan *c, Chan *nc, char **name, int nname) { return devwalk(c, nc, name, nname, (Dirtab*)0, 0, bridgegen); } static int bridgestat(Chan* c, uchar* db, int n) { return devstat(c, db, n, (Dirtab *)0, 0L, 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 Qlog: logopen(b); break; case Qcache: 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, ingood, outgood; 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 %s: ", port->name); break; case Ttun: i += snprint(buf+i, sizeof(buf)-i, "tunnel %s: ", port->name); break; } ingood = port->in - port->inmulti - port->inunknown; outgood = port->out - port->outmulti - port->outunknown; i += snprint(buf+i, sizeof(buf)-i, "in=%d(%d:%d:%d) out=%d(%d:%d:%d:%d)\n", port->in, ingood, port->inmulti, port->inunknown, port->out, outgood, port->outmulti, port->outunknown, port->outfrag); USED(i); } n = readstr(off, a, n, buf); qunlock(b); return n; case Qbctl: snprint(buf, sizeof(buf), "%s tcpmss\ndelay %ld %ld\n", b->tcpmss ? "set" : "clear", b->delay0, b->delayn); n = readstr(off, a, n, buf); return n; case Qcache: n = readstr(off, a, n, c->aux); return n; case Qstats: snprint(buf, sizeof(buf), "hit=%uld miss=%uld copy=%uld\n", b->hit, b->miss, b->copy); n = readstr(off, a, n, buf); return n; } } static void bridgeoption(Bridge *b, char *option, int value) { if(strcmp(option, "tcpmss") == 0) b->tcpmss = value; else error("unknown bridge option"); } static long bridgewrite(Chan *c, void *a, long n, vlong off) { Bridge *b = bridgetab + c->dev; Cmdbuf *cb; char *arg0, *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 if(strcmp(arg0, "set") == 0) { if(cb->nf != 2) error("usage: set option"); bridgeoption(b, cb->f[1], 1); } else if(strcmp(arg0, "clear") == 0) { if(cb->nf != 2) error("usage: clear option"); bridgeoption(b, cb->f[1], 0); } else if(strcmp(arg0, "delay") == 0) { if(cb->nf != 3) error("usage: delay delay0 delayn"); b->delay0 = strtol(cb->f[1], nil, 10); b->delayn = strtol(cb->f[2], nil, 10); } 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, char *, Dirtab*, int, int s, Dir *dp) { Bridge *b = bridgetab + c->dev; int type = TYPE(c->qid); Dirtab *dt; Qid qid; if(s == DEVDOTDOT){ switch(TYPE(c->qid)){ case Qtopdir: case Qbridgedir: snprint(up->genbuf, sizeof(up->genbuf), "#B%ld", c->dev); mkqid(&qid, Qtopdir, 0, QTDIR); devdir(c, qid, up->genbuf, 0, eve, 0555, dp); break; case Qportdir: snprint(up->genbuf, sizeof(up->genbuf), "bridge%ld", c->dev); mkqid(&qid, Qbridgedir, 0, QTDIR); devdir(c, qid, up->genbuf, 0, eve, 0555, dp); break; default: panic("bridgewalk %llux", c->qid.path); } return 1; } switch(type) { default: /* non-directory entries end up here */ if(c->qid.type & QTDIR) panic("bridgegen: unexpected directory"); if(s != 0) return -1; dt = dirtab[TYPE(c->qid)]; if(dt == nil) panic("bridgegen: unknown type: %lud", TYPE(c->qid)); devdir(c, c->qid, dt->name, dt->length, eve, dt->perm, dp); return 1; case Qtopdir: if(s != 0) return -1; snprint(up->genbuf, sizeof(up->genbuf), "bridge%ld", c->dev); mkqid(&qid, QID(0, Qbridgedir), 0, QTDIR); devdir(c, qid, up->genbuf, 0, eve, 0555, dp); return 1; case Qbridgedir: if(s<nelem(bridgedirtab)) { dt = bridgedirtab+s; devdir(c, dt->qid, dt->name, dt->length, eve, dt->perm, dp); return 1; } s -= nelem(bridgedirtab); if(s >= b->nport) return -1; mkqid(&qid, QID(s, Qportdir), 0, QTDIR); snprint(up->genbuf, sizeof(up->genbuf), "%d", s); devdir(c, qid, up->genbuf, 0, eve, 0555, dp); return 1; case Qportdir: if(s>=nelem(portdirtab)) return -1; dt = portdirtab+s; mkqid(&qid, QID(PORT(c->qid),TYPE(dt->qid)), 0, QTFILE); devdir(c, qid, dt->name, dt->length, eve, dt->perm, dp); return 1; } } // parse mac address; also in netif.c static int parseaddr(uchar *to, char *from, int alen) { char nip[4]; char *p; int i;