Hack 78. Create a Premium Class of Service
You can produce high-octane voice service for premium users by building distinct classes of service. In this hack, we will be using the AstShape script that we played with in "Shape Network Traffic to Improve Quality of Service" [Hack #77]. However, you'll now be prioritizing just one class of voice traffic over another. This is particularly useful when you need to segment two groups of users, regardless of the applications they're running. In the other hack, I showed you how to prioritize voice for everybody. But suppose you want to prioritize it only under certain circumstances. If you were going to launch a service like Skype, where users can make free calls to other Skype users and pay for calls to non-Skype users, you would want to provide the highest possible quality for the paying users, right? Let's say that you have a VoIP service that allows callers to interconnect with the PSTN (like Skype) and with other VoIP users on the Internet. Let's also say that you have two pricing levels. The "economy" pricing level does not guarantee quality (and is less expensive, or perhaps free), but the "premium" pricing level does (and costs more in return). I will show you how you can implement this using a slightly modified version of AstShape.
First, I am going to assume you've got a PC with two Ethernet interfaces loaded up with Linux, and the ability to control the IP ToS bits or port numbers used by the VoIP devices you're going to support, be they IP phones or VoIP servers. This is absolutely critical. All we are going to use to separate the two levels of our traffic are the IP ToS bits or UDP/TCP port numbers, so without that ability, this hack will be less than useful (and not much fun either). But how do you know if you can control the port numbers used by VoIP applications? Just about all VoIP software lets you control the port numbers employed by the signaling protocol (SIP) and the voice stream (RTP). Asterisk lets you adjust these settings in /etc/asterisk/sip.conf, and IP phones and softphones like X-Lite have user-configurable port-number preferences.
As I mention in other hacks, SIP is just the call signaling protocol. RTP is the protocol that carries voice and other data (video, images, data, etc). The port numbers used by RTP are pseudo-randomly selected from a predefined port range. In Asterisk, this can be configured in /etc/asterisk/rtp.conf. Actually, the default range of 1000020000 is considered by many to be too wide for most installs. Please adjust according to the size of your installation. Altering the ToS bits can be a little trickier. Most IP phones and VoIP services tag media packets with the highest possible priority, so forcing them to "downgrade" some of the packets into a lower class of service is hard. Ideally, you'll build your premium and economy service classes using the port number rather than the ToS bits. Once you have met these conditions, you are ready to proceed. Surf over to http://www.kriscompanies.com/. Go to Downloads, and then Asterisk, and then locate AstShape (Provider). Download it to your machine, place it somewhere in your $PATH (like /usr/local/sbin), make it executable (chmod +x astshape-provider), and optionally change the name to something that you will remember. Let's take a look at the script, shall we? 6.8.1. Get Started with AstShape ProviderOpen AstShape Provider in your favorite text editor. If you have ever seen AstShape, you will notice that AstShape Provider is actually smaller and simpler. That's because we are assuming that all this router will handle is VoIP traffic. There are no provisions for handling other types of traffic, and, as the script says, you will want to block this traffic with iptables or some other firewall. There are four possible knobs to turn, and they look conspicuously like those in plain-vanilla AstShape. This is the speed (in kilobits) of your Internet connection. This value can be best determined by testing, and testing often. This will be the hardest part: LINKSPEED=1000 This is the wide area network (WAN) interface on which to do QoS: DEV=eth1 What you have here is a list of ports, separated by spaces, that will be placed in "Class 1." This is the premium (more important) class of service. I've chosen 5000 and 5001 for my premium class's ports: #Class 1 priority ports CLASS1PORTS="5000 5001" Next, you have a list of ports, separated by spaces, that will be placed in "Class 2," the economy class. In this class, I've put ports 10000 and 10001: #Class 2 priority ports CLASS2PORTS="10000 10001" Once you have these values set to the correct port numbers, save the script and exit. Now all you have to do is run it: # ./astshape-provider You shouldn't see any errors. The ./astshape-provider status command will list the queues that have been defined. 6.8.2. Explaining the AstShape Provider ScriptWhat is this doing? How does this work? What if I need more? Hold on! Slow down! The AstShape Provider script is actually quite simple. Let me break it down for you on a line-by-line basis: Here you can see that the first packet queue set up by tc is known as the root queue. I've told tc to use HTB queuing. This is just one of many packet-queuing techniques supported by the Linux kernel: tc qdisc add dev $DEV root handle 1: htb default 30 This says to slow everything to $LINKSPEED to prevent queuing at our ISP. Traffic class prioritization works only if you aren't being speed-limited by the router on the other end of the connection: tc class add dev $DEV parent 1: classid 1:1 htb \ rate ${LINKSPEED}kbit burst 6k Now, I'll add the first class of service, the premium one, as shown earlier: tc class add dev $DEV parent 1:1 classid 1:10 htb \ rate ${LINKSPEED}kbit burst 6k prio 1 Here I'll the second class of service, the economy one, as shown earlier: tc class add dev $DEV parent 1:1 classid 1:20 htb \ rate ${LINKSPEED}kbit burst 6k prio 2 Finally, I'll added the "default" class, as shown earlier. This is where undefined traffic will falli.e., anything not on the ports I specified in the previous section: tc class add dev $DEV parent 1:1 classid 1:30 htb \ rate $[9*$LINKSPEED/10]kbit burst 6k prio 3 The following command makes IP packets that have the IP TOS header set to 0x19 match Class 1, our premium class. Remember that if you're prioritizing by port number, you might have no need to prioritize by the ToS header. So, you might be able to skip this line, especially if you have no control over your ToS headers, as discussed earlier: tc filter add dev $DEV parent 1:0 protocol ip \ prio 10 u32 match ip tos 0x19 0xff flowid 1:10 The following line says that any packets with an IP TOS header equal to 0x18 will match class two, the economy class. This isn't foolproof either; not all packets even have a ToS header value inserted. Starting to get the idea? Discriminating by port numbers is more consistent and easier to manage than discriminating by ToS bits, but there it is, so you can see how it's done: tc filter add dev $DEV parent 1:0 protocol ip \ prio 20 u32 match ip tos 0x18 0xff flowid 1:20 The simple loop shown in the following snippet makes sure that the ports defined in the $CLASS1PORTS variable match Class 1: for a in $CLASS1PORTS do tc filter add dev $DEV parent 1:0 protocol ip \ prio 11 u32 match ip dport $a 0xffff flowid 1:10 tc filter add dev $DEV parent 1:0 protocol ip \ prio 11 u32 match ip sport $a 0xffff flowid 1:10 done Likewise, the simple loop in the following snippet makes sure that the ports defined in the $CLASS2PORTS variable match Class 2: for a in $CLASS2PORTS do tc filter add dev $DEV parent 1:0 protocol ip \ prio 24 u32 match ip dport $a 0xffff flowid 1:20 tc filter add dev $DEV parent 1:0 protocol ip \ prio 24 u32 match ip sport $a 0xffff flowid 1:20 done The following code says that any traffic not matching the other rules is "bulk" and ends up in the bulk class, neither economy nor premium: tc filter add dev $DEV parent 1: protocol ip \ prio 30 u32 match ip dst 0.0.0.0/0 flowid 1:30 For any type of commercial service, you will certainly want to work on this script a bit. Smart users could cheat you by hacking their IP ToS headers or by using a different port number to "stow away" in the premium class. So, regardless of how you implement classes of service, you'll also need to ensure that users of your service are authenticated with usernames and passwords, and you'll need to emphasize good logging so that you always know who's using which levels of service and so that nobody breaks the rules. Kristian Kielhofner |