ловушка netfilter не получает полный пакет - PullRequest
0 голосов
/ 22 октября 2019

Я пишу модуль сетевого фильтра, который глубоко проверяет пакет. Однако во время тестов я обнаружил, что модуль netfilter не получает пакет в полном объеме.

Чтобы проверить это, я написал следующий код для выгрузки пакета, полученного через порт 80, и записи результата в буфер dmesg:

const struct iphdr *ip_header = ip_hdr(skb);
if (ip_header->protocol == IPPROTO_TCP)
{
    const struct tcphdr *tcp_header = tcp_hdr(skb);
    if (ntohs(tcp_header->dest) != 80)
    {
        return NF_ACCEPT;
    }

    buff = (char *)kzalloc(skb->len * 10, GFP_KERNEL);
    if (buff != NULL)
    {
        int pos = 0, i = 0;
        for (i = 0; i < skb->len; i ++)
        {
            pos += sprintf(buff + pos, "%02X", skb->data[i] & 0xFF);
        }

        pr_info("(%pI4):%d --> (%pI4):%d, len=%d, data=%s\n",
            &ip_header->saddr,
            ntohs(tcp_header->source),
            &ip_header->daddr,
            ntohs(tcp_header->dest),
            skb->len,
            buff
        );
        kfree (buff);
    }
}

В виртуальной машине, работающей локально, я могу получить полный HTTP-запрос;В облаке Alibaba и некоторых других VPS-провайдерах на основе OpenStack пакет обрезается посередине.

Чтобы проверить это, я выполняю curl http://VPS_IP на другом VPS и получаю следующий вывод в буфере dmesg:

[ 1163.370483] (XXXX):5007 --> (XXXX):80, len=237, data=451600ED000040003106E3983D87A950AC11D273138F00505A468086B44CE19E80180804269300000101080A1D07500A000D2D90474554202F20485454502F312E310D0A486F73743A2033392E3130372E32342E37370D0A4163636570743A202A2F2A0D0A557365722D4167656E743A204D012000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000001E798090F5FFFF8C0000007B00000000E0678090F5FFFF823000003E00000040AE798090F5FFFF8C0000003E000000000000000000000000000000000000000000000000000000000000

При декодировании результат выглядит примерно так:

enter image description here

Это совершенно странно, все после User-Agent: M "пропало"или ноль ред. Хотя skb-> len равен 237, но половина пакета отсутствует.

Есть идеи? Пробовал оба PRE_ROUTING и LOCAL_IN, без изменений.

1 Ответ

1 голос
/ 23 октября 2019

Кажется, что иногда вы получаете линейный skb, а иногда ваш skb не линейный. В последнем случае вы не читаете полное содержимое данных skb.

Если skb->data_len равно нулю, тогда ваш skb является линейным, а полное содержимое данных skb находится в skb->data. Если skb->data_len не равно нулю, тогда ваш skb не является линейным, а skb->data содержит только первую (линейную) часть данных. Длина этой области skb->len - skb->data_len. skb_headlen() вспомогательная функция рассчитывает это для удобства. skb_is_nonlinear() Вспомогательная функция сообщает в skb, является ли она линейной или нет.

Остальные данные могут быть в разбитых на фрагменты фрагментах и ​​во фрагментах skb в указанном порядке.

skb_shinfo(skb)->nr_frags говорит о количестве фрагментов. Каждый выгружаемый фрагмент описывается структурой данных в массиве структур skb_shinfo(skb)->frags[0..skb_shinfo(skb)->nr_frags]. Вспомогательные функции skb_frag_size() и skb_frag_address() помогают работать с этими данными. Они принимают адрес структуры, которая описывает выгружаемый фрагмент. Существуют и другие полезные вспомогательные функции, зависящие от версии вашего ядра.

Если общий размер данных в разбитых на фрагменты меньше, чем skb->data_len, тогда остальные данные находятся во фрагментах skb. Это список skb, который прикреплен к этому skb в skb_shinfo(skb)->frag_list (см. skb_walk_frags() в ядре).

Обратите внимание, что может быть, что в линейной части нет данных и / или нетданные в постраничных фрагментах. Вам просто нужно обрабатывать данные по частям в только что описанном порядке.

...