Как я могу прочитать полученные пакеты с помощью драйвера фильтра NDIS?

В настоящее время я экспериментирую с примерами драйвера NDIS. Я пытаюсь распечатать содержимое пакетов (включая MAC-адреса, EtherType и данные).

Моей первой догадкой было реализовать это в функции FilterReceiveNetBufferLists. К сожалению, я не знаю, как извлечь содержимое пакетов из файла NetBufferLists.


person Gigliotti    schedule 22.07.2020    source источник


Ответы (1)


Это правильное место для начала. Рассмотрим этот код:

void FilterReceiveNetBufferLists(..., NET_BUFFER_LIST *nblChain, ...)
{
    UCHAR buffer[14];
    UCHAR *header;

    for (NET_BUFFER_LIST *nbl = nblChain; nbl; nbl = nbl->Next) {
        header = NdisGetDataBuffer(nbl->FirstNetBuffer, sizeof(buffer), buffer, 1, 1);
        if (!header)
            continue;

        DbgPrint("MAC address: %02x-%02x-%02x-%02x-%02x-%02x\n",
            header[0], header[1], header[2],
            header[3], header[4], header[5]);
    }

    NdisFIndicateReceiveNetBufferLists(..., nblChain, ...);
}

Есть несколько моментов, которые следует учитывать в отношении этого кода.

Путь данных NDIS использует NET_BUFFER_LIST (nbl) в качестве своей первичной структуры данных. NBL представляет собой набор пакетов с одинаковыми метаданными. Что касается пути приема, то о метаданных мало что известно, поэтому в этом наборе всегда ровно 1 пакет. Другими словами, nbl — это список... длины 1. Для пути получения вы можете рассчитывать на него.

nbl — это список одной или нескольких структур NET_BUFFER (nb). nb представляет одиночный сетевой кадр (с учетом LSO или RSC). Таким образом, nb наиболее точно соответствует тому, что вы считаете пакетом. Его метаданные хранятся в nbl, который его содержит.

Внутри nb фактическая полезная нагрузка пакета хранится в виде одного или нескольких буферов, каждый из которых представлен в виде MDL. Мысленно вы должны представить, что MDL просто объединены вместе. Например, сетевые заголовки могут быть в одном MDL, а остальная часть полезной нагрузки может быть в другом MDL.

Наконец, для повышения производительности NDIS предоставляет вашему LWF как можно больше NBL. Это означает, что существует список из одного или нескольких NBL.

Сложите все вместе, и у вас есть:

  • Ваша функция получает список NBL.
  • Каждый NBL содержит ровно 1 NB (на пути приема).
  • Каждый NB содержит список MDL.
  • Каждый MDL указывает на буфер полезной нагрузки.

Таким образом, в приведенном выше примере кода цикл for выполняет итерацию по первому пункту: цепочке NBL. Внутри цикла нам нужно смотреть только на nbl->FirstNetBuffer, так как мы можем с уверенностью предположить, что нет другого nb, кроме первого.

Неудобно возиться со всеми этими MDL напрямую, поэтому мы используем вспомогательную процедуру NdisGetDataBuffer. Вы говорите этому парню, сколько байт полезной нагрузки вы хотите видеть, и он даст вам указатель на непрерывный диапазон полезной нагрузки.

  • В хорошем случае ваш буфер содержится в одном MDL, поэтому NdisGetDataBuffer просто возвращает вам указатель на буфер этого MDL.
  • В медленном случае ваш буфер охватывает более одного MDL, поэтому NdisGetDataBuffer тщательно копирует соответствующий бит полезной нагрузки в предоставленный вами временной буфер.

Последний случай может быть неудобным, если вы пытаетесь проверить больше, чем несколько байтов. Если вы читаете все 1500 байтов пакета, вы не можете просто выделить 1500 байтов в стеке (место стека ядра ограничено, в отличие от пользовательского режима), поэтому вы должны выделить его из пула. Как только вы это выясните, обратите внимание, что копирование всех 1500 байт данных в буфер для каждого пакета замедлит работу. Замедление слишком велико? Это зависит от ваших потребностей. Если вы проверяете только отдельные пакеты или развертываете LWF на сетевом адаптере с низкой пропускной способностью, это не имеет значения. Если вы пытаетесь выйти за пределы 1 Гбит / с, вам не следует запоминать так много данных.

Также обратите внимание, что если вы в конечном итоге захотите изменить пакет, вам нужно опасаться NdisGetDataBuffer. Он может дать вам копию данных (хранящуюся в вашем локальном буфере), поэтому, если вы измените полезную нагрузку, эти изменения фактически не сохранятся в пакете.

Что делать, если вам нужно увеличить пропускную способность или изменить полезную нагрузку? Затем вам нужно выяснить, как манипулировать цепочкой MDL. Поначалу это немного сбивает с толку, но потратьте немного времени на документацию и нарисуйте несколько диаграмм на доске.

Я предлагаю сначала начать с понимания MDL. С сетевой точки зрения MDL — это просто причудливый способ хранения { char * buffer, size_t length } вместе со ссылкой на следующий MDL.

Далее рассмотрим DataOffset и DataLength NB. Они концептуально перемещают границы буфера из начала и конца буфера. Они не заботятся о границах MDL — например, вы можете уменьшить длину полезной нагрузки пакета, уменьшив DataLength, и если это означает, что один или несколько MDL больше не вносят никакого буферного пространства в полезную нагрузку пакета, это не так. большое дело, они просто игнорируются.

Наконец, добавьте поверх CurrentMdl и CurrentMdlOffset. Они избыточны со всем вышеперечисленным, но они существуют для (микробенчмарка) производительности. Вам не нужно даже думать о них, если вы читаете NB, но если вы редактируете размер NB, вам необходимо их обновить.

person Jeffrey Tippet    schedule 23.07.2020
comment
Ответ мне очень помог. Большое спасибо! - person Gigliotti; 23.07.2020