+ Licensed to the public under the Apache License 2.0.
+-%>
+
+<% css = [[
+
+ #troubleshoot_text {
+ padding: 20px;
+ text-align: left;
+ }
+ #troubleshoot_text pre {
+ word-break: break-all;
+ margin: 0;
+ }
+ .description {
+ background-color: #33CCFF;
+ }
+
+]]
+-%>
+
+<%+header%>
+
+
+
+
+
+
+ <%:Troubleshooting Data%>
+ <%:Collecting data...%>
+
+
+
+<%+footer%>
diff --git a/package/ctcgfw/luci-app-qos-gargoyle/po/zh-cn/qos-gargoyle.po b/package/ctcgfw/luci-app-qos-gargoyle/po/zh-cn/qos-gargoyle.po
new file mode 100644
index 0000000000..4b1d10624a
--- /dev/null
+++ b/package/ctcgfw/luci-app-qos-gargoyle/po/zh-cn/qos-gargoyle.po
@@ -0,0 +1,334 @@
+msgid ""
+msgstr "Content-Type: text/plain; charset=UTF-8\n"
+
+msgid "\"Active Congestion Control\" not enabled"
+msgstr "“主动拥塞控制”未启用"
+
+msgid ""
+"The active congestion control (ACC) observes your download activity and "
+"automatically adjusts your download link limit to maintain proper QoS "
+"performance. ACC automatically compensates for changes in your ISP's "
+"download speed and the demand from your network adjusting the link speed to "
+"the highest speed possible which will maintain proper QoS function. The "
+"effective range of this control is between 15% and 100% of the total "
+"download bandwidth you entered above.
"
+msgstr ""
+"主动拥塞控制系统(ACC)观察你的下载活动并自动调整你的下载链接限制以保持适"
+"当的 QoS 性能。ACC 自动调整 QoS 功能以补偿来自你 ISP 的下载速度变化及来自你网"
+"络链接速度的调整需求,使速度最大化。这个控制的有效范围在你上面输入的下载总带"
+"宽的 15% 至 100% 之间。
"
+
+msgid ""
+"While ACC does not adjust your upload link speed you must enable and "
+"properly configure your upload QoS for it to function properly.
"
+msgstr ""
+"虽然 ACC 不调整你的上传链路速度,但你必须启用并正确配置你的上传 QoS 带宽以"
+"使该功能正常工作。
"
+
+msgid "All"
+msgstr "全部"
+
+msgid "Auto"
+msgstr "自动"
+
+msgid "Class Name"
+msgstr "类型名称"
+
+msgid "Classification Rules"
+msgstr "分类规则"
+
+msgid "Collecting data..."
+msgstr "正在收集数据..."
+
+msgid "Connection Bytes Reach"
+msgstr "连接流量达到"
+
+msgid "DPI Protocol"
+msgstr "DPI 协议"
+
+msgid "Default Service Class"
+msgstr "默认服务类型"
+
+msgid "Destination IP(s)"
+msgstr "目标 IP"
+
+msgid "Destination Port(s)"
+msgstr "目标端口"
+
+msgid "Disable QoS"
+msgstr "禁用 QoS"
+
+msgid "Download Settings"
+msgstr "下载设置"
+
+msgid ""
+"Each service class is specified by four parameters: percent bandwidth at "
+"capacity, realtime bandwidth and maximum bandwidth and the minimimze round "
+"trip time flag."
+msgstr ""
+"每个下载服务类型由四个参数指定:带宽占用百分比、最小保证带宽、最大带宽和最小"
+"往返延时标志。"
+
+msgid ""
+"Each upload service class is specified by three parameters: percent "
+"bandwidth at capacity, minimum bandwidth and maximum bandwidth."
+msgstr "每个上传服务类型由三个参数指定:带宽占用百分比、最小带宽和最大带宽。"
+
+msgid "Edit Download Classification Rule"
+msgstr "编辑下载分类规则"
+
+msgid "Edit Download Service Class"
+msgstr "编辑下载服务类型"
+
+msgid "Edit Upload Classification Rule"
+msgstr "编辑上传分类规则"
+
+msgid "Edit Upload Service Class"
+msgstr "编辑上传服务类型"
+
+msgid "Enable Active Congestion Control"
+msgstr "启用主动拥塞控制"
+
+msgid "Enable QoS"
+msgstr "启用 QoS"
+
+msgid "Error collecting troubleshooting information"
+msgstr "收集故障排查信息失败"
+
+msgid "Gargoyle QoS"
+msgstr "石像鬼 QoS"
+
+msgid "Global Settings"
+msgstr "全局设置"
+
+msgid ""
+"Indicates to the active congestion controller that you wish to minimize "
+"round trip times (RTT) when this class is active. Use this setting for "
+"online gaming or VoIP applications that need low round trip times (ping "
+"times). Minimizing RTT comes at the expense of efficient WAN throughput so "
+"while these class are active your WAN throughput will decline (usually "
+"around 20%)."
+msgstr ""
+"告诉主动拥塞控制器你希望该服务类型启用时尽量减少往返延时(RTT)。该设置一般用"
+"在 VoIP 或在线游戏这类需要低延时(Ping 值)的应用上。减小往返延时(RTT)会带"
+"来WAN有效吞吐量的额外花销,所以当这些服务类型启用时你的 WAN 吞吐量将下降(通"
+"常在20%左右)"
+
+msgid "Load"
+msgstr "负载"
+
+msgid "Loading"
+msgstr "正在加载"
+
+msgid "Manual Ping Limit"
+msgstr "手动 Ping 限制"
+
+msgid "Maximum Bandwidth"
+msgstr "最大带宽"
+
+msgid "Maximum Packet Length"
+msgstr "最大数据包长度"
+
+msgid "Minimize RTT"
+msgstr "最小往返延时"
+
+msgid "Minimum Bandwidth"
+msgstr "最小带宽"
+
+msgid "Minimum Packet Length"
+msgstr "最小数据包长度"
+
+msgid "No"
+msgstr "否"
+
+msgid "No data found"
+msgstr "无数据"
+
+msgid "None"
+msgstr "无"
+
+msgid "Not set"
+msgstr "未设置"
+
+msgid ""
+"Packet's destination ip, can optionally have /[mask] after it (see -d option "
+"in iptables man page)."
+msgstr ""
+"数据包的目标 IP,可以在后面加子网掩码(/[mask],请看 iptables 的 -d 参数说"
+"明)"
+
+msgid "Packet's destination port, can be a range (eg. 80-90)."
+msgstr "数据包的目标端口,可以是一个范围(例如:80-90)"
+
+msgid "Packet's maximum size (in bytes)."
+msgstr "数据包的最大大小(单位:bytes)"
+
+msgid "Packet's minimum size (in bytes)."
+msgstr "数据包的最小大小(单位:bytes)"
+
+msgid ""
+"Packet's source ip, can optionally have /[mask] after it (see -s option in "
+"iptables man page)."
+msgstr ""
+"数据包的源 IP,可以在后面加子网掩码(/[mask],请看 iptables 的 -s 参数说明)"
+
+msgid "Packet's source port, can be a range (eg. 80-90)."
+msgstr "数据包的源端口,可以是一个范围(例如:80-90)"
+
+msgid ""
+"Packets are tested against the rules in the order specified -- rules toward "
+"the top have priority. As soon as a packet matches a rule it is classified, "
+"and the rest of the rules are ignored. The order of the rules can be altered "
+"using the arrow controls."
+msgstr ""
+"数据包将按规则中指定的顺序进行匹配 —— 靠上的规则优先进行匹配。一旦数据包匹配"
+"一条规则那它将被归类,并且其余的规则将被忽略。使用上下箭头可调整规则的顺序。"
+
+msgid "Percent Bandwidth At Capacity"
+msgstr "带宽占用百分比"
+
+msgid "QoS Switch"
+msgstr "QoS 开关"
+
+msgid ""
+"Quality of Service (QoS) provides a way to control how available bandwidth "
+"is allocated."
+msgstr "QoS 可以用来分配和控制可用带宽。"
+
+msgid ""
+"Round trip ping times are compared against the ping limits. ACC controls the "
+"link limit to maintain ping times under the appropriate limit. By default "
+"ACC attempts to automatically select appropriate target ping limits for you "
+"based on the link speeds you entered and the performance of your link it "
+"measures during initialization. You cannot change the target ping time for "
+"the minRTT mode but by entering a manual time you can control the target "
+"ping time of the active mode. The time you enter becomes the increase in the "
+"target ping time between minRTT and active mode. Leave empty to use the "
+"default settings."
+msgstr ""
+"Ping 延时会与 Ping 限制进行比较。ACC 控制链路限制以保持 Ping 延时在适当范围。"
+"默认情况下,ACC 会自动根据你输入的链接适当为你选择适当的 Ping 限制。如果你想"
+"尝试不同的 Ping 限制,你可以在这里输入一个时间值。输入高的时间值将导致更高的 "
+"Ping 限制,低的时间值会有更低的限制。留空则将由 ACC 自动控制。"
+
+msgid "Service Class"
+msgstr "服务类型"
+
+msgid "Service Class Name"
+msgstr "服务类型名称"
+
+msgid "Service Classes"
+msgstr "服务类型"
+
+msgid ""
+"Should be set to around 98% of your available upload bandwidth. Entering a "
+"number which is too high will result in QoS not meeting its class "
+"requirements. Entering a number which is too low will needlessly penalize "
+"your upload speed. You should use a speed test program (with QoS off) to "
+"determine available upload bandwidth. Note that bandwidth is specified in "
+"kbps, leave blank to disable update QoS. There are 8 kilobits per kilobyte."
+msgstr ""
+"应被设置为你可用上传带宽的 98% 左右。输入数值太高将导致 QoS 不能匹配服务类型"
+"的要求。输入数值太低将造成不必要的上传速度限制。你应当在 QoS 关闭的情况下使用"
+"测速程序以确定可用的上传带宽。带宽以 kbps 为单位,留空以禁用上传 QoS。"
+"(1KByte/s=8Kbps)"
+
+msgid "Source IP(s)"
+msgstr "源 IP"
+
+msgid "Source Port(s)"
+msgstr "源端口"
+
+msgid "Specifie how packets that do not match any rule should be classified."
+msgstr "指定当数据包不匹配任何规则时将被如何归类。"
+
+msgid ""
+"Specifying correctly is crucial to making QoS work. Note that bandwidth is "
+"specified in kbps, leave blank to disable download QoS. There are 8 kilobits "
+"per kilobyte."
+msgstr ""
+"正确设置对于 QoS 的工作至关重要。带宽以 kbps 为单位,留空以禁用下载 QoS。"
+"(1KByte/s=8Kbps)"
+
+msgid ""
+"The maximum amount of bandwidth this class will be allocated in kbit/s. Even "
+"if unused bandwidth is available, this service class will never be permitted "
+"to use more than this amount of bandwidth."
+msgstr ""
+"该服务类型可被分配的带宽最大值(以 kbit/s 为单位)。即使存在未使用带宽,该服"
+"务类型也将永远不被允许使用超过此量的带宽。"
+
+msgid ""
+"The minimum service this class will be allocated when the link is at "
+"capacity. Classes which specify minimum service are known as realtime "
+"classes by the active congestion controller. Streaming video, VoIP and "
+"interactive online gaming are all examples of applications that must have a "
+"minimum bandwith to function. To determine what to enter use the application "
+"on an unloaded LAN and observe how much bandwidth it uses. Then enter a "
+"number only slightly higher than this into this field. QoS will satisfiy the "
+"minimum service of all classes first before allocating to other waiting "
+"classes so be careful to use minimum bandwidths sparingly."
+msgstr ""
+"将被分配用于该服务类型的最低链路带宽容量。指定了最小带宽的服务类型会被主动拥"
+"塞控制器看作实时类应用。例如视频流、VoIP 和在线互动游戏都应该设置最小带宽。要"
+"确定需要多少最小带宽,可以在一个没有负载的局域网中使用应用程序并观察它使用了"
+"多少带宽。然后输入一个略高于这个值的数字到字段中。在分配剩余带宽到其它服务类"
+"型前,QoS 将优先满足所有服务类型的最小带宽要求,所以要谨慎地使用最小带宽。"
+
+msgid ""
+"The percentage of the total available bandwidth that should be allocated to "
+"this class when all available bandwidth is being used. If unused bandwidth "
+"is available, more can (and will) be allocated. The percentages can be "
+"configured to equal more (or less) than 100, but when the settings are "
+"applied the percentages will be adjusted proportionally so that they add to "
+"100. This setting only comes into effect when the WAN link is saturated."
+msgstr ""
+"当所有可用带宽被占满后该服务类型占据总带宽的百分比。如果带宽未用完,该服务类"
+"型会被分配更多带宽。该百分比值可被设置为等于、大于或小于 100,但当设置被应用"
+"时,百分比值将会被按比例调整以便使它们加起来等于 100。该设置只在 WAN 端链路饱"
+"和时才生效。(PS:WAN 端链路饱和即带宽被占用完) "
+
+msgid ""
+"The segment of network between your router and the ping target is where "
+"congestion is controlled. By monitoring the round trip ping times to the "
+"target congestion is detected. By default ACC uses your WAN gateway as the "
+"ping target. If you know that congestion on your link will occur in a "
+"different segment then you can enter an alternate ping target. Leave empty "
+"to use the default settings."
+msgstr ""
+"在路由器和 Ping 目标之间的网络部分是拥塞控制的地方。拥塞通过监视和目标间的 "
+"Ping 延时来检测。默认情况下 ACC 使用你的 WAN 网关作为 Ping 的目标。假如你知道"
+"拥塞会在你链路的不同段发生,你可用输入一个备用的 Ping 目标。留空则将由ACC 自"
+"动控制。"
+
+msgid ""
+"The total size of data transmitted since the establishment of the link (in "
+"kBytes)."
+msgstr "自连接建立以来,传输的数据总量(单位:kBytes)"
+
+msgid "Total Download Bandwidth"
+msgstr "下载总带宽"
+
+msgid "Total Upload Bandwidth"
+msgstr "上传总带宽"
+
+msgid "Transport Protocol"
+msgstr "协议"
+
+msgid "Troubleshooting"
+msgstr "故障排除"
+
+msgid "Troubleshooting Data"
+msgstr "故障排除数据"
+
+msgid "Unlimited"
+msgstr "不限制"
+
+msgid "Upload Settings"
+msgstr "上传设置"
+
+msgid "Use Non-standard Ping Target"
+msgstr "使用自定义 Ping 目标"
+
+msgid "Zero"
+msgstr "零"
diff --git a/package/ctcgfw/luci-app-qos-gargoyle/root/etc/uci-defaults/40_luci-qos_gargoyle b/package/ctcgfw/luci-app-qos-gargoyle/root/etc/uci-defaults/40_luci-qos_gargoyle
new file mode 100755
index 0000000000..675fd39fc3
--- /dev/null
+++ b/package/ctcgfw/luci-app-qos-gargoyle/root/etc/uci-defaults/40_luci-qos_gargoyle
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+uci -q batch <<-EOF >/dev/null
+ delete ucitrack.@qos_gargoyle[-1]
+ add ucitrack qos_gargoyle
+ set ucitrack.@qos_gargoyle[-1].init=qos_gargoyle
+ commit ucitrack
+EOF
+
+rm -rf /tmp/luci-modulecache /tmp/luci-indexcache
+exit 0
diff --git a/package/ctcgfw/qos-gargoyle/Makefile b/package/ctcgfw/qos-gargoyle/Makefile
new file mode 100644
index 0000000000..2bedade23f
--- /dev/null
+++ b/package/ctcgfw/qos-gargoyle/Makefile
@@ -0,0 +1,68 @@
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=qos-gargoyle
+PKG_VERSION:=$(GARGOYLE_VERSION)
+ifeq ($(GARGOYLE_VERSION),)
+ PKG_VERSION:=1.0.0
+endif
+PKG_RELEASE:=1
+
+PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
+
+include $(INCLUDE_DIR)/package.mk
+include $(INCLUDE_DIR)/kernel.mk
+
+define Package/qos-gargoyle
+ SECTION:=net
+ CATEGORY:=Network
+ TITLE:=A set of QoS scripts designed for use with Gargoyle Web Interface
+ DEPENDS:=+tc +ip +kmod-sched +iptables-mod-filter +iptables-mod-ipopt +iptables-mod-imq +gargoyle-firewall-util
+ MAINTAINER:=Eric Bishop
+endef
+
+define Package/qos-gargoyle/description
+ A set of QoS scripts designed for use with Gargoyle Web Interface
+endef
+
+define Build/Prepare
+ mkdir -p $(PKG_BUILD_DIR)
+ $(CP) ./src/* $(PKG_BUILD_DIR)/
+endef
+
+
+define Build/Configure
+endef
+
+define Build/Compile
+ -$(MAKE) -C $(PKG_BUILD_DIR) clean
+ $(MAKE) -C $(PKG_BUILD_DIR) \
+ $(TARGET_CONFIGURE_OPTS) \
+ CFLAGS="$(TARGET_CFLAGS) -I $(STAGING_DIR)/usr/include" \
+ LDFLAGS="$(TARGET_LDFLAGS) -L $(STAGING_DIR)/usr/lib" \
+ BUILD_DIR="$(KERNEL_BUILD_DIR)" \
+ all
+
+
+endef
+
+define Package/qos-gargoyle/install
+ $(INSTALL_DIR) $(1)/etc/init.d
+ $(INSTALL_DIR) $(1)/etc/hotplug.d/iface
+ $(INSTALL_DIR) $(1)/etc/config
+
+ $(INSTALL_BIN) ./files/qos_gargoyle.init $(1)/etc/init.d/qos_gargoyle
+ $(INSTALL_BIN) ./files/qos_gargoyle.hotplug $(1)/etc/hotplug.d/iface/23-qos_gargoyle
+ $(INSTALL_DATA) ./files/qos_gargoyle.conf $(1)/etc/config/qos_gargoyle
+
+ $(INSTALL_DIR) $(1)/usr/sbin
+ $(INSTALL_BIN) $(PKG_BUILD_DIR)/qosmon $(1)/usr/sbin/qosmon
+
+endef
+
+define Package/qos-gargoyle/prerm
+ $${IPKG_INSTROOT}/etc/init.d/qos_gargoyle stop 2>/dev/null
+ $${IPKG_INSTROOT}/etc/init.d/qos_gargoyle disable 2>/dev/null
+ ls >/dev/null 2>&1
+endef
+
+$(eval $(call BuildPackage,qos-gargoyle))
diff --git a/package/ctcgfw/qos-gargoyle/files/example-qos-config2.conf b/package/ctcgfw/qos-gargoyle/files/example-qos-config2.conf
new file mode 100644
index 0000000000..e248b6b255
--- /dev/null
+++ b/package/ctcgfw/qos-gargoyle/files/example-qos-config2.conf
@@ -0,0 +1,119 @@
+
+config global global
+ option mtu "1500" # the maximum allowed packet size (in bytes) on the interface being shaped
+ option network "wan" # the name of the network whose interface should have its outgoing traffic shaped
+ #option interface "eth1" # the name of the network interface which should have its outgoing traffic shaped
+
+
+config upload upload
+ option total_bandwidth "600" # 600kbit/s
+ option default_class "uclass_2" # default traffic class, must be a section of type upload_class
+
+
+config download download
+ option total_bandwidth "4000" # 4000kbit/s (500Kbyte/s)
+ option default_class "dclass_1" # default traffic class, must be a section of type download_class
+
+
+
+
+
+
+#upload classes
+config upload_class uclass_1
+ option percent_bandwidth "40" # percent of total bandwidth to use
+
+config upload_class uclass_2
+ option percent_bandwidth "20" # percent of total bandwidth to use
+ option max_bandwidth "30" # max bandwidth useage in absolute speed (kbit/s)
+
+config upload_class uclass_3
+ option percent_bandwidth "30" # percent of total bandwidth to use
+ option min_bandwidth "160" # min bandwidth to allocate to this class
+
+config upload_class uclass_4
+ option percent_bandwidth "10" # percent of total bandwidth to use
+
+
+
+#download classes
+config download_class dclass_1
+ option percent_bandwidth "30" # percent of total bandwidth to use
+ option min_bandwidth "80" # min bandwidth to allocate to this class
+
+config download_class dclass_2
+ option percent_bandwidth "60" # percent of total bandwidth to use
+
+config download_class dclass_3
+ option percent_bandwidth "10" # percent of total bandwidth to use
+ option min_bandwidth "80" # min bandwidth to allocate to this class
+
+
+# classification rules
+#
+# POSSIBLE OPTIONS:
+# class name of bandwidth class to use if rule matches, this is required in each rule section
+# test_order an integer that specifies the order in which the rule should be checked for a match (lower numbers are checked first)
+# proto check that packet has this protocol (tcp, udp, both), if port is specified default is both
+# source check that packet has this source ip, can optionally have /[mask] after it (see -s option in iptables man page)
+# destination check that packet has this destination ip, can optionally have /[mask] after it (see -d option in iptables man page)
+# dport check that packet has this destination port
+# sport check that packet has this source port
+# min_pkt_size check that packet is at least this size (in bytes)
+# max_pkt_size check that packet is no larger than this size (in bytes)
+# layer7 check whether packet matches layer7 specification
+# ipp2p check wither packet matches ipp2p specification (used to recognize p2p protocols)
+# "ipp2p" or "all" will match any of the specified p2p protocols, you can
+# also specifically match any protocol listed in the documentation here:
+# http://ipp2p.org/docu_en.html
+#
+# sytnax for upload rules and download rules is identical
+config upload_rule
+ option class "uclass_4"
+ option test_order "1"
+ option destination "195.56.146.238"
+
+config upload_rule
+ option class "uclass_3"
+ option test_order "2"
+ option proto "both"
+ option dstport "80-90"
+
+config upload_rule
+ option class "uclass_1"
+ option test_order "3"
+ option dstport "22"
+
+config upload_rule
+ option class "uclass_3"
+ option test_order "44"
+ option proto "udp"
+ option max_pkt_size "250"
+
+config upload_rule
+ option class "uclass_3"
+ option test_order "5"
+ option proto "udp"
+ option max_pkt_size "250"
+
+config upload_rule
+ option class "uclass_4"
+ option test_order "6"
+ option ipp2p "all"
+
+config upload_rule
+ option class "uclass_3"
+ option test_order "7"
+ option layer7 "pop3"
+
+#download rules
+config download_rule
+ option class "dclass_2"
+ option test_order "1"
+ option dstport "80-90"
+
+config download_rule
+ option class "dclass_3"
+ option test_order "2"
+ option ipp2p "all"
+
diff --git a/package/ctcgfw/qos-gargoyle/files/qos_gargoyle.conf b/package/ctcgfw/qos-gargoyle/files/qos_gargoyle.conf
new file mode 100644
index 0000000000..557ee64efc
--- /dev/null
+++ b/package/ctcgfw/qos-gargoyle/files/qos_gargoyle.conf
@@ -0,0 +1,25 @@
+
+config upload 'upload'
+ option default_class 'uclass_2'
+
+config download 'download'
+ option qos_monenabled 'true'
+ option default_class 'dclass_1'
+
+config download_class 'dclass_1'
+ option name 'Normal'
+ option percent_bandwidth '100'
+
+config upload_class 'uclass_1'
+ option name 'Fast'
+ option percent_bandwidth '90'
+
+config upload_class 'uclass_2'
+ option name 'Normal'
+ option percent_bandwidth '10'
+
+config upload_rule 'upload_rule_100'
+ option class 'uclass_1'
+ option test_order '100'
+ option max_pkt_size '128'
+
diff --git a/package/ctcgfw/qos-gargoyle/files/qos_gargoyle.hotplug b/package/ctcgfw/qos-gargoyle/files/qos_gargoyle.hotplug
new file mode 100644
index 0000000000..39e491269a
--- /dev/null
+++ b/package/ctcgfw/qos-gargoyle/files/qos_gargoyle.hotplug
@@ -0,0 +1,12 @@
+#!/bin/sh
+if [ "$INTERFACE" = "wan" ]; then
+ if [ -h /etc/rc.d/S50qos_gargoyle ] ; then
+ if [ "$ACTION" = "ifup" ]; then
+ /etc/init.d/qos_gargoyle start $DEVICE
+ fi
+ fi
+
+ if [ "$ACTION" = "ifdown" ]; then
+ /etc/init.d/qos_gargoyle stop
+ fi
+fi
diff --git a/package/ctcgfw/qos-gargoyle/files/qos_gargoyle.init b/package/ctcgfw/qos-gargoyle/files/qos_gargoyle.init
new file mode 100755
index 0000000000..3a79481918
--- /dev/null
+++ b/package/ctcgfw/qos-gargoyle/files/qos_gargoyle.init
@@ -0,0 +1,868 @@
+#!/bin/sh /etc/rc.common
+#
+# Copyright Eric Bishop, 2008
+# This is free software licensed under the terms of the GNU GPL v2.0
+#
+
+START=50
+
+EXTRA_COMMANDS=show
+EXTRA_HELP=" show Show current Qos configuration (if active)"
+
+include /lib/network
+include /usr/lib/gargoyle_firewall_util
+
+config_file_name="qos_gargoyle"
+upload_mask="0x007F"
+download_mask="0x7F00"
+qos_mark_file="/etc/qos_class_marks"
+
+#created while qos is being initialized so hotplug and init script don't
+#both try to initialize qos at the same time
+lock_file="/var/run/qos_updating"
+
+load_all_config_options()
+{
+ local config_name="$1"
+ local section_id="$2"
+
+ ALL_OPTION_VARIABLES=""
+ # this callback loads all the variables
+ # in the section_id section when we do
+ # config_load. We need to redefine
+ # the option_cb for different sections
+ # so that the active one isn't still active
+ # after we're done with it. For reference
+ # the $1 variable is the name of the option
+ # and $2 is the name of the section
+ config_cb()
+ {
+ if [ ."$2" = ."$section_id" ]; then
+ option_cb()
+ {
+ ALL_OPTION_VARIABLES="$ALL_OPTION_VARIABLES $1"
+ }
+ else
+ option_cb() { return 0; }
+ fi
+ }
+
+ config_load "$config_name"
+
+ for var in $ALL_OPTION_VARIABLES
+ do
+ config_get "$var" "$section_id" "$var"
+ done
+}
+
+load_all_config_sections()
+{
+ local config_name="$1"
+ local section_type="$2"
+
+ all_config_sections=""
+ section_order=""
+ config_cb()
+ {
+ if [ -n "$2" ] || [ -n "$1" ] ; then
+ if [ -n "$section_type" ] ; then
+ if [ "$1" = "$section_type" ] ; then
+ all_config_sections="$all_config_sections $2"
+ fi
+ else
+ all_config_sections="$all_config_sections $2"
+ fi
+ fi
+ }
+
+ config_load "$config_name"
+ echo "$all_config_sections"
+
+}
+
+
+
+
+load_and_sort_all_config_sections()
+{
+ local config_name="$1"
+ local section_type="$2"
+ local sort_variable="$3"
+
+ all_config_sections=""
+ defined_option_cb()
+ {
+ if [ "$1" = "$sort_variable" ]; then
+ all_config_sections=" $2:$all_config_sections"
+ fi
+ }
+
+ config_cb()
+ {
+ if [ -n "$2" ] || [ -n "$1" ] ; then
+ if [ -n "$section_type" ] ; then
+ if [ "$1" = "$section_type" ] ; then
+ all_config_sections="$2 $all_config_sections"
+ option_cb() { defined_option_cb $1 $2 ; }
+ else
+ option_cb() { return 0; }
+ fi
+ else
+ all_config_sections="$2 $all_config_sections"
+ option_cb(){ defined_option_cb $1 $2 ; }
+ fi
+ fi
+ }
+
+ config_load "$config_name"
+
+ echo "$all_config_sections" | awk ' {for(i=1; i <= NF; i++){ print $i }}' | sort -n -t ":" | awk 'BEGIN {FS=":"}; {print $2}'
+}
+
+
+get_classname_mark()
+{
+ local class="$1"
+ local class_mark_list="$2"
+ echo "$class_mark_list" | awk -v class="$class" '{ for (i = 1; i <= NF; i++){ if($i~class":"){ gsub(class":",""); print $i } }}'
+}
+
+apply_all_rules()
+{
+ local rule_type="$1"
+ local class_mark_list="$2"
+ local chain="$3"
+ local table="$4"
+
+ local need_proto
+ local tmp_proto
+
+ # add filter rules
+ rule_list=$(load_and_sort_all_config_sections "$config_file_name" "$rule_type" "test_order")
+ for rule in $rule_list ; do
+ class=""
+ proto=""
+ min_pkt_size=""
+ max_pkt_size=""
+ match_str=""
+ need_proto=""
+
+ load_all_config_options "$config_file_name" "$rule"
+
+ for option in $ALL_OPTION_VARIABLES ; do
+
+ option_value=$(eval echo \$$option)
+ case "$option" in
+ source)
+ if [ "$3" = "qos_egress" ] ; then
+ if [ "$option_value" = "$local_ip" ] || [ "$option_value" = "$wan_ip" ]; then
+ option_value="$wan_ip"
+ fi
+ fi
+ match_str="$match_str -s $option_value"
+ ;;
+ destination)
+ if [ "$3" = "qos_ingress" ] ; then
+ if [ "$option_value" = "$local_ip" ] || [ "$option_value" = "$wan_ip" ]; then
+ option_value="$wan_ip"
+ fi
+ fi
+ match_str="$match_str -d $option_value"
+ ;;
+ srcport)
+ if [ -n $(echo $option_value | grep "-") ] ; then option_value="$(echo "$option_value" | sed -e 's,-,:,g')" ; fi
+ match_str="$match_str --sport $option_value"
+ need_proto="1"
+ ;;
+ dstport)
+ if [ -n $(echo $option_value | grep "-") ] ; then option_value="$(echo "$option_value" | sed -e 's,-,:,g')" ; fi
+ match_str="$match_str --dport $option_value"
+ need_proto="1"
+
+ ;;
+ layer7)
+ layer7_connmark=$(cat /etc/l7marker.marks 2>/dev/null | grep "$option_value" | awk '{ print $2 }')
+ layer7_mask=$(cat /etc/l7marker.marks 2>/dev/null | grep "$option_value" | awk '{ print $3 }')
+ if [ -n "$layer7_connmark" ] ; then
+ match_str="$match_str -m connmark --mark $layer7_connmark/$layer7_mask "
+ else
+ match_str="$match_str -m layer7 --l7proto $option_value"
+ fi
+ ;;
+ connbytes_kb)
+ match_str="$match_str -m connbytes --connbytes $(($option_value*1024)): --connbytes-dir both --connbytes-mode bytes"
+ ;;
+ esac
+ done
+
+
+ if [ -n "$min_pkt_size" ] || [ -n "$max_pkt_size" ] ; then
+ if [ -z "$min_pkt_size" ] ; then min_pkt_size=0 ; fi
+ if [ -z "$max_pkt_size" ] ; then max_pkt_size=1500 ; fi
+ match_str="$match_str -m length --length $min_pkt_size:$max_pkt_size"
+ fi
+
+ if [ -n "$class" ] ; then
+ if [ -n "$proto" ] || [ -n "$match_str" ] ; then
+ next_mark=$(get_classname_mark "$class" "$class_mark_list" )
+
+ #We need to specify both udp and tcp if the user indicated a port
+ #and he did not indiate a protocal.
+ if [ -z "$proto" ] && [ -n "$need_proto" ] ; then
+
+ $echo_on
+ iptables -t $table -I $chain -p tcp $match_str -j MARK --set-mark $next_mark
+ iptables -t $table -I $chain -p udp $match_str -j MARK --set-mark $next_mark
+ $echo_off
+
+ else
+
+ #Otherwise just specify what the user requested (or nothing)
+
+ if [ -n "$proto" ] ; then
+ tmp_proto="-p $proto"
+ else
+ tmp_proto=""
+ fi
+
+ $echo_on
+ iptables -t $table -I $chain $tmp_proto $match_str -j MARK --set-mark $next_mark
+ $echo_off
+ fi
+
+ fi
+ fi
+ done
+}
+
+
+update_markfile()
+{
+
+ #initialize mark file in /tmp first, and test md5sum
+ #this should speed things up and prevent writing to flash unnecessarily (since /tmp is ramdisk)
+
+ tmp_qos_mark_file="/tmp/qos_marks.tmp.tmp"
+ rm -rf "$tmp_qos_mark_file"
+
+ #re-populate per the QoS setup.
+ if [ $total_upload_bandwidth -ge 0 ] ; then
+
+ upload_class_list=$(load_all_config_sections "$config_file_name" "upload_class")
+
+ next_class_index=2
+ for uclass_name in $upload_class_list ; do
+ printf "upload $uclass_name %d $upload_mask\n" $next_class_index >> "$tmp_qos_mark_file"
+ next_class_index=$(($next_class_index+1))
+ done
+ fi
+
+ if [ $total_download_bandwidth -ge 0 ] ; then
+
+ download_class_list=$(load_all_config_sections "$config_file_name" "download_class")
+
+ next_class_index=2
+ for dclass_name in $download_class_list ; do
+ printf "download $dclass_name %d $download_mask\n" $(($next_class_index << 8)) >> "$tmp_qos_mark_file"
+ next_class_index=$(($next_class_index+1))
+ done
+ fi
+
+ mark_files_match="0"
+ if [ -e "$qos_mark_file" ] ; then
+ new_md5=$(md5sum "$tmp_qos_mark_file" | awk '{ print $1 ; } ')
+ old_md5=$(md5sum "$qos_mark_file" | awk '{ print $1 ; } ')
+ if [ "$new_md5" = "$old_md5" ] ; then
+ mark_files_match="1"
+ fi
+ fi
+
+ if [ "$mark_files_match" = "0" ] ; then
+ mv "$tmp_qos_mark_file" "$qos_mark_file"
+ else
+ rm -rf "$tmp_qos_mark_file"
+ fi
+
+}
+
+
+
+initialize_qos()
+{
+ #initialize layer7_marker if necessary
+ create_l7marker_chain
+
+ # Now, load/insert necessary kernel modules
+ # The following packages are required for the modules:
+ rmmod imq >&- 2>&-
+ insmod imq numdevs=1 hook_chains="INPUT,FORWARD" hook_tables="mangle,mangle" >&- 2>&-
+ ip link set dev imq0 mtu 1500
+
+ # Make sure the other kernel modules we need are loaded
+ insmod cls_fw >&- 2>&-
+ insmod cls_flow >&- 2>&-
+ insmod sch_hfsc >&- 2>&-
+ insmod sch_sfq >&- 2>&-
+
+ #Deciding how to set sfq_depth is not straight forward. Too high and we burn up RAM
+ #needlessly on low memory routers. Too low and our maximum bandwidth gets limited.
+ #
+ #On low memory routers we need to take it easy on how big the queues can get.
+ #When depth is limited to 32 maximum bandwidth through any class will be around 11Mbps.
+ #Otherwise it will be around 350Mbps.
+ total_mem="$(sed -e '/^MemTotal: /!d; s#MemTotal: *##; s# kB##g' /proc/meminfo)"
+ if [ "$total_mem" -lt 16000 ] ; then
+ sfq_depth="depth 32";
+ else
+ sfq_depth="";
+ fi
+
+ $echo_off
+ #load upload variables
+ load_all_config_options "$config_file_name" "upload"
+ $echo_on
+ if [ -n "$total_bandwidth" ] ; then
+ total_upload_bandwidth="$total_bandwidth"
+ else
+ total_upload_bandwidth=-1
+ fi
+ upload_default_class="$default_class"
+
+ #load download variables
+ total_bandwidth=""
+ default_class=""
+ $echo_off
+ load_all_config_options "$config_file_name" "download"
+ $echo_on
+ if [ -n "$total_bandwidth" ] ; then
+ total_download_bandwidth="$total_bandwidth"
+ else
+ total_download_bandwidth=-1
+ fi
+ download_default_class="$default_class"
+
+ #Since the introduction of ADSL IP Extensions its not so easy to tell if we have a DSL connection
+ #or not making it hard to set the stab parameters correctly.
+ #Stab parameters here are derived from tc-stab(8).html
+ overhead="stab linklayer atm overhead 32 mtu 2048 "
+
+ #It is now often difficult to tell if the overhead should be used or not because even DSL modems use DHCP or may
+ #not be in bridge mode. So I make the assumption that all connections with less than 3Mbps down or 1Mbps up
+ #are DSL regardless of the protocol selection type.
+ wan_proto=$(uci get network.wan.proto 2>/dev/null)
+ if [ "$wan_proto" != "pppoe" ] && [ "$total_upload_bandwidth" -ge 1100 ] && [ "$total_download_bandwidth" -ge 3100 ] ; then
+ overhead=""
+ fi
+
+ $echo_off
+
+ if [ $total_upload_bandwidth -ge 0 ] ; then
+
+ #load upload classes
+ upload_class_list=$(load_all_config_sections "$config_file_name" "upload_class")
+ for uclass_name in $upload_class_list ; do
+ percent_bandwidth=""
+ min_bandwidth=""
+ max_bandwidth=""
+ load_all_config_options "$config_file_name" "$uclass_name"
+ if [ -z "$percent_bandwidth" ] ; then
+ percent_bandwidth="0"
+ fi
+ if [ -z "$min_bandwidth" ] ; then
+ min_bandwidth="-1"
+ fi
+ if [ -z "$max_bandwidth" ] ; then
+ max_bandwidth="-1"
+ fi
+ classdef="$percent_bandwidth $max_bandwidth $min_bandwidth"
+ eval $uclass_name=\"\$classdef\" #"#comment quote here so formatting in editor isn't FUBAR
+ done
+
+
+ # Attach egress queuing discipline to QoS interface, now with temperary default
+ $echo_on
+ tc qdisc add dev $qos_interface root handle 1:0 hfsc default 1
+
+ # For the root qdisc, only ul and ls are relevant since rt only applies to leaf qdiscs
+ #
+ # A detailed explanation of how/why/what is being set is warranted here...
+ # Link Share bandwidths of the leaf nodes are all relative to their parents link share parameter and those of their
+ # fellow leaf nodes competing for shares. We set the root class link share to 1000Mbit as per unit bandwidth.
+ # Leaf nodes are then set with the respective percent of this per unit number.
+ #
+ # The actual maximum link speed is the lower of the ul and the ls and this is going to always be the ul parameter in our
+ # design.
+ #
+ # Again, for ls only the ratios matter, the absolute values do not.
+ tc class add dev $qos_interface parent 1:0 classid 1:1 hfsc ls rate 1000Mbit ul rate ${total_upload_bandwidth}kbit
+ $echo_off
+
+ class_mark_list=""
+ upload_shift=0
+ next_class_index=2
+ next_classid=$(printf "0x%X" $(($next_class_index << $upload_shift)) )
+ def_upload_idx=$next_class_index
+ def_upload_class=$next_classid
+
+ for uclass_name in $upload_class_list ; do
+
+ class_mark_list="$class_mark_list$uclass_name:$next_classid "
+
+ $echo_on
+ uclass_def=$(eval echo "\$$uclass_name")
+
+ #Bandwidth at capacity for this class
+ m2=$(( 10 * $(echo $uclass_def | awk ' {print $1}' ) ))
+
+ #is there a minimum bandwidth specified in kbps?
+ min_bandwidth=$( echo $uclass_def | awk ' {print $3}' )
+ if [ "$min_bandwidth" -gt 0 ] ; then
+ ll_str=" rt m1 $((2*$min_bandwidth))kbit d 2ms m2 ${min_bandwidth}kbit"
+ else
+ ll_str=""
+ fi
+
+ #is there an upper limit specified in kbps?
+ max_bandwidth=$( echo $uclass_def | awk ' {print $2}' )
+ if [ "$max_bandwidth" -ge 0 ] ; then
+ ul_str=" ul m2 ${max_bandwidth}kbit"
+ else
+ #Calculate from the class link share.
+ max_bandwidth=$(($m2*$total_upload_bandwidth/1000000))
+ ul_str=""
+ fi
+
+
+ # For leaf nodes in HFSC we calculate the latency as the time spent waiting to earn enough credit (credit_t) to
+ #get selected to send plus the time to transmit (tts). The maximum latency can be calculated as shown
+ #below assuming an MTU of 1500bytes and 8.2bits/byte including overhead.
+ #credit_t = $((1500*82/$max_bandwidth/10));
+ #tts = $((1500*82/$total_upload_bandwidth/10));
+
+ #tbw is the Delay x Bandwidth product in bytes. We do not actually know the packet
+ #delay so we make an estimate of 150ms here and hope for the best. max_bandwidth is in kbps
+ #we multiply 100ms by 1000 below so the units work out.
+ tbw=$(($max_bandwidth*100/8));
+ if [ "$tbw" -lt 6000 ] ; then
+ tbw=6000
+ fi
+
+ #We will use the SFQ qdisc with flow classifier. The limit on the depth of our qdisc depends on the upper limit
+ #of the bandwidth allocated to this class. To impliment per IP sharing of the class we use the flow classifier
+ #and the 'nfct-src' on the upload side and 'dst' on the download side. I found a nice man page here
+ #https://arndtroide.homelinux.org/cgi-bin/man/man2html?tc-sfq+8
+
+ #Add the leaf class
+ tc class add dev $qos_interface parent 1:1 classid 1:$next_class_index hfsc ls m2 ${m2}Mbit $ll_str $ul_str
+ #Add the qdisc to the leaf class, assuming average packet at 250 bytes.
+ tc qdisc add dev $qos_interface parent 1:$next_class_index handle $next_class_index:1 sfq headdrop limit $(($tbw/250)) $sfq_depth divisor 256
+
+ #
+ #Folks interested in experimenting with CoDEL can comment out the preceeding line and uncomment hte next line.
+ #This will work for the upload direction. Left to the student how to mod the download.
+ #As of Chaos Calmer there does not seem to be any benefit in changing SFQ to FQ_CODEL and some stability
+ #issues as well. Will re-evaluate once Openwrt 18.06 based Gargoyle is released.
+ #fq_codel parameters
+ # limit - SFQ used 127 so i suggest the same here.
+ # target - At and above 6Mbps = 5ms (min). At 100kbps = 100ms (max). Select accordingly
+ # interval - 100ms, This is the default
+ # flows - 255 (One for each IP)
+ #
+ #tc qdisc add dev $qos_interface parent 1:$next_class_index handle $next_class_index:1 fq_codel limit 127 target 5ms interval 100ms flows 255 quantum 1514
+
+ #Add a filter to the root class to direct packets to this leaf class according to the conntrack mark
+ tc filter add dev $qos_interface parent 1:0 protocol ip handle $next_classid fw flowid 1:$next_class_index
+ #Add a filter to the leaf class to define flows as being the source IP address.
+ tc filter add dev $qos_interface parent $next_class_index: handle 1 flow divisor 256 map key nfct-src and 0xff
+ $echo_off
+
+ if [ "$upload_default_class" = "$uclass_name" ] ; then
+ def_upload_idx=$next_class_index
+ def_upload_class=$next_classid
+ fi
+
+ next_class_index=$(($next_class_index+1))
+ next_classid=$(printf "0x%X" $(($next_class_index << $upload_shift)) )
+ done
+
+ $echo_on
+
+ #Go back and touch up the root qdisc to have the proper default class
+ tc qdisc change dev $qos_interface $overhead root handle 1:0 hfsc default $def_upload_idx
+
+ # Set up egress chain
+ iptables -t mangle -N qos_egress
+ iptables -t mangle -A POSTROUTING -o $qos_interface -j qos_egress
+
+ #Next the user entered rules.
+ $echo_off
+ apply_all_rules "upload_rule" "$class_mark_list" "qos_egress" "mangle"
+ $echo_on
+
+ #set default class mark first in case we don't match anything
+ iptables -t mangle -I qos_egress -j MARK --set-mark $def_upload_class
+
+ #if we already set a mark in quota chain, we need to save that mark to the connmark, then return so it doesn't get over-written
+ iptables -t mangle -I qos_egress -m mark ! --mark 0x0 -j RETURN
+ iptables -t mangle -I qos_egress -m mark ! --mark 0x0 -j CONNMARK --save-mark --mask $upload_mask
+
+ # save current mark to connmark at end of chain
+ iptables -t mangle -A qos_egress -j CONNMARK --save-mark --mask $upload_mask
+ fi
+
+
+
+ #Only if both upload and download QoS are enabled can we enable Gargoyle active QoS monitor
+ if [ $total_download_bandwidth -eq 0 ] || [ $total_upload_bandwidth -eq 0 ] ; then
+ qos_monenabled = "false" ;
+ fi
+
+ if [ $total_download_bandwidth -ge 0 ] ; then
+ # Set up the InterMediate Queuing device (IMQ0) for ingress
+ ip link set imq0 up
+
+ # Attach ingress queuing discipline to IMQ0 with temporary default
+ tc qdisc add dev imq0 root handle 1:0 hfsc default 1
+
+ # For the root qdisc, only ul is relevant, since there is no link sharing, and rt only applies to leaf qdiscs
+ tc class add dev imq0 parent 1:0 classid 1:1 hfsc ls rate 1000Mbit ul m2 ${total_download_bandwidth}kbit
+
+ #load download classes
+ $echo_off
+ download_class_list=$(load_all_config_sections "$config_file_name" "download_class")
+ for dclass_name in $download_class_list ; do
+ percent_bandwidth=""
+ min_bandwidth=""
+ max_bandwidth=""
+ minRTT=""
+
+ load_all_config_options "$config_file_name" "$dclass_name"
+ if [ -z "$percent_bandwidth" ] ; then
+ percent_bandwidth="0"
+ fi
+ if [ -z "$min_bandwidth" ] ; then
+ min_bandwidth="-1"
+ fi
+ if [ -z "$max_bandwidth" ] ; then
+ max_bandwidth="-1"
+ fi
+
+ classdef="$percent_bandwidth $max_bandwidth $min_bandwidth $minRTT"
+ eval $dclass_name=\"\$classdef\" #"#comment quote here so formatting in editor isn't FUBAR
+ done
+
+
+ class_mark_list=""
+ download_shift=8
+ next_class_index=2
+ next_classid=$(printf "0x%X" $(($next_class_index << $download_shift)) )
+ def_download_idx=$next_class_index
+ def_download_class=$next_classid
+
+ for dclass_name in $download_class_list ; do
+ $echo_on
+ class_mark_list="$class_mark_list$dclass_name:$next_classid "
+ dclass_def=$(eval echo "\$$dclass_name")
+
+ #bandwidth for this class
+ m2=$(( 10 * $(echo $dclass_def | awk ' {print $1}' ) ))
+
+ #The Gargoyle ACC switches from optimum WAN utilization mode to minimum RTT mode
+ #when it detects a class has become active that includes a two part service curve.
+ #So to trigger this behaviour we create two parts curves when minRTT is set.
+
+ #Calculations for the delay parameter. Assuming PKT is the packet size the best we
+ #could possibly do is PKT/total_download_bandwidth. If we do nothing we get the
+ #worst delay of PKT/$m2 (the class bandwidth). Since this class has minRTT set we
+ #are going to select the best case and allow up to one max size packet to go at the highest
+ #speed.
+
+ #is there an upper limit specified?
+ max_bandwidth=$( echo $dclass_def | awk ' {print $2}' )
+ ul_str=""
+ if [ "$max_bandwidth" -ge 0 ] ; then
+ ul_str=" ul m2 ${max_bandwidth}kbit"
+ else
+ max_bandwidth="$total_download_bandwidth"
+ fi
+
+ #How about a link share in percent?
+ minRTT=$( echo $dclass_def | awk ' {print $4}' )
+ if [ "$minRTT" = "Yes" ] ; then
+ d1=$((15000/$max_bandwidth+1))
+ ll_str=" ls m1 $((2*$m2))Mbit d ${d1}ms m2 ${m2}Mbit"
+ else
+ ll_str=" ls m2 ${m2}Mbit"
+ fi
+
+ #is there a minimum bandwidth specified?
+ min_bandwidth=$( echo $dclass_def | awk ' {print $3}' )
+ rt_str=""
+ if [ "$min_bandwidth" -gt 0 ] ; then
+ if [ "$minRTT" = "Yes" ] ; then
+ d2=$((15000/$max_bandwidth+1))
+ rt_str=" rt m1 $(($max_bandwidth))kbit d ${d2}ms m2 ${min_bandwidth}kbit"
+ else
+ rt_str=" rt m2 ${min_bandwidth}kbit"
+ fi
+ fi
+
+
+ tbw=$(($max_bandwidth*100/8));
+ if [ "$tbw" -lt 10000 ] ; then
+ tbw=10000
+ fi
+
+ tc class add dev imq0 parent 1:1 classid 1:$next_class_index hfsc $rt_str $ll_str $ul_str
+ #Assume average download packet size is 250 bytes.
+ tc qdisc add dev imq0 parent 1:$next_class_index handle $next_class_index:1 sfq headdrop limit $(($tbw/250)) $sfq_depth divisor 256
+ tc filter add dev imq0 parent 1:0 prio $next_class_index protocol ip handle $next_classid fw flowid 1:$next_class_index
+ tc filter add dev imq0 parent $next_class_index: handle 1 flow divisor 256 map key dst and 0xff
+ $echo_off
+
+ if [ "$download_default_class" = "$dclass_name" ] ; then
+ def_download_idx=$next_class_index
+ def_download_class=$next_classid
+ fi
+
+ next_class_index=$(($next_class_index+1))
+ next_classid=$(printf "0x%X" $(($next_class_index << $download_shift)) )
+ done
+
+ $echo_on
+
+ #Go back and touch up the root qdisc to have the proper default class
+ tc qdisc change dev imq0 $overhead root handle 1:0 hfsc default $def_download_idx
+
+ # Create ingress chain
+ iptables -t mangle -N qos_ingress
+
+ # Mark ingress in FORWARD and INPUT chains to make sure any DNAT (virt. server) is taken into account
+ iptables -t mangle -A FORWARD -i $qos_interface -j qos_ingress
+ iptables -t mangle -A INPUT -i $qos_interface -j qos_ingress
+
+ #Now the rest of the user entered rules.
+ $echo_off
+ apply_all_rules "download_rule" "$class_mark_list" "qos_ingress" "mangle" "$download_mask"
+ $echo_on
+
+ #set default class mark first in case we don't match anything
+ iptables -t mangle -I qos_ingress -j MARK --set-mark $def_download_class
+
+ #If we already set a mark in quota chain, we need to save that mark to the connmark, then return so it doesn't get over-written
+ iptables -t mangle -I qos_ingress -m mark ! --mark 0x0 -j RETURN
+ iptables -t mangle -I qos_ingress -m mark ! --mark 0x0 -j CONNMARK --save-mark --mask $download_mask
+
+ #Make sure all packets get sent through IMQ
+ iptables -t mangle -I qos_ingress -j IMQ --todev 0
+
+ #save current mark to connmark at end of chain
+ iptables -t mangle -A qos_ingress -j CONNMARK --save-mark --mask $download_mask
+
+ $echo_off
+
+ fi
+
+ #Enable Gargoyle active QoS monitor
+ if [ $total_upload_bandwidth -ge 0 ] && [ $total_download_bandwidth -ge 0 ] && [ "$qos_monenabled" = "true" ] ; then
+
+ $echo_on
+
+ #if the user specified a ping target then use that otherwise use the gateway.
+ if [ -z "$ptarget_ip" ] ; then
+ old_ifs="$IFS"
+ IFS=$(printf "\n\r")
+ targets=$(traceroute -n -I -w 1 -q 2 -m6 8.8.8.8 | grep -o '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}.*ms' | grep -v '8.8.8.8' | sed 's/ms//g')
+ ptarget_ip=""
+ for t in $targets ; do
+ if [ -z "$ptarget_ip" ] ; then
+ #ip of potential gateway
+ target=$(echo "$t" | awk '{ print $1 ; }')
+ target_is_local=$(echo "$target" | grep -E '^(192\.168|10\.|172\.1[6789]\.|172\.2[0-9]\.|172\.3[01]\.|0\.0\.0\.0|127\.|255\.)')
+
+
+ # round or rather ceil() time up to nearest millisecond since bash doesn't like working with decimals
+ time=$(echo "$t" | awk ' { print $3 ; }' | sed 's/\..*$//g')
+ time=$(( $time + 1 ))
+
+ if [ -z "$target_is_local" ] || [ "$time" -gt 5 ] ; then
+ ptarget_ip="$target"
+ fi
+ fi
+ done
+ IFS="$old_ifs"
+
+ #in case ping target is still not defined use default gateway
+ if [ -z "$ptarget_ip" ] ; then
+ ptarget_ip=$(gargoyle_header_footer -i gargoyle | sed -n 's/.*currentWanGateway.*"\(.*\)".*/\1/p')
+ fi
+ fi
+
+
+ #Ping responses from the ping target never go to ingress QoS (IMQ0 device).
+ iptables -t mangle -I qos_ingress -p icmp --icmp-type 0 -s $ptarget_ip -j RETURN
+
+ #Make a class to handle the outgoing ping requests from the router.
+ #These pings 84 bytes each for ethernet plus 22 more for PPPoE connections, allowing a maximum rate of 200ms we get 2.6kbps
+ tc class add dev $qos_interface parent 1:1 classid 1:127 hfsc rt umax 106 dmax 10ms rate 4kbit
+ tc qdisc add dev $qos_interface parent 1:127 pfifo
+ tc filter add dev $qos_interface parent 1:0 prio 1 protocol ip handle 127 fw flowid 1:127
+
+ #Mark all ping requests from the router to the ptarget to the above special class overriding any other mark.
+ iptables -t mangle -I qos_egress -p icmp --icmp-type 8 -s $wan_ip -d $ptarget_ip -j MARK --set-mark 127
+
+ #Start the monitor
+ if [ -n "$pinglimit" ] ; then
+ #In manual mode the user selects the active mode pinglimit indirectly. qosmon always measures the RTT of a ping on an unloaded link.
+ #This is called the ping entitlement. With a manual entry the minRTT ping limit is 110% of this measured ping entitlement
+ #and the active mode ping limit is the minRTT limit plus the user entered value. See the qosmon source code for more details.
+ #In summary manaully entered ping times only affect the active mode, not the minRTT mode ping time limits.
+ qosmon -a -b 800 $ptarget_ip $total_download_bandwidth $pinglimit
+ else
+ #In auto mode we calculate transmission delay based on our bandwidth and then ask qosmon
+ #to add this value to its measured ping entitlement to form the final ping limit.
+ pinglimit=$((1500*10*2/3/$total_download_bandwidth+1500*10/$total_upload_bandwidth+2))
+ qosmon -a -b 800 $ptarget_ip $total_download_bandwidth $pinglimit
+ fi
+
+ $echo_off
+ fi
+
+ update_markfile
+
+}
+
+
+define_interface()
+{
+ $echo_on
+ #Wait for up to 15 seconds for the wan interface to indicate it is up.
+ wait_sec=15
+ while [ -z $(uci -P /var/state get network.wan.up 2>/dev/null) ] && [ $wait_sec -gt 0 ] ; do
+ sleep 1
+ wait_sec=$(($wait_sec - 1))
+ done
+
+ #The wan interface name will depend on if pppoe is used or not. If pppoe is used then
+ #the name we are looking for is in network.wan.ifname. If there is nothing there
+ #use the device named by network.wan.device
+
+ qos_interface=$(uci -P /var/state get network.wan.ifname 2>/dev/null)
+ if [ -z $qos_interface ] ; then
+ qos_interface=$(uci -P /var/state get network.wan.device 2>/dev/null)
+ fi
+
+ #Determine our wan ip from the wan interface we detected.
+ wan_ip=$(ifconfig $qos_interface | sed -n 's/.*inet addr:\([0-9/.]*\).*/\1/p')
+ local_ip=$(ifconfig br-lan | sed -n 's/.*inet addr:\([0-9/.]*\).*/\1/p')
+
+ #Determine the VPN interface if there is one.
+ vpn_interface=$(uci -P /var/state get firewall.vpn_zone.device 2>/dev/null)
+ $echo_off
+}
+
+stop()
+{
+ #if already in process of being initialized, do not continue
+ #until that is finished, and then exit cleanly, without
+ #doing anything further
+ if [ -e "$lock_file" ] ; then
+ while [ -e "$lock_file" ] ; do
+ sleep 1
+ done
+ exit
+ fi
+
+ #Kill the qos monitor in case it is running
+ killall qosmon 2>/dev/null
+
+
+ $echo_on
+ #Delete the qdiscs we hung on the devices
+ for iface in $(tc qdisc show | grep hfsc | awk '{print $5}'); do
+ tc qdisc del dev "$iface" root
+ done
+
+ # eliminate existing rules in mangle table
+ delete_chain_from_table "mangle" "qos_egress"
+ delete_chain_from_table "mangle" "qos_ingress"
+ $echo_off
+
+}
+
+
+start()
+{
+ test_total_up=$(uci get qos_gargoyle.upload.total_bandwidth 2>/dev/null)
+ test_total_down=$(uci get qos_gargoyle.download.total_bandwidth 2>/dev/null)
+ if [ -z "$test_total_up" ] && [ -z "$test_total_down" ] ;then
+ disable
+ exit 0
+ fi
+
+
+ #This script is called by a hotplug event. If the WAN comes
+ #up fast we could end up trying to run qos_gargoyle while the
+ #boot is still in progress which causes issues in low memory
+ #routers. To avoid this we check to see if rcS is still running
+ #or not. If it is we wait until it completes or 60 seconds.
+ cnt=0
+ while ps | grep '[//]rcS S boot' >/dev/null
+ do
+ sleep 4
+ cnt=`expr $cnt + 1`
+ if [ $cnt -ge 15 ] ; then
+ break;
+ fi
+ done
+
+ stop
+ touch "$lock_file"
+
+ #load qos_interface from global variables
+ define_interface
+ if [ -n "$qos_interface" ] ; then
+
+ load_all_config_options "$config_file_name" "global"
+ initialize_qos
+ fi
+
+ rm -rf "$lock_file"
+
+}
+
+restart()
+{
+ echo_on="set -x"
+ echo_off="set +x"
+ start "$1"
+}
+
+show()
+{
+ #load global variables
+ load_all_config_options "$config_file_name" "global"
+
+ #load qos_interface from global variables
+ define_interface
+
+ echo "Egress configuration on $qos_interface"
+ iptables -t mangle -vnL qos_egress 2>/dev/null
+ tc -s qdisc show dev $qos_interface
+ tc -s class show dev $qos_interface
+ tc -s filter show dev $qos_interface
+
+ echo "Ingress configuration in imq0"
+ iptables -t mangle -vnL qos_ingress 2>/dev/null
+ tc -s qdisc show dev imq0
+ tc -s class show dev imq0
+ tc -s filter show dev imq0
+ tc -s filter show dev imq0 parent 2:
+}
+
+boot()
+{
+ #Do nothing during init. Start is called by hotplug.
+ return
+}
+
diff --git a/package/ctcgfw/qos-gargoyle/src/Makefile b/package/ctcgfw/qos-gargoyle/src/Makefile
new file mode 100644
index 0000000000..674fc942d7
--- /dev/null
+++ b/package/ctcgfw/qos-gargoyle/src/Makefile
@@ -0,0 +1,35 @@
+BINDIR = /usr/sbin
+#TCDIR:=$(BUILD_DIR)/iproute2*/ip*
+TCDIR:=$(shell if [ -e "${BUILD_DIR}/iproute2-full" ] ; then echo "${BUILD_DIR}/iproute2-full"/ip* ; else echo "${BUILD_DIR}/iproute2-tiny"/ip* ; fi ; )
+#The ncurses library only needed if we remove the ONLYBG switch
+#below. Mostly for debug
+#LDLIBS += -lncurses
+
+LDLIBS += -lm
+
+
+TCOBJS := $(TCDIR)/tc/tc_util.o
+TCOBJS += $(TCDIR)/tc/tc_core.o
+TCOBJS += $(TCDIR)/tc/q_hfsc.o
+TCOBJS += $(TCDIR)/lib/libnetlink.a
+TCOBJS += $(TCDIR)/lib/libutil.a
+LDFLAGS += -Wl,-export-dynamic
+
+all: qosmon
+
+qosmon: qosmon.o
+ $(CC) $(LDFLAGS) $^ $(TCOBJS) -o $@ $(LDLIBS)
+
+qosmon.o: qosmon.c
+ $(CC) -D ONLYBG $(CFLAGS) -I $(TCDIR)/include -I $(TCDIR)/tc -c $^ -o $@
+
+install: all uninstall
+ -mkdir -p $(BINDIR)
+ cp qosmon $(BINDIR)
+
+uninstall:
+ rm -f $(BINDIR)/qosmon
+
+clean:
+ rm -rf *.o *~ .*sw* qosmon
+
diff --git a/package/ctcgfw/qos-gargoyle/src/qosmon.c b/package/ctcgfw/qos-gargoyle/src/qosmon.c
new file mode 100644
index 0000000000..1d112c83d6
--- /dev/null
+++ b/package/ctcgfw/qos-gargoyle/src/qosmon.c
@@ -0,0 +1,1256 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/* qosmon - An active QoS monitor for gargoyle routers.
+ * Created By Paul Bixel
+ * http://www.gargoyle-router.com
+ *
+ * Copyright © 2010 by Paul Bixel
+ *
+ * This file is free software: you may copy, redistribute and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+*/
+#define _GNU_SOURCE 1
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "utils.h"
+#include "tc_util.h"
+#include "tc_common.h"
+
+#include
+#include
+#include
+
+#ifndef ONLYBG
+#include
+#endif
+
+
+#define MAXPACKET 100 /* max packet size */
+#define BACKGROUND 3 /* Detact and run in the background */
+#define ADDENTITLEMENT 4
+
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 64
+#endif
+
+//The number of arguments needed for two of our kernel calls changed
+//in iproute2 after v2.6.29 (not sure when). We will use the new define
+//RTNL_FAMILY_MAX to tell us that we are linking against a version of iproute2
+//after then and define dump_filter and talk accordingly.
+#ifdef RTNL_FAMILY_MAX
+ #define dump_filter(a,b,c) rtnl_dump_filter(a,b,c)
+ #ifdef IFLA_STATS_RTA
+ #define talk(a,b,c) rtnl_talk(a,b,c)
+ #else
+ #define talk(a,b,c,d,e) rtnl_talk(a,b,c,NULL,NULL)
+ #endif
+#else
+#define dump_filter(a,b,c) rtnl_dump_filter(a,b,c,NULL,NULL)
+#define talk(a,b,c,d,e) rtnl_talk(a,b,c,NULL,NULL,NULL,NULL)
+#endif
+
+#define MIN(a,b) (((a)<(b))?(a):(b))
+
+/* use_names is required when linking to tc_util.o */
+bool use_names = false;
+
+
+u_char packet[MAXPACKET];
+int pingflags, options;
+
+#define DEAMON (pingflags & BACKGROUND)
+
+struct rtnl_handle rth;
+
+int s; /* Socket file descriptor */
+struct hostent *hp; /* Pointer to host info */
+struct timezone tz; /* leftover */
+
+struct sockaddr_in whereto;/* Who to ping */
+int datalen=64-8; /* How much data */
+
+const char usage[] =
+"Gargoyle active congestion controller version 2.4\n\n"
+"Usage: qosmon [options] pingtime pingtarget bandwidth [pinglimit]\n"
+" pingtime - The ping interval the monitor will use when active in ms.\n"
+" pingtarget - The URL or IP address of the target host for the monitor.\n"
+" bandwidth - The maximum download speed the WAN link will support in kbps.\n"
+" pinglimit - Optional pinglimit to use for control, otherwise measured.\n"
+" Options:\n"
+" -b - Run in the background\n"
+" -a - Add entitlement to pinglimt, enable auto ACTIVE/MINRTT mode switching.\n\n"
+" SIGUSR1 can be used to reset the link bandwidth at anytime.\n";
+
+char *hostname;
+char hnamebuf[MAXHOSTNAMELEN];
+
+uint16_t ntransmitted = 0; /* sequence # for outbound packets = #sent */
+uint16_t ident;
+uint16_t nreceived = 0; /* # of packets we got back */
+
+// For our digital filters we use Y = Y(-1) + alpha * (X - Y(-1))
+// where alpha = Sample_Period / (TC + Sample_Period)
+
+int fil_triptime; //Filter ping times in uS
+int alpha; //Actually alpha * 1000
+int period; //PING period In milliseconds
+int rawfltime; //Trip time in milliseconds
+int rawfltime_max; //The maximum measured ping time we have seen in uS.
+char nopingresponse; //Set to true when ping response is dropped.
+
+
+// Struct of data we keep on our classes
+struct CLASS_STATS {
+ int ID; //Class leaf ID
+ __u64 bytes; //Work bytes last query
+ u_char rtclass; //True if class is realtime.
+ u_char backlog; //Number of packets waiting
+ u_char actflg; //True if class is active.
+ long int cbw_flt; //Class bandwidth subject to filter. (bps)
+ long int cbw_flt_rt; //Class realtime bandwidth subject to filter. (bps)
+ long bwtime; //Timestamp of last byte reading.
+};
+
+#define STATCNT 30
+struct CLASS_STATS dnstats[STATCNT];
+struct CLASS_STATS *classptr;
+u_char classcnt;
+u_char errorflg;
+u_char firstflg=1; //First pass flag
+
+u_char DCA; //Number of download classes active
+u_char RTDCA; //Number of realtime download classes active
+u_char pingon=0; //Set to one when pinger becomes active.
+int pinglimit=0; //MinRTT mode ping time.
+int pinglimit_cl=0; //Ping limit entered on the commandline.
+int plimit; //Currently enforce ping limit
+
+float BWTC; //Time constant of the bandwidth filter
+int DBW_UL; //This the absolute limit of the link passed in as a parameter.
+int dbw_ul; //This is the last value of the limit sent to the kernel.
+int new_dbw_ul; //The new link limit proposed by the state machine.
+int saved_active_limit; //The new link limit last known to work with active mode.
+int saved_realtime_limit;//The new link limit last known to work with realtime mode.
+long int dbw_fil; //Filtered total download load (bps).
+
+#define QMON_CHK 0
+#define QMON_INIT 1
+#define QMON_ACTIVE 2
+#define QMON_REALTIME 3
+#define QMON_IDLE 4
+#define QMON_EXIT 5
+char *statename[]= {"CHECK","INIT","ACTIVE","MINRTT","IDLE","DISABLED"};
+unsigned char qstate=QMON_CHK;
+
+u_short cnt_mismatch=0;
+u_short cnt_errorflg=0;
+u_short last_errorflg=0;
+
+FILE *statusfd; //Filestream for updating our status to.
+char sigterm=0; //Set when we get a signal to terminal
+int sel_err=0; //Last error code returned by select
+
+#define DAEMON_NAME "qosmon"
+
+#ifndef DEVICE
+#define DEVICE "imq0"
+#endif
+
+#define EXIT_SUCCESS 0
+#define EXIT_FAILURE 1
+
+/* In a world were size is everything we can avoid linking to libm
+ if we can come up with replacements for rint() and ceil(). This will
+ save around 64k of RAM.
+
+ rint() is used in tc_util.c in five places. By code inspection I can see that
+ the parameter x will always be less than or equal to LONG_MAX so the following
+ simplified rint() will work for us.
+*/
+double rint(double x)
+{
+ long i;
+ if (x > LONG_MAX) i = LONG_MAX; else i = x+.5;
+ return i;
+}
+
+/* ceil() is used in q_hfsc.c in three places. There I can see that x is always
+ positive and less than LONG_MAX. This leads to a much simplified routine.
+*/
+double ceil(double x)
+{
+ long i;
+
+ if (x > LONG_MAX) x = LONG_MAX;
+ i = x;
+ if ((double)i != x) i++;
+ return i;
+}
+
+
+
+/*
+ * F I N I S H
+ *
+ * Sets a global to cause the main loop to terminate.
+ */
+void finish(int parm)
+{
+ sigterm=parm;
+}
+
+/*
+ * I N _ C K S U M
+ *
+ * Checksum routine for Internet Protocol family headers (C Version)
+ *
+ */
+int in_cksum(u_short *addr, int len)
+{
+ int nleft = len;
+ u_short *w = addr;
+ u_short answer;
+ int sum = 0;
+
+ /*
+ * Our algorithm is simple, using a 32 bit accumulator (sum),
+ * we add sequential 16 bit words to it, and at the end, fold
+ * back all the carry bits from the top 16 bits into the lower
+ * 16 bits.
+ */
+ while( nleft > 1 ) {
+ sum += *w++;
+ nleft -= 2;
+ }
+
+ /* mop up an odd byte, if necessary */
+ if( nleft == 1 ) {
+ u_short u = 0;
+
+ *(u_char *)(&u) = *(u_char *)w ;
+ sum += u;
+ }
+
+ /*
+ * add back carry outs from top 16 bits to low 16 bits
+ */
+ sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
+ sum += (sum >> 16); /* add carry */
+ answer = ~sum; /* truncate to 16 bits */
+ return (answer);
+}
+
+/*
+ * P I N G E R
+ *
+ * Compose and transmit an ICMP ECHO REQUEST packet. The IP packet
+ * will be added on by the kernel. The ID field is our UNIX process ID,
+ * and the sequence number is an ascending integer. The first 8 bytes
+ * of the data portion are used to hold a UNIX "timeval" struct in VAX
+ * byte-order, to compute the round-trip time.
+ */
+void pinger(void)
+{
+ static u_char outpack[MAXPACKET];
+ struct icmp *icp = (struct icmp *) outpack;
+ int i, cc;
+ struct timeval *tp = (struct timeval *) &outpack[8];
+ u_char *datap = &outpack[8+sizeof(struct timeval)];
+
+ icp->icmp_type = ICMP_ECHO;
+ icp->icmp_code = 0;
+ icp->icmp_cksum = 0;
+ icp->icmp_seq = ++ntransmitted;
+ icp->icmp_id = ident; /* ID */
+
+ cc = datalen+8; /* skips ICMP portion */
+
+ gettimeofday( tp, &tz );
+
+ for( i=8; iicmp_cksum = in_cksum( (u_short *) icp, cc );
+
+ /* cc = sendto(s, msg, len, flags, to, tolen) */
+ i = sendto( s, outpack, cc, 0, (const struct sockaddr *) &whereto, sizeof(whereto) );
+
+}
+
+
+/*
+ * T V S U B
+ *
+ * Subtract 2 timeval structs: out = out - in.
+ *
+ * Out is assumed to be >= in.
+ */
+void tvsub(register struct timeval *out, register struct timeval *in)
+{
+ if( (out->tv_usec -= in->tv_usec) < 0 ) {
+ out->tv_sec--;
+ out->tv_usec += 1000000;
+ }
+ out->tv_sec -= in->tv_sec;
+}
+
+/*
+ * P R _ P A C K
+ *
+ * Print out the packet, if it came from us. This logic is necessary
+ * because ALL readers of the ICMP socket get a copy of ALL ICMP packets
+ * which arrive ('tis only fair). This permits multiple copies of this
+ * program to be run without having intermingled output (or statistics!).
+ */
+char pr_pack( void *buf, int cc, struct sockaddr_in *from )
+{
+ struct ip *ip;
+ struct icmp *icp;
+ struct timeval tv;
+ struct timeval *tp;
+ int hlen,triptime;
+ struct in_addr tip;
+
+ from->sin_addr.s_addr = ntohl( from->sin_addr.s_addr );
+ gettimeofday( &tv, &tz );
+
+ ip = (struct ip *) buf;
+ hlen = ip->ip_hl << 2;
+ if (cc < hlen + ICMP_MINLEN) {
+ tip.s_addr = ntohl(*(uint32_t *) &from->sin_addr);
+ return 0;
+ }
+
+ icp = (struct icmp *)(buf + hlen);
+ if( icp->icmp_type != ICMP_ECHOREPLY ) {
+ tip.s_addr = ntohl(*(uint32_t *) &from->sin_addr);
+ return 0;
+ }
+
+ if( icp->icmp_id != ident )
+ return 0; /* 'Twas not our ECHO */
+
+ nreceived++;
+
+ //If it was not the packet we are looking for return now.
+ if (icp->icmp_seq != ntransmitted) return 0;
+
+ tp = (struct timeval *)&icp->icmp_data[0];
+ tvsub( &tv, tp );
+ triptime = tv.tv_sec*1000+(tv.tv_usec/1000);
+
+ //We are now ready to update the filtered round trip time.
+ //Check for some possible errors first.
+ if (triptime > period) triptime = period;
+
+ //If this was the most recent one we sent then update the rawfltime.
+ rawfltime=triptime;
+
+ //Is this a new maximum?
+ if (rawfltime > rawfltime_max/1000) rawfltime_max = rawfltime*1000;
+
+ //return 1 if we got a valid time.
+ return 1;
+
+}
+
+//These variables referenced but not used by the tc code we link to.
+int filter_ifindex;
+int use_iec = 0;
+int resolve_hosts = 0;
+
+
+int print_class(struct nlmsghdr *n, void *arg)
+{
+ struct tcmsg *t = NLMSG_DATA(n);
+ int len = n->nlmsg_len;
+ struct rtattr * tb[TCA_MAX+1];
+ int leafid;
+ u_char actflg=0;
+ unsigned long long work=0;
+ struct timespec newtime;
+
+ if (n->nlmsg_type != RTM_NEWTCLASS && n->nlmsg_type != RTM_DELTCLASS) {
+ fprintf(stderr, "Not a class\n");
+ return 0;
+ }
+ len -= NLMSG_LENGTH(sizeof(*t));
+ if (len < 0) {
+ fprintf(stderr, "Wrong len %d\n", len);
+ return -1;
+ }
+
+ memset(tb, 0, sizeof(tb));
+ parse_rtattr(tb, TCA_MAX, TCA_RTA(t), len);
+ clock_gettime(CLOCK_MONOTONIC,&newtime);
+
+ if (tb[TCA_KIND] == NULL) {
+ fprintf(stderr, "print_class: NULL kind\n");
+ return -1;
+ }
+
+ if (n->nlmsg_type == RTM_DELTCLASS) return 0;
+
+ //We only deal with hfsc classes.
+ if (strcmp((char*)RTA_DATA(tb[TCA_KIND]),"hfsc")) return 0;
+
+ //Reject the root node
+ if (t->tcm_parent == TC_H_ROOT) return 0;
+
+ //A previous error backs us out.
+ if (errorflg) return 0;
+
+ //If something has changed about the class structure or we reached the
+ //end of the array we need to reset and back out.
+ if (classcnt >= STATCNT) {
+ errorflg=1;
+ return 0;
+ }
+
+ //Get the leafid or set to -1 if parent.
+ if (t->tcm_info) leafid = t->tcm_info>>16;
+ else leafid = -1;
+
+ //If this is not the first pass and the leafid does not
+ //match then the class list changed so backout.
+ if ((!firstflg) && (leafid != classptr->ID) ) {
+ errorflg=1;
+ return 0;
+ }
+
+ //First time through so record the ID.
+ if (firstflg) {
+ classptr->ID = leafid;
+ }
+
+ //Pickup some hfsc basic stats
+ if (tb[TCA_STATS2]) {
+
+ struct tc_stats st;
+
+ /* handle case where kernel returns more/less than we know about */
+ memset(&st, 0, sizeof(st));
+ memcpy(&st, RTA_DATA(tb[TCA_STATS]), MIN(RTA_PAYLOAD(tb[TCA_STATS]), sizeof(st)));
+ work = st.bytes;
+ classptr->backlog = st.qlen;
+
+ /*Checkout if this class will trigger realtime mode by looking to see if either
+ the realtime or fair service curves are two part. */
+ if (firstflg) {
+
+ struct tc_service_curve *sc = NULL;
+ struct rtattr *tbs[TCA_STATS_MAX + 1];
+
+ classptr->rtclass=0;
+ parse_rtattr_nested(tbs, TCA_HFSC_MAX, tb[TCA_OPTIONS]);
+ if (tbs[TCA_HFSC_RSC] && (RTA_PAYLOAD(tbs[TCA_HFSC_RSC]) >= sizeof(*sc))) {
+ sc = RTA_DATA(tbs[TCA_HFSC_RSC]);
+ classptr->rtclass |= (sc && sc->m1);
+ }
+
+ if (tbs[TCA_HFSC_FSC] && (RTA_PAYLOAD(tbs[TCA_HFSC_FSC]) >= sizeof(*sc))) {
+ sc = RTA_DATA(tbs[TCA_HFSC_FSC]);
+ classptr->rtclass |= (sc && sc->m1);
+ }
+
+ }
+
+ } else {
+ errorflg=1;
+ return 0;
+ }
+
+
+ //Avoid a big jolt on the first pass.
+ if (firstflg) {
+ classptr->bytes = work;
+ }
+
+ //Update the filtered bandwidth based on what happened unless a rollover occured.
+ if (work >= classptr->bytes) {
+ long int bw;
+ long bperiod;
+
+ //Calculate an accurate time period for the bps calculation.
+ bperiod=(newtime.tv_nsec-classptr->bwtime)/1000000;
+ if (bperiodbytes)*8000/bperiod; //bps per second x 1000 here
+
+ //Convert back to bps as part of the filter calculation
+ classptr->cbw_flt=(bw-classptr->cbw_flt)*BWTC/1000+classptr->cbw_flt;
+
+ //A class is considered active if its BW exceeds 4000bps
+ if ((leafid != -1) && (classptr->cbw_flt > 4000)) {
+ DCA++;actflg=1;
+ if (classptr->rtclass) RTDCA++;
+ }
+
+ //Calculate the total link load by adding up all the classes.
+ if (leafid == -1) {
+ dbw_fil = 0;
+ } else {
+ dbw_fil += classptr->cbw_flt;
+ }
+
+ }
+
+ classptr->bwtime=newtime.tv_nsec;
+ classptr->bytes = work;
+ classptr->actflg = actflg;
+
+ classptr++;
+ classcnt++;
+ return 0;
+}
+
+/*Gather stats for classes attached to device d */
+int class_list(char *d)
+{
+ struct tcmsg t;
+
+ RTDCA=DCA =0;
+ memset(&t, 0, sizeof(t));
+ t.tcm_family = AF_UNSPEC;
+
+ ll_init_map(&rth);
+
+ if (d[0]) {
+ if ((t.tcm_ifindex = ll_name_to_index(d)) == 0) {
+ fprintf(stderr, "Cannot find device \"%s\"\n", d);
+ return 1;
+ }
+ filter_ifindex = t.tcm_ifindex;
+ }
+
+ if (rtnl_dump_request(&rth, RTM_GETTCLASS, &t, sizeof(t)) < 0) {
+ perror("Cannot send dump request");
+ return 1;
+ }
+
+ if (dump_filter(&rth, print_class, stdout) < 0) {
+ fprintf(stderr, "Dump terminated\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/*
+ * tc_class_modify
+ *
+ * This function changes the upper limit rate of the 'DEVICE' class to match
+ * the rate passed in as the sole parameter. This is the throttle means
+ * we will use to maintian the QoS performance as the link becomes saturated.
+ *
+ * The structure of this code is gleaned from the source code of 'tc' and is
+ * specific the the gargoyle QoS design.
+ */
+int tc_class_modify(__u32 rate)
+{
+ struct {
+ struct nlmsghdr n;
+ struct tcmsg t;
+ char buf[4096];
+ } req;
+
+ char k[16];
+ __u32 handle;
+
+ if (dbw_ul == rate) return 0;
+ dbw_ul=rate;
+
+ memset(&req, 0, sizeof(req));
+ memset(k, 0, sizeof(k));
+
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));
+ req.n.nlmsg_flags = NLM_F_REQUEST;
+ req.n.nlmsg_type = RTM_NEWTCLASS;
+ req.t.tcm_family = AF_UNSPEC;
+
+ //We are only going to modify the upper limit rate of the parent class.
+ if (get_tc_classid(&handle, "1:1")) {
+ fprintf(stderr,"invalid class ID");
+ return 1;
+ }
+ req.t.tcm_handle = handle;
+
+ if (get_tc_classid(&handle, "1:0")) {
+ fprintf(stderr,"invalid parent ID");
+ return 1;
+ }
+ req.t.tcm_parent = handle;
+
+ strcpy(k,"hsfc");
+ addattr_l(&req.n, sizeof(req), TCA_KIND, k, strlen(k)+1);
+
+ {
+ struct tc_service_curve usc;
+ struct rtattr *tail;
+
+ memset(&usc, 0, sizeof(usc));
+
+ usc.m2 = rate/8;
+
+ tail = NLMSG_TAIL(&req.n);
+
+ addattr_l(&req.n, 1024, TCA_OPTIONS, NULL, 0);
+ addattr_l(&req.n, 1024, TCA_HFSC_USC, &usc, sizeof(usc));
+
+ tail->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail;
+ }
+
+
+ //Communicate our change to the kernel.
+ ll_init_map(&rth);
+
+ if ((req.t.tcm_ifindex = ll_name_to_index(DEVICE)) == 0) {
+ fprintf(stderr, "Cannot find device %s\n",DEVICE);
+ return 1;
+ }
+
+
+ if (talk(&rth, &req.n, NULL) < 0)
+ return 2;
+
+ return 0;
+}
+
+/*
+ This function is periodically called and updates the
+ status file for the deamon. The status file can then
+ be viewed by other processes to tell what is going on.
+*/
+void update_status( FILE* fd )
+{
+
+ struct CLASS_STATS *cptr=dnstats;
+ u_char i;
+ char nstr[10];
+ int dbw;
+
+ //Link load includes the ping traffic when the pinger is on.
+ if (pingon) dbw = dbw_fil + 64 * 8 * 1000/period;
+ else dbw = dbw_fil;
+
+ //Update the status file.
+ rewind(fd);
+ fprintf(fd,"State: %s\n",statename[qstate]);
+ fprintf(fd,"Link limit: %d (kbps)\n",dbw_ul/1000);
+ fprintf(fd,"Fair Link limit: %d (kbps)\n",new_dbw_ul/1000);
+ fprintf(fd,"Link load: %d (kbps)\n",dbw/1000);
+
+ if (pingon) {
+ if (nopingresponse) fprintf(fd,"Ping: Dropped, assume %d mS\n",rawfltime_max/1000);
+ else fprintf(fd,"Ping: %d (ms)\n",rawfltime);
+ }
+ else
+ fprintf(fd,"Ping: off\n");
+
+ fprintf(fd,"Filtered/Max recent RTT: %d/%d (ms)\n",fil_triptime/1000,rawfltime_max/1000);
+ fprintf(fd,"RTT time limit: %d (ms) [%d/%d]\n",plimit/1000,pinglimit/1000,(pinglimit+135*pinglimit_cl/100)/1000);
+ fprintf(fd,"Classes Active: %u\n",DCA);
+
+ fprintf(fd,"Errors: (mismatch,errors,last err,selerr): %u,%u,%u,%i\n", cnt_mismatch, cnt_errorflg,last_errorflg,sel_err);
+
+
+ i=0;
+ while ((i++ID != 0)) {
+ fprintf(fd,"ID %4X, Active %u, Backlog %u, BW bps (filtered): %ld\n",
+ (short unsigned) cptr->ID,
+ cptr->actflg,
+ cptr->backlog,
+ cptr->cbw_flt);
+ cptr++;
+ }
+
+ fflush(fd);
+
+#ifndef ONLYBG
+ if (DEAMON) return;
+
+ //Home the cursor
+ mvprintw(0,0,"");
+ printw("\nqosmon status\n");
+
+ if (pingon) {
+ sprintf(nstr,"%d",rawfltime);
+ } else {
+ strcpy(nstr,"*");
+ }
+
+ printw("ping (%s/%d) DCA=%d, RTDCA=%d, plim=%d, plim2=%d, state=%s\n",nstr,fil_triptime/1000,
+ DCA,RTDCA,pinglimit/1000,plimit/1000,statename[qstate]);
+ printw("Link Limit=%6d, Fair Limit=%6d, Current Load=%6d (kbps)\n",
+ dbw_ul/1000,new_dbw_ul/1000,dbw/1000);
+ printw("Saved Active Limit=%6d, Saved Realtime Limit=%6d\n",saved_active_limit/1000,saved_realtime_limit/1000);
+ printw("pings sent=%d, pings received=%d\n",
+ ntransmitted,nreceived);
+
+ printw("Defined classes for %s\n",DEVICE);
+ printw("Errors: (mismatches,errors,last err,selerr): %u,%u,%u,%i\n", cnt_mismatch, cnt_errorflg,last_errorflg,sel_err);
+ cptr=dnstats;
+ i=0;
+ while ((i++ID != 0)) {
+ printw("ID %4X, Active %u, Realtime %u. Backlog %u, BW (filtered kbps): %ld\n",
+ (short unsigned) cptr->ID,
+ cptr->actflg,
+ cptr->rtclass,
+ cptr->backlog,
+ cptr->cbw_flt/1000);
+ cptr++;
+ }
+
+ refresh();
+#endif
+
+}
+
+/* v2.3 feature allows reseting the link limit to the initial value by sending the process SIGUSR1. */
+sig_atomic_t resetbw=1;
+void resetsig(int parm)
+{
+ resetbw=1;
+}
+
+
+/*
+ * M A I N
+ */
+int main(int argc, char *argv[])
+{
+ struct sockaddr_in from;
+ char **av = argv;
+ struct sockaddr_in *to = &whereto;
+ int on = 1;
+ struct protoent *proto;
+ float err;
+
+
+ argc--, av++;
+ while (argc > 0 && *av[0] == '-') {
+ while (*++av[0]) switch (*av[0]) {
+ case 'b':
+ pingflags |= BACKGROUND;
+ break;
+
+ case 'a':
+ pingflags |= ADDENTITLEMENT;
+ break;
+ }
+ argc--, av++;
+ }
+ if ((argc < 3) || (argc >4)) {
+ printf(usage);
+ exit(1);
+ }
+
+#ifdef ONLYBG
+ if (!(pingflags & BACKGROUND)) {
+ fprintf(stderr, "Must use the -b switch\n");
+ exit(1);
+ }
+#endif
+
+ //The first parameter is the ping time in ms.
+ period = atoi( av[0] );
+ if ((period > 2000) || (period < 100)) {
+ fprintf(stderr, "Invalid ping interval '%s'\n", av[0]);
+ exit(1);
+ }
+
+ bzero((char *)&whereto, sizeof(whereto) );
+ to->sin_family = AF_INET;
+ to->sin_addr.s_addr = inet_addr(av[1]);
+ if(to->sin_addr.s_addr != (unsigned)-1) {
+ strcpy(hnamebuf, av[1]);
+ hostname = hnamebuf;
+ } else {
+ hp = gethostbyname(av[1]);
+ if (hp) {
+ to->sin_family = hp->h_addrtype;
+ bcopy(hp->h_addr, (caddr_t)&to->sin_addr, hp->h_length);
+ hostname = hp->h_name;
+ } else {
+ fprintf(stderr, "%s: unknown host %s\n", argv[1], av[1]);
+ exit(1);
+ }
+ }
+
+ //The third parameter is the maximum download speed in kbps.
+ DBW_UL = atoi( av[2] );
+ if ((DBW_UL < 100) || (DBW_UL >= INT_MAX/1000)) {
+ fprintf(stderr, "Invalid download bandwidth '%s'\n", av[2]);
+ exit(1);
+ }
+
+ //Convert kbps to bps.
+ dbw_ul = DBW_UL = DBW_UL*1000;
+
+ //The fourth optional parameter is the ping limit in ms.
+ if (argc == 4) {
+ pinglimit_cl = pinglimit = atoi( av[3] )*1000;
+ }
+
+
+ ident = getpid() & 0xFFFF;
+
+ if ((proto = getprotobyname("icmp")) == NULL) {
+ fprintf(stderr, "icmp: unknown protocol\n");
+ exit(10);
+ }
+
+ // where alpha = Sample_Period / (TC + Sample_Period)
+ // TC needs to be not less than 3 times the sample period
+ alpha = (period*1000. / (period*4 + period));
+
+ //Class bandwidth filter time constants
+ BWTC= (period*1000. / (7500. + period));
+
+ //Check that we have access to tc functions.
+ tc_core_init();
+ if (rtnl_open(&rth, 0) < 0) {
+ fprintf(stderr, "Cannot open rtnetlink\n");
+ exit(1);
+ }
+
+ //Make sure the device is present and that we can scan it.
+ classptr=dnstats;
+ errorflg=0;
+ class_list(DEVICE);
+ if (errorflg) {
+ fprintf(stderr, "Cannot scan ingress device %s\n",DEVICE);
+ exit(1);
+ }
+
+ //If running in the background fork()
+ if (DEAMON) {
+
+ /* Ignore most signals in background */
+ signal( SIGINT, SIG_IGN );
+ signal( SIGQUIT, SIG_IGN );
+ signal( SIGCHLD, SIG_IGN );
+ signal( SIGALRM, SIG_IGN );
+ signal( SIGUSR1, SIG_IGN );
+ signal( SIGUSR2, SIG_IGN );
+ signal( SIGHUP, SIG_IGN );
+ signal( SIGTSTP, SIG_IGN );
+ signal( SIGPIPE, (sighandler_t) finish );
+ signal( SIGSEGV, (sighandler_t) finish );
+ signal( SIGILL, (sighandler_t) finish );
+ signal( SIGFPE, (sighandler_t) finish );
+ signal( SIGSYS, (sighandler_t) finish );
+ signal( SIGURG, (sighandler_t) finish );
+ signal( SIGTTIN, (sighandler_t) finish );
+ signal( SIGTTOU, (sighandler_t) finish );
+
+ //daemonize();
+ if ( daemon( 0, 0) < 0 )
+ {
+ fprintf(stderr,"deamon() failed with %i\n",errno);
+ exit( 1 );
+ }
+
+ /* Initialize the logging interface */
+ openlog( DAEMON_NAME, LOG_PID, LOG_LOCAL5 );
+ }
+
+ //SIGTERM is what we expect to kill us.
+ signal( SIGTERM, (sighandler_t) finish );
+
+ //SIGUSR1 resets the link speed.
+ signal( SIGUSR1, (sighandler_t) resetsig );
+
+ //Create the status file and ping socket
+ //These are called here because the above daemon() call closes
+ //open files.
+ statusfd = fopen("/tmp/qosmon.status","w");
+ s = socket(AF_INET, SOCK_RAW, proto->p_proto);
+
+
+ //Check that things opened correctly.
+ if (DEAMON) {
+ if (statusfd == NULL) {
+ syslog( LOG_CRIT, "Cannot open /tmp/qosmon.status - %i",errno );
+ exit(EXIT_FAILURE);
+ }
+
+ if (s < 0) {
+ syslog( LOG_CRIT, "Cannot open ping socket - %i",errno );
+ exit(EXIT_FAILURE);
+ }
+
+ syslog(LOG_INFO, "starting socketfd = %i, statusfd = %i",s,fileno(statusfd));
+ }
+
+#ifndef ONLYBG
+ else {
+ if (statusfd == NULL) {
+ fprintf(stderr, "Cannot open /tmp/qosmon.status - %i",errno );
+ exit(EXIT_FAILURE);
+ }
+
+ if (s < 0) {
+ fprintf( stderr, "Cannot open ping socket - %i",errno );
+ exit(EXIT_FAILURE);
+ }
+
+ //Ctrl-C terminates
+ signal( SIGINT, (sighandler_t) finish );
+
+ //Close terminal terminates
+ signal( SIGHUP, (sighandler_t) finish );
+
+ setlinebuf( stdout );
+
+ //Bring up ncurses
+ initscr();
+
+ }
+#endif
+
+ //Clear all initial stats.
+ memset((void *)&dnstats,0,sizeof(dnstats));
+
+ //Initialize the max ping to something reasonable.
+ //We will fix it later.
+ rawfltime_max = period*1000;
+
+ while (!sigterm) {
+ int len = sizeof (packet);
+ socklen_t fromlen = sizeof (from);
+ int cc;
+ u_char chill;
+ struct timeval timeout;
+ fd_set fdmask;
+ FD_ZERO(&fdmask);
+ FD_SET(s , &fdmask);
+
+ //rawfltime variable will be set in pr_pack() if we get a pong that matched
+ //our ping, but clearing it here will let us know we did not get a response to our
+ //ping.
+ rawfltime=0;
+
+ //Send the next ping
+ if (pingon) pinger();
+
+ //Wait for the pong(s).
+ timeout.tv_sec = 0;
+ timeout.tv_usec = period*1000;
+
+ //Need a loop here to clean out any old pongs that show up.
+ //select() returns 1 if there is data to read.
+ // 0 if the time has expired.
+ // -1 if a signal arrived.
+ while (sel_err=select(s+1, &fdmask, NULL, NULL, &timeout)) {
+
+ //Signal arrived, just loop and keep waiting.
+ if (sel_err == -1) continue;
+
+ //If we got here then data must be waiting, try to read the whole packet
+ if ( (cc=recvfrom(s,packet,len,0,(struct sockaddr *) &from, &fromlen)) < 0) {
+ continue;
+ }
+
+ //OK there is a whole packet, get it and record the triptime.
+ pr_pack( packet, cc, &from );
+
+ }
+
+ //Gather new statistics
+ classptr=dnstats;
+ cc=classcnt;
+ classcnt=0;
+ errorflg=0;
+ class_list(DEVICE);
+
+ //If there was an error or the number of classes changed then reset everything
+ if (errorflg || (!firstflg && (cc !=classcnt))) {
+
+ if (errorflg) {cnt_errorflg++; last_errorflg=errorflg;}
+ else if (cc != classcnt) cnt_mismatch++;
+
+ firstflg=1;
+ pingon=0;
+ qstate=QMON_CHK;
+ continue;
+ }
+
+
+ //Initialize or reinitialize the fair linklimit.
+ if (resetbw) {
+ saved_realtime_limit=saved_active_limit=new_dbw_ul= DBW_UL * .9;
+ resetbw=0;
+ }
+
+ //Look at an ping response time we got. If we did not get any then it most likely
+ //got dropped so use the maximum value that we have recently seen as we know the downlink
+ //queue must be at least this long.
+ if (!rawfltime) {
+ rawfltime = rawfltime_max/1000;
+ nopingresponse=1;
+ } else
+ nopingresponse=0;
+
+ //Update the filtered ping response time based on what happened.
+ //If we are not pinging then no change in the filtered value.
+ if (pingon)
+ fil_triptime = ((rawfltime*1000 - fil_triptime)*alpha)/1000 + fil_triptime;
+
+ //Run the state machine
+ switch (qstate) {
+
+ // Wait to see if the ping targer will respond at all before doing anything
+ case QMON_CHK:
+ pingon=1;
+
+ //If we get two pings go ahead and lower the link speed.
+ if (nreceived >= 2) {
+
+ //If the pinglimit was entered on the command line
+ //without the add flag then go directly to the
+ //IDLE state otherwise automatically determine an appropriate
+ //ping limit.
+ if ((pinglimit) && !(pingflags & ADDENTITLEMENT)) {
+ dbw_ul=0; //Forces an update in tc_class_modify()
+ tc_class_modify(new_dbw_ul);
+ fil_triptime = rawfltime*1000;
+ qstate=QMON_IDLE;
+ } else {
+ tc_class_modify(1000); //Unload the link for the measurement.
+ nreceived=0;
+ qstate=QMON_INIT;
+ }
+ }
+ break;
+
+ // Take a measurement of the practical ping time we can expect in an unsaturated
+ // link. We do this by making pings and using the filter response after
+ // throttling all traffic in the link.
+ case QMON_INIT:
+ //Filter starts at ten seconds and runs until 15 seconds.
+ //For the first ten seconds we initialize the filter to the last ping time we saw.
+ //After the seventh second we start filtering.
+ if (nreceived < (10000/period)+1) fil_triptime = rawfltime*1000;
+
+ //After 15 seconds we have measured our ping response entitlement.
+ //Move on to the active state.
+ if (nreceived > (15000/period)+1) {
+ qstate=QMON_IDLE;
+ tc_class_modify(new_dbw_ul); //Restore reasonable bandwidth
+
+ //If the user specified no limit then the RTT ping limit is computed from what was
+ //entered on the command line.
+ if (pingflags & ADDENTITLEMENT) {
+ //Add what the user specified to the 110% of the measure ping time.
+ pinglimit += (fil_triptime*1.1);
+ } else {
+ //Without the '-a' flag we just use 200% of measure ping time.
+ //This works OK in my system but I have no evidence that it will work in other systems.
+ pinglimit = fil_triptime*2.0;
+ }
+
+ //Sanity Checks
+ if (pinglimit < 10000) pinglimit=10000;
+ if (pinglimit > 800000) pinglimit=800000;
+
+ //Reasonable max ping.
+ rawfltime_max = 2*pinglimit;
+ }
+ break;
+
+ // In the idle state we have a nearly idle link.
+ // In these cases it is not necessary to monitor delay times so the active
+ // ping is disabled.
+ case QMON_IDLE:
+ pingon=0;
+
+ //Add a hysterisis band when going in/out of IDLE mode.
+ //to try and prevent getting stuck in IDLE mode at the edge of the dynamic range
+ //
+ //We exit idle mode when the link gets above 12% of the upper limit.
+ //We enter idle mode when we get below 10%. (2% hysterisis band).
+ //With this setup at 15% it would be possible to get stuck here since the dynamic limit
+ //can fall as low as 15% which would mean we might not be able to get above 15% to restart the ACTIVE mode.
+ //Hopefully we will always be able to get above 12% at least.
+ if (dbw_fil < 0.12 * DBW_UL) break;
+
+ // In the ACTIVE & REALTIME states we observe ping times as long as the
+ // link remains active. While we are observing we adjust the
+ // link upper limit speed to maintain the specified pinglimit.
+ // If the amount of data we are recieving dies down we enter the WAIT state
+ case QMON_ACTIVE:
+ case QMON_REALTIME:
+ pingon=1;
+
+ //Save the bandwidth limit for each mode.
+ if (qstate == QMON_REALTIME) saved_realtime_limit = new_dbw_ul;
+ if (qstate == QMON_ACTIVE) saved_active_limit = new_dbw_ul;
+
+ //The pinglimit we will use depends on if any realtime classes are active
+ //or not. In realtime mode we only allow 'pinglimit' round trip times which
+ //makes our pings low but also lowers our throughput. The automatic measurement
+ //above set pinglimit to the average RTT of the ping assuming it has to wait on
+ //average for 2/3 of an single MTU sized packet to transmit. The means on
+ //average there is nothing in the buffer but a packet is transmitting.
+
+ //When not in realtime mode the stradegy is that we allow enough packets in the queue
+ //to fully utilize the downlink.
+
+ //We are talking about a queue controlled by the ISP so we don't know much about it.
+ //We make an assumption that the queue is long enough to allow full utilization of the link.
+ //This should be the case and often the queue is much longer than needed (bufferbloat).
+ //When not in realtime mode we can allow this buffer to fill but we don't want it to overflow
+ //because it will then drop packets which will cause our QoS to breakdown. So we want it to fill
+ //just enough to promote full link utilization.
+
+ //The classical optimum queue size would be equal to the bandwidth * RTT and the
+ //additional time it will take our ping to pass through such a queue turns out to be the RTT.
+ //But Barman et all, Globecomm2004 indicates that only 20-30% of this is really needed.
+ //
+ //When we measured an RTT above that it was to the ISPs gateway so we do not really know what the average
+ //RTT time to other IPs on the internet. And since not all hosts respond the same anyway I doubt there
+ //is consistant RTT that we could use.
+ //
+ //For ACTIVE mode on a 925kbps/450kbps link I measured the following
+ //relationship between ping limit and throughput with large packets downloading.
+ //
+ //Ping Limit Throughput Percent
+ // 612ms 918kbps 100
+ // 525ms 915kbps 99.6
+ // 437ms 898kbps 97.8
+ // 350ms 875kbps 95.3
+ // 262ms 862kbps 93.8
+ // 81ms 870kbps 94.7
+ // 60ms 680kbps 69.8
+ // 50ms 630kbps 68.6
+ // 40ms 490kbps 53.3
+ //
+ //The 1500 byte packet time is 1500*10/925kbps download and 1500*10/425kbps upload for a total
+ //RTT of around 48ms. Idle ping times on this link are around 35ms.
+ //
+ //These results indicate that on my link not much is gained by increasing beyond 81ms. This is pretty much
+ //the MINRTT mode computed with the -a switch. Still other links may be different so I suspect that
+ //switching to active mode will benefit some people.
+ //
+ //The statedgy I will use for the ACTIVE mode limit will be to add an additional 135% packet delay over
+ //what we have in RTT mode. The packet delay was entered on the command line or zero if nothing was entered.
+
+ //I hope that this will work well for a broad range of users from satellite links with RTTs of 1 second or more
+ //to users with hot connections that have small queues upstream of them.
+
+ if ((RTDCA == 0) && (pingflags & ADDENTITLEMENT)) {
+ plimit=135*pinglimit_cl/100+pinglimit;
+
+ //When switching into active mode for the first time initialize the bandwidth
+ //limit to the last value that was known to work.
+ if (qstate != QMON_ACTIVE) {
+ qstate=QMON_ACTIVE;
+ new_dbw_ul=saved_active_limit;
+ tc_class_modify(new_dbw_ul);
+ }
+
+ } else {
+ plimit = pinglimit;
+
+ //When switching into realtime mode for the first time initialize the bandwidth
+ //limit to the last value that was known to work.
+ if (qstate != QMON_REALTIME) {
+ qstate=QMON_REALTIME;
+ new_dbw_ul=saved_realtime_limit;
+ tc_class_modify(new_dbw_ul);
+ }
+
+ }
+
+ //When the downlink falls below 10% utilization we turn off the pinger.
+ if (dbw_fil < 0.1 * DBW_UL) qstate=QMON_IDLE;
+
+ //Compute the ping error
+ err = fil_triptime - plimit;
+
+ //Negative error means we might be able to increase the link limit.
+ if (err < 0) {
+
+ //Do not increase the bandwidth until we reach 85% of the current limit.
+ if (dbw_fil < dbw_ul * 0.85) break;
+
+ //Increase slowly (0.4%/sec). err is negative here.
+ new_dbw_ul = new_dbw_ul * (1.0 - 0.004*err*(float)period/(float)plimit/1000.0);
+ if (new_dbw_ul > DBW_UL) new_dbw_ul=DBW_UL;
+
+ } else {
+ //Positive error means we need to decrease the bandwidth.
+
+ new_dbw_ul = new_dbw_ul * (1.0 - 0.004*err*(float)period/(float)plimit/1000.0);
+
+ //Dynamic range is 1/.15 or 6.67 : 1.
+ if (new_dbw_ul < DBW_UL*.15) new_dbw_ul=DBW_UL*.15;
+ }
+
+ //Modify parent download limit as needed.
+ tc_class_modify(new_dbw_ul);
+
+ //Keep downward pressure on rawfltime_max to keep it fresh.
+ if (rawfltime_max > plimit) rawfltime_max -= 100;
+
+ break;
+
+
+ }
+
+ update_status(statusfd);
+
+ //If we get here the first pass is over.
+ firstflg=0;
+
+ } //Next ping
+
+
+ qstate=QMON_EXIT;
+
+ //We got a signal to terminate so start by restoring the root TC class to
+ //the original upper limit.
+ tc_class_modify(DBW_UL);
+
+ update_status(statusfd);
+
+ //Write a message in the system log
+ if (DEAMON) {
+ syslog( LOG_NOTICE, "terminated sigterm=%i, sel_err=%i", sigterm, sel_err );
+ closelog();
+ }
+
+#ifndef ONLYBG
+ else {
+ endwin();
+ fflush(stdout);
+ }
+#endif
+
+}
+
diff --git a/package/network/utils/iptables/Makefile b/package/network/utils/iptables/Makefile
index 2ec3f1118d..e3016d734c 100644
--- a/package/network/utils/iptables/Makefile
+++ b/package/network/utils/iptables/Makefile
@@ -187,6 +187,14 @@ $(call Package/iptables/Module, +kmod-ipt-imq)
TITLE:=IMQ support
endef
+define Package/iptables-mod-imq/description
+iptables extension for IMQ support.
+
+ Targets:
+ - IMQ
+
+endef
+
define Package/iptables-mod-bandwidth
$(call Package/iptables/Module, +kmod-ipt-bandwidth)
TITLE:=bandwidth option extensions
@@ -207,14 +215,6 @@ $(call Package/iptables/Module, +kmod-ipt-weburl)
TITLE:=weburl option extensions
endef
-define Package/iptables-mod-imq/description
-iptables extension for IMQ support.
-
- Targets:
- - IMQ
-
-endef
-
define Package/iptables-mod-ipopt
$(call Package/iptables/Module, +kmod-ipt-ipopt)
TITLE:=IP/Packet option extensions