diff --git a/net/luci-app-sqm/Makefile b/net/luci-app-sqm/Makefile new file mode 100644 index 000000000..649424002 --- /dev/null +++ b/net/luci-app-sqm/Makefile @@ -0,0 +1,66 @@ +# +# Copyright (C) 2014 OpenWrt.org +# +# This is free software, licensed under the GNU General Public License v2. +# See /LICENSE for more information. +# + +include $(TOPDIR)/rules.mk + +PKG_NAME:=luci-app-sqm +PKG_VERSION:=3 +PKG_RELEASE:=1 +LUCI_DIR:=/usr/lib/lua/luci + +include $(INCLUDE_DIR)/package.mk + +define Package/luci-app-sqm + SECTION:=luci + CATEGORY:=LuCI + TITLE:=SQM Scripts - LuCI interface + MAINTAINER:=Toke Høiland-Jørgensen + PKGARCH:=all + DEPENDS:= lua luci-base +sqm-scripts + SUBMENU:=3. Applications +endef + +define Package/luci-app-sqm/description + Control the simple_qos SQM script +endef + +define Build/Compile +endef + +define Build/Configure +endef + +define Package/luci-app-sqm/install + $(INSTALL_DIR) $(1)$(LUCI_DIR)/controller $(1)$(LUCI_DIR)/model/cbi + $(INSTALL_DATA) ./files/sqm-controller.lua $(1)$(LUCI_DIR)/controller/sqm.lua + $(INSTALL_DATA) ./files/sqm-cbi.lua $(1)$(LUCI_DIR)/model/cbi/sqm.lua + $(INSTALL_DIR) $(1)/etc/uci-defaults + $(INSTALL_BIN) ./files/uci-defaults-sqm $(1)/etc/uci-defaults/luci-sqm +endef + +define Package/luci-app-sqm/postinst +#!/bin/sh +which uci > /dev/null || exit 0 +uci -q get ucitrack.@sqm[0] > /dev/null || { + uci add ucitrack sqm > /dev/null + uci set ucitrack.@sqm[0].init=sqm + uci add_list ucitrack.@firewall[0].affects=sqm + uci commit +} +endef + +define Package/luci-app-sqm/postrm +#!/bin/sh +which uci > /dev/null || exit 0 +uci -q get ucitrack.@sqm[0] > /dev/null && { + uci delete ucitrack.@sqm[0] + uci del_list ucitrack.@firewall[0].affects=sqm + uci commit +} +endef + +$(eval $(call BuildPackage,luci-app-sqm)) diff --git a/net/luci-app-sqm/files/sqm-cbi.lua b/net/luci-app-sqm/files/sqm-cbi.lua new file mode 100644 index 000000000..e7d79df00 --- /dev/null +++ b/net/luci-app-sqm/files/sqm-cbi.lua @@ -0,0 +1,217 @@ +--[[ +LuCI - Lua Configuration Interface + +Copyright 2014 Steven Barth +Copyright 2014 Dave Taht + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +$Id$ +]]-- + +local wa = require "luci.tools.webadmin" +local fs = require "nixio.fs" +local net = require "luci.model.network".init() +local sys = require "luci.sys" +--local ifaces = net:get_interfaces() +local ifaces = sys.net:devices() +local path = "/usr/lib/sqm" + +m = Map("sqm", translate("Smart Queue Management"), + translate("With SQM you " .. + "can enable traffic shaping, better mixing (Fair Queueing)," .. + " active queue length management (AQM) " .. + " and prioritisation on one " .. + "network interface.")) + +s = m:section(TypedSection, "queue", translate("Queues")) +s:tab("tab_basic", translate("Basic Settings")) +s:tab("tab_qdisc", translate("Queue Discipline")) +s:tab("tab_linklayer", translate("Link Layer Adaptation")) +s.addremove = true -- set to true to allow adding SQM instances in the GUI +s.anonymous = true + +-- BASIC +e = s:taboption("tab_basic", Flag, "enabled", translate("Enable")) +e.rmempty = false + +n = s:taboption("tab_basic", ListValue, "interface", translate("Interface name")) +-- sm lifted from luci-app-wol, the original implementation failed to show pppoe-ge00 type interface names +for _, iface in ipairs(ifaces) do +-- if iface:is_up() then +-- n:value(iface:name()) +-- end + if iface ~= "lo" then + n:value(iface) + end +end +n.rmempty = false + + +dl = s:taboption("tab_basic", Value, "download", translate("Download speed (kbit/s) (ingress):")) +dl.datatype = "and(uinteger,min(0))" +dl.rmempty = false + +ul = s:taboption("tab_basic", Value, "upload", translate("Upload speed (kbit/s) (egress):")) +ul.datatype = "and(uinteger,min(0))" +ul.rmempty = false + +-- QDISC + +c = s:taboption("tab_qdisc", ListValue, "qdisc", translate("Queueing discipline")) +c:value("fq_codel", "fq_codel ("..translate("default")..")") +c:value("efq_codel") +c:value("nfq_codel") +c:value("sfq") +c:value("codel") +c:value("ns2_codel") +c:value("pie") +c:value("sfq") +c.default = "fq_codel" +c.rmempty = false + +local qos_desc = "" +sc = s:taboption("tab_qdisc", ListValue, "script", translate("Queue setup script")) +for file in fs.dir(path) do + if string.find(file, ".qos$") then + sc:value(file) + end + if string.find(file, ".qos.help$") then + fh = io.open(path .. "/" .. file, "r") + qos_desc = qos_desc .. "

" .. file:gsub(".help$", "") .. ":
" .. fh:read("*a") .. "

