code wiki / (root) / nx_netscope_dissect_test.nx

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}