📄 devbridge.c
字号:
#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;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= 64, // 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) ((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]; 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 int mcast; // send multi cast packets Proc *readp; // read proc // the following uniquely identifies the port int type; char name[NAMELEN]; // 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_VER = 0x40, /* Using IP version 4 */ IP_HLEN = 0x05, /* Header length in characters */ IP_DF = 0x4000, /* Don't fragment */ IP_MF = 0x2000, /* More fragments */ IP_MAX = (32*1024), /* Maximum Internet packet size */ 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, 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);extern ulong parseip(uchar*, char*);extern ushort ipcsum(uchar *addr);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); c->qid = (Qid){QID(0, Qtopdir)|CHDIR, 0}; c->dev = dev; return c;}static intbridgewalk(Chan *c, char *name){ return devwalk(c, name, 0, 0, bridgegen);}static voidbridgestat(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 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; 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 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, Dirtab*, int, int s, Dir *dp){ Bridge *b = bridgetab + c->dev; int type = TYPE(c->qid); char buf[32]; Dirtab *dt; Qid qid; if(s == DEVDOTDOT){ switch(TYPE(c->qid)){ case Qtopdir: case Qbridgedir: snprint(buf, sizeof(buf), "#B%ld", c->dev); devdir(c, (Qid){CHDIR|Qtopdir, 0}, buf, 0, eve, 0555, dp); break; case Qportdir: sprint(buf, "bridge%ld", c->dev); devdir(c, (Qid){CHDIR|Qbridgedir, 0}, buf, 0, eve, 0555, dp); break; default: panic("bridgewalk %lux", c->qid.path); } return 1; } 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(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; 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.cstatic intparseaddr(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 lockedstatic voidportbind(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 name ownhash dev [dev2]"; char name[NAMELEN]; ulong ownhash; memset(name, 0, NAMELEN); if(argc < 4) error(usage); if(strcmp(argv[0], "ether") == 0) { if(argc != 4) error(usage); type = Tether; strncpy(name, argv[1], NAMELEN); name[NAMELEN-1] = 0;// parseaddr(addr, argv[1], Eaddrlen); } else if(strcmp(argv[0], "tunnel") == 0) { if(argc != 5) error(usage); type = Ttun; strncpy(name, argv[1], NAMELEN); name[NAMELEN-1] = 0;// parseip(addr, argv[1]); dev2 = argv[4]; } else error(usage); ownhash = atoi(argv[2]); dev = argv[3]; for(i=0; i<b->nport; i++) { port = b->port[i]; if(port != nil) if(port->type == type) if(memcmp(port->name, name, NAMELEN) == 0) error("port in use"); } for(i=0; i<Maxport; i++) if(b->port[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->name, name, NAMELEN); 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); snprint(buf, sizeof(buf), "bridge"); 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -