Back to posts
Post

Linux tc VoIP DSCP EF marking and priority queue configuration

Configure Linux traffic control to prioritize VoIP traffic using DSCP EF markings and reduce latency and jitter for real-time calls.

NetworktcVoIPQoSDSCP

VoIP traffic needs consistent low latency to avoid choppy audio or dropped calls. On Linux servers acting as gateways or routers you can use tc (traffic control) to mark packets with DSCP EF and place them in a high-priority queue. This ensures voice packets get forwarded before bulk data even when the link is congested.

In my environment I run a small SIP gateway on Ubuntu 22.04 that handles inbound and outbound calls for a local PBX. Without QoS the voice traffic competes with backups updates and web traffic leading to noticeable delays during peak hours. After implementing tc-based DSCP EF marking and a priority queue the MOS score improved from 3.2 to 4.4 during stress tests.

Marking VoIP packets with DSCP EF

First identify the VoIP traffic. In my case SIP signaling uses UDP 5060 and RTP media ports range from 10000 to 20000. I use iptables to set the DSCP field to EF (0x2b) for these ports.

# Mark SIP signaling
iptables -t mangle -A POSTROUTING -p udp --dport 5060 -j DSCP --set-dscp-class EF
# Mark RTP media range
iptables -t mangle -A POSTROUTING -p udp --dport 10000:20000 -j DSCP --set-dscp-class EF

You can verify the markings with tcpdump:

tcpdump -i eth0 -vv -s 0 'udp and (port 5060 or portrange 10000-20000)'

Look for DS field: 0x2e (46) in the output which confirms EF marking.

Creating a priority queue with tc

Now that packets are marked we shape egress traffic on the outward interface (eth0). I use HTB (Hierarchical Token Bucket) with two classes: one high priority for EF and one default for everything else.

# Clear existing qdisc
tc qdisc del dev eth0 root 2>/dev/null || true

# Add root HTB with 100mbit ceiling
tc qdisc add dev eth0 root handle 1: htb default 20

# High priority class (10mbit guaranteed)
tc class add dev eth0 parent 1: classid 1:10 htb rate 10mbit ceil 100mbit

# Default class (remaining bandwidth)
tc class add dev eth0 parent 1: classid 1:20 htb rate 20mbit ceil 100mbit

# Attach SFQ to both classes for fairness
tc qdisc add dev eth0 parent 1:10 handle 10: sfq perturb 10
tc qdisc add dev eth0 parent 1:20 handle 20: sfq perturb 10

# Filter EF marked packets to high priority class
tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32
  match ip dscp 0x2e 0xfc flowid 1:10

# Filter remaining traffic to default class
tc filter add dev eth0 protocol ip parent 1:0 prio 2 u32
  match ip dst 0.0.0.0/0 flowid 1:20

This configuration guarantees up to 10mbit for VoIP traffic while allowing it to burst to the full link speed if the priority class is idle. Other traffic gets at least 20mbit guaranteed but can also use spare bandwidth.

Monitoring and troubleshooting

After applying the rules check statistics with:

tc -s class show dev eth0

Look at the dropped packets and backlog counters. If you see drops in the high priority class you may need to increase its rate or ceil value.

Use tcptrack or iftop to observe real-time bandwidth usage. For VoIP specifically monitor jitter with tools like sipp or pjsua during test calls.

A common pitfall is forgetting to mark traffic on both ingress and egress if your server is symmetric. In my setup I only mark outbound because the upstream router handles inbound QoS but verify your path.

Persisting the configuration

These commands do not survive a reboot. I save them in /etc/network/if-up.d/00-voip-qos and make the script executable.

#!/bin/bash
[ "$IFACE" != "eth0" ] || exit 0
# Insert the tc and iptables commands here

Alternatively use systemd-networkd or Netplan hooks if you prefer declarative configuration.

Final notes

VoIP QoS is not about giving voice unlimited bandwidth it is about ensuring timely delivery when the link is busy. With DSCP EF and tc you can achieve noticeable improvements in call quality without overprovisioning your circuit.

As I mentioned before in my post about DNS-over-TLS (https://furkanikkan.com/urun/systemd-resolved-ile-dns-over-tls-ve-dnssec-yapilandirma-kilavuzu-102) small per-packet optimizations compound to create a reliable user experience. The same principle applies here: mark classify and queue.

If you are running a Linux-based VoIP gateway spend an hour tuning these settings. Your users will hear the difference.


Cover image: Unknown · CC0 (Openverse / kamu malı) · https://www.rawpixel.com/image/6038427/photo-image-public-domain-technology-line