Обработчик FilterReceiveNetBufferLists драйвера фильтра NDIS не вызывается

Я разрабатываю драйвер фильтра NDIS, и я обнаружил, что его FilterReceiveNetBufferLists никогда не вызывается (сеть заблокирована) при определенных условиях (например, откройте Wireshark или нажмите кнопку «Список интерфейсов»). Но когда я начинаю захват, FilterReceiveNetBufferLists становится нормальным (сеть восстановлена), это так странно.

Я обнаружил, что когда я вручную возвращаю NDIS_STATUS_FAILURE для функции NdisFOidRequest в месте происхождения OID драйвера WinPcap (ветвь переключения BIOCQUERYOID и BIOCSETOID NPF_IoControl), драйвер не блокирует сеть (также не может работать winpcap).

Что-то не так с вызовом NdisFOidRequest?

Подпрограмма DeviceIO в Packet.c, которая генерирует запросы OID:

case BIOCQUERYOID:
case BIOCSETOID:

    TRACE_MESSAGE(PACKET_DEBUG_LOUD, "BIOCSETOID - BIOCQUERYOID");

    //
    // gain ownership of the Ndis Handle
    //
    if (NPF_StartUsingBinding(Open) == FALSE)
    {
        //
        // MAC unbindind or unbound
        //
        SET_FAILURE_INVALID_REQUEST();
        break;
    }


    // Extract a request from the list of free ones
    RequestListEntry = ExInterlockedRemoveHeadList(&Open->RequestList, &Open->RequestSpinLock);
    if (RequestListEntry == NULL)
    {
        //
        // Release ownership of the Ndis Handle
        //
        NPF_StopUsingBinding(Open);

        SET_FAILURE_NOMEM();
        break;
    }

    pRequest = CONTAINING_RECORD(RequestListEntry, INTERNAL_REQUEST, ListElement);

    //
    //  See if it is an Ndis request
    //
    OidData = Irp->AssociatedIrp.SystemBuffer;

    if ((IrpSp->Parameters.DeviceIoControl.InputBufferLength == IrpSp->Parameters.DeviceIoControl.OutputBufferLength) &&
        (IrpSp->Parameters.DeviceIoControl.InputBufferLength >= sizeof(PACKET_OID_DATA)) &&
        (IrpSp->Parameters.DeviceIoControl.InputBufferLength >= sizeof(PACKET_OID_DATA) - 1 + OidData->Length))
    {
        TRACE_MESSAGE2(PACKET_DEBUG_LOUD, "BIOCSETOID|BIOCQUERYOID Request: Oid=%08lx, Length=%08lx", OidData->Oid, OidData->Length);

        //
        //  The buffer is valid
        //
        NdisZeroMemory(&pRequest->Request, sizeof(NDIS_OID_REQUEST));
        pRequest->Request.Header.Type = NDIS_OBJECT_TYPE_OID_REQUEST;
        pRequest->Request.Header.Revision = NDIS_OID_REQUEST_REVISION_1;
        pRequest->Request.Header.Size = NDIS_SIZEOF_OID_REQUEST_REVISION_1;

        if (FunctionCode == BIOCSETOID)
        {
            pRequest->Request.RequestType = NdisRequestSetInformation;
            pRequest->Request.DATA.SET_INFORMATION.Oid = OidData->Oid;

            pRequest->Request.DATA.SET_INFORMATION.InformationBuffer = OidData->Data;
            pRequest->Request.DATA.SET_INFORMATION.InformationBufferLength = OidData->Length;
        }
        else
        {
            pRequest->Request.RequestType = NdisRequestQueryInformation;
            pRequest->Request.DATA.QUERY_INFORMATION.Oid = OidData->Oid;

            pRequest->Request.DATA.QUERY_INFORMATION.InformationBuffer = OidData->Data;
            pRequest->Request.DATA.QUERY_INFORMATION.InformationBufferLength = OidData->Length;
        }

        NdisResetEvent(&pRequest->InternalRequestCompletedEvent);

        if (*((PVOID *) pRequest->Request.SourceReserved) != NULL)
        {
            *((PVOID *) pRequest->Request.SourceReserved) = NULL;
        }
        //
        //  submit the request
        //
        pRequest->Request.RequestId = (PVOID) NPF6X_REQUEST_ID;
        ASSERT(Open->AdapterHandle != NULL);
        Status = NdisFOidRequest(Open->AdapterHandle, &pRequest->Request);
        //Status = NDIS_STATUS_FAILURE;
    }
    else
    {
        //
        // Release ownership of the Ndis Handle
        //
        NPF_StopUsingBinding(Open);

        //
        //  buffer too small
        //
        SET_FAILURE_BUFFER_SMALL();
        break;
    }

    if (Status == NDIS_STATUS_PENDING)
    {
        NdisWaitEvent(&pRequest->InternalRequestCompletedEvent, 1000);
        Status = pRequest->RequestStatus;
    }

    //
    // Release ownership of the Ndis Handle
    //
    NPF_StopUsingBinding(Open);

    //
    // Complete the request
    //
    if (FunctionCode == BIOCSETOID)
    {
        OidData->Length = pRequest->Request.DATA.SET_INFORMATION.BytesRead;
        TRACE_MESSAGE1(PACKET_DEBUG_LOUD, "BIOCSETOID completed, BytesRead = %u", OidData->Length);
    }
    else
    {
        if (FunctionCode == BIOCQUERYOID)
        {
            OidData->Length = pRequest->Request.DATA.QUERY_INFORMATION.BytesWritten;

            if (Status == NDIS_STATUS_SUCCESS)
            {
                //
                // check for the stupid bug of the Nortel driver ipsecw2k.sys v. 4.10.0.0 that doesn't set the BytesWritten correctly
                // The driver is the one shipped with Nortel client Contivity VPN Client V04_65.18, and the MD5 for the buggy (unsigned) driver
                // is 3c2ff8886976214959db7d7ffaefe724 *ipsecw2k.sys (there are multiple copies of this binary with the same exact version info!)
                // 
                // The (certified) driver shipped with Nortel client Contivity VPN Client V04_65.320 doesn't seem affected by the bug.
                //
                if (pRequest->Request.DATA.QUERY_INFORMATION.BytesWritten > pRequest->Request.DATA.QUERY_INFORMATION.InformationBufferLength)
                {
                    TRACE_MESSAGE2(PACKET_DEBUG_LOUD, "Bogus return from NdisRequest (query): Bytes Written (%u) > InfoBufferLength (%u)!!", pRequest->Request.DATA.QUERY_INFORMATION.BytesWritten, pRequest->Request.DATA.QUERY_INFORMATION.InformationBufferLength);

                    Status = NDIS_STATUS_INVALID_DATA;
                }
            }

            TRACE_MESSAGE1(PACKET_DEBUG_LOUD, "BIOCQUERYOID completed, BytesWritten = %u", OidData->Length);
        }
    }


    ExInterlockedInsertTailList(&Open->RequestList, &pRequest->ListElement, &Open->RequestSpinLock);

    if (Status == NDIS_STATUS_SUCCESS)
    {
        SET_RESULT_SUCCESS(sizeof(PACKET_OID_DATA) - 1 + OidData->Length);
    }
    else
    {
        SET_FAILURE_INVALID_REQUEST();
    }

    break;

