diff --git a/net/trafficshaper/Makefile b/net/trafficshaper/Makefile index 880fd28036301..8be1f38a02b6c 100644 --- a/net/trafficshaper/Makefile +++ b/net/trafficshaper/Makefile @@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=trafficshaper PKG_VERSION:=1.0.0 -PKG_RELEASE:=3 +PKG_RELEASE:=4 PKG_MAINTAINER:=Luiz Angelo Daros de Luca PKG_LICENSE:=GPL-2.0-or-later @@ -19,7 +19,12 @@ define Package/trafficshaper SECTION:=net CATEGORY:=Network TITLE:=WAN traffic shaper based on LAN addresses - DEPENDS:=+tc +kmod-sched-core +kmod-sched-connmark +kmod-ifb +iptables +IPV6:ip6tables +kmod-sched-cake +iptables-mod-conntrack-extra + DEPENDS:=+tc +kmod-sched-core +kmod-sched-connmark +kmod-ifb \ + +(PACKAGE_nftables-json||PACKAGE_nftables-nojson):nftables \ + +!(PACKAGE_nftables-json||PACKAGE_nftables-nojson):iptables \ + +(IPV6&&!(PACKAGE_nftables-json||PACKAGE_nftables-nojson)):ip6tables \ + +kmod-sched-cake \ + +!(PACKAGE_nftables-json||PACKAGE_nftables-nojson):iptables-mod-conntrack-extra PKGARCH:=all endef diff --git a/net/trafficshaper/files/trafficshaper.conf b/net/trafficshaper/files/trafficshaper.conf index daee1033248b7..825ec6f0dd6d0 100644 --- a/net/trafficshaper/files/trafficshaper.conf +++ b/net/trafficshaper/files/trafficshaper.conf @@ -2,6 +2,7 @@ package trafficshaper config globals 'globals' option mark_mask '0xFF' + option firewall_backend 'auto' config wan 'wan' option downlink '20000' diff --git a/net/trafficshaper/files/trafficshaper.init b/net/trafficshaper/files/trafficshaper.init index ee10204f58320..1c29d0f0bb397 100755 --- a/net/trafficshaper/files/trafficshaper.init +++ b/net/trafficshaper/files/trafficshaper.init @@ -26,6 +26,7 @@ die() { APPNAME="trafficshaper" IPT_CHAIN=$APPNAME +NFT_TABLE=$APPNAME debug_exec(){ local err @@ -43,6 +44,7 @@ IP="debug_exec ip" TC="debug_exec tc" IP4T="debug_exec iptables -w 5" IP6T="debug_exec ip6tables -w 5" +NFT="debug_exec nft" #QDISC="cake autorate_ingress internet ethernet diffserv4 triple-isolate" QDISC="cake" @@ -56,14 +58,37 @@ preinit(){ } requires() { - if ! command -v ip6tables &>/dev/null; then - v "Disabling IPv6 as ip6tables was not found" - IP6T=true - fi - . /lib/functions/network.sh config_load $APPNAME + config_get firewall_backend globals firewall_backend auto + + case "$firewall_backend" in + auto) + if command -v nft >/dev/null; then + firewall_backend="nftables" + else + firewall_backend="iptables" + fi + ;; + nft|nftables) + firewall_backend="nftables" + ;; + iptables) + ;; + *) + die 2 "unknown firewall_backend '$firewall_backend'" + ;; + esac + + if [ "$firewall_backend" = "nftables" ] && ! command -v nft &>/dev/null; then + die 1 "firewall_backend nftables selected but nft was not found" + fi + + if [ "$firewall_backend" = "iptables" ] && ! command -v ip6tables &>/dev/null; then + v "Disabling IPv6 as ip6tables was not found" + IP6T=true + fi } do_stop() { @@ -74,15 +99,7 @@ do_stop() { v "Stopping $APPNAME${only_int:+ for interface $only_int}" if [ -z "$only_int" ]; then - d "Cleaning iptables" - # Cleaning iptables - for IPT in "$IP4T" "$IP6T"; do - $IPT -t mangle -D FORWARD -j $IPT_CHAIN &>/dev/null || : - $IPT -t mangle -F $IPT_CHAIN &>/dev/null || : - $IPT -t mangle -X $IPT_CHAIN &>/dev/null || : - $IPT -t mangle -F $IPT_CHAIN-classify &>/dev/null || : - $IPT -t mangle -X $IPT_CHAIN-classify &>/dev/null || : - done + stop_firewall fi d "Cleaning tc" @@ -156,6 +173,32 @@ mask_range() { return 0 } +stop_firewall_iptables() { + command -v iptables &>/dev/null || return 0 + command -v ip6tables &>/dev/null || IP6T=true + + d "Cleaning iptables" + for IPT in "$IP4T" "$IP6T"; do + $IPT -t mangle -D FORWARD -j $IPT_CHAIN &>/dev/null || : + $IPT -t mangle -F $IPT_CHAIN &>/dev/null || : + $IPT -t mangle -X $IPT_CHAIN &>/dev/null || : + $IPT -t mangle -F $IPT_CHAIN-classify &>/dev/null || : + $IPT -t mangle -X $IPT_CHAIN-classify &>/dev/null || : + done +} + +stop_firewall_nftables() { + command -v nft &>/dev/null || return 0 + + d "Cleaning nftables" + $NFT destroy table inet "$NFT_TABLE" &>/dev/null || : +} + +stop_firewall() { + stop_firewall_iptables + stop_firewall_nftables +} + start_iptables(){ d "Creating iptables mangle rules" @@ -226,6 +269,81 @@ start_iptables(){ $IP6T -t mangle -A $IPT_CHAIN-classify -j CONNMARK --save-mark --nfmask $mark_mask --ctmask $mark_mask } +start_nftables() { + d "Creating nftables mangle rules" + + config_get mark_mask globals mark_mask 0xFF + local mark_mask_dec mark_mask_inv fsb_lst class_id_max class_id_shift nft_rules + mark_mask_dec=$(($mark_mask)) + mark_mask=$(printf '0x%X\n' "$mark_mask_dec") + mark_mask_inv=$(printf '0x%X\n' $((0xFFFFFFFF & ~mark_mask_dec))) + + fsb_lst=$(mask_range $mark_mask) + class_id_max=$(((1<<(${fsb_lst#* } - ${fsb_lst% *} +1))+1)) + class_id_shift=$((${fsb_lst% *})) + + nft_rules="$( + { + printf '%s\n' "#!/usr/sbin/nft -f" "" + $NFT list table inet "$NFT_TABLE" &>/dev/null && \ + printf '%s\n' "destroy table inet $NFT_TABLE" + printf '%s\n' \ + "add table inet $NFT_TABLE" \ + "add chain inet $NFT_TABLE forward { type filter hook forward priority mangle + 10; policy accept; }" \ + "add chain inet $NFT_TABLE classify" \ + "add rule inet $NFT_TABLE forward meta mark & $mark_mask != 0x0 counter return" \ + "add rule inet $NFT_TABLE forward counter goto classify comment \"If no class, try to classify\"" + + local class class_reserved_uplink class_reserved_downlink class_nets i=2 xi nft_family + for class in $(config_foreach echo class); do + config_get class_reserved_uplink $class reserved_uplink + config_get class_reserved_downlink $class reserved_downlink + config_get class_nets $class network + if [ "$class" = default ]; then + if [ -z "$class_reserved_uplink" -a -z "$class_reserved_downlink" ] ; then + die 2 "class default must defined either reserved uplink or downlink!" + fi + if [ "$class_nets" ]; then + die 2 "class default must not have any network defined!" + fi + else + if [ "$i" -ge "$class_id_max" ]; then + die 1 "Max client classes reached. Please, use less classes or increase option mark_mask '$mark_mask' in globals. Current mask allows only $((class_id_max-2)) classes if default is the last one." + fi + fi + + xi=$(printf '0x%X\n' $(((i-1)</dev/null + case "$firewall_backend" in + nftables) $NFT list table inet "$NFT_TABLE" &>/dev/null ;; + iptables) $IP4T -t mangle -L $IPT_CHAIN &>/dev/null ;; + esac } reload_service() { preinit + requires if ! is_running; then d "Not running. Nothing to reload" return 0 @@ -442,7 +564,8 @@ service_triggers() { validate_trafficshaper_global() { uci_validate_section $APPNAME global "${1}" \ - 'mark_mask:uinteger:0xFF' + 'mark_mask:uinteger:0xFF' \ + 'firewall_backend:string:auto' } validate_trafficshaper_wan() {