Help me update my HFSC shaper scripts for fw4/nftables

Only issue I am having is when I test bufferbloat I get 13-16ms of unloaded ping then it jumps to 22-26 and my min and max numbers of ping spike up like crazy even with cake in effect and internet speeds lowered. Not sure if it is my line or what but it’s annoying. Never used to have this issue before.

Have you noticed any improvements from using the snapshot compared to the stock FWRT??

Not really TBH, i used the snapshot because i found FRWT too bloated with packages i dont use, also i had issues with dscp classify, but that was down to FWRT firmware, so decided to use snapshot until offical release.

If my speeds are 2.5G down and 250mb up. Should I still input DOCSIS under link type in the script or stick with ethernet?

I have couple of questions if may

gameqdisc="pfifo"

#gameqdisc="netem"

netemdelayms="1"
netemjitterms="7"
netemdist="normal"

pktlossp="none" # set to "none" for no packet loss, or use a fraction
# like 0.015 for 1.5% packet loss in the realtime UDP
# streams

i tried increase my ping to previous isp level 30ms but when i adjust netem still showing in game 19ms?

Also to run start stop script,copy code and paste into init.d folder and then run command? /etc/init.d/SimpleHFSCgamerscript"
when i do it says error

Zia

Because the relevant line in your script is commented out. Comments in scripts (and many other types of code) start with a # symbol and are ignored by the computer. They are there for us humans, to leave notes or hints in the code without affecting its execution.

In your case, #gameqdisc="netem" means netem is not activated because the # signals that it’s a comment. To activate netem, remove the # so it looks like this: gameqdisc="netem". If you currently don’t want to use pfifo, you can comment it out by adding a # in front of it: #gameqdisc="pfifo". This way, you can switch between different settings by adding or removing comments.

In summary: Remove the # before gameqdisc="netem" to activate netem and add a # before gameqdisc="pfifo" if you want to temporarily deactivate it. Don’t forget to reload your script afterward!

Also, as mentioned before, please use the code formatting feature for your code and configurations, as it makes them much easier to read.

#gameqdisc="pfifo"

gameqdisc="netem"

netemdelayms="120"
netemjitterms="1"
netemdist="normal"

pktlossp="0.015" # set to "none" for no packet loss, or use a fraction
		# like 0.015 for 1.5% packet loss in the realtime UDP
		# streams

this is how i has set it to
yet in game report 19 in speed test also get very low ping

Zia

Have you actually installed netem? What is the output of:


opkg list-installed | grep kmod-netem

echo "adding fq_codel qdisc for non-game traffic"
for i in 12 13 14 15; do 
   #tc qdisc add dev $DEV parent 1:$i fq_codel memory_limit $((RATE*200/8)) interval "${INTVL}ms" target "${TARG}ms" quantum $((MTU * 2))

	tc qdisc add dev $DEV parent 1:$i netem

to use netem you will need to the above, as you cant use it with other qdiscs

Are my settings correct?I'm a call of duty player.

#!/bin/sh

## "atm" for old-school DSL or change to "DOCSIS" for cable modem, or
## "other" or anything else, for everything else

LINKTYPE="ethernet"

USEVETHDOWN=no
LANBR=br-lan

WAN=eth0.835 # change this to your WAN device name
UPRATE=100000 #change this to your kbps upload speed
LAN=br-lan # change to your LAN device if you don't use veth/bridge,
	   # leave it alone if you use veth, it will get set in the
	   # script below


DOWNRATE=100000 #change this to about 80% of your download speed (in kbps)
OH=44 # number of bytes of Overhead on your line (37 is reasonable
      # starting point, better to be too big than too small) probably
      # likely values are between 20 and 50

PFIFOMIN=4 ## minimum number of packets in pfifo, 4 to 10 is good guess
PACKETSIZE=350 # bytes per game packet avg (guess, 250 to 500 is likely) 
MAXDEL=15 # ms we try to keep max delay below for game packets after
	  # burst 10-25 is good 1 clock tick at 64Hz is ~16ms

BWMAXRATIO=15 ## prevent ack floods by limiting download to at most
	      ## upload times this amount... ratio somewhere between
	      ## 10 and 20 probably optimal. we down-prioritize
	      ## certain ACKs to reduce the chance of a flood as well.

if [ $((DOWNRATE > UPRATE*BWMAXRATIO)) -eq 1 ]; then
    echo "We limit the downrate to at most $BWMAXRATIO times the upstream rate to ensure no upstream ACK floods occur which can cause game packet drops"
    DOWNRATE=$((BWMAXRATIO*UPRATE))
fi

## how many kbps of UDP upload and download do you need for your games
## across all gaming machines? 

## you can tune these yourself, but a good starting point is this
## formula.  this script will not work for UPRATE less than about
## 600kbps or downrate less than about 1000kbps

GAMEUP=$((UPRATE*15/100+400))
GAMEDOWN=$((DOWNRATE*15/100+400))

## you can try setting GAMEUP and GAMEDOWN manually, some report this
## works well for CoD
#GAMEUP=1000
#GAMEDOWN=500


DSCPSCRIPT="/usr/share/nftables.d/ruleset-post/dscptag.nft"

if [ ! -f $DSCPSCRIPT ]; then
    workdir=$(pwd)
    echo "You do not have the DSCP tagging script, downloading from github"
    cd /usr/share/nftables.d/ruleset-post/
    wget https://raw.githubusercontent.com/dlakelan/routerperf/master/dscptag.nft
    cd $workdir
fi



