为什么我没有使用带有udp套接字的BPF接收任何数据包?

时间:2019-07-25 10:06:03

标签: c sockets udp bpf

目标::编写一个BPF过滤器,该过滤器仅允许来自特定src地址的UDP数据包并将其附加到UDP套接字。

问题::如果我执行该程序,并尝试从具有正确src IP的VM发送udp数据包,我什么也不会收到

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>

#include <arpa/inet.h>
#include <linux/filter.h>

/* udp and src 192.168.56.101 */
struct sock_filter bpfcode[] = {
    { 0x28, 0, 0, 0x0000000c },
    { 0x15, 6, 0, 0x000086dd },
    { 0x15, 0, 5, 0x00000800 },
    { 0x30, 0, 0, 0x00000017 },
    { 0x15, 0, 3, 0x00000011 },
    { 0x20, 0, 0, 0x0000001a },
    { 0x15, 0, 1, 0xc0a83865 },
    { 0x6,  0, 0, 0x00040000 },
    { 0x6,  0, 0, 0x00000000 },
};

int main(void)
{
    struct sock_fprog bpf = {
        sizeof(bpfcode) / sizeof(struct sock_filter),
        bpfcode
    };
    struct sockaddr_in src = {
        .sin_family = AF_INET,
        .sin_addr.s_addr = INADDR_ANY,
        .sin_port = htons(1025)
    };
    char buf[1024];
    ssize_t res;
    int fd, ret;

    fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (fd < 0) {
        printf("error: socket\n");
        exit(EXIT_FAILURE);
    }

    ret = setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &bpf, sizeof(bpf));
    if (ret < 0) {
        perror("error: setsockopt\n");
        close(fd);
        exit(EXIT_FAILURE);
    }

    ret = bind(fd, (struct sockaddr *)&src, sizeof(src));
    if (ret < 0) {
        printf("error: bind\n");
        close(fd);
        exit(EXIT_FAILURE);
    }

    res = recvfrom(fd, buf, sizeof(buf), 0, NULL, 0);
    printf("res = %zi\n", res);

    close(fd);

    return 0;
}

1 个答案:

答案 0 :(得分:1)

评论中的摘要:

首先,套接字的类型为SOCK_DGRAM,因此您获得的数据始于L4(UDP),而不是过滤器期望的L2。请改用SOCK_RAW

然后,使用SOCK_RAW将使您可以访问L3,而不是L2(您需要为此更改套接字域)。因此,您需要对过滤器进行一些调整:

    { 0x28, 0, 0, 0x0000000c }, // load Ethertype
    { 0x15, 6, 0, 0x000086dd }, // If IPv6 goto drop
    { 0x15, 0, 5, 0x00000800 }, // If not IPv4 (and not IPv6) goto drop
    { 0x30, 0, 0, 0x00000017 }, // Load IP protocol
    { 0x15, 0, 3, 0x00000011 }, // If not UDP goto drop
    { 0x20, 0, 0, 0x0000001a }, // Load src address
    { 0x15, 0, 1, 0xc0a83865 }, // If not 192.168.56.1.1 goto drop
    { 0x6,  0, 0, 0x00040000 }, // Pass packet
    { 0x6,  0, 0, 0x00000000 }, // Drop

应成为(由OP自行修复的信用:)):

    // UDP check is harmless but useless
    // { 0x30, 0, 0, 0x00000009 }, // Note the offset update
    // { 0x15, 0, 3, 0x00000011 },
    { 0x20, 0, 0, 0x0000000c }, // Note the offset update
    { 0x15, 0, 1, 0xc0a83865 },
    { 0x6,  0, 0, 0x00040000 },
    { 0x6,  0, 0, 0x00000000 },