locked
how to add WSACMSGHDR to controlData of FWPS_TRANSPORT_SEND_PARAMS0 RRS feed

  • Question

  • I try to inject a packet at layer FWPS_LAYER_OUTBOUND_TRANSPORT_V4 to change/set IP_TOS' value 

    In the packet I set IP_TOS control data to 0x02 (also tried other possible values, 4, 8, etc). When I run my WFP driver, I can see the packet was injected by checking the debug output. The question is, when i inspect the netmon capture,  the DSCP field in the IP header is 0. Here are my questions:

    Question 1. How can I tell if inMetValues->controlData contains IP_TOS control data? 

    Question 2. If inMetValues->controlData doesn't contain IP_TOS control data, is what I did below the right way to add control data?

     Here is what I did:

            FWPS_TRANSPORT_SEND_PARAMS0 sendArgs = {0};
            WSACMSGHDR ipTos;
            ULONG controlSize = 0;
            ULONG controlSizeInMeta = 0;
            DWORD tos = 0x02; //pick up a value

            //
            // packet to inject
            //
            ntstatus = FwpsAllocateCloneNetBufferList0(
                (NET_BUFFER_LIST *)layerData,
                NULL,
                NULL,
                0,
                &clonedNetBufferList);
            if(!NT_SUCCESS(ntstatus))
            {
                   //exit
            }

            ipTos.cmsg_level = IPPROTO_IP;
            ipTos.cmsg_type = IP_TOS;
            ipTos.cmsg_len = sizeof(DWORD);

            controlSize = sizeof(WSACMSGHDR) + ipTos.cmsg_len;

            //
            // control data
            //
            if (FWPS_IS_METADATA_FIELD_PRESENT(
                inMetaValues,
                FWPS_METADATA_FIELD_TRANSPORT_CONTROL_DATA))
            {
                controlSizeInMeta = inMetaValues->controlDataLength;
                controlSize += controlSizeInMeta;
            }
           
            sendArgs.controlData = ExAllocatePoolWithTag(
                NonPagedPool,
                controlSize,
                TAG_NAME_MNPDRV);
            if (sendArgs.controlData == NULL)
            {
                   //exit
            }


            //deep copy control data from inMetaValues
            if(controlSizeInMeta > 0)
            {
                RtlCopyMemory(
                    sendArgs.controlData,
                    inMetaValues->controlData,
                    inMetaValues->controlDataLength);
            }
           
            //append IP_TOS
            RtlCopyMemory(
                ((UINT8*)sendArgs.controlData) + controlSizeInMeta,
                &ipTos,
                sizeof(WSACMSGHDR));

            //copy top value
            RtlCopyMemory(
                ((UINT8*)sendArgs.controlData) + controlSizeInMeta + sizeof(WSACMSGHDR),
                &tos,
                ipTos.cmsg_len);

            sendArgs.controlDataLength = controlSize;

            //
            // remote address
            //
            ....

            //
            // scope id
            //
            ...

            //
            // inject the packet now
            //
            ntstatus = FwpsInjectTransportSendAsync0(injectHandle,
                NULL,
                inMetaValues->transportEndpointHandle,
                0,
                &sendArgs,
                AF_INET,
                compartmentId,
                clonedNetBufferList,
                completionFn,
                &sendArgs);
            if (!NT_SUCCESS(ntstatus))
            {
                //exit
            }

     

    Sunday, February 21, 2010 6:39 PM

Answers

  • Looks like we cannot set IP_TOS via sendArgs, rather we have to build ip header and modify IP_TOS value directly from the ip header.

    • Marked as answer by myq Saturday, March 6, 2010 4:31 PM
    Saturday, March 6, 2010 4:31 PM

