code wiki / _hdl_build / nx_netscope_capture.nx
nx_netscope_capture.nx source
↩ module page · 80 lines · 4199 B
1// nx_netscope_capture.nx -- NX-NETSCOPE L-CAPTURE: the SOVEREIGN raw-wire capture front-end (the
2// "wireshark" tap, in-ecosystem, NO tcpdump). Opens an AF_PACKET raw socket, reads frames, and feeds
3// each to the EXISTING pure dissector nx_packet_dissect (nx_netscope_dissect.nx). Filters inbound TCP
4// SYN (no ACK) to a chosen dest port and prints src-ip -> dst-ip:port -- so we can SEE which NAS
5// interface/IP a public :443 connection actually lands on (answering "where does external ingress go"
6// with a capture, not a guess). Bounded: per-recv timeout + wall-clock deadline, so it NEVER hangs.
7// usage: nx_netscope_capture [dport=443] [seconds=90] [maxrows=200]
8// Needs CAP_NET_RAW (run as root). license_tier: ORIGINAL
9import "nx_syscalls.nx"
10import "nx_netscope_dissect.nx"
11const NXC_MAGIC_65536: i64 = 65536
12
13const NXC_AF_PACKET: i64 = 17
14const NXC_SOCK_RAW: i64 = 3
15const NXC_ETH_P_ALL_BE: i64 = 768 // htons(0x0003) = 0x0300
16
17func c_puts(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
18func c_n(v: i64) -> i64 { let b: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0-m;sys_write(1,"-" as *u8,1)} let t: *u8=sys_mmap(28); var k: i64=0; if m==0{t[0]=48 as u8;k=1} while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1} var i: i64=0; while i<k{b[i]=t[k-1-i];i=i+1} sys_write(1,b,k); return 0 }
19func c_slen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n }
20func c_atoi(s: *u8) -> i64 { var v: i64=0; var i: i64=0; while s[i]!=(0 as u8){ let c: i64=s[i] as i64; if c>=48 { if c<=57 { v=v*10+(c-48) } } i=i+1 } return v }
21// packed-BE ipv4 -> a.b.c.d
22func c_ip(p: i64) -> i64 {
23 c_n((p>>24)&0xff); c_puts("." as *u8); c_n((p>>16)&0xff); c_puts("." as *u8)
24 c_n((p>>8)&0xff); c_puts("." as *u8); c_n(p&0xff); return 0
25}
26
27func main(argc: i64, argv: *i64) -> i64 {
28 var dport: i64 = 443
29 var secs: i64 = 90
30 var maxrows: i64 = 200
31 if argc > 1 { dport = c_atoi(argv[1] as *u8) }
32 if argc > 2 { secs = c_atoi(argv[2] as *u8) }
33 if argc > 3 { maxrows = c_atoi(argv[3] as *u8) }
34
35 c_puts("=== nx_netscope_capture: sovereign wire tap for inbound TCP SYN -> :" as *u8); c_n(dport)
36 c_puts(" window=" as *u8); c_n(secs); c_puts("s ===\n" as *u8)
37
38 let fd: i64 = sys_socket(NXC_AF_PACKET, NXC_SOCK_RAW, NXC_ETH_P_ALL_BE)
39 if fd < 0 { c_puts("SOCKET-FAIL rc=" as *u8); c_n(fd); c_puts(" (need root / CAP_NET_RAW)\n" as *u8); return 1 }
40 sys_set_socket_timeout(fd, 3) // 3s per-recv, so we loop + honor the wall-clock deadline (never hang)
41
42 let buf: *u8 = sys_mmap(NXC_MAGIC_65536)
43 let d: *Dissection = sys_mmap(NXD_DISSECTION_BYTES) as *Dissection
44 let start: i64 = sys_now_realtime_sec()
45 let deadline: i64 = start + secs
46 var frames: i64 = 0
47 var rows: i64 = 0
48 var run: i64 = 1
49 while run == 1 {
50 if sys_now_realtime_sec() >= deadline { run = 0 }
51 if rows >= maxrows { run = 0 }
52 if run == 1 {
53 let n: i64 = sys_recvfrom(fd, buf, NXC_MAGIC_65536, 0, 0 as *u8, 0 as *i64)
54 if n > 0 {
55 frames = frames + 1
56 nx_packet_dissect(buf, n, d)
57 if d.deepest == NXD_L_TCP {
58 if d.l4_dport == dport {
59 var syn: i64 = 0
60 if (d.tcp_flags & NXD_TCP_SYN) != 0 { syn = 1 }
61 var ack: i64 = 0
62 if (d.tcp_flags & NXD_TCP_ACK) != 0 { ack = 1 }
63 if syn == 1 { if ack == 0 {
64 c_puts(" SYN src " as *u8); c_ip(d.ip_src)
65 c_puts(":" as *u8); c_n(d.l4_sport)
66 c_puts(" -> dst " as *u8); c_ip(d.ip_dst)
67 c_puts(":" as *u8); c_n(d.l4_dport); c_puts("\n" as *u8)
68 rows = rows + 1
69 } }
70 }
71 }
72 }
73 }
74 }
75 sys_close(fd)
76 c_puts("--- capture done: frames_seen=" as *u8); c_n(frames)
77 c_puts(" inbound_syn_to_:" as *u8); c_n(dport); c_puts("=" as *u8); c_n(rows)
78 c_puts(" (the dst IP is the NAS interface the connection landed on)\n" as *u8)
79 return 0
80}