Task 04 added dependencies to toolkit and its git add line left out the lock file, so the driver stopped on an unclean tree. The lock change is folded into task 04's commit. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
3.4 KiB
M3b task 07: which addresses are public
Branch: m3b (run git switch m3b; git status --short must be empty, otherwise stop)
Commit subject: toolkit: is_public, the addresses the egress proxy may reach
Goal
The egress proxy (task 08) connects only to public addresses. A grant allows a host on the
internet; if its name resolves into the tailnet (100.64.0.0/10), the host (127.0.0.1) or a
private network, the connection is refused even though the name is allowed. This task is the one
pure function that decides it. Spec section 5, "Public".
Files
- Copy:
crates/toolkit/tests/addr.rs - Create:
crates/toolkit/src/addr.rs - Modify:
crates/toolkit/src/lib.rs(pub mod addr;),docs/implementer-log.md
Interface
/// True if `ip` is a public unicast address.
pub fn is_public(ip: std::net::IpAddr) -> bool;
Refused ranges: every one of these is not public
IPv4 (with [a, b, c, _] = ip.octets()):
| Range | Test |
|---|---|
0.0.0.0/8 |
a == 0 |
10.0.0.0/8 |
a == 10 |
100.64.0.0/10 (the tailnet) |
a == 100 && (64..=127).contains(&b) |
127.0.0.0/8 |
a == 127 |
169.254.0.0/16 |
a == 169 && b == 254 |
172.16.0.0/12 |
a == 172 && (16..=31).contains(&b) |
192.0.0.0/24 |
a == 192 && b == 0 && c == 0 |
192.0.2.0/24 |
a == 192 && b == 0 && c == 2 |
192.168.0.0/16 |
a == 192 && b == 168 |
198.18.0.0/15 |
a == 198 && (b == 18 || b == 19) |
198.51.100.0/24 |
a == 198 && b == 51 && c == 100 |
203.0.113.0/24 |
a == 203 && b == 0 && c == 113 |
224.0.0.0/4 and 240.0.0.0/4 |
a >= 224 |
IPv6 (with s = ip.segments(), eight u16s):
| Range | Test |
|---|---|
::/96 (holds ::, ::1, and the old IPv4-compatible form) |
the first six segments are all 0 |
IPv4-mapped ::ffff:0:0/96 |
s[0..5] all 0 and s[5] == 0xffff: judge the last 32 bits as IPv4 |
NAT64 64:ff9b::/96 |
s[0..6] == [0x64, 0xff9b, 0, 0, 0, 0]: judge the last 32 bits as IPv4 |
fc00::/7 |
s[0] & 0xfe00 == 0xfc00 |
fe80::/10 |
s[0] & 0xffc0 == 0xfe80 |
ff00::/8 |
s[0] & 0xff00 == 0xff00 |
2001:db8::/32 |
s[0] == 0x2001 && s[1] == 0x0db8 |
Check the three "judge as IPv4" rows before the others. The last 32 bits as IPv4 are
Ipv4Addr::from(((s[6] as u32) << 16) | s[7] as u32) in spirit, but no as casts: use
u32::from(s[6]) and u32::from(s[7]), or s[6].to_be_bytes() and s[7].to_be_bytes().
Everything else is public. Do not use the standard library's is_global (unstable) or add ranges
that are not in these tables: the test checks public neighbours just outside each range too.
Steps
- 1. Copy.
git switch m3b, thencp docs/plans/M3b/files/crates/toolkit/tests/addr.rs crates/toolkit/tests/ - 2. See it fail.
cargo test -p toolkit --test addr. Expected: it does not compile. - 3. Write
addr.rs. Runcargo fmt --all. - 4. See it pass.
cargo test -p toolkit --test addr. Expected: 4 passed. - 5. Walk the tables. Point at the line for each row of both tables.
- 6. Run the gate.
make gate. Expected last line:gate: ok. - 7. Log and commit.
git add crates/toolkit docs/implementer-log.md Cargo.lock && git commit
Done when
cargo test -p toolkit --test addrreports 4 passed;make gateprintsgate: ok.
Stop and report if
- A test's expectation disagrees with these tables.