I increased the port range in the rule, and everything is perfect now. I think it is a bug in PCAPdroid; it still shows port 7500, but when I cross-checked with tcpdump, the port number was different from PCAPdroid output.
Hi everyone, I only found about this script recently. I went to thank for the people involved for making this since it allows me to prioritize game traffic on my home network. Regular CAKE without DiffServ/tins works OK but I still see increased latency whereas with DSCP tagging, I don't see any latency spikes at all.
Anyway, it seems DSCP tagging applications on Windows is easy but on Linux, it's a bit more complicated. On Linux, I can use nftable's skuid
and run a process as another user or use network namespaces. Either of those solutions requires manual modification of game launch scripts in Steam which is not ideal so I'd prefer an automated solution without intervention like on Windows. I was wondering if anyone has a solution?
On reddit, someone mentioned a potential solution:
One thing I can think of is SELinux. Theoretically, if you were to assign network security context to each application, you could use the SECMARK module in iptables to shove all packets originating from a particular security context into a specific chain (for example, have a chain just for Firefox). Then the Firefox chain sets the DSCP field, does any other filtering you want, and forwards.
Is this feasible? I don't know as much about SELinux so I was wondering if anyone had more insight before I spend time learning SELinux.
Thanks @tahayassen! I like to think cake-qos-simple is an elegant implementation and the nftables template hopefully provides a springboard for whatever complexity is needed for each user.
@patrakov or @moeller0 is it really so hard in Linux just to set DSCP or even a firewall mark at application level? In Windows this is easy.
Unless the application allows to configure a specific DSCP it is harder than on windows... assuming you want to assign that DSCP by application... any of the port/ip based nftables rules that work on OpenWrt will also work on Linux computers... As @tahayassen indicated there are potential work arounds, out of which network namespaces might be the easiest... for applications you can start via a small script that should work.
@tahayassen does your linux distribution use nftables? Can't you use:
I'm no linux expert, but doesn't this allow something like defining a new group, e.g. 'gaming', and then this will allow matching on traffic associated with group 'gaming'?
That is, packets associated with GID 'gaming' can be matched and then tagged with the DSCP?
So you could e.g. start application with group 'gaming' when you want to use DSCPs:
@moeller0 does this seem about right?
I am using Ubuntu which uses nftables. Actually I have a solution working using nftables's skuid
matching feature to DSCP tag applications started by a certain user. Unfortunately, games from Steam on Linux are very fragile in my experience so the following side-effects from running a game as another user can break games or trigger their anti-cheat:
- game startup script is modified to start it as another user
- modified environment
- modified file permissions
I wasn't aware of the skgid
matching feature so this seems like a potential solution to alleviate problems #2 and #3 above.
Someone on reddit mentioned using cgroups. They are suggesting to use systemd-run
to run a process with a certain cgroup. Alternatively, the arch wiki mentions that you can move already running processes into a cgroup using cgclassify
which seems more appealing.
I will look into all the above and get back to you guys with the best solution. I have full-time work though and I'm doing this in my free time so it might take me a while.
This looks promising, in that it is simpler than setting up network name spaces... I have so far tried nether, so I am wildly speculating here...
Thanks @moeller0.
Yes so @tahayassen seems like there will be a variant that works. So long as you can get your client device to tag outgoing packets then cake-qos-simple will handle the rest in ensuring that packets in both directions associated with the connections are tagged.
I tinkered a bit with this tonight and I ended up using a cgroup-based solution.
- Prefix the application you want to run with
systemd-run
and give it a slice/cgroup name (in this case I choseprioritize
). You need to run your application prefixed withsystemd-run
every time. [1]
systemd-run --user --scope --slice=prioritize %command%
- Query the PID to get the cgroup path. You only need to do this once.
[1] owner@Taha-PC-Linux ~> cat /proc/8923/cgroup
0::/user.slice/user-1000.slice/user@1000.service/prioritize.slice/run-p8808-i8809.scope
- Add the DSCP tagging rule in the output chain in
/etc/nftables.conf
. You only need to do this once.
chain output {
type filter hook output priority filter; policy accept;
socket cgroupv2 level 4 "user.slice/user-1000.slice/user@1000.service/prioritize.slice" ip dscp set ef
}
- Restart
nftables
so that the rule is applied to the slice/cgroup. Unfortunately, you need to do this once every system boot after starting your application because the slice/cgroups are not persistent. [2]
systemctl restart nftables
[1]: There's a tool called cgclassify that can move running processes to cgroups if you want to avoid prefixing launches.
[2]: There's supposedly a tool so that step 4 doesn't need to be run for every system boot but I couldn't get it work because of this strange error:
[1] owner@Taha-PC-Linux ~> sudo nft add rule inet filter output socket cgroupv2 level 4 "user.slice/user-1000.slice/user@1000.service/prioritize.slice" ip dscp set ef
Error: syntax error, unexpected number, expecting string or ll or nh or th
add rule inet filter output socket cgroupv2 level 4 user.slice/user-1000.slice/user@1000.service/prioritize.slice ip dscp set ef
For reasons stated earlier, I prefer the cgroup approach over filtering by user/group in nftables but for completion's sake, below are the steps for the latter.
Edit /etc/nftables.conf
to filter based on voice
user:
chain output {
type filter hook output priority filter; policy accept;
meta skuid voice ip dscp set ef
}
And then run:
# add voice user
sudo useradd -m -g users voice
# logout and log back in
systemctl start nftables
cd /tmp/
# instigate network activity by running a process under voice user
sudo -u voice wget https://releases.ubuntu.com/25.04/ubuntu-25.04-desktop-amd64.iso
Pretty cool. Have you written a wrapper script to automate your preferred approach? If so would you mind sharing it?
Honestly it's just a one-time setup for steps 2 and 3 so there's nothing to script. Step 1 is just prefixing the app you want to tag. Step 4 shouldn't be necessary once I file a bug with nftables about the syntax error.
[1] owner@Taha-PC-Linux ~> sudo nft add rule inet filter output socket cgroupv2 level 4 "user.slice/user-1000.slice/user@1000.service/prioritize.slice" ip dscp set ef
Error: syntax error, unexpected number, expecting string or ll or nh or th
add rule inet filter output socket cgroupv2 level 4 user.slice/user-1000.slice/user@1000.service/prioritize.slice ip dscp set ef
So from the peanut gallery, I think I like the sg route best... it allows me to continue to ignore the details of systemd... (I am not a systemd-hater, but my little contact with it indicated that I want to keep my distance, that is not interface with systemd closely unless I really have to).
Try putting everything after rule
in single quotes.
Like this right?
[1] owner@Taha-PC-Linux ~> sudo nft add rule 'inet filter output socket cgroupv2 level 4 "user.slice/user-1000.slice/user@1000.service/prioritize.slice" ip dscp set ef'
[sudo] password for owner:
Error: cgroupv2 path fails: No such file or directory
add rule inet filter output socket cgroupv2 level 4 "user.slice/user-1000.slice/user@1000.service/prioritize.slice" ip dscp set ef
Has the cgroup changed from earlier?
Oh right! I'm not a smart man. That fixed it, thank you.
Okay now that you helped me with that, I'll post my results soon with the tool described in this article
After failing to compile systemd-cgroup-nftables-policy-manager
in that article to make the cgroup method less painful, I ended up going with the sg route (match by group). It seems to work with dota (unlike matching by user).
I created a PR summarizing the 3 methods.
I have not tried it for DSCP purposes, but indeed, sg
to one of the groups you are already a member of is something that doesn't require root. Having to do the root-only setup once (in /etc/nftables.conf
and /etc/group
) and then running the game without needing to enter any passwords has its beauty.
Sweet - thanks!
I guess I should go and test this with steam on ubuntu...