pythonsocketspacket-sniffersvlanraw-ethernet

Reading VLAN field of a raw ethernet packet in Python


I have a low level comunication between two nodes using Ethernet packets (2-layer, no UDP/IP nor TCP/IP). These packets has the VLAN field inside, my interface is configured in promiscuous mode and it is able to read them completely due to I can see the VLAN tag in Wireshark in my Ubuntu system.

Using python, I'm able to read the entire packet except the VLAN field. The field is vanished and after the source MAC field the Ethertype comes in place.

import socket

sock = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(0x0003))

msg = sock.recvmsg(4096)

It is possible to do this with socket python module? Am I missing something in my configuration or it is a NIC issue?

Thanks in advance,


Solution

  • Late to the party, see https://stackoverflow.com/a/59760058/5459467 for full details.

    I advise to use scapy's sockets (conf.L2socket or sniff) that have this code implemented. Otherwise use the snippet below:

    import ctypes, socket
    
    # From bits/socket.h
    SOL_PACKET = 263
    # From asm/socket.h
    SO_ATTACH_FILTER = 26
    ETH_P_8021Q = 0x8100
    PACKET_AUXDATA = 8
    TP_STATUS_VLAN_VALID = 1 << 4
    
    class tpacket_auxdata(ctypes.Structure):
        _fields_ = [
            ("tp_status", ctypes.c_uint),
            ("tp_len", ctypes.c_uint),
            ("tp_snaplen", ctypes.c_uint),
            ("tp_mac", ctypes.c_ushort),
            ("tp_net", ctypes.c_ushort),
            ("tp_vlan_tci", ctypes.c_ushort),
            ("tp_padding", ctypes.c_ushort),
        ]
    
    def _recv_raw(sock, x=65535):
        """Internal function to receive a Packet,
        and process ancillary data.
        """
        flags_len = socket.CMSG_LEN(4096)
        pkt, ancdata, flags, sa_ll = sock.recvmsg(x, flags_len)
        if not pkt:
            return pkt, sa_ll
        for cmsg_lvl, cmsg_type, cmsg_data in ancdata:
            # Check available ancillary data
            if (cmsg_lvl == SOL_PACKET and cmsg_type == PACKET_AUXDATA):
                # Parse AUXDATA
                auxdata = tpacket_auxdata.from_buffer_copy(cmsg_data)
                if auxdata.tp_vlan_tci != 0 or \
                        auxdata.tp_status & TP_STATUS_VLAN_VALID:
                    # Insert VLAN tag
                    tag = struct.pack(
                        "!HH",
                        ETH_P_8021Q,
                        auxdata.tp_vlan_tci
                    )
                        pkt = pkt[:12] + tag + pkt[12:]
            return pkt
    

    (From https://github.com/secdev/scapy/pull/2091)

    But first, while starting your socket, use

    sock.setsockopt(SOL_PACKET, PACKET_AUXDATA, 1)