import assert from "node:assert/strict" import { applicationName } from "./traffic-flow-apps.js" import { parseFlowPacket, protoName, resetFlowTemplatesForTests, templateExporterCountForTests } from "./traffic-flow-parse.js" import { allocateOverlayAddress, FLOW_CACHE_ENTRIES, FLOW_TARGET_SRC_AUTO, usablePublicHost } from "./traffic-flow-overlay.js" function netflowV5One(): Buffer { const buf = Buffer.alloc(24 + 48) buf.writeUInt16BE(5, 0) buf.writeUInt16BE(1, 2) buf[24] = 10; buf[25] = 1; buf[26] = 1; buf[27] = 8 buf[28] = 8; buf[29] = 8; buf[30] = 8; buf[31] = 8 buf.writeUInt16BE(1, 24 + 12) buf.writeUInt32BE(10, 24 + 16) buf.writeUInt32BE(1500, 24 + 20) buf.writeUInt16BE(443, 24 + 32) buf.writeUInt16BE(443, 24 + 34) buf.writeUInt8(6, 24 + 38) return buf } resetFlowTemplatesForTests() const flows = parseFlowPacket(netflowV5One(), "10.255.254.5") assert.equal(flows.length, 1) assert.equal(flows[0]?.src, "10.1.1.8") assert.equal(flows[0]?.dst, "8.8.8.8") assert.equal(flows[0]?.proto, 6) assert.equal(flows[0]?.bytes, 1500) assert.equal(flows[0]?.inIface, "1") assert.equal(protoName(6), "TCP") assert.equal(protoName(58), "ICMPv6") assert.equal(applicationName(58, 0), "ICMPv6") assert.equal(applicationName(1, 0), "ICMP") assert.equal(protoName(255), "255") assert.equal(parseFlowPacket(Buffer.from([0, 1]), "1.1.1.1").length, 0) const taken = new Set(["10.255.254.2"]) assert.equal(allocateOverlayAddress("10.255.254.0/24", "10.255.254.1", 1, taken), "10.255.254.3") assert.equal(allocateOverlayAddress("10.255.254.0/24", "10.255.254.1", 2, new Set()), "10.255.254.3") assert.equal(usablePublicHost("localhost:8000"), "") assert.equal(usablePublicHost("127.0.0.1"), "") assert.equal(usablePublicHost("192.168.1.10"), "") assert.equal(usablePublicHost("mm.example.com:443"), "mm.example.com") assert.equal(usablePublicHost("203.0.113.10"), "203.0.113.10") assert.equal(FLOW_TARGET_SRC_AUTO, "0.0.0.0") assert.equal(FLOW_CACHE_ENTRIES, "256k") resetFlowTemplatesForTests() { const tpl = Buffer.alloc(16 + 16 + 20) tpl.writeUInt16BE(10, 0) tpl.writeUInt16BE(tpl.length, 2) tpl.writeUInt16BE(2, 16) tpl.writeUInt16BE(16, 18) tpl.writeUInt16BE(256, 20) tpl.writeUInt16BE(2, 22) tpl.writeUInt16BE(8, 24) tpl.writeUInt16BE(4, 26) tpl.writeUInt16BE(12, 28) tpl.writeUInt16BE(4, 30) const data = Buffer.alloc(16 + 12) data.writeUInt16BE(10, 0) data.writeUInt16BE(data.length, 2) data.writeUInt16BE(256, 16) data.writeUInt16BE(12, 18) data[20] = 10; data[21] = 1; data[22] = 1; data[23] = 8 data[24] = 8; data[25] = 8; data[26] = 8; data[27] = 8 const fromTpl = parseFlowPacket(tpl, "172.18.0.2") assert.equal(fromTpl.length, 0) const fromData = parseFlowPacket(data, "172.18.0.2") assert.equal(fromData.length, 1) assert.equal(fromData[0]?.src, "10.1.1.8") assert.equal(fromData[0]?.dst, "8.8.8.8") } resetFlowTemplatesForTests() { const tpl = Buffer.alloc(16 + 24) tpl.writeUInt16BE(10, 0) tpl.writeUInt16BE(tpl.length, 2) tpl.writeUInt16BE(2, 16) tpl.writeUInt16BE(24, 18) tpl.writeUInt16BE(256, 20) tpl.writeUInt16BE(4, 22) tpl.writeUInt16BE(8, 24) tpl.writeUInt16BE(4, 26) tpl.writeUInt16BE(12, 28) tpl.writeUInt16BE(4, 30) tpl.writeUInt16BE(10, 32) tpl.writeUInt16BE(4, 34) tpl.writeUInt16BE(82, 36) tpl.writeUInt16BE(6, 38) const data = Buffer.alloc(16 + 22) data.writeUInt16BE(10, 0) data.writeUInt16BE(data.length, 2) data.writeUInt16BE(256, 16) data.writeUInt16BE(22, 18) data[20] = 10; data[21] = 1; data[22] = 1; data[23] = 8 data[24] = 8; data[25] = 8; data[26] = 8; data[27] = 8 data.writeUInt32BE(13, 28) data.write("ether1", 32) parseFlowPacket(tpl, "10.255.254.3") const named = parseFlowPacket(data, "10.255.254.3") assert.equal(named.length, 1) assert.equal(named[0]?.inIface, "13") assert.equal(named[0]?.src, "10.1.1.8") } resetFlowTemplatesForTests() { const tpl = Buffer.alloc(16 + 20) tpl.writeUInt16BE(10, 0) tpl.writeUInt16BE(tpl.length, 2) tpl.writeUInt16BE(2, 16) tpl.writeUInt16BE(20, 18) tpl.writeUInt16BE(256, 20) tpl.writeUInt16BE(3, 22) tpl.writeUInt16BE(8, 24) tpl.writeUInt16BE(4, 26) tpl.writeUInt16BE(12, 28) tpl.writeUInt16BE(4, 30) tpl.writeUInt16BE(82, 32) tpl.writeUInt16BE(6, 34) const data = Buffer.alloc(16 + 18) data.writeUInt16BE(10, 0) data.writeUInt16BE(data.length, 2) data.writeUInt16BE(256, 16) data.writeUInt16BE(18, 18) data[20] = 10; data[21] = 1; data[22] = 1; data[23] = 8 data[24] = 8; data[25] = 8; data[26] = 8; data[27] = 8 data.write("ether1", 28) parseFlowPacket(tpl, "10.255.254.4") const namedOnly = parseFlowPacket(data, "10.255.254.4") assert.equal(namedOnly[0]?.inIface, "ether1") } resetFlowTemplatesForTests() { const fieldSpecs: Array<[number, number]> = [ [8, 4], [12, 4], [10, 4], [14, 4], [15, 4], [152, 8], [153, 8], [1, 4], ] const tplSetLen = 4 + 4 + fieldSpecs.length * 4 const tpl = Buffer.alloc(16 + tplSetLen) tpl.writeUInt16BE(10, 0) tpl.writeUInt16BE(tpl.length, 2) tpl.writeUInt16BE(2, 16) tpl.writeUInt16BE(tplSetLen, 18) tpl.writeUInt16BE(256, 20) tpl.writeUInt16BE(fieldSpecs.length, 22) let off = 24 for (const [type, len] of fieldSpecs) { tpl.writeUInt16BE(type, off) tpl.writeUInt16BE(len, off + 2) off += 4 } const recLen = fieldSpecs.reduce((n, [, len]) => n + len, 0) const data = Buffer.alloc(16 + 4 + recLen) data.writeUInt16BE(10, 0) data.writeUInt16BE(data.length, 2) data.writeUInt16BE(256, 16) data.writeUInt16BE(4 + recLen, 18) let d = 20 data[d] = 10; data[d + 1] = 100; data[d + 2] = 1; data[d + 3] = 17; d += 4 data[d] = 173; data[d + 1] = 194; data[d + 2] = 160; data[d + 3] = 163; d += 4 data.writeUInt32BE(13, d); d += 4 data.writeUInt32BE(42, d); d += 4 data[d] = 198; data[d + 1] = 51; data[d + 2] = 100; data[d + 3] = 1; d += 4 data.writeBigUInt64BE(1_700_000_000_000n, d); d += 8 data.writeBigUInt64BE(1_700_000_060_000n, d); d += 8 data.writeUInt32BE(1500, d) parseFlowPacket(tpl, "10.255.254.5") const extra = parseFlowPacket(data, "10.255.254.5") assert.equal(extra.length, 1) assert.equal(extra[0]?.src, "10.100.1.17") assert.equal(extra[0]?.dst, "173.194.160.163") assert.equal(extra[0]?.inIface, "13") assert.equal(extra[0]?.outIface, "42") assert.equal(extra[0]?.nextHop, "198.51.100.1") assert.equal(extra[0]?.flowStartMs, 1_700_000_000_000) assert.equal(extra[0]?.flowEndMs, 1_700_000_060_000) assert.equal(extra[0]?.bytes, 1500) } resetFlowTemplatesForTests() { const fieldSpecs: Array<[number, number]> = [ [8, 4], [12, 4], [225, 4], [226, 4], [227, 2], [228, 2], [1, 4], ] const tplSetLen = 4 + 4 + fieldSpecs.length * 4 const tpl = Buffer.alloc(16 + tplSetLen) tpl.writeUInt16BE(10, 0) tpl.writeUInt16BE(tpl.length, 2) tpl.writeUInt16BE(2, 16) tpl.writeUInt16BE(tplSetLen, 18) tpl.writeUInt16BE(256, 20) tpl.writeUInt16BE(fieldSpecs.length, 22) let off = 24 for (const [type, len] of fieldSpecs) { tpl.writeUInt16BE(type, off) tpl.writeUInt16BE(len, off + 2) off += 4 } const recLen = fieldSpecs.reduce((n, [, len]) => n + len, 0) const data = Buffer.alloc(16 + 4 + recLen) data.writeUInt16BE(10, 0) data.writeUInt16BE(data.length, 2) data.writeUInt16BE(256, 16) data.writeUInt16BE(4 + recLen, 18) let d = 20 data[d] = 10; data[d + 1] = 200; data[d + 2] = 100; data[d + 3] = 53; d += 4 data[d] = 10; data[d + 1] = 200; data[d + 2] = 100; data[d + 3] = 1; d += 4 data[d] = 0; data[d + 1] = 0; data[d + 2] = 0; data[d + 3] = 0; d += 4 data[d] = 8; data[d + 1] = 8; data[d + 2] = 8; data[d + 3] = 8; d += 4 data.writeUInt16BE(53880, d); d += 2 data.writeUInt16BE(443, d); d += 2 data.writeUInt32BE(900, d) parseFlowPacket(tpl, "10.255.254.9") const nat = parseFlowPacket(data, "10.255.254.9") assert.equal(nat.length, 1) assert.equal(nat[0]?.src, "10.200.100.53") assert.equal(nat[0]?.dst, "10.200.100.1") assert.equal(nat[0]?.natSrc, "0.0.0.0") assert.equal(nat[0]?.natDst, "8.8.8.8") assert.equal(nat[0]?.natSrcPort, 53880) assert.equal(nat[0]?.natDstPort, 443) assert.equal(nat[0]?.bytes, 900) } resetFlowTemplatesForTests() { const fieldSpecs: Array<[number, number]> = [ [225, 4], [12, 4], [1, 4], ] const tplSetLen = 4 + 4 + fieldSpecs.length * 4 const tpl = Buffer.alloc(16 + tplSetLen) tpl.writeUInt16BE(10, 0) tpl.writeUInt16BE(tpl.length, 2) tpl.writeUInt16BE(2, 16) tpl.writeUInt16BE(tplSetLen, 18) tpl.writeUInt16BE(256, 20) tpl.writeUInt16BE(fieldSpecs.length, 22) let off = 24 for (const [type, len] of fieldSpecs) { tpl.writeUInt16BE(type, off) tpl.writeUInt16BE(len, off + 2) off += 4 } const recLen = fieldSpecs.reduce((n, [, len]) => n + len, 0) const data = Buffer.alloc(16 + 4 + recLen) data.writeUInt16BE(10, 0) data.writeUInt16BE(data.length, 2) data.writeUInt16BE(256, 16) data.writeUInt16BE(4 + recLen, 18) let d = 20 data[d] = 0; data[d + 1] = 0; data[d + 2] = 0; data[d + 3] = 0; d += 4 data[d] = 8; data[d + 1] = 8; data[d + 2] = 8; data[d + 3] = 8; d += 4 data.writeUInt32BE(10, d) parseFlowPacket(tpl, "10.255.254.10") const zeroNat = parseFlowPacket(data, "10.255.254.10") assert.equal(zeroNat[0]?.src, "") assert.equal(zeroNat[0]?.natSrc, "0.0.0.0") assert.equal(zeroNat[0]?.dst, "8.8.8.8") } resetFlowTemplatesForTests() { const tpl = Buffer.alloc(16 + 16 + 20) tpl.writeUInt16BE(10, 0) tpl.writeUInt16BE(tpl.length, 2) tpl.writeUInt16BE(2, 16) tpl.writeUInt16BE(16, 18) tpl.writeUInt16BE(256, 20) tpl.writeUInt16BE(2, 22) tpl.writeUInt16BE(8, 24) tpl.writeUInt16BE(4, 26) tpl.writeUInt16BE(12, 28) tpl.writeUInt16BE(4, 30) for (let i = 0; i < 260; i++) { parseFlowPacket(tpl, `203.0.${Math.floor(i / 250)}.${i % 250}`) } assert.ok(templateExporterCountForTests() <= 256) } console.log("traffic-flow-parse.test.ts: ok")