trafficshaper: metadata and makefile cleanup
authorLuiz Angelo Daros de Luca <luizluca@gmail.com>
Wed, 17 Jul 2019 06:34:31 +0000 (03:34 -0300)
committerLuiz Angelo Daros de Luca <luizluca@gmail.com>
Wed, 17 Jul 2019 06:37:12 +0000 (03:37 -0300)
This results in minor metadata changes (spaces).
No need to bump release.

Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
net/trafficshaper/Makefile
net/trafficshaper/files/etc/config/trafficshaper [deleted file]
net/trafficshaper/files/etc/init.d/trafficshaper [deleted file]
net/trafficshaper/files/trafficshaper.conf [new file with mode: 0644]
net/trafficshaper/files/trafficshaper.init [new file with mode: 0755]

index 939c37a977fd69e653434972bc9aa693fb2b7607..cedf29479e0b06fb4cbdffede3656e151bba14b9 100644 (file)
@@ -1,5 +1,5 @@
 #
-# Copyright (C) 2018 Luiz Angelo Daros de Luca
+# Copyright (C) 2018-2019 Luiz Angelo Daros de Luca
 #
 # This is free software, licensed under the GNU General Public License v2.
 #
@@ -10,30 +10,25 @@ PKG_NAME:=trafficshaper
 PKG_VERSION:=1.0.0
 PKG_RELEASE:=1
 PKG_MAINTAINER:=Luiz Angelo Daros de Luca <luizluca@gmail.com>
-PKG_LICENSE:=GPLv2
-PKG_ARCH:=all
 
-PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
-include $(INCLUDE_DIR)/package.mk
+PKG_LICENSE:=GPL-2.0-or-later
 
 include $(INCLUDE_DIR)/package.mk
 
 define Package/trafficshaper
-   SECTION:=net
-   CATEGORY:=Network
-   DEPENDS:=+tc +kmod-sched-core +kmod-sched-connmark +kmod-ifb +iptables +kmod-sched-cake +iptables-mod-conntrack-extra
-   TITLE:=WAN traffic shaper based on LAN addresses
-   MAINTAINER:=Luiz Angelo Daros de Luca <luizluca@gmail.com>
-   PKGARCH:=all
+  SECTION:=net
+  CATEGORY:=Network
+  TITLE:=WAN traffic shaper based on LAN addresses
+  DEPENDS:=+tc +kmod-sched-core +kmod-sched-connmark +kmod-ifb +iptables +kmod-sched-cake +iptables-mod-conntrack-extra
+  PKGARCH:=all
 endef
 
 define Package/trafficshaper/description
-Setup QoS rules to limit (or reserve) traffic used by classes of clients.
-Uplink and downlink can be controled (or not controlled) independently.
-Client classes are defined by its network addresses (IPv4 or IPv6). Each
-client class can define absolute or relative (to wan) bandwith, and also
-the use (or not) of spare wan bandwidth when avaiable.
-
+  Setup QoS rules to limit (or reserve) traffic used by classes of clients.
+  Uplink and downlink can be controled (or not controlled) independently.
+  Client classes are defined by its network addresses (IPv4 or IPv6). Each
+  client class can define absolute or relative (to wan) bandwith, and also
+  the use (or not) of spare wan bandwidth when avaiable.
 endef
 
 define Package/trafficshaper/conffiles
@@ -44,7 +39,10 @@ define Build/Compile
 endef
 
 define Package/trafficshaper/install
