locked
flowContext not available at FWPS_LAYER_INBOUND_IPPACKET_V4 problem or how to set the TOS field based on the flowContext RRS feed

  • Question

  • Hi everyone,

    I just wanted to thank for all the replies and answers I got to my previously asked questions. I really appreciate the help.And here comes another question ;)

    Before I start with the problem, I need to example al little what  I am doing. Basically my driver does some manipulation on the payload (so at FWPS_LAYER_STREAM_V4  layer). The decision, if and what is manipulated, is based on the process name and the destinaition IP and port. Because the process name is not available at this layer and making this decision is kind of time consuming, the decision is made for each flow once at a callout at FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 and tagged to the flow. This part works like a charm.

    Additonally, I need to set the TOS field of the IP Header based on the decision. This is the point where my problem starts. As far as I am informed is the flowContext not avaiblable at FWPS_LAYER_OUTBOUND_IPPACKET_V4.  But this would be the best (and maybe only) layer where I can manipulate the TOS field. But I can't determine without the flowContext what the value is at FWPS_LAYER_OUTBOUND_IPPACKET_V4 layer. I need to do this at FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 and pass the decision / value  using the flowContext.

    So I am trying to figure out how to achieve this. The "last" layer on which the flow context is available is the FWPS_LAYER_OUTBOUND_TRANSPORT_V4 layer. 
    But at the ip header (of course) is not built at this layer. I have read that I can walk the netbuffer list forward to the position at which the IP Header will be built. Is this correct? And can I set the TOS field there? Will the building of the IP Header overrride the value? If this solution would work, I think it is kind of ugly.
    Therefore, has somebody an idea how to pass on the TOS value (just 8 bits) to the FWPS_LAYER_OUTBOUND_IPPACKET_V4 layer. Or how can I set the TOS field in kernel space for a connection?

    I am glad for any advise or hint how to solve the problem.

    Kind regards
    Dennis  
    Wednesday, November 18, 2009 9:16 AM

