Как работает захват пакетов (NPF; WinPcap)? - PullRequest
1 голос
/ 10 апреля 2019

Я читал о том, как WinPcap фильтрует пакеты здесь и исследовал ndis / filter project из GitHub ( Microsoft / Windows-драйвер-примеры ). Я предоставляю основные цифры со страницы WinPcap ниже, потому что они связаны с вопросом.

enter image description here


enter image description here

Мой главный вопрос: если NPF отбрасывает пакет (пакеты), это означает, что пакеты не будут перехвачены или пакеты не будут отправлены / получены? Например (как я вижу):

  1. dumpcap запускает прослушивание пакетов на eth0 .
  2. Chrome отправляет DNS-запрос.
  3. Драйвер NDIS обрабатывает этот пакет. (если быть точным - NetBufferList ; через SendNetBufferListsHandler и SendNetBufferListsCompleteHandler функции)
  4. Разбор NBL: анализ каждого буфера и проверка характеристик пакета;
  5. Если мы хотим отбросить пакет, нам нужно собрать новый NBL (без нежелательных пакетов) и вызвать SendNetBufferListsCompleteHandler с новым NBList;
  6. Если мы хотим исключить этот пакет из захвата, нам нужно собрать новый NBL (без нежелательных пакетов) и вызвать NdisFIndicateReceiveNetBufferLists с новым NBList;

Тот же случай с получением пакетов (через функции ReceiveNetBufferListsHandler и ReturnNetBufferListsHandler ).

Правильно ли я понимаю, что есть возможность отбросить пакет с помощью NPF, отправить / recv it в / из сети и удалить его из "списка перехвата пакетов" ?

Если да, как правильно реализовать отбрасывание пакетов?

Я не нашел примеров кода для отбрасывания пакетов через SendNetBufferListsHandler / SendNetBufferListsCompleteHandler и ReceiveNetBufferListsHandler / ReturnNetBufferListsHandler . *

1 Ответ

2 голосов
/ 13 апреля 2019

если NPF отбрасывает пакет (пакеты), это означает, что пакеты не будут перехвачены или пакеты не будут отправлены / получены?

Пакет не будет перехвачен, но пакет все равно будет доставлен в остальную часть сетевого стека. Это, я полагаю, по двум причинам:

  1. Инструменты сбора пакетов обычно используются для диагностики, поэтому они придерживаются философии "не усложняй ситуацию". Все известные мне средства захвата пакетов позволят пакетам проходить мимо них, даже если они не в состоянии справиться.

  2. NPF (он же winpcap / wireshark) спроектирован таким образом, что предотвращает блокирование / отбрасывание трафика. Даже если вы захотите внести несколько изменений в NPF, вы не сможете этого сделать. Причина в том, что NPF реализован как драйвер протокола. Как драйвер протокола, это peer TCPIP, и он не может напрямую влиять на то, что делает TCPIP. (Это маленькое чудо, что NPF может даже видеть, что передает TCPIP - это делается с помощью магии loopback уровня 2. [Не относится к loopback уровня 3, как :: 1 и 127.0.0.1 и т. Д.]))

Проект nmap имеет форк NPF, который реализует его как драйвер фильтра NDIS. Этот тип драйвера способен блокировать, задерживать, переписывать или вводить трафик. Так что если вы заинтересованы в изменении философии №1 выше, вы должны начать с форка nmap, а не с «официального» winpcap.

(И вообще, я лично рекомендовал бы разветвление nmap, даже если вам не нужно отбрасывать трафик. Драйверы фильтра будут намного быстрее, чем драйвер протокола, который переводит сетевой адаптер в режим обратной связи уровня 2. )

Как только вы посмотрите на nmap-npf, вы сможете найти обратные вызовы из драйвера примера фильтра NDIS, например FilterReceiveNetBufferLists.

Отбрасывать пакеты на самом деле довольно легко;) Однако есть некоторые ошибки, поэтому давайте рассмотрим несколько примеров.

На пути передачи у нас есть связанный список NBL, и мы хотим разделить его на два списка, один для отбрасывания и один для продолжения отправки. Один NBL может содержать несколько пакетов, но гарантированно, что каждый пакет имеет один и тот же «поток» (например, сокет TCP). Поэтому обычно вы можете сделать упрощенное предположение, что каждый пакет в NBL всегда обрабатывается одинаково: если вы хотите отбросить один, вы хотите отбросить их все.

Если это предположение не true, т. Е. Если вы хотите выборочно отбрасывать некоторые пакеты из сокета TCP, но не все пакеты, то вам нужно сделать что-то более сложное. Вы не можете напрямую удалить один NET_BUFFER из NET_BUFFER_LIST; вместо этого вы должны клонировать NET_BUFFER_LIST и скопировать NET_BUFFER, которые вы хотите сохранить.

