Throttling, DSCP, and 802.1p with Traffic Control

In his introductory post about the legacy Traffic Control (TC) API, Gabe discussed the host-based model that TC provides. In this post, we will see how Traffic Control APIs can be used to achieve the following for TCP/IPv4 and UDP/IPv4 traffic sent from a host:

  • Throttle (rate-limit) outgoing traffic
  • Add DSCP value in layer-3 (IPv4) header
  • Indicate that an 802.1p tag value should be added in layer-2 header

At the bottom of the post, we’ve provided a link to the Networking Connect site to download full source and binaries to a tool which implements all of the above functionality.

The following are the steps involved:

1. The first step in the process is to obtain a handle to the Traffic Control subsystem through a call to TcRegisterClient().

2. Next, make a call to TcEnumerateInterfaces() using the registration handle obtained in step #1. This call returns a list of all TC enabled interfaces on the system. Iterate through the list to find the interface(s) on which you want to prioritize and/or throttle outgoing traffic.

3. For each interface of interest, issue a TcOpenInterface() using the pInterfaceName from the corresponding TC_IFC_DESCRIPTOR for that interface from the list returned in step#2. Store the handle returned by TcOpenInterface(); let’s call it the ifchandle.

4. At this point, create a TC Flow and add it to the interface(s) of interest.

a. Create the TC Flow:
A TC flow is a way of describing various QoS characteristics to be applied to a set of packets and is represented by a TC_GEN_FLOW structure. The following code snippet shows how to create a TC Flow given the DSCP value, 802.1p value and the throttle rate.

BOOL CreateFlow(PTC_GEN_FLOW * _ppTcFlowObj, USHORT DSCPValue, USHORT OnePValue, ULONG ThrottleRate)
  BOOL status = FALSE;

  // Flow Parameters
  PVOID pCurrentObject;
  PTC_GEN_FLOW _pTcFlowObj = NULL;
  int Length = 0;

  // Calculate the memory size required for the optional TC objects

  Length += (OnePValue == NOT_SPECIFIED ? 0:sizeof(QOS_TRAFFIC_CLASS)) + (DSCPValue == NOT_SPECIFIED ? 0:sizeof(QOS_DS_CLASS));

  // Print the Flow parameters

  printf("Flow Parameters:\n");
  DSCPValue == NOT_SPECIFIED ? printf("\tDSCP: *\n"):printf("\tDSCP: %u\n", DSCPValue);
  OnePValue == NOT_SPECIFIED ? printf("\t802.1p: *\n"):printf("\t802.1p: %u\n", OnePValue);
  ThrottleRate == QOS_NOT_SPECIFIED ? printf("\tThrottleRate: *\n"):printf("\tThrottleRate: %u\n", ThrottleRate);
  TokenRate = TokenBucketSize = ThrottleRate;

  // Allocate the flow descriptor
  _pTcFlowObj = (PTC_GEN_FLOW)malloc(FIELD_OFFSET(TC_GEN_FLOW, TcObjects) + Length);

  if (!_pTcFlowObj)
    printf("Flow Allocation Failed\n");
    goto Exit;

  _pTcFlowObj->SendingFlowspec.TokenRate = TokenRate;
  _pTcFlowObj->SendingFlowspec.TokenBucketSize = TokenBucketSize;
  _pTcFlowObj->SendingFlowspec.PeakBandwidth = PeakBandwidth;
  _pTcFlowObj->SendingFlowspec.Latency = Latency;
  _pTcFlowObj->SendingFlowspec.DelayVariation = DelayVariation;
  _pTcFlowObj->SendingFlowspec.ServiceType = ServiceType;
  _pTcFlowObj->SendingFlowspec.MaxSduSize = MaxSduSize;
  _pTcFlowObj->SendingFlowspec.MinimumPolicedSize = MinimumPolicedSize;

  // Currently TC only supports QoS on the send path
  // ReceivingFlowSpec is legacy and ignored

  memcpy(&(_pTcFlowObj->ReceivingFlowspec), &(_pTcFlowObj->SendingFlowspec), sizeof(_pTcFlowObj->ReceivingFlowspec));
  _pTcFlowObj->TcObjectsLength = Length;

  // Add any requested objects
  pCurrentObject = (PVOID)_pTcFlowObj->TcObjects;
  if(OnePValue != NOT_SPECIFIED)
    QOS_TRAFFIC_CLASS *pTClassObject = (QOS_TRAFFIC_CLASS*)pCurrentObject;
    pTClassObject->ObjectHdr.ObjectType = QOS_OBJECT_TRAFFIC_CLASS;
    pTClassObject->ObjectHdr.ObjectLength = sizeof(QOS_TRAFFIC_CLASS);
    pTClassObject->TrafficClass = OnePValue; //802.1p tag to be used
    pCurrentObject = (PVOID)(pTClassObject + 1);

    QOS_DS_CLASS *pDSClassObject = (QOS_DS_CLASS*)pCurrentObject;
    pDSClassObject->ObjectHdr.ObjectType = QOS_OBJECT_DS_CLASS;
    pDSClassObject->ObjectHdr.ObjectLength = sizeof(QOS_DS_CLASS);
    pDSClassObject->DSField = DSCPValue; //Services Type

  *_ppTcFlowObj = _pTcFlowObj;
  status = TRUE;

    printf("Flow Creation Failed\n");
    printf("Flow Creation Succeeded\n");

  return status;

