tc full packet test
All checks were successful
Build and Deploy MITM Webserver / build (push) Successful in 10s
All checks were successful
Build and Deploy MITM Webserver / build (push) Successful in 10s
This commit is contained in:
@@ -33,6 +33,7 @@ from src.utilities.interface_bridge_helpers import (
|
||||
)
|
||||
from src.config import settings
|
||||
from src.utilities.bridge_telemetry import bridge_telemetry_manager
|
||||
from src.utilities.capture_header import parse_capture_header
|
||||
from src.utilities.ndpi_classifier import ndpi_classifier
|
||||
from src.utilities.packet_identity import build_packet_uid
|
||||
from src.utilities.packet_tracker import packet_tracker
|
||||
@@ -61,8 +62,13 @@ sessions: Dict[str, Dict[str, Any]] = {}
|
||||
class PacketInfo(TypedDict, total=False):
|
||||
"""TypedDict for parsed packet data used by persistence and telemetry."""
|
||||
|
||||
correlation_key: str
|
||||
correlation_source: str
|
||||
packet_id: Optional[str]
|
||||
packet_uid: str
|
||||
skb_mark: Optional[int]
|
||||
iface: str
|
||||
capture_iface: Optional[str]
|
||||
length: int
|
||||
raw: bytes
|
||||
src_mac: Optional[str]
|
||||
@@ -85,6 +91,7 @@ class PacketInfo(TypedDict, total=False):
|
||||
app_is_encrypted: Optional[bool]
|
||||
app_risk_score: Optional[int]
|
||||
dpi_metadata: Optional[Dict[str, Any]]
|
||||
capture_metadata: Optional[Dict[str, Any]]
|
||||
ip_id: Optional[int]
|
||||
icmp_type: Optional[int]
|
||||
icmp_code: Optional[int]
|
||||
@@ -155,7 +162,7 @@ def _safe_get_attr(layer, attr: str):
|
||||
return None
|
||||
|
||||
|
||||
def parse_packet(pkt, bridge_label: str) -> None:
|
||||
def parse_packet(pkt, bridge_label: str, capture_metadata: Optional[Dict[str, Any]] = None) -> None:
|
||||
"""
|
||||
Parse a scapy Packet object into a normalized PacketInfo and schedule DB insert.
|
||||
bridge_label indicates whether the packet was captured as part of a bridge-snapshot or single-interface.
|
||||
@@ -168,8 +175,11 @@ def parse_packet(pkt, bridge_label: str) -> None:
|
||||
|
||||
pkt_info: PacketInfo = {
|
||||
"iface": pkt_iface,
|
||||
"capture_iface": pkt_iface if capture_metadata else None,
|
||||
"length": len(pkt),
|
||||
"raw": bytes(pkt),
|
||||
"packet_id": capture_metadata.get("packet_id") if capture_metadata else None,
|
||||
"skb_mark": capture_metadata.get("skb_mark") if capture_metadata else None,
|
||||
"src_mac": None,
|
||||
"dst_mac": None,
|
||||
"eth_type_raw": None,
|
||||
@@ -190,6 +200,7 @@ def parse_packet(pkt, bridge_label: str) -> None:
|
||||
"app_is_encrypted": None,
|
||||
"app_risk_score": None,
|
||||
"dpi_metadata": None,
|
||||
"capture_metadata": capture_metadata,
|
||||
"ip_id": None,
|
||||
"icmp_type": None,
|
||||
"icmp_code": None,
|
||||
@@ -369,7 +380,13 @@ def parse_packet(pkt, bridge_label: str) -> None:
|
||||
pkt_info["icmp_embedded_src_port"] = _safe_get_attr(inner[UDP], "sport")
|
||||
pkt_info["icmp_embedded_dst_port"] = _safe_get_attr(inner[UDP], "dport")
|
||||
|
||||
pkt_info["packet_uid"] = build_packet_uid(pkt_info)
|
||||
if pkt_info.get("packet_id"):
|
||||
pkt_info["correlation_key"] = f"pid:{pkt_info['packet_id']}"
|
||||
pkt_info["correlation_source"] = "kernel_mark"
|
||||
else:
|
||||
pkt_info["packet_uid"] = build_packet_uid(pkt_info)
|
||||
pkt_info["correlation_key"] = f"uid:{pkt_info['packet_uid']}"
|
||||
pkt_info["correlation_source"] = "legacy_hash"
|
||||
|
||||
try:
|
||||
web_loop = getattr(shared_objects, "web_loop", None)
|
||||
@@ -548,9 +565,13 @@ def _session_reader_loop(session_id: str) -> None:
|
||||
|
||||
# parse with scapy
|
||||
try:
|
||||
pkt = Ether(raw)
|
||||
capture_meta = None
|
||||
packet_bytes = raw
|
||||
if settings.capture_header_enabled and settings.capture_interface and iface == settings.capture_interface:
|
||||
capture_meta, packet_bytes = parse_capture_header(raw)
|
||||
pkt = Ether(packet_bytes)
|
||||
pkt.sniffed_on = iface
|
||||
parse_packet(pkt, label)
|
||||
parse_packet(pkt, label, capture_metadata=capture_meta)
|
||||
logger.debug("Captured packet on %s in session %s (len=%d)", iface, session_id, len(raw))
|
||||
except Exception:
|
||||
logger.exception("Failed to parse/process packet from %s in session %s", iface, session_id)
|
||||
@@ -595,19 +616,23 @@ def start_afpacket_sniffer(target: str, target_is_interface: bool = False) -> st
|
||||
"label": target,
|
||||
"is_bridge": not target_is_interface,
|
||||
"ports": [],
|
||||
"capture_ifaces": [],
|
||||
}
|
||||
sessions[session_id] = session
|
||||
|
||||
# determine ports for this session
|
||||
if target_is_interface:
|
||||
ports = [target]
|
||||
capture_ifaces = [target]
|
||||
else:
|
||||
ports = get_bridge_ports_once(target)
|
||||
capture_ifaces = [settings.capture_interface] if settings.capture_interface else list(ports)
|
||||
|
||||
session["ports"] = ports
|
||||
session["capture_ifaces"] = capture_ifaces
|
||||
|
||||
# create sockets for this session only
|
||||
for iface in ports:
|
||||
for iface in capture_ifaces:
|
||||
if not check_interface_exists(iface):
|
||||
logger.warning("Snapshot port %s missing for session %s, skipping", iface, session_id)
|
||||
continue
|
||||
@@ -620,7 +645,13 @@ def start_afpacket_sniffer(target: str, target_is_interface: bool = False) -> st
|
||||
session["thread"] = t
|
||||
t.start()
|
||||
_sync_bridge_telemetry()
|
||||
logger.info("Started sniffer session %s label=%s ports=%s", session_id, target, ports)
|
||||
logger.info(
|
||||
"Started sniffer session %s label=%s ports=%s capture_ifaces=%s",
|
||||
session_id,
|
||||
target,
|
||||
ports,
|
||||
capture_ifaces,
|
||||
)
|
||||
return session_id
|
||||
|
||||
|
||||
@@ -718,6 +749,7 @@ def get_internal_debug_state() -> dict:
|
||||
"label": s.get("label"),
|
||||
"is_bridge": s.get("is_bridge"),
|
||||
"ports": list(s.get("ports", [])),
|
||||
"capture_ifaces": list(s.get("capture_ifaces", [])),
|
||||
"sockets": list(s.get("sockets", {}).keys()),
|
||||
"thread_alive": bool(s.get("thread") and s.get("thread").is_alive()),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user