Poll on BPF device descriptor

46 Views Asked by At

In my macOS (Sonoma 14.3) program, I'm hand-crafting a packet filter instead of using libpcap:

int
create_filter(const char *device, struct bpf_insn *instructions, unsigned int num_instructions) {
    int fd, flags;
    unsigned int immediate = 1;
    struct ifreq ifr;
    struct bpf_program prog = {
        .bf_insns = instructions,
        .bf_len = num_instructions,
    };

    for (int k=0; k<256; k++) {
        char device[15];

        snprintf(device, sizeof(device), "/dev/bpf%i", k);
        fd = open(device, O_RDONLY);
        if ( fd < 0 ) {
            if ( errno == EBUSY ) {
                continue;
            }
            return -1;
        }
        break;
    }

    if ( fd < 0 ) {
        return -1;
    }

    snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", device);
    if ( ioctl(fd, BIOCSETIF, &ifr) == -1 ) {
        goto error;
    }

    if ( ioctl(fd, BIOCIMMEDIATE, &immediate) == -1 ) {
        goto error;
    }

    if ( ioctl(fd, BIOCSETF, &prog) == -1 ) {
        goto error;
    }

    if ( (flags = fcntl(fd, F_GETFL)) == -1 || fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1 ) {
        goto error;
    }

    return fd;

error:
    close(fd);
    return -1;
}

This function works just fine. I'm able to create the BPF and read and parse packets from the descriptor with read. However, when I try to use poll:

struct pollfd poller = {.fd = fd, .events = POLLIN};

poll(&poller, 1, -1); // Omitting error checking for brevity's sake.

poller.revents comes back as POLLNVAL. The man page describes this value as meaning

The file descriptor is not open.

I've confirmed that I'm not closing my descriptor anywhere before poll is called. Why can't I poll the descriptor?

This answer seems relevant except that I'm already following its advice. That is, I've put the socket in non-blocking mode and set capture to be immediate.

To replicate this, you can use

struct bpf_insn filter[] = {
    BPF_STMT(BPF_LD + BPF_W + BPF_LEN, 0),
    BPF_STMT(BPF_RET + BPF_A, 0),
};

for the filter.

0

There are 0 best solutions below