code wiki / _hdl_build / nx_portdup.nx

nx_portdup.nx source

↩ module page · 242 lines · 10911 B

1// nx_portdup.nx -- detect the WRONG-ANSWER MACHINE: two daemons LISTENING on one port (seq1785450532). 2// 3// WHAT IT CATCHES, and why nothing else did. On 2026-07-30 both nx_mgmt_api AND nx_opaque_login were 4// LISTENING on 127.0.0.1:18098 at the same time. The kernel hands each connection to whichever accepts 5// first, so a VALID control-plane route answered `404 no such route` on some requests and worked on 6// others. Every component was individually healthy -- correct binary, correct source, listener present, 7// health OK -- so the fault was filed for MONTHS as a "transport flake" and blamed on the edge. It was 8// two listeners. The banked fd-scrub note predicted the successor would fail to BIND (EADDRINUSE); the 9// reality is worse, because BOTH hold the socket and there is NO ERROR ANYWHERE to find. 10// 11// WHY A DEDICATED TOOTH: liveness ("is it up?") and health ("does it answer?") are both TRUE during this 12// fault -- the squatter answers, just wrongly. Only the SHAPE of the listen table shows it, and nothing 13// in the ecosystem looked at that shape. 14// 15// FALSE-POSITIVE DISCIPLINE (this is why it is a watch list, not a sweep of every port): DSM legitimately 16// runs SO_REUSEPORT worker pools -- :443, :5000, :5001 each show EIGHT holders by design, and the sovereign 17// edge deliberately co-binds :443 alongside DSM's nginx (a known, accepted arrangement). Flagging those 18// would train the operator to ignore this tooth, so it reports ONLY on ports declared as 19// single-owner in knowledge/portdup_watch.conf. A port not on the list is never judged. 20// 21// nx_portdup check [watchconf] -- exit 1 if any WATCHED port has more than one LISTEN holder 22// nx_portdup list [watchconf] -- holder count for every watched port (no verdict) 23// license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0 24import "nx_syscalls.nx" 25 26const PD_NETCAP: i64 = 2097152 27const PD_CONFCAP: i64 = 65536 28const PD_MAXPORT: i64 = 512 29const PD_NL: i64 = 10 30const PD_HASH: i64 = 35 31const PD_COLON: i64 = 58 32const PD_SPACE: i64 = 32 33const PD_TAB: i64 = 9 34const PD_STDOUT: i64 = 1 35const PD_STDERR: i64 = 2 36const PD_LISTEN_HI: i64 = 48 // '0' of the "0A" state field 37const PD_LISTEN_LO: i64 = 65 // 'A' 38const PD_EXIT_DUP: i64 = 1 39const PD_EXIT_USAGE: i64 = 2 40const PD_EXIT_REFUSED: i64 = 3 41const PD_DEF_CONF: *u8 = "knowledge/portdup_watch.conf" as *u8 42const PD_TCP4: *u8 = "/proc/net/tcp" as *u8 43const PD_TCP6: *u8 = "/proc/net/tcp6" as *u8 44 45func pd_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 46func pd_puts(s: *u8) -> i64 { sys_write(PD_STDOUT, s, pd_slen(s)); return 0 } 47func pd_werr(s: *u8) -> i64 { sys_write(PD_STDERR, s, pd_slen(s)); return 0 } 48func pd_putn(v: i64) -> i64 { 49 let t: *u8 = sys_mmap(32); var m: i64 = v 50 if m < 0 { m = 0 - m; sys_write(PD_STDOUT, "-" as *u8, 1) } 51 var k: i64 = 0 52 if m == 0 { t[0] = 48 as u8; k = 1 } 53 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 54 let b: *u8 = sys_mmap(32); var i: i64 = 0 55 while i < k { b[i] = t[k-1-i]; i = i + 1 } 56 sys_write(PD_STDOUT, b, k) 57 return 0 58} 59func pd_eqs(a: *u8, b: *u8) -> i64 { 60 var i: i64 = 0 61 while a[i] != (0 as u8) { if a[i] != b[i] { return 0 } i = i + 1 } 62 if b[i] != (0 as u8) { return 0 } 63 return 1 64} 65func pd_read(path: *u8, b: *u8, cap: i64) -> i64 { 66 let fd: i64 = sys_openat_rd(path) 67 if fd < 0 { return 0 - 1 } 68 var n: i64 = 0 69 var go: i64 = 1 70 while go == 1 { 71 let r: i64 = sys_read(fd, (b as i64 + n) as *u8, cap - n) 72 if r > 0 { n = n + r } else { go = 0 } 73 if n >= cap { go = 0 } 74 } 75 sys_close(fd) 76 return n 77} 78func pd_hexval(c: i64) -> i64 { 79 if c >= 48 { if c <= 57 { return c - 48 } } 80 if c >= 65 { if c <= 70 { return c - 55 } } 81 if c >= 97 { if c <= 102 { return c - 87 } } 82 return 0 - 1 83} 84 85// Count LISTEN sockets per port in a /proc/net/tcp-style table, accumulating into cnt[] indexed by the 86// caller's port list. Format: " sl local_address rem_address st ...", local_address = HEXIP:HEXPORT, 87// st == "0A" is LISTEN. Parsed positionally by whitespace tokens so it does not care about column widths. 88func pd_scan(buf: *u8, n: i64, ports: *i64, nports: i64, cnt: *i64) -> i64 { 89 var rows: i64 = 0 90 var ls: i64 = 0 91 var i: i64 = 0 92 var line: i64 = 0 93 while i <= n { 94 var eol: i64 = 0 95 if i == n { eol = 1 } else { if buf[i] == (PD_NL as u8) { eol = 1 } } 96 if eol == 1 { 97 if line > 0 { if i > ls { 98 // token 1 = sl, token 2 = local_address, token 4 = st 99 // Whitespace tokenizer, written as explicit skip-then-take with single exit flags. The 100 // first draft folded the two scans together with index arithmetic and was simply wrong; 101 // this function decides whether a port is judged at all, so it is written to be read. 102 var tok: i64 = 0 103 var p: i64 = ls 104 var lport: i64 = 0 - 1 105 var isl: i64 = 0 106 var scan: i64 = 1 107 while scan == 1 { 108 var sgo: i64 = 1 109 while sgo == 1 { 110 if p < i { if buf[p] == (PD_SPACE as u8) { p = p + 1 } else { sgo = 0 } } else { sgo = 0 } 111 } 112 if p >= i { scan = 0 } else { 113 let st2: i64 = p 114 var ego: i64 = 1 115 while ego == 1 { 116 if p < i { if buf[p] != (PD_SPACE as u8) { p = p + 1 } else { ego = 0 } } else { ego = 0 } 117 } 118 let e: i64 = p 119 if e > st2 { 120 tok = tok + 1 121 if tok == 2 { 122 // local_address: find ':' then parse hex port 123 var c: i64 = st2 124 var cp: i64 = 0 - 1 125 while c < e { if buf[c] == (PD_COLON as u8) { cp = c; c = e } else { c = c + 1 } } 126 if cp > 0 { 127 var v: i64 = 0 128 var h: i64 = cp + 1 129 var ok: i64 = 1 130 while h < e { 131 let d: i64 = pd_hexval(buf[h] as i64) 132 if d < 0 { ok = 0; h = e } else { v = v * 16 + d; h = h + 1 } 133 } 134 if ok == 1 { lport = v } 135 } 136 } 137 if tok == 4 { 138 if e - st2 == 2 { 139 if buf[st2] == (PD_LISTEN_HI as u8) { if buf[st2+1] == (PD_LISTEN_LO as u8) { isl = 1 } } 140 } 141 } 142 } 143 } 144 } 145 if isl == 1 { if lport >= 0 { 146 rows = rows + 1 147 var q: i64 = 0 148 while q < nports { if ports[q] == lport { cnt[q] = cnt[q] + 1; q = nports } else { q = q + 1 } } 149 } } 150 } } 151 line = line + 1 152 ls = i + 1 153 } 154 i = i + 1 155 } 156 return rows 157} 158 159func main(argc: i64, argv: *i64) -> i64 { 160 if argc < 2 { pd_werr("usage: nx_portdup check|list [watchconf]\n" as *u8); sys_exit(PD_EXIT_USAGE); return PD_EXIT_USAGE } 161 let verb: *u8 = argv[1] as *u8 162 var confp: *u8 = PD_DEF_CONF 163 if argc >= 3 { confp = argv[2] as *u8 } 164 165 let conf: *u8 = sys_mmap(PD_CONFCAP) 166 let cn: i64 = pd_read(confp, conf, PD_CONFCAP - 1) 167 if cn <= 0 { 168 pd_werr("REFUSED: watch list absent/empty: " as *u8); pd_werr(confp); pd_werr("\n" as *u8) 169 sys_exit(PD_EXIT_REFUSED); return PD_EXIT_REFUSED 170 } 171 // conf rows: "<port> <name>"; '#' comments. Only DECLARED single-owner ports are ever judged. 172 let ports: *i64 = sys_mmap(8 * PD_MAXPORT) as *i64 173 let cnt: *i64 = sys_mmap(8 * PD_MAXPORT) as *i64 174 let nameoff: *i64 = sys_mmap(8 * PD_MAXPORT) as *i64 175 var nports: i64 = 0 176 var ls: i64 = 0 177 var i: i64 = 0 178 while i <= cn { 179 var eol: i64 = 0 180 if i == cn { eol = 1 } else { if conf[i] == (PD_NL as u8) { eol = 1 } } 181 if eol == 1 { 182 if i > ls { if conf[ls] != (PD_HASH as u8) { 183 var v: i64 = 0 184 var got: i64 = 0 185 var p: i64 = ls 186 while p < i { 187 let c: i64 = conf[p] as i64 188 if c >= 48 { if c <= 57 { v = v * 10 + (c - 48); got = 1; p = p + 1 } else { p = i } } else { p = i } 189 } 190 if got == 1 { if nports < PD_MAXPORT { 191 ports[nports] = v; cnt[nports] = 0; nameoff[nports] = ls; nports = nports + 1 192 } } 193 } } 194 ls = i + 1 195 } 196 i = i + 1 197 } 198 if nports == 0 { 199 pd_werr("REFUSED: no ports declared in the watch list\n" as *u8) 200 sys_exit(PD_EXIT_REFUSED); return PD_EXIT_REFUSED 201 } 202 203 let nb: *u8 = sys_mmap(PD_NETCAP) 204 var scanned: i64 = 0 205 let n4: i64 = pd_read(PD_TCP4, nb, PD_NETCAP - 1) 206 if n4 > 0 { scanned = scanned + pd_scan(nb, n4, ports, nports, cnt) } 207 let n6: i64 = pd_read(PD_TCP6, nb, PD_NETCAP - 1) 208 if n6 > 0 { scanned = scanned + pd_scan(nb, n6, ports, nports, cnt) } 209 if n4 <= 0 { if n6 <= 0 { 210 // FAIL-CLOSED: an unreadable listen table means the tooth CANNOT answer, and a tooth that 211 // silently answers GREEN when it cannot see is worse than no tooth. 212 pd_werr("REFUSED: cannot read /proc/net/tcp or tcp6 -- UNMEASURED, not clean\n" as *u8) 213 sys_exit(PD_EXIT_REFUSED); return PD_EXIT_REFUSED 214 } } 215 216 var dups: i64 = 0 217 var missing: i64 = 0 218 var k: i64 = 0 219 while k < nports { 220 if sd_is_list(verb) == 1 { 221 pd_puts(" port " as *u8); pd_putn(ports[k]) 222 pd_puts(" holders=" as *u8); pd_putn(cnt[k]); pd_puts("\n" as *u8) 223 } 224 if cnt[k] > 1 { 225 pd_puts("PORT-DUP " as *u8); pd_putn(ports[k]) 226 pd_puts(" has " as *u8); pd_putn(cnt[k]) 227 pd_puts(" LISTEN holders -- connections are split between them, so a VALID request can get a WRONG answer from whichever accepts first (seq1785450532)\n" as *u8) 228 dups = dups + 1 229 } 230 if cnt[k] == 0 { missing = missing + 1 } 231 k = k + 1 232 } 233 pd_puts("PORTDUP watched=" as *u8); pd_putn(nports) 234 pd_puts(" listen_rows=" as *u8); pd_putn(scanned) 235 pd_puts(" dup=" as *u8); pd_putn(dups) 236 pd_puts(" unbound=" as *u8); pd_putn(missing) 237 pd_puts("\n" as *u8) 238 if dups > 0 { sys_exit(PD_EXIT_DUP); return PD_EXIT_DUP } 239 sys_exit(0) 240 return 0 241} 242func sd_is_list(v: *u8) -> i64 { if pd_eqs(v, "list" as *u8) == 1 { return 1 } return 0 }