code wiki / _hdl_build / nx_lan_scan.nx
nx_lan_scan.nx
buildroot/runtime/_hdl_build/nx_lan_scan.nx
about
nx_lan_scan.nx -- shared sovereign LAN sweep primitive (no main).
WHY THIS EXISTS (rule 15, DRY through shared libraries): two organs now ask
the SAME question -- "is host H open on port P?" -- nx_printer_ctl (printer
discovery/survey) and nx_iot_ctl (home-electronics inventory). If each keeps
its own copy of the sweep, one gets fixed and the other silently does not,
and the two tools then DISAGREE about the same LAN while both look healthy.
The attribution rule they must agree on is the reason to share, not the
line count.
The implementation is lifted VERBATIM from the proven, gate-green
pctl_discover_scan / pctl_probe_host (nx_printer_ctl_lib), so behaviour is
identical BY CONSTRUCTION rather than by re-derivation.
SCALE LAW (learned the hard way, banked in the iot-printer-fabric memory):
any organ that fans out network I/O over MCP must bound total latency to the
tools-daemon fork-capture window, or the call returns status=0 with no output
while working fine on the CLI. Three rules, all encoded below:
1. BATCHED -- fire all 254 non-blocking connects first, then poll.
2. POLL-UNTIL-RESOLVED -- a single poll() returns as soon as ANY fd is
ready (the ~250 instantly-refused hosts) BEFORE a real host finishes its
handshake, so it finds NOTHING. Mark each fd done as its revents fire
(write 0xff over the pollfd fd bytes so poll ignores it next round) and
keep polling the still-connecting ones.
3. TIGHT BUDGET -- responsive devices answer in <50ms; unreachable hosts
must not be waited on.
Open iff POLLOUT && !POLLERR && !POLLHUP.
license_tier: ORIGINAL No hw writes (Rule 26).
dependencies 2 imports · 3 importers
imports: nx_syscalls.nxnx_connect.nx
imported by: nx_iot_ctl_gate.nxnx_iot_ctl_lib.nxnx_printer_ctl_lib.nx
structs
| none |
consts
| 33 | const LSCAN_MAGIC_65535: i64 = 65535 |
| 35 | const LSCAN_SOCK_NONBLOCK: i64 = 2048 // SOCK_NONBLOCK |
| 36 | const LSCAN_POLLOUT: i64 = 4 |
| 37 | const LSCAN_POLLERR: i64 = 8 |
| 38 | const LSCAN_POLLHUP: i64 = 16 |
| 39 | const LSCAN_HOSTS: i64 = 254 // .1 .. .254 of a /24 |
| 40 | const LSCAN_ROUNDS: i64 = 12 // poll-until-resolved rounds |
| 41 | const LSCAN_MIN_PER_MS: i64 = 5 // floor on the per-round poll slice |
| 55 | const LSCAN_BUDGET_MS: i64 = 120 |
functions
| 59 | func lscan_parse_prefix(s: *u8) -> i64 |
| 98 | func lscan_parse_ip(s: *u8) -> i64 called by 1: main |
| 136 | func lscan_parse_port(s: *u8) -> i64 called by 1: main |
| 159 | func lscan_fill_sockaddr(addr: *u8, ipv4: i64, port: i64) -> i64 |
| 176 | func lscan_probe_host(ipv4: i64, port: i64, timeout_ms: i64) -> i64 called by 1: pctl_probe_host calls 6: sys_socketsys_mmaplscan_fill_sockaddrnx_connect_boundedsys_pollsys_close |
| 212 | func lscan_sweep(base24: i64, port: i64, timeout_ms: i64, out_ips: *i64, max: i64) -> i64 |