Три процедуры фильтрации OID:

_Use_decl_annotations_
NDIS_STATUS
NPF_OidRequest(
    NDIS_HANDLE         FilterModuleContext,
    PNDIS_OID_REQUEST   Request
    )
{
    POPEN_INSTANCE          Open = (POPEN_INSTANCE) FilterModuleContext;
    NDIS_STATUS             Status;
    PNDIS_OID_REQUEST       ClonedRequest=NULL;
    BOOLEAN                 bSubmitted = FALSE;
    PFILTER_REQUEST_CONTEXT Context;
    BOOLEAN                 bFalse = FALSE;

    TRACE_ENTER();

    do
    {
        Status = NdisAllocateCloneOidRequest(Open->AdapterHandle,
                                            Request,
                                            NPF6X_ALLOC_TAG,
                                            &ClonedRequest);
        if (Status != NDIS_STATUS_SUCCESS)
        {
            TRACE_MESSAGE(PACKET_DEBUG_LOUD, "FilerOidRequest: Cannot Clone Request\n");
            break;
        }

        Context = (PFILTER_REQUEST_CONTEXT)(&ClonedRequest->SourceReserved[0]);
        *Context = Request;

        bSubmitted = TRUE;

        //
        // Use same request ID
        //
        ClonedRequest->RequestId = Request->RequestId;

        Open->PendingOidRequest = ClonedRequest;

        Status = NdisFOidRequest(Open->AdapterHandle, ClonedRequest);

        if (Status != NDIS_STATUS_PENDING)
        {
            NPF_OidRequestComplete(Open, ClonedRequest, Status);
            Status = NDIS_STATUS_PENDING;
        }


    }while (bFalse);

    if (bSubmitted == FALSE)
    {
        switch(Request->RequestType)
        {
            case NdisRequestMethod:
                Request->DATA.METHOD_INFORMATION.BytesRead = 0;
                Request->DATA.METHOD_INFORMATION.BytesNeeded = 0;
                Request->DATA.METHOD_INFORMATION.BytesWritten = 0;
                break;

            case NdisRequestSetInformation:
                Request->DATA.SET_INFORMATION.BytesRead = 0;
                Request->DATA.SET_INFORMATION.BytesNeeded = 0;
                break;

            case NdisRequestQueryInformation:
            case NdisRequestQueryStatistics:
            default:
                Request->DATA.QUERY_INFORMATION.BytesWritten = 0;
                Request->DATA.QUERY_INFORMATION.BytesNeeded = 0;
                break;
        }

    }

    TRACE_EXIT();
    return Status;

}