## Right now there are four possible leaf qdiscs: pfifo, red,
## fq_codel, or netem. If you use netem it's so you can intentionally
## add delay to your packets, set netemdelayms to the number of ms you
## want to add each direction. Our default is pfifo it is reported to
## be the best for use in the realtime queue

gameqdisc="pfifo"

#gameqdisc="netem"

netemdelayms="1"
netemjitterms="7"
netemdist="normal"

pktlossp="none" # set to "none" for no packet loss, or use a fraction
		# like 0.015 for 1.5% packet loss in the realtime UDP
		# streams


if [ $gameqdisc != "fq_codel" -a $gameqdisc != "red" -a $gameqdisc != "pfifo" -a $gameqdisc != "netem" ]; then
    echo "Other qdiscs are not tested and do not work on OpenWrt yet anyway, reverting to red"
    gameqdisc="red"
fi




## Help the system prioritize your gaming by telling it what is bulk
## traffic ... define a list of udp and tcp ports used for bulk
## traffic such as torrents. By default we include the transmission
## torrent client default port 51413 and the default TCP ports for
## bittorrent. Use comma separated values or ranges A:B as shown. Set
## your torrent client to use a known port and include it here

UDPBULKPT="51413"
TCPBULKPT="51413,6881:6889"


WASHDSCPUP="yes"
WASHDSCPDOWN="yes"


######################### CUSTOMIZATIONS GO ABOVE THIS LINE ###########

if [ $USEVETHDOWN = "yes" ] ; then

    ip link show lanveth || ip link add lanveth type veth peer name lanbrport
    LAN=lanveth
    ip link set lanveth up
    ip link set lanbrport up
    ip link set lanbrport master $LANBR
    ip route flush table 100
    ip route add default dev $LAN table 100
    ip -6 route add default dev $LAN table 100
    ip rule add iif $WAN priority 100 table 100
    ip -6 rule add iif $WAN priority 100 table 100
fi



cat <<EOF

This script prioritizes the UDP packets from / to a set of gaming
machines into a real-time HFSC queue with guaranteed total bandwidth 

Based on your settings:

Game upload guarantee = $GAMEUP kbps
Game download guarantee = $GAMEDOWN kbps

Download direction only works if you install this on a *wired* router
and there is a separate AP wired into your network, because otherwise
there are multiple parallel queues for traffic to leave your router
heading to the LAN.

Based on your link total bandwidth, the **minimum** amount of jitter
you should expect in your network is about:

UP = $(((1500*8)*3/UPRATE)) ms

DOWN = $(((1500*8)*3/DOWNRATE)) ms

In order to get lower minimum jitter you must upgrade the speed of
your link, no queuing system can help.

Please note for your display rate that:

at 30Hz, one on screen frame lasts:   33.3 ms
at 60Hz, one on screen frame lasts:   16.6 ms
at 144Hz, one on screen frame lasts:   6.9 ms

This means the typical gamer is sensitive to as little as on the order
of 5ms of jitter. To get 5ms minimum jitter you should have bandwidth
in each direction of at least:

$((1500*8*3/5)) kbps

The queue system can ONLY control bandwidth and jitter in the link
between your router and the VERY FIRST device in the ISP
network. Typically you will have 5 to 10 devices between your router
and your gaming server, any of those can have variable delay and ruin
your gaming, and there is NOTHING that your router can do about it.

EOF


setqdisc () {
DEV=$1
RATE=$2
MTU=1492
highrate=$((RATE*90/100))
lowrate=$((RATE*10/100))
gamerate=$3
useqdisc=$4
DIR=$5


tc qdisc del dev "$DEV" root > /dev/null 2>&1

case $LINKTYPE in
    "atm")
	tc qdisc replace dev "$DEV" handle 1: root stab mtu 2047 tsize 512 mpu 68 overhead ${OH} linklayer atm hfsc default 13
	;;
    "DOCSIS")
	tc qdisc replace dev $DEV stab overhead 25 linklayer ethernet handle 1: root hfsc default 13
	;;
    *)
	tc qdisc replace dev $DEV stab overhead 40 linklayer ethernet handle 1: root hfsc default 13
	;;
esac
     

DUR=$((5*1500*8/RATE))
if [ $DUR -lt 25 ]; then
    DUR=25
fi

# if we're on the LAN side, create a queue just for traffic from the
# router, like LUCI and DNS lookups
if [ $DIR = "lan" ]; then
    tc class add dev "$DEV" parent 1: classid 1:2 hfsc ls m1 50000kbit d "${DUR}ms" m2 10000kbit
fi


#limit the link overall:
tc class add dev "$DEV" parent 1: classid 1:1 hfsc ls m2 "${RATE}kbit" ul m2 "${RATE}kbit"


gameburst=$((gamerate*10))
if [ $gameburst -gt $((RATE*97/100)) ] ; then
    gameburst=$((RATE*97/100));
fi


# high prio realtime class
tc class add dev "$DEV" parent 1:1 classid 1:11 hfsc rt m1 "${gameburst}kbit" d "${DUR}ms" m2 "${gamerate}kbit"

# fast non-realtime
tc class add dev "$DEV" parent 1:1 classid 1:12 hfsc ls m1 "$((RATE*70/100))kbit" d "${DUR}ms" m2 "$((RATE*30/100))kbit"

# normal
tc class add dev "$DEV" parent 1:1 classid 1:13 hfsc ls m1 "$((RATE*20/100))kbit" d "${DUR}ms" m2 "$((RATE*45/100))kbit"

