代码之家  ›  专栏  ›  技术社区  ›  intrigued_66

原始套接字未在正确的接口上捕获DHCP数据包,但Wireshark

  •  0
  • intrigued_66  · 技术社区  · 1 年前

    下面是一个原始套接字嗅探器的C应用程序示例:

    https://www.binarytides.com/packet-sniffer-code-c-linux/

    除了我在创建套接字后将套接字绑定到接口之外:

    const std::string& iff = "wlp3s0";
    
    int r = setsockopt(sock_raw, SOL_SOCKET, SO_BINDTODEVICE, iff.c_str(), iff.length());
    if (r == -1)
    {
        std::abort();
    }
    

    然后我让应用程序继续运行:

    sudo ./the_app
    

    我打开Wireshark,收听wlp3s0无线接口。我从捕获中删除了有线以太网接口。

    然后我就跑 sudo dhclient -r sudo dhclient 停止/启动DHCP,Wireshark检测到4或5个DHCP数据包,如预期的那样:

    enter image description here

    然而,C原始套接字嗅探器显示绝对没有接收到UDP数据包。

    如果我打开一个网络浏览器,它会开始显示TCP数据包(Wireshark报告DHCP数据包是UDP)。

    发生了什么事?我使用的是Ubuntu 22.04

    最小示例:

    #include<stdio.h>   //For standard things
    #include<stdlib.h>  //malloc
    #include<string.h>  //memset
    #include<netinet/ip_icmp.h> //Provides declarations for icmp header
    #include<netinet/udp.h> //Provides declarations for udp header
    #include<netinet/tcp.h> //Provides declarations for tcp header
    #include<netinet/ip.h>  //Provides declarations for ip header
    #include<sys/socket.h>
    #include<arpa/inet.h>
    #include <unistd.h>
    
    void ProcessPacket(unsigned char* , int);
    
    int tcp=0,udp=0,icmp=0,others=0,igmp=0,total=0,i,j;
    struct sockaddr_in source,dest;
    
    int main()
    {    
        unsigned char *buffer = (unsigned char *)malloc(65536); //Its Big!
        
        printf("Starting...\n");
        int sock_raw = socket(AF_INET , SOCK_RAW , IPPROTO_TCP);
    
        if(sock_raw < 0)
        {
            std::abort();
        }
    
        int r = setsockopt(sock_raw, SOL_SOCKET, SO_BINDTODEVICE, "wlp3s0", strlen("wlp3s0"));
        
        if (r == -1)
        {
            std::abort();
        }
       
        while(1)
        {
            int data_size = recv(sock_raw , buffer , 65536 , 0);
            
            if(data_size <0 )
            {
                std::abort();
            }
    
            ProcessPacket(buffer , data_size);
        }
        
        ::close(sock_raw);
        return 0;
    }
    
    void ProcessPacket(unsigned char* buffer, int size)
    {
        //Get the IP Header part of this packet
        struct iphdr *iph = (struct iphdr*)buffer;
        ++total;
        switch (iph->protocol) //Check the Protocol and do accordingly...
        {
            case 1:  //ICMP Protocol
                ++icmp;
                //print_icmp_packet(buffer, size);
                break;
            
            case 2:  //IGMP Protocol
                ++igmp;
                break;
            
            case 6:  //TCP Protocol
                ++tcp;
                //print_tcp_packet(buffer , size);
                break;
            
            case 17: //UDP Protocol
                ++udp;
                //print_udp_packet(buffer , size);
                break;
            
            default: //Some Other Protocol like ARP etc.
                ++others;
                break;
        }
    
        printf("TCP : %d   UDP : %d   ICMP : %d   IGMP : %d   Others : %d   Total : %d\r",tcp,udp,icmp,igmp,others,total);
    }
    
    1 回复  |  直到 1 年前
        1
  •  1
  •   Marco Bonelli    1 年前

    当然,你没有收到UDP数据包。您明确只要求使用的TCP数据包 IPPROTO_TCP :

    int sock_raw = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);
    

    毕竟,你正在跟随的向导告诉你:

    1. 由于声明的原因,上面的嗅探器只会拾取TCP数据包:

      sock_raw = socket(AF_INET , SOCK_RAW , IPPROTO_TCP);
      

    现在您可以考虑使用 IPPROTO_RAW ,但这只可能用于 发送 man 7 raw 解释:

    的协议 IPPROTO_RAW 暗示已启用 IP_HDRINCL 并且能够发送在所传递的报头中指定的任何IP协议。 通过接收所有IP协议 IPPROTO_RAW 不可能使用原始套接字 .

    因此,您无法通过 AF_INET 插座你需要一个 AF_PACKET 套接字和稍微复杂一点的代码。看见 man 7 packet 了解更多信息。

    请注意,在这种情况下,您必须使用绑定 bind(2) struct sockaddr_ll ,您不能使用 setsockopt(SO_BINDTODEVICE, ...) 具有 AF_数据包 插座,如 man 7 socket 状态:

    SO_BINDTODEVICE

    […]请注意,这只适用于某些套接字类型,特别是 AF_INET 插座。数据包套接字不支持它(使用正常 绑定(2) 那里)。