Написать программу фильтрации пакетов в ebpf

Если я хочу написать программу cBPF, которая фильтрует пакеты icmp, я могу сделать это, выполнив tcpdump с параметром -dd, который

Дамп кода сопоставления пакетов в виде фрагмента программы на C.

.. см. пример ниже

Как я могу написать ту же программу с инструкциями eBPF?

#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <linux/if_ether.h>
/* ... */

/* From the example above: tcpdump -i lo icmp -dd */
struct sock_filter code[] = {
{ 0x28, 0, 0, 0x0000000c },
{ 0x15, 0, 3, 0x00000800 },
{ 0x30, 0, 0, 0x00000017 },
{ 0x15, 0, 1, 0x00000001 },
{ 0x6, 0, 0, 0x00040000 },
{ 0x6, 0, 0, 0x00000000 },
};

struct sock_fprog bpf = {
    .len = ARRAY_SIZE(code),
    .filter = code,
};

sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (sock < 0)
    /* ... bail out ... */

ret = setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &bpf, sizeof(bpf));
if (ret < 0)
    /* ... bail out ... */

/* ... */
close(sock);

person Maicake    schedule 15.07.2019    source источник


Ответы (1)


Вы можете передавать инструкции eBPF способом, очень похожим на то, что вы делаете для своей программы cBPF: у вас может быть что-то вроде

struct bpf_insn ebpf_code[] = {
{ 0xb7, 0, 0, 0, 0 },
{ 0x95, 0, 0, 0, 0 },
};

(Обратите внимание, что длина инструкций не такая, как для cBPF.)

Но в настоящее время нет инструмента, который бы выдавал вам инструкции так же, как это делает tcpdump -dd. Это означает, что вы должны построить свою программу другим способом.

Одним из решений является самостоятельное написание инструкций eBPF. Это очень похоже на программирование на ассемблере. У вас есть документация по BPF в ядре или список существующие инструкции и их синтаксис здесь.

Поскольку писать отдельные инструкции eBPF вручную неинтересно, типичный рабочий процесс для eBPF несколько отличается. clang/LLVM имеет серверную часть для eBPF, и большинство программ eBPF, созданных сегодня, полагаются на нее. Рабочий процесс выглядит следующим образом:

  1. Напишите свою программу BPF на C.
  2. Скомпилируйте его с помощью clang/LLVM в объектный файл (ELF).
  3. Загрузите байт-код из объектного файла с помощью инструмента или библиотеки (ip link< /a>, tc filter, bpftool, bcc, libbpf, gobpf, ...).
  4. Вставьте байт-код в ядро ​​и прикрепите его к хуку (например, к сокету) (обычно это делается теми же инструментами или библиотеками).

В ядре есть куча примеры программ BPF, написанных на C. Вы можете посмотреть и посмотреть, можно ли адаптировать одну из них к вашим потребностям. Что вам нужно реализовать, вероятно, что-то вроде:

  1. Убедитесь, что ваш пакет достаточно длинный, чтобы иметь полный заголовок Ethernet.
  2. Убедитесь, что ethertype — IPv4.
  3. Убедитесь, что после заголовка Ethernet ваш пакет имеет достаточную длину для полного заголовка IPv4.
  4. Убедитесь, что номер IP-протокола — ICMP.

Затем верните действие, связанное с тем, что вы хотите сделать с этим пакетом (значение зависит от того, к какому хуку, сокету/TC/XDP, вы привязываете свою программу).

person Qeole    schedule 15.07.2019
comment
Спасибо за ваш ответ, я бы спросил вас еще 10000 вещей, я могу написать вам в личку? У вас есть ссылка, где я могу узнать, как написать программу ebpf на C? В смысле, я не могу найти дружелюбного гида. XD - person Maicake; 16.07.2019
comment
Конечно, не стесняйтесь присылать свои вопросы! Но имейте в виду, что здесь также неплохо задать общий вопрос, чтобы другие тоже могли извлечь пользу из ответов :). На этой странице есть куча ссылок на ресурсы BPF , некоторые более полные (руководство по Cilium в частности), некоторые более доступны. Чтобы начать работу с сетевой обработкой, я бы порекомендовал взглянуть на руководства по XDP (большинство из них относится и к другим сетевым хукам). - person Qeole; 16.07.2019
comment
Спасибо еще раз. Если бы вы могли посмотреть на это, было бы здорово заголовок stackoverflow.com/questions/57053518/ Я пытаюсь написать простую программу bpf. Но это не работает. - person Maicake; 16.07.2019
comment
У меня есть сомнения, почему я могу передавать инструкции ebpf, которые длиннее, чем cbpf, используя ту же структуру sock_filter? - person Maicake; 19.07.2019
comment
Да, мой плохой, я сделал некоторое чрезмерное упрощение. Это будет вовсе не struct sock_filter, это будет массив struct bpf_insn, как определено в /usr/include/linux/bpf.h (я отредактирую ответ), и в конечном итоге вам нужно будет загрузить его через bpf() системный вызов (для справки, libbpf предоставляет полезные помощники). У вас есть примеры в репозиторий ядра, если хотите посмотреть. Но, честно говоря, типичный рабочий процесс eBPF компилируется из C, так намного проще. - person Qeole; 19.07.2019
comment
благодарю за разъяснение. у вас есть полный пример, который также объясняет, как скомпилировать ограниченный C из дерева ядра? Или мне в любом случае нужно загрузить весь исходный код ядра? - person Maicake; 19.07.2019
comment
Вам не нужно загружать код ядра. Вы ознакомились с учебниками, на которые я ссылался? выше? Вероятно, это одно из лучших мест для начала. У нас (моей компании) также есть несколько образцов вне дерева, немного более сложных, но надеюсь, все еще доступны для новичков. - person Qeole; 19.07.2019
comment
Спасибо. Обязательно ли использовать xdp для фильтрации пакетов или только для фильтрации на более низком уровне? Я пытался связаться с вами по электронной почте, указанной на вашем веб-сайте, но я думаю, что вы ее больше не используете. - person Maicake; 19.07.2019
comment
Не обязательно использовать XDP. Альтернативой является TC (перехватчик управления трафиком) или уровень сокета. Я получил ваше письмо и отвечу, просто пока не нашел времени :). - person Qeole; 20.07.2019