Linux: limit specific port rate by combining tc with iptables does not work as expected
The script blow tries to limit the incoming rate of the port 2000, when using iptables
to mark the INPUT
packets does not work, but OUTPUT
works fine.
I use nc -kl 2000
on machine 10.0.1.54
and iperf -c 10.0.1.54 -p 2000 -t 10
on another machine to test it.
Why OUTPUT
works but not INPUT
?
dev=enp3s0
ip_addr=10.0.1.54
ip_port=2000
rate_limit=20kbit
htb_class=10
if [ "$(id -u)" != "0" ]; then
echo "This script must be run as root" 1>&2
exit 1
fi
if [ "$1" = "enable" ]; then
echo "enabling rate limits"
tc qdisc del dev $dev root > /dev/null 2>&1
tc qdisc add dev $dev root handle 1: htb
tc class add dev $dev parent 1: classid 1:$htb_class htb rate $rate_limit ceil $rate_limit
tc filter add dev $dev parent 1: prio 0 protocol ip handle $htb_class fw flowid 1:$htb_class
# marking the INPUT chain does dot work as expected
#iptables -t mangle -A INPUT -d $ip_addr -p tcp --dport $ip_port -j MARK --set-mark $htb_class
# marking the OUTPUT chain works fine
iptables -t mangle -A OUTPUT -s $ip_addr -p tcp --sport $ip_port -j MARK --set-mark $htb_class
elif [ "$1" = "disable" ]; then
echo "disabling rate limits"
tc qdisc del dev $dev root > /dev/null 2>&1
#iptables -t mangle -D INPUT -d $ip_addr -p tcp --dport $ip_port -j MARK --set-mark $htb_class
iptables -t mangle -D OUTPUT -s $ip_addr -p tcp --sport $ip_port -j MARK --set-mark $htb_class
elif [ "$1" = "show" ]; then
tc qdisc show dev $dev
tc class show dev $dev
tc filter show dev $dev
iptables -t mangle -vnL INPUT
iptables -t mangle -vnL OUTPUT
else
echo "invalid arg $1"
fi
I try to answer this question by listing links below:
There are two modes of traffic shaping, INGRESS and EGRESS. INGRESS handles incoming traffic and EGRESS outgoing traffic. Linux does not support shaping/queuing on INGRESS, but only policing. Therefore IFB exists, which we can attach to the INGRESS queue while we can add any normal queuing like FQ_CODEL as EGRESS queue on the IFB device. [http://wiki.gentoo.org/wiki/Traffic_shaping]
Why tc cannot do ingress shaping? Does ingress shaping make sense?
Tc: ingress policing and ifb mirroring
So, my solution to this question is:
#!/bin/bash
dev=enp3s0
ifb_dev=ifb0
ip_addr=10.0.1.54
ip_port=2000
rate_limit=20kbit
htb_class=10
if [ "$(id -u)" != "0" ]; then
echo "This script must be run as root" 1>&2
exit 1
fi
modprobe ifb
ifconfig ifb0 up
if [ "$1" = "enable" ]; then
echo "enabling rate limits"
tc qdisc add dev $dev handle ffff: ingress
tc filter add dev $dev parent ffff: protocol ip u32 match u32 0 0 action mirred egress redirect dev $ifb_dev
tc qdisc add dev $ifb_dev root handle 1: htb
tc class add dev $ifb_dev parent 1: classid 1:$htb_class htb rate $rate_limit ceil $rate_limit
tc filter add dev $ifb_dev parent 1: protocol ip prio 1000 u32 match ip dport 2001 0xffff flowid 1:$htb_class
elif [ "$1" = "disable" ]; then
echo "disabling rate limits"
tc qdisc del dev $dev root > /dev/null 2>&1
tc qdisc del dev $dev ingress > /dev/null 2>&1
tc qdisc del dev $ifb_dev root > /dev/null 2>&1
tc qdisc del dev $ifb_dev ingress > /dev/null 2>&1
elif [ "$1" = "show" ]; then
tc qdisc show dev $dev
tc class show dev $dev
tc filter show dev $dev
else
echo "invalid arg $1"
fi
what jinzheng said is right, you cannot shape ingress traffic. but you can do it like captain jack sparrow...change the facts.
create a openvpn connection between your host and a vps with "redirect-gateway" so all the traffic goes over the vps. so that it looks like this: your home network/pc -> internet -> vps -> internet
what for your home network is INGRESS is EGRESS for the vps then shape the outgoing interface on the vps.
some tips: remember to specify your home network/ip or you will kill the vps upload rate. the vps has to have better bandwidth than your homenetwork, measure the bandwith. in germany you could just get a 7€ vps from hetzner.de they are pretty reliable.