test ebpf
All checks were successful
Build and Deploy MITM Webserver / build (push) Successful in 1m40s

This commit is contained in:
2026-03-07 10:06:24 +01:00
parent a98054cb5b
commit f9bbe9b73c
13 changed files with 1271 additions and 177 deletions

View File

@@ -84,8 +84,12 @@ function colorForName(name: string) {
return result;
}
/** Parse iface field into an array of strings */
function parseIfaces(ifaceField?: string | string[]) {
function packetKey(packet: PacketRow) {
return String(packet.packet_uid ?? packet.id ?? `${packet.iface ?? 'if'}:${packet.timestamp ?? ''}`);
}
function parseIfaces(ifaceField?: string | string[] | null, observedIfaces?: string[] | null) {
if (Array.isArray(observedIfaces) && observedIfaces.length > 0) return observedIfaces;
if (!ifaceField) return [];
if (Array.isArray(ifaceField)) return ifaceField;
return String(ifaceField)
@@ -131,14 +135,32 @@ export default function PacketViewer(): ReactElement {
const [selectedPacket, setSelectedPacket] = useState<PacketRow | null>(null);
const queuedDuringPause = useRef<PacketRow[]>([]);
// helper: prepend new packets (newest first). Keep list capped.
const pushNew = useCallback((newPkts: PacketRow[] | PacketRow) => {
setPackets((prev) => {
const arr = Array.isArray(newPkts) ? newPkts : [newPkts];
const normalized = arr.map((p) => p);
const merged = [...normalized, ...prev];
if (merged.length > MAX_PACKETS) return merged.slice(0, MAX_PACKETS);
return merged;
const next = [...prev];
for (const packet of arr) {
const key = packetKey(packet);
const index = next.findIndex((item) => packetKey(item) === key);
if (index >= 0) {
next[index] = { ...next[index], ...packet };
} else {
next.unshift(packet);
}
}
next.sort((left, right) => {
const leftId = Number(left.id ?? 0);
const rightId = Number(right.id ?? 0);
if (!Number.isNaN(leftId) && !Number.isNaN(rightId) && leftId !== rightId) {
return rightId - leftId;
}
return String(right.timestamp ?? '').localeCompare(String(left.timestamp ?? ''));
});
if (next.length > MAX_PACKETS) return next.slice(0, MAX_PACKETS);
return next;
});
}, []);
@@ -284,7 +306,7 @@ export default function PacketViewer(): ReactElement {
key: 'iface',
width: 100,
render: (_: any, rec: PacketRow) => {
const ifaces = parseIfaces(rec.iface);
const ifaces = parseIfaces(rec.iface, rec.observed_ifaces);
if (ifaces.length === 0) return <Text type="secondary">-</Text>;
return (
<Space wrap size={[6, 6]}>
@@ -301,10 +323,38 @@ export default function PacketViewer(): ReactElement {
);
},
},
{
title: 'Path',
key: 'path',
width: 150,
render: (_: any, rec: PacketRow) => (
<Space direction="vertical" size={0}>
<Text>{rec.ingress_if ?? '-'}</Text>
<Text type="secondary">{rec.egress_if ?? '-'}</Text>
</Space>
),
},
{
title: 'Verdict',
key: 'verdict',
width: 120,
render: (_: any, rec: PacketRow) => {
const verdict = rec.verdict ?? '-';
const confidence = rec.verdict_confidence ? ` (${rec.verdict_confidence})` : '';
return (
<Space direction="vertical" size={0}>
<Text>{verdict}</Text>
<Text type="secondary" style={{ fontSize: 12 }}>
{(rec.verdict_reason ?? '') + confidence}
</Text>
</Space>
);
},
},
{
title: 'MAC (src → dst)',
key: 'macs',
width: 100,
width: 180,
render: (_: any, rec: PacketRow) => (
<div>
<div>
@@ -445,7 +495,14 @@ export default function PacketViewer(): ReactElement {
<Switch checked={!paused} onChange={(checked) => handlePauseToggle(!checked ? true : false)} />
<Button icon={<SyncOutlined />} onClick={handleRefresh} loading={statusLoading} />
<Button onClick={() => clearPackets()} danger icon={<DeleteOutlined />} />
<Button
onClick={async () => {
await clearPackets();
setPackets([]);
}}
danger
icon={<DeleteOutlined />}
/>
</Space>
</Col>
</Row>
@@ -461,9 +518,7 @@ export default function PacketViewer(): ReactElement {
</div>
) : (
<Table
rowKey={(rec: PacketRow) =>
String(rec.id ?? `${rec.iface ?? 'if'}:${rec.timestamp ?? ''}:${Math.random()}`)
}
rowKey={(rec: PacketRow) => packetKey(rec)}
dataSource={packets}
columns={columns as any}
pagination={{ pageSize: 25 }}