Classify Marked Packets with the tc basic Filter
By the end of this guide, a packet carrying a chosen netfilter mark will be classified by a Linux traffic-control filter. You will attach a basic filter, send matching packets to a class, inspect the installed rule, and remove it without disturbing the rest of the qdisc.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about 15 minutes if the classful qdisc already exists. This guide uses the tc shipped by iproute2 6.1.0 on the reference machine. You need root privileges for changes to traffic control, a classful qdisc with a usable class, and a test interface. Replace every value in angle brackets before running a command.
Safety: traffic-control changes affect live packets. Do not paste the examples into a production interface until you have recorded its current configuration. A filter can redirect traffic into a class with a different queueing policy, and a mistaken match can affect more packets than intended.
1. Record the current configuration
First, choose the interface and inspect its qdisc and filters. This is read-only and does not need root on most systems, although permissions can vary.
DEV=<interface>
tc -s qdisc show dev "$DEV"
tc -s class show dev "$DEV"
tc -s filter show dev "$DEV"
Look for a classful qdisc and the class ID you intend to use, such as 1:10. The basic filter does not create a qdisc or a class. It only decides what happens to a packet that reaches the filter, so classid 1:10 must refer to a class already provided by the parent qdisc.
Checkpoint: the destination exists
Do not continue until the chosen class appears in the class listing. If the interface only has a classless qdisc, stop and design the qdisc and classes first. Adding an unrelated qdisc here would change live queueing behaviour and is outside this focused example.
2. Add a narrow metadata match
The extended match infrastructure, or ematch, supplies the expression after match. The example below matches the netfilter mark value 0x42. The quotes are significant: parentheses and other ematch syntax must reach tc instead of being interpreted by the shell.
sudo tc filter add dev "$DEV" parent <parent> protocol ip prio 10 \
basic match 'meta(nfmark eq 0x42)' classid 1:10
Replace <parent> with the parent shown by the qdisc, for example 1: where that is the qdisc's filter parent. The protocol ip selector limits this filter invocation to IPv4 traffic. The priority is the filter ordering value; choose a value that does not collide with an existing rule on the same parent.
This command requires root. It changes kernel state immediately. Keep the exact command or note the priority so that you can remove the rule later.
3. Verify the installed rule
Ask tc to display filters on the interface. Use statistics when you want packet and byte counters.
sudo tc -s filter show dev "$DEV"
Find a basic filter with the ematch expression, the selected protocol and priority, and a destination class of 1:10. Counters normally remain at zero until matching traffic traverses the filter. A successful add command may print nothing; the listing is the useful verification.
The filter classifies packets; it does not itself set the netfilter mark. If the counter does not move, inspect the component that assigns the mark, confirm that the traffic is IPv4, and check that the filter is attached at the expected parent. Do not widen the match just to make the counter increase.
4. Test the match without guessing
Generate or select a test packet using the system that owns your marking policy. For example, an existing firewall rule might set mark 0x42. Record the mark with your normal firewall diagnostics, then repeat the filter listing:
sudo tc -s filter show dev "$DEV"
sudo nft list ruleset
The second command is only an inspection example; use it if nftables is the component responsible for marking. If you use another packet-marking system, inspect that system instead. A mark is metadata, not a visible property of an ordinary packet capture, so a capture alone cannot prove that nfmark matched.
Remember that a class assignment is meaningful only if the parent qdisc uses classes. A successful filter installation does not prove that the class has the bandwidth, priority or queueing behaviour you expected.
5. Remove the rule cleanly
Warning
Deleting the filter changes classification immediately. If you are testing on a live interface, first confirm that removing it will not send traffic into an unintended default class.
Delete the rule using the same identifying fields used when it was added. The priority and parent are especially useful when several filters exist.
sudo tc filter del dev "$DEV" parent <parent> protocol ip prio 10 basic
Then verify that it has gone:
sudo tc filter show dev "$DEV"
If deletion reports that no matching filter exists, do not keep retrying variants blindly. Display the filters, copy the actual parent, protocol and priority, and compare them with the original command. If you need a complete rollback for a larger experiment, restore the configuration you recorded in step 1, one object at a time.
Common traps
- Confusing
basicwith a standalone classifier: it is a filter attached within traffic control. It does not replace the qdisc or define classes. - Quoting the ematch incorrectly: without shell quoting, parentheses can be treated as shell syntax and the expression never reaches
tcintact. - Expecting a mark to be created here:
meta(nfmark ...)tests metadata already present on the packet. It does not apply a firewall mark. - Using the wrong layer in another ematch: packet-data matches such as
nbyte,cmpandu32have their own offsets and layer rules. Readtc-ematch(8)before adapting this example. - Reading zero counters as proof of failure: first confirm that traffic traverses this interface, protocol and parent, then confirm the mark assignment.
Done means
- The intended class existed before the filter was added.
- The rule used a quoted ematch and a deliberately chosen priority.
tc -s filter show dev <interface>displayed the rule and its counters.- A known marked IPv4 test packet was used, or the lack of one was explained.
- The test filter was deleted, or its exact rollback command was recorded and applied.