Bandwidth limit per IP/Mac

Sorry for the late reply. For some bazaar reason I didn't select the appropriate notifications for this thread and I've also been caught up doing some DIY :grin:

Please see the relevant information below:

nft list ruleset
table inet nft-qos-monitor {
        chain upload {
                type filter hook postrouting priority filter; policy accept;
                ip saddr 192.168.5.145 counter packets 2158 bytes 123786
                ip saddr 192.168.5.145 counter packets 2158 bytes 123786
                ip saddr 192.168.7.108 counter packets 0 bytes 0
                ip saddr 192.168.7.58 counter packets 0 bytes 0
                ip saddr 192.168.7.76 counter packets 0 bytes 0
                ip saddr 192.168.5.26 counter packets 0 bytes 0
                ip saddr 192.168.7.99 counter packets 8 bytes 546
                ip saddr 192.168.6.2 counter packets 0 bytes 0
                ip saddr 192.168.5.27 counter packets 0 bytes 0
                ip saddr 192.168.5.147 counter packets 1276 bytes 806657
        }

        chain download {
                type filter hook prerouting priority filter; policy accept;
                ip daddr 192.168.5.145 counter packets 5367 bytes 34985474
                ip daddr 192.168.5.145 counter packets 5367 bytes 34985474
                ip daddr 192.168.7.108 counter packets 0 bytes 0
                ip daddr 192.168.7.58 counter packets 0 bytes 0
                ip daddr 192.168.7.76 counter packets 0 bytes 0
                ip daddr 192.168.5.26 counter packets 0 bytes 0
                ip daddr 192.168.7.99 counter packets 6 bytes 466
                ip daddr 192.168.6.2 counter packets 0 bytes 0
                ip daddr 192.168.5.27 counter packets 0 bytes 0
                ip daddr 192.168.5.147 counter packets 1136 bytes 352753
        }
}
table inet nft-qos-static {
        chain upload {
                type filter hook postrouting priority filter; policy accept;
        }

        chain download {
                type filter hook prerouting priority filter; policy accept;
        }
}
table inet nft-qos-mac {
        chain upload {
                type filter hook postrouting priority filter; policy accept;
        }

        chain download {
                type filter hook prerouting priority filter; policy accept;
        }
}
uci show network
network.loopback=interface
network.loopback.device='lo'
network.loopback.proto='static'
network.loopback.ipaddr='127.0.0.1'
network.loopback.netmask='255.0.0.0'
network.globals=globals
network.globals.ula_prefix='fdb0:dc72:61cd::/48'
network.br0=device
network.br0.type='bridge'
network.br0.name='br0'
network.br0.ports='lan1' 'lan2' 'lan3' 'lan4' 'wan'
network.admin_vlan=bridge-vlan
network.admin_vlan.device='br0'
network.admin_vlan.vlan='1'
network.admin_vlan.ports='lan1:t*' 'lan2:t'
network.admin=interface
network.admin.proto='static'
network.admin.device='br0.1'
network.admin.ipaddr='192.168.1.1'
network.admin.netmask='255.255.255.0'
network.admin.delegate='0'
network.private_vlan=bridge-vlan
network.private_vlan.device='br0'
network.private_vlan.vlan='5'
network.private_vlan.ports='lan1:t' 'lan2:t'
network.private=interface
network.private.proto='static'
network.private.device='br0.5'
network.private.ipaddr='192.168.5.1'
network.private.netmask='255.255.255.0'
network.private.broadcast='192.168.5.255'
network.private.ip6assign='64'
network.private.ip6hint='5'
network.private.ip6ifaceid='::1'
network.guest_vlan=bridge-vlan
network.guest_vlan.device='br0'
network.guest_vlan.vlan='6'
network.guest_vlan.ports='lan1:t' 'lan2:t'
network.guest=interface
network.guest.proto='static'
network.guest.device='br0.6'
network.guest.ipaddr='192.168.6.1'
network.guest.netmask='255.255.255.224'
network.guest.broadcast='192.168.6.31'
network.guest.ip6assign='64'
network.guest.ip6hint='6'
network.guest.delegate='0'
network.guest.ip6ifaceid='::1'
network.guest.auto='0'
network.iot_vlan=bridge-vlan
network.iot_vlan.device='br0'
network.iot_vlan.vlan='7'
network.iot_vlan.ports='lan1:t' 'lan2:t'
network.iot=interface
network.iot.proto='static'
network.iot.device='br0.7'
network.iot.ipaddr='192.168.7.1'
network.iot.netmask='255.255.255.0'
network.iot.broadcast='192.168.7.255'
network.iot.delegate='0'
network.wan_vlan=bridge-vlan
network.wan_vlan.device='br0'
network.wan_vlan.vlan='10'
network.wan_vlan.local='0'
network.wan_vlan.ports='lan1:t' 'wan'
network.wan=interface
network.wan.device='br0.20'
network.wan.proto='static'
network.wan.ipaddr='192.168.20.1'
network.wan.netmask='255.255.255.0'
network.wan.dns='208.67.220.220' '208.67.222.222'
network.wan.delegate='0'
network.wan.gateway='192.168.20.253'
network.@bridge-vlan[5]=bridge-vlan
network.@bridge-vlan[5].device='br0'
network.@bridge-vlan[5].vlan='20'
network.@bridge-vlan[5].ports='lan3' 'lan4'
network.wan6=interface
network.wan6.proto='dhcpv6'
network.wan6.device='br0.20'
network.wan6.reqprefix='auto'
network.wan6.reqaddress='none'
network.102_vlan=bridge-vlan
network.102_vlan.device='br0'
network.102_vlan.vlan='102'
network.102_vlan.ports='lan1:t' 'lan2:t'
network.102=interface
network.102.proto='static'
network.102.device='br0.102'
network.102.ipaddr='172.16.102.1/30'
network.102.ip6assign='64'
network.102.ip6hint='102'
network.102.ip6ifaceid='::1'
uci show nft-qos
nft-qos.default=default
nft-qos.default.priority_enable='0'
nft-qos.default.limit_enable='0'
nft-qos.default.limit_mac_enable='0'
nft-qos.@download[0]=download
nft-qos.@download[0].hostname='undefined'
nft-qos.@download[0].rate='50'
nft-qos.@download[0].unit='kbytes'
nft-qos.@client[0]=client
nft-qos.@client[0].drunit='kbytes'
nft-qos.@client[0].urunit='kbytes'
nft-qos.@client[0].macaddr='C4:85:08:EB:08:89'
nft-qos.@client[0].drate='2500'
nft-qos.@client[0].urate='1250'
1 Like

