[Info] Limiting Download Speed based on MAC

I am a noob in iptables so please bear with me. I found an article relating to iptables and hashlimit here. The article specifies some iptables rules to limit a specific host's download speed through dropping packets.

I have seen so many posts on internet about people asking for QOS in openwrt where they can limit the download speeds for specific host or device(s) on their local network. Thanks to the original blog's owner, I was able to do it with the script provided below.

I created the script for myself but you can edit it and use for yourself as you please...

The script is available here. I am using Sqm QOS already and this script creates another layer of QOS scripting for certain users on my network limiting their maximum speeds according to 2 groups that I created in the script. The script contains 2 groups: 1) Slow 2) Medium, There is place to put 5 users per group but you can add more and also add more rules according to your needs.

Please dont hesitate to improve the script and post your results here. Any improvement will be greatly appreciated.

Thanks

2 Likes

See if anything noted here is helpful:
https://openwrt.org/docs/guide-user/network/traffic-shaping/packet.scheduler

LP,
Jure

Not sure why I needed to re-register as a new user but heh-ho...

Great script-works as advertised.I did find restarting the firewall before any of the rules were implemented/changed seemed to help

sleep 2
/etc/init.d/firewall restart
sleep 2

Also, I take it this speed limit will apply across the LAN network, as well as the WAN? My ultimate goal is to be able to rate limit users on a per MAC basis-but only on the incoming WAN-I dont care if they go full throttle across the LAN. Can this script be adjusted in that way easily?
I do have SQM but I find that a bit of a blunt tool-.i.e marking down all users on an interface by interface basis doesn't work for me (I dont want my Smart TV to be limited in the same way as my son's phone as an example)

cheers

cabs

are you sure that just/only sqm with piece_of_cake on the wan interface does not produce acceptable results?

Hi
My (limited) understanding is that this is more of a fair share across all devices, rather than locking down to a specific bytes per second (that this script does)?
I have SQM, have been using it but found it a bit of a sledge hammer approach-but if I have this wrong/it can limit specific devices, I'm happy to be educated on it :slight_smile:

cheers
cabs

your understanding is not wrong, fair-queueing is at the core of its functionality.

imho fixed priorities and fixed limits are the "sledge hammer approach" rather than accepting that it is a best-effort medium and thus one might as well optimize for arbitary applications (ab-)using it to their liking (which is what cake tries).

edit:
cake rudimentary supports the limiting of traffic-classes in a congestion situation and you can have a say in how it classifies (dscp markings and tc-filters in newer versions) but this brings you back to a situation where you have to change your filters/rules when an application changes its behavior.

edit2:
fixed limiting of a device/application very rarely makes sense apart from charging the user for more speed :wink:
cake is apparently growing support for this...

Fair comment-my issue which I have is that I have a pretty poor ADSL2 connection with only 9Mb/sec on the WAN side, and I have a few users, who, left unchecked, will easily consume all of that with a few streaming connections. They only need 2-3Mb/sec for a decent picture-what I have observed is that with the real time graphs of OpenWRT, they will spike up and hit that 9Mb/sec if left unchecked-for a sustained period of time. Using this ip-tables approach is the best fit I have seen so far. Once I get my 150/150 full fibre connection, then I'll probably go back to the SQM approach :slight_smile:

is this a problem on its own, is your connection metered?
or is your streaming bad in this case?

your latency(ping) should not be affected if cake is working as intended.

cheers

Have you tried cake's per IP fairness modes as described in the " Making cake sing and dance, on a tight rope without a safety net" section that can be found under https://openwrt.org/docs/guide-user/network/traffic-shaping/sqm-details that should give you fairness per-internal IP addresses so that your streamers will get the full 9 Mbps if they are the only users, but will be scaled back proportionally the more different internal IP-addresses send/receive traffic. For many (but not all) users this kind of fairness seems to be good enough not wanting to bother any more about more detailed QoS configuration. So I do not want to claim that it will be good enough for you, but if you have not tried that yet maybe you want to play with it.
Also, since you are on ADSL2, even though you probably know already, have you properly accounted for the link layer peculiarities of the ATM/AAL5 carrier used by adsl?

On poster Fuller's point, latency seems fine/its not a big concern. The connection is not metered, no. The streamers-they are fine-but when I'm on a Webex downstairs, it goes all to pot-and when I look at my graphs, the ADSL is being hammered by them upstairs! :slight_smile:

On poster moeller0, that sounds like a good shout-I must admit, when looking at the help stuff, when you look at SQM, it does seem rather overwhelming-so I probably need to spend some time on it, and not be lazy. When I came across the ip-tables script, it just "worked"-and seemed simple enough to implement-and does work as described-hence why I used it.
Final point on the ADSL mac/phy side-where I am, and with the full fibre about to hit us, I've resigned myself to no mater what I do, I'm not going to squeeze much more out of this line :frowning:

cheers

