6. Classifying Flows

A variety of methods exist to classify flows. You can use tc to classify traffic, but it suffers from being entirely stateless. The Netfilter framework is a stateful firewall which can be used to classify flows in additional to providing firewalling services. Moreover, it's often more convenient to simply add a classification chain to an existing Netfilter configuration. The simplest method for classifying traffic with Netfilter is the CLASSIFY target, although the MARK target combined with tc filters is also effective.

6.1. Using tc and the u32 selector

The u32 selector allows you to match specific bits in the headers of IP, TCP, UDP, and ICMP packets. Most commonly it is used to classify packets based on the usual suspects, including source or destination address and source or destination port number. I only intend to cover aliases for commonly sought after bit offsets included within the tc binary. Matching portions of packets by hand is covered in light detail in the Linux Advanced Routing and Traffic Control HOWTO.

tc filter add dev eth0 parent 1:0 protocol ip u32 match ip sport 80 0xffff classid 1:10

The syntax for tc when adding filters is verbose, but few of the values will change. First we specify that we want to work with a filter. We indicate we want to add a new filter for dev eth0. The parent is generally the parent qdisc for the specified interface, often 1:0 unless you choose a different major node number. Next, protocol is specified with ip as its value. Last, the u32 selector is specified.

With that, the stage is set for values to be passed off to the u32 selector itself. These values will always take the format of the keyword match followed by keyword ip. Next, for readability a selector of src, dst, sport, or dport is specified along with relevant arguments. Finally, classid is specified and should correspond to the qdisc or class you wish to assign the packet to.

tc filter add dev eth0 parent 1:0 protocol ip u32 match ip dport 22 0xffff \
  match ip dst classid 1:20

For example, the above u32 match will assign packets with a destination port of 22 and a destination IP address of to the qdisc described by 1:20. Notice you can include multiple instances of match in a single filter. The syntax for matching a source IP address and source port are the same, with only the selector name changing. You can match entire ranges of IP addresses using standard CIDR notation.

Deleting filters is an involved process. The syntax presented above lacks some of the keywords necessary to allow effective, consistent deletion of individual filters. You can delete all filter entries by deleting the egress qdisc for a device, demonstrated earlier.

6.2. Using the Netfilter CLASSIFY Target

Since Linux 2.6 the CLASSIFY target has been part of the standard distribution, so you need not patch your kernel. The CLASSIFY extension was added to Netfilter in version 1.2.9.

The CLASSIFY target is simple to use, provided you have some existing familiarity with Netfilter.

iptables -t mangle -A POSTROUTING -o eth2 -p tcp --sport 80 -j CLASSIFY --set-class 1:10

Briefly, iptables is being instructed to append a rule to the POSTROUTING section of mangle table. The rule matches TCP packets with a source port of 80 that are passing out of the eth2 network interface. The target of this rule is the CLASSIFY extension, which is directed to classify this traffic into the class described by the major node number 1 and the minor node number 10. The careful reader will notice that, based on the minor node number being greater than zero, the target must be a class assigned to a classful qdisc. A detailed discussion of how to use Netfilter and iptables is beyond this document's scope.

You can only use CLASSIFY from the POSTROUTING chain of the mangle table. It is prohibited elsewhere. If you find you need to classify packets elsewhere, you may need to use the MARK target instead.

6.3. Using the Netfilter MARK Target

If you cannot use the CLASSIFY target, you can use the mark target in conjunction with tc to classify flows.

iptables -t mangle -A POSTROUTING -o eth2 -p tcp --sport 80 -j MARK --set-mark 1

The above iptables rule will set an invisible mark on any packet it matches. The mark exists in kernel space only. The packet is not actually modified. The tc binary can be used to classify flows based on these marks.

tc filter add dev eth0 protocol ip parent 1:0 prio 1 handle 1 fw classid 1:10

The above tc command is not unlike the familiar qdisc and class variants, except now you're adding a filter instead. The parent parameter will always refer to the root qdisc for the given interface, which must exist prior to creating the filter. The actual parameter handle refers to the mark that you gave the flow earlier. The parameter classid refers to, unsurprisingly, the handle of the class you wish to assign this flow to. It's generally only useful to add filters for interfaces which have classful qdiscs configured.

6.4. Using the L7 Filter Netfilter Module

L7 is an exciting new extension module for Netfilter which lets you match layer 7 traffic. L7 will investigate the contents of one or more packets in a flow to match patterns you specify. L7 already has a rich database of matching patterns for different application layer protocols. L7 is very useful for matching the flows of peer to peer (p2p) applications, many of which use random ports, but generally do not (yet) encrypt their data payloads to avoid detection.

Since L7 is not yet officially part of the standard Netfilter distribution, you will need to download the sources and patch both your kernel and iptables. There is an excellent HOWTO that details the steps to compile L7. Once you have installed your patched kernel and iptables with L7 support, you can start using it to match flows based on layer 7 protocols.

iptables -t mangle -A POSTROUTING -m layer7 --l7proto edonkey \
  -j CLASSIFY --set-class 2:1

For example, assuming the patterns are installed in the default location, the above will match flows generated by the popular p2p application eMule and use the CLASSIFY target described above to classify the flow appropriately. The POSTROUTING and PREROUTING chains are recommended since a flow's packets pass through either in both directions, so you aren't listening to half a conversation, which will break some layer 7 matches.