Продолжаем начатый цикл статей о NDIS. В этом топике рассмотрим версии NDIS, и принципы передачи пакетов между драйверами «стека» и архитектуру NET_BUFFER.
Для начала разберемся с версиями NDIS и их особенностями в связи с тем, что их чуточку больше чем одна.
Старый хлам в виде W2K с его NDIS 4.0 я трогать не буду, т.к. смысла это поддерживать нет. Так же майкрософт прекращает поддержку NDIS 5.1 в Windows 8.
Как видно из списка огромная пропасть располагается между NDIS 5.x и NDIS 6.x. Фактически, из обшего у них остались только архитектурная особенность и библиотека функций. Вся остальная начинка была перебрана для повышения производительности. Кстати, этого повышения наисследовали 20%, так что хоть в чем-то Vista лучше предшественника. К архитектуре добавились драйверы-фильтры, которых так не хватало в предыдущих версиях. Принципы передачи пакетов кардинально изменились, а именно: если раньше промежуточный драйвер должен был реализовывать как минимум по три функции приёма и отправки пакетов, то сейчас количество этих функций в любом случае равняется одной. Это стало возможно благодаря новой архитектуре передачи пакетов, называемой NET_BUFFER. О ней поговорим ниже.
Итак, что же это такое? По сути, NET_BUFFER — это замена предшествующему NDIS_BUFFER'y, но обо всём по порядку. Что было? Драйвер реализует по три функции отправки и приема пакетов. Говоря русским языком назовём эти функции так:
Понятное дело, что для приёма одного пакета небходимо было выполнить кучу действий. А для промежуточных драйверов так вообще, разобрать пакет до косточек, собрать свой (ибо нельзя трогать то, что и так не твоё) и отправить. Поэтому в NDIS 6.x ввели архитектуру NET_BUFFER.
Согласно это архитектуре достаточно реализовать по 2 функции на прием и отправку пакета (теперь он не пакет, а буфер, поэтому ниже воспринимайте эти слова как синонимы, когда речь идёт о NDIS 6+). Да, я приврал по поводу 2х, но эти две не делают ничего особенного и кочуют из драйвера в драйвер без изменений. Итак, функции приема и отправки, напишу тут их сигнатуры для общего понимания:
Первые параметры — по сути указатель, который будет передавать вам NDIS. При регистрации драйвера, а точнее при его присоединении к очереди вы можете указать этот самый указатель. Т.о. у нас в распоряжении всегда находится ссылка на пользовательскую переменную, у которой, как известно, безграничная сфера применения. Очень удобно.
Вторые параметры — указатели на списки NET_BUFFER'ов. С этим разберемся позже.
Все остальные параметры используются редко, поэтому их рассматривать мы не будем.
Переходим к самой архитектуре.
Картинка ниже примерно обрисовывает архитектуру:
Каждый NET_BUFFER может описывать пакет так, как на картинке ниже:
В заштрихованном пространстве и хранится наш пакет. Как видите, это не обязательно непрерывный блок памяти, один пакет может быть разбросан по всему системному адресному пространству. Так же советуется хранить пакеты в невыгружаемой памяти (для справки: при попытке доступа к выгружаемой памяти на 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
До следующего раза.
Введение
Для начала разберемся с версиями 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'ов. С этим разберемся позже.
Все остальные параметры используются редко, поэтому их рассматривать мы не будем.
Переходим к самой архитектуре.
Картинка ниже примерно обрисовывает архитектуру:
Каждый NET_BUFFER может описывать пакет так, как на картинке ниже:
В заштрихованном пространстве и хранится наш пакет. Как видите, это не обязательно непрерывный блок памяти, один пакет может быть разбросан по всему системному адресному пространству. Так же советуется хранить пакеты в невыгружаемой памяти (для справки: при попытке доступа к выгружаемой памяти на 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
До следующего раза.