summaryrefslogtreecommitdiff
path: root/fw/src/rules.h
diff options
context:
space:
mode:
authorCalvin Morrison <calvin@pobox.com>2026-08-18 17:01:49 -0400
committerCalvin Morrison <calvin@pobox.com>2026-08-18 17:01:49 -0400
commit0f922552ad8cc73c0c3c3674d484c3d78dd8c557 (patch)
tree4bfc4222ccc202daa2d79d6a782727fac1fb9ae0 /fw/src/rules.h
parent441c64d81594bb521350dbee6348f552401a1a2e (diff)
fw: a firewall, at a card, between two networks, or in front of a namespace
One program with three modes, sharing one rule engine and one ndb rule language. Which mode it is depends on what you point it at, and it says so at startup rather than choosing silently. fw -e /net/ether0 rules.ndb a card: every packet in or out fw rules.ndb <side> <side> two networks: everything crossing fw rules.ndb one namespace: what programs ask for The first two filter packets on a wire, using the pkt medium: the stack gives up its card and gets a synthetic one with fw on the other end, so nothing reaches it that fw did not pass. Since the stack no longer has ethernet, fw answers ARP for the address it stands in for. The third serves a filtered /net and matches connect and announce before they reach the kernel, so a refusal comes back out of dial(2) with a reason. That is only a boundary if the program also loses #I, which /dev/drivers does and cannot be undone; fw.rc does it in the right order. Rules are ndb, matched top to bottom, first match wins, no match denies. Connections are tracked, so permitting traffic one way permits the replies. A rule change drops connections the new rules forbid rather than letting them finish: a block blocks. Logging is per rule, to /sys/log/fw. Tested on the init-test VM in all three modes: a page fetched through a real card, a TCP handshake across two networks, request filtering with the escape routes closed, live rule changes killing established connections, and one rule file working unchanged at both altitudes. doc/todo.md has what is not done. Item 1 is the one that matters: a fw that dies takes the card's address with it, so the machine loses its network and fw cannot restart unaided. That also blocks svc supervision. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Diffstat (limited to 'fw/src/rules.h')
-rw-r--r--fw/src/rules.h57
1 files changed, 57 insertions, 0 deletions
diff --git a/fw/src/rules.h b/fw/src/rules.h
new file mode 100644
index 0000000..6aad0ae
--- /dev/null
+++ b/fw/src/rules.h
@@ -0,0 +1,57 @@
+/*
+ * The rule engine, shared by both halves of fw: request filtering (the
+ * ctl layer) and packet filtering (the wire). One rule language, two enforcement points: a rule says what
+ * may happen, and the caller decides where it is enforced.
+ */
+typedef struct Rule Rule;
+
+/*
+ * Direction, not layer. A "connect" written to a ctl file and an
+ * egress packet are the same intent seen from two places, so they are
+ * the same verb and one rule file serves both enforcement points.
+ */
+enum
+{
+ Vin, /* inbound: announce, or a packet arriving */
+ Vout, /* outbound: connect, or a packet leaving */
+ Vany,
+};
+
+struct Rule
+{
+ int allow;
+ int verb;
+ char *proto; /* nil: any */
+ int anyip;
+ uchar ip[IPaddrlen]; /* the peer, whichever end that is */
+ uchar mask[IPaddrlen];
+ int port; /* peer port, -1: any */
+ int lport; /* local port, -1: any */
+ int log; /* note matches in /sys/log/fw */
+ long hits; /* how often it has decided something */
+ int nr;
+ Rule *next;
+};
+
+/*
+ * Rules are swapped wholesale, never mutated in place, so no packet is
+ * ever judged against a half-applied rule set. rulechanged, if set, is
+ * called after a swap - the wire half uses it to re-check live flows.
+ */
+extern Rule *rules;
+extern void (*rulechanged)(void);
+
+void readrules(char*); /* parse and install, fatal on error */
+Rule* parserules(char*, char**); /* parse only; nil + reason on error */
+void installrules(Rule*); /* swap in, then call rulechanged */
+void freerules(Rule*);
+long fmtrules(char*, long); /* current set, back in ndb form */
+long fmthits(char*, long); /* the same, with hit counts */
+void dumprules(void);
+void checklogging(void);
+char* matchrule(int verb, char *proto, uchar *ip, int anyip, int port, int lport, Rule**);
+int protoname2num(char*);
+char* protonum2name(int);
+
+void* emalloc(ulong);
+char* estrdup(char*);