" + end +end +sc.default = "simple.qos" +sc.rmempty = false +sc.description = qos_desc + +ad = s:taboption("tab_qdisc", Flag, "qdisc_advanced", translate("Show Advanced Configuration")) +ad.default = false +ad.rmempty = true + +squash_dscp = s:taboption("tab_qdisc", ListValue, "squash_dscp", translate("Squash DSCP on inbound packets (ingress):")) +squash_dscp:value("1", "SQUASH") +squash_dscp:value("0", "DO NOT SQUASH") +squash_dscp.default = "1" +squash_dscp.rmempty = true +squash_dscp:depends("qdisc_advanced", "1") + +squash_ingress = s:taboption("tab_qdisc", ListValue, "squash_ingress", translate("Ignore DSCP on ingress:")) +squash_ingress:value("1", "Ignore") +squash_ingress:value("0", "Allow") +squash_ingress.default = "1" +squash_ingress.rmempty = true +squash_ingress:depends("qdisc_advanced", "1") + +iecn = s:taboption("tab_qdisc", ListValue, "ingress_ecn", translate("Explicit congestion notification (ECN) status on inbound packets (ingress):")) +iecn:value("ECN", "ECN ("..translate("default")..")") +iecn:value("NOECN") +iecn.default = "ECN" +iecn.rmempty = true +iecn:depends("qdisc_advanced", "1") + +eecn = s:taboption("tab_qdisc", ListValue, "egress_ecn", translate("Explicit congestion notification (ECN) status on outbound packets (egress).")) +eecn:value("NOECN", "NOECN ("..translate("default")..")") +eecn:value("ECN") +eecn.default = "NOECN" +eecn.rmempty = true +eecn:depends("qdisc_advanced", "1") + +ad2 = s:taboption("tab_qdisc", Flag, "qdisc_really_really_advanced", translate("Show Dangerous Configuration")) +ad2.default = false +ad2.rmempty = true +ad2:depends("qdisc_advanced", "1") + +ilim = s:taboption("tab_qdisc", Value, "ilimit", translate("Hard limit on ingress queues; leave empty for default.")) +-- ilim.default = 1000 +ilim.isnumber = true +ilim.datatype = "and(uinteger,min(0))" +ilim.rmempty = true +ilim:depends("qdisc_really_really_advanced", "1") + +elim = s:taboption("tab_qdisc", Value, "elimit", translate("Hard limit on egress queues; leave empty for default.")) +-- elim.default = 1000 +elim.datatype = "and(uinteger,min(0))" +elim.rmempty = true +elim:depends("qdisc_really_really_advanced", "1") + + +itarg = s:taboption("tab_qdisc", Value, "itarget", translate("Latency target for ingress, e.g 5ms [units: s, ms, or us]; leave empty for default, or auto for automatic selection.")) +itarg.datatype = "string" +itarg.rmempty = true +itarg:depends("qdisc_really_really_advanced", "1") + +etarg = s:taboption("tab_qdisc", Value, "etarget", translate("Latency target for egress, e.g. 5ms [units: s, ms, or us]; leave empty for default, or auto for automatic selection.")) +etarg.datatype = "string" +etarg.rmempty = true +etarg:depends("qdisc_really_really_advanced", "1") + + + +iqdisc_opts = s:taboption("tab_qdisc", Value, "iqdisc_opts", translate("Advanced option string to pass to the ingress queueing disciplines; no error checking, use very carefully.")) +iqdisc_opts.rmempty = true +iqdisc_opts:depends("qdisc_really_really_advanced", "1") + +eqdisc_opts = s:taboption("tab_qdisc", Value, "eqdisc_opts", translate("Advanced option string to pass to the egress queueing disciplines; no error checking, use very carefully.")) +eqdisc_opts.rmempty = true +eqdisc_opts:depends("qdisc_really_really_advanced", "1") + +-- LINKLAYER +ll = s:taboption("tab_linklayer", ListValue, "linklayer", translate("Which link layer to account for:")) +ll:value("none", "none ("..translate("default")..")") +ll:value("ethernet", "Ethernet with overhead: select for e.g. VDSL2.") +ll:value("atm", "ATM: select for e.g. ADSL1, ADSL2, ADSL2+.") +-- ll:value("adsl") -- reduce the options +ll.default = "none" + +po = s:taboption("tab_linklayer", Value, "overhead", translate("Per Packet Overhead (byte):")) +po.datatype = "and(integer,min(-1500))" +po.default = 0 +po.isnumber = true +po.rmempty = true +po:depends("linklayer", "ethernet") +-- po:depends("linklayer", "adsl") +po:depends("linklayer", "atm") + + +adll = s:taboption("tab_linklayer", Flag, "linklayer_advanced", translate("Show Advanced Linklayer Options, (only needed if MTU > 1500)")) +adll.rmempty = true +adll:depends("linklayer", "ethernet") +-- adll:depends("linklayer", "adsl") +adll:depends("linklayer", "atm") + +smtu = s:taboption("tab_linklayer", Value, "tcMTU", translate("Maximal Size for size and rate calculations, tcMTU (byte); needs to be >= interface MTU + overhead:")) +smtu.datatype = "and(uinteger,min(0))" +smtu.default = 2047 +smtu.isnumber = true +smtu.rmempty = true +smtu:depends("linklayer_advanced", "1") + +stsize = s:taboption("tab_linklayer", Value, "tcTSIZE", translate("Number of entries in size/rate tables, TSIZE; for ATM choose TSIZE = (tcMTU + 1) / 16:")) +stsize.datatype = "and(uinteger,min(0))" +stsize.default = 128 +stsize.isnumber = true +stsize.rmempty = true +stsize:depends("linklayer_advanced", "1") + +smpu = s:taboption("tab_linklayer", Value, "tcMPU", translate("Minimal packet size, MPU (byte); needs to be > 0 for ethernet size tables:")) +smpu.datatype = "and(uinteger,min(0))" +smpu.default = 0 +smpu.isnumber = true +smpu.rmempty = true +smpu:depends("linklayer_advanced", "1") + +lla = s:taboption("tab_linklayer", ListValue, "linklayer_adaptation_mechanism", translate("Which linklayer adaptation mechanism to use; for testing only")) +lla:value("htb_private") +lla:value("tc_stab", "tc_stab ("..translate("default")..")") +lla.default = "tc_stab" +lla.rmempty = true +lla:depends("linklayer_advanced", "1") + +-- PRORITIES? + +return m diff --git a/net/luci-app-sqm/files/sqm-controller.lua b/net/luci-app-sqm/files/sqm-controller.lua new file mode 100644 index 000000000..d36f5fd67 --- /dev/null +++ b/net/luci-app-sqm/files/sqm-controller.lua @@ -0,0 +1,26 @@ +--[[ +LuCI - Lua Configuration Interface + +Copyright 2008 Steven Barth + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +$Id$ +]]-- + +module("luci.controller.sqm", package.seeall) + +function index() + if not nixio.fs.access("/etc/config/sqm") then + return + end + + local page + + page = entry({"admin", "network", "sqm"}, cbi("sqm"), _("SQM QoS")) + page.dependent = true +end diff --git a/net/luci-app-sqm/files/uci-defaults-sqm b/net/luci-app-sqm/files/uci-defaults-sqm new file mode 100644 index 000000000..cd9e7b2b1 --- /dev/null +++ b/net/luci-app-sqm/files/uci-defaults-sqm @@ -0,0 +1,12 @@ +#!/bin/sh + +uci -q batch <<-EOF >/dev/null + delete ucitrack.@sqm[-1] + add ucitrack sqm + set ucitrack.@sqm[-1].init=sqm + add_list ucitrack.@firewall[0].affects=sqm + commit ucitrack +EOF + +rm -f /tmp/luci-indexcache +exit 0 diff --git a/net/sqm-scripts/Makefile b/net/sqm-scripts/Makefile new file mode 100644 index 000000000..9f78e06a9 --- /dev/null +++ b/net/sqm-scripts/Makefile @@ -0,0 +1,50 @@ +# +# Copyright (C) 2014 OpenWrt.org +# +# This is free software, licensed under the GNU General Public License v2. +# See /LICENSE for more information. +# + +include $(TOPDIR)/rules.mk + +PKG_NAME:=sqm-scripts +PKG_VERSION:=6 +PKG_RELEASE:=1 + +PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME) + +include $(INCLUDE_DIR)/package.mk + +define Package/sqm-scripts + SECTION:=net + CATEGORY:=Base system + DEPENDS:=+tc +kmod-sched +kmod-ifb iptables +ip \ + +iptables-mod-filter +iptables-mod-ipopt +iptables-mod-conntrack-extra + TITLE:=SQM Scripts (QoS) + PKGARCH:=all + MAINTAINER:=Toke Høiland-Jørgensen +endef + +define Package/sqm-scripts/description + A set of scripts that does simple SQM configuration. +endef + +define Package/sqm-scripts/conffiles +/etc/config/sqm +endef + +define Build/Prepare +endef + +define Build/Configure +endef + +define Build/Compile +endef + +define Package/sqm-scripts/install + $(INSTALL_DIR) $(1) + $(CP) ./files/* $(1)/ +endef + +$(eval $(call BuildPackage,sqm-scripts)) diff --git a/net/sqm-scripts/files/etc/config/sqm b/net/sqm-scripts/files/etc/config/sqm new file mode 100644 index 000000000..d03980853 --- /dev/null +++ b/net/sqm-scripts/files/etc/config/sqm @@ -0,0 +1,16 @@ + +config queue 'eth1' + option enabled '0' + option interface 'eth1' + option download '85000' + option upload '10000' + option qdisc 'fq_codel' + option script 'simple.qos' + option qdisc_advanced '0' + option ingress_ecn 'ECN' + option egress_ecn 'NOECN' + option qdisc_really_really_advanced '0' + option itarget 'auto' + option etarget 'auto' + option linklayer 'none' + diff --git a/net/sqm-scripts/files/etc/init.d/sqm b/net/sqm-scripts/files/etc/init.d/sqm new file mode 100755 index 000000000..70307a6e1 --- /dev/null +++ b/net/sqm-scripts/files/etc/init.d/sqm @@ -0,0 +1,23 @@ +#!/bin/sh /etc/rc.common + +START=50 + +reload() +{ +/usr/lib/sqm/run.sh +} + +restart() +{ +reload +} + +start() +{ +reload +} + +stop() +{ +/usr/lib/sqm/run.sh stop +} \ No newline at end of file diff --git a/net/sqm-scripts/files/usr/lib/sqm/functions.sh b/net/sqm-scripts/files/usr/lib/sqm/functions.sh new file mode 100644 index 000000000..63a67e358 --- /dev/null +++ b/net/sqm-scripts/files/usr/lib/sqm/functions.sh @@ -0,0 +1,493 @@ +#improve the logread output +sqm_logger() { + logger -t SQM -s ${1} +} + +insmod() { + lsmod | grep -q ^$1 || $INSMOD $1 +} + +ipt() { + d=`echo $* | sed s/-A/-D/g` + [ "$d" != "$*" ] && { + iptables $d > /dev/null 2>&1 + ip6tables $d > /dev/null 2>&1 + } + d=`echo $* | sed s/-I/-D/g` + [ "$d" != "$*" ] && { + iptables $d > /dev/null 2>&1 + ip6tables $d > /dev/null 2>&1 + } + iptables $* > /dev/null 2>&1 + ip6tables $* > /dev/null 2>&1 +} + +do_modules() { + insmod act_ipt + insmod sch_$QDISC + insmod sch_ingress + insmod act_mirred + insmod cls_fw + insmod sch_htb +} + + +# You need to jiggle these parameters. Note limits are tuned towards a <10Mbit uplink <60Mbup down + +[ -z "$UPLINK" ] && UPLINK=2302 +[ -z "$DOWNLINK" ] && DOWNLINK=14698 +[ -z "$IFACE" ] && IFACE=ge00 +[ -z "$QDISC" ] && QDISC=fq_codel +[ -z "$LLAM" ] && LLAM="tc_stab" +[ -z "$LINKLAYER" ] && LINKLAYER="none" +[ -z "$OVERHEAD" ] && OVERHEAD=0 +[ -z "$STAB_MTU" ] && STAB_MTU=2047 +[ -z "$STAB_MPU" ] && STAB_MPU=0 +[ -z "$STAB_TSIZE" ] && STAB_TSIZE=512 +[ -z "$AUTOFLOW" ] && AUTOFLOW=0 +[ -z "$LIMIT" ] && LIMIT=1001 # sane global default for *LIMIT for fq_codel on a small memory device +[ -z "$ILIMIT" ] && ILIMIT= +[ -z "$ELIMIT" ] && ELIMIT= +[ -z "$ITARGET" ] && ITARGET= +[ -z "$ETARGET" ] && ETARGET= +[ -z "$IECN" ] && IECN="ECN" +[ -z "$EECN" ] && EECN="NOECN" +[ -z "$SQUASH_DSCP" ] && SQUASH_DSCP="1" +[ -z "SQUASH_INGRESS" ] && SQUASH_INGRESS="1" +[ -z "$IQDISC_OPTS" ] && IQDISC_OPTS="" +[ -z "$EQDISC_OPTS" ] && EQDISC_OPTS="" +[ -z "$TC" ] && TC=`which tc` +#[ -z "$TC" ] && TC="sqm_logger tc"# this redirects all tc calls into the log +[ -z "$IP" ] && IP=$( which ip ) +[ -z "$INSMOD" ] && INSMOD=`which insmod` +[ -z "TARGET" ] && TARGET="5ms" + + + + +# find the ifb device associated with a specific interface, return nothing of no ifb is associated with IF +get_ifb_associated_with_if() { + CUR_IF=$1 + # CUR_IFB=$( tc -p filter show parent ffff: dev ${CUR_IF} | grep -o -e ifb'[[:digit:]]\+' ) + CUR_IFB=$( tc -p filter show parent ffff: dev ${CUR_IF} | grep -o -e ifb'[^)]\+' ) # my editor's syntax coloration is limitied so I need a single quote in this line (between eiditor and s) + sqm_logger "ifb associated with interface ${CUR_IF}: ${CUR_IFB}" + echo ${CUR_IFB} +} + +# what is the lowest-index currently not used ifb device +#sm: hopefully this is not required any longer, and can be deleted after a bit more testing... +get_lowest_unused_ifb() { + LOWEST_FREE_IFB= + # this just returns a list of currently existing IFBs, these need not be associated with any interface + CUR_UP_IFBS=$( ifconfig | grep -o -e ifb'[[:digit:]]\+' ) + # the possible IFBs (N in insmod ifb numifbs=N) + here=$( pwd ) + cd /sys/devices/virtual/net/ + CUR_ALLOWED_IFBS=$( ls -d ifb* ) + sqm_logger "Currently allowed IFBs: ${CUR_ALLOWED_IFBS}" + cd ${here} + # this is the sorted list of the active ifbs + # note for 3.10.32 unused and even down IFBs linger on in the tc output, so take $CUR_UP_IFBS instead + # a better test might be to check for each allowed IFB whether it is in use + # but the only way I figured out doing this means interating over all interfaces and that sounds costly + # so instead we rely on stop.sh actually calling ifconfig ${LAST_USED_IFB} down + CUR_USED_IFBS=$( tc -d qdisc | grep -o -e ifb'[[:digit:]]\+' | sort -u) + sqm_logger "Currently used IFBs: ${CUR_USED_IFBS}" + # now find the lowest index not in the sorted list + local CUR_IDX=0 + while [ -z "$LOWEST_FREE_IFB" ] + do + #TMP=$( echo "${CUR_USED_IFBS}" | grep -o -e ifb${CUR_IDX} ) + TMP=$( echo "${CUR_UP_IFBS}" | grep -o -e ifb${CUR_IDX} ) + [ -z "$TMP" ] && LOWEST_FREE_IFB="ifb"${CUR_IDX} + CUR_IDX=$(( $CUR_IDX + 1 )) + done + # check whether the number is in the allowed range + LOWEST_FREE_IFB=$( echo "${CUR_ALLOWED_IFBS}" | grep -o -e ${LOWEST_FREE_IFB} ) + [ -z "${LOWEST_FREE_IFB}" ] && sqm_logger "The IFB candidate ifb$(( ${CUR_IDX} - 1 )) is not in the range of allowed IFBs, bailing out..." + sqm_logger "selected ifb index: ${LOWEST_FREE_IFB}" + echo ${LOWEST_FREE_IFB} +} + +# instead of playing around with indices just create a named IFB +# ATTENTION, IFB names can only be 15 chararcters, so we chop of excessive characters at the start of the interface name +# if required +create_new_ifb_for_if() { + CUR_IF=$1 + MAX_IF_NAME_LENGTH=15 + IFB_PREFIX="ifb4" + NEW_IFB="${IFB_PREFIX}${CUR_IF}" + IFB_NAME_LENGTH=${#NEW_IFB} + if [ ${IFB_NAME_LENGTH} -gt ${MAX_IF_NAME_LENGTH} ]; + then + sqm_logger "The requsted IFB name ${NEW_IFB} is longer than the allowed 15 characters, trying to make it shorter" + OVERLIMIT=$(( ${#NEW_IFB} - ${MAX_IF_NAME_LENGTH} )) + NEW_IFB=${IFB_PREFIX}${CUR_IF:${OVERLIMIT}:$(( ${MAX_IF_NAME_LENGTH} - ${#IFB_PREFIX} ))} + fi + sqm_logger "trying to create new IFB: ${NEW_IFB}" + $IP link add name ${NEW_IFB} type ifb #>/dev/null 2>&1 # better be verbose + echo ${NEW_IFB} +} + +# the best match is either the IFB already associated with the current interface or a new named IFB +get_ifb_for_if() { + CUR_IF=$1 + # if an ifb is already associated return that + CUR_IFB=$( get_ifb_associated_with_if ${CUR_IF} ) + # otherwise find the lowest unused ifb device + #[ -z "$CUR_IFB" ] && CUR_IFB=$( get_lowest_unused_ifb ) + [ -z "$CUR_IFB" ] && CUR_IFB=$( create_new_ifb_for_if ${CUR_IF} ) + [ -z "$CUR_IFB" ] && sqm_logger "Could not find existing IFB for ${CUR_IF}, nor create a new IFB instead..." + echo ${CUR_IFB} +} + +#sm: we need the functions above before trying to set the ingress IFB device +[ -z "$DEV" ] && DEV=$( get_ifb_for_if ${IFACE} ) # automagically get the right IFB device for the IFACE" + + + +#sqm_logger "iqdisc opts: ${iqdisc_opts}" +#sqm_logger "eqdisc opts: ${eqdisc_opts}" + +#sqm_logger "LLAM: ${LLAM}" +#sqm_logger "LINKLAYER: ${LINKLAYER}" + +get_htb_adsll_string() { + ADSLL="" + if [ "$LLAM" = "htb_private" -a "$LINKLAYER" != "none" ]; + then + # HTB defaults to MTU 1600 and an implicit fixed TSIZE of 256, but HTB as of around 3.10.0 + # does not actually use a table in the kernel + ADSLL="mpu ${STAB_MPU} linklayer ${LINKLAYER} overhead ${OVERHEAD} mtu ${STAB_MTU}" + sqm_logger "ADSLL: ${ADSLL}" + fi + echo ${ADSLL} +} + +get_stab_string() { + STABSTRING="" + if [ "${LLAM}" = "tc_stab" -a "$LINKLAYER" != "none" ]; + then + STABSTRING="stab mtu ${STAB_MTU} tsize ${STAB_TSIZE} mpu ${STAB_MPU} overhead ${OVERHEAD} linklayer ${LINKLAYER}" + sqm_logger "STAB: ${STABSTRING}" + fi + echo ${STABSTRING} +} + +sqm_stop() { + $TC qdisc del dev $IFACE ingress + $TC qdisc del dev $IFACE root + $TC qdisc del dev $DEV root +} + +# Note this has side effects on the prio variable +# and depends on the interface global too + +fc() { + $TC filter add dev $interface protocol ip parent $1 prio $prio u32 match ip tos $2 0xfc classid $3 + prio=$(($prio + 1)) + $TC filter add dev $interface protocol ipv6 parent $1 prio $prio u32 match ip6 priority $2 0xfc classid $3 + prio=$(($prio + 1)) +} + +fc_pppoe() { + PPPOE_SESSION_ETHERTYPE="0x8864" + PPPOE_DISCOVERY_ETHERTYPE="0x8863" + PPP_PROTO_IP4="0x0021" + PPP_PROTO_IP6="0x0057" + ARP_PROTO_IP4="0x0806" + $TC filter add dev $interface protocol ip parent $1 prio $prio u32 match ip tos $2 0xfc classid $3 + $TC filter add dev $interface parent $1 protocol ${PPPOE_SESSION_ETHERTYPE} prio $(( 400 + ${prio} )) u32 \ + match u16 ${PPP_PROTO_IP4} 0xffff at 6 \ + match u8 $2 0xfc at 9 \ + flowid $3 + + prio=$(($prio + 1)) + $TC filter add dev $interface protocol ipv6 parent $1 prio $prio u32 match ip6 priority $2 0xfc classid $3 + $TC filter add dev $interface parent $1 protocol ${PPPOE_SESSION_ETHERTYPE} prio $(( 600 + ${prio} )) u32 \ + match u16 ${PPP_PROTO_IP6} 0xffff at 6 \ + match u16 0x0${2:2:2}0 0x0fc0 at 8 \ + flowid $3 + + + + + prio=$(($prio + 1)) + + +} +# FIXME: actually you need to get the underlying MTU on PPOE thing + +get_mtu() { + BW=$2 + F=`cat /sys/class/net/$1/mtu` + if [ -z "$F" ] + then + F=1500 + fi + if [ $BW -gt 20000 ] + then + F=$(($F * 2)) + fi + if [ $BW -gt 30000 ] + then + F=$(($F * 2)) + fi + if [ $BW -gt 40000 ] + then + F=$(($F * 2)) + fi + if [ $BW -gt 50000 ] + then + F=$(($F * 2)) + fi + if [ $BW -gt 60000 ] + then + F=$(($F * 2)) + fi + if [ $BW -gt 80000 ] + then + F=$(($F * 2)) + fi + echo $F +} + +# FIXME should also calculate the limit +# Frankly I think Xfq_codel can pretty much always run with high numbers of flows +# now that it does fate sharing +# But right now I'm trying to match the ns2 model behavior better +# So SET the autoflow variable to 1 if you want the cablelabs behavior + +get_flows() { + if [ "$AUTOFLOW" -eq "1" ] + then + FLOWS=8 + [ $1 -gt 999 ] && FLOWS=16 + [ $1 -gt 2999 ] && FLOWS=32 + [ $1 -gt 7999 ] && FLOWS=48 + [ $1 -gt 9999 ] && FLOWS=64 + [ $1 -gt 19999 ] && FLOWS=128 + [ $1 -gt 39999 ] && FLOWS=256 + [ $1 -gt 69999 ] && FLOWS=512 + [ $1 -gt 99999 ] && FLOWS=1024 + case $QDISC in + codel|ns2_codel|pie|*fifo|pfifo_fast) ;; + fq_codel|*fq_codel|sfq) echo flows $FLOWS ;; + esac + fi +} + +# set the target parameter, also try to only take well formed inputs +# Note, the link bandwidth in the current direction (ingress or egress) +# is required to adjust the target for slow links +get_target() { + local CUR_TARGET=${1} + local CUR_LINK_KBPS=${2} + [ ! -z "$CUR_TARGET" ] && sqm_logger "cur_target: ${CUR_TARGET} cur_bandwidth: ${CUR_LINK_KBPS}" + CUR_TARGET_STRING= + # either e.g. 100ms or auto + CUR_TARGET_VALUE=$( echo ${CUR_TARGET} | grep -o -e \^'[[:digit:]]\+' ) + CUR_TARGET_UNIT=$( echo ${CUR_TARGET} | grep -o -e '[[:alpha:]]\+'\$ ) +# [ ! -z "$CUR_TARGET" ] && sqm_logger "CUR_TARGET_VALUE: $CUR_TARGET_VALUE" +# [ ! -z "$CUR_TARGET" ] && sqm_logger "CUR_TARGET_UNIT: $CUR_TARGET_UNIT" + + AUTO_TARGET= + UNIT_VALID= + + case $QDISC in + *codel|*pie) + if [ ! -z "${CUR_TARGET_VALUE}" -a ! -z "${CUR_TARGET_UNIT}" ]; + then + case ${CUR_TARGET_UNIT} in + # permissible units taken from: tc_util.c get_time() + s|sec|secs|ms|msec|msecs|us|usec|usecs) + CUR_TARGET_STRING="target ${CUR_TARGET_VALUE}${CUR_TARGET_UNIT}" + UNIT_VALID="1" + ;; + esac + fi + case ${CUR_TARGET_UNIT} in + auto|Auto|AUTO) + if [ ! -z "${CUR_LINK_KBPS}" ]; + then + TMP_TARGET_US=$( adapt_target_to_slow_link $CUR_LINK_KBPS ) + TMP_INTERVAL_STRING=$( adapt_interval_to_slow_link $TMP_TARGET_US ) + CUR_TARGET_STRING="target ${TMP_TARGET_US}us ${TMP_INTERVAL_STRING}" + AUTO_TARGET="1" + else + sqm_logger "required link bandwidth in kbps not passed to get_target()." + fi + ;; + esac + if [ ! -z "${CUR_TARGET}" ]; + then + if [ -z "${CUR_TARGET_VALUE}" -o -z "${UNIT_VALID}" ]; + then + [ -z "$AUTO_TARGET" ] && sqm_logger "${CUR_TARGET} is not a well formed tc target specifier; e.g.: 5ms (or s, us), or the string auto." + fi + fi + ;; + esac +# sqm_logger "target: ${CUR_TARGET_STRING}" + echo $CUR_TARGET_STRING +} + +# for low bandwidth links fq_codels default target of 5ms does not work too well +# so increase target for slow links (note below roughly 2500kbps a single packet will \ +# take more than 5 ms to be tansfered over the wire) +adapt_target_to_slow_link() { + CUR_LINK_KBPS=$1 + CUR_EXTENDED_TARGET_US= + MAX_PAKET_DELAY_IN_US_AT_1KBPS=$(( 1000 * 1000 *1540 * 8 / 1000 )) + CUR_EXTENDED_TARGET_US=$(( ${MAX_PAKET_DELAY_IN_US_AT_1KBPS} / ${CUR_LINK_KBPS} )) # note this truncates the decimals + # do not change anything for fast links + [ "$CUR_EXTENDED_TARGET_US" -lt 5000 ] && CUR_EXTENDED_TARGET_US=5000 + case ${QDISC} in + *codel|pie) + echo "${CUR_EXTENDED_TARGET_US}" + ;; + esac +} + +# codel looks at a whole interval to figure out wether observed latency stayed below target +# if target >= interval that will not work well, so increase interval by the same amonut that target got increased +adapt_interval_to_slow_link() { + CUR_TARGET_US=$1 + case ${QDISC} in + *codel) + CUR_EXTENDED_INTERVAL_US=$(( (100 - 5) * 1000 + ${CUR_TARGET_US} )) + echo "interval ${CUR_EXTENDED_INTERVAL_US}us" + ;; + pie) + ## not sure if pie needs this, probably not + #CUR_EXTENDED_TUPDATE_US=$(( (30 - 20) * 1000 + ${CUR_TARGET_US} )) + #echo "tupdate ${CUR_EXTENDED_TUPDATE_US}us" + ;; + esac +} + + +# set quantum parameter if available for this qdisc +get_quantum() { + case $QDISC in + *fq_codel|fq_pie|drr) echo quantum $1 ;; + *) ;; + esac +} + +# only show limits to qdiscs that can handle them... +# Note that $LIMIT contains the default limit +get_limit() { + CURLIMIT=$1 + case $QDISC in + *codel|*pie|pfifo_fast|sfq|pfifo) [ -z ${CURLIMIT} ] && CURLIMIT=${LIMIT} # use the global default limit + ;; + bfifo) [ -z "$CURLIMIT" ] && [ ! -z "$LIMIT" ] && CURLIMIT=$(( ${LIMIT} * $( cat /sys/class/net/${IFACE}/mtu ) )) # bfifo defaults to txquelength * MTU, + ;; + *) sqm_logger "${QDISC} does not support a limit" + ;; + esac + sqm_logger "get_limit: $1 CURLIMIT: ${CURLIMIT}" + + if [ ! -z "$CURLIMIT" ] + then + echo "limit ${CURLIMIT}" + fi +} + +get_ecn() { + CURECN=$1 + #sqm_logger CURECN: $CURECN + case ${CURECN} in + ECN) + case $QDISC in + *codel|*pie|*red) + CURECN=ecn + ;; + *) + CURECN="" + ;; + esac + ;; + NOECN) + case $QDISC in + *codel|*pie|*red) + CURECN=noecn + ;; + *) + CURECN="" + ;; + esac + ;; + *) + sqm_logger "ecn value $1 not handled" + ;; + esac + #sqm_logger "get_ECN: $1 CURECN: ${CURECN} IECN: ${IECN} EECN: ${EECN}" + echo ${CURECN} + +} + +# This could be a complete diffserv implementation + +diffserv() { + +interface=$1 +prio=1 + +# Catchall + +$TC filter add dev $interface parent 1:0 protocol all prio 999 u32 \ + match ip protocol 0 0x00 flowid 1:12 + +# Find the most common matches fast +#fc_pppoe() instead of fc() with effectice ingress classification for pppoe is very expensive and destroys LUL + +fc 1:0 0x00 1:12 # BE +fc 1:0 0x20 1:13 # CS1 +fc 1:0 0x10 1:11 # IMM +fc 1:0 0xb8 1:11 # EF +fc 1:0 0xc0 1:11 # CS3 +fc 1:0 0xe0 1:11 # CS6 +fc 1:0 0x90 1:11 # AF42 (mosh) + +# Arp traffic +$TC filter add dev $interface protocol arp parent 1:0 prio $prio handle 500 fw flowid 1:11 + + +prio=$(($prio + 1)) + + +} + +diffserv_pppoe() { + +interface=$1 +prio=1 + +# Catchall + +$TC filter add dev $interface parent 1:0 protocol all prio 999 u32 \ + match ip protocol 0 0x00 flowid 1:12 + +# Find the most common matches fast +#fc_pppoe() instead of fc() with effectice ingress classification for pppoe is very expensive and destroys LUL + +fc_pppoe 1:0 0x00 1:12 # BE +fc_pppoe 1:0 0x20 1:13 # CS1 +fc_pppoe 1:0 0x10 1:11 # IMM +fc_pppoe 1:0 0xb8 1:11 # EF +fc_pppoe 1:0 0xc0 1:11 # CS3 +fc_pppoe 1:0 0xe0 1:11 # CS6 +fc_pppoe 1:0 0x90 1:11 # AF42 (mosh) + +# Arp traffic +$TC filter add dev $interface protocol arp parent 1:0 prio $prio handle 500 fw flowid 1:11 + + +prio=$(($prio + 1)) + + +} + + diff --git a/net/sqm-scripts/files/usr/lib/sqm/run.sh b/net/sqm-scripts/files/usr/lib/sqm/run.sh new file mode 100755 index 000000000..5453bea3f --- /dev/null +++ b/net/sqm-scripts/files/usr/lib/sqm/run.sh @@ -0,0 +1,82 @@ +#!/bin/sh + +. /lib/functions.sh + +STOP=$1 +ACTIVE_STATE_PREFIX="SQM_active_on_" +ACTIVE_STATE_FILE_DIR="/var/run/SQM" +mkdir -p ${ACTIVE_STATE_FILE_DIR} + +# the current uci config file does not necessarily contain sections for all interfaces with active +# SQM instances, so use the ACTIVE_STATE_FILES to detect the interfaces on which to stop SQM. +# Currently the .qos scripts start with stopping any existing traffic shaping so this should not +# effectively change anything... +PROTO_STATE_FILE_LIST=$( ls ${ACTIVE_STATE_FILE_DIR}/${ACTIVE_STATE_PREFIX}* 2> /dev/null ) +for STATE_FILE in ${PROTO_STATE_FILE_LIST} ; do + if [ -f ${STATE_FILE} ] ; + then + STATE_FILE_BASE_NAME=$( basename ${STATE_FILE} ) + CURRENT_INTERFACE=${STATE_FILE_BASE_NAME:${#ACTIVE_STATE_PREFIX}:$(( ${#STATE_FILE_BASE_NAME} - ${#ACTIVE_STATE_PREFIX} ))} + logger -t SQM -s "Stopping SQM on interface: ${CURRENT_INTERFACE}" + /usr/lib/sqm/stop.sh ${CURRENT_INTERFACE} + rm ${STATE_FILE} # well, we stop it so it is not running anymore and hence no active state file needed... + fi +done + +config_load sqm + +run_simple_qos() { + local section="$1" + export IFACE=$(config_get "$section" interface) + ACTIVE_STATE_FILE_FQN="${ACTIVE_STATE_FILE_DIR}/${ACTIVE_STATE_PREFIX}${IFACE}" # this marks interfaces as active with SQM + [ -f "${ACTIVE_STATE_FILE_FQN}" ] && logger -t SQM -s "Uh, oh, ${ACTIVE_STATE_FILE_FQN} should already be stopped." # Not supposed to happen + + if [ $(config_get "$section" enabled) -ne 1 ]; + then + if [ -f "${ACTIVE_STATE_FILE_FQN}" ]; + then + # this should not be possible, delete after testing + local SECTION_STOP="stop" # it seems the user just de-selected enable, so stop the active SQM + else + logger -t SQM -s "SQM for interface ${IFACE} is not enabled, skipping over..." + return 0 # since SQM is not active on the current interface nothing to do here + fi + fi + + export UPLINK=$(config_get "$section" upload) + export DOWNLINK=$(config_get "$section" download) + export LLAM=$(config_get "$section" linklayer_adaptation_mechanism) + export LINKLAYER=$(config_get "$section" linklayer) + export OVERHEAD=$(config_get "$section" overhead) + export STAB_MTU=$(config_get "$section" tcMTU) + export STAB_TSIZE=$(config_get "$section" tcTSIZE) + export STAB_MPU=$(config_get "$section" tcMPU) + export ILIMIT=$(config_get "$section" ilimit) + export ELIMIT=$(config_get "$section" elimit) + export ITARGET=$(config_get "$section" itarget) + export ETARGET=$(config_get "$section" etarget) + export IECN=$(config_get "$section" ingress_ecn) + export EECN=$(config_get "$section" egress_ecn) + export IQDISC_OPTS=$(config_get "$section" iqdisc_opts) + export EQDISC_OPTS=$(config_get "$section" eqdisc_opts) + export TARGET=$(config_get "$section" target) + export SQUASH_DSCP=$(config_get "$section" squash_dscp) + export SQUASH_INGRESS=$(config_get "$section" squash_ingress) + + export QDISC=$(config_get "$section" qdisc) + export SCRIPT=/usr/lib/sqm/$(config_get "$section" script) + +# # there should be nothing left to stop, so just avoid calling the script + if [ "$STOP" == "stop" -o "$SECTION_STOP" == "stop" ]; + then +# /usr/lib/sqm/stop.sh +# [ -f ${ACTIVE_STATE_FILE_FQN} ] && rm ${ACTIVE_STATE_FILE_FQN} # conditional to avoid errors ACTIVE_STATE_FILE_FQN does not exist anymore +# $(config_set "$section" enabled 0) # this does not save to the config file only to the loaded memory representation +# logger -t SQM -s "SQM qdiscs on ${IFACE} removed" + return 0 + fi + logger -t SQM -s "Queue Setup Script: ${SCRIPT}" + [ -x "$SCRIPT" ] && { $SCRIPT ; touch ${ACTIVE_STATE_FILE_FQN}; } +} + +config_foreach run_simple_qos diff --git a/net/sqm-scripts/files/usr/lib/sqm/simple.qos b/net/sqm-scripts/files/usr/lib/sqm/simple.qos new file mode 100755 index 000000000..efafada4c --- /dev/null +++ b/net/sqm-scripts/files/usr/lib/sqm/simple.qos @@ -0,0 +1,212 @@ +#!/bin/sh +# Cero3 Shaper +# A 3 bin tc_codel and ipv6 enabled shaping script for +# ethernet gateways + +# Copyright (C) 2012 Michael D Taht +# GPLv2 + +# Compared to the complexity that debloat had become +# this cleanly shows a means of going from diffserv marking +# to prioritization using the current tools (ip(6)tables +# and tc. I note that the complexity of debloat exists for +# a reason, and it is expected that script is run first +# to setup various other parameters such as BQL and ethtool. +# (And that the debloat script has setup the other interfaces) + +# You need to jiggle these parameters. Note limits are tuned towards a <10Mbit uplink <60Mbup down + +. /usr/lib/sqm/functions.sh + +ipt_setup() { + +ipt -t mangle -N QOS_MARK_${IFACE} + +ipt -t mangle -A QOS_MARK_${IFACE} -j MARK --set-mark 0x2 +# You can go further with classification but... +ipt -t mangle -A QOS_MARK_${IFACE} -m dscp --dscp-class CS1 -j MARK --set-mark 0x3 +ipt -t mangle -A QOS_MARK_${IFACE} -m dscp --dscp-class CS6 -j MARK --set-mark 0x1 +ipt -t mangle -A QOS_MARK_${IFACE} -m dscp --dscp-class EF -j MARK --set-mark 0x1 +ipt -t mangle -A QOS_MARK_${IFACE} -m dscp --dscp-class AF42 -j MARK --set-mark 0x1 +ipt -t mangle -A QOS_MARK_${IFACE} -m tos --tos Minimize-Delay -j MARK --set-mark 0x1 + +# and it might be a good idea to do it for udp tunnels too + +# Turn it on. Preserve classification if already performed + +if [ "$SQUASH_DSCP" = "1" ] +then +sqm_logger "Squashing differentiad services code points (DSCP) from ingress." +ipt -t mangle -I PREROUTING -i $IFACE -m dscp ! --dscp 0 -j DSCP --set-dscp-class be +else +sqm_logger "Keeping differentiad services code points (DSCP) from ingress." +ipt -t mangle -A PREROUTING -i $IFACE -m mark --mark 0x00 -g QOS_MARK_${IFACE} +fi + +ipt -t mangle -A POSTROUTING -o $IFACE -m mark --mark 0x00 -g QOS_MARK_${IFACE} + +# The Syn optimization was nice but fq_codel does it for us +# ipt -t mangle -A PREROUTING -i s+ -p tcp -m tcp --tcp-flags SYN,RST,ACK SYN -j MARK --set-mark 0x01 +# Not sure if this will work. Encapsulation is a problem period + +ipt -t mangle -I PREROUTING -i vtun+ -p tcp -j MARK --set-mark 0x2 # tcp tunnels need ordering + +# Emanating from router, do a little more optimization +# but don't bother with it too much. + +ipt -t mangle -A OUTPUT -p udp -m multiport --ports 123,53 -j DSCP --set-dscp-class AF42 + +#Not clear if the second line is needed +#ipt -t mangle -A OUTPUT -o $IFACE -g QOS_MARK_${IFACE} + +} + + +# TC rules + +egress() { + +CEIL=${UPLINK} +PRIO_RATE=`expr $CEIL / 3` # Ceiling for prioirty +BE_RATE=`expr $CEIL / 6` # Min for best effort +BK_RATE=`expr $CEIL / 6` # Min for background +BE_CEIL=`expr $CEIL - 16` # A little slop at the top + +LQ="quantum `get_mtu $IFACE $CEIL`" + +$TC qdisc del dev $IFACE root 2> /dev/null +$TC qdisc add dev $IFACE root handle 1: `get_stab_string` htb default 12 +$TC class add dev $IFACE parent 1: classid 1:1 htb $LQ rate ${CEIL}kbit ceil ${CEIL}kbit `get_htb_adsll_string` +$TC class add dev $IFACE parent 1:1 classid 1:10 htb $LQ rate ${CEIL}kbit ceil ${CEIL}kbit prio 0 `get_htb_adsll_string` +$TC class add dev $IFACE parent 1:1 classid 1:11 htb $LQ rate 128kbit ceil ${PRIO_RATE}kbit prio 1 `get_htb_adsll_string` +$TC class add dev $IFACE parent 1:1 classid 1:12 htb $LQ rate ${BE_RATE}kbit ceil ${BE_CEIL}kbit prio 2 `get_htb_adsll_string` +$TC class add dev $IFACE parent 1:1 classid 1:13 htb $LQ rate ${BK_RATE}kbit ceil ${BE_CEIL}kbit prio 3 `get_htb_adsll_string` + +$TC qdisc add dev $IFACE parent 1:11 handle 110: $QDISC `get_limit ${ELIMIT}` `get_target "${ETARGET}" ${UPLINK}` `get_ecn ${EECN}` `get_quantum 300` `get_flows ${PRIO_RATE}` ${EQDISC_OPTS} +$TC qdisc add dev $IFACE parent 1:12 handle 120: $QDISC `get_limit ${ELIMIT}` `get_target "${ETARGET}" ${UPLINK}` `get_ecn ${EECN}` `get_quantum 300` `get_flows ${BE_RATE}` ${EQDISC_OPTS} +$TC qdisc add dev $IFACE parent 1:13 handle 130: $QDISC `get_limit ${ELIMIT}` `get_target "${ETARGET}" ${UPLINK}` `get_ecn ${EECN}` `get_quantum 300` `get_flows ${BK_RATE}` ${EQDISC_OPTS} + + +# Need a catchall rule + +$TC filter add dev $IFACE parent 1:0 protocol all prio 999 u32 \ + match ip protocol 0 0x00 flowid 1:12 + +# FIXME should probably change the filter here to do pre-nat + +$TC filter add dev $IFACE parent 1:0 protocol ip prio 1 handle 1 fw classid 1:11 +$TC filter add dev $IFACE parent 1:0 protocol ip prio 2 handle 2 fw classid 1:12 +$TC filter add dev $IFACE parent 1:0 protocol ip prio 3 handle 3 fw classid 1:13 + +# ipv6 support. Note that the handle indicates the fw mark bucket that is looked for + +$TC filter add dev $IFACE parent 1:0 protocol ipv6 prio 4 handle 1 fw classid 1:11 +$TC filter add dev $IFACE parent 1:0 protocol ipv6 prio 5 handle 2 fw classid 1:12 +$TC filter add dev $IFACE parent 1:0 protocol ipv6 prio 6 handle 3 fw classid 1:13 + +# Arp traffic + +$TC filter add dev $IFACE parent 1:0 protocol arp prio 7 handle 1 fw classid 1:11 + +# ICMP traffic - Don't impress your friends. Deoptimize to manage ping floods +# better instead + +$TC filter add dev $IFACE parent 1:0 protocol ip prio 8 \ + u32 match ip protocol 1 0xff flowid 1:13 + +$TC filter add dev $IFACE parent 1:0 protocol ipv6 prio 9 \ + u32 match ip protocol 1 0xff flowid 1:13 + +#diffserv $IFACE + +} + +ingress() { + +CEIL=$DOWNLINK +PRIO_RATE=`expr $CEIL / 3` # Ceiling for prioirty +BE_RATE=`expr $CEIL / 6` # Min for best effort +BK_RATE=`expr $CEIL / 6` # Min for background +BE_CEIL=`expr $CEIL - 16` # A little slop at the top + +LQ="quantum `get_mtu $IFACE $CEIL`" + +$TC qdisc del dev $IFACE handle ffff: ingress 2> /dev/null +$TC qdisc add dev $IFACE handle ffff: ingress + +$TC qdisc del dev $DEV root 2> /dev/null + +if [ "$SQUASH_INGRESS" = "1" ] +then +sqm_logger "Do not perform DSCP based filtering on ingress. (1-tier classification)" +# Revert to no dscp based filtering +$TC qdisc del dev $DEV root 2>/dev/null +$TC qdisc add dev $DEV root handle 1: `get_stab_string` htb default 10 +$TC class add dev $DEV parent 1: classid 1:1 htb $LQ rate ${DOWNLINK}kbit ceil ${DOWNLINK}kbit `get_htb_adsll_string` +$TC class add dev $DEV parent 1:1 classid 1:10 htb $LQ rate ${DOWNLINK}kbit ceil ${DOWNLINK}kbit prio 0 `get_htb_adsll_string` +$TC qdisc add dev $DEV parent 1:10 handle 110: $QDISC `get_limit ${ILIMIT}` `get_target "${ITARGET}" ${DOWNLINK}` `get_ecn ${IECN}` `get_flows ${DOWNLINK}` ${IQDISC_OPTS} + +else +sqm_logger "Perform DSCP based filtering on ingress. (3-tier classification)" +$TC qdisc add dev $DEV root handle 1: `get_stab_string` htb default 12 +$TC class add dev $DEV parent 1: classid 1:1 htb $LQ rate ${CEIL}kbit ceil ${CEIL}kbit `get_htb_adsll_string` +$TC class add dev $DEV parent 1:1 classid 1:10 htb $LQ rate ${CEIL}kbit ceil ${CEIL}kbit prio 0 `get_htb_adsll_string` +$TC class add dev $DEV parent 1:1 classid 1:11 htb $LQ rate 32kbit ceil ${PRIO_RATE}kbit prio 1 `get_htb_adsll_string` +$TC class add dev $DEV parent 1:1 classid 1:12 htb $LQ rate ${BE_RATE}kbit ceil ${BE_CEIL}kbit prio 2 `get_htb_adsll_string` +$TC class add dev $DEV parent 1:1 classid 1:13 htb $LQ rate ${BK_RATE}kbit ceil ${BE_CEIL}kbit prio 3 `get_htb_adsll_string` + +# I'd prefer to use a pre-nat filter but that causes permutation... + +$TC qdisc add dev $DEV parent 1:11 handle 110: $QDISC `get_limit ${ILIMIT}` `get_target "${ITARGET}" ${DOWNLINK}` `get_ecn ${IECN}` `get_quantum 500` `get_flows ${PRIO_RATE}` ${IQDISC_OPTS} +$TC qdisc add dev $DEV parent 1:12 handle 120: $QDISC `get_limit ${ILIMIT}` `get_target "${ITARGET}" ${DOWNLINK}` `get_ecn ${IECN}` `get_quantum 1500` `get_flows ${BE_RATE}` ${IQDISC_OPTS} +$TC qdisc add dev $DEV parent 1:13 handle 130: $QDISC `get_limit ${ILIMIT}` `get_target "${ITARGET}" ${DOWNLINK}` `get_ecn ${IECN}` `get_quantum 300` `get_flows ${BK_RATE}` ${IQDISC_OPTS} + +diffserv $DEV + +fi + +ifconfig $DEV up + +# redirect all IP packets arriving in $IFACE to ifb0 + +$TC filter add dev $IFACE parent ffff: protocol all prio 10 u32 \ + match u32 0 0 flowid 1:1 action mirred egress redirect dev $DEV + +} + +do_modules +ipt_setup + +if [ "$UPLINK" -ne 0 ]; +then + egress + sqm_logger "egress shaping activated" +else + sqm_logger "egress shaping deactivated" + tc qdisc del dev $IFACE root 2> /dev/null +fi +if [ "$DOWNLINK" -ne 0 ]; +then + ingress + sqm_logger "ingress shaping activated" +else + sqm_logger "ingress shaping deactivated" + tc qdisc del dev $DEV root 2> /dev/null + tc qdisc del dev $IFACE ingress 2> /dev/null +fi + + + +# References: +# This alternate shaper attempts to go for 1/u performance in a clever way +# http://git.coverfire.com/?p=linux-qos-scripts.git;a=blob;f=src-3tos.sh;hb=HEAD + +# Comments +# This does the right thing with ipv6 traffic. +# It also tries to leverage diffserv to some sane extent. In particular, +# the 'priority' queue is limited to 33% of the total, so EF, and IMM traffic +# cannot starve other types. The rfc suggested 30%. 30% is probably +# a lot in today's world. + +# Flaws +# Many! diff --git a/net/sqm-scripts/files/usr/lib/sqm/simple.qos.help b/net/sqm-scripts/files/usr/lib/sqm/simple.qos.help new file mode 100644 index 000000000..b05960109 --- /dev/null +++ b/net/sqm-scripts/files/usr/lib/sqm/simple.qos.help @@ -0,0 +1 @@ +BW-limited three-tier prioritisation scheme with fq_codel on each queue. (default) diff --git a/net/sqm-scripts/files/usr/lib/sqm/simple_pppoe.qos b/net/sqm-scripts/files/usr/lib/sqm/simple_pppoe.qos new file mode 100755 index 000000000..4a47d4676 --- /dev/null +++ b/net/sqm-scripts/files/usr/lib/sqm/simple_pppoe.qos @@ -0,0 +1,387 @@ +#!/bin/sh +# Cero3 Shaper +# A 3 bin tc_codel and ipv6 enabled shaping script for +# ethernet gateways + +# Copyright (C) 2012 Michael D Taht +# GPLv2 + +# Compared to the complexity that debloat had become +# this cleanly shows a means of going from diffserv marking +# to prioritization using the current tools (ip(6)tables +# and tc. I note that the complexity of debloat exists for +# a reason, and it is expected that script is run first +# to setup various other parameters such as BQL and ethtool. +# (And that the debloat script has setup the other interfaces) + +# You need to jiggle these parameters. Note limits are tuned towards a <10Mbit uplink <60Mbup down + +. /usr/lib/sqm/functions.sh + +ipt_setup() { + +ipt -t mangle -N QOS_MARK_${IFACE} + +ipt -t mangle -A QOS_MARK_${IFACE} -j MARK --set-mark 0x2 +# You can go further with classification but... +ipt -t mangle -A QOS_MARK_${IFACE} -m dscp --dscp-class CS1 -j MARK --set-mark 0x3 +ipt -t mangle -A QOS_MARK_${IFACE} -m dscp --dscp-class CS6 -j MARK --set-mark 0x1 +ipt -t mangle -A QOS_MARK_${IFACE} -m dscp --dscp-class EF -j MARK --set-mark 0x1 +ipt -t mangle -A QOS_MARK_${IFACE} -m dscp --dscp-class AF42 -j MARK --set-mark 0x1 +ipt -t mangle -A QOS_MARK_${IFACE} -m tos --tos Minimize-Delay -j MARK --set-mark 0x1 + +# and it might be a good idea to do it for udp tunnels too + +# Turn it on. Preserve classification if already performed + +if [ "$SQUASH_DSCP" = "1" ] +then +sqm_logger "Squashing differentiad services code points (DSCP) from ingress." +ipt -t mangle -I PREROUTING -i $IFACE -m dscp ! --dscp 0 -j DSCP --set-dscp-class be +else +sqm_logger "Keeping differentiad services code points (DSCP) from ingress." +ipt -t mangle -A PREROUTING -i $IFACE -m mark --mark 0x00 -g QOS_MARK_${IFACE} +fi + +ipt -t mangle -A POSTROUTING -o $IFACE -m mark --mark 0x00 -g QOS_MARK_${IFACE} + +# The Syn optimization was nice but fq_codel does it for us +# ipt -t mangle -A PREROUTING -i s+ -p tcp -m tcp --tcp-flags SYN,RST,ACK SYN -j MARK --set-mark 0x01 +# Not sure if this will work. Encapsulation is a problem period + +ipt -t mangle -I PREROUTING -i vtun+ -p tcp -j MARK --set-mark 0x2 # tcp tunnels need ordering + +# Emanating from router, do a little more optimization +# but don't bother with it too much. + +ipt -t mangle -A OUTPUT -p udp -m multiport --ports 123,53 -j DSCP --set-dscp-class AF42 + +#Not clear if the second line is needed +#ipt -t mangle -A OUTPUT -o $IFACE -g QOS_MARK_${IFACE} + +} + + +# TC rules + +egress() { + +CEIL=${UPLINK} +PRIO_RATE=`expr $CEIL / 3` # Ceiling for prioirty +BE_RATE=`expr $CEIL / 6` # Min for best effort +BK_RATE=`expr $CEIL / 6` # Min for background +BE_CEIL=`expr $CEIL - 16` # A little slop at the top + +LQ="quantum `get_mtu $IFACE $CEIL`" + +$TC qdisc del dev $IFACE root 2> /dev/null +$TC qdisc add dev $IFACE root handle 1: `get_stab_string` htb default 12 +$TC class add dev $IFACE parent 1: classid 1:1 htb $LQ rate ${CEIL}kbit ceil ${CEIL}kbit `get_htb_adsll_string` +$TC class add dev $IFACE parent 1:1 classid 1:10 htb $LQ rate ${CEIL}kbit ceil ${CEIL}kbit prio 0 `get_htb_adsll_string` +$TC class add dev $IFACE parent 1:1 classid 1:11 htb $LQ rate 128kbit ceil ${PRIO_RATE}kbit prio 1 `get_htb_adsll_string` +$TC class add dev $IFACE parent 1:1 classid 1:12 htb $LQ rate ${BE_RATE}kbit ceil ${BE_CEIL}kbit prio 2 `get_htb_adsll_string` +$TC class add dev $IFACE parent 1:1 classid 1:13 htb $LQ rate ${BK_RATE}kbit ceil ${BE_CEIL}kbit prio 3 `get_htb_adsll_string` + +$TC qdisc add dev $IFACE parent 1:11 handle 110: $QDISC `get_limit ${ELIMIT}` `get_target "${ETARGET}" ${UPLINK}` `get_ecn ${EECN}` `get_quantum 300` `get_flows ${PRIO_RATE}` ${EQDISC_OPTS} +$TC qdisc add dev $IFACE parent 1:12 handle 120: $QDISC `get_limit ${ELIMIT}` `get_target "${ETARGET}" ${UPLINK}` `get_ecn ${EECN}` `get_quantum 300` `get_flows ${BE_RATE}` ${EQDISC_OPTS} +$TC qdisc add dev $IFACE parent 1:13 handle 130: $QDISC `get_limit ${ELIMIT}` `get_target "${ETARGET}" ${UPLINK}` `get_ecn ${EECN}` `get_quantum 300` `get_flows ${BK_RATE}` ${EQDISC_OPTS} + + +#sm: for testing we need a band to collect PPPOEd packets +$TC class add dev $IFACE parent 1:1 classid 1:14 htb $LQ rate ${BE_RATE}kbit ceil ${BE_CEIL}kbit prio 2 `get_htb_adsll_string` +$TC qdisc add dev $IFACE parent 1:14 handle 140: $QDISC `get_limit ${ELIMIT}` `get_target "${ETARGET}" ${UPLINK}` `get_ecn ${EECN}` `get_quantum 300` `get_flows ${BK_RATE}` ${EQDISC_OPTS} + + +# Need a catchall rule (should also match VLANs and PPPoE packets) + +$TC filter add dev $IFACE parent 1:0 protocol all prio 999 u32 \ + match ip protocol 0 0x00 flowid 1:12 + +# FIXME should probably change the filter here to do pre-nat + +$TC filter add dev $IFACE parent 1:0 protocol ip prio 1 handle 1 fw classid 1:11 +$TC filter add dev $IFACE parent 1:0 protocol ip prio 2 handle 2 fw classid 1:12 +$TC filter add dev $IFACE parent 1:0 protocol ip prio 3 handle 3 fw classid 1:13 + +# ipv6 support. Note that the handle indicates the fw mark bucket that is looked for + +$TC filter add dev $IFACE parent 1:0 protocol ipv6 prio 4 handle 1 fw classid 1:11 +$TC filter add dev $IFACE parent 1:0 protocol ipv6 prio 5 handle 2 fw classid 1:12 +$TC filter add dev $IFACE parent 1:0 protocol ipv6 prio 6 handle 3 fw classid 1:13 + +# Arp traffic + +$TC filter add dev $IFACE parent 1:0 protocol arp prio 7 handle 1 fw classid 1:11 + +# ICMP traffic - Don't impress your friends. Deoptimize to manage ping floods +# better instead + +$TC filter add dev $IFACE parent 1:0 protocol ip prio 8 \ + u32 match ip protocol 1 0xff flowid 1:13 + +$TC filter add dev $IFACE parent 1:0 protocol ipv6 prio 9 \ + u32 match ip protocol 1 0xff flowid 1:13 + + + + +# PPPoE encapsulated packets traversing the router (e.g.: the router does PPPoE termination but we shape +# on the underlaying ethernet interface instead of the pppoe device) + +PPPOE_SESSION_ETHERTYPE="0x8864" +PPPOE_DISCOVERY_ETHERTYPE="0x8863" +PPP_PROTO_IP4="0x0021" +PPP_PROTO_IP6="0x0057" +ARP_PROTO_IP4="0x0806" + +# NOTE it seems prio can not be reused? +#$TC filter add dev $IFACE protocol 0x8863 parent 1:0 prio 1 u32 flowid 1:14 +# PPPoE can be selected for by ether_type, the encapsulated IP version from the PPP (0x0021 IPv4, 0x0057 IPv6) +#U32_PREFIX="$TC filter add dev $IFACE" parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE}" + +#BE: 1:12 is the default anyway, but this will catch all non marked packets +#$TC filter add dev $IFACE parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE} prio 400 u32 \ +# match u16 ${PPP_PROTO_IP4} 0xffff at 6 \ +# match u8 0x00 0xfb at 9 \ +# flowid 1:12 + +#AF42 +$TC filter add dev $IFACE parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE} prio 401 u32 \ + match u16 ${PPP_PROTO_IP4} 0xffff at 6 \ + match u8 0x90 0xfc at 9 \ + flowid 1:11 +#EF +$TC filter add dev $IFACE parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE} prio 402 u32 \ + match u16 ${PPP_PROTO_IP4} 0xffff at 6 \ + match u8 0xb8 0xfc at 9 \ + flowid 1:11 +#CS1 +$TC filter add dev $IFACE parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE} prio 403 u32 \ + match u16 ${PPP_PROTO_IP4} 0xffff at 6 \ + match u8 0x20 0xf0 at 9 \ + flowid 1:13 +#IMM +$TC filter add dev $IFACE parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE} prio 404 u32 \ + match u16 ${PPP_PROTO_IP4} 0xffff at 6 \ + match u8 0x10 0xf0 at 9 \ + flowid 1:11 +#CS3 +$TC filter add dev $IFACE parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE} prio 405 u32 \ + match u16 ${PPP_PROTO_IP4} 0xffff at 6 \ + match u8 0xc0 0xf0 at 9 \ + flowid 1:11 +#CS6 +$TC filter add dev $IFACE parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE} prio 406 u32 \ + match u16 ${PPP_PROTO_IP4} 0xffff at 6 \ + match u8 0xe0 0xf0 at 9 \ + flowid 1:11 + + +## Arp traffic +#$TC filter add dev $IFACE parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE} prio 409 u32 \ +# match u16 ${ARP_PROTO_IP4} 0xffff at 6 \ +# flowid 1:14 + +# ICMP traffic - Don't impress your friends. Deoptimize to manage ping floods +# better instead; sm: really only deprio echo requestst and echo replies instead? +# ECHO request, the rest stays in best effort +$TC filter add dev $IFACE parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE} prio 410 u32 \ + match u16 ${PPP_PROTO_IP4} 0xffff at 6 \ + match u8 0x01 0xff at 17 \ + match u8 0x08 0xff at 28 \ + flowid 1:13 +# ECHO reply +$TC filter add dev $IFACE parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE} prio 410 u32 \ + match u16 ${PPP_PROTO_IP4} 0xffff at 6 \ + match u8 0x01 0xff at 17 \ + match u8 0x00 0xff at 28 \ + flowid 1:13 + +## ICMPv6 133-137 (NDP) is equivalent to IPv4 ARP, so only push echo request and reply into the bulk class +## 133 +#$TC filter add dev $IFACE parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE} prio 610 u32 \ +# match u16 ${PPP_PROTO_IP6} 0xffff at 6 \ +# match u8 0x85 0xff at 48 \ +# match u8 0x3a 0xff at 14 \ +# flowid 1:14 +## 134 +#$TC filter add dev $IFACE parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE} prio 610 u32 \ +# match u16 ${PPP_PROTO_IP6} 0xffff at 6 \ +# match u8 0x86 0xff at 48 \ +# match u8 0x3a 0xff at 14 \ +# flowid 1:14 +## 135 +#$TC filter add dev $IFACE parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE} prio 610 u32 \ +# match u16 ${PPP_PROTO_IP6} 0xffff at 6 \ +# match u8 0x87 0xff at 48 \ +# match u8 0x3a 0xff at 14 \ +# flowid 1:14 +## 136 +#$TC filter add dev $IFACE parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE} prio 610 u32 \ +# match u16 ${PPP_PROTO_IP6} 0xffff at 6 \ +# match u8 0x88 0xff at 48 \ +# match u8 0x3a 0xff at 14 \ +# flowid 1:14 +## 137 +#$TC filter add dev $IFACE parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE} prio 610 u32 \ +# match u16 ${PPP_PROTO_IP6} 0xffff at 6 \ +# match u8 0x89 0xff at 48 \ +# match u8 0x3a 0xff at 14 \ +# flowid 1:14 + +# ICMPv6 echo request +$TC filter add dev $IFACE parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE} prio 610 u32 \ + match u16 ${PPP_PROTO_IP6} 0xffff at 6 \ + match u8 0x3a 0xff at 14 \ + match u8 0x80 0xff at 48 \ + flowid 1:13 +# ICMPv6 echo reply +$TC filter add dev $IFACE parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE} prio 610 u32 \ + match u16 ${PPP_PROTO_IP6} 0xffff at 6 \ + match u8 0x3a 0xff at 14 \ + match u8 0x81 0xff at 48 \ + flowid 1:13 + + + + +#IPV6 +#BE: careful, will override ICMP +#$TC filter add dev $IFACE parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE} prio 600 u32 \ +# match u16 ${PPP_PROTO_IP6} 0xffff at 6 \ +# match u16 0x0000 0x0fb0 at 8 \ +# flowid 1:12 +#AF42 +$TC filter add dev $IFACE parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE} prio 601 u32 \ + match u16 ${PPP_PROTO_IP6} 0xffff at 6 \ + match u16 0x0900 0x0fc0 at 8 \ + flowid 1:11 +#EF +$TC filter add dev $IFACE parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE} prio 602 u32 \ + match u16 ${PPP_PROTO_IP6} 0xffff at 6 \ + match u16 0x0b80 0x0fc0 at 8 \ + flowid 1:11 +#CS1 +$TC filter add dev $IFACE parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE} prio 603 u32 \ + match u16 ${PPP_PROTO_IP6} 0xffff at 6 \ + match u16 0x0200 0x0fc0 at 8 \ + flowid 1:13 +#IMM +$TC filter add dev $IFACE parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE} prio 604 u32 \ + match u16 ${PPP_PROTO_IP6} 0xffff at 6 \ + match u16 0x0100 0x0fc0 at 8 \ + flowid 1:11 +#CS3 +$TC filter add dev $IFACE parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE} prio 605 u32 \ + match u16 ${PPP_PROTO_IP6} 0xffff at 6 \ + match u16 0x0c00 0x0fc0 at 8 \ + flowid 1:11 +#CS6 +$TC filter add dev $IFACE parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE} prio 606 u32 \ + match u16 ${PPP_PROTO_IP6} 0xffff at 6 \ + match u16 0x0e00 0x0fc0 at 8 \ + flowid 1:11 + + + + +#diffserv $IFACE + +} + +ingress() { + +CEIL=$DOWNLINK +PRIO_RATE=`expr $CEIL / 3` # Ceiling for prioirty +BE_RATE=`expr $CEIL / 6` # Min for best effort +BK_RATE=`expr $CEIL / 6` # Min for background +BE_CEIL=`expr $CEIL - 16` # A little slop at the top + +LQ="quantum `get_mtu $IFACE $CEIL`" + +$TC qdisc del dev $IFACE handle ffff: ingress 2> /dev/null +$TC qdisc add dev $IFACE handle ffff: ingress + +$TC qdisc del dev $DEV root 2> /dev/null + +if [ "$SQUASH_INGRESS" = "1" ] +then +sqm_logger "Do not perform DSCP based filtering on ingress. (1-tier classification)" +# Revert to no dscp based filtering +$TC qdisc del dev $DEV root 2>/dev/null +$TC qdisc add dev $DEV root handle 1: `get_stab_string` htb default 10 +$TC class add dev $DEV parent 1: classid 1:1 htb $LQ rate ${DOWNLINK}kbit ceil ${DOWNLINK}kbit `get_htb_adsll_string` +$TC class add dev $DEV parent 1:1 classid 1:10 htb $LQ rate ${DOWNLINK}kbit ceil ${DOWNLINK}kbit prio 0 `get_htb_adsll_string` +$TC qdisc add dev $DEV parent 1:10 handle 110: $QDISC `get_limit ${ILIMIT}` `get_target "${ITARGET}" ${DOWNLINK}` `get_ecn ${IECN}` `get_flows ${DOWNLINK}` ${IQDISC_OPTS} + +else +sqm_logger "Perform DSCP based filtering on ingress. (3-tier classification)" +$TC qdisc add dev $DEV root handle 1: `get_stab_string` htb default 12 +$TC class add dev $DEV parent 1: classid 1:1 htb $LQ rate ${CEIL}kbit ceil ${CEIL}kbit `get_htb_adsll_string` +$TC class add dev $DEV parent 1:1 classid 1:10 htb $LQ rate ${CEIL}kbit ceil ${CEIL}kbit prio 0 `get_htb_adsll_string` +$TC class add dev $DEV parent 1:1 classid 1:11 htb $LQ rate 32kbit ceil ${PRIO_RATE}kbit prio 1 `get_htb_adsll_string` +$TC class add dev $DEV parent 1:1 classid 1:12 htb $LQ rate ${BE_RATE}kbit ceil ${BE_CEIL}kbit prio 2 `get_htb_adsll_string` +$TC class add dev $DEV parent 1:1 classid 1:13 htb $LQ rate ${BK_RATE}kbit ceil ${BE_CEIL}kbit prio 3 `get_htb_adsll_string` + +# I'd prefer to use a pre-nat filter but that causes permutation... + +$TC qdisc add dev $DEV parent 1:11 handle 110: $QDISC `get_limit ${ILIMIT}` `get_target "${ITARGET}" ${DOWNLINK}` `get_ecn ${IECN}` `get_quantum 500` `get_flows ${PRIO_RATE}` ${IQDISC_OPTS} +$TC qdisc add dev $DEV parent 1:12 handle 120: $QDISC `get_limit ${ILIMIT}` `get_target "${ITARGET}" ${DOWNLINK}` `get_ecn ${IECN}` `get_quantum 1500` `get_flows ${BE_RATE}` ${IQDISC_OPTS} +$TC qdisc add dev $DEV parent 1:13 handle 130: $QDISC `get_limit ${ILIMIT}` `get_target "${ITARGET}" ${DOWNLINK}` `get_ecn ${IECN}` `get_quantum 300` `get_flows ${BK_RATE}` ${IQDISC_OPTS} + +#sm: for PPPoE packet testing +$TC class add dev $DEV parent 1:1 classid 1:14 htb $LQ rate ${BK_RATE}kbit ceil ${BE_CEIL}kbit prio 3 `get_htb_adsll_string` +$TC qdisc add dev $DEV parent 1:14 handle 140: $QDISC `get_limit ${ILIMIT}` `get_target "${ITARGET}" ${DOWNLINK}` `get_ecn ${IECN}` `get_quantum 300` `get_flows ${BK_RATE}` ${IQDISC_OPTS} + + + +#diffserv $DEV +diffserv_pppoe $DEV + +fi + +ifconfig $DEV up + +# redirect all IP packets arriving in $IFACE to ifb0 + +$TC filter add dev $IFACE parent ffff: protocol all prio 10 u32 \ + match u32 0 0 flowid 1:1 action mirred egress redirect dev $DEV + +} + +do_modules +ipt_setup + +if [ "$UPLINK" -ne 0 ]; +then + egress + sqm_logger "egress shaping activated" +else + sqm_logger "egress shaping deactivated" + tc qdisc del dev $IFACE root 2> /dev/null +fi +if [ "$DOWNLINK" -ne 0 ]; +then + ingress + sqm_logger "ingress shaping activated" +else + sqm_logger "ingress shaping deactivated" + tc qdisc del dev $DEV root 2> /dev/null + tc qdisc del dev $IFACE ingress 2> /dev/null +fi + + + +# References: +# This alternate shaper attempts to go for 1/u performance in a clever way +# http://git.coverfire.com/?p=linux-qos-scripts.git;a=blob;f=src-3tos.sh;hb=HEAD + +# Comments +# This does the right thing with ipv6 traffic. +# It also tries to leverage diffserv to some sane extent. In particular, +# the 'priority' queue is limited to 33% of the total, so EF, and IMM traffic +# cannot starve other types. The rfc suggested 30%. 30% is probably +# a lot in today's world. + +# Flaws +# Many! diff --git a/net/sqm-scripts/files/usr/lib/sqm/simple_pppoe.qos.help b/net/sqm-scripts/files/usr/lib/sqm/simple_pppoe.qos.help new file mode 100644 index 000000000..cc9af9d61 --- /dev/null +++ b/net/sqm-scripts/files/usr/lib/sqm/simple_pppoe.qos.help @@ -0,0 +1,2 @@ +BW-limited three-tier prioritisation scheme with fq_codel on each queue. Temporary version to implement shaping +of pass through PPPOE encapsulated packets. diff --git a/net/sqm-scripts/files/usr/lib/sqm/simplest.qos b/net/sqm-scripts/files/usr/lib/sqm/simplest.qos new file mode 100755 index 000000000..e82bcff5c --- /dev/null +++ b/net/sqm-scripts/files/usr/lib/sqm/simplest.qos @@ -0,0 +1,81 @@ +#!/bin/sh +# Cero3 Simple Shaper +# A 1 bin tc_codel and ipv6 enabled shaping script for +# ethernet gateways. This is nearly the simplest possible + +# Copyright (C) 2013 Michael D Taht +# GPLv2 + +. /usr/lib/sqm/functions.sh +sqm_logger "Starting simplest.qos" + +egress() { + +LQ="quantum `get_mtu $IFACE ${UPLINK}`" + +$TC qdisc del dev $IFACE root 2>/dev/null +$TC qdisc add dev $IFACE root handle 1: `get_stab_string` htb default 10 +$TC class add dev $IFACE parent 1: classid 1:1 htb $LQ rate ${UPLINK}kbit ceil ${UPLINK}kbit `get_htb_adsll_string` +$TC class add dev $IFACE parent 1:1 classid 1:10 htb $LQ rate ${UPLINK}kbit ceil ${UPLINK}kbit prio 0 `get_htb_adsll_string` +$TC qdisc add dev $IFACE parent 1:10 handle 110: $QDISC `get_limit ${ELIMIT}` `get_target "${ETARGET}" ${UPLINK}` `get_ecn ${EECN}` `get_flows ${UPLINK}` ${EQDISC_OPTS} + +} + + +ingress() { +sqm_logger "ingress" +$TC qdisc del dev $IFACE handle ffff: ingress 2>/dev/null +$TC qdisc add dev $IFACE handle ffff: ingress + +LQ="quantum `get_mtu $IFACE ${DOWNLINK}`" + +$TC qdisc del dev $DEV root 2>/dev/null +$TC qdisc add dev $DEV root handle 1: `get_stab_string` htb default 10 +$TC class add dev $DEV parent 1: classid 1:1 htb $LQ rate ${DOWNLINK}kbit ceil ${DOWNLINK}kbit `get_htb_adsll_string` +$TC class add dev $DEV parent 1:1 classid 1:10 htb $LQ rate ${DOWNLINK}kbit ceil ${DOWNLINK}kbit prio 0 `get_htb_adsll_string` + +# FIXME: I'd prefer to use a pre-nat filter but we need to detect if nat is on this interface +# AND we need to permute by a random number which we can't do from userspace filters + +# Most high rate flows are REALLY close. This stomps on those harder, but hurts on high rate long distance +#$TC qdisc add dev $DEV parent 1:10 handle 110: $QDISC limit $LIMIT $ECN interval 20ms target 3ms `get_flows ${DOWNLINK}` +$TC qdisc add dev $DEV parent 1:10 handle 110: $QDISC `get_limit ${ILIMIT}` `get_target "${ITARGET}" ${DOWNLINK}` `get_ecn ${IECN}` `get_flows ${DOWNLINK}` ${IQDISC_OPTS} + +ifconfig $DEV up + +# redirect all IP packets arriving in $IFACE to ifb0 + +$TC filter add dev $IFACE parent ffff: protocol all prio 10 u32 \ + match u32 0 0 flowid 1:1 action mirred egress redirect dev $DEV + +} + +do_modules + +if [ "$UPLINK" -ne 0 ]; +then + egress + sqm_logger "egress shaping activated" +else + sqm_logger "egress shaping deactivated" + tc qdisc del dev $IFACE root 2> /dev/null +fi +if [ "$DOWNLINK" -ne 0 ]; +then + ingress + sqm_logger "ingress shaping activated" +else + sqm_logger "ingress shaping deactivated" + tc qdisc del dev $DEV root 2> /dev/null + tc qdisc del dev $IFACE ingress 2> /dev/null +fi + + +# References: +# This alternate shaper attempts to go for 1/u performance in a clever way +# http://git.coverfire.com/?p=linux-qos-scripts.git;a=blob;f=src-3tos.sh;hb=HEAD + +# Comments +# This does the right thing with ipv6 traffic. +# Flaws +# Many! diff --git a/net/sqm-scripts/files/usr/lib/sqm/simplest.qos.help b/net/sqm-scripts/files/usr/lib/sqm/simplest.qos.help new file mode 100644 index 000000000..c3592562e --- /dev/null +++ b/net/sqm-scripts/files/usr/lib/sqm/simplest.qos.help @@ -0,0 +1 @@ +Simplest possible configuration: HTB rate limiter with your qdisc attached. diff --git a/net/sqm-scripts/files/usr/lib/sqm/stop.sh b/net/sqm-scripts/files/usr/lib/sqm/stop.sh new file mode 100755 index 000000000..f7323e8b9 --- /dev/null +++ b/net/sqm-scripts/files/usr/lib/sqm/stop.sh @@ -0,0 +1,35 @@ +#!/bin/sh + +. /usr/lib/sqm/functions.sh + +# allow passing in the IFACE as first command line argument +[ ! -z ${1} ] && IFACE=${1} +sqm_logger "${0} Stopping ${IFACE}" + +# make sure to only delete the ifb associated with the current interface +CUR_IFB=$( get_ifb_associated_with_if ${IFACE} ) + +sqm_stop() { + tc qdisc del dev $IFACE ingress 2> /dev/null + tc qdisc del dev $IFACE root 2> /dev/null + [ ! -z "$CUR_IFB" ] && tc qdisc del dev $CUR_IFB root 2> /dev/null + [ ! -z "$CUR_IFB" ] && sqm_logger "${CUR_IFB} shaper deleted" +} + +ipt_stop() { + [ ! -z "$CUR_IFB" ] && ipt -t mangle -D POSTROUTING -o $CUR_IFB -m mark --mark 0x00 -g QOS_MARK_${IFACE} + ipt -t mangle -D POSTROUTING -o $IFACE -m mark --mark 0x00 -g QOS_MARK_${IFACE} + ipt -t mangle -D PREROUTING -i vtun+ -p tcp -j MARK --set-mark 0x2 + ipt -t mangle -D OUTPUT -p udp -m multiport --ports 123,53 -j DSCP --set-dscp-class AF42 + ipt -t mangle -F QOS_MARK_${IFACE} + ipt -t mangle -X QOS_MARK_${IFACE} +} + + +sqm_stop +ipt_stop +[ ! -z "$CUR_IFB" ] && ifconfig ${CUR_IFB} down +[ ! -z "$CUR_IFB" ] && ip link delete ${CUR_IFB} type ifb +[ ! -z "$CUR_IFB" ] && sqm_logger "${CUR_IFB} interface deleted" + +exit 0 \ No newline at end of file