From 6e720859ff58dd3a96188cb5dd1d565d4bf828f4 Mon Sep 17 00:00:00 2001 From: malmert Date: Mon, 1 Dec 2025 21:04:37 +0100 Subject: [PATCH] feat: add EtherType and IP Protocol enums for enhanced packet parsing and logging --- backend/src/Models/etherType.py | 139 +++++++++++++ backend/src/Models/ip_protocol.py | 315 ++++++++++++++++++++++++++++++ backend/src/network_sniffer.py | 170 ++++++++++++---- 3 files changed, 583 insertions(+), 41 deletions(-) create mode 100644 backend/src/Models/etherType.py create mode 100644 backend/src/Models/ip_protocol.py diff --git a/backend/src/Models/etherType.py b/backend/src/Models/etherType.py new file mode 100644 index 0000000..872d18e --- /dev/null +++ b/backend/src/Models/etherType.py @@ -0,0 +1,139 @@ +from enum import Enum +from typing import Dict + +""" +This module defines an enumeration for Ethernet frame types (EtherTypes) and provides a mapping +from numeric EtherType values to their corresponding enum representation. +Source: https://en.wikipedia.org/wiki/EtherType (01/12/2025) +""" + + +class EtherTypeEnum(str, Enum): + IPv4 = "Internet Protocol version 4" + CHAOSNET = "Chaosnet" + ARP = "Address Resolution Protocol" + WAKE_ON_LAN = "Wake-on-LAN" + SRP = "Stream Reservation Protocol" + AVTP = "Audio Video Transport Protocol" + TRILL = "IETF TRILL Protocol" + DEC_MOP_RC = "DEC MOP RC" + DECNET_PHASE_IV = "DECnet Phase IV / DNA Routing" + DEC_LAT = "DEC Local Area Transport" + RARP = "Reverse Address Resolution Protocol" + APPLETALK = "AppleTalk (EtherTalk)" + IBM_SNA = "IBM SNA / LLC PDU" + AARP = "AppleTalk Address Resolution Protocol" + VLAN_8021Q = "VLAN-tagged (802.1Q)" + SLPP = "Simple Loop Prevention Protocol" + VLACP = "Virtual Link Aggregation Control Protocol" + IPX = "IPX" + QNX_QNET = "QNX Qnet" + IPv6 = "Internet Protocol version 6" + FLOW_CONTROL = "Ethernet Flow Control" + SLOW_PROTOCOLS = "Ethernet Slow Protocols (LACP)" + COBRANET = "CobraNet" + MPLS_UNICAST = "MPLS Unicast" + MPLS_MULTICAST = "MPLS Multicast" + PPPoE_DISCOVERY = "PPPoE Discovery Stage" + PPPoE_SESSION = "PPPoE Session Stage" + HOMEPLUG = "HomePlug 1.0 MME" + EAPOL = "EAP over LAN (802.1X)" + PROFINET = "PROFINET Protocol" + HYPERSCSI = "HyperSCSI" + ATA_OVER_ETHERNET = "ATA over Ethernet" + ETHERCAT = "EtherCAT Protocol" + Q_IN_Q = "Service VLAN (Q-in-Q S-Tag)" + ETH_POWERLINK = "Ethernet Powerlink" + GOOSE = "Generic Substation Events (GOOSE)" + GSE = "GSE Management Services" + SV = "Sampled Value Transmission" + MIKROTIK_ROMON = "MikroTik RoMON" + LLDP = "Link Layer Discovery Protocol" + SERCOS_III = "SERCOS III" + HOMEPLUG_GREENPHY = "HomePlug Green PHY" + MRP = "Media Redundancy Protocol" + MACSEC = "MAC Security (MACsec)" + PBB = "Provider Backbone Bridges (PBB)" + PTP = "Precision Time Protocol over Ethernet" + NC_SI = "NC-SI" + PRP = "Parallel Redundancy Protocol" + CFM = "Connectivity Fault Management (802.1ag / Y.1731)" + FCoE = "Fibre Channel over Ethernet" + FCoE_INIT = "FCoE Initialization Protocol" + ROCE = "RDMA over Converged Ethernet (RoCE)" + TTE = "TTEthernet Protocol Control Frame" + IEEE_1905_1 = "1905.1 IEEE Protocol" + HSR = "High-availability Seamless Redundancy" + ETH_CONFIG_TEST = "Ethernet Configuration Testing Protocol" + REDUNDANCY_TAG = "Redundancy Tag (802.1CB)" + UNKNOWN = "Unknown EtherType" + + +# Mapping from integer EtherType → enum +ETHERTYPE_MAP: Dict[int, EtherTypeEnum] = { + 0x0800: EtherTypeEnum.IPv4, + 0x0804: EtherTypeEnum.CHAOSNET, + 0x0806: EtherTypeEnum.ARP, + 0x0842: EtherTypeEnum.WAKE_ON_LAN, + 0x22EA: EtherTypeEnum.SRP, + 0x22F0: EtherTypeEnum.AVTP, + 0x22F3: EtherTypeEnum.TRILL, + 0x6002: EtherTypeEnum.DEC_MOP_RC, + 0x6003: EtherTypeEnum.DECNET_PHASE_IV, + 0x6004: EtherTypeEnum.DEC_LAT, + 0x8035: EtherTypeEnum.RARP, + 0x809B: EtherTypeEnum.APPLETALK, + 0x80D5: EtherTypeEnum.IBM_SNA, + 0x80F3: EtherTypeEnum.AARP, + 0x8100: EtherTypeEnum.VLAN_8021Q, + 0x8102: EtherTypeEnum.SLPP, + 0x8103: EtherTypeEnum.VLACP, + 0x8137: EtherTypeEnum.IPX, + 0x8204: EtherTypeEnum.QNX_QNET, + 0x86DD: EtherTypeEnum.IPv6, + 0x8808: EtherTypeEnum.FLOW_CONTROL, + 0x8809: EtherTypeEnum.SLOW_PROTOCOLS, + 0x8819: EtherTypeEnum.COBRANET, + 0x8847: EtherTypeEnum.MPLS_UNICAST, + 0x8848: EtherTypeEnum.MPLS_MULTICAST, + 0x8863: EtherTypeEnum.PPPoE_DISCOVERY, + 0x8864: EtherTypeEnum.PPPoE_SESSION, + 0x887B: EtherTypeEnum.HOMEPLUG, + 0x888E: EtherTypeEnum.EAPOL, + 0x8892: EtherTypeEnum.PROFINET, + 0x889A: EtherTypeEnum.HYPERSCSI, + 0x88A2: EtherTypeEnum.ATA_OVER_ETHERNET, + 0x88A4: EtherTypeEnum.ETHERCAT, + 0x88A8: EtherTypeEnum.Q_IN_Q, + 0x88AB: EtherTypeEnum.ETH_POWERLINK, + 0x88B8: EtherTypeEnum.GOOSE, + 0x88B9: EtherTypeEnum.GSE, + 0x88BA: EtherTypeEnum.SV, + 0x88BF: EtherTypeEnum.MIKROTIK_ROMON, + 0x88CC: EtherTypeEnum.LLDP, + 0x88CD: EtherTypeEnum.SERCOS_III, + 0x88E1: EtherTypeEnum.HOMEPLUG_GREENPHY, + 0x88E3: EtherTypeEnum.MRP, + 0x88E5: EtherTypeEnum.MACSEC, + 0x88E7: EtherTypeEnum.PBB, + 0x88F7: EtherTypeEnum.PTP, + 0x88F8: EtherTypeEnum.NC_SI, + 0x88FB: EtherTypeEnum.PRP, + 0x8902: EtherTypeEnum.CFM, + 0x8906: EtherTypeEnum.FCoE, + 0x8914: EtherTypeEnum.FCoE_INIT, + 0x8915: EtherTypeEnum.ROCE, + 0x891D: EtherTypeEnum.TTE, + 0x893A: EtherTypeEnum.IEEE_1905_1, + 0x892F: EtherTypeEnum.HSR, + 0x9000: EtherTypeEnum.ETH_CONFIG_TEST, + 0xF1C1: EtherTypeEnum.REDUNDANCY_TAG, +} + + +def ethertype_from_int(value: int) -> EtherTypeEnum: + """ + Map a numeric EtherType value to the corresponding enum. + Returns UNKNOWN if not found. + """ + return ETHERTYPE_MAP.get(value, EtherTypeEnum.UNKNOWN) diff --git a/backend/src/Models/ip_protocol.py b/backend/src/Models/ip_protocol.py new file mode 100644 index 0000000..5d9465a --- /dev/null +++ b/backend/src/Models/ip_protocol.py @@ -0,0 +1,315 @@ +from enum import Enum + +""" +This module defines an enumeration for IP protocol types and provides a mapping +from numeric IP protocol values to their corresponding enum representation. +Source: https://en.wikipedia.org/wiki/List_of_IP_protocol_numbers (01/12/2025) +""" + + +class IPProtocolEnum(str, Enum): + HOPOPT = "IPv6 Hop-by-Hop Option" + ICMP = "Internet Control Message Protocol" + IGMP = "Internet Group Management Protocol" + GGP = "Gateway-to-Gateway Protocol" + IP_IN_IP = "IP in IP Encapsulation" + ST = "Internet Stream Protocol" + TCP = "Transmission Control Protocol" + CBT = "Core-based Trees" + EGP = "Exterior Gateway Protocol" + IGP = "Interior Gateway Protocol" + BBN_RCC_MON = "BBN RCC Monitoring" + NVP_II = "Network Voice Protocol" + PUP = "Xerox PUP" + ARGUS = "ARGUS" + EMCON = "EMCON" + XNET = "Cross Net Debugger" + CHAOS = "Chaos Protocol" + UDP = "User Datagram Protocol" + MUX = "Multiplexing" + DCN_MEAS = "DCN Measurement Subsystems" + HMP = "Host Monitoring Protocol" + PRM = "Packet Radio Measurement" + XNS_IDP = "XEROX NS IDP" + TRUNK_1 = "Trunk 1" + TRUNK_2 = "Trunk 2" + LEAF_1 = "Leaf 1" + LEAF_2 = "Leaf 2" + RDP = "Reliable Data Protocol" + IRTP = "Internet Reliable Transaction Protocol" + ISO_TP4 = "ISO Transport Protocol Class 4" + NETBLT = "Bulk Data Transfer Protocol" + MFE_NSP = "MFE Network Services Protocol" + MERIT_INP = "MERIT Internodal Protocol" + DCCP = "Datagram Congestion Control Protocol" + THREE_PC = "Third Party Connect Protocol" + IDPR = "Inter-Domain Policy Routing" + XTP = "Xpress Transport Protocol" + DDP = "Datagram Delivery Protocol" + IDPR_CMTP = "IDPR Control Message Transport Protocol" + TP_PP = "TP++ Transport Protocol" + IL = "IL Transport Protocol" + IPV6 = "IPv6 Encapsulation" + SDRP = "Source Demand Routing Protocol" + IPV6_ROUTE = "IPv6 Routing Header" + IPV6_FRAG = "IPv6 Fragment Header" + IDRP = "Inter-Domain Routing Protocol" + RSVP = "Resource Reservation Protocol" + GRE = "Generic Routing Encapsulation" + DSR = "Dynamic Source Routing" + BNA = "Burroughs Network Architecture" + ESP = "Encapsulating Security Payload" + AH = "Authentication Header" + I_NLSP = "Integrated Net Layer Security Protocol" + SWIPE = "SwIPe" + NARP = "NBMA Address Resolution Protocol" + MOBILE = "IP Mobility" + TLSP = "Transport Layer Security Protocol" + SKIP = "Simple Key-Management for IP" + IPV6_ICMP = "ICMP for IPv6" + IPV6_NONXT = "No Next Header for IPv6" + IPV6_OPTS = "Destination Options for IPv6" + PROTO_61 = "Any host internal protocol" + CFTP = "CFTP" + PROTO_63 = "Any local network" + SAT_EXPAK = "SATNET and Backroom EXPAK" + KRYPTOLAN = "Kryptolan" + RVD = "MIT Remote Virtual Disk Protocol" + IPPC = "Internet Pluribus Packet Core" + PROTO_68 = "Any distributed file system" + SAT_MON = "SATNET Monitoring" + VISA = "VISA Protocol" + IPCU = "Internet Packet Core Utility" + CPNX = "Computer Protocol Network Executive" + CPHB = "Computer Protocol Heart Beat" + WSN = "Wang Span Network" + PVP = "Packet Video Protocol" + BR_SAT_MON = "Backroom SATNET Monitoring" + SUN_ND = "SUN ND Protocol (temporary)" + WB_MON = "Wideband Monitoring" + WB_EXPAK = "Wideband EXPAK" + ISO_IP = "International Organization for Standardization Internet Protocol" + VMTP = "Versatile Message Transaction Protocol" + SECURE_VMTP = "Secure VMTP" + VINES = "VINES" + TTP = "TTP (Transaction Transport Protocol / IPTM)" + NSFNET_IGP = "NSFNET-IGP" + DGP = "Dissimilar Gateway Protocol" + TCF = "TCF" + EIGRP = "EIGRP" + OSPF = "Open Shortest Path First" + SPRITE_RPC = "Sprite RPC Protocol" + LARP = "Locus Address Resolution Protocol" + MTP = "Multicast Transport Protocol" + AX25 = "AX.25 Packet Radio" + OS = "KA9Q NOS compatible IP over IP tunneling" + MICP = "Mobile Internetworking Control Protocol" + SCC_SP = "Semaphore Communications Security Protocol" + ETHERIP = "Ethernet-within-IP Encapsulation" + ENCAP = "Encapsulation Header" + PROTO_99 = "Any private encryption scheme" + GMTP = "GMTP" + IFMP = "Ipsilon Flow Management Protocol" + PNNI = "PNNI over IP" + PIM = "Protocol Independent Multicast" + ARIS = "IBM ARIS" + SCPS = "Space Communications Protocol Standards" + QNX = "QNX" + AN = "Active Networks" + IPCOMP = "IP Payload Compression Protocol" + SNP = "Sitara Networks Protocol" + COMPAQ_PEER = "Compaq Peer Protocol" + IPX_IN_IP = "IPX in IP" + VRRP = "Virtual Router Redundancy Protocol" + PGM = "PGM Reliable Transport Protocol" + PROTO_114 = "Any 0-hop protocol" + L2TP = "Layer Two Tunneling Protocol Version 3" + DDX = "D-II Data Exchange" + IATP = "Interactive Agent Transfer Protocol" + STP = "Schedule Transfer Protocol" + SRP = "SpectraLink Radio Protocol" + UTI = "Universal Transport Interface Protocol" + SMP = "Simple Message Protocol" + SM = "Simple Multicast Protocol" + PTP = "Performance Transparency Protocol" + ISIS = "IS-IS over IPv4" + FIRE = "Flexible Intra-AS Routing Environment" + CRTP = "Combat Radio Transport Protocol" + CRUDP = "Combat Radio User Datagram" + SSCOPMCE = "Service-Specific Connection-Oriented Protocol" + IPLT = "IPLT" + SPS = "Secure Packet Shield" + PIPE = "Private IP Encapsulation within IP" + SCTP = "Stream Control Transmission Protocol" + FC = "Fibre Channel" + RSVP_E2E_IGNORE = "RSVP End-to-End Ignore" + MOBILITY_HEADER = "Mobility Extension Header for IPv6" + UDPLITE = "UDPLite" + MPLS_IN_IP = "MPLS-in-IP" + MANET = "MANET Protocols" + HIP = "Host Identity Protocol" + SHIM6 = "Shim6" + WESP = "Wrapped Encapsulating Security Payload" + ROHC = "Robust Header Compression" + SRV6 = "Segment Routing over IPv6" + AGGFRAG = "AGGFRAG Encapsulation Payload for ESP" + NSH = "Network Service Header" + HOMA = "Homa transport protocol" + BIT_EMU = "Bit-stream Emulation" + EXPERIMENTAL_253 = "Use for experimentation (RFC 3692)" + EXPERIMENTAL_254 = "Use for experimentation (RFC 3692)" + RESERVED = "Reserved" + UNKNOWN = "Unknown" + +IP_PROTOCOL_MAP = { + 0: IPProtocolEnum.HOPOPT, + 1: IPProtocolEnum.ICMP, + 2: IPProtocolEnum.IGMP, + 3: IPProtocolEnum.GGP, + 4: IPProtocolEnum.IP_IN_IP, + 5: IPProtocolEnum.ST, + 6: IPProtocolEnum.TCP, + 7: IPProtocolEnum.CBT, + 8: IPProtocolEnum.EGP, + 9: IPProtocolEnum.IGP, + 10: IPProtocolEnum.BBN_RCC_MON, + 11: IPProtocolEnum.NVP_II, + 12: IPProtocolEnum.PUP, + 13: IPProtocolEnum.ARGUS, + 14: IPProtocolEnum.EMCON, + 15: IPProtocolEnum.XNET, + 16: IPProtocolEnum.CHAOS, + 17: IPProtocolEnum.UDP, + 18: IPProtocolEnum.MUX, + 19: IPProtocolEnum.DCN_MEAS, + 20: IPProtocolEnum.HMP, + 21: IPProtocolEnum.PRM, + 22: IPProtocolEnum.XNS_IDP, + 23: IPProtocolEnum.TRUNK_1, + 24: IPProtocolEnum.TRUNK_2, + 25: IPProtocolEnum.LEAF_1, + 26: IPProtocolEnum.LEAF_2, + 27: IPProtocolEnum.RDP, + 28: IPProtocolEnum.IRTP, + 29: IPProtocolEnum.ISO_TP4, + 30: IPProtocolEnum.NETBLT, + 31: IPProtocolEnum.MFE_NSP, + 32: IPProtocolEnum.MERIT_INP, + 33: IPProtocolEnum.DCCP, + 34: IPProtocolEnum.THREE_PC, + 35: IPProtocolEnum.IDPR, + 36: IPProtocolEnum.XTP, + 37: IPProtocolEnum.DDP, + 38: IPProtocolEnum.IDPR_CMTP, + 39: IPProtocolEnum.TP_PP, + 40: IPProtocolEnum.IL, + 41: IPProtocolEnum.IPV6, + 42: IPProtocolEnum.SDRP, + 43: IPProtocolEnum.IPV6_ROUTE, + 44: IPProtocolEnum.IPV6_FRAG, + 45: IPProtocolEnum.IDRP, + 46: IPProtocolEnum.RSVP, + 47: IPProtocolEnum.GRE, + 48: IPProtocolEnum.DSR, + 49: IPProtocolEnum.BNA, + 50: IPProtocolEnum.ESP, + 51: IPProtocolEnum.AH, + 52: IPProtocolEnum.I_NLSP, + 53: IPProtocolEnum.SWIPE, + 54: IPProtocolEnum.NARP, + 55: IPProtocolEnum.MOBILE, + 56: IPProtocolEnum.TLSP, + 57: IPProtocolEnum.SKIP, + 58: IPProtocolEnum.IPV6_ICMP, + 59: IPProtocolEnum.IPV6_NONXT, + 60: IPProtocolEnum.IPV6_OPTS, + 62: IPProtocolEnum.CFTP, + 64: IPProtocolEnum.SAT_EXPAK, + 65: IPProtocolEnum.KRYPTOLAN, + 66: IPProtocolEnum.RVD, + 67: IPProtocolEnum.IPPC, + 69: IPProtocolEnum.SAT_MON, + 70: IPProtocolEnum.VISA, + 71: IPProtocolEnum.IPCU, + 72: IPProtocolEnum.CPNX, + 73: IPProtocolEnum.CPHB, + 74: IPProtocolEnum.WSN, + 75: IPProtocolEnum.PVP, + 76: IPProtocolEnum.BR_SAT_MON, + 77: IPProtocolEnum.SUN_ND, + 78: IPProtocolEnum.WB_MON, + 79: IPProtocolEnum.WB_EXPAK, + 80: IPProtocolEnum.ISO_IP, + 81: IPProtocolEnum.VMTP, + 82: IPProtocolEnum.SECURE_VMTP, + 83: IPProtocolEnum.VINES, + 84: IPProtocolEnum.TTP, + 85: IPProtocolEnum.NSFNET_IGP, + 86: IPProtocolEnum.DGP, + 87: IPProtocolEnum.TCF, + 88: IPProtocolEnum.EIGRP, + 89: IPProtocolEnum.OSPF, + 90: IPProtocolEnum.SPRITE_RPC, + 91: IPProtocolEnum.LARP, + 92: IPProtocolEnum.MTP, + 93: IPProtocolEnum.AX25, + 94: IPProtocolEnum.OS, + 95: IPProtocolEnum.MICP, + 96: IPProtocolEnum.SCC_SP, + 97: IPProtocolEnum.ETHERIP, + 98: IPProtocolEnum.ENCAP, + 100: IPProtocolEnum.GMTP, + 101: IPProtocolEnum.IFMP, + 102: IPProtocolEnum.PNNI, + 103: IPProtocolEnum.PIM, + 104: IPProtocolEnum.ARIS, + 105: IPProtocolEnum.SCPS, + 106: IPProtocolEnum.QNX, + 107: IPProtocolEnum.AN, + 108: IPProtocolEnum.IPCOMP, + 109: IPProtocolEnum.SNP, + 110: IPProtocolEnum.COMPAQ_PEER, + 111: IPProtocolEnum.IPX_IN_IP, + 112: IPProtocolEnum.VRRP, + 113: IPProtocolEnum.PGM, + 115: IPProtocolEnum.L2TP, + 116: IPProtocolEnum.DDX, + 117: IPProtocolEnum.IATP, + 118: IPProtocolEnum.STP, + 119: IPProtocolEnum.SRP, + 120: IPProtocolEnum.UTI, + 121: IPProtocolEnum.SMP, + 122: IPProtocolEnum.SM, + 123: IPProtocolEnum.PTP, + 124: IPProtocolEnum.ISIS, + 125: IPProtocolEnum.FIRE, + 126: IPProtocolEnum.CRTP, + 127: IPProtocolEnum.CRUDP, + 128: IPProtocolEnum.SSCOPMCE, + 129: IPProtocolEnum.IPLT, + 130: IPProtocolEnum.SPS, + 131: IPProtocolEnum.PIPE, + 132: IPProtocolEnum.SCTP, + 133: IPProtocolEnum.FC, + 134: IPProtocolEnum.RSVP_E2E_IGNORE, + 135: IPProtocolEnum.MOBILITY_HEADER, + 136: IPProtocolEnum.UDPLITE, + 137: IPProtocolEnum.MPLS_IN_IP, + 138: IPProtocolEnum.MANET, + 139: IPProtocolEnum.HIP, + 140: IPProtocolEnum.SHIM6, + 141: IPProtocolEnum.WESP, + 142: IPProtocolEnum.ROHC, + 143: IPProtocolEnum.SRV6, + 144: IPProtocolEnum.AGGFRAG, + 145: IPProtocolEnum.NSH, + 146: IPProtocolEnum.HOMA, + 147: IPProtocolEnum.BIT_EMU, + 253: IPProtocolEnum.EXPERIMENTAL_253, + 254: IPProtocolEnum.EXPERIMENTAL_254, + 255: IPProtocolEnum.RESERVED, +} + +def protocol_from_number(n: int) -> IPProtocolEnum: + return IP_PROTOCOL_MAP.get(n, IPProtocolEnum.UNKNOWN) + diff --git a/backend/src/network_sniffer.py b/backend/src/network_sniffer.py index 80a2e4d..b128ba7 100644 --- a/backend/src/network_sniffer.py +++ b/backend/src/network_sniffer.py @@ -4,7 +4,6 @@ import threading import os import time from typing import List, Dict, Optional - import asyncpg from scapy.all import ( Ether, @@ -18,13 +17,15 @@ from scapy.all import ( Dot1Q, Raw, ) - -# ---- New imports for AF_PACKET optimized reader ---------------------- import socket import selectors import errno import struct +from src.Models.etherType import EtherTypeEnum, ethertype_from_int +from src.Models.ip_protocol import IPProtocolEnum, protocol_from_number + + # ---- Logging ---------------------------------------------------------- logging.basicConfig(level=logging.INFO) logger = logging.getLogger("af_packet_sniffer") @@ -170,7 +171,7 @@ async def db_insert_packet(pkt_info: dict, bridge: str) -> None: pkt_info.get("vlan_id"), pkt_info.get("src_ip"), pkt_info.get("dst_ip"), - pkt_info.get("protocol_name"), + pkt_info.get("protocol"), pkt_info.get("src_port"), pkt_info.get("dst_port"), pkt_info["length"], @@ -192,13 +193,15 @@ def parse_packet(pkt, bridge: str) -> None: Parse a scapy packet object and collect a normalized dict of metadata which is then scheduled to be written to the database asynchronously. - The function expects that 'pkt' is a Scapy Packet and that we set - 'pkt.sniffed_on' before calling this function. + Enhancements: + - Uses EtherType and IP Protocol enums (human-readable) for API documentation. + - Records both raw numeric values and enum descriptions. + - Proper VLAN handling (Dot1Q inner ethertype). + - Keeps `protocol_name` (string) for backward compatibility. """ pkt_iface = getattr(pkt, "sniffed_on", None) if not pkt_iface: - # If sniffed_on is missing we cannot determine the interface context; - # skip this packet. + # If sniffed_on is missing we cannot determine the interface context; skip this packet. return logger.debug("Packet captured on %s, bridge %s", pkt_iface, bridge) @@ -210,78 +213,163 @@ def parse_packet(pkt, bridge: str) -> None: "raw": bytes(pkt), "src_mac": None, "dst_mac": None, - "eth_type": None, + # eth types: both raw numeric and enum/description + "eth_type_raw": None, + "eth_type": EtherTypeEnum.UNKNOWN, # enum / human description "vlan_id": None, + # IP protocol: raw numeric and enum/description + "protocol_raw": None, + "protocol": IPProtocolEnum.UNKNOWN, + # backward-compatible string label (older code) + "protocol_name": None, + # L3/L4 fields "src_ip": None, "dst_ip": None, - "protocol_name": None, "src_port": None, "dst_port": None, } # --- Layer extraction --- - # Ethernet layer + # Ethernet layer + EtherType if Ether in pkt: - pkt_info["src_mac"] = pkt[Ether].src - pkt_info["dst_mac"] = pkt[Ether].dst - pkt_info["eth_type"] = hex(pkt[Ether].type) + try: + pkt_info["src_mac"] = pkt[Ether].src + except Exception: + pkt_info["src_mac"] = None + try: + pkt_info["dst_mac"] = pkt[Ether].dst + except Exception: + pkt_info["dst_mac"] = None - # VLAN (802.1Q) - if Dot1Q in pkt: - pkt_info["vlan_id"] = pkt[Dot1Q].vlan + # Base ethertype (may be 0x8100 for VLAN) + eth_type_raw: Optional[int] = None + try: + eth_type_raw = int(pkt[Ether].type) + except Exception: + eth_type_raw = None - # ARP + # VLAN (Dot1Q) may contain the inner ethertype + if Dot1Q in pkt: + try: + # Dot1Q.type is the encapsulated ethertype + inner = int(pkt[Dot1Q].type) + if inner: + eth_type_raw = inner + except Exception: + # ignore and keep whatever eth_type_raw was + pass + # capture vlan id if present + try: + pkt_info["vlan_id"] = int(pkt[Dot1Q].vlan) + except Exception: + pkt_info["vlan_id"] = None + + # Fill eth_type fields (raw + enum) + if eth_type_raw is not None: + pkt_info["eth_type_raw"] = eth_type_raw + try: + pkt_info["eth_type"] = ethertype_from_int(eth_type_raw) + except Exception: + pkt_info["eth_type"] = EtherTypeEnum.UNKNOWN + else: + # Unknown / missing ethertype + pkt_info["eth_type_raw"] = None + pkt_info["eth_type"] = EtherTypeEnum.UNKNOWN + + # ARP (layer 2/3) if ARP in pkt: pkt_info["protocol_name"] = "ARP" - pkt_info["src_ip"] = pkt[ARP].psrc - pkt_info["dst_ip"] = pkt[ARP].pdst + pkt_info["src_ip"] = getattr(pkt[ARP], "psrc", None) + pkt_info["dst_ip"] = getattr(pkt[ARP], "pdst", None) pkt_info["src_port"] = None pkt_info["dst_port"] = None + # ARP is a L2 protocol — leave protocol_raw/protocol as UNKNOWN (or set to a sentinel if desired) # IPv4 if IP in pkt: - pkt_info["src_ip"] = pkt[IP].src - pkt_info["dst_ip"] = pkt[IP].dst - proto = pkt[IP].proto - if proto == 6 and TCP in pkt: + try: + pkt_info["src_ip"] = pkt[IP].src + except Exception: + pkt_info["src_ip"] = None + try: + pkt_info["dst_ip"] = pkt[IP].dst + except Exception: + pkt_info["dst_ip"] = None + + # protocol number (IPv4 'protocol' field) + try: + proto_num = int(pkt[IP].proto) + except Exception: + proto_num = None + + if proto_num is not None: + pkt_info["protocol_raw"] = proto_num + try: + pkt_info["protocol"] = protocol_from_number(proto_num) + except Exception: + pkt_info["protocol"] = IPProtocolEnum.UNKNOWN + + # Common transports + if proto_num == 6 and TCP in pkt: pkt_info["protocol_name"] = "TCP" - pkt_info["src_port"] = pkt[TCP].sport - pkt_info["dst_port"] = pkt[TCP].dport - elif proto == 17 and UDP in pkt: + pkt_info["src_port"] = getattr(pkt[TCP], "sport", None) + pkt_info["dst_port"] = getattr(pkt[TCP], "dport", None) + elif proto_num == 17 and UDP in pkt: pkt_info["protocol_name"] = "UDP" - pkt_info["src_port"] = pkt[UDP].sport - pkt_info["dst_port"] = pkt[UDP].dport - elif proto == 1 and ICMP in pkt: + pkt_info["src_port"] = getattr(pkt[UDP], "sport", None) + pkt_info["dst_port"] = getattr(pkt[UDP], "dport", None) + elif proto_num == 1 and ICMP in pkt: pkt_info["protocol_name"] = "ICMP" else: - pkt_info["protocol_name"] = f"IP_PROTO_{proto}" + # leave protocol_name as numeric fallback if not matched + if pkt_info["protocol_name"] is None: + pkt_info["protocol_name"] = f"IP_PROTO_{proto_num}" if proto_num is not None else None # IPv6 if IPv6 in pkt: - pkt_info["src_ip"] = pkt[IPv6].src - pkt_info["dst_ip"] = pkt[IPv6].dst - nh = pkt[IPv6].nh + try: + pkt_info["src_ip"] = pkt[IPv6].src + except Exception: + pkt_info["src_ip"] = None + try: + pkt_info["dst_ip"] = pkt[IPv6].dst + except Exception: + pkt_info["dst_ip"] = None + + # next header / nh value + try: + nh = int(pkt[IPv6].nh) + except Exception: + nh = None + + if nh is not None: + pkt_info["protocol_raw"] = nh + try: + pkt_info["protocol"] = protocol_from_number(nh) + except Exception: + pkt_info["protocol"] = IPProtocolEnum.UNKNOWN + if nh == 6 and TCP in pkt: pkt_info["protocol_name"] = "TCP" - pkt_info["src_port"] = pkt[TCP].sport - pkt_info["dst_port"] = pkt[TCP].dport + pkt_info["src_port"] = getattr(pkt[TCP], "sport", None) + pkt_info["dst_port"] = getattr(pkt[TCP], "dport", None) elif nh == 17 and UDP in pkt: pkt_info["protocol_name"] = "UDP" - pkt_info["src_port"] = pkt[UDP].sport - pkt_info["dst_port"] = pkt[UDP].dport + pkt_info["src_port"] = getattr(pkt[UDP], "sport", None) + pkt_info["dst_port"] = getattr(pkt[UDP], "dport", None) elif ICMPv6Unknown in pkt: pkt_info["protocol_name"] = "ICMPv6" else: - pkt_info["protocol_name"] = f"IPV6_PROTO_{nh}" + if pkt_info["protocol_name"] is None: + pkt_info["protocol_name"] = f"IPV6_PROTO_{nh}" if nh is not None else None # Raw payload / fallback protocol label if Raw in pkt and not pkt_info["protocol_name"]: pkt_info["protocol_name"] = "RAW" - # Submit DB insert to background asyncio loop from this thread. + # Submit DB insert to background asyncio loop from this thread asyncio.run_coroutine_threadsafe(db_insert_packet(pkt_info, bridge), async_loop) - # ------------------------- # AF_PACKET optimized reader # -------------------------