Pull to refresh

NDIS. NET_BUFFER architecture

Reading time4 min
Views5.6K
Продолжаем начатый цикл статей о NDIS. В этом топике рассмотрим версии NDIS, и принципы передачи пакетов между драйверами «стека» и архитектуру NET_BUFFER. image



Введение



Для начала разберемся с версиями NDIS и их особенностями в связи с тем, что их чуточку больше чем одна.

  • NDIS 5.1. Windows XP, Server 2003. Большинство драйверов написано под эту версию и портировать их никто не берется, т.к. портировать там особо и нечего — почти всё придётся переписывать заново;
  • NDIS 5.2. Windows Server 2003 SP2. Под эту версию, скорее всего не написано ни одного драйвера, т.к. тут прекрасно работают драйвера от 5.1;
  • NDIS 6.0. Windows Vista. подсистема NDIS в Windows Vista претерпела громаднейшие изменения, добавлены новые виды драйверов, улучшена производительность;
  • NDIS 6.1. Windows Vista SP2, Server 2008. Та же история, что и с NDIS 5.2;
  • NDIS 6.20. Windows 7. Небольшие изменения относительно NDIS 6.0, поддержка последней в режиме эмуляции.


Старый хлам в виде W2K с его NDIS 4.0 я трогать не буду, т.к. смысла это поддерживать нет. Так же майкрософт прекращает поддержку NDIS 5.1 в Windows 8.
Как видно из списка огромная пропасть располагается между NDIS 5.x и NDIS 6.x. Фактически, из обшего у них остались только архитектурная особенность и библиотека функций. Вся остальная начинка была перебрана для повышения производительности. Кстати, этого повышения наисследовали 20%, так что хоть в чем-то Vista лучше предшественника. К архитектуре добавились драйверы-фильтры, которых так не хватало в предыдущих версиях. Принципы передачи пакетов кардинально изменились, а именно: если раньше промежуточный драйвер должен был реализовывать как минимум по три функции приёма и отправки пакетов, то сейчас количество этих функций в любом случае равняется одной. Это стало возможно благодаря новой архитектуре передачи пакетов, называемой NET_BUFFER. О ней поговорим ниже.

NET_BUFFER architecture


Итак, что же это такое? По сути, NET_BUFFER — это замена предшествующему NDIS_BUFFER'y, но обо всём по порядку. Что было? Драйвер реализует по три функции отправки и приема пакетов. Говоря русским языком назовём эти функции так:

  • ПринятьПакет;
  • ПринятьПакеты;
  • ПодтвердитьПриемПакетов;
  • ОтправитьПакет;
  • ОтправитьПакеты;
  • ПодвердитьОтправкуПакета.


Понятное дело, что для приёма одного пакета небходимо было выполнить кучу действий. А для промежуточных драйверов так вообще, разобрать пакет до косточек, собрать свой (ибо нельзя трогать то, что и так не твоё) и отправить. Поэтому в NDIS 6.x ввели архитектуру NET_BUFFER.
Согласно это архитектуре достаточно реализовать по 2 функции на прием и отправку пакета (теперь он не пакет, а буфер, поэтому ниже воспринимайте эти слова как синонимы, когда речь идёт о NDIS 6+). Да, я приврал по поводу 2х, но эти две не делают ничего особенного и кочуют из драйвера в драйвер без изменений. Итак, функции приема и отправки, напишу тут их сигнатуры для общего понимания:

VOID
  FilterSendNetBufferLists(
    IN NDIS_HANDLE  FilterModuleContext,
    IN PNET_BUFFER_LIST  NetBufferLists,
    IN NDIS_PORT_NUMBER  PortNumber,
    IN ULONG  SendFlags
    ); 

VOID
  FilterReceiveNetBufferLists(
    IN NDIS_HANDLE  FilterModuleContext,
    IN PNET_BUFFER_LIST  NetBufferLists,
    IN NDIS_PORT_NUMBER  PortNumber,
    IN ULONG  NumberOfNetBufferLists,
    IN ULONG  ReceiveFlags
    );


Первые параметры — по сути указатель, который будет передавать вам NDIS. При регистрации драйвера, а точнее при его присоединении к очереди вы можете указать этот самый указатель. Т.о. у нас в распоряжении всегда находится ссылка на пользовательскую переменную, у которой, как известно, безграничная сфера применения. Очень удобно.
Вторые параметры — указатели на списки NET_BUFFER'ов. С этим разберемся позже.
Все остальные параметры используются редко, поэтому их рассматривать мы не будем.

Переходим к самой архитектуре.
Картинка ниже примерно обрисовывает архитектуру:

image

Каждый NET_BUFFER может описывать пакет так, как на картинке ниже:
image

В заштрихованном пространстве и хранится наш пакет. Как видите, это не обязательно непрерывный блок памяти, один пакет может быть разбросан по всему системному адресному пространству. Так же советуется хранить пакеты в невыгружаемой памяти (для справки: при попытке доступа к выгружаемой памяти на IRQL >= DISPATCH_LEVEL мы получим синий экран с кодом D1. DRIVER_IRQL_NOT_LESS_OR_EQUAL, самая частая ошибка драйверо-писателей).

Сразу вопрос, зачем оставлять пустое пространство перед пакетом? Затем, что когда IP-пакет спускается вниз, то он оборачивается в Ethernet-фрейм для отправки. И чтобы это дело не пересобирать, Ethernet-заголовок просто записывается передпакетом. Достаточно продуманно. Тот же принцип реализован и в драйверах верхнего уровня. Вся информация о т.н. backfill (оно же DataOffset) пространстве, смещениях и длине буфера хранится в структуре NET_BUFFER. Так же хочу отметить, что размер буфера можно менять, т.е. начало буфера можно отодвинуть влево, или конец вправо. Для этого используются специальные функции, экспортируемые ядром NDIS.

Вместо заключения


В этой статье намерено упущено огромное количество деталей, с которыми я столкнулся в процессе изучения подсистемы NDIS. Их я не публикую, т.к. количество материала, при этом, не уместилось бы ни в какие разумные рамки. Одновременно я стараюсь пролить свет на некоторые key features, которые считаю базовыми для понимания сути происходящего.

Дополнительную литературу читаем тут:

www.codemachine.com/article_ndis6nbls.html
msdn.microsoft.com/en-us/library/ff564881(v=vs.85).aspx

До следующего раза.
Tags:
Hubs:
Total votes 40: ↑33 and ↓7+26
Comments10

Articles