diff options
Diffstat (limited to 'fw/src/fw.c')
| -rw-r--r-- | fw/src/fw.c | 89 |
1 files changed, 70 insertions, 19 deletions
diff --git a/fw/src/fw.c b/fw/src/fw.c index 4f94090..a136194 100644 --- a/fw/src/fw.c +++ b/fw/src/fw.c @@ -48,6 +48,7 @@ struct Pkt uchar dst[IPaddrlen]; int sport; int dport; + int frag; /* a later fragment: no ports in it */ int verb; }; @@ -130,6 +131,15 @@ flowlook(int proto, uchar *src, uchar *dst, int sport, int dport, long now) h = flowhash(proto, src, dst, sport, dport); for(f = flowtab[h]; f != nil; f = f->next) if(flowis(f, proto, src, dst, sport, dport)){ + /* + * An expired flow must not match. Reaping only + * happens when a flow is added, so on a quiet + * firewall nothing is ever reaped and a flow that + * timed out long ago would keep passing traffic, + * refreshing itself on every packet. + */ + if(now - f->last > flowtimeout(f->proto)) + return 0; f->last = now; return 1; } @@ -295,6 +305,13 @@ wireup(Wire *w) if(fprint(w->cfd, "bind pkt") < 0) sysfatal("%s: bind pkt: %r", w->net); + /* + * pktmedium claims 4096 with no link header; a card is 1514 with + * 14. Left alone the protected stack emits packets the card + * refuses, and only remote peers capping the MSS hide it. + */ + if(fprint(w->cfd, "mtu 1500") < 0) + fprint(2, "fw: %s: cannot set mtu: %r\n", w->net); if(fprint(w->cfd, "add %s %s", w->addr, w->mask) < 0) sysfatal("%s: add %s %s: %r", w->net, w->addr, w->mask); @@ -333,6 +350,15 @@ parsepkt(uchar *b, int n, Pkt *p) p->proto = b[9]; v4tov6(p->src, b + 12); v4tov6(p->dst, b + 16); + /* + * Only the first fragment carries the transport header. + * Reading one out of a later fragment gives payload bytes + * as ports, which both loses the traffic - it matches no + * flow - and lets a crafted fragment whose bytes happen to + * match an open flow through. + */ + if((nhgets(b + 6) & 0x1FFF) != 0) + p->frag = 1; t = b + hl; n -= hl; break; @@ -349,7 +375,7 @@ parsepkt(uchar *b, int n, Pkt *p) return; } - if((p->proto == 6 || p->proto == 17) && n >= 4){ + if(!p->frag && (p->proto == 6 || p->proto == 17) && n >= 4){ p->sport = nhgets(t); p->dport = nhgets(t + 2); } @@ -382,7 +408,7 @@ relay(Wire *from, Wire *to, int verb) if((n = read(from->dfd, buf, Maxpkt)) <= 0){ fprint(2, "fw: %s: read failed: %r\n", from->side); syslog(0, "fw", "%s: read failed: %r", from->side); - return; + threadexitsall("wire"); } parsepkt(buf, n, &p); @@ -818,8 +844,15 @@ fswrite(Req *r) err = editrules(arg, 1, 0); else if(strcmp(buf, "append") == 0) err = editrules(arg, 0, 0); - else if(strcmp(buf, "delete") == 0) - err = editrules(nil, 0, atoi(arg)); + else if(strcmp(buf, "delete") == 0){ + int nr; + + nr = atoi(arg); + if(nr < 1) + err = "delete wants a rule number, from 1"; + else + err = editrules(nil, 0, nr); + } else err = "unknown command; read ctl for the list"; @@ -854,14 +887,21 @@ rulesdidchange(void) static void servectl(char *mtpt, char *srvname) { + char *user; File *root; - fs.tree = alloctree("fw", "fw", DMDIR|0555, nil); + /* + * Owned by whoever is running fw, not by a user called "fw" that + * does not exist: with 0600 that locked out the administrator as + * effectively as everyone else. + */ + user = getuser(); + fs.tree = alloctree(user, user, DMDIR|0555, nil); root = fs.tree->root; - closefile(createfile(root, "ctl", "fw", 0666, (void*)Qctl)); - closefile(createfile(root, "rules", "fw", 0666, (void*)Qrules)); - closefile(createfile(root, "flows", "fw", 0444, (void*)Qflows)); - closefile(createfile(root, "stats", "fw", 0444, (void*)Qstats)); + closefile(createfile(root, "ctl", user, 0600, (void*)Qctl)); + closefile(createfile(root, "rules", user, 0600, (void*)Qrules)); + closefile(createfile(root, "flows", user, 0440, (void*)Qflows)); + closefile(createfile(root, "stats", user, 0440, (void*)Qstats)); threadpostmountsrv(&fs, srvname, mtpt, MREPL); } @@ -947,9 +987,14 @@ etherin(void *a) buf = emalloc(Maxpkt); for(;;){ if((n = read(efd, buf, Maxpkt)) <= 0){ + /* + * One relay stopping would leave the other running + * and that direction unfiltered, with nothing to + * notice. Take the whole firewall down instead. + */ fprint(2, "fw: %s: read the card: %r\n", w->side); syslog(0, "fw", "stopped reading the card: %r"); - return; + threadexitsall("card"); } if(debug > 1) fprint(2, "wire: %d bytes type %.4ux\n", n, (buf[12]<<8)|buf[13]); @@ -977,7 +1022,7 @@ etherout(void *a) if((n = read(w->dfd, buf, Maxpkt)) <= 0){ fprint(2, "fw: %s: read the stack: %r\n", w->side); syslog(0, "fw", "stopped reading the stack: %r"); - return; + threadexitsall("stack"); } if(permitted(buf, n, Vout, &p)) etherwriteip(buf, n, ethermask); @@ -1057,6 +1102,19 @@ learnaddr(char *net, char *dev, char *addr, int naddr, char *gw, int ngw) * it: a live fw would still be holding the card we are about to take. */ /* + * Undo a previous fw that died. + * + * An IP stack outlives the program that configured it, so a fw that is + * killed leaves its pkt interface behind, holding the address, with + * nothing on the other end of it. The machine has no network until + * someone unpicks that by hand, and the next fw to start makes a second + * interface with the same address and routes that could go to either. + * + * So before taking anything, throw away any pkt interface already + * carrying the address we are about to use. Nothing else can have made + * it: a live fw would still be holding the card we are about to take. + */ +/* * Put the card back. * * Taking a card is destructive: the stack loses it, and the pkt @@ -1248,6 +1306,7 @@ threadmain(int argc, char **argv) fmtinstall('I', eipfmt); fmtinstall('V', eipfmt); fmtinstall('E', eipfmt); + fmtinstall('M', eipfmt); /* fmtrules prints masks with it */ rulepath = argv[0]; readrules(rulepath); @@ -1317,14 +1376,6 @@ threadmain(int argc, char **argv) ethersetaddr(ip, gw, haveg); reclaim(netmtpt, abuf); - backdev = etherdev; - backaddr = abuf; - backmask = m; - backnet = netmtpt; - backgw = ethergw; - atexit(putback); - threadnotify(notehandler, 1); - takecard(netmtpt, etherdev); prot = emalloc(sizeof *prot); |