b. Add the Flow on the interface:
After obtaining a TC_GEN_FLOW structure with the desired characteristics using a function similar to the one above, issue a call to TcAddFlow() with the ifchandle (obtained in step #3) and a pointer to the TC_GEN_FLOW object (obtained in step #4a). Store the handle returned by TcAddFlow();let’s call it the flowhandle.

5. The next step is to create a TC Filter and add it to the TC Flow created above.

a. Create the TC Filter:
A TC Filter is a way of describing which packets to apply the QoS characteristics to. The QoS characteristics defined in the TC_GEN_FLOW will only apply to the packets matching the filter(s) associated with the Flow.
The following code snippet describes how to create a TC Filter given the destination address, the destination port, and the protocol (TCP,UDP or IP).

BOOL CreateFilter(PTC_GEN_FILTER * ppFilter, SOCKADDR_STORAGE Address, USHORT Port, UCHAR ProtocolId)

  BOOL status = FALSE;
  USHORT AddressFamily = Address.ss_family;
  PIP_PATTERN pPattern = NULL;

  if(AddressFamily != AF_INET)
    goto Exit;

  // Allocate memory for the filter
  pFilter = (PTC_GEN_FILTER)malloc(sizeof (TC_GEN_FILTER));
    goto Exit;

  ZeroMemory(pFilter, sizeof(TC_GEN_FILTER));

  // Allocate memory for the pattern and mask
  pPattern = (PIP_PATTERN)malloc( sizeof(IP_PATTERN));
  pMask = (PIP_PATTERN)malloc( sizeof(IP_PATTERN));
  if(!pPattern || !pMask)
    goto Exit;

  memset ( pPattern, 0, sizeof(IP_PATTERN) );
  pPattern->DstAddr = ((SOCKADDR_IN *)&Address)->sin_addr.s_addr;
  pPattern->tcDstPort = htons(Port);
  pPattern->ProtocolId = ProtocolId;
  memset ( pMask, (ULONG) -1, sizeof(IP_PATTERN) );

  // Set the source address and port to wildcard
  // 0 -> wildcard, 0xFF-> exact match
  pMask->SrcAddr = 0;
  pMask->tcSrcPort = 0;

  // if the user specified 0 for dest port, dest address or protocol
  // set the appropriate mask as wildcard
  // 0 -> wildcard, 0xFF-> exact match
  if(pPattern->tcDstPort == 0)
    pMask->tcDstPort = 0;

  if(pPattern->ProtocolId == 0)
    pMask->ProtocolId = 0;

  if(pPattern->DstAddr == 0)
    pMask->DstAddr = 0;

  pFilter->AddressType = NDIS_PROTOCOL_ID_TCP_IP;
  pFilter->PatternSize = sizeof(IP_PATTERN);
  pFilter->Pattern = pPattern;
  pFilter->Mask = pMask;

  // Delete any previous instances of the Filter
  *ppFilter = pFilter;
  status = TRUE;

    printf("Filter Creation Failed\n");
    printf("Filter Creation Succeeded\n");

  return status;

b. Adding the Filter to the TC Flow:
Once a TC Filter structure is obtained using a function similar to the one above, issue a call to TcAddFilter() passing the flowhandle obtained in step #4b and a pointer to the TC_GEN_FILTER structure obtained in step #5a. Store the filter handle returned by TcAddFilter(); let’s call it filterhandle.
You can add multiple filters on the same flow causing different sets of packets matching each filter to get the same QoS characteristics applied to them.

6. At this point, your application is applying QoS on all matching outgoing packets as specified in the TC Filter and TC Flow. Finally, once your purpose is served, make sure you call the respective close calls on all the open TC handles – TcDeletefilter(), TcDeleteFlow(), TcCloseInterface() and TcDeregisterClient().

You can download the full source and binaries of a simple command line tool – tcmonlite, which takes the filter and flow parameters as input, creates TC Flow and Filter, and configures the QoS subsystem with them using the Traffic Control API. All the outgoing traffic on the system matching the filter gets the desired QoS characteristics as long as tcmonlite is running. Go to the Microsoft Connect website, choose Available Connections on the left-hand side of the page, and select Windows Networking from the available connections (bottom half of the page). On the left-hand side of the Windows Networking page, choose Downloads, and select TCMonLite.

This tool can be used in conjunction with the NDIS LWF driver to detect 802.1p tags in the Ethernet header and DSCP in the IP header of packets. Let us know what you think!

-- Hemant Banavar 

Comments (27)

  1. ddebug says:

    This is great, but how about network connections with intrinsic QoS –  Will you support technologies that "offload" both the classification/tagging and throttling functionality from the OS?

  2. wndpteam says:

    I’m not certain what "intrinsic" QoS means? Perhaps you are refering to a fabric that intermediate elements already support strict priorities (e.g. switches or routers)? If you’re more specific, I’m happy to comment. As for offloading classification/tagging and throttling functionality from the OS; I’d love to hear your scenarios for why offload would be valuable. We recently explored supporting this very thing, but found the savings (CPU cycles) were negledgeable even for 1Gbps link saturation on average server class machines. The QoS platform on Windows is *extremely* efficient for very large workloads. It does sound like a blog post is in order that describes, architecturally, how the QoS subystem works.

    — Gabe Frost

  3. Sasha O says:

    Great article, thanks. One thing made me very curious, though:


    You can download the full source and binaries of a simple command line tool – tcmonlite… Go to the Microsoft Connect website, choose Available Connections on the left-hand side of the page, and select Windows Networking from the available connections (bottom half of the page). On the left-hand side of the Windows Networking page, choose Downloads, and select TCMonLite.


    Why not to just publish URL?

  4. wndpteam says:

    Hi Sasha,

    We’d like for folks to get a feel for the core networking Connect site; hence the manual steps instead of a URL. For example, on the Connect site you can file bugs directly for the product teams to look at (which we *do* review often), download utilities and pre-published documentation from other networking components, etc.

    Considering that I’ve posted about the site a couple times now, I’ll use a direct URL in the future. Thanks for the comment!

    — Gabe

  5. ddebug says:


    Apologies for the delay with reply.

    My comment refers to certain type of Wireless WAN where the QoS policy comes from the network provider end,

    and filters and throttling are implemented in the WWAN adapter or driver.

    The OS should not do it’s own QoS handling in this case, just pass all traffic to the driver as is.

    OTOH the OS needs to know the filters and shaping parameters of the driver, in order to expose this up to the network stack and UI.

    Disconcert of the OS and the underlying network can lead to unefficient use of bandwidth (which is very limited) or distortion of voice or video timing. This is not about savind CPU cycles on the host.

    But this can be even more useful for power saving on mobile platforms – while the WWAN adapter does the real time work, the system can sleep . It will not need to wake up from a high resolution timer needed for shaping.

    Hope it’s clearer now…

    Hi ddebug.

    I’ve actually thought a lot about the WWAN scenarios and definitely understand that QoS policies come from the carrier who owns the network infrastructure rather than the IT administrator who defines Group Policy. As such, our Group Policy based feature is not applicable to WWAN, but rather, only enterprise networks that IT/network administrators control resources on. An important aspect of policy-based QoS is that the machine must be domain-joined and be able to communicate with the domain controller. For WWAN, these conditions would be met, for example, on a corporate VPN connection.

    We are; however, thinking about how the QoS platform/developer model in Windows should support WWAN scenarios. It sounds like you’ve been thinking about this as well, so I’d *love* to hear more of your thoughts. Send me an email (wndpteam) off-line to discuss if you’re interested.

        — Gabe Frost

  6. Dan Orbach says:

    Hi Gabe

    There are two things that I’m trying to confirm and would very much appreciate your comment on:

    1) It is my understanding that we can use group policy (GPO) to set RTA (audio payload) to expedited forwarding (EF) and RTA (signaling) to assured forwarding (AF31) and make this happen from a centralized location. Is this correct?

    2) Assuming that (1) above is correct, is it also true that in Windows Vista we can BLOCK any Windows Vista compliant AND non-compliant programs from changing these markings?

    Basically I’m trying to understand if I have an enterprise with users logged into the domain, do I have complete control over the DSCP marking of packets leaving the PC?



  7. blankheart says:

    Hi Gabe

    There is just one things that I’m trying to confirm and would very much appreciate your comment on:

    Can the TC api work on a router and mark the packets  forwarded by the router?

    I’m trying to use the TC api to mark the packets forwarded by the router,but i doesn’t work!

    Can somebody give me some advice?


  8. Gabe Frost says:

    Hi blankheart,

    Routers only operate on layer-3 (IP) headers of packets, and never actually look at the layer-2 (MAC) portion. As such, it’s important to recognize that only layer-3 priority (DSCP) will be relevant for a packet transitioning a layer-3 boundary (router).

    Assuming you are adding a DSCP value using TC to the IP header of outgoing packets on your Windows PC, and the destination address is for a machine on the other side of the router, the router *might* prioritize based on that mark. Or, if the router is connected to a network that you do not own, like a broadband router (cable/dsl), you’ll see that it will strip the DSCP from the packets. I suspect this is what you are seeing; especially if you’re sending packets with DSCP over the public Internet.

    First, validate with NetMon or some other sniffing application that DSCP is in-fact being added to your outgoing packets. Then, on the receiving machine on the other side of the router, run the same sniffing software to see if the DSCP is received in-tact. If the mark is gone, the router is configured to strip the value (or another router in the path strippd the value).

    DSCP is intended to be used within a single administrative domain.

    I hope this was helpful.

  9. Now that windows 7 in on the way, do you have any information on how DSCP marking will be possible on the new version of Windows? I would like to get an head start this time around…

    Let’s say a Softphone needs to be able to control the DSCP value used for the audio and video stream. I mean fully control like setting the DSCP value to an arbitrary value like in the good old setsockopt(IP_TOS) days. It would need to be able to set the DSCP bits to 11 different values based on application specific context, not to a flow spec value control by the domain with limited different value. Is there anyway Windows 7 will make my life easier the Vista?

  10. Charley Wen says:

    Hi Martin,

    Thanks for your question.

    Yes, you’ll be able to set an arbitrary DSCP value for your application using QOSSetFlow with a new QOS_SET_FLOW type we’ve introduced in Windows 7. The requirements are

    1) the flow your application creates must be non-adaptive; and

    2) your application is a member of the Administrators group or the Network Configuration Operators group.

    Please let us know if this answers your question.

    We’re also interested in the use cases of your application as you said your application would adapt to 11 different DSCP values. Would you mind sharing a bit more details of those scenarios?


  11. Mike in Video says:

    As the "net" guy for a video based developing application, mobile videocon, we’ll say, I am wondering if a windows based application could incorporate this type of DSCP marking and if only data from that application would do such marking – or if the PC interface will always mark packets once installed.  

    I do not want to effect the way other applications transmit if at all possible.

    I am also looking for a way to change the tags if needed by an appliance before interfacing with the network.  Any input would be greatly appreciated.

    Thank you in advance!

  12. Charley Wen says:

    Hi Mike,

    First of all, to invoke a TC API, an application must have the admin priviledge. If it does, then the flow and the filter it creates do have effects on all the applications on that computer.

    Second, to avoid abuse by an application, Policy based QoS, a feature introduced in Vista, is designed to allow the IT admin to overwrite any QoS settings programmed by a rogue application.

    The third point is sort of implied in my reply to Martin’s question earlier. That is, one can also use the qWave API (aka QoS2 API) to set the DSCP value for an application. In Vista, the API has a limitation – one can only set it to one of a few predefined DSCP values. But in Windows 7, the restriction has been removed. One can now use the qWave API to set any arbitrary DSCP value.

    I’m not too clear about the last question you posted. Are you looking for a way to let your application programmatically overwrite a DSCP value set by another application? Could you please elaborate?


  13. Hi Charley,

    Thanks for your quick answer.

    I am not sure the requirements will be acceptable for our application.

    As I said, our application is a Softphone. It can be used by both home and business users so I cannot assume that the users will have admin right on their PC; installation might be done by an admin and once completed I cannot have UAC prompts.

    It has a Signaling socket that has its own DSCP value.

    It sends its media over RTP/RTCP and both audio and video have their own DSCP requirements.

    The media sessions have an attribute with 5 possible values and this value defines the DSCP value to be used. The value is specified by the end user and authorized by the call server.

    The Softphone is the only component of the system that runs on Windows, so the DSCP policy is completely independent of the Windows domain and is centrally configured on the call server.

    I wish I could give you details more details, but I cannot go further.

    As Mike said, I also only want to affect the signaling and media sockets of my application. I do not want to affect any other data sent by my process (ex: HTTP get) or other applications.

    — Martin

  14. Charley Wen says:

    Hi Martin,

    Thanks for the info.

    Because your application has separate sockets for signaling, media transmission and so on, you’ll be able to set a different DSCP value for each of these socket connections using "QOSAddSocketToFlow" and "QOSSetFlow". Only the packets sent to those sockets will be marked.

    However, the requirement that your application must have the admin or network configuration operator privilege still holds. One possible way to work around it is to write your own miniport driver and mark your application packets there.


  15. George says:

    Hi and Thanks,

    but i don’t know well C++, can anyone have a sample (TC API) for Visual Basic 6… I want to create a small utility for managing traffic (upload/download) for my local network.

    Thanks in advance 🙂

  16. HUNT says:

    Hi Sasha,

    I am using wince 5.0 with wifi card.

    I want to set QOS enabled for WMM ie MULTIMEDIA Data,

    I used setsockopt() but Microsoft says an issue

    after this I switched to QOS structure

    I fulfilled the structure ie defined it.

    Now what’s next to do for enabling QOS / DSCP for WMM?

  17. sarforsh says:

    Does win ce 5.0 supports QOS setting?

    I have been searching but have not found any clue.

    I tried with


    for UDP …….

    But it gives an error with number 10022.

    that tells

    invalid argument.

    Is there any other way to set QOS in IP for WIN CE 5.0.

    Thanks & Revert Awaited.

  18. Hi Sarforsh,

    I never tried it on Win CE 5.0, but here is a though.

    Is the dscp variable defined as an int?

    — Martin

  19. David Sisk says:

    From a comment above:

    "Yes, you’ll be able to set an arbitrary DSCP value for your application using QOSSetFlow with a new QOS_SET_FLOW type we’ve introduced in Windows 7. The requirements are"

    did this really happen? The QOS_SET_FLOW seems to indicate that I have to pass in an enum for the traffic type and not a hex value. The enum does not provide a method for setting the EF DSCP markings. This will need to be done without have a domain.

    Any suggestions?


  20. Gajaraj says:

    Can’t we use SetSocketOption ??

  21. vishal says:


    i’m computer cience student, i wanted to develop a project on Qos implementation on windows.i mean i want apply Qos on differnt kind of data ie.multimedia data for a health information system.

    i want to use C++.please can you guide me,for  from where  should i start? i wanted to implement Diffserv.

    i have seen the TC API at your site,QOS at MSDN libary.

    can i get ready made code for the implementation of Qos techniques in C++?     so that i can use them as refernce for the development of my project.



  22. Deepu says:


    I’m exploring QOS capabilties in Windows networking. After reading the inital msdn documentation, I came across that with Winsock2, programmers are provided access to advanced Microsoft® Windows® networking capabilities such as multicast and Quality of Service (QOS).

    I’ve query whether it is possible to have QOS capabilities along with "WinHttp" ??

    I’m sorry if my query is vague !!



  23. Net Guy says:


    I have been doing network programming for more than a decade, and the API design described in this article and the official replies here is a design and security mistake:

    1. It forces speed optimized network applications to run as administrator, thus increasing the system exposure to any encountered security holes.  If there are ways to run only the QoS calls as administrator (i.e. make QoS calls in one process, network traffic in another) then the answers given here fail to say that at all.

    2. It acts like a filtering configuration API, where packets are first separated from the sending application, then detected by packet filtering rules to apply DSCP values based on a guess as to which application "flow" sent the packet.

    In contrast, other API's I have seen (Windows 2000 and non-Windows in particular) involve the application setting DSCP values on the socket handle or in an extended sendto() call, and (subject to policy ACLs), these values become part of the packets as they enter the network stack.  This:

    1. Allows high-volume network code to run with restricted permissions (like IE8 does on Vista/Win7).

    2. Allows an application to prioritize different data differently even though it is sent on the same address/port pairs (like allowing IE9 to set different DSCP values for file downloads, page viewing and embedded image loading, even though it is all port 80 traffic from the same web server or proxy).

    3. Avoids the risk that packets from one application are accidentally subjected to flow patterns belonging to another application (Like Windows Update BITS traffic accidentally matching a rule for IE9 traffic).

  24. QoS Guy says:


               Are there ways to call the qWAVE APIs from the kernel in a transport driver based on WinSock kernel?

    Thanks for your help!

  25. Adam says:

    Got this working on Windows XP after following instructions here:…/2007.02.cableguy.aspx

    I wasn't able to set the DSCP value until I made the registry change to HKEY_LOCAL_MACHINESYSTEMCurrentControlSetServicesTcpipParametersDisableUserTOSSetting=0

    Thanks for such a great tool!!!!

    Adam 🙂

  26. Spark says:

    Thank you for the article. In the meantime, in order to run this application, I think I need to create a policy-based QOS rule from Group Policy Editor. As a network application tester, I'm looking for a script or application to create a new policy. Is any one who can share the script or application? Thank you. 😉

  27. swpark says:

    What I want to do is testing DSCP value is changing when use tcmonlite for transmission of UDP packet.

    Host A: W2K8 R2 Ent. (

    Host B: W2K8 R2 Ent. (

    They are connected back-to-back

    While sending an UDP packet from A to B's port 9999, Host B confirms that it receives the UDP packet correctly.

    Then, run tcmonlite.exe from Host A

    tcmonlite -proto udp -destip -destport 9999 -dscp 40 -onep 4 -throttle 100000

    Wireshark from Host A capture the udp packet and still DSCP value is x00 not x28.

    Could you explain why? What I did miss?

    Thank you,


Skip to main content