简体   繁体   English

使用 read() 从 PF_PACKET SOCK_RAW 读取丢失数据包

[英]Reading from PF_PACKET SOCK_RAW with read() misses packets

On linux in C, after setting an interface PROMISC and using a raw socket, I can read incoming packets on the interface via read().在 C 中的 linux 上,设置接口 PROMISC 并使用原始套接字后,我可以通过 read() 读取接口上的传入数据包。

However, it does not get all packets.但是,它不会获取所有数据包。 Read() blocks for a "long" time (<1s, but packets flow at hundreds per sec) before reading the next available data from the file descriptor.在从文件描述符读取下一个可用数据之前,Read() 会阻塞“长”时间(<1 秒,但数据包以每秒数百个的速度流动)。

There must be something missing or fundamentally wrong.一定有什么缺失或根本错误。

"use libpcap" is not a valid answser. “使用 libpcap”不是有效的答案。 I looked at their code and cannot find the difference (libpcap does not miss packets)我查看了他们的代码,找不到区别(libpcap 不会错过数据包)

initialize the fd:初始化 fd:

if ((fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) < 0) {
    perror("socket(PF_PACKET) failed");
    return 1;
}

memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name) - 1);

if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) {
    perror("ioctl(SIOCGIFINDEX) failed");
    return 1;
}

memset(&sll, 0, sizeof(sll));
sll.sll_family = AF_PACKET;
sll.sll_ifindex = ifr.ifr_ifindex;
sll.sll_protocol = htons(ETH_P_ALL);

if ((ifr.ifr_flags | IFF_UP | IFF_BROADCAST | IFF_RUNNING) != ifr.ifr_flags) {
    ifr.ifr_flags |= IFF_UP | IFF_BROADCAST | IFF_RUNNING;
    if( ioctl( fd, SIOCSIFFLAGS, &ifr ) < 0 ) {
        perror("ioctl(SIOCSIFFLAGS) failed");
        return 1;
    }
}

if (bind(fd, (struct sockaddr *) &sll, sizeof(sll)) < 0) {
    perror("bind(ETH_P_ALL) failed");
    return 1;
}

if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0)
{
    perror("ioctl(SIOCGIFHWADDR) failed");
    return 1;
}

if (ifr.ifr_hwaddr.sa_family != ARPHRD_IEEE80211 &&
        ifr.ifr_hwaddr.sa_family != ARPHRD_IEEE80211_PRISM &&
        ifr.ifr_hwaddr.sa_family != ARPHRD_IEEE80211_FULL)
{
    if (ifr.ifr_hwaddr.sa_family == 1)
        fprintf(stderr, "\nARP linktype is set to 1 (Ethernet) ");
    else
        fprintf(stderr, "\nUnsupported hardware link type %4d ",
                ifr.ifr_hwaddr.sa_family);

    fprintf(stderr, "- expected ARPHRD_IEEE80211,\nARPHRD_IEEE80211_"
            "FULL or ARPHRD_IEEE80211_PRISM instead.  Make\n"
            "sure RFMON is enabled: run 'airmon-ng start %s"
            " <#>'\nSysfs injection support was not found "
            "either.\n\n", iface);
    return 1;
}

memset(&mr, 0, sizeof(mr));
mr.mr_ifindex = sll.sll_ifindex;
mr.mr_type = PACKET_MR_PROMISC;

if (setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mr, sizeof(mr)) < 0) {
    perror("setsockop(PACKET_MR_PROMISC) failed");
    return 1;
}

read:读:

    while (caplen > 0) {
            if ((caplen = read(fd, p, read_size)) < 0) {
                perror("read failed");
                break;
            }
            p += caplen;
            read_size -= caplen;
        }
    }

Libpcap 1.0 and later probably also use the memory-mapped interface, but the code in the question doesn't; Libpcap 1.0 及更高版本可能也使用内存映射接口,但问题中的代码没有; this might make a difference.这可能会有所作为。

See the Documentation.networking/packet_mmap.txt file in the Linux source, but note that, at least with TPACKET_V1 and TPACKET_V2, all the slots in the memory-mapped buffer have to be big enough to hold the largest possible packet from the.network adapter (which could be larger than you think if, for example, the packet has an additional header such as a radiotap metadata header for 802.11 or if the adapter is doing TCP segmentation or reassembly).请参阅 Linux 源中的 Documentation.networking/packet_mmap.txt 文件,但请注意,至少对于 TPACKET_V1 和 TPACKET_V2,内存映射缓冲区中的所有插槽都必须足够大以容纳来自 .network 的最大可能数据包适配器(这可能比你想象的要大,例如,如果数据包有一个额外的 header,例如 802.11 的 radiotap 元数据 header,或者如果适配器正在执行 TCP 分段或重组)。

Also make sure you're providing as large a socket buffer (or memory-mapped ring buffer) as libpcap is using.还要确保您提供的套接字缓冲区(或内存映射环形缓冲区)与 libpcap 使用的一样大。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM