nx_netscope_dissect_test.nx source
↩ module page · 117 lines · 5934 B
1// nx_netscope_dissect_test.nx -- 1:1 KAT for NX-NETSCOPE L4 pure
2// dissectors + canonical digest (nx_netscope_dissect.nx). Dissects
3// explicit known frame bytes and asserts every extracted field, proves
4// the digest is deterministic + field-sensitive, and proves LOUD-fail
5// (UNRESOLVED, never silent) on IPv6/short frames.
6//
7// expect_exit: 0
8// license_tier: ORIGINAL
9
10import "nx_netscope_dissect.nx"
11
12func dput(n: i64) -> i64 {
13 if n == 0 { sys_write(1, "0" as *u8, 1); return 0 }
14 var m: i64 = n
15 if m < 0 { sys_write(1, "-" as *u8, 1); m = 0 - m }
16 let d: *u8 = sys_mmap(24)
17 var k: i64 = 0
18 while m > 0 { d[k] = (0x30 + (m % 10)) as u8; m = m / 10; k = k + 1 }
19 var i: i64 = k - 1
20 while i >= 0 { let one: *u8 = sys_mmap(1); one[0] = d[i]; sys_write(1, one, 1); i = i - 1 }
21 return 0
22}
23
24// build an Ethernet+IPv4 frame skeleton (eth + 20B IPv4) into f; caller
25// fills L4. proto = IP protocol; returns the L4 offset (34).
26func build_eth_ip(f: *u8, proto: i64, total_len: i64) -> i64 {
27 f[0]=0x11 as u8; f[1]=0x22 as u8; f[2]=0x33 as u8; f[3]=0x44 as u8; f[4]=0x55 as u8; f[5]=0x66 as u8
28 f[6]=0xaa as u8; f[7]=0xbb as u8; f[8]=0xcc as u8; f[9]=0xdd as u8; f[10]=0xee as u8; f[11]=0xff as u8
29 f[12]=0x08 as u8; f[13]=0x00 as u8 // ethertype IPv4
30 f[14]=0x45 as u8; f[15]=0x00 as u8 // v4, IHL=5, TOS
31 f[16]=((total_len >> 8) & 0xff) as u8; f[17]=(total_len & 0xff) as u8
32 f[18]=0 as u8; f[19]=0 as u8; f[20]=0x40 as u8; f[21]=0 as u8
33 f[22]=0x40 as u8 // TTL
34 f[23]=(proto & 0xff) as u8 // protocol
35 f[24]=0 as u8; f[25]=0 as u8 // checksum (ignored)
36 f[26]=192 as u8; f[27]=168 as u8; f[28]=8 as u8; f[29]=10 as u8 // src 192.168.8.10
37 f[30]=192 as u8; f[31]=168 as u8; f[32]=8 as u8; f[33]=240 as u8 // dst 192.168.8.240
38 return 34
39}
40
41func main() -> i64 {
42 let d: *Dissection = sys_mmap(NXD_DISSECTION_BYTES) as *Dissection
43 let sc: *u8 = sys_mmap(2048) // SHA scratch (>=256 i64)
44 let src_ip: i64 = (192 << 24) | (168 << 16) | (8 << 8) | 10
45 let dst_ip: i64 = (192 << 24) | (168 << 16) | (8 << 8) | 240
46
47 // ---- T1: Ethernet + IPv4 + UDP/DNS frame ----
48 let f1: *u8 = sys_mmap(128)
49 let u: i64 = build_eth_ip(f1, NXD_PROTO_UDP, 33) // 20 IP + 8 UDP + 5 payload
50 f1[u]=0x12 as u8; f1[u+1]=0x34 as u8 // sport 0x1234
51 f1[u+2]=0x00 as u8; f1[u+3]=0x35 as u8 // dport 53
52 f1[u+4]=0x00 as u8; f1[u+5]=0x0d as u8 // udp len 13
53 f1[u+6]=0x00 as u8; f1[u+7]=0x00 as u8
54 f1[u+8]=0xaa as u8; f1[u+9]=0xbb as u8; f1[u+10]=0xcc as u8; f1[u+11]=0xdd as u8; f1[u+12]=0xee as u8
55 nx_packet_dissect(f1, u + 13, d)
56 if d.deepest != NXD_L_UDP { return 1 }
57 if d.l2_ethertype != 0x0800 { return 2 }
58 if d.l3_proto != 17 { return 3 }
59 if d.ip_src != src_ip { return 4 }
60 if d.ip_dst != dst_ip { return 5 }
61 if d.l4_sport != 0x1234 { return 6 }
62 if d.l4_dport != 53 { return 7 }
63 if d.payload_off != 42 { return 8 }
64 if d.payload_len != 5 { return 9 }
65 if d.unresolved != 0 { return 10 }
66 sys_write(1, "T1 eth/ipv4/udp dissect OK (sport=", 34); dput(d.l4_sport)
67 sys_write(1, " dport=", 7); dput(d.l4_dport); sys_write(1, " plen=", 6); dput(d.payload_len); sys_write(1, ")\n", 2)
68
69 // ---- T2: Ethernet + IPv4 + TCP SYN to :443 ----
70 let f2: *u8 = sys_mmap(128)
71 let t: i64 = build_eth_ip(f2, NXD_PROTO_TCP, 40) // 20 IP + 20 TCP
72 f2[t]=0xc0 as u8; f2[t+1]=0x00 as u8 // sport 49152
73 f2[t+2]=0x01 as u8; f2[t+3]=0xbb as u8 // dport 443
74 f2[t+12]=0x50 as u8 // data offset = 5*4 = 20
75 f2[t+13]=NXD_TCP_SYN as u8 // flags = SYN
76 nx_packet_dissect(f2, t + 20, d)
77 if d.deepest != NXD_L_TCP { return 11 }
78 if d.l3_proto != 6 { return 12 }
79 if d.l4_dport != 443 { return 13 }
80 if (d.tcp_flags & NXD_TCP_SYN) == 0 { return 14 }
81 if (d.tcp_flags & NXD_TCP_ACK) != 0 { return 15 } // pure SYN, no ACK
82 if d.unresolved != 0 { return 16 }
83 sys_write(1, "T2 eth/ipv4/tcp SYN dissect OK (dport=443, flags=SYN)\n", 53)
84
85 // ---- T3: canonical digest is DETERMINISTIC + field-sensitive ----
86 let dgA: *u8 = sys_mmap(64)
87 let dgB: *u8 = sys_mmap(64)
88 nx_packet_dissect(f1, u + 13, d)
89 nx_dissect_digest(d, sc, dgA)
90 nx_packet_dissect(f1, u + 13, d) // same bytes again
91 nx_dissect_digest(d, sc, dgB)
92 var i: i64 = 0
93 while i < 32 { if (dgA[i] & 0xff) != (dgB[i] & 0xff) { return 17 } i = i + 1 } // identical
94 // now flip the dport (53 -> 5353) and digest must DIFFER
95 f1[u+2]=0x14 as u8; f1[u+3]=0xe9 as u8 // 0x14e9 = 5353
96 nx_packet_dissect(f1, u + 13, d)
97 nx_dissect_digest(d, sc, dgB)
98 var same: i64 = 1
99 i = 0
100 while i < 32 { if (dgA[i] & 0xff) != (dgB[i] & 0xff) { same = 0 } i = i + 1 }
101 if same == 1 { return 18 } // a field change MUST flip the digest
102 sys_write(1, "T3 canonical digest deterministic + field-sensitive (dport flip -> digest changed)\n", 83)
103
104 // ---- T4: LOUD-fail (UNRESOLVED, never silent) on IPv6 + short frame ----
105 let f3: *u8 = sys_mmap(64)
106 let v6: i64 = build_eth_ip(f3, NXD_PROTO_TCP, 40) // overwrite ethertype below
107 f3[12]=0x86 as u8; f3[13]=0xdd as u8 // ethertype IPv6
108 nx_packet_dissect(f3, 34, d)
109 if d.deepest != NXD_L_IPV6 { return 19 }
110 if d.unresolved != 1 { return 20 } // LOUD, not a silent mis-parse
111 nx_packet_dissect(f3, 8, d) // truncated (< 14)
112 if d.unresolved != 1 { return 21 }
113 sys_write(1, "T4 LOUD-fail on IPv6 + short frame (UNRESOLVED, not silent)\n", 59)
114
115 sys_write(1, "NETSCOPE L4 KAT PASS (eth/ipv4/tcp/udp pure dissect + canonical digest + LOUD unresolved)\n", 89)
116 return 0
117}