# low prio
tc class add dev "$DEV" parent 1:1 classid 1:14 hfsc ls m1 "$((RATE*7/100))kbit" d "${DUR}ms" m2 "$((RATE*15/100))kbit"

# bulk
tc class add dev "$DEV" parent 1:1 classid 1:15 hfsc ls m1 "$((RATE*3/100))kbit" d "${DUR}ms" m2 "$((RATE*10/100))kbit"



## set this to "drr" or "qfq" to differentiate between different game
## packets, or use "pfifo" to treat all game packets equally

## games often use a 1/64 s = 15.6ms tick rate +- if we're getting so
## many packets that it takes that long to drain at full RATE, we're
## in trouble, because then everything lags by a full tick... so we
## set our RED minimum to start dropping at 9ms of packets at full
## line rate, and then drop 100% by 3x that much, it's better to drop
## packets for a little while than play a whole game lagged by a full
## tick

REDMIN=$((RATE*MAXDEL/3/8)) 

REDMAX=$((RATE * MAXDEL/8)) 

# for fq_codel
INTVL=$((100+2*1500*8/RATE))
TARG=$((540*8/RATE+4))



case $useqdisc in
    "drr")
	tc qdisc add dev "$DEV" parent 1:11 handle 2:0 drr
	tc class add dev "$DEV" parent 2:0 classid 2:1 drr quantum 8000
	tc qdisc add dev "$DEV" parent 2:1 handle 10: red limit 150000 min $REDMIN max $REDMAX avpkt 500 bandwidth ${RATE}kbit probability 1.0
	tc class add dev "$DEV" parent 2:0 classid 2:2 drr quantum 4000
	tc qdisc add dev "$DEV" parent 2:2 handle 20: red limit 150000 min $REDMIN max $REDMAX avpkt 500 bandwidth ${RATE}kbit probability 1.0
	tc class add dev "$DEV" parent 2:0 classid 2:3 drr quantum 1000
	tc qdisc add dev "$DEV" parent 2:3 handle 30: red limit 150000  min $REDMIN max $REDMAX avpkt 500 bandwidth ${RATE}kbit probability 1.0
	## with this send high priority game packets to 10:, medium to 20:, normal to 30:
	## games will not starve but be given relative importance based on the quantum parameter
    ;;

    "qfq")
	tc qdisc add dev "$DEV" parent 1:11 handle 2:0 qfq
	tc class add dev "$DEV" parent 2:0 classid 2:1 qfq weight 8000
	tc qdisc add dev "$DEV" parent 2:1 handle 10: red limit 150000  min $REDMIN max $REDMAX avpkt 500 bandwidth ${RATE}kbit probability 1.0
	tc class add dev "$DEV" parent 2:0 classid 2:2 qfq weight 4000
	tc qdisc add dev "$DEV" parent 2:2 handle 20: red limit 150000 min $REDMIN max $REDMAX avpkt 500 bandwidth ${RATE}kbit probability 1.0
	tc class add dev "$DEV" parent 2:0 classid 2:3 qfq weight 1000
	tc qdisc add dev "$DEV" parent 2:3 handle 30: red limit 150000  min $REDMIN max $REDMAX avpkt 500 bandwidth ${RATE}kbit probability 1.0
	## with this send high priority game packets to 10:, medium to 20:, normal to 30:
	## games will not starve but be given relative importance based on the weight parameter

    ;;

    "pfifo")
	tc qdisc add dev "$DEV" parent 1:11 handle 10: pfifo limit $((PFIFOMIN+MAXDEL*RATE/8/PACKETSIZE))
	;;
    "red")
	tc qdisc add dev "$DEV" parent 1:11 handle 10: red limit 150000 min $REDMIN max $REDMAX avpkt 500 bandwidth ${RATE}kbit  probability 1.0
	## send game packets to 10:, they're all treated the same
	;;
    "fq_codel")
	tc qdisc add dev "$DEV" parent "1:11" fq_codel memory_limit $((RATE*200/8)) interval "${INTVL}ms" target "${TARG}ms" quantum $((MTU * 2))
	;;
    "netem")
	tc qdisc add dev "$DEV" parent 1:11 handle 10: netem limit $((4+9*RATE/8/500)) delay "${netemdelayms}ms" "${netemjitterms}ms" distribution "$netemdist"
	;;


esac


echo "adding fq_codel qdisc for non-game traffic"
for i in 12 13 14 15; do 
    tc qdisc add dev "$DEV" parent "1:$i" fq_codel memory_limit $((RATE*200/8)) interval "${INTVL}ms" target "${TARG}ms" quantum $((MTU * 2))
done


}


setqdisc $WAN $UPRATE $GAMEUP $gameqdisc wan

setqdisc $LAN $DOWNRATE $GAMEDOWN $gameqdisc lan


echo "DONE!"


if [ "$gameqdisc" = "red" ]; then
   echo "Can not output tc -s qdisc because it crashes on OpenWrt when using RED qdisc, but things are working!"
else
   tc -s qdisc
fi



define udpbulkport = {51413}
define tcpbulkport = {51413,6881-6889}
define vidconfports = {10000,3478-3479,8801-8802,19302-19309,5938,53}
define realtime4 = {192.168.1.180} # example, just add all your game console here
define realtime6 = {fd90::129a} ## example only replace with game console
define lowpriolan4 = {192.168.109.2} # example, add your low priority lan machines here
define lowpriolan6 = {fd90::129a} ## example, add your low priority lan ipv6 PUBLIC addr here