//-------------------------------------------------------------------

_Use_decl_annotations_
VOID
NPF_CancelOidRequest(
    NDIS_HANDLE             FilterModuleContext,
    PVOID                   RequestId
    )
{
    POPEN_INSTANCE                      Open = (POPEN_INSTANCE) FilterModuleContext;
    PNDIS_OID_REQUEST                   Request = NULL;
    PFILTER_REQUEST_CONTEXT             Context;
    PNDIS_OID_REQUEST                   OriginalRequest = NULL;
    BOOLEAN                             bFalse = FALSE;

    FILTER_ACQUIRE_LOCK(&Open->OIDLock, bFalse);

    Request = Open->PendingOidRequest;

    if (Request != NULL)
    {
        Context = (PFILTER_REQUEST_CONTEXT)(&Request->SourceReserved[0]);

        OriginalRequest = (*Context);
    }

    if ((OriginalRequest != NULL) && (OriginalRequest->RequestId == RequestId))
    {
        FILTER_RELEASE_LOCK(&Open->OIDLock, bFalse);

        NdisFCancelOidRequest(Open->AdapterHandle, RequestId);
    }
    else
    {
        FILTER_RELEASE_LOCK(&Open->OIDLock, bFalse);
    }
}

//-------------------------------------------------------------------

