feat(traffic-flow): add NAT fields to flow processing and analytics
Docker images / prepare-release (push) Successful in 10s
Docker images / backend-test (push) Successful in 2m22s
Docker images / frontend-image (push) Successful in 3m30s
Docker images / updater-image (push) Successful in 45s
Docker images / backend-image (push) Successful in 2m51s
Docker images / notify-webhook (push) Skipped
Docker images / publish-release (push) Successful in 9s

- Introduced new fields for NAT source and destination IPs, as well as their respective ports, in the flow data model.
- Updated database schema and migration scripts to accommodate the new NAT fields in the `flow_buckets` table.
- Enhanced flow analytics and processing functions to utilize the new NAT fields, improving accuracy in traffic flow analysis.
- Added tests to validate the handling of NAT data in various scenarios, ensuring robustness in flow processing.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Denozordec
2026-09-11 23:01:48 +07:00
co-authored by Cursor
parent 5f774ce26e
commit fc29dcede7
22 changed files with 578 additions and 85 deletions
@@ -182,6 +182,95 @@ resetFlowTemplatesForTests()
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)