define ackrate = 300

define downrate = 500 # kbits/sec ... CHANGE ME
define uprate = 1000 # kbits/sec ... CHANGE ME

define first500ms = 937500 # downrate * 500/8
define first10s = 18750000 # downrate * 10000/8

define wan = "eth0.835" # change me


table inet dscptag # forward declaration so the next command always works

delete table inet dscptag # clear all the rules

table inet dscptag {

    map priomap { type dscp : classid ;
        elements =  {ef : 1:11, cs5 : 1:11, cs6 : 1:11, cs7 : 1:11,
                    cs4 : 1:12, af41 : 1:12, af42 : 1:12,
                    cs2 : 1:14 , cs1 : 1:15, cs0 : 1:13}
    }


    set xfst4ack { typeof ip daddr . ip saddr . tcp dport . tcp sport
        flags dynamic;
        timeout 5m
    }
    set fast4ack { typeof ip daddr . ip saddr . tcp dport . tcp sport
        flags dynamic;
        timeout 5m
    }
    set med4ack { typeof ip daddr . ip saddr . tcp dport . tcp sport
        flags dynamic;
        timeout 5m
    }
    set slow4ack { typeof ip daddr . ip saddr . tcp dport . tcp sport
        flags dynamic;
        timeout 5m
    }
    set udp_meter4 {typeof ip saddr . ip daddr . udp sport . udp dport
        flags dynamic;
        timeout 5m
    }
    set udp_meter6 {typeof ip6 saddr . ip6 daddr . udp sport . udp dport
        flags dynamic;
        timeout 5m
    }
    set slowtcp4 {typeof ip saddr . ip daddr . tcp sport . tcp dport
        flags dynamic;
        timeout 5m
    }
    set slowtcp6 {typeof ip6 saddr . ip6 daddr . tcp sport . tcp dport
        flags dynamic;
        timeout 5m
    }

    chain drop995 {
        numgen random mod 1000 < 995 drop
    }
    chain drop95 {
        numgen random mod 100 < 95 drop
    }
    chain drop50 {
        numgen random mod 100 < 50 drop
    }


    chain dscptag {
        type filter hook forward priority 0; policy accept;

        # wash all the DSCP to begin with ... you can comment this out
        ip dscp set cs0 counter
        ip6 dscp set cs0 counter

        ip protocol udp udp sport $udpbulkport ip dscp set cs1
        ip6 nexthdr udp udp sport $udpbulkport ip6 dscp set cs1

        ip protocol udp udp dport $udpbulkport ip dscp set cs1
        ip6 nexthdr udp udp dport $udpbulkport ip6 dscp set cs1

        ip protocol tcp tcp sport $tcpbulkport ip dscp set cs1
        ip6 nexthdr tcp tcp sport $tcpbulkport ip6 dscp set cs1
        ip protocol tcp tcp dport $tcpbulkport ip dscp set cs1
        ip6 nexthdr tcp tcp dport $tcpbulkport ip6 dscp set cs1

        ## ack limit rate to about 150 pps by decimating the quantity of pure acks being sent
        ip protocol tcp tcp flags & ack == ack meta length < 100 add @xfst4ack {ip daddr . ip saddr . tcp dport . tcp sport limit rate over 30000/second} jump drop995 
        ip protocol tcp tcp flags & ack == ack meta length < 100 add @fast4ack {ip daddr . ip saddr . tcp dport . tcp sport limit rate over 3000/second} jump drop95
        ip protocol tcp tcp flags & ack == ack meta length < 100 add @med4ack {ip daddr . ip saddr . tcp dport . tcp sport limit rate over 300/second} jump drop50
        ip protocol tcp tcp flags & ack == ack meta length < 100 add @slow4ack {ip daddr . ip saddr . tcp dport . tcp sport limit rate over 300/second} jump drop50
        ## for almost everyone we won't send more than 150-400 acks/second

        ip protocol udp udp dport $vidconfports ip dscp set cs4
        ip6 nexthdr udp udp dport $vidconfports ip6 dscp set cs4

        ip protocol udp ip daddr $realtime4 ip dscp set ef
        ip protocol udp ip saddr $realtime4 ip dscp set ef

        ip6 nexthdr udp ip6 daddr $realtime6 ip6 dscp set ef
        ip6 nexthdr udp ip6 saddr $realtime6 ip6 dscp set ef

        ip protocol udp ip daddr $lowpriolan4 ip dscp set cs2
        ip protocol udp ip saddr $lowpriolan4 ip dscp set cs2

        ip6 nexthdr udp ip6 daddr $lowpriolan6 ip6 dscp set cs2
        ip6 nexthdr udp ip6 saddr $lowpriolan6 ip6 dscp set cs2

        #downgrade udp going faster than 450 pps, probably not realtime traffic
        ip protocol udp ip dscp > cs2 add @udp_meter4 {ip saddr . ip daddr . udp sport . udp dport limit rate over 450/second} counter ip dscp set cs2
        ip6 nexthdr udp ip6 dscp > cs2 add @udp_meter6 {ip6 saddr . ip6 daddr . udp sport . udp dport limit rate over 450/second} counter ip6 dscp set cs2

        # down prioritize the first 500ms of tcp packets
        ip protocol tcp ct bytes < $first500ms ip dscp < cs4 ip dscp set cs2

        # downgrade tcp that has transferred more than 10 seconds worth of packets
        ip protocol tcp ct bytes > $first10s ip dscp < cs4 ip dscp set cs1

        ## tcp with less than 150 pps gets upgraded to cs4
        ip protocol tcp add @slowtcp4 {ip saddr . ip daddr . tcp sport . tcp dport limit rate 150/second burst 150 packets } ip dscp set cs4
        ip6 nexthdr tcp add @slowtcp6 {ip6 saddr . ip6 daddr . tcp sport . tcp dport limit rate 150/second burst 150 packets} ip6 dscp set cs4

        ## classify for the HFSC queues:
        meta priority set ip dscp map @priomap
        meta priority set ip6 dscp map @priomap

        meta oifname $wan ip dscp set cs0 ## comment out if you don't want to wash dscp upload to internet
        meta oifname $wan ip6 dscp set cs0 ## comment out like above
    }
}

