1ac020a8fSDavid du Colombier /*
2ac020a8fSDavid du Colombier * IPv4 Ethernet bridge
3ac020a8fSDavid du Colombier */
47dd7cddfSDavid du Colombier #include "u.h"
57dd7cddfSDavid du Colombier #include "../port/lib.h"
67dd7cddfSDavid du Colombier #include "mem.h"
77dd7cddfSDavid du Colombier #include "dat.h"
87dd7cddfSDavid du Colombier #include "fns.h"
9ac020a8fSDavid du Colombier #include "../ip/ip.h"
107dd7cddfSDavid du Colombier #include "../port/netif.h"
117dd7cddfSDavid du Colombier #include "../port/error.h"
127dd7cddfSDavid du Colombier
137dd7cddfSDavid du Colombier typedef struct Bridge Bridge;
147dd7cddfSDavid du Colombier typedef struct Port Port;
157dd7cddfSDavid du Colombier typedef struct Centry Centry;
167dd7cddfSDavid du Colombier typedef struct Iphdr Iphdr;
177dd7cddfSDavid du Colombier typedef struct Tcphdr Tcphdr;
187dd7cddfSDavid du Colombier
197dd7cddfSDavid du Colombier enum
207dd7cddfSDavid du Colombier {
217dd7cddfSDavid du Colombier Qtopdir= 1, /* top level directory */
227dd7cddfSDavid du Colombier
237dd7cddfSDavid du Colombier Qbridgedir, /* bridge* directory */
247dd7cddfSDavid du Colombier Qbctl,
257dd7cddfSDavid du Colombier Qstats,
267dd7cddfSDavid du Colombier Qcache,
277dd7cddfSDavid du Colombier Qlog,
287dd7cddfSDavid du Colombier
297dd7cddfSDavid du Colombier Qportdir, /* directory for a protocol */
307dd7cddfSDavid du Colombier Qpctl,
317dd7cddfSDavid du Colombier Qlocal,
327dd7cddfSDavid du Colombier Qstatus,
337dd7cddfSDavid du Colombier
347dd7cddfSDavid du Colombier MaxQ,
357dd7cddfSDavid du Colombier
367dd7cddfSDavid du Colombier Maxbridge= 4,
379a747e4fSDavid du Colombier Maxport= 128, // power of 2
387dd7cddfSDavid du Colombier CacheHash= 257, // prime
397dd7cddfSDavid du Colombier CacheLook= 5, // how many cache entries to examine
407dd7cddfSDavid du Colombier CacheSize= (CacheHash+CacheLook-1),
417dd7cddfSDavid du Colombier CacheTimeout= 5*60, // timeout for cache entry in seconds
427dd7cddfSDavid du Colombier
437dd7cddfSDavid du Colombier TcpMssMax = 1300, // max desirable Tcp MSS value
447dd7cddfSDavid du Colombier TunnelMtu = 1400,
457dd7cddfSDavid du Colombier };
467dd7cddfSDavid du Colombier
477dd7cddfSDavid du Colombier static Dirtab bridgedirtab[]={
487dd7cddfSDavid du Colombier "ctl", {Qbctl}, 0, 0666,
497dd7cddfSDavid du Colombier "stats", {Qstats}, 0, 0444,
507dd7cddfSDavid du Colombier "cache", {Qcache}, 0, 0444,
517dd7cddfSDavid du Colombier "log", {Qlog}, 0, 0666,
527dd7cddfSDavid du Colombier };
537dd7cddfSDavid du Colombier
547dd7cddfSDavid du Colombier static Dirtab portdirtab[]={
557dd7cddfSDavid du Colombier "ctl", {Qpctl}, 0, 0666,
567dd7cddfSDavid du Colombier "local", {Qlocal}, 0, 0444,
577dd7cddfSDavid du Colombier "status", {Qstatus}, 0, 0444,
587dd7cddfSDavid du Colombier };
597dd7cddfSDavid du Colombier
607dd7cddfSDavid du Colombier enum {
617dd7cddfSDavid du Colombier Logcache= (1<<0),
627dd7cddfSDavid du Colombier Logmcast= (1<<1),
637dd7cddfSDavid du Colombier };
647dd7cddfSDavid du Colombier
657dd7cddfSDavid du Colombier // types of interfaces
667dd7cddfSDavid du Colombier enum
677dd7cddfSDavid du Colombier {
687dd7cddfSDavid du Colombier Tether,
697dd7cddfSDavid du Colombier Ttun,
707dd7cddfSDavid du Colombier };
717dd7cddfSDavid du Colombier
727dd7cddfSDavid du Colombier static Logflag logflags[] =
737dd7cddfSDavid du Colombier {
747dd7cddfSDavid du Colombier { "cache", Logcache, },
757dd7cddfSDavid du Colombier { "multicast", Logmcast, },
767dd7cddfSDavid du Colombier { nil, 0, },
777dd7cddfSDavid du Colombier };
787dd7cddfSDavid du Colombier
797dd7cddfSDavid du Colombier static Dirtab *dirtab[MaxQ];
807dd7cddfSDavid du Colombier
819a747e4fSDavid du Colombier #define TYPE(x) (((ulong)(x).path) & 0xff)
829a747e4fSDavid du Colombier #define PORT(x) ((((ulong)(x).path) >> 8)&(Maxport-1))
837dd7cddfSDavid du Colombier #define QID(x, y) (((x)<<8) | (y))
847dd7cddfSDavid du Colombier
857dd7cddfSDavid du Colombier struct Centry
867dd7cddfSDavid du Colombier {
877dd7cddfSDavid du Colombier uchar d[Eaddrlen];
887dd7cddfSDavid du Colombier int port;
89ac020a8fSDavid du Colombier long expire; // entry expires this many seconds after bootime
907dd7cddfSDavid du Colombier long src;
917dd7cddfSDavid du Colombier long dst;
927dd7cddfSDavid du Colombier };
937dd7cddfSDavid du Colombier
947dd7cddfSDavid du Colombier struct Bridge
957dd7cddfSDavid du Colombier {
967dd7cddfSDavid du Colombier QLock;
977dd7cddfSDavid du Colombier int nport;
987dd7cddfSDavid du Colombier Port *port[Maxport];
997dd7cddfSDavid du Colombier Centry cache[CacheSize];
1007dd7cddfSDavid du Colombier ulong hit;
1017dd7cddfSDavid du Colombier ulong miss;
1027dd7cddfSDavid du Colombier ulong copy;
1037dd7cddfSDavid du Colombier long delay0; // constant microsecond delay per packet
1047dd7cddfSDavid du Colombier long delayn; // microsecond delay per byte
1057dd7cddfSDavid du Colombier int tcpmss; // modify tcpmss value
1067dd7cddfSDavid du Colombier
1077dd7cddfSDavid du Colombier Log;
1087dd7cddfSDavid du Colombier };
1097dd7cddfSDavid du Colombier
1107dd7cddfSDavid du Colombier struct Port
1117dd7cddfSDavid du Colombier {
1127dd7cddfSDavid du Colombier int id;
1137dd7cddfSDavid du Colombier Bridge *bridge;
1147dd7cddfSDavid du Colombier int ref;
1157dd7cddfSDavid du Colombier int closed;
1167dd7cddfSDavid du Colombier
1177dd7cddfSDavid du Colombier Chan *data[2]; // channel to data
1187dd7cddfSDavid du Colombier
1197dd7cddfSDavid du Colombier Proc *readp; // read proc
1207dd7cddfSDavid du Colombier
1217dd7cddfSDavid du Colombier // the following uniquely identifies the port
1227dd7cddfSDavid du Colombier int type;
1239a747e4fSDavid du Colombier char name[KNAMELEN];
1247dd7cddfSDavid du Colombier
1257dd7cddfSDavid du Colombier // owner hash - avoids bind/unbind races
1267dd7cddfSDavid du Colombier ulong ownhash;
1277dd7cddfSDavid du Colombier
1287dd7cddfSDavid du Colombier // various stats
1297dd7cddfSDavid du Colombier int in; // number of packets read
1307dd7cddfSDavid du Colombier int inmulti; // multicast or broadcast
1317dd7cddfSDavid du Colombier int inunknown; // unknown address
1327dd7cddfSDavid du Colombier int out; // number of packets read
1337dd7cddfSDavid du Colombier int outmulti; // multicast or broadcast
1347dd7cddfSDavid du Colombier int outunknown; // unknown address
1357dd7cddfSDavid du Colombier int outfrag; // fragmented the packet
1367dd7cddfSDavid du Colombier int nentry; // number of cache entries for this port
1377dd7cddfSDavid du Colombier };
1387dd7cddfSDavid du Colombier
1397dd7cddfSDavid du Colombier enum {
1407dd7cddfSDavid du Colombier IP_TCPPROTO = 6,
1417dd7cddfSDavid du Colombier EOLOPT = 0,
1427dd7cddfSDavid du Colombier NOOPOPT = 1,
1437dd7cddfSDavid du Colombier MSSOPT = 2,
1447dd7cddfSDavid du Colombier MSS_LENGTH = 4, /* Mean segment size */
1457dd7cddfSDavid du Colombier SYN = 0x02, /* Pkt. is synchronise */
1467dd7cddfSDavid du Colombier IPHDR = 20, /* sizeof(Iphdr) */
1477dd7cddfSDavid du Colombier };
1487dd7cddfSDavid du Colombier
1497dd7cddfSDavid du Colombier struct Iphdr
1507dd7cddfSDavid du Colombier {
1517dd7cddfSDavid du Colombier uchar vihl; /* Version and header length */
1527dd7cddfSDavid du Colombier uchar tos; /* Type of service */
1537dd7cddfSDavid du Colombier uchar length[2]; /* packet length */
1547dd7cddfSDavid du Colombier uchar id[2]; /* ip->identification */
1557dd7cddfSDavid du Colombier uchar frag[2]; /* Fragment information */
1567dd7cddfSDavid du Colombier uchar ttl; /* Time to live */
1577dd7cddfSDavid du Colombier uchar proto; /* Protocol */
1587dd7cddfSDavid du Colombier uchar cksum[2]; /* Header checksum */
1597dd7cddfSDavid du Colombier uchar src[4]; /* IP source */
1607dd7cddfSDavid du Colombier uchar dst[4]; /* IP destination */
1617dd7cddfSDavid du Colombier };
1627dd7cddfSDavid du Colombier
1637dd7cddfSDavid du Colombier struct Tcphdr
1647dd7cddfSDavid du Colombier {
1657dd7cddfSDavid du Colombier uchar sport[2];
1667dd7cddfSDavid du Colombier uchar dport[2];
1677dd7cddfSDavid du Colombier uchar seq[4];
1687dd7cddfSDavid du Colombier uchar ack[4];
1697dd7cddfSDavid du Colombier uchar flag[2];
1707dd7cddfSDavid du Colombier uchar win[2];
1717dd7cddfSDavid du Colombier uchar cksum[2];
1727dd7cddfSDavid du Colombier uchar urg[2];
1737dd7cddfSDavid du Colombier };
1747dd7cddfSDavid du Colombier
1757dd7cddfSDavid du Colombier static Bridge bridgetab[Maxbridge];
1767dd7cddfSDavid du Colombier
1777dd7cddfSDavid du Colombier static int m2p[] = {
1787dd7cddfSDavid du Colombier [OREAD] 4,
1797dd7cddfSDavid du Colombier [OWRITE] 2,
1807dd7cddfSDavid du Colombier [ORDWR] 6
1817dd7cddfSDavid du Colombier };
1827dd7cddfSDavid du Colombier
1839a747e4fSDavid du Colombier static int bridgegen(Chan *c, char*, Dirtab*, int, int s, Dir *dp);
1847dd7cddfSDavid du Colombier static void portbind(Bridge *b, int argc, char *argv[]);
1857dd7cddfSDavid du Colombier static void portunbind(Bridge *b, int argc, char *argv[]);
1867dd7cddfSDavid du Colombier static void etherread(void *a);
1877dd7cddfSDavid du Colombier static char *cachedump(Bridge *b);
1887dd7cddfSDavid du Colombier static void portfree(Port *port);
1897dd7cddfSDavid du Colombier static void cacheflushport(Bridge *b, int port);
1907dd7cddfSDavid du Colombier static void etherwrite(Port *port, Block *bp);
1917dd7cddfSDavid du Colombier
1927dd7cddfSDavid du Colombier static void
bridgeinit(void)1937dd7cddfSDavid du Colombier bridgeinit(void)
1947dd7cddfSDavid du Colombier {
1957dd7cddfSDavid du Colombier int i;
1967dd7cddfSDavid du Colombier Dirtab *dt;
197ac020a8fSDavid du Colombier
1987dd7cddfSDavid du Colombier // setup dirtab with non directory entries
1997dd7cddfSDavid du Colombier for(i=0; i<nelem(bridgedirtab); i++) {
2007dd7cddfSDavid du Colombier dt = bridgedirtab + i;
2017dd7cddfSDavid du Colombier dirtab[TYPE(dt->qid)] = dt;
2027dd7cddfSDavid du Colombier }
2037dd7cddfSDavid du Colombier for(i=0; i<nelem(portdirtab); i++) {
2047dd7cddfSDavid du Colombier dt = portdirtab + i;
2057dd7cddfSDavid du Colombier dirtab[TYPE(dt->qid)] = dt;
2067dd7cddfSDavid du Colombier }
2077dd7cddfSDavid du Colombier }
2087dd7cddfSDavid du Colombier
2097dd7cddfSDavid du Colombier static Chan*
bridgeattach(char * spec)2107dd7cddfSDavid du Colombier bridgeattach(char* spec)
2117dd7cddfSDavid du Colombier {
2127dd7cddfSDavid du Colombier Chan *c;
2137dd7cddfSDavid du Colombier int dev;
2147dd7cddfSDavid du Colombier
2157dd7cddfSDavid du Colombier dev = atoi(spec);
2167dd7cddfSDavid du Colombier if(dev<0 || dev >= Maxbridge)
2177dd7cddfSDavid du Colombier error("bad specification");
2187dd7cddfSDavid du Colombier
2197dd7cddfSDavid du Colombier c = devattach('B', spec);
2209a747e4fSDavid du Colombier mkqid(&c->qid, QID(0, Qtopdir), 0, QTDIR);
2217dd7cddfSDavid du Colombier c->dev = dev;
2227dd7cddfSDavid du Colombier return c;
2237dd7cddfSDavid du Colombier }
2247dd7cddfSDavid du Colombier
2259a747e4fSDavid du Colombier static Walkqid*
bridgewalk(Chan * c,Chan * nc,char ** name,int nname)2269a747e4fSDavid du Colombier bridgewalk(Chan *c, Chan *nc, char **name, int nname)
2277dd7cddfSDavid du Colombier {
2289a747e4fSDavid du Colombier return devwalk(c, nc, name, nname, (Dirtab*)0, 0, bridgegen);
2297dd7cddfSDavid du Colombier }
2307dd7cddfSDavid du Colombier
2319a747e4fSDavid du Colombier static int
bridgestat(Chan * c,uchar * db,int n)2329a747e4fSDavid du Colombier bridgestat(Chan* c, uchar* db, int n)
2337dd7cddfSDavid du Colombier {
2349a747e4fSDavid du Colombier return devstat(c, db, n, (Dirtab *)0, 0L, bridgegen);
2357dd7cddfSDavid du Colombier }
2367dd7cddfSDavid du Colombier
2377dd7cddfSDavid du Colombier static Chan*
bridgeopen(Chan * c,int omode)2387dd7cddfSDavid du Colombier bridgeopen(Chan* c, int omode)
2397dd7cddfSDavid du Colombier {
2407dd7cddfSDavid du Colombier int perm;
2417dd7cddfSDavid du Colombier Bridge *b;
2427dd7cddfSDavid du Colombier
2437dd7cddfSDavid du Colombier omode &= 3;
2447dd7cddfSDavid du Colombier perm = m2p[omode];
2457dd7cddfSDavid du Colombier USED(perm);
2467dd7cddfSDavid du Colombier
2477dd7cddfSDavid du Colombier b = bridgetab + c->dev;
2487dd7cddfSDavid du Colombier USED(b);
2497dd7cddfSDavid du Colombier
2507dd7cddfSDavid du Colombier switch(TYPE(c->qid)) {
2517dd7cddfSDavid du Colombier default:
2527dd7cddfSDavid du Colombier break;
2537dd7cddfSDavid du Colombier case Qlog:
2547dd7cddfSDavid du Colombier logopen(b);
2557dd7cddfSDavid du Colombier break;
2567dd7cddfSDavid du Colombier case Qcache:
2577dd7cddfSDavid du Colombier c->aux = cachedump(b);
2587dd7cddfSDavid du Colombier break;
2597dd7cddfSDavid du Colombier }
2607dd7cddfSDavid du Colombier c->mode = openmode(omode);
2617dd7cddfSDavid du Colombier c->flag |= COPEN;
2627dd7cddfSDavid du Colombier c->offset = 0;
2637dd7cddfSDavid du Colombier return c;
2647dd7cddfSDavid du Colombier }
2657dd7cddfSDavid du Colombier
2667dd7cddfSDavid du Colombier static void
bridgeclose(Chan * c)2677dd7cddfSDavid du Colombier bridgeclose(Chan* c)
2687dd7cddfSDavid du Colombier {
2697dd7cddfSDavid du Colombier Bridge *b = bridgetab + c->dev;
2707dd7cddfSDavid du Colombier
2717dd7cddfSDavid du Colombier switch(TYPE(c->qid)) {
2727dd7cddfSDavid du Colombier case Qcache:
2737dd7cddfSDavid du Colombier if(c->flag & COPEN)
2747dd7cddfSDavid du Colombier free(c->aux);
2757dd7cddfSDavid du Colombier break;
2767dd7cddfSDavid du Colombier case Qlog:
2777dd7cddfSDavid du Colombier if(c->flag & COPEN)
2787dd7cddfSDavid du Colombier logclose(b);
2797dd7cddfSDavid du Colombier break;
2807dd7cddfSDavid du Colombier }
2817dd7cddfSDavid du Colombier }
2827dd7cddfSDavid du Colombier
2837dd7cddfSDavid du Colombier static long
bridgeread(Chan * c,void * a,long n,vlong off)2847dd7cddfSDavid du Colombier bridgeread(Chan *c, void *a, long n, vlong off)
2857dd7cddfSDavid du Colombier {
2867dd7cddfSDavid du Colombier char buf[256];
2877dd7cddfSDavid du Colombier Bridge *b = bridgetab + c->dev;
2887dd7cddfSDavid du Colombier Port *port;
2897dd7cddfSDavid du Colombier int i, ingood, outgood;
2907dd7cddfSDavid du Colombier
2917dd7cddfSDavid du Colombier USED(off);
2927dd7cddfSDavid du Colombier switch(TYPE(c->qid)) {
2937dd7cddfSDavid du Colombier default:
2947dd7cddfSDavid du Colombier error(Eperm);
2957dd7cddfSDavid du Colombier case Qtopdir:
2967dd7cddfSDavid du Colombier case Qbridgedir:
2977dd7cddfSDavid du Colombier case Qportdir:
2987dd7cddfSDavid du Colombier return devdirread(c, a, n, 0, 0, bridgegen);
2997dd7cddfSDavid du Colombier case Qlog:
3007dd7cddfSDavid du Colombier return logread(b, a, off, n);
3017dd7cddfSDavid du Colombier case Qstatus:
3027dd7cddfSDavid du Colombier qlock(b);
3037dd7cddfSDavid du Colombier port = b->port[PORT(c->qid)];
3047dd7cddfSDavid du Colombier if(port == 0)
3057dd7cddfSDavid du Colombier strcpy(buf, "unbound\n");
3067dd7cddfSDavid du Colombier else {
3077dd7cddfSDavid du Colombier i = 0;
3087dd7cddfSDavid du Colombier switch(port->type) {
309ac020a8fSDavid du Colombier default:
310ac020a8fSDavid du Colombier panic("bridgeread: unknown port type: %d",
311ac020a8fSDavid du Colombier port->type);
3127dd7cddfSDavid du Colombier case Tether:
3137dd7cddfSDavid du Colombier i += snprint(buf+i, sizeof(buf)-i, "ether %s: ", port->name);
3147dd7cddfSDavid du Colombier break;
3157dd7cddfSDavid du Colombier case Ttun:
3167dd7cddfSDavid du Colombier i += snprint(buf+i, sizeof(buf)-i, "tunnel %s: ", port->name);
3177dd7cddfSDavid du Colombier break;
3187dd7cddfSDavid du Colombier }
3197dd7cddfSDavid du Colombier ingood = port->in - port->inmulti - port->inunknown;
3207dd7cddfSDavid du Colombier outgood = port->out - port->outmulti - port->outunknown;
321ac020a8fSDavid du Colombier i += snprint(buf+i, sizeof(buf)-i,
322ac020a8fSDavid du Colombier "in=%d(%d:%d:%d) out=%d(%d:%d:%d:%d)\n",
3237dd7cddfSDavid du Colombier port->in, ingood, port->inmulti, port->inunknown,
324ac020a8fSDavid du Colombier port->out, outgood, port->outmulti,
325ac020a8fSDavid du Colombier port->outunknown, port->outfrag);
3267dd7cddfSDavid du Colombier USED(i);
3277dd7cddfSDavid du Colombier }
3287dd7cddfSDavid du Colombier n = readstr(off, a, n, buf);
3297dd7cddfSDavid du Colombier qunlock(b);
3307dd7cddfSDavid du Colombier return n;
3317dd7cddfSDavid du Colombier case Qbctl:
3326c0968e3SDavid du Colombier snprint(buf, sizeof(buf), "%s tcpmss\ndelay %ld %ld\n",
3336c0968e3SDavid du Colombier b->tcpmss ? "set" : "clear", b->delay0, b->delayn);
3347dd7cddfSDavid du Colombier n = readstr(off, a, n, buf);
3357dd7cddfSDavid du Colombier return n;
3367dd7cddfSDavid du Colombier case Qcache:
3377dd7cddfSDavid du Colombier n = readstr(off, a, n, c->aux);
3387dd7cddfSDavid du Colombier return n;
3397dd7cddfSDavid du Colombier case Qstats:
3407dd7cddfSDavid du Colombier snprint(buf, sizeof(buf), "hit=%uld miss=%uld copy=%uld\n",
3417dd7cddfSDavid du Colombier b->hit, b->miss, b->copy);
3427dd7cddfSDavid du Colombier n = readstr(off, a, n, buf);
3437dd7cddfSDavid du Colombier return n;
3447dd7cddfSDavid du Colombier }
3457dd7cddfSDavid du Colombier }
3467dd7cddfSDavid du Colombier
3477dd7cddfSDavid du Colombier static void
bridgeoption(Bridge * b,char * option,int value)3487dd7cddfSDavid du Colombier bridgeoption(Bridge *b, char *option, int value)
3497dd7cddfSDavid du Colombier {
3507dd7cddfSDavid du Colombier if(strcmp(option, "tcpmss") == 0)
3517dd7cddfSDavid du Colombier b->tcpmss = value;
3527dd7cddfSDavid du Colombier else
3537dd7cddfSDavid du Colombier error("unknown bridge option");
3547dd7cddfSDavid du Colombier }
3557dd7cddfSDavid du Colombier
3567dd7cddfSDavid du Colombier
3577dd7cddfSDavid du Colombier static long
bridgewrite(Chan * c,void * a,long n,vlong off)3587dd7cddfSDavid du Colombier bridgewrite(Chan *c, void *a, long n, vlong off)
3597dd7cddfSDavid du Colombier {
3607dd7cddfSDavid du Colombier Bridge *b = bridgetab + c->dev;
3617dd7cddfSDavid du Colombier Cmdbuf *cb;
362ac020a8fSDavid du Colombier char *arg0, *p;
3637dd7cddfSDavid du Colombier
3647dd7cddfSDavid du Colombier USED(off);
3657dd7cddfSDavid du Colombier switch(TYPE(c->qid)) {
3667dd7cddfSDavid du Colombier default:
3677dd7cddfSDavid du Colombier error(Eperm);
3687dd7cddfSDavid du Colombier case Qbctl:
3697dd7cddfSDavid du Colombier cb = parsecmd(a, n);
3707dd7cddfSDavid du Colombier qlock(b);
3717dd7cddfSDavid du Colombier if(waserror()) {
3727dd7cddfSDavid du Colombier qunlock(b);
3737dd7cddfSDavid du Colombier free(cb);
3747dd7cddfSDavid du Colombier nexterror();
3757dd7cddfSDavid du Colombier }
3767dd7cddfSDavid du Colombier if(cb->nf == 0)
3777dd7cddfSDavid du Colombier error("short write");
3787dd7cddfSDavid du Colombier arg0 = cb->f[0];
3797dd7cddfSDavid du Colombier if(strcmp(arg0, "bind") == 0) {
3807dd7cddfSDavid du Colombier portbind(b, cb->nf-1, cb->f+1);
3817dd7cddfSDavid du Colombier } else if(strcmp(arg0, "unbind") == 0) {
3827dd7cddfSDavid du Colombier portunbind(b, cb->nf-1, cb->f+1);
3837dd7cddfSDavid du Colombier } else if(strcmp(arg0, "cacheflush") == 0) {
3847dd7cddfSDavid du Colombier log(b, Logcache, "cache flush\n");
3857dd7cddfSDavid du Colombier memset(b->cache, 0, CacheSize*sizeof(Centry));
3867dd7cddfSDavid du Colombier } else if(strcmp(arg0, "set") == 0) {
3877dd7cddfSDavid du Colombier if(cb->nf != 2)
3887dd7cddfSDavid du Colombier error("usage: set option");
3897dd7cddfSDavid du Colombier bridgeoption(b, cb->f[1], 1);
3907dd7cddfSDavid du Colombier } else if(strcmp(arg0, "clear") == 0) {
3917dd7cddfSDavid du Colombier if(cb->nf != 2)
3927dd7cddfSDavid du Colombier error("usage: clear option");
3937dd7cddfSDavid du Colombier bridgeoption(b, cb->f[1], 0);
3947dd7cddfSDavid du Colombier } else if(strcmp(arg0, "delay") == 0) {
3957dd7cddfSDavid du Colombier if(cb->nf != 3)
3967dd7cddfSDavid du Colombier error("usage: delay delay0 delayn");
3977dd7cddfSDavid du Colombier b->delay0 = strtol(cb->f[1], nil, 10);
3987dd7cddfSDavid du Colombier b->delayn = strtol(cb->f[2], nil, 10);
3997dd7cddfSDavid du Colombier } else
4007dd7cddfSDavid du Colombier error("unknown control request");
4017dd7cddfSDavid du Colombier poperror();
4027dd7cddfSDavid du Colombier qunlock(b);
4037dd7cddfSDavid du Colombier free(cb);
4047dd7cddfSDavid du Colombier return n;
4057dd7cddfSDavid du Colombier case Qlog:
4067dd7cddfSDavid du Colombier cb = parsecmd(a, n);
4077dd7cddfSDavid du Colombier p = logctl(b, cb->nf, cb->f, logflags);
4087dd7cddfSDavid du Colombier free(cb);
4097dd7cddfSDavid du Colombier if(p != nil)
4107dd7cddfSDavid du Colombier error(p);
4117dd7cddfSDavid du Colombier return n;
4127dd7cddfSDavid du Colombier }
4137dd7cddfSDavid du Colombier }
4147dd7cddfSDavid du Colombier
4157dd7cddfSDavid du Colombier static int
bridgegen(Chan * c,char *,Dirtab *,int,int s,Dir * dp)4169a747e4fSDavid du Colombier bridgegen(Chan *c, char *, Dirtab*, int, int s, Dir *dp)
4177dd7cddfSDavid du Colombier {
4187dd7cddfSDavid du Colombier Bridge *b = bridgetab + c->dev;
4197dd7cddfSDavid du Colombier int type = TYPE(c->qid);
4207dd7cddfSDavid du Colombier Dirtab *dt;
4217dd7cddfSDavid du Colombier Qid qid;
4227dd7cddfSDavid du Colombier
4237dd7cddfSDavid du Colombier if(s == DEVDOTDOT){
4247dd7cddfSDavid du Colombier switch(TYPE(c->qid)){
4257dd7cddfSDavid du Colombier case Qtopdir:
4267dd7cddfSDavid du Colombier case Qbridgedir:
4279a747e4fSDavid du Colombier snprint(up->genbuf, sizeof(up->genbuf), "#B%ld", c->dev);
4289a747e4fSDavid du Colombier mkqid(&qid, Qtopdir, 0, QTDIR);
4299a747e4fSDavid du Colombier devdir(c, qid, up->genbuf, 0, eve, 0555, dp);
4307dd7cddfSDavid du Colombier break;
4317dd7cddfSDavid du Colombier case Qportdir:
4329a747e4fSDavid du Colombier snprint(up->genbuf, sizeof(up->genbuf), "bridge%ld", c->dev);
4339a747e4fSDavid du Colombier mkqid(&qid, Qbridgedir, 0, QTDIR);
4349a747e4fSDavid du Colombier devdir(c, qid, up->genbuf, 0, eve, 0555, dp);
4357dd7cddfSDavid du Colombier break;
4367dd7cddfSDavid du Colombier default:
4379a747e4fSDavid du Colombier panic("bridgewalk %llux", c->qid.path);
4387dd7cddfSDavid du Colombier }
4397dd7cddfSDavid du Colombier return 1;
4407dd7cddfSDavid du Colombier }
4417dd7cddfSDavid du Colombier
4427dd7cddfSDavid du Colombier switch(type) {
4437dd7cddfSDavid du Colombier default:
444ac020a8fSDavid du Colombier /* non-directory entries end up here */
4459a747e4fSDavid du Colombier if(c->qid.type & QTDIR)
4467dd7cddfSDavid du Colombier panic("bridgegen: unexpected directory");
4477dd7cddfSDavid du Colombier if(s != 0)
4487dd7cddfSDavid du Colombier return -1;
4497dd7cddfSDavid du Colombier dt = dirtab[TYPE(c->qid)];
4507dd7cddfSDavid du Colombier if(dt == nil)
4519a747e4fSDavid du Colombier panic("bridgegen: unknown type: %lud", TYPE(c->qid));
4527dd7cddfSDavid du Colombier devdir(c, c->qid, dt->name, dt->length, eve, dt->perm, dp);
4537dd7cddfSDavid du Colombier return 1;
4547dd7cddfSDavid du Colombier case Qtopdir:
4557dd7cddfSDavid du Colombier if(s != 0)
4567dd7cddfSDavid du Colombier return -1;
4579a747e4fSDavid du Colombier snprint(up->genbuf, sizeof(up->genbuf), "bridge%ld", c->dev);
4589a747e4fSDavid du Colombier mkqid(&qid, QID(0, Qbridgedir), 0, QTDIR);
4599a747e4fSDavid du Colombier devdir(c, qid, up->genbuf, 0, eve, 0555, dp);
4607dd7cddfSDavid du Colombier return 1;
4617dd7cddfSDavid du Colombier case Qbridgedir:
4627dd7cddfSDavid du Colombier if(s<nelem(bridgedirtab)) {
4637dd7cddfSDavid du Colombier dt = bridgedirtab+s;
4647dd7cddfSDavid du Colombier devdir(c, dt->qid, dt->name, dt->length, eve, dt->perm, dp);
4657dd7cddfSDavid du Colombier return 1;
4667dd7cddfSDavid du Colombier }
4677dd7cddfSDavid du Colombier s -= nelem(bridgedirtab);
4687dd7cddfSDavid du Colombier if(s >= b->nport)
4697dd7cddfSDavid du Colombier return -1;
4709a747e4fSDavid du Colombier mkqid(&qid, QID(s, Qportdir), 0, QTDIR);
4719a747e4fSDavid du Colombier snprint(up->genbuf, sizeof(up->genbuf), "%d", s);
4729a747e4fSDavid du Colombier devdir(c, qid, up->genbuf, 0, eve, 0555, dp);
4737dd7cddfSDavid du Colombier return 1;
4747dd7cddfSDavid du Colombier case Qportdir:
4757dd7cddfSDavid du Colombier if(s>=nelem(portdirtab))
4767dd7cddfSDavid du Colombier return -1;
4777dd7cddfSDavid du Colombier dt = portdirtab+s;
4789a747e4fSDavid du Colombier mkqid(&qid, QID(PORT(c->qid),TYPE(dt->qid)), 0, QTFILE);
4797dd7cddfSDavid du Colombier devdir(c, qid, dt->name, dt->length, eve, dt->perm, dp);
4807dd7cddfSDavid du Colombier return 1;
4817dd7cddfSDavid du Colombier }
4827dd7cddfSDavid du Colombier }
4837dd7cddfSDavid du Colombier
484ac020a8fSDavid du Colombier // parse mac address; also in netif.c
4857dd7cddfSDavid du Colombier static int
parseaddr(uchar * to,char * from,int alen)4867dd7cddfSDavid du Colombier parseaddr(uchar *to, char *from, int alen)
4877dd7cddfSDavid du Colombier {
4887dd7cddfSDavid du Colombier char nip[4];
4897dd7cddfSDavid du Colombier char *p;
4907dd7cddfSDavid du Colombier int i;
4917dd7cddfSDavid du Colombier
4927dd7cddfSDavid du Colombier p = from;
4937dd7cddfSDavid du Colombier for(i = 0; i < alen; i++){
4947dd7cddfSDavid du Colombier if(*p == 0)
4957dd7cddfSDavid du Colombier return -1;
4967dd7cddfSDavid du Colombier nip[0] = *p++;
4977dd7cddfSDavid du Colombier if(*p == 0)
4987dd7cddfSDavid du Colombier return -1;
4997dd7cddfSDavid du Colombier nip[1] = *p++;
5007dd7cddfSDavid du Colombier nip[2] = 0;
5017dd7cddfSDavid du Colombier to[i] = strtoul(nip, 0, 16);
5027dd7cddfSDavid du Colombier if(*p == ':')
5037dd7cddfSDavid du Colombier p++;
5047dd7cddfSDavid du Colombier }
5057dd7cddfSDavid du Colombier return 0;
5067dd7cddfSDavid du Colombier }
5077dd7cddfSDavid du Colombier
5087dd7cddfSDavid du Colombier // assumes b is locked
5097dd7cddfSDavid du Colombier static void
portbind(Bridge * b,int argc,char * argv[])5107dd7cddfSDavid du Colombier portbind(Bridge *b, int argc, char *argv[])
5117dd7cddfSDavid du Colombier {
5127dd7cddfSDavid du Colombier Port *port;
5137dd7cddfSDavid du Colombier Chan *ctl;
5147dd7cddfSDavid du Colombier int type = 0, i, n;
5157dd7cddfSDavid du Colombier ulong ownhash;
516ac020a8fSDavid du Colombier char *dev, *dev2 = nil, *p;
517ac020a8fSDavid du Colombier char buf[100], name[KNAMELEN], path[8*KNAMELEN];
518ac020a8fSDavid du Colombier static char usage[] = "usage: bind ether|tunnel name ownhash dev [dev2]";
5197dd7cddfSDavid du Colombier
5209a747e4fSDavid du Colombier memset(name, 0, KNAMELEN);
5217dd7cddfSDavid du Colombier if(argc < 4)
5227dd7cddfSDavid du Colombier error(usage);
5237dd7cddfSDavid du Colombier if(strcmp(argv[0], "ether") == 0) {
5247dd7cddfSDavid du Colombier if(argc != 4)
5257dd7cddfSDavid du Colombier error(usage);
5267dd7cddfSDavid du Colombier type = Tether;
5279a747e4fSDavid du Colombier strncpy(name, argv[1], KNAMELEN);
5289a747e4fSDavid du Colombier name[KNAMELEN-1] = 0;
5297dd7cddfSDavid du Colombier // parseaddr(addr, argv[1], Eaddrlen);
5307dd7cddfSDavid du Colombier } else if(strcmp(argv[0], "tunnel") == 0) {
5317dd7cddfSDavid du Colombier if(argc != 5)
5327dd7cddfSDavid du Colombier error(usage);
5337dd7cddfSDavid du Colombier type = Ttun;
5349a747e4fSDavid du Colombier strncpy(name, argv[1], KNAMELEN);
5359a747e4fSDavid du Colombier name[KNAMELEN-1] = 0;
5367dd7cddfSDavid du Colombier // parseip(addr, argv[1]);
5377dd7cddfSDavid du Colombier dev2 = argv[4];
5387dd7cddfSDavid du Colombier } else
5397dd7cddfSDavid du Colombier error(usage);
5407dd7cddfSDavid du Colombier ownhash = atoi(argv[2]);
5417dd7cddfSDavid du Colombier dev = argv[3];
5427dd7cddfSDavid du Colombier for(i=0; i<b->nport; i++) {
5437dd7cddfSDavid du Colombier port = b->port[i];
544ac020a8fSDavid du Colombier if(port != nil && port->type == type &&
545ac020a8fSDavid du Colombier memcmp(port->name, name, KNAMELEN) == 0)
5467dd7cddfSDavid du Colombier error("port in use");
5477dd7cddfSDavid du Colombier }
5487dd7cddfSDavid du Colombier for(i=0; i<Maxport; i++)
5497dd7cddfSDavid du Colombier if(b->port[i] == nil)
5507dd7cddfSDavid du Colombier break;
5517dd7cddfSDavid du Colombier if(i == Maxport)
5527dd7cddfSDavid du Colombier error("no more ports");
5537dd7cddfSDavid du Colombier port = smalloc(sizeof(Port));
5547dd7cddfSDavid du Colombier port->ref = 1;
5557dd7cddfSDavid du Colombier port->id = i;
5567dd7cddfSDavid du Colombier port->ownhash = ownhash;
5577dd7cddfSDavid du Colombier
5587dd7cddfSDavid du Colombier if(waserror()) {
5597dd7cddfSDavid du Colombier portfree(port);
5607dd7cddfSDavid du Colombier nexterror();
5617dd7cddfSDavid du Colombier }
5627dd7cddfSDavid du Colombier port->type = type;
5639a747e4fSDavid du Colombier memmove(port->name, name, KNAMELEN);
5647dd7cddfSDavid du Colombier switch(port->type) {
565ac020a8fSDavid du Colombier default:
566ac020a8fSDavid du Colombier panic("portbind: unknown port type: %d", type);
5677dd7cddfSDavid du Colombier case Tether:
5687dd7cddfSDavid du Colombier snprint(path, sizeof(path), "%s/clone", dev);
5697dd7cddfSDavid du Colombier ctl = namec(path, Aopen, ORDWR, 0);
5707dd7cddfSDavid du Colombier if(waserror()) {
5717dd7cddfSDavid du Colombier cclose(ctl);
5727dd7cddfSDavid du Colombier nexterror();
5737dd7cddfSDavid du Colombier }
5747dd7cddfSDavid du Colombier // check addr?
5757dd7cddfSDavid du Colombier
5767dd7cddfSDavid du Colombier // get directory name
5777dd7cddfSDavid du Colombier n = devtab[ctl->type]->read(ctl, buf, sizeof(buf), 0);
5787dd7cddfSDavid du Colombier buf[n] = 0;
5797dd7cddfSDavid du Colombier for(p = buf; *p == ' '; p++)
5807dd7cddfSDavid du Colombier ;
5817dd7cddfSDavid du Colombier snprint(path, sizeof(path), "%s/%lud/data", dev, strtoul(p, 0, 0));
5827dd7cddfSDavid du Colombier
5837dd7cddfSDavid du Colombier // setup connection to be promiscuous
5847dd7cddfSDavid du Colombier snprint(buf, sizeof(buf), "connect -1");
5857dd7cddfSDavid du Colombier devtab[ctl->type]->write(ctl, buf, strlen(buf), 0);
5867dd7cddfSDavid du Colombier snprint(buf, sizeof(buf), "promiscuous");
5877dd7cddfSDavid du Colombier devtab[ctl->type]->write(ctl, buf, strlen(buf), 0);
5887dd7cddfSDavid du Colombier snprint(buf, sizeof(buf), "bridge");
5897dd7cddfSDavid du Colombier devtab[ctl->type]->write(ctl, buf, strlen(buf), 0);
5907dd7cddfSDavid du Colombier
5917dd7cddfSDavid du Colombier // open data port
5927dd7cddfSDavid du Colombier port->data[0] = namec(path, Aopen, ORDWR, 0);
5937dd7cddfSDavid du Colombier // dup it
5947dd7cddfSDavid du Colombier incref(port->data[0]);
5957dd7cddfSDavid du Colombier port->data[1] = port->data[0];
5967dd7cddfSDavid du Colombier
5977dd7cddfSDavid du Colombier poperror();
5987dd7cddfSDavid du Colombier cclose(ctl);
5997dd7cddfSDavid du Colombier
6007dd7cddfSDavid du Colombier break;
6017dd7cddfSDavid du Colombier case Ttun:
6027dd7cddfSDavid du Colombier port->data[0] = namec(dev, Aopen, OREAD, 0);
6037dd7cddfSDavid du Colombier port->data[1] = namec(dev2, Aopen, OWRITE, 0);
6047dd7cddfSDavid du Colombier break;
6057dd7cddfSDavid du Colombier }
6067dd7cddfSDavid du Colombier
6077dd7cddfSDavid du Colombier poperror();
6087dd7cddfSDavid du Colombier
609ac020a8fSDavid du Colombier /* committed to binding port */
6107dd7cddfSDavid du Colombier b->port[port->id] = port;
6117dd7cddfSDavid du Colombier port->bridge = b;
6127dd7cddfSDavid du Colombier if(b->nport <= port->id)
6137dd7cddfSDavid du Colombier b->nport = port->id+1;
6147dd7cddfSDavid du Colombier
6157dd7cddfSDavid du Colombier // assumes kproc always succeeds
6167dd7cddfSDavid du Colombier kproc("etherread", etherread, port); // poperror must be next
6177dd7cddfSDavid du Colombier port->ref++;
6187dd7cddfSDavid du Colombier }
6197dd7cddfSDavid du Colombier
6207dd7cddfSDavid du Colombier // assumes b is locked
6217dd7cddfSDavid du Colombier static void
portunbind(Bridge * b,int argc,char * argv[])6227dd7cddfSDavid du Colombier portunbind(Bridge *b, int argc, char *argv[])
6237dd7cddfSDavid du Colombier {
6247dd7cddfSDavid du Colombier int type = 0, i;
6259a747e4fSDavid du Colombier char name[KNAMELEN];
6267dd7cddfSDavid du Colombier ulong ownhash;
627ac020a8fSDavid du Colombier Port *port = nil;
628ac020a8fSDavid du Colombier static char usage[] = "usage: unbind ether|tunnel addr [ownhash]";
6297dd7cddfSDavid du Colombier
6309a747e4fSDavid du Colombier memset(name, 0, KNAMELEN);
6317dd7cddfSDavid du Colombier if(argc < 2 || argc > 3)
6327dd7cddfSDavid du Colombier error(usage);
6337dd7cddfSDavid du Colombier if(strcmp(argv[0], "ether") == 0) {
6347dd7cddfSDavid du Colombier type = Tether;
6359a747e4fSDavid du Colombier strncpy(name, argv[1], KNAMELEN);
6369a747e4fSDavid du Colombier name[KNAMELEN-1] = 0;
6377dd7cddfSDavid du Colombier // parseaddr(addr, argv[1], Eaddrlen);
6387dd7cddfSDavid du Colombier } else if(strcmp(argv[0], "tunnel") == 0) {
6397dd7cddfSDavid du Colombier type = Ttun;
6409a747e4fSDavid du Colombier strncpy(name, argv[1], KNAMELEN);
6419a747e4fSDavid du Colombier name[KNAMELEN-1] = 0;
6427dd7cddfSDavid du Colombier // parseip(addr, argv[1]);
6437dd7cddfSDavid du Colombier } else
6447dd7cddfSDavid du Colombier error(usage);
6457dd7cddfSDavid du Colombier if(argc == 3)
6467dd7cddfSDavid du Colombier ownhash = atoi(argv[2]);
6477dd7cddfSDavid du Colombier else
6487dd7cddfSDavid du Colombier ownhash = 0;
6497dd7cddfSDavid du Colombier for(i=0; i<b->nport; i++) {
6507dd7cddfSDavid du Colombier port = b->port[i];
651ac020a8fSDavid du Colombier if(port != nil && port->type == type &&
652ac020a8fSDavid du Colombier memcmp(port->name, name, KNAMELEN) == 0)
6537dd7cddfSDavid du Colombier break;
6547dd7cddfSDavid du Colombier }
6557dd7cddfSDavid du Colombier if(i == b->nport)
6567dd7cddfSDavid du Colombier error("port not found");
6577dd7cddfSDavid du Colombier if(ownhash != 0 && port->ownhash != 0 && ownhash != port->ownhash)
6587dd7cddfSDavid du Colombier error("bad owner hash");
6597dd7cddfSDavid du Colombier
6607dd7cddfSDavid du Colombier port->closed = 1;
6617dd7cddfSDavid du Colombier b->port[i] = nil; // port is now unbound
6627dd7cddfSDavid du Colombier cacheflushport(b, i);
6637dd7cddfSDavid du Colombier
6647dd7cddfSDavid du Colombier // try and stop reader
6657dd7cddfSDavid du Colombier if(port->readp)
6667dd7cddfSDavid du Colombier postnote(port->readp, 1, "unbind", 0);
6677dd7cddfSDavid du Colombier portfree(port);
6687dd7cddfSDavid du Colombier }
6697dd7cddfSDavid du Colombier
6707dd7cddfSDavid du Colombier // assumes b is locked
6717dd7cddfSDavid du Colombier static Centry *
cachelookup(Bridge * b,uchar d[Eaddrlen])6727dd7cddfSDavid du Colombier cachelookup(Bridge *b, uchar d[Eaddrlen])
6737dd7cddfSDavid du Colombier {
6747dd7cddfSDavid du Colombier int i;
6757dd7cddfSDavid du Colombier uint h;
6767dd7cddfSDavid du Colombier Centry *p;
6777dd7cddfSDavid du Colombier long sec;
6787dd7cddfSDavid du Colombier
6797dd7cddfSDavid du Colombier // dont cache multicast or broadcast
6807dd7cddfSDavid du Colombier if(d[0] & 1)
6817dd7cddfSDavid du Colombier return 0;
6827dd7cddfSDavid du Colombier
6837dd7cddfSDavid du Colombier h = 0;
6847dd7cddfSDavid du Colombier for(i=0; i<Eaddrlen; i++) {
6857dd7cddfSDavid du Colombier h *= 7;
6867dd7cddfSDavid du Colombier h += d[i];
6877dd7cddfSDavid du Colombier }
6887dd7cddfSDavid du Colombier h %= CacheHash;
6897dd7cddfSDavid du Colombier p = b->cache + h;
6907dd7cddfSDavid du Colombier sec = TK2SEC(m->ticks);
6917dd7cddfSDavid du Colombier for(i=0; i<CacheLook; i++,p++) {
6927dd7cddfSDavid du Colombier if(memcmp(d, p->d, Eaddrlen) == 0) {
6937dd7cddfSDavid du Colombier p->dst++;
6947dd7cddfSDavid du Colombier if(sec >= p->expire) {
6957dd7cddfSDavid du Colombier log(b, Logcache, "expired cache entry: %E %d\n",
6967dd7cddfSDavid du Colombier d, p->port);
6977dd7cddfSDavid du Colombier return nil;
6987dd7cddfSDavid du Colombier }
6997dd7cddfSDavid du Colombier p->expire = sec + CacheTimeout;
7007dd7cddfSDavid du Colombier return p;
7017dd7cddfSDavid du Colombier }
7027dd7cddfSDavid du Colombier }
7037dd7cddfSDavid du Colombier log(b, Logcache, "cache miss: %E\n", d);
7047dd7cddfSDavid du Colombier return nil;
7057dd7cddfSDavid du Colombier }
7067dd7cddfSDavid du Colombier
7077dd7cddfSDavid du Colombier // assumes b is locked
7087dd7cddfSDavid du Colombier static void
cacheupdate(Bridge * b,uchar d[Eaddrlen],int port)7097dd7cddfSDavid du Colombier cacheupdate(Bridge *b, uchar d[Eaddrlen], int port)
7107dd7cddfSDavid du Colombier {
7117dd7cddfSDavid du Colombier int i;
7127dd7cddfSDavid du Colombier uint h;
7137dd7cddfSDavid du Colombier Centry *p, *pp;
7147dd7cddfSDavid du Colombier long sec;
7157dd7cddfSDavid du Colombier
7167dd7cddfSDavid du Colombier // dont cache multicast or broadcast
7177dd7cddfSDavid du Colombier if(d[0] & 1) {
7187dd7cddfSDavid du Colombier log(b, Logcache, "bad source address: %E\n", d);
7197dd7cddfSDavid du Colombier return;
7207dd7cddfSDavid du Colombier }
7217dd7cddfSDavid du Colombier
7227dd7cddfSDavid du Colombier h = 0;
7237dd7cddfSDavid du Colombier for(i=0; i<Eaddrlen; i++) {
7247dd7cddfSDavid du Colombier h *= 7;
7257dd7cddfSDavid du Colombier h += d[i];
7267dd7cddfSDavid du Colombier }
7277dd7cddfSDavid du Colombier h %= CacheHash;
7287dd7cddfSDavid du Colombier p = b->cache + h;
7297dd7cddfSDavid du Colombier pp = p;
7307dd7cddfSDavid du Colombier sec = p->expire;
7317dd7cddfSDavid du Colombier
7327dd7cddfSDavid du Colombier // look for oldest entry
7337dd7cddfSDavid du Colombier for(i=0; i<CacheLook; i++,p++) {
7347dd7cddfSDavid du Colombier if(memcmp(p->d, d, Eaddrlen) == 0) {
7357dd7cddfSDavid du Colombier p->expire = TK2SEC(m->ticks) + CacheTimeout;
7367dd7cddfSDavid du Colombier if(p->port != port) {
7377dd7cddfSDavid du Colombier log(b, Logcache, "NIC changed port %d->%d: %E\n",
7387dd7cddfSDavid du Colombier p->port, port, d);
7397dd7cddfSDavid du Colombier p->port = port;
7407dd7cddfSDavid du Colombier }
7417dd7cddfSDavid du Colombier p->src++;
7427dd7cddfSDavid du Colombier return;
7437dd7cddfSDavid du Colombier }
7447dd7cddfSDavid du Colombier if(p->expire < sec) {
7457dd7cddfSDavid du Colombier sec = p->expire;
7467dd7cddfSDavid du Colombier pp = p;
7477dd7cddfSDavid du Colombier }
7487dd7cddfSDavid du Colombier }
7497dd7cddfSDavid du Colombier if(pp->expire != 0)
7507dd7cddfSDavid du Colombier log(b, Logcache, "bumping from cache: %E %d\n", pp->d, pp->port);
7517dd7cddfSDavid du Colombier pp->expire = TK2SEC(m->ticks) + CacheTimeout;
7527dd7cddfSDavid du Colombier memmove(pp->d, d, Eaddrlen);
7537dd7cddfSDavid du Colombier pp->port = port;
7547dd7cddfSDavid du Colombier pp->src = 1;
7557dd7cddfSDavid du Colombier pp->dst = 0;
7567dd7cddfSDavid du Colombier log(b, Logcache, "adding to cache: %E %d\n", pp->d, pp->port);
7577dd7cddfSDavid du Colombier }
7587dd7cddfSDavid du Colombier
7597dd7cddfSDavid du Colombier // assumes b is locked
7607dd7cddfSDavid du Colombier static void
cacheflushport(Bridge * b,int port)7617dd7cddfSDavid du Colombier cacheflushport(Bridge *b, int port)
7627dd7cddfSDavid du Colombier {
7637dd7cddfSDavid du Colombier Centry *ce;
7647dd7cddfSDavid du Colombier int i;
7657dd7cddfSDavid du Colombier
7667dd7cddfSDavid du Colombier ce = b->cache;
7677dd7cddfSDavid du Colombier for(i=0; i<CacheSize; i++,ce++) {
7687dd7cddfSDavid du Colombier if(ce->port != port)
7697dd7cddfSDavid du Colombier continue;
7707dd7cddfSDavid du Colombier memset(ce, 0, sizeof(Centry));
7717dd7cddfSDavid du Colombier }
7727dd7cddfSDavid du Colombier }
7737dd7cddfSDavid du Colombier
7747dd7cddfSDavid du Colombier static char *
cachedump(Bridge * b)7757dd7cddfSDavid du Colombier cachedump(Bridge *b)
7767dd7cddfSDavid du Colombier {
7777dd7cddfSDavid du Colombier int i, n;
7787dd7cddfSDavid du Colombier long sec, off;
7797dd7cddfSDavid du Colombier char *buf, *p, *ep;
7807dd7cddfSDavid du Colombier Centry *ce;
7817dd7cddfSDavid du Colombier char c;
7827dd7cddfSDavid du Colombier
7837dd7cddfSDavid du Colombier qlock(b);
7847dd7cddfSDavid du Colombier if(waserror()) {
7857dd7cddfSDavid du Colombier qunlock(b);
7867dd7cddfSDavid du Colombier nexterror();
7877dd7cddfSDavid du Colombier }
7887dd7cddfSDavid du Colombier sec = TK2SEC(m->ticks);
7897dd7cddfSDavid du Colombier n = 0;
7907dd7cddfSDavid du Colombier for(i=0; i<CacheSize; i++)
7917dd7cddfSDavid du Colombier if(b->cache[i].expire != 0)
7927dd7cddfSDavid du Colombier n++;
7937dd7cddfSDavid du Colombier
7947dd7cddfSDavid du Colombier n *= 51; // change if print format is changed
7957dd7cddfSDavid du Colombier n += 10; // some slop at the end
7967dd7cddfSDavid du Colombier buf = malloc(n);
797*aa72973aSDavid du Colombier if(buf == nil)
798*aa72973aSDavid du Colombier error(Enomem);
7997dd7cddfSDavid du Colombier p = buf;
8007dd7cddfSDavid du Colombier ep = buf + n;
8017dd7cddfSDavid du Colombier ce = b->cache;
8027dd7cddfSDavid du Colombier off = seconds() - sec;
8037dd7cddfSDavid du Colombier for(i=0; i<CacheSize; i++,ce++) {
8047dd7cddfSDavid du Colombier if(ce->expire == 0)
8057dd7cddfSDavid du Colombier continue;
8067dd7cddfSDavid du Colombier c = (sec < ce->expire)?'v':'e';
8077dd7cddfSDavid du Colombier p += snprint(p, ep-p, "%E %2d %10ld %10ld %10ld %c\n", ce->d,
8087dd7cddfSDavid du Colombier ce->port, ce->src, ce->dst, ce->expire+off, c);
8097dd7cddfSDavid du Colombier }
8107dd7cddfSDavid du Colombier *p = 0;
8117dd7cddfSDavid du Colombier poperror();
8127dd7cddfSDavid du Colombier qunlock(b);
8137dd7cddfSDavid du Colombier
8147dd7cddfSDavid du Colombier return buf;
8157dd7cddfSDavid du Colombier }
8167dd7cddfSDavid du Colombier
8177dd7cddfSDavid du Colombier
8187dd7cddfSDavid du Colombier
8197dd7cddfSDavid du Colombier // assumes b is locked
8207dd7cddfSDavid du Colombier static void
ethermultiwrite(Bridge * b,Block * bp,Port * port)8217dd7cddfSDavid du Colombier ethermultiwrite(Bridge *b, Block *bp, Port *port)
8227dd7cddfSDavid du Colombier {
8237dd7cddfSDavid du Colombier Port *oport;
8247dd7cddfSDavid du Colombier Block *bp2;
8257dd7cddfSDavid du Colombier Etherpkt *ep;
8266c0968e3SDavid du Colombier int i, mcast;
8277dd7cddfSDavid du Colombier
8287dd7cddfSDavid du Colombier if(waserror()) {
8297dd7cddfSDavid du Colombier if(bp)
8307dd7cddfSDavid du Colombier freeb(bp);
8317dd7cddfSDavid du Colombier nexterror();
8327dd7cddfSDavid du Colombier }
8337dd7cddfSDavid du Colombier
8347dd7cddfSDavid du Colombier ep = (Etherpkt*)bp->rp;
8356c0968e3SDavid du Colombier mcast = ep->d[0] & 1; /* multicast bit of ethernet address */
8367dd7cddfSDavid du Colombier
8377dd7cddfSDavid du Colombier oport = nil;
8387dd7cddfSDavid du Colombier for(i=0; i<b->nport; i++) {
8397dd7cddfSDavid du Colombier if(i == port->id || b->port[i] == nil)
8407dd7cddfSDavid du Colombier continue;
8416c0968e3SDavid du Colombier /*
8426c0968e3SDavid du Colombier * we need to forward multicast packets for ipv6,
8436c0968e3SDavid du Colombier * so always do it.
8446c0968e3SDavid du Colombier */
8457dd7cddfSDavid du Colombier if(mcast)
8467dd7cddfSDavid du Colombier b->port[i]->outmulti++;
8477dd7cddfSDavid du Colombier else
8487dd7cddfSDavid du Colombier b->port[i]->outunknown++;
8497dd7cddfSDavid du Colombier
8507dd7cddfSDavid du Colombier // delay one so that the last write does not copy
8517dd7cddfSDavid du Colombier if(oport != nil) {
8527dd7cddfSDavid du Colombier b->copy++;
8537dd7cddfSDavid du Colombier bp2 = copyblock(bp, blocklen(bp));
8547dd7cddfSDavid du Colombier if(!waserror()) {
8557dd7cddfSDavid du Colombier etherwrite(oport, bp2);
8567dd7cddfSDavid du Colombier poperror();
8577dd7cddfSDavid du Colombier }
8587dd7cddfSDavid du Colombier }
8597dd7cddfSDavid du Colombier oport = b->port[i];
8607dd7cddfSDavid du Colombier }
8617dd7cddfSDavid du Colombier
8627dd7cddfSDavid du Colombier // last write free block
8637dd7cddfSDavid du Colombier if(oport) {
8647dd7cddfSDavid du Colombier bp2 = bp; bp = nil; USED(bp);
8657dd7cddfSDavid du Colombier if(!waserror()) {
8667dd7cddfSDavid du Colombier etherwrite(oport, bp2);
8677dd7cddfSDavid du Colombier poperror();
8687dd7cddfSDavid du Colombier }
8697dd7cddfSDavid du Colombier } else
8707dd7cddfSDavid du Colombier freeb(bp);
8717dd7cddfSDavid du Colombier
8727dd7cddfSDavid du Colombier poperror();
8737dd7cddfSDavid du Colombier }
8747dd7cddfSDavid du Colombier
8757dd7cddfSDavid du Colombier static void
tcpmsshack(Etherpkt * epkt,int n)8767dd7cddfSDavid du Colombier tcpmsshack(Etherpkt *epkt, int n)
8777dd7cddfSDavid du Colombier {
878ac020a8fSDavid du Colombier int hl, optlen;
8797dd7cddfSDavid du Colombier Iphdr *iphdr;
8807dd7cddfSDavid du Colombier Tcphdr *tcphdr;
881ac020a8fSDavid du Colombier ulong mss, cksum;
8827dd7cddfSDavid du Colombier uchar *optr;
8837dd7cddfSDavid du Colombier
884ac020a8fSDavid du Colombier /* ignore non-ipv4 packets */
885ac020a8fSDavid du Colombier if(nhgets(epkt->type) != ETIP4)
8867dd7cddfSDavid du Colombier return;
8877dd7cddfSDavid du Colombier iphdr = (Iphdr*)(epkt->data);
8887dd7cddfSDavid du Colombier n -= ETHERHDRSIZE;
8897dd7cddfSDavid du Colombier if(n < IPHDR)
8907dd7cddfSDavid du Colombier return;
8917dd7cddfSDavid du Colombier
892ac020a8fSDavid du Colombier /* ignore bad packets */
893ac020a8fSDavid du Colombier if(iphdr->vihl != (IP_VER4|IP_HLEN4)) {
8947dd7cddfSDavid du Colombier hl = (iphdr->vihl&0xF)<<2;
895ac020a8fSDavid du Colombier if((iphdr->vihl&0xF0) != IP_VER4 || hl < (IP_HLEN4<<2))
8967dd7cddfSDavid du Colombier return;
8977dd7cddfSDavid du Colombier } else
898ac020a8fSDavid du Colombier hl = IP_HLEN4<<2;
8997dd7cddfSDavid du Colombier
900ac020a8fSDavid du Colombier /* ignore non-tcp packets */
9017dd7cddfSDavid du Colombier if(iphdr->proto != IP_TCPPROTO)
9027dd7cddfSDavid du Colombier return;
9037dd7cddfSDavid du Colombier n -= hl;
9047dd7cddfSDavid du Colombier if(n < sizeof(Tcphdr))
9057dd7cddfSDavid du Colombier return;
9067dd7cddfSDavid du Colombier tcphdr = (Tcphdr*)((uchar*)(iphdr) + hl);
9077dd7cddfSDavid du Colombier // MSS can only appear in SYN packet
9087dd7cddfSDavid du Colombier if(!(tcphdr->flag[1] & SYN))
9097dd7cddfSDavid du Colombier return;
9107dd7cddfSDavid du Colombier hl = (tcphdr->flag[0] & 0xf0)>>2;
9117dd7cddfSDavid du Colombier if(n < hl)
9127dd7cddfSDavid du Colombier return;
9137dd7cddfSDavid du Colombier
9147dd7cddfSDavid du Colombier // check for MSS option
915ac020a8fSDavid du Colombier optr = (uchar*)tcphdr + sizeof(Tcphdr);
9167dd7cddfSDavid du Colombier n = hl - sizeof(Tcphdr);
9177dd7cddfSDavid du Colombier for(;;) {
9187dd7cddfSDavid du Colombier if(n <= 0 || *optr == EOLOPT)
9197dd7cddfSDavid du Colombier return;
9207dd7cddfSDavid du Colombier if(*optr == NOOPOPT) {
9217dd7cddfSDavid du Colombier n--;
9227dd7cddfSDavid du Colombier optr++;
9237dd7cddfSDavid du Colombier continue;
9247dd7cddfSDavid du Colombier }
9257dd7cddfSDavid du Colombier optlen = optr[1];
9267dd7cddfSDavid du Colombier if(optlen < 2 || optlen > n)
9277dd7cddfSDavid du Colombier return;
9287dd7cddfSDavid du Colombier if(*optr == MSSOPT && optlen == MSS_LENGTH)
9297dd7cddfSDavid du Colombier break;
9307dd7cddfSDavid du Colombier n -= optlen;
9317dd7cddfSDavid du Colombier optr += optlen;
9327dd7cddfSDavid du Colombier }
9337dd7cddfSDavid du Colombier
9347dd7cddfSDavid du Colombier mss = nhgets(optr+2);
9357dd7cddfSDavid du Colombier if(mss <= TcpMssMax)
9367dd7cddfSDavid du Colombier return;
9377dd7cddfSDavid du Colombier // fit checksum
9387dd7cddfSDavid du Colombier cksum = nhgets(tcphdr->cksum);
9397dd7cddfSDavid du Colombier if(optr-(uchar*)tcphdr & 1) {
9407dd7cddfSDavid du Colombier print("tcpmsshack: odd alignment!\n");
9417dd7cddfSDavid du Colombier // odd alignments are a pain
9427dd7cddfSDavid du Colombier cksum += nhgets(optr+1);
9437dd7cddfSDavid du Colombier cksum -= (optr[1]<<8)|(TcpMssMax>>8);
9447dd7cddfSDavid du Colombier cksum += (cksum>>16);
9457dd7cddfSDavid du Colombier cksum &= 0xffff;
9467dd7cddfSDavid du Colombier cksum += nhgets(optr+3);
9477dd7cddfSDavid du Colombier cksum -= ((TcpMssMax&0xff)<<8)|optr[4];
9487dd7cddfSDavid du Colombier cksum += (cksum>>16);
9497dd7cddfSDavid du Colombier } else {
9507dd7cddfSDavid du Colombier cksum += mss;
9517dd7cddfSDavid du Colombier cksum -= TcpMssMax;
9527dd7cddfSDavid du Colombier cksum += (cksum>>16);
9537dd7cddfSDavid du Colombier }
9547dd7cddfSDavid du Colombier hnputs(tcphdr->cksum, cksum);
9557dd7cddfSDavid du Colombier hnputs(optr+2, TcpMssMax);
9567dd7cddfSDavid du Colombier }
9577dd7cddfSDavid du Colombier
9587dd7cddfSDavid du Colombier /*
9597dd7cddfSDavid du Colombier * process to read from the ethernet
9607dd7cddfSDavid du Colombier */
9617dd7cddfSDavid du Colombier static void
etherread(void * a)9627dd7cddfSDavid du Colombier etherread(void *a)
9637dd7cddfSDavid du Colombier {
9647dd7cddfSDavid du Colombier Port *port = a;
9657dd7cddfSDavid du Colombier Bridge *b = port->bridge;
9667dd7cddfSDavid du Colombier Block *bp, *bp2;
9677dd7cddfSDavid du Colombier Etherpkt *ep;
9687dd7cddfSDavid du Colombier Centry *ce;
9697dd7cddfSDavid du Colombier long md;
9707dd7cddfSDavid du Colombier
9717dd7cddfSDavid du Colombier qlock(b);
9727dd7cddfSDavid du Colombier port->readp = up; /* hide identity under a rock for unbind */
9737dd7cddfSDavid du Colombier
9747dd7cddfSDavid du Colombier while(!port->closed){
9757dd7cddfSDavid du Colombier // release lock to read - error means it is time to quit
9767dd7cddfSDavid du Colombier qunlock(b);
9777dd7cddfSDavid du Colombier if(waserror()) {
9783ff48bf5SDavid du Colombier print("etherread read error: %s\n", up->errstr);
9797dd7cddfSDavid du Colombier qlock(b);
9807dd7cddfSDavid du Colombier break;
9817dd7cddfSDavid du Colombier }
982ac020a8fSDavid du Colombier if(0)
983ac020a8fSDavid du Colombier print("devbridge: etherread: reading\n");
984ac020a8fSDavid du Colombier bp = devtab[port->data[0]->type]->bread(port->data[0],
985ac020a8fSDavid du Colombier ETHERMAXTU, 0);
986ac020a8fSDavid du Colombier if(0)
987ac020a8fSDavid du Colombier print("devbridge: etherread: blocklen = %d\n",
988ac020a8fSDavid du Colombier blocklen(bp));
9897dd7cddfSDavid du Colombier poperror();
9907dd7cddfSDavid du Colombier qlock(b);
9917dd7cddfSDavid du Colombier if(bp == nil || port->closed)
9927dd7cddfSDavid du Colombier break;
9937dd7cddfSDavid du Colombier if(waserror()) {
9947dd7cddfSDavid du Colombier // print("etherread bridge error\n");
9957dd7cddfSDavid du Colombier if(bp)
9967dd7cddfSDavid du Colombier freeb(bp);
9977dd7cddfSDavid du Colombier continue;
9987dd7cddfSDavid du Colombier }
9997dd7cddfSDavid du Colombier if(blocklen(bp) < ETHERMINTU)
10007dd7cddfSDavid du Colombier error("short packet");
10017dd7cddfSDavid du Colombier port->in++;
10027dd7cddfSDavid du Colombier
10037dd7cddfSDavid du Colombier ep = (Etherpkt*)bp->rp;
10047dd7cddfSDavid du Colombier cacheupdate(b, ep->s, port->id);
10057dd7cddfSDavid du Colombier if(b->tcpmss)
10067dd7cddfSDavid du Colombier tcpmsshack(ep, BLEN(bp));
10077dd7cddfSDavid du Colombier
10087dd7cddfSDavid du Colombier /*
10097dd7cddfSDavid du Colombier * delay packets to simulate a slow link
10107dd7cddfSDavid du Colombier */
10117dd7cddfSDavid du Colombier if(b->delay0 || b->delayn){
10127dd7cddfSDavid du Colombier md = b->delay0 + b->delayn * BLEN(bp);
10137dd7cddfSDavid du Colombier if(md > 0)
10147dd7cddfSDavid du Colombier microdelay(md);
10157dd7cddfSDavid du Colombier }
10167dd7cddfSDavid du Colombier
10177dd7cddfSDavid du Colombier if(ep->d[0] & 1) {
10186c0968e3SDavid du Colombier log(b, Logmcast, "multicast: port=%d src=%E dst=%E type=%#.4ux\n",
10196c0968e3SDavid du Colombier port->id, ep->s, ep->d, ep->type[0]<<8|ep->type[1]);
10207dd7cddfSDavid du Colombier port->inmulti++;
10217dd7cddfSDavid du Colombier bp2 = bp; bp = nil;
10227dd7cddfSDavid du Colombier ethermultiwrite(b, bp2, port);
10237dd7cddfSDavid du Colombier } else {
10247dd7cddfSDavid du Colombier ce = cachelookup(b, ep->d);
10257dd7cddfSDavid du Colombier if(ce == nil) {
10267dd7cddfSDavid du Colombier b->miss++;
10277dd7cddfSDavid du Colombier port->inunknown++;
10287dd7cddfSDavid du Colombier bp2 = bp; bp = nil;
10297dd7cddfSDavid du Colombier ethermultiwrite(b, bp2, port);
10307dd7cddfSDavid du Colombier }else if(ce->port != port->id){
10317dd7cddfSDavid du Colombier b->hit++;
10327dd7cddfSDavid du Colombier bp2 = bp; bp = nil;
10337dd7cddfSDavid du Colombier etherwrite(b->port[ce->port], bp2);
10347dd7cddfSDavid du Colombier }
10357dd7cddfSDavid du Colombier }
10367dd7cddfSDavid du Colombier
10377dd7cddfSDavid du Colombier poperror();
10387dd7cddfSDavid du Colombier if(bp)
10397dd7cddfSDavid du Colombier freeb(bp);
10407dd7cddfSDavid du Colombier }
10417dd7cddfSDavid du Colombier // print("etherread: trying to exit\n");
10427dd7cddfSDavid du Colombier port->readp = nil;
10437dd7cddfSDavid du Colombier portfree(port);
10447dd7cddfSDavid du Colombier qunlock(b);
10457dd7cddfSDavid du Colombier pexit("hangup", 1);
10467dd7cddfSDavid du Colombier }
10477dd7cddfSDavid du Colombier
10487dd7cddfSDavid du Colombier static int
fragment(Etherpkt * epkt,int n)10497dd7cddfSDavid du Colombier fragment(Etherpkt *epkt, int n)
10507dd7cddfSDavid du Colombier {
10517dd7cddfSDavid du Colombier Iphdr *iphdr;
10527dd7cddfSDavid du Colombier
10537dd7cddfSDavid du Colombier if(n <= TunnelMtu)
10547dd7cddfSDavid du Colombier return 0;
10557dd7cddfSDavid du Colombier
1056ac020a8fSDavid du Colombier /* ignore non-ipv4 packets */
1057ac020a8fSDavid du Colombier if(nhgets(epkt->type) != ETIP4)
10587dd7cddfSDavid du Colombier return 0;
10597dd7cddfSDavid du Colombier iphdr = (Iphdr*)(epkt->data);
10607dd7cddfSDavid du Colombier n -= ETHERHDRSIZE;
1061ac020a8fSDavid du Colombier /*
1062ac020a8fSDavid du Colombier * ignore: IP runt packets, bad packets (I don't handle IP
1063ac020a8fSDavid du Colombier * options for the moment), packets with don't-fragment set,
1064ac020a8fSDavid du Colombier * and short blocks.
1065ac020a8fSDavid du Colombier */
1066ac020a8fSDavid du Colombier if(n < IPHDR || iphdr->vihl != (IP_VER4|IP_HLEN4) ||
1067ac020a8fSDavid du Colombier iphdr->frag[0] & (IP_DF>>8) || nhgets(iphdr->length) > n)
10687dd7cddfSDavid du Colombier return 0;
10697dd7cddfSDavid du Colombier
10707dd7cddfSDavid du Colombier return 1;
10717dd7cddfSDavid du Colombier }
10727dd7cddfSDavid du Colombier
10737dd7cddfSDavid du Colombier
10747dd7cddfSDavid du Colombier static void
etherwrite(Port * port,Block * bp)10757dd7cddfSDavid du Colombier etherwrite(Port *port, Block *bp)
10767dd7cddfSDavid du Colombier {
10777dd7cddfSDavid du Colombier Iphdr *eh, *feh;
10787dd7cddfSDavid du Colombier Etherpkt *epkt;
10797dd7cddfSDavid du Colombier int n, lid, len, seglen, chunk, dlen, blklen, offset, mf;
10807dd7cddfSDavid du Colombier Block *xp, *nb;
10817dd7cddfSDavid du Colombier ushort fragoff, frag;
10827dd7cddfSDavid du Colombier
10837dd7cddfSDavid du Colombier port->out++;
10847dd7cddfSDavid du Colombier epkt = (Etherpkt*)bp->rp;
10857dd7cddfSDavid du Colombier n = blocklen(bp);
10867dd7cddfSDavid du Colombier if(port->type != Ttun || !fragment(epkt, n)) {
10877dd7cddfSDavid du Colombier devtab[port->data[1]->type]->bwrite(port->data[1], bp, 0);
10887dd7cddfSDavid du Colombier return;
10897dd7cddfSDavid du Colombier }
10907dd7cddfSDavid du Colombier port->outfrag++;
10917dd7cddfSDavid du Colombier if(waserror()){
10927dd7cddfSDavid du Colombier freeblist(bp);
10937dd7cddfSDavid du Colombier nexterror();
10947dd7cddfSDavid du Colombier }
10957dd7cddfSDavid du Colombier
10967dd7cddfSDavid du Colombier seglen = (TunnelMtu - ETHERHDRSIZE - IPHDR) & ~7;
10977dd7cddfSDavid du Colombier eh = (Iphdr*)(epkt->data);
10987dd7cddfSDavid du Colombier len = nhgets(eh->length);
10997dd7cddfSDavid du Colombier frag = nhgets(eh->frag);
11007dd7cddfSDavid du Colombier mf = frag & IP_MF;
11017dd7cddfSDavid du Colombier frag <<= 3;
11027dd7cddfSDavid du Colombier dlen = len - IPHDR;
11037dd7cddfSDavid du Colombier xp = bp;
11047dd7cddfSDavid du Colombier lid = nhgets(eh->id);
11057dd7cddfSDavid du Colombier offset = ETHERHDRSIZE+IPHDR;
11067dd7cddfSDavid du Colombier while(xp != nil && offset && offset >= BLEN(xp)) {
11077dd7cddfSDavid du Colombier offset -= BLEN(xp);
11087dd7cddfSDavid du Colombier xp = xp->next;
11097dd7cddfSDavid du Colombier }
11107dd7cddfSDavid du Colombier xp->rp += offset;
11117dd7cddfSDavid du Colombier
1112ac020a8fSDavid du Colombier if(0)
1113ac020a8fSDavid du Colombier print("seglen=%d, dlen=%d, mf=%x, frag=%d\n",
1114ac020a8fSDavid du Colombier seglen, dlen, mf, frag);
11157dd7cddfSDavid du Colombier for(fragoff = 0; fragoff < dlen; fragoff += seglen) {
11167dd7cddfSDavid du Colombier nb = allocb(ETHERHDRSIZE+IPHDR+seglen);
11177dd7cddfSDavid du Colombier
11187dd7cddfSDavid du Colombier feh = (Iphdr*)(nb->wp+ETHERHDRSIZE);
11197dd7cddfSDavid du Colombier
11207dd7cddfSDavid du Colombier memmove(nb->wp, epkt, ETHERHDRSIZE+IPHDR);
11217dd7cddfSDavid du Colombier nb->wp += ETHERHDRSIZE+IPHDR;
11227dd7cddfSDavid du Colombier
11237dd7cddfSDavid du Colombier if((fragoff + seglen) >= dlen) {
11247dd7cddfSDavid du Colombier seglen = dlen - fragoff;
11257dd7cddfSDavid du Colombier hnputs(feh->frag, (frag+fragoff)>>3 | mf);
11267dd7cddfSDavid du Colombier }
11277dd7cddfSDavid du Colombier else
11287dd7cddfSDavid du Colombier hnputs(feh->frag, (frag+fragoff>>3) | IP_MF);
11297dd7cddfSDavid du Colombier
11307dd7cddfSDavid du Colombier hnputs(feh->length, seglen + IPHDR);
11317dd7cddfSDavid du Colombier hnputs(feh->id, lid);
11327dd7cddfSDavid du Colombier
11337dd7cddfSDavid du Colombier /* Copy up the data area */
11347dd7cddfSDavid du Colombier chunk = seglen;
11357dd7cddfSDavid du Colombier while(chunk) {
11367dd7cddfSDavid du Colombier blklen = chunk;
11377dd7cddfSDavid du Colombier if(BLEN(xp) < chunk)
11387dd7cddfSDavid du Colombier blklen = BLEN(xp);
11397dd7cddfSDavid du Colombier memmove(nb->wp, xp->rp, blklen);
11407dd7cddfSDavid du Colombier nb->wp += blklen;
11417dd7cddfSDavid du Colombier xp->rp += blklen;
11427dd7cddfSDavid du Colombier chunk -= blklen;
11437dd7cddfSDavid du Colombier if(xp->rp == xp->wp)
11447dd7cddfSDavid du Colombier xp = xp->next;
11457dd7cddfSDavid du Colombier }
11467dd7cddfSDavid du Colombier
11477dd7cddfSDavid du Colombier feh->cksum[0] = 0;
11487dd7cddfSDavid du Colombier feh->cksum[1] = 0;
11497dd7cddfSDavid du Colombier hnputs(feh->cksum, ipcsum(&feh->vihl));
11507dd7cddfSDavid du Colombier
1151ac020a8fSDavid du Colombier /* don't generate small packets */
11527dd7cddfSDavid du Colombier if(BLEN(nb) < ETHERMINTU)
11537dd7cddfSDavid du Colombier nb->wp = nb->rp + ETHERMINTU;
11547dd7cddfSDavid du Colombier devtab[port->data[1]->type]->bwrite(port->data[1], nb, 0);
11557dd7cddfSDavid du Colombier }
11567dd7cddfSDavid du Colombier poperror();
11577dd7cddfSDavid du Colombier freeblist(bp);
11587dd7cddfSDavid du Colombier }
11597dd7cddfSDavid du Colombier
11607dd7cddfSDavid du Colombier // hold b lock
11617dd7cddfSDavid du Colombier static void
portfree(Port * port)11627dd7cddfSDavid du Colombier portfree(Port *port)
11637dd7cddfSDavid du Colombier {
11647dd7cddfSDavid du Colombier port->ref--;
11657dd7cddfSDavid du Colombier if(port->ref < 0)
11667dd7cddfSDavid du Colombier panic("portfree: bad ref");
11677dd7cddfSDavid du Colombier if(port->ref > 0)
11687dd7cddfSDavid du Colombier return;
11697dd7cddfSDavid du Colombier
11707dd7cddfSDavid du Colombier if(port->data[0])
11717dd7cddfSDavid du Colombier cclose(port->data[0]);
11727dd7cddfSDavid du Colombier if(port->data[1])
11737dd7cddfSDavid du Colombier cclose(port->data[1]);
11747dd7cddfSDavid du Colombier memset(port, 0, sizeof(Port));
11757dd7cddfSDavid du Colombier free(port);
11767dd7cddfSDavid du Colombier }
11777dd7cddfSDavid du Colombier
11787dd7cddfSDavid du Colombier Dev bridgedevtab = {
11797dd7cddfSDavid du Colombier 'B',
11807dd7cddfSDavid du Colombier "bridge",
11817dd7cddfSDavid du Colombier
11827dd7cddfSDavid du Colombier devreset,
11837dd7cddfSDavid du Colombier bridgeinit,
11849a747e4fSDavid du Colombier devshutdown,
11857dd7cddfSDavid du Colombier bridgeattach,
11867dd7cddfSDavid du Colombier bridgewalk,
11877dd7cddfSDavid du Colombier bridgestat,
11887dd7cddfSDavid du Colombier bridgeopen,
11897dd7cddfSDavid du Colombier devcreate,
11907dd7cddfSDavid du Colombier bridgeclose,
11917dd7cddfSDavid du Colombier bridgeread,
11927dd7cddfSDavid du Colombier devbread,
11937dd7cddfSDavid du Colombier bridgewrite,
11947dd7cddfSDavid du Colombier devbwrite,
11957dd7cddfSDavid du Colombier devremove,
11967dd7cddfSDavid du Colombier devwstat,
11977dd7cddfSDavid du Colombier };
1198