user1444314
user1444314

Reputation: 49

How to initialize raw socket for VLAN sniffing

I'm trying to create software that sniffes DHCP-offers from diffrent VLAN's and my problem is that the packets recv'ed by me contains no VLAN-tags. I'm using raw sockets, and i've tried to enable promiscuous mode, but no avail.

Question is that how to instruct kernel to keep all of the data in the packet intact?

When using wireshark i can see the reply packet with the VLAN header set ok, but my recv'ed data has all the VLAN information removed.

Thanks in advance!

--Lauri

Upvotes: 4

Views: 7894

Answers (2)

Cukic0d
Cukic0d

Reputation: 5421

This is old, but this still hasn't been solved:

When VLAN offload is enabled on the NIC Linux will not deliver the VLAN tag in the data returned by recv. Instead, it delivers the VLAN TCI in a control message.

To get the control fields, you need to use PACKET_AUXDATA, which will add the following to ancillary data:

 struct tpacket_auxdata {
      __u32 tp_status;
      __u32 tp_len;      /* packet length */
      __u32 tp_snaplen;  /* captured length */
      __u16 tp_mac;
      __u16 tp_net;
      __u16 tp_vlan_tci;
      __u16 tp_padding;
  };

For instance

sock.setsockopt(SOL_PACKET, PACKET_AUXDATA, 1)

Once that is done, use recvmsg() and iterate through the ancillary data looking for PACKET_AUXDATA. Get tp_vlan_tci out of it, and inject this back into the packet data.

Examples of code

  • C: libpcap

https://github.com/the-tcpdump-group/libpcap/blob/3a7962215f4f3b13ac792ad0e0487a53c0f1178e/pcap-linux.c#L1756

  • Python
import ctypes, socket

# From bits/socket.h
SOL_PACKET = 263
# From asm/socket.h
SO_ATTACH_FILTER = 26
ETH_P_8021Q = 0x8100
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)

Upvotes: 1

user1202136
user1202136

Reputation: 11567

If I understand the Linux code correctly, VLAN tags are stripped from packets' payload early and stored in a field that is not accessible through the raw sockets API. Instead, try to do what wireshark does, i.e., use the pcap API.

Upvotes: 0

Related Questions