【问题标题】:How to initialize raw socket for VLAN sniffing如何为 VLAN 嗅探初始化原始套接字
【发布时间】:2012-06-08 10:38:46
【问题描述】:

我正在尝试创建从不同 VLAN 嗅探 DHCP 提供的软件,我的问题是我接收的数据包不包含 VLAN 标记。我正在使用原始套接字,并且尝试启用混杂模式,但无济于事。

问题是如何指示内核保持数据包中的所有数据完好无损?

使用wireshark时,我可以看到VLAN标头设置正常的回复数据包,但我的recv'ed数据已删除所有VLAN信息。

提前致谢!

--劳里

【问题讨论】:

    标签: linux sockets


    【解决方案1】:

    这是旧的,但仍然没有解决:

    在 NIC 上启用 VLAN 卸载时,Linux 将不会在 recv 返回的数据中传递 VLAN 标记。相反,它在控制消息中传递 VLAN TCI。

    要获取控制字段,您需要使用PACKET_AUXDATA,它会将以下内容添加到辅助数据中:

     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;
      };
    

    例如

    sock.setsockopt(SOL_PACKET, PACKET_AUXDATA, 1)
    

    完成后,使用recvmsg() 并遍历辅助数据以查找PACKET_AUXDATA。从中取出 tp_vlan_tci,并将其注入数据包数据中。

    代码示例

    • 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
    

    (来自https://github.com/secdev/scapy

    【讨论】:

      【解决方案2】:

      如果我正确理解Linux code,VLAN 标记会提前从数据包的有效负载中剥离,并存储在无法通过原始套接字 API 访问的字段中。相反,尝试做wireshark 所做的事情,即使用pcap API

      【讨论】:

      • 我不能使用 libpcap,所以该选项不在窗口中。
      • 现在的问题是如何从给定的 skb 中提取 vlan_tci 信息,只需要使用套接字,还是我需要以某种方式重新实现接收(比套接字低一点)?
      • 您可能想阅读this question and answer。它讨论了在 Linux 中需要进行哪些更改才能将 L2 信息传输到用户空间应用程序。
      • 我需要检查的是 libpcap 如何处理接收。目前我有一个 RX-ringbuffer 设置,并且似乎在 paket 标头(大小)中有一些错误。因为大小比预期的少 2 个字节。元数据标头可能包含 VLAN id 信息,但我想知道在转换它时该结构将用于什么。如果我手动将以太网听到的指针移动 2 个字节,则数据包可以接收,但没有 VLAN 标签。目前我正在尝试查看元数据标头的开头应该投射到哪个结构。
      • 你在听什么接口? IIRC,你必须在物理接口上监听,否则你不会得到 VLAN 标签。
      猜你喜欢
      • 2014-02-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-24
      相关资源
      最近更新 更多