DSCP EF for VoIP on a Router: Marking and Queue Setup
DSCP EF for VoIP on a router means marking RTP as DSCP 46 (ToS 0xB8) and then giving that traffic a capped priority queue on a shaped WAN egress. Marking alone changes nothing until a queue forms at the bottleneck.
- RTP is marked EF (DSCP 46, ToS byte 0xB8 / 184) and SIP signalling CS3 (24, ToS 0x60) or AF31 (26, ToS 0x68), but the label does nothing without a queue.
- Shape WAN egress to roughly 85 to 95 percent of the real uplink so the queue forms in your router instead of the modem or ISP.
- Give EF a capped priority class sized to concurrent calls (about 87 kbps per G.711 call on Ethernet) and never an unlimited strict priority.
- Inbound jitter is fixed by shaping download to about 90 percent with FQ-CoDel or CAKE, because you cannot reorder packets after they cross the bottleneck.
- Verify with tcpdump or Wireshark (ip.dsfield.dscp == 46), router queue counters, and a test call during a saturated upload.
Why does DSCP EF not fix choppy VoIP calls by itself?
DSCP is only a 6-bit label in the IP header. It does not speed up, reserve or protect anything. Priority exists only at a point where packets wait in a queue and a scheduler chooses which one leaves first.
Take a 1 Gbps LAN feeding a 20 Mbps uplink. A backup job pushes data at LAN speed into the router, the uplink drains it 50 times slower, and a queue builds in whichever device first meets the slow link. If that device is your router with a priority queue, a voice packet jumps the line. If it is the ISP modem with one dumb FIFO buffer, the voice packet waits behind hundreds of milliseconds of bulk data, whatever its label says.
That standing delay is bufferbloat, and it is the usual cause of choppy, robotic or one-way audio when someone saturates the link. The fix has two parts: move the queue into a device you control by shaping slightly below line rate, then give EF a priority class in that queue. If no queue ever builds, nothing is reordered and marking is irrelevant.
Which DSCP values should RTP and SIP use?
The ToS byte is the DSCP value shifted left two bits (DSCP x 4), because the low two bits carry ECN. Tools differ on which form they want: Asterisk accepts names like ef, iptables and nft use decimal or names, and some phone UIs want the DSCP number. The classic mistake is entering 0xB8 where 46 is expected, or 0x2e (which is DSCP 46 written in hex) where a ToS byte is expected, which produces DSCP 11 instead.
| Traffic | Class | DSCP | ToS byte (hex / dec) |
|---|---|---|---|
| RTP voice | EF | 46 | 0xB8 / 184 |
| SIP signalling (common) | CS3 | 24 | 0x60 / 96 |
| SIP signalling (alternative) | AF31 | 26 | 0x68 / 104 |
| Video | AF41 | 34 | 0x88 / 136 |
| Everything else | Best effort (CS0) | 0 | 0x00 / 0 |
Where do I set the marking: phones, PBX or router?
Mark as close to the source as possible, then decide at the LAN edge whether to trust or rewrite. In a small office, trusting only the voice VLAN or the PBX and re-marking everything else to 0 stops a PC application from claiming EF.
On Asterisk with PJSIP, set it on the transport and endpoint. Older chan_sip used tos_sip, tos_audio, tos_video and cos_* settings. In FreePBX the equivalents sit under Settings, Asterisk SIP Settings, and per-trunk or extension advanced options.
; pjsip.conf
[transport-udp]
type=transport
protocol=udp
bind=0.0.0.0:5060
tos=cs3
cos=3
[office-phone]
type=endpoint
tos_audio=ef
tos_video=af41
cos_audio=5
cos_video=4
; legacy chan_sip.conf [general]
tos_sip=cs3
tos_audio=ef
tos_video=af41What if my phones or hosted provider cannot mark traffic?
Classify at the router instead. Match by source IP or voice VLAN, or by port: SIP on UDP/TCP 5060 and TLS 5061, RTP on UDP 10000-20000 (the Asterisk default in rtp.conf). Set the DSCP there, before the packet enters the WAN queue.
The rules below live in their own nftables table on the postrouting hook, so they mark IPv4 packets just before they leave the router. If your phones use a different RTP range, adjust the port range to match.
For hosted VoIP, remember that providers and ISPs commonly bleach or rewrite DSCP on the public internet. Your marking mainly helps your own egress queue. It does not guarantee anything downstream, and you cannot control the inbound direction with markings at all.
# nftables: mark voice leaving the router (own table, postrouting hook)
nft add table inet qos
nft add chain inet qos post '{ type filter hook postrouting priority 0; }'
nft add rule inet qos post udp sport 10000-20000 ip dscp set ef
nft add rule inet qos post udp dport 5060 ip dscp set cs3
nft add rule inet qos post tcp dport '{ 5060, 5061 }' ip dscp set cs3 How do I build a shaped WAN egress with a priority queue?
Measure the real uplink first, using a wired test at a quiet time. Set the shaper to 85 to 95 percent of that figure so the queue forms in your router. Shaping above the real line rate is the most common reason QoS appears to do nothing.
Then define classes: EF in a priority (LLQ) class with a cap, CS3 with a small guaranteed share, and everything else fair-queued. Size the EF cap as concurrent calls x per-call bandwidth plus headroom, or around 30 percent of the shaped rate, whichever is smaller. Never give strict priority unlimited bandwidth: a misbehaving stream marked EF would starve every other class.
On the inbound side, CAKE accepts only one diffserv mode, so the ingress qdisc uses diffserv4 with the ingress keyword. Do not append besteffort, because it would override diffserv4 and discard tin priority. Inbound EF is prioritised only if the marks actually arrive from your provider. Many ISPs bleach them, in which case inbound voice lands in the best-effort tin and is protected by flow fairness and the shaped, short queue rather than by priority.
| Codec | Per call on Ethernet | 10 concurrent calls |
|---|---|---|
| G.711 (20 ms) | about 87 kbps (80 kbps at IP layer) | about 0.9 Mbps |
| G.729 (20 ms) | about 31 kbps | about 0.3 Mbps |
| Opus | variable, depends on configured bitrate | measure on a live call |
# Linux CAKE on a 20 Mbps uplink (measured), shaped to 18 Mbps
tc qdisc replace dev eth0 root cake bandwidth 18Mbit diffserv4 nat wash
# Inbound: ingress shaping through an ifb device at ~90% of 200 Mbps
ip link add ifb0 type ifb && ip link set ifb0 up
tc qdisc add dev eth0 handle ffff: ingress
tc filter add dev eth0 parent ffff: matchall action mirred egress redirect dev ifb0
tc qdisc replace dev ifb0 root cake bandwidth 180Mbit diffserv4 ingress
# Cisco IOS
class-map match-all VOICE
match dscp ef
class-map match-all SIG
match dscp cs3 af31
policy-map WAN-OUT
class VOICE
priority percent 30
class SIG
bandwidth percent 5
class class-default
fair-queue
policy-map SHAPE
class class-default
shape average 18000000
service-policy WAN-OUT
interface GigabitEthernet0/1
service-policy output SHAPEHow do I configure this on pfSense, MikroTik and UniFi?
The principle is identical everywhere: shape below line rate, prioritise EF, fair-queue the rest. Only the vocabulary changes.
| Platform | What to do |
|---|---|
| pfSense / OPNsense | Firewall, Traffic Shaper wizard (VoIP option) or limiters with FQ_CoDel at about 90 percent of line rate for both directions. Add floating rules on the WAN, direction out, matching UDP 10000-20000 or the phone VLAN, and set the DiffServ code point. |
| MikroTik | /ip firewall mangle add chain=postrouting protocol=udp src-port=10000-20000 action=change-dscp new-dscp=46, then /queue tree with parent set to the WAN interface, max-limit about 90 percent, and a priority 1 child matching the packet mark. Use fq_codel or cake queue types for the default child. |
| Ubiquiti / UniFi | Enable Smart Queues on the WAN with rates set to about 90 percent of measured speeds. It uses fq_codel, which separates flows and sharply reduces loaded latency even without explicit EF rules. |
| Cisco IOS | Use the class-map, policy-map priority percent and shape average example above. |
How do I verify DSCP EF is working, and what usually goes wrong?
Verification has three layers: the marking is present, the queue counts it, and a loaded call stays clean. Aim for added latency under about 30 ms under load, jitter under 30 ms and loss under 1 percent. A loaded-latency test such as Waveform's bufferbloat test, or a continuous ping to the gateway during an upload, shows whether the queue is controlled.
| Symptom | Likely cause | Fix |
|---|---|---|
| Marks show 46 at phone but 0 at router WAN | Switch or router rewriting DSCP to 0 | Trust DSCP on the voice VLAN port; set a rule rather than relying on pass-through |
| Marks fine, calls still choppy on upload | Shaper above real line rate, queue in modem | Lower shaper to 85 to 90 percent of measured uplink |
| Upload fine, download calls choppy | Inbound not shaped | Ingress shaping with CAKE or FQ-CoDel at about 90 percent |
| Wi-Fi phones choppy | WMM off or DSCP not mapped to Voice category | Enable WMM; DSCP 46 maps to the Voice access category |
| Marks vanish inside VPN or IPsec | Inner DSCP not copied to outer header | Enable DSCP copy or re-mark on the tunnel interface |
| One-way audio after QoS changes | Double NAT or SIP ALG rewriting | Disable SIP ALG; set external_media_address and local_net in Asterisk |
- Capture on the phone side: tcpdump -i eth0 -v -n 'udp portrange 10000-20000' and read the tos 0xb8 field on each packet.
- Repeat the capture on the WAN interface (or Wireshark filter ip.dsfield.dscp == 46) to see whether the mark survives the router.
- Read queue counters: tc -s qdisc show dev eth0 on Linux, or show policy-map interface on Cisco, and confirm the priority class counter increases during a call.
- Saturate the uplink with a large upload, place a test call, and check for clean audio and stable ping to the gateway.
tcpdump -i eth0 -v -n 'ip and udp portrange 10000-20000' | grep -m5 'tos 0xb8'
tc -s qdisc show dev eth0
asterisk -rx 'pjsip show endpoints'
sngrep Common questions
Do I need DSCP marking if I only have a few phones?
Shaping and fair queuing give most of the benefit, and FQ-CoDel or CAKE protect voice even without explicit marks. Marking adds a further guarantee when the link is badly congested, so it is worth doing once the shaper is in place.
Should SIP be CS3 or AF31?
Either works, provided the router classifies the same value you send. CS3 (DSCP 24, ToS 0x60) is the more common default, and SIP is small enough that the choice rarely matters compared with protecting the RTP stream.
Can I prioritise incoming voice from my hosted provider?
Not reliably. Packets have already crossed the bottleneck by the time your router sees them, and providers often strip EF marks. Shape download to about 90 percent with FQ-CoDel or CAKE so the ISP queue stays empty; CAKE diffserv4 ingress will use the Voice tin only if the EF marks actually arrive.
Does the codec affect my QoS settings?
Yes, through bandwidth sizing. G.711 needs about 87 kbps per call on Ethernet while G.729 needs about 31 kbps, so the EF priority cap must cover concurrent calls times that figure plus headroom.
Why do Windows softphones show DSCP 0 in a capture?
Windows ignores the application's DSCP request unless a Group Policy QoS policy applies to that application or port range. Either create that policy or re-mark the traffic at the router by source IP or port.