define downrate = 500 # kbits/sec ... CHANGE ME
define uprate = 1000 # kbits/sec ... CHANGE ME
This doesn't work, I did the test with Iperf3 but in UDP the download and upload always remain at 1 MB
The dscp only works on incoming why? from the router to the PC but not the other way around

Thank you for your patience,not most clued up person when it comes to networking,and have only been using openwrt in stock config for 1 year or so.
alot to learn.
this script is interesting am just trying see how,and what changes it make to my connection etc.

#!/bin/sh

## "atm" for old-school DSL or change to "DOCSIS" for cable modem, or
## "other" or anything else, for everything else

LINKTYPE="ethernet"

USEVETHDOWN=yes
LANBR=br-lan

WAN=pppoe-wan # change this to your WAN device name
UPRATE=10000 #change this to your kbps upload speed
LAN=LANBR # change to your LAN device if you don't use veth/bridge,
	   # leave it alone if you use veth, it will get set in the
	   # script below


DOWNRATE=850000 #change this to about 80% of your download speed (in kbps)
OH=44 # number of bytes of Overhead on your line (37 is reasonable
      # starting point, better to be too big than too small) probably
      # likely values are between 20 and 50

PFIFOMIN=5 ## minimum number of packets in pfifo, 4 to 10 is good guess
PACKETSIZE=350 # bytes per game packet avg (guess, 250 to 500 is likely) 
MAXDEL=25 # ms we try to keep max delay below for game packets after
	  # burst 10-25 is good 1 clock tick at 64Hz is ~16ms

BWMAXRATIO=20 ## prevent ack floods by limiting download to at most
	      ## upload times this amount... ratio somewhere between
	      ## 10 and 20 probably optimal. we down-prioritize
	      ## certain ACKs to reduce the chance of a flood as well.

if [ $((DOWNRATE > UPRATE*BWMAXRATIO)) -eq 1 ]; then
    echo "We limit the downrate to at most $BWMAXRATIO times the upstream rate to ensure no upstream ACK floods occur which can cause game packet drops"
    DOWNRATE=$((BWMAXRATIO*UPRATE))
fi

## how many kbps of UDP upload and download do you need for your games
## across all gaming machines? 

## you can tune these yourself, but a good starting point is this
## formula.  this script will not work for UPRATE less than about
## 600kbps or downrate less than about 1000kbps

GAMEUP=$((UPRATE*15/100+400))
GAMEDOWN=$((DOWNRATE*15/100+400))

## you can try setting GAMEUP and GAMEDOWN manually, some report this
## works well for CoD
#GAMEUP=400
#GAMEDOWN=800


DSCPSCRIPT="/usr/share/nftables.d/ruleset-post/dscptag.nft"

if [ ! -f $DSCPSCRIPT ]; then
    workdir=$(pwd)
    echo "You do not have the DSCP tagging script, downloading from github"
    cd /usr/share/nftables.d/ruleset-post/
    wget https://raw.githubusercontent.com/dlakelan/routerperf/master/dscptag.nft
    cd $workdir
fi



## Right now there are four possible leaf qdiscs: pfifo, red,
## fq_codel, or netem. If you use netem it's so you can intentionally
## add delay to your packets, set netemdelayms to the number of ms you
## want to add each direction. Our default is pfifo it is reported to
## be the best for use in the realtime queue

#gameqdisc="pfifo"

gameqdisc="netem"

netemdelayms="70"
netemjitterms="10"
netemdist="normal"

pktlossp="0.010" # set to "none" for no packet loss, or use a fraction
		# like 0.015 for 1.5% packet loss in the realtime UDP
		# streams


if [ $gameqdisc != "fq_codel" -a $gameqdisc != "red" -a $gameqdisc != "pfifo" -a $gameqdisc != "netem" ]; then
    echo "Other qdiscs are not tested and do not work on OpenWrt yet anyway, reverting to red"
    gameqdisc="red"
fi




## Help the system prioritize your gaming by telling it what is bulk
## traffic ... define a list of udp and tcp ports used for bulk
## traffic such as torrents. By default we include the transmission
## torrent client default port 51413 and the default TCP ports for
## bittorrent. Use comma separated values or ranges A:B as shown. Set
## your torrent client to use a known port and include it here

UDPBULKPT="51413"
TCPBULKPT="51413,6881:6889"


WASHDSCPUP="yes"
WASHDSCPDOWN="yes"


######################### CUSTOMIZATIONS GO ABOVE THIS LINE ###########

if [ $USEVETHDOWN = "yes" ] ; then

    ip link show lanveth || ip link add lanveth type veth peer name lanbrport
    LAN=lanveth
    ip link set lanveth up
    ip link set lanbrport up
    ip link set lanbrport master $LANBR
    ip route flush table 100
    ip route add default dev $LAN table 100
    ip -6 route add default dev $LAN table 100
    ip rule add iif $WAN priority 100 table 100
    ip -6 rule add iif $WAN priority 100 table 100
