Чтение поля VLAN необработанного пакета Ethernet в Python

У меня низкоуровневая связь между двумя узлами с использованием пакетов Ethernet (двухуровневая, без UDP/IP и TCP/IP). Внутри этих пакетов есть поле VLAN, мой интерфейс настроен в неразборчивом режиме, и он может их полностью прочитать, потому что я вижу тег VLAN в Wireshark в моей системе Ubuntu.

Используя python, я могу прочитать весь пакет, кроме поля VLAN. Поле исчезает, и после поля исходного MAC-адреса появляется поле Ethertype.

import socket

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

msg = sock.recvmsg(4096)

Можно ли это сделать с помощью модуля сокета python? Я что-то упустил в своей конфигурации или это проблема с сетевой картой?

Заранее спасибо,


person ferdepe    schedule 18.06.2019    source источник


Ответы (2)


Опаздывая на вечеринку, см. https://stackoverflow.com/a/59760058/5459467 для получения полной информации.

Я советую использовать сокеты scapy (conf.L2socket или sniff), в которых реализован этот код. В противном случае используйте фрагмент ниже:

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

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

Но сначала при запуске сокета используйте

sock.setsockopt(SOL_PACKET, PACKET_AUXDATA, 1)
person Cukic0d    schedule 15.01.2020

Драйвер сетевой карты необходимо настроить так, чтобы тег 802.1Q оставался на месте. Не все сетевые карты могут это сделать, и я не верю, что существует стандартный способ сделать это.

person Zac67    schedule 22.06.2019
comment
Итак, почему Wireshark может читать пакеты, а мой скрипт на Python не может? Я не понимаю... - person ferdepe; 28.06.2019
comment
Необработанного сокета может быть недостаточно. Скорее всего, вам нужно перехватывать пакеты прямо из драйвера. - person Zac67; 28.06.2019