【问题标题】:Raw Socket promiscuous mode not sniffing what I write原始套接字混杂模式没有嗅探我写的内容
【发布时间】:2012-08-24 01:11:59
【问题描述】:

我正在使用混杂模式下的原始套接字编写程序,并且我需要原始套接字而不是嗅探我发送的数据包。我只需要通过以太网 rx 线(而不是 tx 线)读取数据。有可能吗?

非常感谢。

【问题讨论】:

  • 您是在同一台机器上发送和嗅探吗?那是个问题。为此,您需要 2 台机器。
  • 真的,我在一台机器上嗅探并通过无线电链路在UDP连接中发送数据包。在其他机器上,我解包并在其他网络中写入。我通过无线电链接加入网络。

标签: c++ c sockets ethernet sniffing


【解决方案1】:

解决方案是查看读取的数据包是否为 PACKET_OUTGOING。使用此选项,您可以区分放入以太网 tx 线的数据包和从 rx 线读取的数据包。

以混杂模式打开 Socket:

char* i = "eth0";
int fd;
struct ifreq ifr;
struct sockaddr_ll interfaceAddr;
struct packet_mreq mreq;

if ((fd = socket(PF_PACKET,SOCK_RAW,htons(ETH_P_ALL))) < 0)
    return -1;

memset(&interfaceAddr,0,sizeof(interfaceAddr));
memset(&ifr,0,sizeof(ifr));
memset(&mreq,0,sizeof(mreq));

memcpy(&ifr.ifr_name,i,IFNAMSIZ);
ioctl(fd,SIOCGIFINDEX,&ifr);

interfaceAddr.sll_ifindex = ifr.ifr_ifindex;
interfaceAddr.sll_family = AF_PACKET;

if (bind(fd, (struct sockaddr *)&interfaceAddr,sizeof(interfaceAddr)) < 0)
    return -2;


mreq.mr_ifindex = ifr.ifr_ifindex;
mreq.mr_type = PACKET_MR_PROMISC;
mreq.mr_alen = 6;

if (setsockopt(fd,SOL_PACKET,PACKET_ADD_MEMBERSHIP,
     (void*)&mreq,(socklen_t)sizeof(mreq)) < 0)
        return -3;
//...

然后阅读。现在,我们可以区分 Rx 和 Tx 以太网线:

unsigned char buf[1500];
struct sockaddr_ll addr;
socklen_t addr_len = sizeof(addr);
n = recvfrom(fd, buf, 2000, 0, (struct sockaddr*)&addr, &addr_len);
if (n <= 0)
{
    //Error reading
}
else if (addr.sll_pkttype == PACKET_OUTGOING)
{
    //The read data are not writing by me.
    //Use only this data to copy in the other network.
}

这就是全部。使用它我不会读取我写的数据。当我将网络 1 帧复制到网络 2 并将网络 2 帧复制到网络 1 时,我避免了循环。

