code wiki / (root) / cidr.nx

cidr.nx source

↩ module page · 101 lines · 3511 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 18import "syscalls.nx" 19import "ipaddr.nx" 20 21const CIDR_ERR_FORMAT: i64 = -1 22const CIDR_ERR_BAD_PREFIX: i64 = -2 23 24// Parse "a.b.c.d/N" into ip (u32) + prefix. Writes to *ip_out, 25// *prefix_out. Returns 0 on success, negative CIDR_ERR_* on error. 26func cidr_parse(s: *u8, n: i64, ip_out: *i64, prefix_out: *i64) -> i64 { 27 // Find '/'. 28 var slash: i64 = 0 - 1 29 var i: i64 = 0 30 while i < n { 31 if s[i] == 0x2F { slash = i; i = n } 32 else { i = i + 1 } 33 } 34 if slash < 0 { return CIDR_ERR_FORMAT } 35 36 // Parse IP portion. 37 let ip_val: i64 = ipv4_parse(s, slash) 38 if ip_val < 0 { return ip_val } 39 40 // Parse prefix: decimal integer after '/'. 41 var prefix: i64 = 0 42 var j: i64 = slash + 1 43 var saw_digit: i64 = 0 44 while j < n { 45 let c: i64 = s[j] 46 if c < 0x30 { return CIDR_ERR_BAD_PREFIX } 47 if c > 0x39 { return CIDR_ERR_BAD_PREFIX } 48 prefix = prefix * 10 + (c - 0x30) 49 saw_digit = 1 50 j = j + 1 51 } 52 if saw_digit == 0 { return CIDR_ERR_BAD_PREFIX } 53 if prefix < 0 { return CIDR_ERR_BAD_PREFIX } 54 if prefix > 32 { return CIDR_ERR_BAD_PREFIX } 55 56 *ip_out = ip_val & 0xFFFFFFFF 57 *prefix_out = prefix 58 return 0 59} 60 61// Compute the bitmask for an IPv4 prefix length. 62// prefix=0 -> 0x00000000; prefix=24 -> 0xFFFFFF00; prefix=32 -> 0xFFFFFFFF. 63func cidr_mask(prefix: i64) -> i64 { 64 if prefix == 0 { return 0 } 65 if prefix >= 32 { return 0xFFFFFFFF } 66 let shift: i64 = 32 - prefix 67 let ones: i64 = (1 << 32) - 1 68 return (ones << shift) & 0xFFFFFFFF 69} 70 71// Canonicalise: clear host bits, return the network address. 72func cidr_network_address(ip: i64, prefix: i64) -> i64 { 73 return (ip & cidr_mask(prefix)) & 0xFFFFFFFF 74} 75 76// Does `addr` fall within the `network/prefix` CIDR block? 77func cidr_contains(network: i64, prefix: i64, addr: i64) -> i64 { 78 let m: i64 = cidr_mask(prefix) 79 if (network & m) == (addr & m) { return 1 } 80 return 0 81} 82 83// Compile-only smoke: parse "10.0.0.0/8", verify containment of 84// 10.1.2.3 and rejection of 192.168.1.1. 85func main() -> i64 { 86 let ip_slot: *i64 = sys_mmap(16) as *i64 87 let pfx_slot: *i64 = sys_mmap(16) as *i64 88 let rc: i64 = cidr_parse("10.0.0.0/8", 10, ip_slot, pfx_slot) 89 if rc != 0 { return 1 } 90 if *pfx_slot != 8 { return 2 } 91 if *ip_slot != 0x0A000000 { return 3 } 92 93 let m: i64 = cidr_mask(8) 94 if m != 0xFF000000 { return 4 } 95 96 // 10.1.2.3 = 0x0A010203 -- should match 10.0.0.0/8. 97 if cidr_contains(*ip_slot, *pfx_slot, 0x0A010203) != 1 { return 5 } 98 // 192.168.1.1 = 0xC0A80101 -- should NOT match. 99 if cidr_contains(*ip_slot, *pfx_slot, 0xC0A80101) != 0 { return 6 } 100 return 0 101}