summaryrefslogtreecommitdiff
path: root/fw/src/fw.c
diff options
context:
space:
mode:
Diffstat (limited to 'fw/src/fw.c')
-rw-r--r--fw/src/fw.c89
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);