-$(CP) ./files/* $(1)
+       $(INSTALL_DIR) $(1)/etc/config
+       $(INSTALL_CONF) ./files/trafficshaper.conf $(1)/etc/config/trafficshaper
+       $(INSTALL_DIR) $(1)/etc/init.d
+       $(INSTALL_BIN) ./files/trafficshaper.init $(1)/etc/init.d/trafficshaper
 endef
 
 $(eval $(call BuildPackage,trafficshaper))
diff --git a/net/trafficshaper/files/etc/config/trafficshaper b/net/trafficshaper/files/etc/config/trafficshaper
deleted file mode 100644 (file)
index daee103..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-package trafficshaper
-
-config globals 'globals'
-       option mark_mask '0xFF'
-
-config wan 'wan'
-       option downlink '20000'
-       option uplink '20000'
-
-config wan 'wanb'
-       option downlink '15000'
-       option uplink '5000'
-
-config class 'corp'
-       list network '192.168.1.0/24'
-       list network 'fdc8:1234:1234:1::/64'
-       option reserved_downlink '50%'
-       option reserved_uplink '35%'
-       option allowed_downlink '100%'
-       option allowed_uplink '100%'
-
-config class 'vpn'
-       list network '192.168.2.0/24'
-       list network 'fdc8:1234:1234:2::/64'
-       option reserved_downlink '25%'
-       option reserved_uplink '50%'
-       option allowed_downlink '100%'
-       option allowed_uplink '100%'
-
-config class 'guest'
-       list network '192.168.3.0/24'
-       list network 'fdc8:1234:1234:3::/64'
-       option reserved_downlink '25%'
-       option reserved_uplink '15%'
-       option allowed_downlink '25%'
-       option allowed_uplink '15%'
-
-config class 'default'
-       option reserved_downlink '1000'
-       option reserved_uplink '1000'
-       option allowed_downlink '100%'
-       option allowed_uplink '100%'
diff --git a/net/trafficshaper/files/etc/init.d/trafficshaper b/net/trafficshaper/files/etc/init.d/trafficshaper
deleted file mode 100755 (executable)
index 445f50e..0000000
+++ /dev/null
@@ -1,477 +0,0 @@
-#!/bin/sh /etc/rc.common
-
-# Internal uci firewall chains are flushed and recreated on reload, so
-# put custom rules into the root chains e.g. INPUT or FORWARD or into the
-# special user chains, e.g. input_wan_rule or postrouting_lan_rule.
-
-START=25
-USE_PROCD=1
-
-echo_err() {
-       echo "$@" >&2
-}
-
-msg() {
-       local level=$1; shift
-       echo_err "$APPNAME[$level]: $*"
-}
-
-LOGLEVEL=${LOGLEVEL:-2}
-
-die() {
-       local err=$1; shift
-       e "$*"
-       exit $err
-}
-
-APPNAME="trafficshaper"
-IPT_CHAIN=$APPNAME
-
-debug_exec(){
-       local err
-       d "exec: $*"
-       if "$@"; then
-               return 0
-       else
-               err="$?"
-       fi
-       e "exec[err=$err]: $*"
-       return "$err"
-}
-
-IP="debug_exec ip"
-TC="debug_exec tc"
-IP4T="debug_exec iptables -w 5"
-IP6T="debug_exec ip6tables -w 5"
-
-#QDISC="cake autorate_ingress internet ethernet diffserv4 triple-isolate"
-QDISC="cake"
-
-REQ_MODULES="sch_htb sch_cake act_connmark act_mirred em_u32"
-REQ_CMDS="ip tc iptables"
-
-preinit(){
-       [ "$LOGLEVEL" -ge 1 ] && e() { msg ERROR "$@"; } || e() { true; }
-       [ "$LOGLEVEL" -ge 2 ] && v() { msg INFO "$@"; } || v() { true; }
-       [ "$LOGLEVEL" -ge 3 ] && d() { msg DEBUG "$@"; } || d() { true; }
-       [ "$LOGLEVEL" -ge 4 ] && set -x
-       set -e
-}
-
-requires() {
-       for module in $REQ_MODULES; do
-               [ -d /sys/module/$module ] || insert_modules "$module" ||
-                       die 2 "cannot load $module. Please install kmod-$module"
-       done
-       for cmd in $REQ_CMDS; do
-               which $cmd &>/dev/null ||
-                       die 2 "cannot find command $cmd. Please install $cmd"
-       done
-
-       if ! which ip6tables &>/dev/null; then
-               v "Disabling IPv6 as ip6tables was not found"
-               IP6T=true
-       fi
-
-       . /lib/functions/network.sh
-
-       config_load $APPNAME
-}
-
-do_stop() {
-       local only_int=$1
-
-       preinit
-       requires
-
-       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
-       fi
-
-       d "Cleaning tc"
-       local dev_done int dev ifb interfaces
-       if [ "$only_int" ]; then
-               config_get type $only_int TYPE
-               if [ "$type" != "wan" ]; then
-                       d "interface $only_int not found in trafficshaper config. Ignoring"
-                       return 0
-               fi
-               interfaces="$only_int"
-
-       else
-               interfaces="$(config_foreach echo wan)"
-       fi
-
-       for int in $interfaces; do
-               d "Cleaning tc for interface $int"
-               network_get_physdev dev "$int" ||
-                       die 1 "failed to get physical dev of interface $int"
-
-               if echo "$dev_done" | grep -x -F -q "$dev"; then
-                       continue
-               fi
-               ifb="ifb_$dev"
-               if [ ${#ifb} -gt 15 ]; then
-                       die 1 "ifb name too long: ${ifb}"
-               fi
-
-               $TC qdisc del dev ${ifb} root 2> /dev/null || :
-               $TC qdisc del dev ${dev} root 2> /dev/null || :
-               $TC qdisc del dev ${dev} ingress 2> /dev/null || :
-
-               d "Removing ${ifb}..."
-               $IP link set dev ${ifb} down 2>/dev/null || :
-               $IP link delete dev ${ifb} 2>/dev/null || :
-
-               intdev_done="$(echo "$dev_done"; echo -n $dev)"
-       done
-}
-
-
-calc_bw() {
-       local value=$1 reference=$2
-       case "${value}" in
-               *%) echo "$((${value%\%} * reference / 100 ))";;
-               *) echo ${value};;
-       esac
-}
-
-mask_range() {
-       local mask=$(($1)) n=0 fsb
-       if [ $mask -le 0 ]; then
-               e "mask '$1' must be greater than 0 (have a sequence of set bit)"
-               return 2
-       fi
-       while [ "$((mask & 0x1))" -eq 0 ]; do
-               mask=$((mask >> 1))
-               : $((n++))
-       done
-       fsb="$n"
-       while [ "$((mask & 0x1))" -eq 1 ]; do
-               mask=$((mask >> 1))
-               : $((n++))
-       done
-       if [ $mask -ne 0 ]; then
-               e "mask '$1' must be a continuos sequence of set bit"
-               return 2
-       fi
-       echo $fsb $((n-1))
-       return 0
-}
-
-start_iptables(){
-       d "Creating iptables mangle rules"
-
-       config_get mark_mask globals mark_mask 0xFF
-       mark_mask=$(printf '0x%X\n' $(($mark_mask)))
-
-       local fsb_lst class_id_max class_id_shift
-       fsb_lst=$(mask_range $mark_mask)
-       class_id_max=$(((1<<(${fsb_lst#* } - ${fsb_lst% *} +1))+1))
-       class_id_shift=$((${fsb_lst% *}))
-
-       d "General iptables rules:"
-       for IPT in "$IP4T" "$IP6T"; do
-               $IPT -t mangle -N $IPT_CHAIN
-               $IPT -t mangle -N $IPT_CHAIN-classify
-
-               $IPT -t mangle -A FORWARD       -j $IPT_CHAIN
-               $IPT -t mangle -A $IPT_CHAIN    -j CONNMARK --restore-mark --nfmask $mark_mask --ctmask $mark_mask \
-                       -m comment --comment "Get previous class"
-               $IPT -t mangle -A $IPT_CHAIN -m mark --mark 0x0/$mark_mask -j $IPT_CHAIN-classify \
-                       -m comment --comment "If no class, try to classify"
-       done
-
-       d "Classes iptables rules:"
-       local class_reserved_uplink class_reserved_downlink class_nets i=2 xi default_class_id
-       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
-                       default_class_id=$i
-                       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)<<class_id_shift)))
-
-               for class_net in $class_nets; do
-                       case $class_net in
-                               *:*) IPT="$IP6T" ;;
-                               *.*) IPT="$IP4T" ;;
-                               *) die 2 "Unknown address family of network $class_net in class $class!"
-                       esac
-                       if [ "$class_reserved_uplink" ]; then
-                               $IPT -t mangle -A $IPT_CHAIN-classify -s $class_net -m mark --mark 0x0/$mark_mask -j MARK --set-mark ${xi}/$mark_mask \
-                                       -m comment --comment "$APPNAME-$class up"
-                       fi
-                       if [ "$class_reserved_downlink" ]; then
-                               $IPT -t mangle -A $IPT_CHAIN-classify -d $class_net -m mark --mark 0x0/$mark_mask -j MARK --set-mark ${xi}/$mark_mask \
-                                       -m comment --comment "$APPNAME-$class down"
-                       fi
-               done
-               : $((i++))
-       done
-       if [ -z "$default_class_id" ]; then
-               die 2 "No default class defined!"
-       fi
-
-       $IP4T -t mangle -A $IPT_CHAIN-classify -j CONNMARK --save-mark --nfmask $mark_mask --ctmask $mark_mask
-       $IP6T -t mangle -A $IPT_CHAIN-classify -j CONNMARK --save-mark --nfmask $mark_mask --ctmask $mark_mask
-}
-
-
-
-start_tc_interface() {
-       local int=$1; shift
-       local dev=$1; shift
-       local default_class_id=$1; shift
-
-       config_get mark_mask globals mark_mask 0xFF
-       local fsb_lst class_id_max class_id_shift
-       fsb_lst=$(mask_range $mark_mask)
-       class_id_max=$(((1<<(${fsb_lst#* } - ${fsb_lst% *} +1))))
-       class_id_shift=$((${fsb_lst% *}))
-
-       local downlink uplink type
-       config_get downlink $int downlink
-       config_get uplink   $int uplink
-
-       d "Creating tc rules for $int ($dev)"
-       local dev_down dev_up
-       if [ "$downlink" ]; then
-               local ifb="ifb_$dev"
-               if [ ${#ifb} -gt 15 ]; then
-                       die 1 "ifb name too long: ${ifb}"
-               fi
-
-               d "Creating ${ifb}..."
-               $IP link add name ${ifb} type ifb
-               $IP link set dev $ifb up
-               d "Redirect ingress $dev to $ifb..."
-               $TC qdisc  add dev $dev handle ffff: ingress
-               $TC filter add dev $dev parent ffff: protocol all u32 match u32 0 0 action connmark action mirred egress redirect dev $ifb
-               dev_down=$ifb
-       else
-               dev_down=
-       fi
-       if [ "$uplink" ]; then
-               dev_up="$dev"
-       fi
-
-       # Download/Upload
-       if [ "$dev_down" ]; then
-               tc qdisc add dev $dev_down root handle 1: htb default "$default_class_id"
-               tc class add dev $dev_down parent 1: classid 1:1 htb rate $(calc_bw ${downlink})kbit burst 500k quantum 1500
-       fi
-
-       if [ "$dev_up" ]; then
-               tc qdisc add dev $dev_up   root handle 1: htb default "$default_class_id"
-               tc class add dev $dev_up   parent 1: classid 1:1 htb rate $(calc_bw ${uplink})kbit   burst 500k quantum 1500
-       fi
-
-       v "$int($dev):" \
-               "${downlink:+downlink of ${downlink}kbit}"\
-               "${uplink:+uplink of ${uplink}kbit}"\
-
-       local class class_reserved_downlink class_reserved_uplink class_allowed_downlink class_allowed_uplink class_nets class_net i=2
-       for class in $(config_foreach echo class); do
-               config_get class_reserved_downlink $class reserved_downlink
-               if [ "$class_reserved_downlink" ]; then
-                       if [ "$dev_down" ]; then
-                               class_reserved_downlink=$(calc_bw $class_reserved_downlink $downlink)
-                               config_get class_allowed_downlink $class allowed_downlink "$class_reserved_downlink"
-                               class_allowed_downlink=$(calc_bw $class_allowed_downlink $downlink)
-                       else
-                               e "class $class defines reserved downlink but not wan $int. Downlink shapping will be ignored"
-                               class_reserved_downlink=
-                       fi
-               elif [ "$dev_down" ]; then
-                       e "class $class does not define reserved downlink but wan $int does. Downlink shapping will use default class"
-               fi
-
-               if [ "$class_allowed_downlink" -lt "$class_reserved_downlink" ]; then
-                       die 1 "Allowed downlink bandwitdh in class $class must not be smaller than reserved downlink."
-               fi
-
-               config_get class_reserved_uplink $class reserved_uplink
-               if [ "$class_reserved_uplink" ]; then
-                       if [ "$dev_up" ]; then
-                               class_reserved_uplink=$(calc_bw $class_reserved_uplink $uplink)
-                               config_get class_allowed_uplink $class allowed_uplink "$class_reserved_uplink"
-                               class_allowed_uplink=$(calc_bw $class_allowed_uplink $uplink)
-                       else
-                               e "class $class defines reserved uplink but not wan $int. Downlink shapping will be ignored"
-                               class_reserved_uplink=
-                       fi
-               elif [ "$dev_up" ]; then
-                       e "class $class does not define reserved uplink but wan $int does. Downlink shapping will use default class"
-               fi
-
-               if [ -n "$class_allowed_uplink" -a -n "$class_reserved_uplink" ] && [ "$class_allowed_uplink" -lt "$class_reserved_uplink" ]; then
-                       die 1 "Allowed uplink bandwitdh in class $class must not be smaller than reserved uplink."
-               fi
-
-               v "$int($dev): $class(class 1:$i) will have" \
-                       "${class_reserved_downlink:+download of ${class_reserved_downlink}kbit (up to ${class_allowed_downlink}kbit)}"\
-                       "${class_reserved_uplink:+upload of ${class_reserved_uplink}kbit up (up to ${class_allowed_uplink}kbit)}"
-
-               xi=$(printf '0x%X\n' $(((i-1)<<class_id_shift)))
-               if [ "$class_reserved_uplink" ]; then
-                       $TC class  add dev $dev_up   parent 1:1  classid  1:$i htb rate ${class_reserved_uplink}kbit ceil ${class_allowed_uplink}kbit   quantum 1500 burst 50k
-                       $TC qdisc  add dev $dev_up   parent 1:$i handle   $i:  $QDISC
-                       if [ "$class" != default ]; then
-                               $TC filter add dev $dev_up   parent 1:   protocol ip prio $i handle ${xi}/$mark_mask fw flowid 1:$i
-                       fi
-               fi
-               if [ "$class_reserved_downlink" ]; then
-                       $TC class  add dev $dev_down parent 1:1  classid  1:$i htb rate ${class_reserved_downlink}kbit ceil ${class_allowed_downlink}kbit quantum 1500 burst 50k
-                       $TC qdisc  add dev $dev_down parent 1:$i handle   $i:  $QDISC
-                       if [ "$class" != default ]; then
-                               $TC filter add dev $dev_down parent 1:   protocol ip   prio $i handle ${xi}/$mark_mask fw flowid 1:$i
-                       fi
-               fi
-               : $((i++))
-       done
-}
-
-start_tc() {
-       d "Creating tc rules"
-       local dev_done int dev interfaces
-       local default_class_id=$1; shift
-       local only_int=$1
-
-       if [ "$only_int" ]; then
-               config_get type $only_int TYPE
-               if [ "$type" != "wan" ]; then
-                       d "interface $only_int not found in trafficshaper config. Ignoring"
-                       return 0
-               fi
-               interfaces="$only_int"
-
-       else
-               interfaces="$(config_foreach echo wan)"
-       fi
-
-       for int in $interfaces; do
-               network_get_physdev dev "$int" ||
-                       die 1 "failed to get physical dev of interface $int"
-
-               if echo "$dev_done" | grep -x -F -q "$dev"; then
-                       e "$int uses $dev which was already configured. Only list each WAN once. Skipping..."
-                       continue
-               fi
-
-               start_tc_interface $int $dev $ifb "$default_class_id"
-               intdev_done="$(echo "$dev_done"; echo -n $dev)"
-       done
-}
-
-do_start() {
-       local only_int=$1 type
-
-       preinit
-       (LOGLEVEL=0 do_stop "$only_int")
-       requires
-
-       trap "set +e; do_stop $only_int" EXIT
-
-       v "Starting $APPNAME${only_int:+ for interface $only_int}"
-
-       local default_class_id
-       if ! default_class_id=$(i=2 config_foreach 'eval echo $((i++))' class '| grep " default"'); then
-               die 2 "No default class defined!"
-       fi
-       default_class_id=${default_class_id% *}
-
-       [ "$only_int" ] || start_iptables
-       start_tc "$default_class_id" "$only_int"
-
-       trap - EXIT
-}
-
-start_service() {
-       ( do_start )
-}
-
-stop_service() {
-       ( do_stop )
-}
-
-restart_service() {
-       ( do_start )
-}
-
-is_running() {
-       $IP4T -t mangle -L $IPT_CHAIN &>/dev/null
-}
-
-reload_service() {
-       preinit
-       if ! is_running; then
-               d "Not running. Nothing to reload"
-               return 0
-       fi
-       logger -t "$APPNAME" "Reloading $*..."
-       ( do_start "$@" )
-}
-
-add_interface_trigger() {
-        procd_add_interface_trigger "interface.update" "$1" /etc/init.d/$APPNAME reload $1
-}
-
-service_triggers() {
-       preinit; set +e
-       requires
-
-       procd_add_reload_trigger "$APPNAME"
-       config_foreach add_interface_trigger wan
-
-       procd_open_validate
-       validate_trafficshaper_global
-       validate_trafficshaper_wan
-       validate_trafficshaper_class
-       procd_close_validate
-}
-
-validate_trafficshaper_global() {
-        uci_validate_section $APPNAME global "${1}" \
-                'mark_mask:uinteger:0xFF'
-}
-
-validate_trafficshaper_wan() {
-       uci_validate_section "$APPNAME" wan "${1}" \
-                'downlink:uinteger' \
-                'uplink:uinteger'
-}
-
-validate_trafficshaper_class() {
-        uci_validate_section "$APPNAME" class "${1}" \
-               'network:cidr' \
-               'reserved_downlink:or(uinteger, string)' \
-               'reserved_uplink:or(uinteger, string)' \
-               'allowed_downlink:or(uinteger, string)' \
-               'allowed_uplink:or(uinteger, string)'
-}
-
-boot() {
-       LOGLEVEL=1 start
-}
diff --git a/net/trafficshaper/files/trafficshaper.conf b/net/trafficshaper/files/trafficshaper.conf
new file mode 100644 (file)
index 0000000..daee103
--- /dev/null
@@ -0,0 +1,42 @@
+package trafficshaper
+
+config globals 'globals'
+       option mark_mask '0xFF'
+
+config wan 'wan'
+       option downlink '20000'
+       option uplink '20000'
+
+config wan 'wanb'
+       option downlink '15000'
+       option uplink '5000'
+
+config class 'corp'
+       list network '192.168.1.0/24'
+       list network 'fdc8:1234:1234:1::/64'
+       option reserved_downlink '50%'
+       option reserved_uplink '35%'
+       option allowed_downlink '100%'
+       option allowed_uplink '100%'
+
+config class 'vpn'
+       list network '192.168.2.0/24'
+       list network 'fdc8:1234:1234:2::/64'
+       option reserved_downlink '25%'
+       option reserved_uplink '50%'
+       option allowed_downlink '100%'
+       option allowed_uplink '100%'
+
+config class 'guest'
+       list network '192.168.3.0/24'
+       list network 'fdc8:1234:1234:3::/64'
+       option reserved_downlink '25%'
+       option reserved_uplink '15%'
+       option allowed_downlink '25%'
+       option allowed_uplink '15%'
+
+config class 'default'
+       option reserved_downlink '1000'
+       option reserved_uplink '1000'
+       option allowed_downlink '100%'
+       option allowed_uplink '100%'
diff --git a/net/trafficshaper/files/trafficshaper.init b/net/trafficshaper/files/trafficshaper.init
new file mode 100755 (executable)
index 0000000..445f50e
--- /dev/null
@@ -0,0 +1,477 @@
+#!/bin/sh /etc/rc.common
+
+# Internal uci firewall chains are flushed and recreated on reload, so
+# put custom rules into the root chains e.g. INPUT or FORWARD or into the
+# special user chains, e.g. input_wan_rule or postrouting_lan_rule.
+
+START=25
+USE_PROCD=1
+
+echo_err() {
+       echo "$@" >&2
+}
+
+msg() {
+       local level=$1; shift
+       echo_err "$APPNAME[$level]: $*"
+}
+
+LOGLEVEL=${LOGLEVEL:-2}
+
+die() {
+       local err=$1; shift
+       e "$*"
+       exit $err
+}
+
+APPNAME="trafficshaper"
+IPT_CHAIN=$APPNAME
+
+debug_exec(){
+       local err
+       d "exec: $*"
+       if "$@"; then
+               return 0
+       else
+               err="$?"
+       fi
+       e "exec[err=$err]: $*"
+       return "$err"
+}
+
+IP="debug_exec ip"
+TC="debug_exec tc"
+IP4T="debug_exec iptables -w 5"
+IP6T="debug_exec ip6tables -w 5"
+
+#QDISC="cake autorate_ingress internet ethernet diffserv4 triple-isolate"
+QDISC="cake"
+
+REQ_MODULES="sch_htb sch_cake act_connmark act_mirred em_u32"
+REQ_CMDS="ip tc iptables"
+
+preinit(){
+       [ "$LOGLEVEL" -ge 1 ] && e() { msg ERROR "$@"; } || e() { true; }
+       [ "$LOGLEVEL" -ge 2 ] && v() { msg INFO "$@"; } || v() { true; }
+       [ "$LOGLEVEL" -ge 3 ] && d() { msg DEBUG "$@"; } || d() { true; }
+       [ "$LOGLEVEL" -ge 4 ] && set -x
+       set -e
+}
+
+requires() {
+       for module in $REQ_MODULES; do
+               [ -d /sys/module/$module ] || insert_modules "$module" ||
+                       die 2 "cannot load $module. Please install kmod-$module"
+       done
+       for cmd in $REQ_CMDS; do
+               which $cmd &>/dev/null ||
+                       die 2 "cannot find command $cmd. Please install $cmd"
+       done
+
+       if ! which ip6tables &>/dev/null; then
+               v "Disabling IPv6 as ip6tables was not found"
+               IP6T=true
+       fi
+
+       . /lib/functions/network.sh
+
+       config_load $APPNAME
+}
+
+do_stop() {
+       local only_int=$1
+
+       preinit
+       requires
+
+       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
+       fi
+
+       d "Cleaning tc"
+       local dev_done int dev ifb interfaces
+       if [ "$only_int" ]; then
+               config_get type $only_int TYPE
+               if [ "$type" != "wan" ]; then
+                       d "interface $only_int not found in trafficshaper config. Ignoring"
+                       return 0
+               fi
+               interfaces="$only_int"
+
+       else
+               interfaces="$(config_foreach echo wan)"
+       fi
+
+       for int in $interfaces; do
+               d "Cleaning tc for interface $int"
+               network_get_physdev dev "$int" ||
+                       die 1 "failed to get physical dev of interface $int"
+
+               if echo "$dev_done" | grep -x -F -q "$dev"; then
+                       continue
+               fi
+               ifb="ifb_$dev"
+               if [ ${#ifb} -gt 15 ]; then
+                       die 1 "ifb name too long: ${ifb}"
+               fi
+
+               $TC qdisc del dev ${ifb} root 2> /dev/null || :
+               $TC qdisc del dev ${dev} root 2> /dev/null || :
+               $TC qdisc del dev ${dev} ingress 2> /dev/null || :
+
+               d "Removing ${ifb}..."
+               $IP link set dev ${ifb} down 2>/dev/null || :
+               $IP link delete dev ${ifb} 2>/dev/null || :
+
+               intdev_done="$(echo "$dev_done"; echo -n $dev)"
+       done
+}
+
+
+calc_bw() {
+       local value=$1 reference=$2
+       case "${value}" in
+               *%) echo "$((${value%\%} * reference / 100 ))";;
+               *) echo ${value};;
+       esac
+}
+
+mask_range() {
+       local mask=$(($1)) n=0 fsb
+       if [ $mask -le 0 ]; then
+               e "mask '$1' must be greater than 0 (have a sequence of set bit)"
+               return 2
+       fi
+       while [ "$((mask & 0x1))" -eq 0 ]; do
+               mask=$((mask >> 1))
+               : $((n++))
+       done
+       fsb="$n"
+       while [ "$((mask & 0x1))" -eq 1 ]; do
+               mask=$((mask >> 1))
+               : $((n++))
+       done
+       if [ $mask -ne 0 ]; then
+               e "mask '$1' must be a continuos sequence of set bit"
+               return 2
+       fi
+       echo $fsb $((n-1))
+       return 0
+}
+
+start_iptables(){
+       d "Creating iptables mangle rules"
+
+       config_get mark_mask globals mark_mask 0xFF
+       mark_mask=$(printf '0x%X\n' $(($mark_mask)))
+
+       local fsb_lst class_id_max class_id_shift
+       fsb_lst=$(mask_range $mark_mask)
+       class_id_max=$(((1<<(${fsb_lst#* } - ${fsb_lst% *} +1))+1))
+       class_id_shift=$((${fsb_lst% *}))
+
+       d "General iptables rules:"
+       for IPT in "$IP4T" "$IP6T"; do
+               $IPT -t mangle -N $IPT_CHAIN
+               $IPT -t mangle -N $IPT_CHAIN-classify
+
+               $IPT -t mangle -A FORWARD       -j $IPT_CHAIN
+               $IPT -t mangle -A $IPT_CHAIN    -j CONNMARK --restore-mark --nfmask $mark_mask --ctmask $mark_mask \
+                       -m comment --comment "Get previous class"
+               $IPT -t mangle -A $IPT_CHAIN -m mark --mark 0x0/$mark_mask -j $IPT_CHAIN-classify \
+                       -m comment --comment "If no class, try to classify"
+       done
+
+       d "Classes iptables rules:"
+       local class_reserved_uplink class_reserved_downlink class_nets i=2 xi default_class_id
+       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
+                       default_class_id=$i
+                       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)<<class_id_shift)))
+
+               for class_net in $class_nets; do
+                       case $class_net in
+                               *:*) IPT="$IP6T" ;;
+                               *.*) IPT="$IP4T" ;;
+                               *) die 2 "Unknown address family of network $class_net in class $class!"
+                       esac
+                       if [ "$class_reserved_uplink" ]; then
+                               $IPT -t mangle -A $IPT_CHAIN-classify -s $class_net -m mark --mark 0x0/$mark_mask -j MARK --set-mark ${xi}/$mark_mask \
+                                       -m comment --comment "$APPNAME-$class up"
+                       fi
+                       if [ "$class_reserved_downlink" ]; then
+                               $IPT -t mangle -A $IPT_CHAIN-classify -d $class_net -m mark --mark 0x0/$mark_mask -j MARK --set-mark ${xi}/$mark_mask \
+                                       -m comment --comment "$APPNAME-$class down"
+                       fi
+               done
+               : $((i++))
+       done
+       if [ -z "$default_class_id" ]; then
+               die 2 "No default class defined!"
+       fi
+
+       $IP4T -t mangle -A $IPT_CHAIN-classify -j CONNMARK --save-mark --nfmask $mark_mask --ctmask $mark_mask
+       $IP6T -t mangle -A $IPT_CHAIN-classify -j CONNMARK --save-mark --nfmask $mark_mask --ctmask $mark_mask
+}
+
+
+
+start_tc_interface() {
+       local int=$1; shift
+       local dev=$1; shift
+       local default_class_id=$1; shift
+
+       config_get mark_mask globals mark_mask 0xFF
+       local fsb_lst class_id_max class_id_shift
+       fsb_lst=$(mask_range $mark_mask)
+       class_id_max=$(((1<<(${fsb_lst#* } - ${fsb_lst% *} +1))))
+       class_id_shift=$((${fsb_lst% *}))
+
+       local downlink uplink type
+       config_get downlink $int downlink
+       config_get uplink   $int uplink
+
+       d "Creating tc rules for $int ($dev)"
+       local dev_down dev_up
+       if [ "$downlink" ]; then
+               local ifb="ifb_$dev"
+               if [ ${#ifb} -gt 15 ]; then
+                       die 1 "ifb name too long: ${ifb}"
+               fi
+
+               d "Creating ${ifb}..."
+               $IP link add name ${ifb} type ifb
+               $IP link set dev $ifb up
+               d "Redirect ingress $dev to $ifb..."
+               $TC qdisc  add dev $dev handle ffff: ingress
+               $TC filter add dev $dev parent ffff: protocol all u32 match u32 0 0 action connmark action mirred egress redirect dev $ifb
+               dev_down=$ifb
+       else
+               dev_down=
+       fi
+       if [ "$uplink" ]; then
+               dev_up="$dev"
+       fi
+
+       # Download/Upload
+       if [ "$dev_down" ]; then
+               tc qdisc add dev $dev_down root handle 1: htb default "$default_class_id"
+               tc class add dev $dev_down parent 1: classid 1:1 htb rate $(calc_bw ${downlink})kbit burst 500k quantum 1500
+       fi
+
+       if [ "$dev_up" ]; then
+               tc qdisc add dev $dev_up   root handle 1: htb default "$default_class_id"
+               tc class add dev $dev_up   parent 1: classid 1:1 htb rate $(calc_bw ${uplink})kbit   burst 500k quantum 1500
+       fi
+
+       v "$int($dev):" \
+               "${downlink:+downlink of ${downlink}kbit}"\
+               "${uplink:+uplink of ${uplink}kbit}"\
+
+       local class class_reserved_downlink class_reserved_uplink class_allowed_downlink class_allowed_uplink class_nets class_net i=2
+       for class in $(config_foreach echo class); do
+               config_get class_reserved_downlink $class reserved_downlink
+               if [ "$class_reserved_downlink" ]; then
+                       if [ "$dev_down" ]; then
+                               class_reserved_downlink=$(calc_bw $class_reserved_downlink $downlink)
+                               config_get class_allowed_downlink $class allowed_downlink "$class_reserved_downlink"
+                               class_allowed_downlink=$(calc_bw $class_allowed_downlink $downlink)
+                       else
+                               e "class $class defines reserved downlink but not wan $int. Downlink shapping will be ignored"
+                               class_reserved_downlink=
+                       fi
+               elif [ "$dev_down" ]; then
+                       e "class $class does not define reserved downlink but wan $int does. Downlink shapping will use default class"
+               fi
+
+               if [ "$class_allowed_downlink" -lt "$class_reserved_downlink" ]; then
+                       die 1 "Allowed downlink bandwitdh in class $class must not be smaller than reserved downlink."
+               fi
+
+               config_get class_reserved_uplink $class reserved_uplink
+               if [ "$class_reserved_uplink" ]; then
+                       if [ "$dev_up" ]; then
+                               class_reserved_uplink=$(calc_bw $class_reserved_uplink $uplink)
+                               config_get class_allowed_uplink $class allowed_uplink "$class_reserved_uplink"
+                               class_allowed_uplink=$(calc_bw $class_allowed_uplink $uplink)
+                       else
+                               e "class $class defines reserved uplink but not wan $int. Downlink shapping will be ignored"
+                               class_reserved_uplink=
+                       fi
+               elif [ "$dev_up" ]; then
+                       e "class $class does not define reserved uplink but wan $int does. Downlink shapping will use default class"
+               fi
+
+               if [ -n "$class_allowed_uplink" -a -n "$class_reserved_uplink" ] && [ "$class_allowed_uplink" -lt "$class_reserved_uplink" ]; then
+                       die 1 "Allowed uplink bandwitdh in class $class must not be smaller than reserved uplink."
+               fi
+
+               v "$int($dev): $class(class 1:$i) will have" \
+                       "${class_reserved_downlink:+download of ${class_reserved_downlink}kbit (up to ${class_allowed_downlink}kbit)}"\
+                       "${class_reserved_uplink:+upload of ${class_reserved_uplink}kbit up (up to ${class_allowed_uplink}kbit)}"
+
+               xi=$(printf '0x%X\n' $(((i-1)<<class_id_shift)))
+               if [ "$class_reserved_uplink" ]; then
+                       $TC class  add dev $dev_up   parent 1:1  classid  1:$i htb rate ${class_reserved_uplink}kbit ceil ${class_allowed_uplink}kbit   quantum 1500 burst 50k
+                       $TC qdisc  add dev $dev_up   parent 1:$i handle   $i:  $QDISC
+                       if [ "$class" != default ]; then
+                               $TC filter add dev $dev_up   parent 1:   protocol ip prio $i handle ${xi}/$mark_mask fw flowid 1:$i
+                       fi
+               fi
+               if [ "$class_reserved_downlink" ]; then
+                       $TC class  add dev $dev_down parent 1:1  classid  1:$i htb rate ${class_reserved_downlink}kbit ceil ${class_allowed_downlink}kbit quantum 1500 burst 50k
+                       $TC qdisc  add dev $dev_down parent 1:$i handle   $i:  $QDISC
+                       if [ "$class" != default ]; then
+                               $TC filter add dev $dev_down parent 1:   protocol ip   prio $i handle ${xi}/$mark_mask fw flowid 1:$i
+                       fi
+               fi
+               : $((i++))
+       done
+}
+
+start_tc() {
+       d "Creating tc rules"
+       local dev_done int dev interfaces
+       local default_class_id=$1; shift
+       local only_int=$1
+
+       if [ "$only_int" ]; then
+               config_get type $only_int TYPE
+               if [ "$type" != "wan" ]; then
+                       d "interface $only_int not found in trafficshaper config. Ignoring"
+                       return 0
+               fi
+               interfaces="$only_int"
+
+       else
+               interfaces="$(config_foreach echo wan)"
+       fi
+
+       for int in $interfaces; do
+               network_get_physdev dev "$int" ||
+                       die 1 "failed to get physical dev of interface $int"
+
+               if echo "$dev_done" | grep -x -F -q "$dev"; then
+                       e "$int uses $dev which was already configured. Only list each WAN once. Skipping..."
+                       continue
+               fi
+
+               start_tc_interface $int $dev $ifb "$default_class_id"
+               intdev_done="$(echo "$dev_done"; echo -n $dev)"
+       done
+}
+
+do_start() {
+       local only_int=$1 type
+
+       preinit
+       (LOGLEVEL=0 do_stop "$only_int")
+       requires
+
+       trap "set +e; do_stop $only_int" EXIT
+
+       v "Starting $APPNAME${only_int:+ for interface $only_int}"
+
+       local default_class_id
+       if ! default_class_id=$(i=2 config_foreach 'eval echo $((i++))' class '| grep " default"'); then
+               die 2 "No default class defined!"
+       fi
+       default_class_id=${default_class_id% *}
+
+       [ "$only_int" ] || start_iptables
+       start_tc "$default_class_id" "$only_int"
+
+       trap - EXIT
+}
+
+start_service() {
+       ( do_start )
+}
+
+stop_service() {
+       ( do_stop )
+}
+
+restart_service() {
+       ( do_start )
+}
+
+is_running() {
+       $IP4T -t mangle -L $IPT_CHAIN &>/dev/null
+}
+
+reload_service() {
+       preinit
+       if ! is_running; then
+               d "Not running. Nothing to reload"
+               return 0
+       fi
+       logger -t "$APPNAME" "Reloading $*..."
+       ( do_start "$@" )
+}
+
+add_interface_trigger() {
+        procd_add_interface_trigger "interface.update" "$1" /etc/init.d/$APPNAME reload $1
+}
+
+service_triggers() {
+       preinit; set +e
+       requires
+
+       procd_add_reload_trigger "$APPNAME"
+       config_foreach add_interface_trigger wan
+
+       procd_open_validate
+       validate_trafficshaper_global
+       validate_trafficshaper_wan
+       validate_trafficshaper_class
+       procd_close_validate
+}
+
+validate_trafficshaper_global() {
+        uci_validate_section $APPNAME global "${1}" \
+                'mark_mask:uinteger:0xFF'
+}
+
+validate_trafficshaper_wan() {
+       uci_validate_section "$APPNAME" wan "${1}" \
+                'downlink:uinteger' \
+                'uplink:uinteger'
+}
+
+validate_trafficshaper_class() {
+        uci_validate_section "$APPNAME" class "${1}" \
+               'network:cidr' \
+               'reserved_downlink:or(uinteger, string)' \
+               'reserved_uplink:or(uinteger, string)' \
+               'allowed_downlink:or(uinteger, string)' \
+               'allowed_uplink:or(uinteger, string)'
+}
+
+boot() {
+       LOGLEVEL=1 start
+}