set this to on

option limit_mac_enable '1'

I've enabled that but the listed device is still not being limited.

this is my working config;

#dca632 /usbstick 52°# uci show nft-qos
nft-qos.default=default
nft-qos.default.limit_enable='0'
nft-qos.default.priority_enable='0'
nft-qos.default.limit_mac_enable='1'
nft-qos.@client[0]=client
nft-qos.@client[0].drunit='kbytes'
nft-qos.@client[0].urunit='kbytes'
nft-qos.@client[0].hostname='pc'
nft-qos.@client[0].macaddr='d0:50:99:60:5b:b7'
nft-qos.@client[0].drate='5000'
nft-qos.@client[0].urate='500'
#dca632 /usbstick 53°# nft list ruleset
table bridge nft-qos-mac {
	chain upload {
		type filter hook prerouting priority 0; policy accept;
		ether saddr d0:50:99:60:5b:b7 limit rate over 500 kbytes/second drop
	}

	chain download {
		type filter hook postrouting priority 0; policy accept;
		ether daddr d0:50:99:60:5b:b7 limit rate over 5000 kbytes/second drop
	}
}

pay attention to the output above... the 'table bridge' section is what the last fix in the master branch was supposed to address on DSA devices...
(if lan is a bridge)

comparing with yours (previous outputs)... the only key thing i'm seeing is that you've used uppercase for the mac-address... which also appears to work on my system... so I assume the DSA fix has not worked (at least for the config/network layout you currently have i.e. no 'lan' at all)

this app and quite a few other ones in OpenWrt rely on 'lan' being present... ideally in the future said logic will support manual selection of these... but expect various unusual behavior with certain apps if you have no 'lan/br-lan' present...

