diff --git a/package/ctcgfw/qos-gargoyle/Makefile b/package/ctcgfw/qos-gargoyle/Makefile deleted file mode 100644 index 2bedade23f..0000000000 --- a/package/ctcgfw/qos-gargoyle/Makefile +++ /dev/null @@ -1,68 +0,0 @@ -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 deleted file mode 100644 index e248b6b255..0000000000 --- a/package/ctcgfw/qos-gargoyle/files/example-qos-config2.conf +++ /dev/null @@ -1,119 +0,0 @@ - -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 deleted file mode 100644 index c102193856..0000000000 --- a/package/ctcgfw/qos-gargoyle/files/qos_gargoyle.conf +++ /dev/null @@ -1,27 +0,0 @@ - -config global 'global' - -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 deleted file mode 100644 index 39e491269a..0000000000 --- a/package/ctcgfw/qos-gargoyle/files/qos_gargoyle.hotplug +++ /dev/null @@ -1,12 +0,0 @@ -#!/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 deleted file mode 100755 index 3a79481918..0000000000 --- a/package/ctcgfw/qos-gargoyle/files/qos_gargoyle.init +++ /dev/null @@ -1,868 +0,0 @@ -#!/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 deleted file mode 100644 index 88aca6022d..0000000000 --- a/package/ctcgfw/qos-gargoyle/src/Makefile +++ /dev/null @@ -1,36 +0,0 @@ -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 ; ) -TCDIR:=$(STAGING_DIR)/usr/lib/iproute2 -#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 deleted file mode 100644 index 1d112c83d6..0000000000 --- a/package/ctcgfw/qos-gargoyle/src/qosmon.c +++ /dev/null @@ -1,1256 +0,0 @@ -/* -*- 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 - -} -