_Use_decl_annotations_
VOID
NPF_OidRequestComplete(
    NDIS_HANDLE         FilterModuleContext,
    PNDIS_OID_REQUEST   Request,
    NDIS_STATUS         Status
    )
{
    POPEN_INSTANCE                      Open = (POPEN_INSTANCE) FilterModuleContext;
    PNDIS_OID_REQUEST                   OriginalRequest;
    PFILTER_REQUEST_CONTEXT             Context;
    BOOLEAN                             bFalse = FALSE;

    TRACE_ENTER();

    Context = (PFILTER_REQUEST_CONTEXT)(&Request->SourceReserved[0]);
    OriginalRequest = (*Context);

    //
    // This is an internal request
    //
    if (OriginalRequest == NULL)
    {
        TRACE_MESSAGE1(PACKET_DEBUG_LOUD, "Status= %p", Status);
        NPF_InternalRequestComplete(Open, Request, Status);
        TRACE_EXIT();
        return;
    }


    FILTER_ACQUIRE_LOCK(&Open->OIDLock, bFalse);

    ASSERT(Open->PendingOidRequest == Request);
    Open->PendingOidRequest = NULL;

    FILTER_RELEASE_LOCK(&Open->OIDLock, bFalse);


    //
    // Copy the information from the returned request to the original request
    //
    switch(Request->RequestType)
    {
        case NdisRequestMethod:
            OriginalRequest->DATA.METHOD_INFORMATION.OutputBufferLength =  Request->DATA.METHOD_INFORMATION.OutputBufferLength;
            OriginalRequest->DATA.METHOD_INFORMATION.BytesRead = Request->DATA.METHOD_INFORMATION.BytesRead;
            OriginalRequest->DATA.METHOD_INFORMATION.BytesNeeded = Request->DATA.METHOD_INFORMATION.BytesNeeded;
            OriginalRequest->DATA.METHOD_INFORMATION.BytesWritten = Request->DATA.METHOD_INFORMATION.BytesWritten;
            break;

        case NdisRequestSetInformation:
            OriginalRequest->DATA.SET_INFORMATION.BytesRead = Request->DATA.SET_INFORMATION.BytesRead;
            OriginalRequest->DATA.SET_INFORMATION.BytesNeeded = Request->DATA.SET_INFORMATION.BytesNeeded;
            break;

        case NdisRequestQueryInformation:
        case NdisRequestQueryStatistics:
        default:
            OriginalRequest->DATA.QUERY_INFORMATION.BytesWritten = Request->DATA.QUERY_INFORMATION.BytesWritten;
            OriginalRequest->DATA.QUERY_INFORMATION.BytesNeeded = Request->DATA.QUERY_INFORMATION.BytesNeeded;
            break;
    }


    (*Context) = NULL;

    NdisFreeCloneOidRequest(Open->AdapterHandle, Request);

    NdisFOidRequestComplete(Open->AdapterHandle, OriginalRequest, Status);

    TRACE_EXIT();
}

