summaryrefslogtreecommitdiff
path: root/fw/doc/todo.md
blob: 97d1d71aab5930aacd966e51f6d5336cd791569e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# fw: open items

Ranked. Item 1 is the only thing between this and running it on a
machine you care about.

## 1. A dead fw takes the network with it

**Severity: blocks use.**

Taking a card is destructive and is not undone. The `pkt` interface that
replaces it is `unbindonclose`, so when `fw` stops the interface goes and
the address goes with it. The machine is left with a card bound to
nothing and no network.

Worse, `fw` cannot restart unaided: the address it would read off the
card is the address that just vanished. So it exits, and a supervisor
with `restart=always` would spin.

`fw` tries to put the card back as it exits (`putback`, via `atexit` and
`threadnotify`). That covers an orderly stop. **It did not fire on a kill
in testing** and I did not chase it further — a cleanup that works
sometimes is worse than none, because you would trust it.

The fix is probably not more note handling. Whatever restarts `fw` has to
be able to configure the card first, which means the address has to
survive somewhere `fw` does not own. That is a supervisor's job.

This is also what blocks `svc` supervision (item 3), so one fix, two
payoffs.

## 2. Broadcast handling is written but unwitnessed

Broadcast and multicast are addressed directly rather than resolved:
`255.255.255.255` and the subnet broadcast to `ff:ff:ff:ff:ff:ff`,
`224/4` to `01:00:5e:...`, `ff00::/8` to `33:33:...`. Without this a
DHCP renewal would be ARPed for the gateway and unicast there, and the
lease would quietly never renew.

The mapping is the standard one and normal traffic is unaffected, but
**I never managed to get a broadcast to cross `fw` to confirm it.**
Reviewed, not observed.

Related and unfixed: `fw` reads the address once at startup, so a lease
that *changes* the address goes unnoticed until restart.

## 3. fw daemonizes, so svc cannot supervise it

The process you exec returns immediately and leaves the server behind.
`svc` would see an instant exit and, with `restart=always`, spin.

Needs a foreground mode where the process started is the process that
stays. `svc`'s `ready=srv:name` fits: `fw -s fw.ether0` already posts to
`/srv`.

`fwstart` is the wrong shape and should probably go. `svc` already does
dependency ordering and per-service supervision; `fwstart` re-implements
the loop in rc and then exits, so `svc` would be supervising a process
that has already gone. One service per card is the right shape.

## 4. One card per fw, and rules cannot name a card

Two cards means two `fw`s with two rule files. Tested and it works, but
a rule cannot say `ifc=ether0`, so one file cannot express different
policy for different cards. Wants repeatable `-e` and an `ifc=`
attribute, and those go together.

## 5. Tflush is not implemented

A request `fw` is blocked on cannot be abandoned, so killing a program
that is waiting for an inbound connection does not reach `fw`. Only
affects the namespace mode; nothing in packet mode blocks indefinitely.

Doable: record the worker's pid against the Req, post an `interrupt`
note on flush, catch it with `threadnotify` so the syscall returns
`interrupted` rather than killing the proc. Maybe 80 lines. There is a
race that cannot be fully closed — between the syscall returning and the
handler clearing its entry, a note may already be in flight and land on
a worker that has moved on, failing an unrelated request. Rare,
unreproducible, and the reason to do it deliberately.

## 6. Positional delete renumbers

`delete n` counts lines of `rules`, so numbers shift after each delete
and `delete 3` twice removes two different rules. Inherent to positional
deletion — iptables has it too — but it should be said out loud.

## 7. Untested at the edges

- The gateway has only been tested between two synthetic stacks on one
  machine. `run.sh` has `-gw`/`-lan` for a two-VM test; the client VM
  was never built.
- No IPv6 traffic has been pushed through any mode. The code paths
  exist and parse v6, but nothing has exercised them.
- No test with a real second NIC carrying real traffic.

## Deliberately not doing

**NAT.** See design.md. A 9front client imports `/net`; anything else
behind an IPv4 gateway with one address is the only case that needs it,
and that case can wait for someone who actually has it.

**Rate limiting, fragment logic, ICMP type matching, deep IPv6.** Scope,
not difficulty. The useful firewall is the one that ships.