Почему я не получаю пакеты, использующие BPF с сокетом udp?

ЦЕЛЬ: написать фильтр BPF, который разрешает только UDP-пакеты с определенного адреса src, и прикреплять его к UDP-сокету.

ПРОБЛЕМА: если я запускаю программу и пытаюсь отправить udp-пакеты с виртуальной машины с правильным IP-адресом src, я не получаю ни одного из них.

#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;
}

person Maicake    schedule 25.07.2019    source источник
comment
См. этот ответ   -  person Qeole    schedule 25.07.2019
comment
Возможный дубликат классического BPF в Linux: фильтр не работает   -  person Qeole    schedule 25.07.2019
comment
Итак, если я хорошо понял в моем случае, поскольку я создаю сокет UDP, проверки начинаются с заголовка IP, а не с заголовка Ethernet, потому что это не сырой сокет, поэтому уровень передачи данных удаляется, верно?   -  person Maicake    schedule 25.07.2019
comment
Я бы модифицировал фильтр таким образом. {0x30, 0, 0, 0x00000009}, {0x15, 0, 3, 0x00000011}, {0x20, 0, 0, 0x0000000c}, {0x15, 0, 1, 0xc0a83865}, {0x6, 0, 0, 0x00040000}, {0x6, 0, 0, 0x00000000},   -  person Maicake    schedule 25.07.2019
comment
Точно. Я предполагаю, что вы создали свой фильтр BPF с tcpdump, который использует сырые сокеты и должен анализировать заголовки L2 / L3, но с вашим SOCK_DGRAM, IPPROTO_UDP вы можете с уверенностью предположить, что уже работаете с IPv4 и UDP. Он должен работать с изменениями, которые вы предлагаете, хотя вы можете даже удалить первые две инструкции из программы в своем комментарии (они проверяют, что пакет является UDP, но мы это уже знаем) и оставить только последние четыре. Я вижу, вы также обновили смещение для исходного адреса, так что все должно быть в порядке.   -  person Qeole    schedule 25.07.2019
comment
Еще раз спасибо. Я также удалил первые две инструкции, как вы сказали, но все же программа просто не получает ни одного пакета.   -  person Maicake    schedule 25.07.2019
comment
Хм. Вы ведь отправляете пакеты с адреса 192.168.56.101, верно? Можете ли вы увидеть эти пакеты на своей виртуальной машине с помощью tcpdump udp and src 192.168.56.101? Также, в отличие от tcpdump, ваш сокет привязан к определенному порту UDP (1025), вы отправляете свои пакеты на правильный порт?   -  person Qeole    schedule 25.07.2019
comment
Да, в виртуальной машине (192.168.56.101) я отправляю пакеты, используя nc -u 192.168.56.1 1025, и я вижу эти пакеты также из виртуальной машины, выполняющей tcpdump udp и src 192.168.56.101, а также с хост-машины с теми же командами. Это просто программа на C, которая не работает должным образом   -  person Maicake    schedule 25.07.2019
comment
Ваша nc команда не имеет правильного IP-адреса, это опечатка или ошибка в вашей настройке?   -  person Qeole    schedule 25.07.2019
comment
Я отправил пакеты с виртуальной машины (101) на хост (1), на котором запущена программа C.   -  person Maicake    schedule 25.07.2019
comment
Да, глупый я ›‹. Хорошо, нашел вашу проблему. Я не понимал, что ваш сокет SOCK_DGRAM на самом деле был даже более высоким уровнем, чем я думал, ваш пакет начинается непосредственно с заголовка L4 (например, { 0x28, 0, 0, 0x00000002 }, { 0x15, 0, 1, 0x00000401 }, { 0x6, 0, 0, 0x00040000 }, { 0x6, 0, 0, 0x00000000 },, поскольку программа соответствует порту UDP dst и работает). Вам нужно изменить тип сокета для фильтрации по IP-адресу, возможно, см. Пример в старом посте.   -  person Qeole    schedule 25.07.2019
comment
Хорошо, если я понимаю, что пакет состоит только из заголовка UDP + данные, поэтому я не могу получить доступ к информации заголовка IP. Следовательно, я могу просто фильтровать данные поля UDP. Я видел, что в другом примере используется RAW_SOCK. Обязательно ли использовать его для фильтрации полей заголовков IP? Большое спасибо @Qeole, я очень ценю вашу помощь.   -  person Maicake    schedule 25.07.2019
comment
Да, я пробовал код и только что получил данные L4 +. Это правильно, я думаю, вам понадобится SOCK_RAW, если вы хотите получить доступ к заголовкам L3.   -  person Qeole    schedule 25.07.2019
comment
как ты это нашел? Wireshark или аналогичный инструмент?   -  person Maicake    schedule 25.07.2019
comment
Я запустил ваш фрагмент кода и сделал несколько попыток фильтрации, изменив программу: пробовал значения, которые, как я знал, мог найти для L3, для данных, затем для L4 ... L4 работал :). Я отредактировал ваш вопрос, чтобы фрагмент скомпилировался (#includes), кстати.   -  person Qeole    schedule 25.07.2019
comment
Еще и еще раз спасибо. Уже почти пора переехать и попробовать eBPF, но глядя на эту сборку, я боюсь XD.   -  person Maicake    schedule 25.07.2019
comment
:) Не пишите сборку eBPF, напишите свою программу на C и скомпилируйте ее в eBPF с помощью clang / LLVM!   -  person Qeole    schedule 25.07.2019


Ответы (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 },
person Qeole    schedule 25.07.2019
comment
Я хотел бы лучше понять, как обрабатывается пакет, когда я использую сокет с фильтром cbpf. Пакет поступает от сетевого устройства, теперь, в зависимости от типа сокета, пакет обрабатывается в протоколах стека, которые соответственно удаляют заголовки нижних уровней, а затем cbpf воздействует на данные, полученные в результате этих шагов? Извините, если мои условия неверны. - person Maicake; 26.07.2019
comment
Да, это идея, хотя пакет все равно обрабатывается в стеке, и это больше похоже на «крючки» в различных точках стека ядра, чем на исходное решение действительно подготовить все для сокета дейтаграммы. И да, ваша программа cBPF запускается, когда пакет достигает сокета. - person Qeole; 29.07.2019
comment
Еще раз спасибо, linuxjournal.com/files/linuxjournal .com / linuxjournal / article / это представление все еще в силе? Я имею в виду, что это статья 2002 года. - person Maicake; 29.07.2019
comment
У вас не все на этой диаграмме (например, XDP, управление трафиком, перехватчики netfilter), но да, основы остаются прежними. В качестве дополнения вы можете посмотреть этот, хотя он нижние уровни, и у вас не так много розеток. - person Qeole; 30.07.2019