person Yang Luo    schedule 15.08.2013    source источник
comment
Я не могу найти ошибку в этом коде. Я думаю, что один из OID, которые отправляет Wireshark, нарушает работу сети. Наиболее вероятным подозреваемым является OID_GEN_CURRENT_PACKET_FILTER. Когда вы вызываете сбой, это, вероятно, останавливает Wireshark от отправки следующего OID. Попробуйте добавить задержку к коду OID и проверьте, принимаются ли пакеты после каждого OID. Выясните, какой OID нарушает сетевую карту.   -  person Jeffrey Tippet    schedule 15.08.2013
comment
Еще одна диагностика, которую стоит попробовать: возвращайте NDIS_STATUS_SUCCESS для каждого OID, но если код OID равен OID_GEN_CURRENT_PACKET_FILTER, то фактически не отправляйте OID вниз. Посмотрите, заработает ли это путь получения.   -  person Jeffrey Tippet    schedule 15.08.2013
comment
Ты такой волшебный! Я последовал вашему совету, изменил статус на NDIS_STATUS_SUCCESS для каждого OID, независимо от того, что возвращал NdisFOidRequest, обошел вызов NdisFOidRequest, когда код OID был OID_GEN_CURRENT_PACKET_FILTER. Тогда, как вы догадались, Wireshark сработал! Но при этом я так и не понял, почему так? что не так с OID_GEN_CURRENT_PACKET_FILTER? Спасибо   -  person Yang Luo    schedule 16.08.2013
comment
Я предположил, что проблема заключается в OID_GEN_CURRENT_PACKET_FILTER, потому что NDIS обрабатывает его по-разному для протоколов и фильтров. Все протоколы равноправны друг другу, но фильтры накладываются друг на друга. Таким образом, когда протокол отправляет OID_GEN_CURRENT_PACKET_FILTER, NDIS объединит этот фильтр пакетов с фильтрами пакетов всех других протоколов. Однако, когда фильтр устанавливает фильтр пакетов, это просто перезаписывает фильтр пакетов на мини-порте, потенциально теряя фильтр пакетов, который запрашивали другие протоколы (например, TCPIP).   -  person Jeffrey Tippet    schedule 17.08.2013
comment
Но приведенный выше способ модификации предназначен только для теста, верно? Мне по-прежнему нужно получить фактическое возвращаемое значение для каждого OID, кроме OID_GEN_CURRENT_PACKET_FILTER, только обойти вызов NdisFOidRequest, когда код OID был OID_GEN_CURRENT_PACKET_FILTER, правильный ли это способ для окончательного выпуска моего драйвера? Также OID_GEN_CURRENT_PACKET_FILTER, по-видимому, связан с переключателем беспорядочного режима в Wireshark. Если мы отключим запросы OID_GEN_CURRENT_PACKET_FILTER, означает ли это, что переключатель беспорядочного режима всегда будет включен? Есть ли способ решить это? Спасибо.   -  person Yang Luo    schedule 17.08.2013
comment
Да, это было просто, чтобы попытаться сузить проблему. Производственный код должен передавать истинный код NDIS_STATUS с нижнего уровня. Лучший способ справиться с этим - заставить ваш фильтр отслеживать текущий пакетный фильтр и ИЛИ в беспорядочном флаге, когда ему нужно включить неразборчивый режим.   -  person Jeffrey Tippet    schedule 20.08.2013
comment
Извините, но что вы подразумеваете под отслеживанием текущего фильтра пакетов и ИЛИ в беспорядочном флаге, когда необходимо включить беспорядочный режим? пакетный фильтр - это не драйвер фильтра или модуль фильтра, верно? Также я не понял, что такое ИЛИ? Спасибо   -  person Yang Luo    schedule 20.08.2013
comment
Пакетный фильтр представляет собой битовую маску флагов. Когда NDIS вызывает FilterOidRequest вашего фильтра, вы можете просмотреть OID, которые проходят через фильтр. Если вы видите, что запрос OID_GEN_CURRENT_PACKET_FILTER не работает, спрячьте фильтр пакетов в этом OID — это значение выше вашего фильтра. Теперь, когда вы хотите изменить фильтр пакетов, выполните операцию побитового ИЛИ, чтобы добавить новые флаги к значению, указанному выше, и отправьте этот OID вниз. Наконец, чтобы очистить фильтр пакетов, установите предыдущее значение, которое вы видели на верхнем уровне. Вот некоторый псевдокод: pastebin.com/ZMzuBpF8   -  person Jeffrey Tippet    schedule 21.08.2013
comment
Я сделал как пример, но не повезло. Но я обнаружил, что проблема действительно в пакетном фильтре. Когда Wireshark перечисляет адаптеры, мой драйвер вызывает NdisFOidRequest с данными NDIS_PACKET_TYPE_ALL_LOCAL под OID: OID_GEN_CURRENT_PACKET_FILTER, после чего я больше не могу получать индикацию. Когда Wireshark начинает захват, мой драйвер вызывает NdisFOidRequest с данными NDIS_PACKET_TYPE_PROMISCUOUS под OID: OID_GEN_CURRENT_PACKET_FILTER, после чего я могу получить снова. HigherPacketFilter всегда имеет значение NULL, так как никакие другие приложения не устанавливают фильтр пакетов.   -  person Yang Luo    schedule 21.08.2013
comment
Я просто не понял, почему NDIS_PACKET_TYPE_ALL_LOCAL нарушает путь приема? NDIS_PACKET_TYPE_ALL_LOCAL и NDIS_PACKET_TYPE_PROMISCUOUS — два лучших флага для программного обеспечения для захвата пакетов, такого как WinPcap, не так ли? NDIS_PACKET_TYPE_ALL_LOCAL для обычного захвата и NDIS_PACKET_TYPE_PROMISCUOUS для беспорядочного захвата?   -  person Yang Luo    schedule 21.08.2013
comment
О, вам больше не нужен NDIS_PACKET_TYPE_ALL_LOCAL. Ваш фильтр всегда получает весь локальный трафик, так как он находится ниже в сетевом стеке. Этот флаг используется только для петлевого трафика, который вам больше не нужен.   -  person Jeffrey Tippet    schedule 22.08.2013
comment
Вы имеете в виду, что NDIS_PACKET_TYPE_ALL_LOCAL предназначен только для драйвера протокола, верно? Также я должен использовать флаг NDIS_PACKET_TYPE_PROMISCUOUS для случайного захвата. Нетрудно подумать. Но если я не могу использовать флаг NDIS_PACKET_TYPE_ALL_LOCAL, какой флаг мне следует использовать для беспорядочного захвата в Wireshark? Вы знаете, что WinPcap использует package.dll в качестве более высокого уровня для драйвера. Может быть, мне следует изменить функцию PacketSetHwFilter в package.dll, чтобы использовать другой флаг вместо NDIS_PACKET_TYPE_ALL_LOCAL? Спасибо   -  person Yang Luo    schedule 22.08.2013
comment
Да, NDIS_PACKET_TYPE_ALL_LOCAL бесполезен для LWF. По умолчанию вы получите неразборчивый захват автоматически; нет необходимости отправлять какой-либо OID. Только когда вы включаете беспорядочный режим, вы должны отправлять новый OID с добавленным флагом неразборчивости.   -  person Jeffrey Tippet    schedule 22.08.2013
comment
Но если Wireshark переключается из беспорядочного режима в неслучайный, драйвер npf должен отправить новый флаг, чтобы заменить старый флаг NDIS_PACKET_TYPE_PROMISCUOUS, верно? Может быть, NULL в порядке? или NDIS_PACKET_TYPE_DIRECTED | NDIS_PACKET_TYPE_MULTICAST | NDIS_PACKET_TYPE_BROADCAST? Какой из них по умолчанию? Спасибо   -  person Yang Luo    schedule 22.08.2013
comment
Спасибо, Джеффри, ты так хорошо решил эту проблему! Вы не возражаете, если я опубликую вашу почту в качестве ответа на этот вопрос? Или вы можете ответить на него сами, тогда я его закрою :)   -  person Yang Luo    schedule 26.08.2013
comment
Вы, безусловно, можете процитировать мое письмо здесь, если считаете, что можете сохранить форматирование и изображения :)   -  person Jeffrey Tippet    schedule 27.08.2013