fi



cat <<EOF

This script prioritizes the UDP packets from / to a set of gaming
machines into a real-time HFSC queue with guaranteed total bandwidth 

Based on your settings:

Game upload guarantee = $GAMEUP kbps
Game download guarantee = $GAMEDOWN kbps

Download direction only works if you install this on a *wired* router
and there is a separate AP wired into your network, because otherwise
there are multiple parallel queues for traffic to leave your router
heading to the LAN.

Based on your link total bandwidth, the **minimum** amount of jitter
you should expect in your network is about:

UP = $(((1500*8)*3/UPRATE)) ms

DOWN = $(((1500*8)*3/DOWNRATE)) ms

In order to get lower minimum jitter you must upgrade the speed of
your link, no queuing system can help.

Please note for your display rate that:

at 30Hz, one on screen frame lasts:   33.3 ms
at 60Hz, one on screen frame lasts:   16.6 ms
at 144Hz, one on screen frame lasts:   6.9 ms

This means the typical gamer is sensitive to as little as on the order
of 5ms of jitter. To get 5ms minimum jitter you should have bandwidth
in each direction of at least:

$((1500*8*3/5)) kbps

The queue system can ONLY control bandwidth and jitter in the link
between your router and the VERY FIRST device in the ISP
network. Typically you will have 5 to 10 devices between your router
and your gaming server, any of those can have variable delay and ruin
your gaming, and there is NOTHING that your router can do about it.

EOF


setqdisc () {
DEV=$1
RATE=$2
MTU=1500
highrate=$((RATE*90/100))
lowrate=$((RATE*10/100))
gamerate=$3
useqdisc=$4
DIR=$5


tc qdisc del dev "$DEV" root > /dev/null 2>&1

case $LINKTYPE in
    "atm")
	tc qdisc replace dev "$DEV" handle 1: root stab mtu 2047 tsize 512 mpu 68 overhead ${OH} linklayer atm hfsc default 13
	;;
    "DOCSIS")
	tc qdisc replace dev $DEV stab overhead 25 linklayer ethernet handle 1: root hfsc default 13
	;;
    *)
	tc qdisc replace dev $DEV stab overhead 40 linklayer ethernet handle 1: root hfsc default 13
	;;
esac
     

DUR=$((5*1500*8/RATE))
if [ $DUR -lt 25 ]; then
    DUR=25
fi

# if we're on the LAN side, create a queue just for traffic from the
# router, like LUCI and DNS lookups
if [ $DIR = "lan" ]; then
    tc class add dev "$DEV" parent 1: classid 1:2 hfsc ls m1 50000kbit d "${DUR}ms" m2 10000kbit
fi


#limit the link overall:
tc class add dev "$DEV" parent 1: classid 1:1 hfsc ls m2 "${RATE}kbit" ul m2 "${RATE}kbit"


gameburst=$((gamerate*10))
if [ $gameburst -gt $((RATE*97/100)) ] ; then
    gameburst=$((RATE*97/100));
fi


# high prio realtime class
tc class add dev "$DEV" parent 1:1 classid 1:11 hfsc rt m1 "${gameburst}kbit" d "${DUR}ms" m2 "${gamerate}kbit"

# fast non-realtime
tc class add dev "$DEV" parent 1:1 classid 1:12 hfsc ls m1 "$((RATE*70/100))kbit" d "${DUR}ms" m2 "$((RATE*30/100))kbit"

# normal
tc class add dev "$DEV" parent 1:1 classid 1:13 hfsc ls m1 "$((RATE*20/100))kbit" d "${DUR}ms" m2 "$((RATE*45/100))kbit"

# low prio
tc class add dev "$DEV" parent 1:1 classid 1:14 hfsc ls m1 "$((RATE*7/100))kbit" d "${DUR}ms" m2 "$((RATE*15/100))kbit"

# bulk
tc class add dev "$DEV" parent 1:1 classid 1:15 hfsc ls m1 "$((RATE*3/100))kbit" d "${DUR}ms" m2 "$((RATE*10/100))kbit"



## set this to "drr" or "qfq" to differentiate between different game
## packets, or use "pfifo" to treat all game packets equally

## games often use a 1/64 s = 15.6ms tick rate +- if we're getting so
## many packets that it takes that long to drain at full RATE, we're
## in trouble, because then everything lags by a full tick... so we
## set our RED minimum to start dropping at 9ms of packets at full
## line rate, and then drop 100% by 3x that much, it's better to drop
## packets for a little while than play a whole game lagged by a full
## tick

REDMIN=$((RATE*MAXDEL/3/8)) 

REDMAX=$((RATE * MAXDEL/8)) 

# for fq_codel
INTVL=$((100+2*1500*8/RATE))
TARG=$((540*8/RATE+4))



