code wiki / (root) / nx_cidr_smoke.nx

nx_cidr_smoke.nx source

↩ module page · 107 lines · 3667 B

1// cidr.nx -- IPv4 CIDR (RFC 4632) parser + match. 2// 3// Parses "a.b.c.d/N" notation and provides address-in-range 4// matching. Used by: ACLs, rate-limit rules, VPN route tables, 5// service discovery (DHCP scope definition). 6// 7// IPv6 CIDR (a:b:c::d/prefix) deferred -- same algorithm but 8// wider masks. Would share helpers with ipaddr.nx. 9// 10// Invariants: 11// CIDR1 Prefix length range-checked: [0, 32] for IPv4. 12// CIDR2 Host bits in the network address are IGNORED on 13// parse (RFC 4632 permissive). Canonical form masks 14// them out via cidr_network_address. 15// CIDR3 Match is bit-exact against the computed network + 16// mask; no regex, no partial parsing, no DNS resolution. 17 18// nx_safety_envelope: 19// intended_use: AUTO_APPLIED -- primitive-specific tuning queued 20// sil_target: SIL1 21// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail] 22// verdict: NOT_YET_EVALUATED 23 24import "nx_syscalls.nx" 25import "nx_ipaddr.nx" 26 27const CIDR_ERR_FORMAT: i64 = -1 28const CIDR_ERR_BAD_PREFIX: i64 = -2 29 30// Parse "a.b.c.d/N" into ip (u32) + prefix. Writes to *ip_out, 31// *prefix_out. Returns 0 on success, negative CIDR_ERR_* on error. 32func cidr_parse(s: *u8, n: i64, ip_out: *i64, prefix_out: *i64) -> i64 { 33 // Find '/'. 34 var slash: i64 = 0 - 1 35 var i: i64 = 0 36 while i < n { 37 if s[i] == 0x2F { slash = i; i = n } 38 else { i = i + 1 } 39 } 40 if slash < 0 { return CIDR_ERR_FORMAT } 41 42 // Parse IP portion. 43 let ip_val: i64 = ipv4_parse(s, slash) 44 if ip_val < 0 { return ip_val } 45 46 // Parse prefix: decimal integer after '/'. 47 var prefix: i64 = 0 48 var j: i64 = slash + 1 49 var saw_digit: i64 = 0 50 while j < n { 51 let c: i64 = s[j] 52 if c < 0x30 { return CIDR_ERR_BAD_PREFIX } 53 if c > 0x39 { return CIDR_ERR_BAD_PREFIX } 54 prefix = prefix * 10 + (c - 0x30) 55 saw_digit = 1 56 j = j + 1 57 } 58 if saw_digit == 0 { return CIDR_ERR_BAD_PREFIX } 59 if prefix < 0 { return CIDR_ERR_BAD_PREFIX } 60 if prefix > 32 { return CIDR_ERR_BAD_PREFIX } 61 62 *ip_out = ip_val & 0xFFFFFFFF 63 *prefix_out = prefix 64 return 0 65} 66 67// Compute the bitmask for an IPv4 prefix length. 68// prefix=0 -> 0x00000000; prefix=24 -> 0xFFFFFF00; prefix=32 -> 0xFFFFFFFF. 69func cidr_mask(prefix: i64) -> i64 { 70 if prefix == 0 { return 0 } 71 if prefix >= 32 { return 0xFFFFFFFF } 72 let shift: i64 = 32 - prefix 73 let ones: i64 = (1 << 32) - 1 74 return (ones << shift) & 0xFFFFFFFF 75} 76 77// Canonicalise: clear host bits, return the network address. 78func cidr_network_address(ip: i64, prefix: i64) -> i64 { 79 return (ip & cidr_mask(prefix)) & 0xFFFFFFFF 80} 81 82// Does `addr` fall within the `network/prefix` CIDR block? 83func cidr_contains(network: i64, prefix: i64, addr: i64) -> i64 { 84 let m: i64 = cidr_mask(prefix) 85 if (network & m) == (addr & m) { return 1 } 86 return 0 87} 88 89// Compile-only smoke: parse "10.0.0.0/8", verify containment of 90// 10.1.2.3 and rejection of 192.168.1.1. 91func main() -> i64 { 92 let ip_slot: *i64 = sys_mmap(16) as *i64 93 let pfx_slot: *i64 = sys_mmap(16) as *i64 94 let rc: i64 = cidr_parse("10.0.0.0/8", 10, ip_slot, pfx_slot) 95 if rc != 0 { return 1 } 96 if *pfx_slot != 8 { return 2 } 97 if *ip_slot != 0x0A000000 { return 3 } 98 99 let m: i64 = cidr_mask(8) 100 if m != 0xFF000000 { return 4 } 101 102 // 10.1.2.3 = 0x0A010203 -- should match 10.0.0.0/8. 103 if cidr_contains(*ip_slot, *pfx_slot, 0x0A010203) != 1 { return 5 } 104 // 192.168.1.1 = 0xC0A80101 -- should NOT match. 105 if cidr_contains(*ip_slot, *pfx_slot, 0xC0A80101) != 0 { return 6 } 106 return 0 107}