you can copy the nft section above and change only the mac and speed and manually import/load it with nft and it should work...

That makes logical sense as I changed lan to private and on my DSA capable device I name the bridge from br-lan to br0.

Do you have any instructions on importing the configuration please?

I've been following this documentation on nftables.

  • create a text file copy my block into it
  • change the mac (lowercase) and bandwidth
  • do nft -f YOURFILE

Contents of /etc/nftables/test

table bridge nft-qos-mac {
	chain upload {
		type filter hook prerouting priority 0; policy accept;
		ether saddr c4:85:08:eb:08:89 limit rate over 1250 kbytes/second drop
	}

	chain download {
		type filter hook postrouting priority 0; policy accept;
		ether daddr c4:85:08:eb:08:89 limit rate over 2500 kbytes/second drop
	}
}

Error message received importing

/etc/nftables/test:1:27-27: Error: syntax error, unexpected junk
table bridge nft-qos-mac {
                          ^
/etc/nftables/test:2:16-16: Error: syntax error, unexpected junk
        chain upload {
                      ^
/etc/nftables/test:3:57-57: Error: syntax error, unexpected junk
                type filter hook prerouting priority 0; policy accept;
                                                                      ^
/etc/nftables/test:4:72-72: Error: syntax error, unexpected junk, expecting newline or semicolon
                ether saddr c4:85:08:eb:08:89 limit rate over 1250 kbytes/second drop
                                                                                     ^
/etc/nftables/test:5:3-3: Error: syntax error, unexpected junk, expecting newline or semicolon
        }
         ^
/etc/nftables/test:6:1-1: Error: syntax error, unexpected junk

^
/etc/nftables/test:7:2-6: Error: syntax error, unexpected chain
        chain download {
        ^^^^^
/etc/nftables/test:8:44-56: Error: you cannot set chain policy twice
                type filter hook postrouting priority 0; policy accept;
                                                         ^^^^^^^^^^^^^
/etc/nftables/test:8:58-58: Error: syntax error, unexpected junk
                type filter hook postrouting priority 0; policy accept;
                                                                       ^
/etc/nftables/test:9:72-72: Error: syntax error, unexpected junk, expecting newline or semicolon
                ether daddr c4:85:08:eb:08:89 limit rate over 2500 kbytes/second drop
                                                                                     ^

Any ideas what's going on here?

UPDATE
Visual Code set End of Line Sequence to CRLF rather LF

1 Like
grep BUILD_ID /etc/os-release
opkg list-installed | grep nft

I've now managed to create a nftables script stored at /etc/nftables/ which I've added to the Local Startup tab in LuCI.

Firstly I've removed any old tables from luci-app-nft-qos

nft delete table inet nft-qos-monitor &> /dev/null
nft delete table inet nft-qos-static &> /dev/null
nft delete table inet nft-qos-mac &> /dev/null

Secondly I then created the script

cat <<-"EOF" > "/etc/nftables/test.nft"
#!/usr/sbin/nft -f
flush ruleset

# Create a table
	add table bridge nft-qos-mac

# Create chain(s)
	add chain bridge nft-qos-mac upload {type filter hook postrouting priority 0; policy accept;}
	add chain bridge nft-qos-mac download {type filter hook prerouting priority 0; policy accept;}

# Create rule(s)
	insert rule bridge nft-qos-mac upload ether saddr C4:85:08:EB:08:89 limit rate over 1250 kbytes/second drop
	insert rule bridge nft-qos-mac download ether daddr C4:85:08:EB:08:89 limit rate over 2500 kbytes/second drop
EOF

Next I removed any unnecessary packages

opkg remove nft-qos --nodeps
opkg remove luci-app-nft-qos --nodeps

Make the script executable

chmod u+x /etc/nftables/test.nft

Lastly add the script to the startup script.

echo "/etc/nftables/test.nft" >> /etc/rc.local

Via LuCI, head to System > Startup > Local Startup and add /etc/nftables/test.nft

1 Like

good work i'm very interested by your script :wink:

Do you have any idea what the rules would be for ip6tables? Because ipv6 does not use nat the script rules would be different.
I use both ipv4 and ipv6 so I need a script that would cover both firewalls.
The script works great with ipv4.
Thanks