case $useqdisc in
    "drr")
	tc qdisc add dev "$DEV" parent 1:11 handle 2:0 drr
	tc class add dev "$DEV" parent 2:0 classid 2:1 drr quantum 8000
	tc qdisc add dev "$DEV" parent 2:1 handle 10: red limit 150000 min $REDMIN max $REDMAX avpkt 500 bandwidth ${RATE}kbit probability 1.0
	tc class add dev "$DEV" parent 2:0 classid 2:2 drr quantum 4000
	tc qdisc add dev "$DEV" parent 2:2 handle 20: red limit 150000 min $REDMIN max $REDMAX avpkt 500 bandwidth ${RATE}kbit probability 1.0
	tc class add dev "$DEV" parent 2:0 classid 2:3 drr quantum 1000
	tc qdisc add dev "$DEV" parent 2:3 handle 30: red limit 150000  min $REDMIN max $REDMAX avpkt 500 bandwidth ${RATE}kbit probability 1.0
	## with this send high priority game packets to 10:, medium to 20:, normal to 30:
	## games will not starve but be given relative importance based on the quantum parameter
    ;;

    "qfq")
	tc qdisc add dev "$DEV" parent 1:11 handle 2:0 qfq
	tc class add dev "$DEV" parent 2:0 classid 2:1 qfq weight 8000
	tc qdisc add dev "$DEV" parent 2:1 handle 10: red limit 150000  min $REDMIN max $REDMAX avpkt 500 bandwidth ${RATE}kbit probability 1.0
	tc class add dev "$DEV" parent 2:0 classid 2:2 qfq weight 4000
	tc qdisc add dev "$DEV" parent 2:2 handle 20: red limit 150000 min $REDMIN max $REDMAX avpkt 500 bandwidth ${RATE}kbit probability 1.0
	tc class add dev "$DEV" parent 2:0 classid 2:3 qfq weight 1000
	tc qdisc add dev "$DEV" parent 2:3 handle 30: red limit 150000  min $REDMIN max $REDMAX avpkt 500 bandwidth ${RATE}kbit probability 1.0
	## with this send high priority game packets to 10:, medium to 20:, normal to 30:
	## games will not starve but be given relative importance based on the weight parameter

    ;;

    "pfifo")
	tc qdisc add dev "$DEV" parent 1:11 handle 10: pfifo limit $((PFIFOMIN+MAXDEL*RATE/8/PACKETSIZE))
	;;
    "red")
	tc qdisc add dev "$DEV" parent 1:11 handle 10: red limit 150000 min $REDMIN max $REDMAX avpkt 500 bandwidth ${RATE}kbit  probability 1.0
	## send game packets to 10:, they're all treated the same
	;;
    "fq_codel")
	tc qdisc add dev "$DEV" parent "1:11" fq_codel memory_limit $((RATE*200/8)) interval "${INTVL}ms" target "${TARG}ms" quantum $((MTU * 2))
	;;
    "netem")
	tc qdisc add dev "$DEV" parent 1:11 handle 10: netem limit $((4+9*RATE/8/500)) delay "${netemdelayms}ms" "${netemjitterms}ms" distribution "$netemdist"
	;;


esac


echo "adding fq_codel qdisc for non-game traffic"
for i in 12 13 14 15; do 
    #tc qdisc add dev "$DEV" parent "1:$i" fq_codel memory_limit $((RATE*200/8)) interval "${INTVL}ms" target "${TARG}ms" quantum $((MTU * 2))
     
    tc qdisc add dev $DEV parent 1:$i netem



done


}


setqdisc $WAN $UPRATE $GAMEUP $gameqdisc wan

setqdisc $LAN $DOWNRATE $GAMEDOWN $gameqdisc lan


echo "DONE!"

With script as so,this is the result

Don't seem to effect download latency

Zia

@choppyc This actually isn’t needed. With this you are applying netem on all your traffic which probably isn’t what you want… I mean for testing this could be helpful as netem only gets applied on your realtime class aka your gaming machines.

Just copy the netem dist files to
/usr/lib/tc
as described in daniels github readme.

And you should be good to go…