【讨论】:

    【解决方案2】:

    您需要创建与传入数据包相对应的BPF(BSD 数据包过滤器)过滤器:

    /* To obtain the BPF filter corresponding to incoming traffic:
     * sudo tcpdump -dd -i eth0 dst host YOUR_IP_ADDRESS and not src host YOUR_IP_ADDRESS 
     * The filter given below is what i get on my local machine (192.168.1.7):
     * sudo tcpdump -dd -i eth0 dst host 192.168.1.7 and not src host 192.168.1.7
     */
    struct sock_filter incoming_filter[] = {       
        { 0x28, 0, 0, 0x0000000c },
        { 0x15, 0, 4, 0x00000800 },
        { 0x20, 0, 0, 0x0000001e },
        { 0x15, 0, 9, 0xc0a80107 },
        { 0x20, 0, 0, 0x0000001a },
        { 0x15, 7, 6, 0xc0a80107 },
        { 0x15, 1, 0, 0x00000806 },
        { 0x15, 0, 5, 0x00008035 },
        { 0x20, 0, 0, 0x00000026 },
        { 0x15, 0, 3, 0xc0a80107 },
        { 0x20, 0, 0, 0x0000001c },
        { 0x15, 1, 0, 0xc0a80107 },
        { 0x6, 0, 0, 0x0000ffff },
        { 0x6, 0, 0, 0x00000000 },
    };
    
    int s;
    struct sockaddr_ll sock_address;
    struct sock_fprog prog;
    
    /* Init the program filter */
    prog.len = 14;
    prog.filter = incoming_filter;
    

    然后是您的 RAW 套接字,然后绑定和...:

    /* Create the raw socket */
    s = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
    if (s < 0) 
    {
        /* Error handling */
    }
    
    /* Build our socket */
    sock_address.sll_family = AF_PACKET;
    sock_address.sll_protocol = htons(ETH_P_IP);
    sock_address.sll_ifindex = if_nametoindex(your_interface_name);
    
    /* Bind */
    if (bind(s, (struct sockaddr*)&sock_address, sizeof(sock_address)) < 0)
    {
        /* Error handling */
    }
    
    /* Apply the filter */
    if (setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &prog, sizeof(prog)) < 0)
    {
        /* Error handling */
    }
    
    /* Infinite listen loop */
    while (1)
    {
    
        /* Handle received packet */
    }
    

    编辑:如果你想按 Mac 地址过滤,很简单,像这样生成你的过滤器(我在这里使用我的 Mac 地址):

    sudo tcpdump -dd -i eth0 ether dst 00:0f:b0:68:0f:92 and not ether src 00:0f:b0:68:0f:92
    { 0x20, 0, 0, 0x00000002 },
    { 0x15, 0, 7, 0xb0680f92 },
    { 0x28, 0, 0, 0x00000000 },
    { 0x15, 0, 5, 0x0000000f },
    { 0x20, 0, 0, 0x00000008 },
    { 0x15, 0, 2, 0xb0680f92 },
    { 0x28, 0, 0, 0x00000006 },
    { 0x15, 1, 0, 0x0000000f },
    { 0x6, 0, 0, 0x0000ffff },
    { 0x6, 0, 0, 0x00000000 },
    

    【讨论】:

    • 我们在第 2 层工作,使用 MAC 地址。我们没有 IP 地址。
    • @JoséMaríaB :这不会改变任何事情!只需按您的主机 Mac 地址过滤即可。查看我的编辑
    • 非常感谢。我认为在这种情况下使用 PACKET_OUTGOING 解决方案更容易(Merci beaucoup. Je pense que c'est plus facile à utiliser la solution PACKET_OUTGOING pour ce cas :-))
    【解决方案3】:

    您可以轻松过滤来自您的 IP 地址的内容,然后将它们从您的列表中排除。

    【讨论】:

    • 我们有两个网络和一台带有两个以太网设备(eth0 和 eth1)的 PC。第一个连接到第一个网络,第二个连接到第二个网络。我们喜欢在 2 级搭建软件桥梁;在混杂模式下使用原始套接字,我们读取网络 1 的所有流量并将其复制到网络 2 中,反之亦然。这一切都在无限循环中完成,因为我们复制到网络 1 的帧被复制到网络 2 并被复制到网络 1...我们不需要嗅探我们写入的帧,这些帧位于第 2 层(使用 mac)和来自完整神经网络中的任何机器。
    • 那么您不能简单地在 MAC 级别进行过滤吗?仅查看 MAC 地址并跳过您知道您发送的那些并不难。我错过了什么?
    • 该软件用于通过无线电链路在网络之间建立桥梁。我在网络 1 中读取的数据包通过无线电链路发送到其他计算机(UDP 连接中的数据包),在网络 2 中解包并写入(反之亦然)。因此,我不能使用 linux 桥接功能 :-( 我相信...
    • 存储 MAC 以了解我们不必编写的内容似乎是个好主意! “pcaplib”有一些关于它的功能吗?我会检查的。
    【解决方案4】:

    遗憾的是,Linux 没有提供任何选项来指定不接收原始套接字的传出数据包。

    如果允许重建 Linux 内核,我建议只使用 packet_socket_type.patch 修补内核。

    在用户程序中,您可以指定您希望接收的数据包类型。

    int mask=0;
    mask = PACKET_MASK_ANY & ~(1<<PACKET_OUTGOING) & ~(1 << PACKET_LOOPBACK);
    setsockopt( raw_sock, SOL_PACKET, PACKET_RECV_TYPE, &mask, sizeof(mask));
    

    IMO,这是真正解决问题的解决方案。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-02-14
      • 2014-01-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-24
      • 1970-01-01
      相关资源
      最近更新 更多