mirror of
https://github.com/novatiq/packages.git
synced 2026-04-30 07:28:39 +01:00
nft-qos: add new package
This is the nftables implementation for qos on OpenWrt, Currently, it has below features: * Static QoS : setting limit rate for devices or global network. * Dynamic/Auto QoS : setting limit rate according to the network bandwidth and adjust itself automatically (hotplug event). * Traffic Priority : this feature is like traffic shaping under tc, it uses ingress hook to handle to packets here. Signed-off-by: Rosy Song <rosysong@rosinson.com>
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (C) 2018 rosysong@rosinson.com
|
||||
#
|
||||
|
||||
# for uci_validate_section()
|
||||
. /lib/functions/procd.sh
|
||||
|
||||
NFT_QOS_HAS_BRIDGE=
|
||||
NFT_QOS_INET_FAMILY=ip
|
||||
NFT_QOS_SCRIPT_TEXT=
|
||||
NFT_QOS_SCRIPT_FILE=/tmp/qos.nft
|
||||
|
||||
qosdef_appendx() { # <string to be appended>
|
||||
NFT_QOS_SCRIPT_TEXT="$NFT_QOS_SCRIPT_TEXT""$1"
|
||||
}
|
||||
|
||||
qosdef_append_chain_def() { # <type> <hook> <priority> <policy>
|
||||
qosdef_appendx "\t\ttype $1 hook $2 priority $3; policy $4;\n"
|
||||
}
|
||||
|
||||
qosdef_append_chain_ingress() { # <type> <device> <priority> <policy>
|
||||
qosdef_appendx "\t\ttype $1 hook ingress device $2 priority $3; policy $4;\n"
|
||||
}
|
||||
|
||||
# qosdef_append_rule_{MATCH}_{STATEMENT}
|
||||
qosdef_append_rule_ip_limit() { # <ipaddr> <operator> <unit> <rate>
|
||||
local ipaddr=$1
|
||||
local operator=$2
|
||||
local unit=$3
|
||||
local rate=$4
|
||||
|
||||
qosdef_appendx \
|
||||
"\t\tip $operator $ipaddr limit rate over $rate $unit/second drop\n"
|
||||
}
|
||||
|
||||
# qosdef_append_rule_{MATCH}_{POLICY}
|
||||
qosdef_append_rule_ip_policy() { # <operator> <ipaddr> <policy>
|
||||
qosdef_appendx "\t\tip $1 $2 $3\n"
|
||||
}
|
||||
|
||||
_handle_limit_whitelist() { # <value> <chain>
|
||||
local ipaddr=$1
|
||||
local operator
|
||||
|
||||
[ -z "$ipaddr" ] && return
|
||||
|
||||
case "$2" in
|
||||
download) operator="daddr";;
|
||||
upload) operator="saddr";;
|
||||
esac
|
||||
|
||||
qosdef_append_rule_ip_policy $operator $ipaddr accept
|
||||
}
|
||||
|
||||
qosdef_append_rule_limit_whitelist() { # <chain>
|
||||
config_list_foreach default limit_whitelist _handle_limit_whitelist $1
|
||||
}
|
||||
|
||||
qosdef_flush_table() { # <family> <table>
|
||||
nft flush table $1 $2 2>/dev/null
|
||||
}
|
||||
|
||||
qosdef_remove_table() { # <family> <table>
|
||||
nft delete table $1 $2 2>/dev/null
|
||||
}
|
||||
|
||||
qosdef_init_header() { # add header for nft script
|
||||
qosdef_appendx "#!/usr/sbin/nft -f\n"
|
||||
qosdef_appendx "# Copyright (C) 2018 rosysong@rosinson.com\n"
|
||||
qosdef_appendx "#\n\n"
|
||||
}
|
||||
|
||||
qosdef_init_env() {
|
||||
# check interface type of lan
|
||||
local lt="$(uci_get "network.lan.type")"
|
||||
[ "$lt" = "bridge" ] && export NFT_QOS_HAS_BRIDGE="y"
|
||||
|
||||
# check if ipv6 support
|
||||
[ -e /proc/sys/net/ipv6 ] && export NFT_QOS_INET_FAMILY="inet"
|
||||
}
|
||||
|
||||
qosdef_clean_cache() {
|
||||
rm -f $NFT_QOS_SCRIPT_FILE
|
||||
}
|
||||
|
||||
qosdef_init_done() {
|
||||
echo -e $NFT_QOS_SCRIPT_TEXT > $NFT_QOS_SCRIPT_FILE 2>/dev/null
|
||||
}
|
||||
|
||||
qosdef_start() {
|
||||
nft -f $NFT_QOS_SCRIPT_FILE 2>/dev/null
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (C) 2018 rosysong@rosinson.com
|
||||
#
|
||||
|
||||
. /lib/nft-qos/core.sh
|
||||
|
||||
# return average rate for dhcp leases
|
||||
qosdef_dynamic_rate() { # <bandwidth>
|
||||
local c=0 c6=0
|
||||
|
||||
[ ! -e /tmp/dhcp.leases -a \
|
||||
! -e /var/dhcp6.leases ] && return
|
||||
|
||||
[ -e /tmp/dhcp.leases ] && \
|
||||
c=$(wc -l < /tmp/dhcp.leases 2>/dev/null)
|
||||
[ -e /var/dhcp6.leases ] && \
|
||||
c6=$(wc -l < /var/dhcp6.leases 2>/dev/null)
|
||||
[ $c -eq 0 -a $c6 -eq 0 ] && \
|
||||
{ echo 12500; return; }
|
||||
|
||||
echo $(($1 / ($c + $c6)))
|
||||
}
|
||||
|
||||
qosdef_append_chain_dym() { # <hook> <name> <bandwidth>
|
||||
local cidr cidr6
|
||||
local operator rate
|
||||
local hook=$1 name=$2 bandwidth=$3
|
||||
|
||||
config_get cidr default 'dynamic_cidr'
|
||||
config_get cidr6 default 'dynamic_cidr6'
|
||||
|
||||
[ -z "$cidr" -a -z "$cidr6" ] && return
|
||||
|
||||
case "$2" in
|
||||
download) operator=daddr;;
|
||||
upload) operator=saddr;;
|
||||
esac
|
||||
|
||||
rate=$(qosdef_dynamic_rate $bandwidth)
|
||||
|
||||
qosdef_appendx "\tchain $name {\n"
|
||||
qosdef_append_chain_def filter $hook 0 accept
|
||||
qosdef_append_rule_limit_whitelist $name
|
||||
[ -n "$cidr" ] && \
|
||||
qosdef_append_rule_ip_limit $cidr $operator kbytes $rate
|
||||
[ -n "$cidr6" ] && \
|
||||
qosdef_append_rule_ip_limit $cidr6 $operator kbytes $rate
|
||||
qosdef_appendx "\t}\n"
|
||||
}
|
||||
|
||||
qosdef_flush_dynamic() {
|
||||
qosdef_flush_table "$NFT_QOS_INET_FAMILY" nft-qos-dynamic
|
||||
}
|
||||
|
||||
# init dynamic qos
|
||||
qosdef_init_dynamic() {
|
||||
local dynamic_bw_up dynamic_bw_down limit_enable limit_type
|
||||
local hook_ul="input" hook_dl="postrouting"
|
||||
|
||||
uci_validate_section nft-qos default default \
|
||||
'limit_enable:bool:0' \
|
||||
'limit_type:maxlength(8)' \
|
||||
'dynamic_bw_up:uinteger:100' \
|
||||
'dynamic_bw_down:uinteger:100'
|
||||
|
||||
[ $? -ne 0 ] && {
|
||||
logger -t nft-qos-dynamic "validation failed"
|
||||
return 1
|
||||
}
|
||||
|
||||
[ $limit_enable -eq 0 -o \
|
||||
"$limit_type" = "static" ] && return 1
|
||||
|
||||
# Transfer mbits/s to mbytes/s
|
||||
# e.g. 100,000 kbits == 12,500 kbytes
|
||||
dynamic_bw_up=$(($dynamic_bw_up * 1000 / 8))
|
||||
dynamic_bw_down=$(($dynamic_bw_down * 1000 / 8))
|
||||
|
||||
[ -z "$NFT_QOS_HAS_BRIDGE" ] && {
|
||||
hook_ul="postrouting"
|
||||
hook_dl="input"
|
||||
}
|
||||
|
||||
qosdef_appendx "table $NFT_QOS_INET_FAMILY nft-qos-dynamic {\n"
|
||||
qosdef_append_chain_dym $hook_ul upload $dynamic_bw_up
|
||||
qosdef_append_chain_dym $hook_dl download $dynamic_bw_down
|
||||
qosdef_appendx "}\n"
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (C) 2018 rosysong@rosinson.com
|
||||
#
|
||||
|
||||
. /lib/nft-qos/core.sh
|
||||
|
||||
qosdef_monitor_get_ip_handle() { # <family> <chain> <ip>
|
||||
echo $(nft list chain $1 nft-qos-monitor $2 -a 2>/dev/null | grep $3 | awk '{print $11}')
|
||||
}
|
||||
|
||||
qosdef_monitor_add() { # <mac> <ip> <hostname>
|
||||
handle_dl=$(qosdef_monitor_get_ip_handle $NFT_QOS_INET_FAMILY download $2)
|
||||
[ -z "$handle_dl" ] && nft add rule $NFT_QOS_INET_FAMILY nft-qos-monitor download ip daddr $2 counter
|
||||
handle_ul=$(qosdef_monitor_get_ip_handle $NFT_QOS_INET_FAMILY upload $2)
|
||||
[ -z "$handle_ul" ] && nft add rule $NFT_QOS_INET_FAMILY nft-qos-monitor upload ip saddr $2 counter
|
||||
}
|
||||
|
||||
qosdef_monitor_del() { # <mac> <ip> <hostname>
|
||||
local handle_dl handle_ul
|
||||
handle_dl=$(qosdef_monitor_get_ip_handle $NFT_QOS_INET_FAMILY download $2)
|
||||
handle_ul=$(qosdef_monitor_get_ip_handle $NFT_QOS_INET_FAMILY upload $2)
|
||||
[ -n "$handle_dl" ] && nft delete handle $handle_dl
|
||||
[ -n "$handle_ul" ] && nft delete handle $handle_ul
|
||||
}
|
||||
|
||||
# init qos monitor
|
||||
qosdef_init_monitor() {
|
||||
local hook_ul="input" hook_dl="postrouting"
|
||||
|
||||
[ -z "$NFT_QOS_HAS_BRIDGE" ] && {
|
||||
hook_ul="postrouting"
|
||||
hook_dl="input"
|
||||
}
|
||||
|
||||
nft add table $NFT_QOS_INET_FAMILY nft-qos-monitor
|
||||
nft add chain $NFT_QOS_INET_FAMILY nft-qos-monitor upload { type filter hook $hook_ul priority 0\; }
|
||||
nft add chain $NFT_QOS_INET_FAMILY nft-qos-monitor download { type filter hook $hook_dl priority 0\; }
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (C) 2018 rosysong@rosinson.com
|
||||
#
|
||||
|
||||
. /lib/functions/network.sh
|
||||
. /lib/nft-qos/core.sh
|
||||
|
||||
P1=""; P2=""; P3=""; P4=""; P5=""; P6="";
|
||||
P7=""; P8=""; P9=""; P10=""; P11="";
|
||||
|
||||
_qosdef_handle_protox() { # <priority> <rule>
|
||||
case "$1" in
|
||||
-400) P1="$P1""$2";;
|
||||
-300) P2="$P2""$2";;
|
||||
-225) P3="$P3""$2";;
|
||||
-200) P4="$P4""$2";;
|
||||
-150) P5="$P5""$2";;
|
||||
-100) P6="$P6""$2";;
|
||||
0) P7="$P7""$2";;
|
||||
50) P8="$P8""$2";;
|
||||
100) P9="$P9""$2";;
|
||||
225) P10="$P10""$2";;
|
||||
300) P11="$P11""$2";;
|
||||
esac
|
||||
}
|
||||
|
||||
qosdef_handle_protox() { # <section>
|
||||
local proto prio srv
|
||||
|
||||
config_get proto $1 'protocol'
|
||||
config_get prio $1 'priority'
|
||||
config_get srv $1 'service'
|
||||
|
||||
[ -z "$proto" -o \
|
||||
-z "$prio" -o \
|
||||
-z "$srv" ] && return
|
||||
|
||||
_qosdef_handle_protox $prio \
|
||||
"\t\t$proto dport { $srv } accept\n"
|
||||
}
|
||||
|
||||
qosdef_append_rule_protox() { # <section>
|
||||
config_foreach qosdef_handle_protox $1
|
||||
qosdef_appendx \
|
||||
"${P1}${P2}${P3}${P4}${P5}${P6}${P7}${P8}${P9}${P10}${P11}"
|
||||
}
|
||||
|
||||
qosdef_append_chain_priority() { # <name> <section> <device>
|
||||
local name=$1 device=$3
|
||||
|
||||
qosdef_appendx "\tchain $name {\n"
|
||||
qosdef_append_chain_ingress filter $device 0 accept
|
||||
qosdef_append_rule_protox $2
|
||||
qosdef_appendx "\t}\n"
|
||||
}
|
||||
|
||||
qosdef_remove_priority() {
|
||||
qosdef_remove_table netdev nft-qos-priority
|
||||
}
|
||||
|
||||
# init traffic priority
|
||||
qosdef_init_priority() {
|
||||
local priority_enable priority_netdev ifname="br-lan"
|
||||
|
||||
uci_validate_section nft-qos default default \
|
||||
'priority_enable:bool:0' \
|
||||
'priority_netdev:maxlength(8)'
|
||||
|
||||
[ $? -ne 0 ] && {
|
||||
logger -t nft-qos-priority "validation failed"
|
||||
return 1
|
||||
}
|
||||
|
||||
[ $priority_enable -eq 0 ] && return 1
|
||||
|
||||
case "$priority_netdev" in
|
||||
lan) [ "$(uci_get network.lan.type)" != "bridge" ] && {
|
||||
network_get_device ifname "$priority_netdev" || \
|
||||
ifname="$(uci_get network.lan.ifname)"
|
||||
}
|
||||
;;
|
||||
wan*) network_get_device ifname "$priority_netdev" || \
|
||||
ifname="$(uci_get network.$priority_netdev.ifname)"
|
||||
esac
|
||||
|
||||
qosdef_appendx "table netdev nft-qos-priority {\n"
|
||||
qosdef_append_chain_priority filter priority $ifname
|
||||
qosdef_appendx "}\n"
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (C) 2018 rosysong@rosinson.com
|
||||
#
|
||||
|
||||
. /lib/nft-qos/core.sh
|
||||
|
||||
# append rule for static qos
|
||||
qosdef_append_rule_sta() { # <section> <operator> <default-unit> <default-rate>
|
||||
local ipaddr unit rate
|
||||
local operator=$2
|
||||
|
||||
config_get ipaddr $1 ipaddr
|
||||
config_get unit $1 unit $3
|
||||
config_get rate $1 rate $4
|
||||
|
||||
[ -z "$ipaddr" ] && return
|
||||
|
||||
qosdef_append_rule_ip_limit $ipaddr $operator $unit $rate
|
||||
}
|
||||
|
||||
# append chain for static qos
|
||||
qosdef_append_chain_sta() { # <hook> <name> <section> <unit> <rate>
|
||||
local hook=$1 name=$2
|
||||
local config=$3 operator
|
||||
|
||||
case "$name" in
|
||||
download) operator="daddr";;
|
||||
upload) operator="saddr";;
|
||||
esac
|
||||
|
||||
qosdef_appendx "\tchain $name {\n"
|
||||
qosdef_append_chain_def filter $hook 0 accept
|
||||
qosdef_append_rule_limit_whitelist $name
|
||||
config_foreach qosdef_append_rule_sta $config $operator $4 $5
|
||||
qosdef_appendx "\t}\n"
|
||||
}
|
||||
|
||||
qosdef_flush_static() {
|
||||
qosdef_flush_table "$NFT_QOS_INET_FAMILY" nft-qos-static
|
||||
}
|
||||
|
||||
# static limit rate init
|
||||
qosdef_init_static() {
|
||||
local unit_dl unit_ul rate_dl rate_ul
|
||||
local limit_enable limit_type hook_ul="input" hook_dl="postrouting"
|
||||
|
||||
uci_validate_section nft-qos default default \
|
||||
'limit_enable:bool:0' \
|
||||
'limit_type:maxlength(8)' \
|
||||
'static_unit_dl:string:kbytes' \
|
||||
'static_unit_ul:string:kbytes' \
|
||||
'static_rate_dl:uinteger:50' \
|
||||
'static_rate_ul:uinteger:50'
|
||||
|
||||
[ $? -ne 0 ] && {
|
||||
logger -t nft-qos-static "validation failed"
|
||||
return 1
|
||||
}
|
||||
|
||||
[ $limit_enable -eq 0 -o \
|
||||
$limit_type = "dynamic" ] && return 1
|
||||
|
||||
[ -z "$NFT_QOS_HAS_BRIDGE" ] && {
|
||||
hook_ul="postrouting"
|
||||
hook_dl="input"
|
||||
}
|
||||
|
||||
qosdef_appendx "table $NFT_QOS_INET_FAMILY nft-qos-static {\n"
|
||||
qosdef_append_chain_sta $hook_ul upload upload $unit_ul $rate_ul
|
||||
qosdef_append_chain_sta $hook_dl download download $unit_dl $rate_dl
|
||||
qosdef_appendx "}\n"
|
||||
}
|
||||
Reference in New Issue
Block a user