Well, all of this is no rocket sicence :wink: How about you post:
a) the sync values of your modem for up- and downstream
b) the output of "cat /etc/config/sqm"
c) the output of "tc -s qdisc"
d) the output of "tc -d qdisc"
with b-d run from a ssh session to your openwrt router? I will happily try to see whether we can try to improve your sqm experience with a single round (well, make that a few :wink: ) of changes.

not sure why webex would suffer from cake, so i suspect this is rather an issue with wifi that you are running into because the streamers consume too much airtime...
speaking of time, fair point not wanting to spend much time on this. considering you have a working solution.
i like to consider cake as the touch-once and be done with it solution :slight_smile:

will be interesting to see if the problem persists with your faster connection

Thanks for the script, It's simple and useful.
Also TC is not needed.

Hi @ahmar16

Thanks a lot for your script. I have use it and I have change some little things (I think that in delete commands there is an error (not see because > /dev/null)

I am improving it by doing some optional daily limit and after that, the bandwidth will be reduced. (I haven't yet done the montly limit, probably another iptables command before the daily one. (only if values are great than 0)

I also have to add crontab jobs to reset daily and monthly limits.

I want to share it in order to help other users, so they will help me also, and all of us will improve the script

What do you think about it ? I am a newbie so I hope you could check it

But is this script neccesary to run at start up? or do we have to save iptables rules?
For example: OpenWRT will run the contents of /etc/firewall.user at startup. Is this the better place to write down this script?

The lines which I have doubt or pending are markt with ###

And the ones about "QUOTA" are between #QUOTA lines

Hope your answer/help

NOTE: what is "2" in delete lines? ( .... 2 > /dev/null ) I have commented it and everything works well.

#! /bin/sh
# Slow Marking Users
S1Name="ther phone"
S1MAC=xx:xx:xx:xx:xx:xx
S2Name="Nomi Phone"
S2MAC=xx:xx:xx:xx:xx:xx
S3Name="Zoha Phone"
S3MAC=xx:xx:xx:xx:xx:xx
S4Name=""
S4MAC=
S5Name=""
S5MAC=


# Medium Marking Users
M1Name=""
M1MAC=
M2Name=""
M2MAC=
M3Name=""
M3MAC=
M4Name=""
M4MAC=
M5Name=""
M5MAC=


# Speed Groups
Group1Name="Slow Internet"
Group2Name="Medium Internet"
SlowInternet=60kb/s
MediumInternet=100kb/s
# kb/s means kilobytes per second
SlowMark=0xA
MediumMark=0xB

# QUOTA (bytes. 0 === no limit)
SlowInternetQuotaDay=0
MediumInternetQuotaDay=20480   #for example, 20Mb.
SlowInternetQuotaMonth=0
MediumInternetQuotaMonth=614400 
# QUOTA

### 0 === no limit is pending
### monthly quota is pending

### Have we got to delete it ? 
iptables -t mangle -D PREROUTING -j CONNMARK --restore-mark

# delete the Connection markings and suppress the errors
echo "Deleting existing rules..."
iptables -t nat -D prerouting_rule -m mark --mark $SlowMark -j CONNMARK --save-mark # 2> /dev/null
iptables -t nat -D prerouting_rule -m mark --mark $MediumMark -j CONNMARK --save-mark # 2> /dev/null

# delete the limiting rules and suppress errors
iptables -D forwarding_rule -m mark --mark $SlowMark -m conntrack --ctstate ESTABLISHED,RELATED -m hashlimit --hashlimit-name "$Group1Name" --hashlimit-above $SlowInternet -j DROP # 2> /dev/null
iptables -D forwarding_rule -m mark --mark $MediumMark -m conntrack --ctstate ESTABLISHED,RELATED -m hashlimit --hashlimit-name "$Group2Name" --hashlimit-above $MediumInternet -j DROP # 2> /dev/null

#delete the slow markings
iptables -t nat -D prerouting_rule -m mac --mac-source $S1MAC -j MARK --set-mark $SlowMark -m comment --comment "$S1Name" # 2> /dev/null
iptables -t nat -D prerouting_rule -m mac --mac-source $S2MAC -j MARK --set-mark $SlowMark -m comment --comment "$S2Name" # 2> /dev/null
iptables -t nat -D prerouting_rule -m mac --mac-source $S3MAC -j MARK --set-mark $SlowMark -m comment --comment "$S3Name" # 2> /dev/null

# delete the medium markings
iptables -t nat -D prerouting_rule -m mac --mac-source $M1MAC -j MARK --set-mark $MediumMark -m comment --comment "$M1Name" # 2> /dev/null


# QUOTA
# delete the quota entries
iptables -t nat -D prerouting_rule -m mac --mac-source $S1MAC -m quota --quota $SlowInternetQuotaDay -j RETURN -m comment --comment "$S1Name" # 2> /dev/null
iptables -t nat -D prerouting_rule -m mac --mac-source $M1MAC -m quota --quota $MediumInternetQuotaDay -j RETURN -m comment --comment "$M1Name" # 2> /dev/null
# QUOTA


echo "Deletion of existing rules -> Done!"

# QUOTA
echo "Creating QUOTA new rules for slow markings..."
# Rules to set slow markings
iptables -t nat -A prerouting_rule -m mac --mac-source $S1MAC -m quota --quota $SlowInternetQuotaDay -j RETURN -m comment --comment "$S1Name"
# Rules to set medium markings
echo "Creating QUOTA new rules for medium markings..."
iptables -t nat -A prerouting_rule -m mac --mac-source $M1MAC -m quota --quota $MediumInternetQuotaDay -j RETURN -m comment --comment "$M1Name"
# QUOTA



echo "Creating new rules for slow markings..."
# Rules to set slow markings
iptables -t nat -A prerouting_rule -m mac --mac-source $S1MAC -j MARK --set-mark $SlowMark -m comment --comment "$S1Name"
#iptables -t nat -A prerouting_rule -m mac --mac-source $S2MAC -j MARK --set-mark $SlowMark -m comment --comment "$S2Name"
#iptables -t nat -A prerouting_rule -m mac --mac-source $S3MAC -j MARK --set-mark $SlowMark -m comment --comment "$S3Name"

# Rules to set medium markings
echo "Creating new rules for medium markings..."
iptables -t nat -A prerouting_rule -m mac --mac-source $M1MAC -j MARK --set-mark $MediumMark -m comment --comment "$M1Name"

echo "Marking packet connections with appropriate marks..."
# If a packet is marked, make sure the connection is marked as well
# mark the appropriate connections
iptables -t nat -A prerouting_rule -m mark --mark $SlowMark -j CONNMARK --save-mark
iptables -t nat -A prerouting_rule -m mark --mark $MediumMark -j CONNMARK --save-mark

# On incoming packets, make sure to read the connection mark back into the packet
iptables -t mangle -A PREROUTING -j CONNMARK --restore-mark

echo "Marking connections with appropriate limited speed..."
# limit the speed
iptables -A forwarding_rule -m mark --mark $SlowMark -m conntrack --ctstate ESTABLISHED,RELATED -m hashlimit --hashlimit-name "$Group1Name" --hashlimit-above $SlowInternet -j DROP
iptables -A forwarding_rule -m mark --mark $MediumMark -m conntrack --ctstate ESTABLISHED,RELATED -m hashlimit --hashlimit-name "$Group2Name" --hashlimit-above $MediumInternet -j DROP

Thanks for taking your time to improve it @ortegafernando. I haven't really worked on it for quite some time. But I'll let you know when I check it and hopefully make some improvements in handling all the rules and for checking any errors.

I'd also like to point out that 2> was supposed to suppress any output related to errors or otherwise but maybe I'll need to figure out a better way to do it. I'll take a look at it in the morning and update this reply with more info.

Update: You may be able to achieve your quotas through this but I have not tested it myself. I have also updated the script with some more info, you can test with that.

Hi @ahmar16 Thanks a lot

In order to continue with this script, please answer me about:

" But is this script neccesary to run at start up? or do we have to save iptables rules?
For example: OpenWRT will run the contents of /etc/firewall.user at startup. Is this the better place to write down this script? You can write down in LUCI web interface"

Does the script need to have included the crontabs commands? Or is there any way to delete/check if crontab command exists and do what you need: delete, update, ...

About OpenWrtQuotas I had already seen it, but I prefer yours as it is configurable per MAC, and OpenWrtQuotas is more for a free wifi AP, isn't it?. May be I have to recheck it again after understanding yours.

Thanks.

It's better to run the script in /etc/firewall.user because the script will run on every firewall restart too. There's no need to save iptables rules, firewall will take care of everything.

Putting it in crontab can be an overkill, you’ll need to modify the script to check if the rules exist already or not and then add them accordingly.

I checked the script and made some adjustments. You can try it and see if it works for you. I don't use the script myself anymore because I don't need to hard limit any speeds for now.

i got following error in system log
Wed May 6 12:53:21 2020 daemon.err uhttpd[906]: * Running script '/etc/firewall.user'
Wed May 6 12:53:21 2020 daemon.err uhttpd[906]: iptables v1.8.3 (legacy): unknown option "--save-mark"
Wed May 6 12:53:21 2020 daemon.err uhttpd[906]: Try iptables -h' or 'iptables --help' for more information. Wed May 6 12:53:21 2020 daemon.err uhttpd[906]: iptables v1.8.3 (legacy): unknown option "--restore-mark" Wed May 6 12:53:21 2020 daemon.err uhttpd[906]: Try iptables -h' or 'iptables --help' for more information

I think something has changed in the newer versions of iptables so the script will need to be adapted. But I can't work on it right now, so it will have to wait.

1 Like

i have fixed the problems, some necessary pakages was missing for CONNMARK.. installtion of them fixes the problem. thank you.