简体   繁体   中英

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().

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.

There must be something missing or fundamentally wrong.

"use libpcap" is not a valid answser. I looked at their code and cannot find the difference (libpcap does not miss packets)

initialize the 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; 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).

Also make sure you're providing as large a socket buffer (or memory-mapped ring buffer) as libpcap is using.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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