IP-заголовок программы XDP, данные, путаница с nh_off

Я сейчас изучаю коды 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]

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

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


person Rosè    schedule 06.10.2019    source источник


Ответы (1)


Все зависит от вашего кода, так как я не понимаю, как в вашем случае определяется 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
person Qeole    schedule 06.10.2019
comment
Большое спасибо за ваше любезное объяснение! Мне действительно очень помогает! Кажется, теперь я наконец понял, что означают эти коды :D - person Rosè; 07.10.2019