если NPF отбрасывает пакет (пакеты), это означает, что пакеты не будут перехвачены или пакеты не будут отправлены / получены?
Пакет не будет перехвачен, но пакет все равно будет доставлен в остальную часть сетевого стека. Это, я полагаю, по двум причинам:
Инструменты сбора пакетов обычно используются для диагностики, поэтому они придерживаются философии "не усложняй ситуацию". Все известные мне средства захвата пакетов позволят пакетам проходить мимо них, даже если они не в состоянии справиться.
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.