All replies

  • hi, I believe 'cmsg_len' should include the header size (i.e. sizeof(WSACMSGHDR)).

    you will need to parse the controlData blob to see if type  IP_TOS already exists.

    Hope this helps,
    Biao.W.
    Wednesday, February 24, 2010 5:15 AM
  • I tried to have cmsg_len to include the header size: I got blue screen.

    See also http://msdn.microsoft.com/en-us/library/ms803521.aspx cmsg_len: The number of bytes of additional data that immediately follow the CMSGHDR structure.
    Wednesday, February 24, 2010 5:29 AM
  • The MSDN doc you pointed out is incorrect. I have pointed it out to a Winsock doc writer here.

    Blue screen is probably due to alignment issues. Can you try use the WSA_CMSG_SPACE macro?

    #define WSA_CMSG_SPACE(length)  \

            (WSA_CMSGDATA_ALIGN(sizeof(WSACMSGHDR) + WSA_CMSGHDR_ALIGN(length)))


    • Marked as answer by Biao Wang [MSFT] Friday, February 26, 2010 7:11 PM
    • Unmarked as answer by myq Saturday, February 27, 2010 8:53 PM
    Friday, February 26, 2010 7:11 PM
  • Thank you, Biao.  That solves my blue screen issue.

    But, when I use Micosoft Network Monitor 3.3 to view the traffic, the DSCP field in IpV4 header is still 0, does not show my IP_TOS settings. Debug output shows that the call to  FwpsInjectTransportSendAsync0 succeeds.

    Saturday, February 27, 2010 8:53 PM
  • Have you fixed up the controlSize calculation with the WSA macro?

    Can you dump out the byte sequence of sendArgs.controlData here?

    Thanks,
    Biao.W.
    Monday, March 1, 2010 8:59 PM
  • with this code:

    typedef

     

    struct WSACMSGHDR_VALUE_ {
    WSACMSGHDR msgHdr;
    INT value;

    } WSACMSGHDR_VALUE;

     

    // pendedPacket gets deleted in FreePendedPacket

    pendedPacket = ExAllocatePoolWithTag(

    NonPagedPool,

     

    sizeof(DDP_INJECT_PENDED_PACKET),

    TAG_NAME_PDRV);

     

    if (pendedPacket == NULL)

    {

    ntstatus = STATUS_NO_MEMORY;

    DoTraceMessage(TRACE_ERROR,

    "out of memory.\n");

     

    __leave;

    }

    RtlZeroMemory(pendedPacket,

    sizeof(DDP_INJECT_PENDED_PACKET));

     

    if(layerData != NULL)

    {

    pendedPacket->netBufferList = layerData;

    FwpsReferenceNetBufferList0(pendedPacket->netBufferList, TRUE);

    }

    pendedPacket->compartmentId = UNSPECIFIED_COMPARTMENT_ID;

     

    if(FWPS_IS_METADATA_FIELD_PRESENT(inMetaValues, FWPS_METADATA_FIELD_COMPARTMENT_ID))

    pendedPacket->compartmentId = inMetaValues->compartmentId;

    pendedPacket->endpointHandle = inMetaValues->transportEndpointHandle;

    pendedPacket->remoteScopeId = inMetaValues->remoteScopeId;

    ipTos.msgHdr.cmsg_level = IPPROTO_IP;

    ipTos.msgHdr.cmsg_type = IP_TOS;

    ipTos.msgHdr.cmsg_len =

    sizeof(WSACMSGHDR_VALUE);

    ipTos.value = 0x02;

    controlSize = ipTos.msgHdr.cmsg_len;

     

    //

     

    // control data

     

    //

     

    if (FWPS_IS_METADATA_FIELD_PRESENT(

    inMetaValues,

    FWPS_METADATA_FIELD_TRANSPORT_CONTROL_DATA))

    {

    ASSERT(inMetaValues->controlDataLength > 0);

    controlSize += inMetaValues->controlDataLength;

    }

    pendedPacket->controlData = ExAllocatePoolWithTag(

    NonPagedPool,

    controlSize,

    TAG_NAME_MNPDRV);

     

    if (pendedPacket->controlData == NULL)

    {

    ntstatus = STATUS_NO_MEMORY;

     

    __leave;

    }

    RtlZeroMemory(pendedPacket->controlData, controlSize);

     

    if (FWPS_IS_METADATA_FIELD_PRESENT(

    inMetaValues,

    FWPS_METADATA_FIELD_TRANSPORT_CONTROL_DATA))

    {

    RtlCopyMemory(

    pendedPacket->controlData,

    inMetaValues->controlData,

    inMetaValues->controlDataLength);

    pendedPacket->controlDataLength = inMetaValues->controlDataLength;

    }

    RtlCopyMemory(

    ((

    unsigned char*)pendedPacket->controlData) + pendedPacket->controlDataLength,

    &ipTos,

     

    sizeof(WSACMSGHDR_VALUE));

    pendedPacket->controlDataLength +=

    sizeof(WSACMSGHDR_VALUE);

     

     

     


    With the above code, I expect pendedPacket->controlData to be the same when inMetaValues->controlDataLength is 0 but I got this:

    (dumped starting from p = (unsigned char*)&sendArgs.controlData:
    sendArgs.controlData = pendedPacket->controlData;
    and sendArgs.controlDataLength  = pendedPacket->controlDataLength)

    one dump:


    10 14 d6 88 10 00 00 00 00 00 00 00 00 00 00 00

    Another one:
    d0 20 f4 88 10 00 00 00 00 00 00 00 00 00 00 00


    What makes even confusing is, when inMetaValues->controlDataLength == 20  and hence

    sendArgs.controlDataLength == 36

     I got this:

     I got this:



    60 66 03 89 24 00 00 00 00 00 00 00 00 00 00 00 c8 ac cc 88 d4 20 ca 8e a0 e7 fc 9a 48 21 ca 8e a0 e7 fc 9a

    I did append WSACMSGHDR_VALUE at the end and it does not look like the tos settings for the last 16 bytes.

    Thanks a lot.

    Wednesday, March 3, 2010 5:45 AM
  • i suspect you want 'p = (unsigned char*)sendArgs.controlData' (no &) for data dumping.

    Wednesday, March 3, 2010 7:04 AM
  • that is correct. Here is the output:

    when sendArgs.controlDataLength == 16:
    10 00 00 00 00 00 00 00 03 00 00 00 02 00 00 00

    when sendArgs.controlDataLength == 36:
    14 00 00 00 00 00 00 00 13 00 00 00 c0 a8 89 69 0f 00 00 00 10 00 00 00 00 00 00 00 03 00 00 00 02 00 00 00
    Thursday, March 4, 2010 4:11 AM
  • The sequences looks OK. Was the injection successful? Does the NetBufferList->Status field contains any error when the injection completion function is called?
    Friday, March 5, 2010 1:50 AM
  • yes, the injecttion succeeds

    ntstatus = FwpsInjectTransportSendAsync0(
    gInjectHandle,
    NULL,
    packet->endpointHandle,
    0,
    &sendArgs,
    AF_INET,
    packet->compartmentId,
    clonedNetBufferList,
    completionFn,
    packet);

     

    if (!NT_SUCCESS(ntstatus))
    {
    DoTraceMessage(TRACE_ERROR,
    "FwpsInjectTransportSendAsync0 returned error 0x%x.\n",
    ntstatus);
    __leave;
    }

    DoTraceMessage(TRACE_INFO,
    "InjectSignalingPacketOutbound succeeds.\n");


    I can see the debug output "InjectSignalingPacketOutbound succeeds".

    However, I am not sure whether or not I should check DSCP field in the Network Monitor capture to verify this: IPv4 header's DSCP is always 0, doesn't matter what values i set IP_TOS. Is this the right way to verify the IP_TOS setting?

    A few more facts I missed:
    1. I duplicate the first packet for each flow and set the IP_TOS on this first packet. I can see the packet is duplicated from Network Monitor.
    2.
    I run the driver on my VM (Windows 7 x86) hosted on Hyper-V.
    3.
    I did not inject this packet from a DCP (when I inject the packet via another thread, it crashes my VM on Hyper-V, but not crash when a VM hosted on VMServer, which is another issue).

    Thanks a lot!

    Friday, March 5, 2010 3:31 AM
  • The change should be visible to netmon.

    When your 'completionFn' is called, what's the value of netBufferList->Status?
    Friday, March 5, 2010 3:48 AM
  • I have this completionFn:
    VOID
    NTAPI 
    completionFn(
        __in VOID  *context,   
        __out NET_BUFFER_LIST  *netBufferList,  
        __in BOOLEAN  dispatchLevel)
    {
        MNP_INJECT_PENDED_PACKET* packet = (MNP_INJECT_PENDED_PACKET*)context;
        UNREFERENCED_PARAMETER(dispatchLevel);

        if(netBufferList != NULL)
        {       
            if(netBufferList->Status != NDIS_STATUS_SUCCESS)
            {
                DoTraceMessage(TRACE_ERROR,
                    "netBufferList->Status == 0x%x.\n",
                    netBufferList->Status);
            }

            FwpsFreeCloneNetBufferList0(netBufferList, 0);
        }

        FreePendedPacket(packet);

        DoTraceMessage(TRACE_INFO, "InjectionCompletionFn cleaned.\n");
    }

    No error reported but I do see the message "InjectionCompletionFn cleaned."

    Friday, March 5, 2010 4:03 AM
  • How and when the sendArgs->controlData gets free'd?
    Friday, March 5, 2010 6:18 AM
  • via FreePendedPacket(packet); inside of completionFn. It was assigned as follows:

    sendArgs.controlData = packet->controlData;
    sendArgs.controlDataLength = packet->controlDataLength;

    Friday, March 5, 2010 6:38 AM
  • Looks like we cannot set IP_TOS via sendArgs, rather we have to build ip header and modify IP_TOS value directly from the ip header.

    • Marked as answer by myq Saturday, March 6, 2010 4:31 PM
    Saturday, March 6, 2010 4:31 PM
  • Looking into this further, this is not a limitation imposed by WFP, but rather Windows TCPIP stack itself.

    http://msdn.microsoft.com/en-us/library/ms738586(VS.85).aspx lists the valid options that can be set.  My understanding is that the limitation was set due to QoS wanting ownership of the field, and to use their APIs to set this.  As stated however, one can either construct the IPHeader yourself, or catch the packet @ OUTBOUND_IPPACKET and modify the header and inject.

    Hope this helps.

    Dusty Harper [MSFT]
    Microsoft Corporation
    ------------------------------------------------------------
    This posting is provided "AS IS", with NO warranties and confers NO rights
    ------------------------------------------------------------
    Tuesday, March 9, 2010 8:36 PM
    Moderator