This is precisely what the script is designed to do. It limits the real-time class (especially UDP) to a specific bandwidth (since gaming traffic doesn't require much bandwidth) and ensures this bandwidth is always guaranteed, so that nothing competes with the bandwidth allocated for gaming traffic.

you could change this by manually changing the gameup/gamedown:

Where are you capturing your traffic (router, pc, …) and what interface?

1 Like

In this script netem is only applied on egress (upload)

When i used netem, i was getting disconnects, until i added netem as both qdisc, you dont need to add any delay to the main, just the 1.11 queue. But from my experience netem does not work properly with other qdiscs.

The NetEm qdisc does not work in conjunction with other qdiscs.

The Linux network emulator qdisc, “netem”, in its current incarnation, although useful for inserting delay and packet loss, cannot be effectively used in combination with other queueing disciplines. If you intend to insert delays or other forms of netem based packet manipulation, an entirely separate machine is required. A combination of netem + any complex qdisc (such as htb + fq_codel or RED, or qfq) WILL misbehave. Don’t do it; your data is immediately suspect if you do.

Note: netem has been improving of late… and some are using mininet and other tools.

Example of a flawed use: a netem queue has a default 1000 packet limit - if it is too small (say, you insert a 200ms delay on a gig link), it will drop packets when in excess of the queue limit. If your packet limit is too big and netem is the actual bottleneck link, it will accrue all the packets there, not in the qdisc supposedly managing the link.

https://www.bufferbloat.net/projects/codel/wiki/Best_practices_for_benchmarking_Codel_and_FQ_Codel/

You told the script you have 10Mbps upload and 850Mbps download, but the test shows you have around 8Mbps upload and 190Mbps download.

I suggest you try changing your settings to:

DOWNRATE=180000
UPRATE=7500

and retry your experience.

i has test the package ip full he broke my connexion after i don't know why

@dlakelan you think the ack is important ? because in the past i has used your script but my upload is so bad after

## ack limit rate to about 150 pps by decimating the quantity of pure acks being sent
        ip protocol tcp tcp flags & ack == ack meta length < 100 add @xfst4ack {ip daddr . ip saddr . tcp dport . tcp sport limit rate over 30000/second} jump drop995 
        ip protocol tcp tcp flags & ack == ack meta length < 100 add @fast4ack {ip daddr . ip saddr . tcp dport . tcp sport limit rate over 3000/second} jump drop95
        ip protocol tcp tcp flags & ack == ack meta length < 100 add @med4ack {ip daddr . ip saddr . tcp dport . tcp sport limit rate over 300/second} jump drop50
        ip protocol tcp tcp flags & ack == ack meta length < 100 add @slow4ack {ip daddr . ip saddr . tcp dport . tcp sport limit rate over 300/second} jump drop50
        ## for almost everyone we won't send more than 150-400 acks/second


anythink like this?

## ack limit rate to about 150 pps by decimating the quantity of pure acks being sent not fiber
        #ip protocol tcp tcp flags & ack == ack meta length < 100 add @xfst4ack {ip daddr . ip saddr . tcp dport . tcp sport limit rate over 30000/second} jump drop995 
        #ip protocol tcp tcp flags & ack == ack meta length < 100 add @fast4ack {ip daddr . ip saddr . tcp dport . tcp sport limit rate over 3000/second} jump drop95
        #ip protocol tcp tcp flags & ack == ack meta length < 100 add @med4ack {ip daddr . ip saddr . tcp dport . tcp sport limit rate over 300/second} jump drop50
        #ip protocol tcp tcp flags & ack == ack meta length < 100 add @slow4ack {ip daddr . ip saddr . tcp dport . tcp sport limit rate over 300/second} jump drop50
        ## for almost everyone we won't send more than 150-400 acks/second
	 ## ack limit rate to about 150 pps by decimating the quantity of pure acks being sent FIBER OPTIC
	ip protocol tcp tcp flags & ack == ack meta length < 100 add @xfst4ack {ip daddr . ip saddr . tcp dport . tcp sport limit rate over 30000/second} jump drop50 
  	ip protocol tcp tcp flags & ack == ack meta length < 100 add @fast4ack {ip daddr . ip saddr . tcp dport . tcp sport limit rate over 15000/second} jump drop50
  	ip protocol tcp tcp flags & ack == ack meta length < 100 add @med4ack {ip daddr . ip saddr . tcp dport . tcp sport limit rate over 10000/second} jump drop50

being less aggressive about dropping acks is a very valid strategy yes.

The ack dropping was really necessary for people doing gaming on 15000/750 kbps type lines. Upstream could be completely clogged by acks.

On more symmetric and higher speed lines like 100/100 Mbps or more ack dropping is probably unneeded entirely, but I tried to do something that would be OK anyway. Without a lot of testing it's hard to know what the right numbers are really.

1 Like

oh thank you for your very clear answer daniel,

I'm going to try your script this evening for a friend who has fiber and comment on the ACKs

Hey everyone,

I've been working on making the setup of the SimpleHFSCgamerscript even easier, especially for folks who might not be too tech-savvy. My buddy needed something simple, so I put together an init script that pretty much handles everything automatically.

Here's what the init script does:

  • Automatically installs any necessary packages.
  • Downloads missing files and places them in the right directories (this includes netem dists too).
  • Directly accesses the main script (SimpleHFSCgamerscript.sh) and sets up a veth interface if needed (for lanveth).
  • You can easily start and stop the script.
  • When stopping, it cleans up by deleting all qdiscs, veth, and nftables.

I also tweaked the main script a bit for better usability:

  • Restructured a few things to make it easier to use.
  • Integrated the dscptag.nft script directly into the main script using a "here document", so it gets created in /usr/share/nftables.d/ruleset-post every time the script runs. It uses variables from the main script, which means less duplication.

Overall, it should now be simpler to apply, though there might still be a bug or two lurking around. If anyone wants to give it a try, here's a quick guide:

How to Use the SimpleHFSCgamerscript

First, download the scripts to your OpenWrt router with this command:

wget -O /etc/init.d/SimpleHFSCgamerscript https://raw.githubusercontent.com/hudra0/routerperf/master/SimpleHFSCgamerscript && chmod +x /etc/init.d/SimpleHFSCgamerscript && wget -O /etc/SimpleHFSCgamerscript.sh https://raw.githubusercontent.com/hudra0/routerperf/master/SimpleHFSCgamerscript.sh && chmod +x /etc/SimpleHFSCgamerscript.sh

This command does a few things:

  • Downloads the init script to /etc/init.d/SimpleHFSCgamerscript and makes it executable.
  • Grabs the main script and saves it to /etc/SimpleHFSCgamerscript.sh, then makes that executable too.

Before you kick off the service, you might want to tweak the main script a bit to fit your network. Look for variables like WAN, LAN, UPRATE, DOWNRATE, etc., in the /etc/SimpleHFSCgamerscript.sh file. Just open it up in any text editor and adjust as needed.

To get everything rolling, enable and start the service with:

/etc/init.d/SimpleHFSCgamerscript enable
/etc/init.d/SimpleHFSCgamerscript start
## or
service SimpleHFSCgamerscript enable
service SimpleHFSCgamerscript start

... or via LUCI (System/Startup)

To make sure everything's running smoothly, you can check the service status:

/etc/init.d/SimpleHFSCgamerscript status

Hope this helps someone.

I'd also be interested in hearing what @dlakelan has to say about the approach.

4 Likes