Help me update my HFSC shaper scripts for fw4/nftables

original list was:

Preliminary package list suggests this is enough:

    kmod-sched
    ipset
    kmod-ipt-ipset
    ip-full
    kmod-veth
    iptables-mod-hashlimit
    kmod-ipt-hashlimit
    tc
    iptables-mod-ipopt
    iptables-mod-conntrack-extra

Now it's probably something like:

kmod-sched
ip-full
kmod-veth
tc
2 Likes

do you have the file twice? or maybe linked from different directories, or an /etc/config.d/fw4 config + the file in /usr/share.../?

@dlakelan

just for be clear i has create a file in usr/share... this is my files


define udpbulkport = {51413}
define tcpbulkport = {51413,6881-6889}
define vidconfports = {10000,3478-3479,8801-8802,19302-19309,5938,53}
define realtime4 = {192.168.2.160} # 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 = 46000 # kbits/sec ... CHANGE ME
define uprate = 16000 # kbits/sec ... CHANGE ME

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



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
        timeout 1h
    }
    set fast4ack { typeof ip daddr . ip saddr . tcp dport . tcp sport
        timeout 1h
    }
    set med4ack { typeof ip daddr . ip saddr . tcp dport . tcp sport
        timeout 1h
    }
    set slow4ack { typeof ip daddr . ip saddr . tcp dport . tcp sport
        timeout 1h
    }
    set udp_meter4 {typeof ip saddr . ip daddr . udp sport . udp dport
        timeout 1h
    }
    set udp_meter6 {typeof ip6 saddr . ip6 daddr . udp sport . udp dport
        timeout 1h
    }
    set slowtcp4 {typeof ip saddr . ip daddr . tcp sport . tcp dport
        timeout 1h
    }
    set slowtcp6 {typeof ip6 saddr . ip6 daddr . tcp sport . tcp dport
        timeout 1h
    }

    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 cs5
        ip protocol udp ip saddr $realtime4 ip dscp set cs5

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

        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

    }
}


and my root file is simplehfscgamerscript

this is my files

#!/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=wan # change this to your WAN device name
UPRATE=16000 #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=46000 #change this to about 80% of your download speed (in kbps)
OH=22 # 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="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=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))
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


you see an error ?thanks

@segal_72 you are the same connexion of mine if you want test :slight_smile:

ok mu rules duplicate after reboot ...

me no and my rules duplicate too

Ok, I'm going to invoke @jow and see if he knows why the firewall is duplicating these rules?

1 Like

Do a forward declaration.

table inet dscptag
flush table inet dscptag
table inet dscptag {
    ...
}

This will work in all cases.

See also

3 Likes

ok thans for your help
i has add in dscptag.nft

table inet dscptag
flush table inet dscptag
table inet dscptag {
    ...
}

my firewall appair empty now

great, I pushed a version that does this, so it should only have one set of rules from now on.

1 Like

re: I just tried the rules do not inject into the firewall

hmmm... @jow? I'm guessing because nft is atomic there isn't a clear sense of the order in which these things are happening, and the table is getting flushed after being created?

maybe try

delete  table inet dscptag

something like that?

my dscptag is well 0644 ansimplehfscgamr 0755 it's right ?

those are ok, you could make the gamer script 0744 too, so only root can execute

imho restarting the firewall should flush the ruleset entirely, but I'm not clear why it doesn't.

1 Like

i has think like this maybe

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

delete table inet dscptag # clear all the rules

add table inet dscptag {

my simple hfscgamerscript has the last update

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

and my dscptag.nft is well in ruleset-post in usr share

ok is good after has reboot my router it's a good news :wink: @dlakelan

i will reboot a second time for see :slight_smile:

perfect is not duplicate

1 Like

Good morning everyone, updated both files, but, for me it still not working the flushing of tables, even the approach provided by @Dopam-IT_1987
Router is starting with firewall table empty and without any connectivity.
BR

Yes for me this is a problem that I hope can be solved in 22.03. If a bad firewall configuration is set there is a loss of connectivity and I found I even had to reset router to failsafe to get connectivity back to remove the bad firewall entries. But for just trying stuff out this is a big problem.

Maybe there should be a default failsafe configuration that firewall falls back to in the event of any errors are encountered with custom files? Rather than crapping out and locking everything down.

How about it @jow?

Hello are you on the last snapshot,

Maybe for that

@di_Niko

1 Like

Yes, im using the latest snapshot available each day, i will test later with rc6. Thanks

1 Like

Installing 22.03 RC6 did a trick, dscptag table is loaded correctly without duplicates.

@dlakelan @Lynx

2 Likes

@dlakelan - im getting nothing in fw4, but it is working based on tc -s qdisc

ive also installed 22.03 rc6 running on NanoPI R4S