2
votes

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,

2

2 Answers

1
votes

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)
0
votes

The NIC driver needs to be configured to leave the 802.1Q tag in place. Not all NICs can do that and I don't believe there's a standard way to do it.