Add sqm-scripts & luci-app-sqm: experimental QoSlilik-openwrt-22.03
@ -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 <toke@toke.dk> | |||
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)) |
@ -0,0 +1,217 @@ | |||
--[[ | |||
LuCI - Lua Configuration Interface | |||
Copyright 2014 Steven Barth <steven@midlink.org> | |||
Copyright 2014 Dave Taht <dave.taht@bufferbloat.net> | |||
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 <abbr title=\"Smart Queue Management\">SQM</abbr> 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 .. "<p><b>" .. file:gsub(".help$", "") .. ":</b><br />" .. fh:read("*a") .. "</p>" | |||
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 |
@ -0,0 +1,26 @@ | |||
--[[ | |||
LuCI - Lua Configuration Interface | |||
Copyright 2008 Steven Barth <steven@midlink.org> | |||
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 |
@ -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 |
@ -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 <toke@toke.dk> | |||
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)) |
@ -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' | |||
@ -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 | |||
} |
@ -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)) | |||
} | |||
@ -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 |
@ -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! |
@ -0,0 +1 @@ | |||
BW-limited three-tier prioritisation scheme with fq_codel on each queue. (default) |
@ -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! |
@ -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. |
@ -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! |
@ -0,0 +1 @@ | |||
Simplest possible configuration: HTB rate limiter with your qdisc attached. |
@ -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 |