From a99b9f128dab3503133c57bb13ee07f915ee49ce Mon Sep 17 00:00:00 2001 From: Luiz Angelo Daros de Luca Date: Fri, 13 Apr 2018 20:13:55 -0300 Subject: trafficshaper: new package trafficshaper create 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. Signed-off-by: Luiz Angelo Daros de Luca --- net/trafficshaper/Makefile | 50 +++ net/trafficshaper/files/etc/config/trafficshaper | 42 ++ net/trafficshaper/files/etc/init.d/trafficshaper | 477 +++++++++++++++++++++++ 3 files changed, 569 insertions(+) create mode 100644 net/trafficshaper/Makefile create mode 100644 net/trafficshaper/files/etc/config/trafficshaper create mode 100755 net/trafficshaper/files/etc/init.d/trafficshaper (limited to 'net/trafficshaper') diff --git a/net/trafficshaper/Makefile b/net/trafficshaper/Makefile new file mode 100644 index 000000000..939c37a97 --- /dev/null +++ b/net/trafficshaper/Makefile @@ -0,0 +1,50 @@ +# +# Copyright (C) 2018 Luiz Angelo Daros de Luca +# +# This is free software, licensed under the GNU General Public License v2. +# + +include $(TOPDIR)/rules.mk + +PKG_NAME:=trafficshaper +PKG_VERSION:=1.0.0 +PKG_RELEASE:=1 +PKG_MAINTAINER:=Luiz Angelo Daros de Luca +PKG_LICENSE:=GPLv2 +PKG_ARCH:=all + +PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME) +include $(INCLUDE_DIR)/package.mk + +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 + 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. + +endef + +define Package/trafficshaper/conffiles +/etc/config/trafficshaper +endef + +define Build/Compile +endef + +define Package/trafficshaper/install +$(CP) ./files/* $(1) +endef + +$(eval $(call BuildPackage,trafficshaper)) diff --git a/net/trafficshaper/files/etc/config/trafficshaper b/net/trafficshaper/files/etc/config/trafficshaper new file mode 100644 index 000000000..daee10332 --- /dev/null +++ b/net/trafficshaper/files/etc/config/trafficshaper @@ -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/etc/init.d/trafficshaper b/net/trafficshaper/files/etc/init.d/trafficshaper new file mode 100755 index 000000000..445f50e38 --- /dev/null +++ b/net/trafficshaper/files/etc/init.d/trafficshaper @@ -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)</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 +} -- cgit v1.2.3