Files
boxmaker/docs/plans/M3b/07-toolkit-addr.md
T
kyleandClaude Opus 5.5 5e55fe4c66 M3b plan: every task's git add includes Cargo.lock
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>
2026-09-23 00:00:12 -07:00

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, then cp 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. Run cargo 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 addr reports 4 passed; make gate prints gate: ok.

Stop and report if

  • A test's expectation disagrees with these tables.