Ответы (1)



Ниже приведено письмо, которое я получил от Джеффри, я думаю, что это лучший ответ на этот вопрос :)


Пакетный фильтр работает по-разному для LWF и для протоколов. Позвольте мне дать вам некоторые сведения. Я уверен, что кое-что из этого вы уже знаете, но всегда полезно просмотреть основы, чтобы мы могли быть уверены, что находимся на одной странице. Путь данных NDIS организован в виде дерева:

введите здесь описание изображения

Фильтрация пакетов происходит в двух местах этого стека:

(a) один раз в аппаратном обеспечении минипорта, и

(б) в верхней части стека, сразу под протоколами.

NDIS будет отслеживать фильтр пакетов каждого протокола отдельно для повышения эффективности. Если один протокол запрашивает просмотр ВСЕХ пакетов (неразборчивый режим), то не все протоколы должны сортировать весь этот трафик. Так что на самом деле в системе есть (P) разных пакетных фильтров, где P — количество протоколов:

введите здесь описание изображения

Теперь, если существуют все эти различные фильтры пакетов, как на самом деле работает OID_GEN_CURRENT_PACKET_FILTER? Что делает NDIS, так это то, что NDIS отслеживает фильтр пакетов каждого протокола, а также объединяет фильтры в верхней части стека мини-портов. Итак, предположим, что протокол 0 запрашивает фильтр пакетов A+B, протокол 1 запрашивает фильтр пакетов C, а протокол 2 запрашивает фильтр пакетов B+D:

введите здесь описание изображения

Затем в верхней части стека NDIS объединяет фильтры пакетов в A+B+C+D. Это то, что отправляется вниз по стеку фильтров и, в конечном итоге, в мини-порт.

Из-за этого процесса слияния, независимо от того, какой протокол2 устанавливает фильтр пакетов, протокол2 не может влиять на другие протоколы. Таким образом, протоколам не нужно беспокоиться о «совместном использовании» фильтра пакетов. Однако то же самое не верно для LWF. Если LWF1 решает установить новый фильтр пакетов, он не объединяется:

введите здесь описание изображения

На приведенном выше рисунке LWF1 решил изменить пакетный фильтр на C+E. Это перезаписало фильтр пакетов протоколов A+B+C+D, а это означает, что флаги A, B и D никогда не попадут в аппаратное обеспечение. Если протоколы полагались на флаги A, B или D, то функциональность протоколов будет нарушена.

Так задумано — LWF обладают большой мощностью и могут делать со стеком все, что угодно. Они предназначены для того, чтобы иметь право наложить вето на фильтры пакетов всех других протоколов. Но в вашем случае вы не хотите связываться с другими протоколами; вы хотите, чтобы ваш фильтр оказывал минимальное влияние на остальную часть системы.

Итак, что вы хотите сделать, так это всегда отслеживать, что такое фильтр пакетов, и никогда не удалять флаги из текущего фильтра пакетов. Это означает, что вы должны запрашивать фильтр пакетов, когда ваш фильтр подключается, и обновлять кэшированное значение всякий раз, когда вы видите OID_GEN_CURRENT_PACKET_FILTER, спускающийся сверху.

Если вашему приложению пользовательского режима требуется больше флагов, чем есть у текущего фильтра пакетов, вы можете выдать OID и добавить дополнительные флаги. Это означает, что аппаратный фильтр пакетов будет иметь больше флагов. Но фильтр пакетов протокола не изменится, поэтому протоколы по-прежнему будут видеть то же самое.

введите здесь описание изображения

В приведенном выше примере фильтр LWF1 работает хорошо. Несмотря на то, что LWF1 заботится только о флаге E, LWF1 по-прежнему передает вниз все флаги A, B, C и D, поскольку LWF1 знает, что вышестоящие протоколы хотят, чтобы эти флаги были установлены.

Код для управления этим не так уж плох, если вы понимаете, что нужно сделать для управления фильтром пакетов:

  1. Всегда отслеживайте последний фильтр пакетов из протоколов выше.

  2. Никогда не позволяйте сетевой карте видеть фильтр пакетов, у которого меньше флагов, чем у фильтра пакетов протоколов.

  3. Добавьте свои собственные флаги по мере необходимости.

Хорошо, надеюсь, это дало вам хорошее представление о том, что такое фильтр пакетов и как им управлять. Следующий вопрос заключается в том, как сопоставить «неразборчивый режим» и «неразборчивый режим» с фактическими флагами? Давайте тщательно определим эти два режима:

Неразборчивый режим: инструмент захвата видит только входящий трафик, который обычно получает операционная система. Если аппаратное обеспечение отфильтровывает трафик, то мы не хотим видеть этот трафик. Пользователь хочет диагностировать локальную операционную систему в ее нормальном состоянии.

Неразборчивый режим: дайте инструменту захвата как можно больше принимаемых пакетов — в идеале каждый бит, передаваемый по сети. Неважно, был ли пакет предназначен для локального хоста или нет. Пользователь хочет диагностировать сеть и поэтому хочет видеть все, что происходит в сети.

Я думаю, что если посмотреть на это таким образом, последствия для флагов пакетного фильтра довольно просты. Для неслучайного режима не меняйте фильтр пакетов. Просто позвольте аппаратному фильтру пакетов быть таким, каким хочет его видеть операционная система. Затем для неразборчивого режима добавьте флаг NDIS_PACKET_TYPE_PROMISCUOUS, и оборудование сетевой карты предоставит вам все, что возможно.