Поскольку это бесплатный форум, я приведу лишь пример простого и распространенного случая;)

void
FilterSendNetBufferLists(NET_BUFFER_LIST *nblChain, ULONG sendFlags)
{
    NET_BUFFER_LIST *drop = NULL;
    NET_BUFFER_LIST *keep = NULL;

    NET_BUFFER_LIST *next = NULL;
    NET_BUFFER_LIST *nbl = NULL;

    for (nbl = nblChain; nbl != NULL; nbl = next) {
        next = nbl->Next;

        // If the first NB in the NBL is drop-worthy, then all NBs are
        if (MyShouldDropPacket(nbl->FirstNetBuffer)) {
            nbl->Next = drop;
            drop = nbl;
            nbl->Status = NDIS_STATUS_FAILURE; // tell the protocol
        } else {
            nbl->Next = keep;
            keep = nbl;
        }
    }

    // Above would reverse the order of packets; let's undo that here.
    keep = ReverseNblChain(keep);

    . . . do something with the NBLs you want to keep. . .;

    // Send the keepers down the stack to be transmitted by the NIC.
    NdisFSendNetBufferLists(context, keep, portNumber, sendFlags);

    // Return the dropped packets back up to whoever tried to send them.
    NdisFSendCompleteNetBufferLists(context, drop, 0);
}

На пути получения вы гарантированно имеете только один NET_BUFFER на NET_BUFFER_LIST. (Сетевая карта не может полностью знать, какие пакеты являются частью одного и того же потока, поэтому группирование еще не выполнено.) Итак, небольшая ошибка исчезла, но есть новая ошибка: вы должны проверить NDIS_RECEIVE_FLAGS_RESOURCES флаг. Отсутствие проверки этого флага является причиной № 1 потери потерянного времени в погоне за ошибками в драйверах фильтров, поэтому я должен сделать из этого много.

void
FilterReceiveNetBufferLists(NET_BUFFER_LIST *nblChain, ULONG count, ULONG receiveFlags)
{
    NET_BUFFER_LIST *drop = NULL;
    NET_BUFFER_LIST *keep = NULL;

    NET_BUFFER_LIST *next = NULL;
    NET_BUFFER_LIST *nbl = NULL;

    for (nbl = nblChain; nbl != NULL; nbl = next) {
        next = nbl->Next;

        // There's only one packet in the NBL
        if (MyShouldDropPacket(nbl->FirstNetBuffer)) {
            nbl->Next = drop;
            drop = nbl;
            count -= 1; // decrement the NumberOfNetBufferLists
        } else {
            nbl->Next = keep;
            keep = nbl;
        }
    }

    keep = ReverseNblChain(keep);

    . . . do something with the NBLs you want to keep. . .;

    // Pass the keepers up the stack to be processed by protocols.
    NdisFIndicateReceiveNetBufferLists(context, keep, portNumber, count, receiveFlags);

    // Checking this flag is critical; never ever call
    // NdisFReturnNetBufferLists if the flag is set.
    if (0 == (NDIS_RECEIVE_FLAGS_RESOURCES & receiveFlags)) {
        NdisFReturnNetBufferLists(context, keep, 0);
    }
}

Обратите внимание, что я использовал вспомогательную функцию с именем ReverseNblChain. Технически законно менять порядок пакетов, но это снижает производительность. TCPIP может достичь своей максимальной производительности только тогда, когда пакеты обычно приходят в порядке. Цикл манипуляции со связанным списком в примере кода имеет побочный эффект обращения списка NBL, поэтому мы устраняем повреждение с помощью ReverseNblChain. Нам не нужно переворачивать цепочку отбрасывания, так как никто не пытается пересобрать отброшенные пакеты; Вы можете оставить их в любом порядке.

NET_BUFFER_LIST * ReverseNblChain(NET_BUFFER_LIST *nblChain)
{
    NET_BUFFER_LIST *head = NULL;
    NET_BUFFER_LIST *next = NULL;
    NET_BUFFER_LIST *nbl = NULL;

    for (nbl = nblChain; nbl != NULL; nbl = next) {
        next = nbl->Next;
        nbl->Next = head;
        head = nbl;
    }

    return head;
}

Наконец, если вы читаете это из нескольких лет в будущем, я предлагаю вам найти образец файла заголовка от Microsoft с именем nblutil.h.(Мы еще не публиковали его, но я над ним работаю.) У него есть очень хорошая подпрограмма с именем ndisClassifyNblChain, которая выполняет почти всю работу за вас.Он разработан для высокой масштабируемости и использует несколько приемов для лучшей производительности, чем то, что вы найдете в уже длинном ответе StackOverflow.

...