Answers

  • You have two options --

    1) Clone the packets at OUTBOUND_TRANSPORT and block/absorb the original. The clone won't have IP header (IP header is not constructed at this layer) but you can use FwpsConstructIpHeaderForTransportPacket0 to construct one in front of it (pass NULL as sendArgs). You then modify the TOS field and fix up the checksum (you could call FwpsConstructIpHeaderForTransportPacket0 again but this time pass in the correct headerIncludeHeaderSizevalue to "re-construct" the header with the right checksum). Lastly you call FwpsInjectTransportSendAsync0 to re-inject the packet back to the data flow (you will need to pass NULL as "sendArgs"  because the packet now had IP header.

    2) For Win7, you could use the new "packet" tagging feature. You would request a unique tag for each flow using FwpsNetBufferListGetTagForContext0, and use the tag to tag your contexts onto packets from OUTBOUND_TRANSPORT layers. From OUTBOUND_TRANSPORT you would query/remove such tags using FwpsNetBufferListRetrieveContext0 to get your context back. Now the packets could get cloned/duplicated in between these two layers so you should handle packet events such as FWPS_NET_BUFFER_LIST_CLONED_BY_NETIO etc. (you can find more details on packet tagging here --- http://go.microsoft.com/fwlink/?LinkId=140050)

    Hope this helps,
    Biao.W.

    • Edited by Biao Wang [MSFT] Friday, November 20, 2009 3:16 AM typo
    • Marked as answer by Mips128 Thursday, November 26, 2009 4:49 PM
    Friday, November 20, 2009 3:14 AM

All replies

  • You have two options --

    1) Clone the packets at OUTBOUND_TRANSPORT and block/absorb the original. The clone won't have IP header (IP header is not constructed at this layer) but you can use FwpsConstructIpHeaderForTransportPacket0 to construct one in front of it (pass NULL as sendArgs). You then modify the TOS field and fix up the checksum (you could call FwpsConstructIpHeaderForTransportPacket0 again but this time pass in the correct headerIncludeHeaderSizevalue to "re-construct" the header with the right checksum). Lastly you call FwpsInjectTransportSendAsync0 to re-inject the packet back to the data flow (you will need to pass NULL as "sendArgs"  because the packet now had IP header.

    2) For Win7, you could use the new "packet" tagging feature. You would request a unique tag for each flow using FwpsNetBufferListGetTagForContext0, and use the tag to tag your contexts onto packets from OUTBOUND_TRANSPORT layers. From OUTBOUND_TRANSPORT you would query/remove such tags using FwpsNetBufferListRetrieveContext0 to get your context back. Now the packets could get cloned/duplicated in between these two layers so you should handle packet events such as FWPS_NET_BUFFER_LIST_CLONED_BY_NETIO etc. (you can find more details on packet tagging here --- http://go.microsoft.com/fwlink/?LinkId=140050)

    Hope this helps,
    Biao.W.

    • Edited by Biao Wang [MSFT] Friday, November 20, 2009 3:16 AM typo
    • Marked as answer by Mips128 Thursday, November 26, 2009 4:49 PM
    Friday, November 20, 2009 3:14 AM
  • Hello,

    these two options help a lot! Today I tried to use option 1, because even with option2 I needed to add a context to the package at transport level (meaning having a callout at transport layer), because stream alyer does not support tagging (as far as I've read).

    So I wrote a callout at FWPM_LAYER_OUTBOUND_TRANSPORT_V4 which clones the NBL and blocks the original. Then (as you described) called FwpsConstructIpHeaderForTransportPacket0 two times to create the IP Header,  being able to change TOS after the first call and adjusting the checksum with the second call. Then the cloned nbl is added to a queue. A worker thread reinjects the pkgs from the queue, because as far as I know it is not possible to call FwpsInjectTransportSendAsync0 in the classifyfn function if it is a tcp package.

    But for some odd reason the first call to FwpsConstructIpHeaderForTransportPacket0 always fails with: C0220035 meaning STATUS_FWP_INVALID_PARAMETER. But I've double checked the parameters and to my knowledge everything should be all right. Except maybe: Do I need to offset the NetBuffers in the NBL before calling FwpsConstructIpHeaderForTransportPacket0?

    So here is the code of the classifyfn function:
    NET_BUFFER_LIST *nbl;
        NET_BUFFER *nb;
        NTSTATUS status;
        PDEVICE_EXTENSION pDevX = (PDEVICE_EXTENSION) pDeviceObject->DeviceExtension;
        FWPS_PACKET_INJECTION_STATE state;
        IP_HEADER *ip_header;
        UINT32 src;
        UINT32 dest;
        UINT32 i;
        QElement *element;

        if(inMetaValues->currentMetadataValues & FWPS_METADATA_FIELD_TRANSPORT_ENDPOINT_HANDLE)
            DbgPrint("-Log- EndPoint available!\n");

        state = FwpsQueryPacketInjectionState0(InjectionHandle2, (NET_BUFFER_LIST *) layerData, NULL);
        if(state == FWPS_PACKET_INJECTED_BY_SELF
            || state == FWPS_PACKET_PREVIOUSLY_INJECTED_BY_SELF)
        {
            classifyOut->actionType = FWP_ACTION_PERMIT;
            return;
        }

        classifyOut->actionType = FWP_ACTION_BLOCK;
        classifyOut->flags |= FWPS_CLASSIFY_OUT_FLAG_ABSORB;

        src = inFixedValues->incomingValue[FWPS_FIELD_OUTBOUND_TRANSPORT_V4_IP_LOCAL_ADDRESS].value.uint32;
        dest = inFixedValues->incomingValue[FWPS_FIELD_OUTBOUND_TRANSPORT_V4_IP_REMOTE_ADDRESS].value.uint32;

        status = FwpsAllocateCloneNetBufferList0(
            (NET_BUFFER_LIST *) layerData,
            NULL,
            NULL,
            0,
            &nbl);
        if(!NT_SUCCESS(status))
        {
            DbgPrint("-Error- Can't clone NBL: %u\n", status);
            nbl = NULL;
            return;
        }

        status = FwpsConstructIpHeaderForTransportPacket0(
                        nbl,
                        0,
                        AF_INET,
                        (UCHAR*) &src,
                        (UCHAR*) &dest,
                        IPPROTO_IP,
                        inMetaValues->transportEndpointHandle,
                        0,
                        0,
                        0,
                        NULL,
                        0,
                        0);
        if(!NT_SUCCESS(status))
        {
            DbgPrint("-Error- Creating IP Header: %u\n", status);
            FwpsFreeCloneNetBufferList0(nbl, 0);
            return;
        }

        // get first NetBUffer and change the TOS Field of the first package
        nb = NET_BUFFER_LIST_FIRST_NB(nbl);
        ip_header = (IP_HEADER *) NdisGetDataBuffer(
            nb,
            sizeof(IP_HEADER),
            NULL,
            sizeof(UINT16),
            0);   
        ip_header->TOS = 4;
        status = FwpsConstructIpHeaderForTransportPacket0(
                        nbl,
                        20,
                        AF_INET,
                        (UCHAR*) &src,
                        (UCHAR*) &dest,
                        IPPROTO_IP,
                        inMetaValues->transportEndpointHandle,
                        NULL,
                        0,
                        0,
                        NULL,
                        0,
                        0);
        if(!NT_SUCCESS(status))
        {
            DbgPrint("-Error- Creating IP Header 2: %u\n", status);
            FwpsFreeCloneNetBufferList0(nbl, 0);
            return;
        }

        element = ExAllocatePoolWithTag(NonPagedPool, sizeof(QElement), TEST_TAG);
        if(element == NULL)
            return;

        element->id = inMetaValues->compartmentId;
        element->endPointHandle = inMetaValues->transportEndpointHandle;
        element->nbl = nbl;

        ExInterlockedInsertTailList(&queue, &element->list, &lock);
        KeSetEvent(&pDevX->kevent,EVENT_INCREMENT, FALSE);

        return;
    }


    What I've noticed is that if I don't pass metaVlues->transportEndPointHandle to the FwpsConstructIpHeaderForTransportPacket0 functions, it succeeds, but when call the injection function (with transportEndPoint set to 0, too, the package seems to be discarded and when using element->endPointHandle in the ininjection functions  I get BSODed). The BugCheck call is triggered through some function called by the injection function:

    STACK_TEXT: 
    a9bd2b04 828dee71 00000003 e4aaa1b0 00000065 nt!RtlpBreakWithStatusInstruction
    a9bd2b54 828df96d 00000003 00000000 88a7dad1 nt!KiBugCheckDebugBreak+0x1c
    a9bd2f1c 828487eb 0000000a 00000000 00000002 nt!KeBugCheck2+0x68b
    a9bd2f1c 88a7dad1 0000000a 00000000 00000002 nt!KiTrap0E+0x2cf
    a9bd3160 88a7b117 86a45b38 85600002 00000000 tcpip!WfpAleInsertRemoteEndpoint+0x4c7
    a9bd35c8 88a79ef1 86a45b38 00000030 88ae53c0 tcpip!WfpAlepAuthorizeSend+0xe11
    a9bd3784 88a7ffa8 86a45b38 85600002 00000000 tcpip!WfpAleAuthorizeSend+0x1da
    a9bd37f0 88a81610 86a45b38 85600002 00000000 tcpip!WfpAleConnectAcceptIndicate+0x62
    a9bd3950 88a76e07 a9bd3ab8 00000000 85600002 tcpip!ProcessALEForTransportPacket+0x3a5
    a9bd39cc 88a7654f a9bd3ab8 00000000 85600002 tcpip!ProcessAleForNonTcpOut+0x57
    a9bd3a78 88a75927 00000000 85600002 00000000 tcpip!WfpProcessOutTransportStackIndication+0x255
    a9bd3ae0 88a7c264 00000000 8685e0a4 856025f0 tcpip!IppInspectLocalDatagramsOut+0x101
    a9bd3ba8 88ac899f 00000000 00000000 88aeed98 tcpip!IppSendDatagramsCommon+0x5a8
    a9bd3c90 88b5594a 00000000 00000001 86a45b38 tcpip!IppInspectInjectRawSend+0xc6
    a9bd3cd0 88b55a4d 00000000 00000000 84b21008 fwpkclnt!FwppInjectTransportSendAsync+0x21d
    a9bd3d08 933a7cc9 84adb598 00000000 00000095 fwpkclnt!FwpsInjectTransportSendAsync0+0x2d
    a9bd3d50 82a1066d 84b45fa8 e4aab774 00000000 TOS!WT+0x89 [c:\users\dennis\desktop\sqos\tos\hw\tos.c @ 585]
    a9bd3d90 828c20d9 933a7c40 84b45fa8 00000000 nt!PspSystemThreadStartup+0x9e
    00000000 00000000 00000000 00000000 00000000 nt!KiThreadStartup+0x19

    I guess the error lies in FwpsConstructIpHeaderForTransportPacket0, or being more precise, in the argument I am passing to it.

    Has somebody an idea what is wrong?

    Thanks a lot!

    Dennis


    Friday, November 20, 2009 3:19 PM
  • i think for the "nextProtocol" parameter is should be "inFixedValues->incomingValue[FWPS_FIELD_OUTBOUND_TRANSPORT_V4_PROTOCOL].value.uint8" instead of "IPPROTO_IP".

    Biao.W.
    Saturday, November 21, 2009 1:37 AM