Итак, если для LWF все так просто, то зачем старому протокольному драйверу NPF требовалось так много дополнительных флагов? У старого драйвера на основе протокола было несколько проблем:

  1. Он не может получить «неразборчивый режим» совершенно правильно

  2. Он не может легко перехватывать пакеты отправки других протоколов.

Первая проблема с NPF-протоколом заключается в том, что он не может легко правильно реализовать наше определение «неразборчивого режима». Если NPF-протокол хочет видеть входящий трафик так же, как его видит ОС, то какой фильтр пакетов он должен использовать? Если он установит нулевой пакетный фильтр, то NPF не увидит никакого трафика. Таким образом, NPF может установить пакетный фильтр Directed|Broadcast|Multicast. Но это только предположение о том, что устанавливают TCPIP и другие протоколы. Если TCPIP решит установить флаг Promiscuous (некоторые флаги сокетов вызывают это), то NPF на самом деле будет видеть меньше пакетов, чем то, что видит TCPIP, что неверно. Но если NPF установит флаг Promiscuous, то он увидит больше трафика, чем увидел бы TCPIP, что тоже неправильно. Таким образом, протоколу захвата сложно решить, какие флаги установить, чтобы он видел точно те же пакеты, что и остальная часть ОС. У LWF нет этой проблемы, поскольку LWF видят объединенный OID после объединения фильтров всех протоколов.

Вторая проблема с NPF-протоколом заключается в том, что для перехвата отправленных пакетов ему требовался режим обратной связи. LWF не нуждаются в петле — на самом деле, это было бы активно вредно. Давайте воспользуемся той же диаграммой, чтобы понять, почему. Вот NPF, перехватывающий путь приема в неразборчивом режиме:

введите здесь описание изображения

Теперь давайте посмотрим, что происходит, когда принимается одноадресный пакет:

введите здесь описание изображения

Поскольку пакет соответствует аппаратному фильтру, он попадает в стек. Затем, когда пакет попадает на уровень протокола, NDIS передает пакет обоим протоколам, tcpip и npf, поскольку фильтры пакетов обоих протоколов соответствуют пакету. Так что работает достаточно хорошо.

Но теперь путь отправки сложен:

введите здесь описание изображения

tcpip отправил пакет, но npf так и не увидел его! Чтобы решить эту проблему, NDIS добавила понятие флага фильтрации пакетов «loopback». Этот флаг немного особенный, так как он не относится к оборудованию. Вместо этого петлевой фильтр пакетов сообщает NDIS, что весь отправляемый трафик должен возвращаться обратно по пути получения, чтобы диагностические инструменты, такие как npf, могли видеть пакеты. Это выглядит так:

введите здесь описание изображения

Теперь петлевой путь действительно используется только для инструментов диагностики, поэтому мы не тратили много времени на его оптимизацию. И, поскольку это означает, что все отправляемые пакеты проходят через стек дважды (один раз для обычного пути отправки и еще раз для пути получения), он как минимум удваивает затраты ЦП. Вот почему я сказал, что LWF NDIS сможет выполнять захват с более высокой пропускной способностью, чем протокол, поскольку LWF не нужен петлевой путь.

Почему бы нет? Почему LWF не нужна петля? Что ж, если вы вернетесь и посмотрите на несколько последних диаграмм, вы увидите, что все наши LWF видели весь трафик — как отправку, так и получение — без обратной связи. Таким образом, LWF отвечает требованиям просмотра всего трафика без необходимости возиться с обратной связью. Вот почему LWF обычно никогда не должен устанавливать флаги замыкания на себя.

Хорошо, это письмо получилось длиннее, чем я хотел, но я надеюсь, что оно проясняет некоторые вопросы, связанные с фильтром пакетов, петлевым путем и тем, чем LWF отличаются от протоколов. Пожалуйста, дайте мне знать, если что-то было неясно, или если диаграммы не пришли.

person Yang Luo    schedule 28.08.2013