#!/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 command -v $cmd &>/dev/null || die 2 "cannot find command $cmd. Please install $cmd" done 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 } 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 }