Here's an example nftables file that I use on my kids computers it might be helpful to get you started:
# A simple firewall based on nftables archlinux wiki
# https://wiki.archlinux.org/index.php/nftables
flush ruleset
table inet retag {
chain dscptag {
type filter hook output priority -150;
## set cs3 for default because cs2 is throttled by ATT
ip dscp set cs3
ip6 dscp set cs3
## downgrade NFS traffic slightly
ip protocol tcp tcp dport 2049 ip dscp set cs2
ip6 nexthdr tcp tcp dport 2049 ip6 dscp set cs2
## upgrade ICMP and UDP game traffic for Steam games
ip protocol icmp ip dscp set cs5
udp dport {7000-9000, 27000-27200} ip dscp set cs5
udp sport {7000-9000, 27000-27200} ip dscp set cs5
ip6 nexthdr icmpv6 ip6 dscp set cs5
ip6 nexthdr udp udp dport {7000-9000, 27000-27200} ip6 dscp set cs5
ip6 nexthdr udp udp sport {7000-9000, 27000-27200} ip6 dscp set cs5
## VOIP servers UDP traffic (sanitized, insert your own xxxx here)
ip saddr {xxxx} ip protocol udp ip dscp set cs6
ip daddr {xxxx} ip protocol udp ip dscp set cs6
ip6 saddr {xxxx} ip6 nexthdr udp ip6 dscp set cs6
ip6 daddr {xxxx} ip6 nexthdr udp ip6 dscp set cs6
## prioritize small packet udp flows on any ports:
ip protocol udp ip dscp < cs5 udp dport != {http,https,domain} udp sport != {http,https, domain} ct avgpkt 0-450 counter ip dscp set cs5
ip6 nexthdr udp ip6 dscp < cs5 udp dport != {http,https,domain} udp sport != {http,https,domain} ct avgpkt 0-450 counter ip6 dscp set cs5
# set hfsc class based on dscp
meta priority set 1:40 ## default
ip dscp {ef,cs6} meta priority set 1:10
ip dscp {cs5} meta priority set 1:20
ip dscp {af41, af42, af43} meta priority set 1:30
ip dscp cs2 meta priority set 1:50
ip dscp {cs1} meta priority set 1:60
ip6 dscp {ef,cs6} meta priority set 1:10
ip6 dscp {cs5} meta priority set 1:20
ip6 dscp {af41, af42, af43} meta priority set 1:30
ip6 dscp cs2 meta priority set 1:50
ip6 dscp {cs1} meta priority set 1:60
}
}
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
# established/related connections
ct state established,related accept
# invalid connections
# ct state invalid log prefix "DROP INVALID: " drop
# loopback interface
iifname lo accept
# ICMP & IGMP
ip6 nexthdr icmpv6 icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem, mld-listener-query, mld-listener-report, mld-listener-reduction, nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert, ind-neighbor-solicit, ind-neighbor-advert, mld2-listener-report } accept
ip protocol icmp icmp type { destination-unreachable, router-solicitation, router-advertisement, time-exceeded, parameter-problem } accept
ip protocol igmp accept
# SSH (port 22)
ct state new ip protocol tcp tcp dport ssh meter ssh-meter4 {ip saddr limit rate 10/minute burst 10 packets} accept
ct state new ip6 nexthdr tcp tcp dport ssh meter ssh-meter6 {ip6 saddr limit rate 10/minute burst 10 packets} accept
# HTTP (ports 80 & 443)
# tcp dport { http, https } accept
udp dport bootps drop # quietly
udp dport 5353 accept # local mDNS traffic
udp dport 1900 accept # UpNP/service announcements
# steam ports, accept some, others drop quietly https://support.steampowered.com/kb_article.php?ref=8571-GLVN-8711
# https://steamcommunity.com/discussions/forum/10/359543951704487251/
udp dport 27000-27030 accept
udp dport 27031-27036 drop
tcp dport 27036-27037 drop
## rocket league from the LAN
ip protocol udp ip saddr xxxx/24 accept
ip6 nexthdr udp ip6 saddr xxxx::/64 accept
ip protocol tcp ip saddr xxxx/24 tcp dport > 4096 accept ## minecraft?
udp dport 10999 accept ## don't starve together
counter log prefix "DROP INPUT: " drop
}
chain forward {
type filter hook forward priority 0; policy drop;
}
chain output {
type filter hook output priority 0; policy accept;
}
}
Obviously since the machines this is used on are not routers, they only deal with tagging output traffic, but if you put this kind of thing on a router, you can tag incoming traffic with DSCP and prioritize it as well.
Also my custom QoS has 6 separate channels.... channel 10 and 20 are for VOIP and game traffic and use a real-time scheduler, whereas channels 30,40,50,60 are for high, medium, low, and bulk traffic.
I use the same system for my router, where I do more sophisticated stuff, like down-prioritize large transfers into cs1, and up prioritize small udp flows based on their statistics (packet size and total transfer rates)
Basically video streaming goes into high, medium is normal browsing, low is NFS, and bulk is things like large downloads (things that have transferred more than 1 second of gigabit speeds) or torrents