使用原始套接字捕获传入和传出数据包

capturing both incoming and outgoing packets using raw socket

我正在用 C 编写一个工具,用于在我的 Linux 系统上记录不同应用程序 运行 的数据使用情况。为此,我创建了一个原始套接字,然后将其与 "eth0" 绑定,这是我的接口名称。但我的问题是,这个套接字只捕获传入的数据包(即:目标 MAC 地址作为我系统的 MAC 地址的数据包)。我找不到任何将源 MAC 地址作为我系统的 MAC 地址的数据包。所以这意味着我自己的机器写入的数据包没有被原始套接字捕获。但我想在两个方向上捕获数据包以识别上传和下载的数据大小。有人可以帮忙吗?

int main()
{
    int rs,len;
    struct sockaddr_ll addr;
    char buf[65535];

    rs = socket(PF_PACKET,SOCK_RAW,htons(ETH_ALL));
    setsockopt(rs,SOL_SOCKET,SO_BINDDEVICE,"eth0",4);
    while(recvfrom(rs,buf,65535,&addr,&len) > 0){
        //print packets
    }
    return 0;
}

我在搜索你的问题时发现了这个。我没试过这个。也许这会奏效。

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

来自socket(7)

SO_BINDTODEVICE

Bind this socket to a particular device like “eth0”, as specified in the passed interface name. [...] Note that this works only for some socket types, particularly AF_INET sockets. It is not supported for packet sockets (use normal bind(2) there).

您应该查看 packet(7) 手册页。

下载 libpcap 库源代码并查看它在做什么(如果您不能只使用 libpcap 而不是自己动手)可能会有所帮助。它肯定会收到传出数据包和传入数据包。

它似乎进行了这些调用(有许多受支持的选项和配置,因此您必须遍历无数的 ifdef 才能准确发现正在发生的事情——请参阅 pcap-linux.c):

fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL);
...
struct sockaddr_ll sll;
memset(&sll, 0, sizeof(sll));
sll.sll_family          = AF_PACKET;
sll.sll_ifindex         = ifindex;   // Interface index from SIOCGIFINDEX
sll.sll_protocol        = htons(ETH_P_ALL);
bind(fd, (struct sockaddr *) &sll, sizeof(sll));
...
struct packet_mreq      mr;
memset(&mr, 0, sizeof(mr));
mr.mr_ifindex = handlep->ifindex;
mr.mr_type    = PACKET_MR_PROMISC;
setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mr, sizeof(mr))

还有一件事要记住。当接收到您自己的盒子发送的数据包时,经常会出现输出数据包中的校验和(IP、TCP 和 UDP)不正确的情况。那是因为大多数现代网络控制器都支持校验和卸载。这意味着卡将在数据包通过 DMA 进入卡内存后计算校验和。因此,那些用于传出数据包的数据在您的程序缓冲区(由内核从本地内存填充)中将不正确。