4

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

user1444314
  • 51
  • 1
  • 3

2 Answers2

1

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)

Cukic0d
  • 5,111
  • 2
  • 19
  • 48
0

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.

user1202136
  • 11,171
  • 4
  • 41
  • 62
  • I can't use libpcap, so that option is out of the window. – user1444314 Jun 13 '12 at 06:52
  • Now question is how to extract the vlan_tci information from given skb, with only socket to got with, or do i need to reimplement the receiving somehow (bit lower level than sockets)? – user1444314 Jun 13 '12 at 09:21
  • You might want to read [this question and answer](http://stackoverflow.com/questions/10947433/linux-user-space-l2-control-protocols). It discusses what changes are necessary in Linux to transmit L2 information to a user-space application. – user1202136 Jun 14 '12 at 13:41
  • All I need to check is how the libpcap handles the receiving. Currently i got a RX-ringbuffer setup, and it seems that there is some error in paket header (size).. because the size is 2 bytes less then expected. The metadata header propably holds the VLAN id info, but i'm wondering what the struct is to be used to when casting it. If I manually move the ethernet heared pointer by 2 bytes, then the packet is recv'd ok, but with no VLAN tags. Currently im trying to see to which struct the start of the metadata header should be cast. – user1444314 Jun 19 '12 at 06:51
  • What interface are you listening on? IIRC, you must listen on the physical interface, otherwise you don't get VLAN tags. – user1202136 Jun 19 '12 at 10:59
  • In listening ETH0, and receive all other data but the VLAN tags. Now i'm wondering how to enable kernel's TPACKET_V2 support to gain the VLAN-tags from the header kernel puts before the raw packet data. – user1444314 Jun 19 '12 at 11:27
  • Hehee, problem solved but another issue arises..I made it work with following code ` int val,ret; val = TPACKET_V2; ret = setsockopt(fd, SOL_PACKET, PACKET_VERSION, &val, sizeof(val)); errno = 0; if (setsockopt(fd, SOL_PACKET, PACKET_RX_RING, (void*) &tp, sizeof(tp))) { DEBUG("setsockopt(fd, SOL_PACKET) failed !\n"); return -1; } DEBUG("SSO1 last error %d: %s\n", errno, strerror(errno)); ` – user1444314 Jun 19 '12 at 12:32
  • Now issue is the extra bytes i get from (14 bytes) before the actual ethernet frame etc. starts.. do i just use fixed magic number to correct the packet start to the right place or is there more elegant way? – user1444314 Jun 19 '12 at 12:36
  • Can you provide a hexdump of such a packet? – user1202136 Jun 20 '12 at 15:00