Ipheader программы XDP, данные, путаница nh_off - PullRequest
1 голос
/ 06 октября 2019

Я сейчас изучаю XDP-коды, и у меня возникла путаница в отношении того, как программы подходят к определенным частям заголовка пакета. Так! Когда я смотрю на код, который получает IP-адрес пакета, он выглядит следующим образом:

static inline int parse_ipv4(void *data, u64 nh_off, void *data_end) {
struct iphdr *iph = data + nh_off;

if ((void*)&iph[1] > data_end)
    return 0;
    return iph->protocol;
}

Теперь вот некоторые вещи, которые меня смущают:

struct iphdr *iph = data + nh_off;
  1. Я думал, nh_off - это значение смещения для следующего заголовка, поэтому, если вы добавите data + nh_off, разве это не приведет вас к следующему пакету? Потому что, насколько я понимаю, если вы добавите следующее смещение заголовка к данным, должен быть следующий пакет, ожидающий обработки!

  2. Что означает

    (void*)&iph[1]

    точно? Я пытался угадать, что эта строка кода делает в течение нескольких дней, но у меня все еще нет никакой подсказки.

Мне очень жаль, если мои вопросы слишком поглощены или расплывчаты. меня это беспокоило некоторое время, и я был бы очень признателен, если бы кто-то поделился со мной своими знаниями. Заранее большое спасибо.

1 Ответ

3 голосов
/ 06 октября 2019

Все зависит от вашего кода, так как я не вижу, как nh_off определяется в вашем случае. Но в большинстве случаев он указывает на следующий заголовок, поэтому мы будем иметь:

  1. nh_off - смещение следующего заголовка после анализа заголовка Ethernet, т. Е. nh_off - это смещение заголовка IP в пакете (на данном этапе обычно устанавливается значение 14, число байтов в заголовке Ethernet, если не используется VLAN / encap).

    Установка struct iphdr *iph = data + nh_off; объявляет и инициализирует iph как указатель struct iphdr, поэтому мы можем использовать его впоследствии, чтобы легко добраться до каждого поля из заголовка IPv4. Он указывает на data + nh_off, то есть начало пакета плюс смещение, с которого начинается заголовок IPv4 в пакете.

    Следующий пакет, подлежащий обработке, недоступен из вашей программы eBPF;вы получите новый ctx с указателем data, указывающим на него, когда этот новый пакет обрабатывается с новым вызовом программы BPF, но вы видите только один пакет одновременно.

  2. То есть iph указывает на начало вашего заголовка IPv4. Мы можем использовать этот указатель, чтобы легко добраться до отдельных полей (например, iph->protocol, чтобы получить протокол L4). Но прежде чем мы это сделаем, мы должны убедиться, что пакет достаточно длинный и действительно содержит эти поля. В противном случае мы могли бы сделать внешний доступ (следовательно, верификатор отклонил бы программу в первую очередь). Вот проверка, которую мы делаем здесь: if ((void*)&iph[1] > data_end) return 0;

    В этой проверке (void*)&iph[1] означает: i) Рассмотрим массив struct iphdr * (&iph, указатель на указатель на struct iphdr),ii) Возьмите вторую ячейку этого массива, например, адрес структуры, указанной вторым struct iphdr *, например, адрес байта, который начинается сразу после первого struct iphdr в пакете. И iii) приведите его как void *, чтобы мы могли сравнить его с data_end. Другими словами, это способ сравнить data_end (адрес в памяти сразу после последнего байта пакета) и адрес байта сразу после заголовка IPv4 (поэтому, возможно, первый байт L4 - это пакет достаточно длинный),Если (void*)&iph[1] больше data_end, то рассматриваемый нами заголовок IPv4 длиннее, чем фактический полученный нами пакет, и мы не можем позволить разыменовать iph, чтобы попытаться достичь, например, поля protocol.

С диаграммой, может быть:

Packet data

| Ethernet     | IPv4               | IPv4 data (e.g. L4, data)       |
+--------------+--------------------+------ ... ----------------------+
^              ^                    ^                                 ^
data           data + nh_off        |                                 data_end
               iph                  |
               &iph[0]              &iph[1]

У нас была бы проблема с доступом к iph->protocol, если бы вместо этого было следующее (вот почему мы return 0, если сравнениеуспешно):

Packet data

| Ethernet     | <something>   | End of packet
+--------------+----------------    +
^              ^               ^    ^
data           data + nh_off   |    |
               iph             |    |
               &iph[0]         |    &iph[1]
                               data_end
...