📄 devbridge.c
字号:
break; case Ttun: 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 kproc("etherread", etherread, port); // poperror must be next port->ref++;}// assumes b is lockedstatic voidportunbind(Bridge *b, int argc, char *argv[]){ Port *port=nil; int type=0, i; char *usage = "usage: unbind ether|tunnel addr [ownhash]"; char name[KNAMELEN]; ulong ownhash; memset(name, 0, KNAMELEN); if(argc < 2 || argc > 3) error(usage); if(strcmp(argv[0], "ether") == 0) { type = Tether; strncpy(name, argv[1], KNAMELEN); name[KNAMELEN-1] = 0;// parseaddr(addr, argv[1], Eaddrlen); } else if(strcmp(argv[0], "tunnel") == 0) { type = Ttun; strncpy(name, argv[1], KNAMELEN); name[KNAMELEN-1] = 0;// parseip(addr, argv[1]); } else error(usage); if(argc == 3) ownhash = atoi(argv[2]); else ownhash = 0; for(i=0; i<b->nport; i++) { port = b->port[i]; if(port != nil) if(port->type == type) if(memcmp(port->name, name, KNAMELEN) == 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 lockedstatic 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; i<Eaddrlen; i++) { h *= 7; h += d[i]; } h %= CacheHash; p = b->cache + h; sec = TK2SEC(m->ticks); for(i=0; i<CacheLook; i++,p++) { if(memcmp(d, p->d, 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 lockedstatic voidcacheupdate(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; i<Eaddrlen; i++) { h *= 7; h += d[i]; } h %= CacheHash; p = b->cache + h; pp = p; sec = p->expire; // look for oldest entry for(i=0; i<CacheLook; i++,p++) { if(memcmp(p->d, 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 lockedstatic voidcacheflushport(Bridge *b, int port){ Centry *ce; int i; ce = b->cache; for(i=0; i<CacheSize; i++,ce++) { if(ce->port != 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; i<CacheSize; i++) if(b->cache[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; i<CacheSize; i++,ce++) { if(ce->expire == 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 lockedstatic voidethermultiwrite(Bridge *b, Block *bp, Port *port){ Port *oport; 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; oport = nil; for(i=0; i<b->nport; i++) { if(i == port->id || b->port[i] == nil) continue; if(mcast && !bcast && !b->port[i]->mcast) continue; if(mcast) b->port[i]->outmulti++; else b->port[i]->outunknown++; // delay one so that the last write does not copy if(oport != nil) { b->copy++; bp2 = copyblock(bp, blocklen(bp)); if(!waserror()) { etherwrite(oport, bp2); poperror(); } } oport = b->port[i]; } // last write free block if(oport) { bp2 = bp; bp = nil; USED(bp); if(!waserror()) { etherwrite(oport, bp2); poperror(); } } else freeb(bp); poperror();}static voidtcpmsshack(Etherpkt *epkt, int n){ int hl; Iphdr *iphdr; Tcphdr *tcphdr; ulong mss; ulong cksum; int optlen; uchar *optr; // check it is an ip packet if(nhgets(epkt->type) != 0x800) return; iphdr = (Iphdr*)(epkt->data); n -= ETHERHDRSIZE; if(n < IPHDR) return; // check it is ok IP packet if(iphdr->vihl != (IP_VER|IP_HLEN)) { hl = (iphdr->vihl&0xF)<<2; if((iphdr->vihl&0xF0) != IP_VER || hl < (IP_HLEN<<2)) return; } else hl = IP_HLEN<<2; // check TCP if(iphdr->proto != IP_TCPPROTO) return; n -= hl; if(n < sizeof(Tcphdr)) return; tcphdr = (Tcphdr*)((uchar*)(iphdr) + hl); // MSS can only appear in SYN packet if(!(tcphdr->flag[1] & SYN)) return; hl = (tcphdr->flag[0] & 0xf0)>>2; if(n < hl) return; // check for MSS option optr = (uchar*)(tcphdr) + sizeof(Tcphdr); n = hl - sizeof(Tcphdr); for(;;) { if(n <= 0 || *optr == EOLOPT) return; if(*optr == NOOPOPT) { n--; optr++; continue; } optlen = optr[1]; if(optlen < 2 || optlen > n) return; if(*optr == MSSOPT && optlen == MSS_LENGTH) break; n -= optlen; optr += optlen; } mss = nhgets(optr+2); if(mss <= TcpMssMax) return; // fit checksum cksum = nhgets(tcphdr->cksum); if(optr-(uchar*)tcphdr & 1) {print("tcpmsshack: odd alignment!\n"); // odd alignments are a pain cksum += nhgets(optr+1); cksum -= (optr[1]<<8)|(TcpMssMax>>8); cksum += (cksum>>16); cksum &= 0xffff; cksum += nhgets(optr+3); cksum -= ((TcpMssMax&0xff)<<8)|optr[4]; cksum += (cksum>>16); } else { cksum += mss; cksum -= TcpMssMax; cksum += (cksum>>16); } hnputs(tcphdr->cksum, cksum); hnputs(optr+2, TcpMssMax);}/* * process to read from the ethernet */static voidetherread(void *a){ Port *port = a; Bridge *b = port->bridge; Block *bp, *bp2; Etherpkt *ep; Centry *ce; long md; 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()) {print("etherread read error: %s\n", up->errstr); qlock(b); break; }if(0)print("devbridge: etherread: reading\n"); bp = devtab[port->data[0]->type]->bread(port->data[0], ETHERMAXTU, 0);if(0)print("devbridge: etherread: blocklen = %d\n", blocklen(bp)); poperror(); qlock(b); if(bp == nil || port->closed) break; if(waserror()) {//print("etherread bridge error\n"); if(bp) freeb(bp); continue; } if(blocklen(bp) < ETHERMINTU) error("short packet"); port->in++; ep = (Etherpkt*)bp->rp; cacheupdate(b, ep->s, port->id); if(b->tcpmss) tcpmsshack(ep, BLEN(bp)); /* * delay packets to simulate a slow link */ if(b->delay0 || b->delayn){ md = b->delay0 + b->delayn * BLEN(bp); if(md > 0) microdelay(md); } 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; etherwrite(b->port[ce->port], bp2); } } poperror(); if(bp) freeb(bp); }//print("etherread: trying to exit\n"); port->readp = nil; portfree(port); qunlock(b); pexit("hangup", 1);}static intfragment(Etherpkt *epkt, int n){ Iphdr *iphdr; if(n <= TunnelMtu) return 0; // check it is an ip packet if(nhgets(epkt->type) != 0x800) return 0; iphdr = (Iphdr*)(epkt->data); n -= ETHERHDRSIZE; if(n < IPHDR) return 0; // check it is ok IP packet - I don't handle IP options for the momment if(iphdr->vihl != (IP_VER|IP_HLEN)) return 0; // check for don't fragment if(iphdr->frag[0] & (IP_DF>>8)) return 0; // check for short block if(nhgets(iphdr->length) > n) return 0; return 1;}static voidetherwrite(Port *port, Block *bp){ Iphdr *eh, *feh; Etherpkt *epkt; int n, lid, len, seglen, chunk, dlen, blklen, offset, mf; Block *xp, *nb; ushort fragoff, frag; port->out++; epkt = (Etherpkt*)bp->rp; n = blocklen(bp); if(port->type != Ttun || !fragment(epkt, n)) { devtab[port->data[1]->type]->bwrite(port->data[1], bp, 0); return; } port->outfrag++; if(waserror()){ freeblist(bp); nexterror(); } seglen = (TunnelMtu - ETHERHDRSIZE - IPHDR) & ~7; eh = (Iphdr*)(epkt->data); len = nhgets(eh->length); frag = nhgets(eh->frag); mf = frag & IP_MF; frag <<= 3; dlen = len - IPHDR; xp = bp; lid = nhgets(eh->id); offset = ETHERHDRSIZE+IPHDR; while(xp != nil && offset && offset >= BLEN(xp)) { offset -= BLEN(xp); xp = xp->next; } xp->rp += offset; if(0) print("seglen=%d, dlen=%d, mf=%x, frag=%d\n", seglen, dlen, mf, frag); for(fragoff = 0; fragoff < dlen; fragoff += seglen) { nb = allocb(ETHERHDRSIZE+IPHDR+seglen); feh = (Iphdr*)(nb->wp+ETHERHDRSIZE); memmove(nb->wp, epkt, ETHERHDRSIZE+IPHDR); nb->wp += ETHERHDRSIZE+IPHDR; if((fragoff + seglen) >= dlen) { seglen = dlen - fragoff; hnputs(feh->frag, (frag+fragoff)>>3 | mf); } else hnputs(feh->frag, (frag+fragoff>>3) | IP_MF); hnputs(feh->length, seglen + IPHDR); hnputs(feh->id, lid); /* Copy up the data area */ chunk = seglen; while(chunk) { blklen = chunk; if(BLEN(xp) < chunk) blklen = BLEN(xp); memmove(nb->wp, xp->rp, blklen); nb->wp += blklen; xp->rp += blklen; chunk -= blklen; if(xp->rp == xp->wp) xp = xp->next; } feh->cksum[0] = 0; feh->cksum[1] = 0; hnputs(feh->cksum, ipcsum(&feh->vihl)); // don't generate small packets if(BLEN(nb) < ETHERMINTU) nb->wp = nb->rp + ETHERMINTU; devtab[port->data[1]->type]->bwrite(port->data[1], nb, 0); } poperror(); freeblist(bp); }// hold b lockstatic voidportfree(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, devshutdown, bridgeattach, bridgewalk, bridgestat, bridgeopen, devcreate, bridgeclose, bridgeread, devbread, bridgewrite, devbwrite, devremove, devwstat,};
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -