Guest User

Untitled

a guest
Oct 21st, 2015
473
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Bash 26.33 KB | None | 0 0
  1. #!/bin/sh /etc/rc.common
  2. #
  3. # Copyright Eric Bishop, 2008
  4. # This is free software licensed under the terms of the GNU GPL v2.0
  5. #
  6.  
  7. START=50
  8.  
  9. EXTRA_COMMANDS=show
  10. EXTRA_HELP="    show    Show current Qos configuration (if active)"
  11.  
  12. include /lib/network
  13. include /usr/lib/gargoyle_firewall_util
  14.  
  15. config_file_name="qos_gargoyle"
  16. upload_mask="0x007F"
  17. download_mask="0x7F00"
  18. qos_mark_file="/etc/qos_class_marks"
  19.  
  20. #created while qos is being initialized so hotplug and init script don't
  21. #both try to initialize qos at the same time
  22. lock_file="/var/run/qos_updating"
  23.  
  24. load_all_config_options()
  25. {
  26.     local config_name="$1"
  27.     local section_id="$2"
  28.  
  29.     ALL_OPTION_VARIABLES=""
  30.     # this callback loads all the variables
  31.     # in the section_id section when we do
  32.     # config_load. We need to redefine
  33.     # the option_cb for different sections
  34.     # so that the active one isn't still active
  35.     # after we're done with it.  For reference
  36.     # the $1 variable is the name of the option
  37.     # and $2 is the name of the section
  38.     config_cb()
  39.     {
  40.         if [ ."$2" = ."$section_id" ]; then
  41.             option_cb()
  42.             {
  43.                 ALL_OPTION_VARIABLES="$ALL_OPTION_VARIABLES $1"
  44.             }
  45.         else
  46.             option_cb() { return 0; }
  47.         fi
  48.     }
  49.  
  50.     config_load "$config_name"
  51.  
  52.     for var in $ALL_OPTION_VARIABLES
  53.     do
  54.         config_get "$var" "$section_id" "$var"
  55.     done
  56. }
  57.  
  58. load_all_config_sections()
  59. {
  60.     local config_name="$1"
  61.     local section_type="$2"
  62.  
  63.     all_config_sections=""
  64.     section_order=""
  65.     config_cb()
  66.     {
  67.         if [ -n "$2" ] || [ -n "$1" ] ; then
  68.             if [ -n "$section_type" ] ; then
  69.                 if [ "$1" = "$section_type" ] ; then
  70.                     all_config_sections="$all_config_sections $2"
  71.                 fi
  72.             else
  73.                 all_config_sections="$all_config_sections $2"
  74.             fi
  75.         fi
  76.     }
  77.  
  78.     config_load "$config_name"
  79.     echo "$all_config_sections"
  80.  
  81. }
  82.  
  83.  
  84.  
  85.  
  86. load_and_sort_all_config_sections()
  87. {
  88.     local config_name="$1"
  89.     local section_type="$2"
  90.     local sort_variable="$3"
  91.  
  92.     all_config_sections=""
  93.     defined_option_cb()
  94.     {
  95.         if [ "$1" = "$sort_variable" ]; then
  96.             all_config_sections=" $2:$all_config_sections"
  97.         fi
  98.     }
  99.  
  100.     config_cb()
  101.     {
  102.         if [ -n "$2" ] || [ -n "$1" ] ; then
  103.             if [ -n "$section_type" ] ; then
  104.                 if [ "$1" = "$section_type" ] ; then
  105.                     all_config_sections="$2 $all_config_sections"
  106.                     option_cb() { defined_option_cb $1 $2 ; }
  107.                 else
  108.                     option_cb() { return 0; }
  109.                 fi
  110.             else
  111.                 all_config_sections="$2 $all_config_sections"
  112.                 option_cb(){ defined_option_cb $1 $2 ; }
  113.             fi
  114.         fi
  115.     }
  116.  
  117.     config_load "$config_name"
  118.  
  119.     echo "$all_config_sections" | awk ' {for(i=1; i <= NF; i++){ print $i }}' | sort -n -t ":" | awk 'BEGIN {FS=":"}; {print $2}'
  120. }
  121.  
  122.  
  123. get_classname_mark()
  124. {
  125.     local class="$1"
  126.     local class_mark_list="$2"
  127.     echo "$class_mark_list" | awk -v class="$class" '{ for (i = 1; i <= NF; i++){  if($i~class":"){ gsub(class":",""); print $i } }}'
  128. }
  129.  
  130. apply_all_rules()
  131. {
  132.     local rule_type="$1"
  133.     local class_mark_list="$2"
  134.     local chain="$3"
  135.     local table="$4"
  136.  
  137.     local need_proto
  138.     local tmp_proto
  139.  
  140.     # add filter rules
  141.     rule_list=$(load_and_sort_all_config_sections "$config_file_name" "$rule_type" "test_order")
  142.     for rule in $rule_list ; do
  143.         class=""
  144.         proto=""
  145.         min_pkt_size=""
  146.         max_pkt_size=""
  147.         match_str=""
  148.         need_proto=""
  149.  
  150.         load_all_config_options "$config_file_name" "$rule"
  151.  
  152.         for option in $ALL_OPTION_VARIABLES ; do
  153.  
  154.             option_value=$(eval echo \$$option)
  155.             case "$option" in
  156.                 source)
  157.                     if [ "$3" = "qos_egress" ] ; then
  158.                         if [ "$option_value" = "$local_ip" ] || [ "$option_value" = "$wan_ip" ]; then
  159.                             option_value="$wan_ip"
  160.                         fi
  161.                     fi
  162.                     match_str="$match_str -s $option_value"
  163.                 ;;
  164.                 destination)
  165.                     if [ "$3" = "qos_ingress" ] ; then
  166.                         if [ "$option_value" = "$local_ip" ] || [ "$option_value" = "$wan_ip" ]; then
  167.                             option_value="$wan_ip"
  168.                         fi
  169.                     fi
  170.                     match_str="$match_str -d $option_value"
  171.                 ;;
  172.                 srcport)
  173.                     if [ -n $(echo $option_value | grep "-") ] ; then option_value="$(echo "$option_value" | sed -e 's,-,:,g')" ; fi
  174.                     match_str="$match_str --sport $option_value"
  175.                     need_proto="1"
  176.                 ;;
  177.                 dstport)
  178.                     if [ -n $(echo $option_value | grep "-") ] ; then option_value="$(echo "$option_value" | sed -e 's,-,:,g')" ; fi
  179.                     match_str="$match_str --dport $option_value"
  180.                     need_proto="1"
  181.  
  182.                 ;;
  183.                 layer7)
  184.                     layer7_connmark=$(cat /etc/l7marker.marks 2>/dev/null | grep "$option_value" | awk '{ print $2 }')
  185.                     layer7_mask=$(cat /etc/l7marker.marks 2>/dev/null | grep "$option_value" | awk '{ print $3 }')
  186.                     if [ -n "$layer7_connmark" ] ; then
  187.                         match_str="$match_str -m connmark --mark $layer7_connmark/$layer7_mask "
  188.                     else
  189.                         match_str="$match_str -m layer7 --l7proto $option_value"
  190.                     fi
  191.                 ;;
  192.                 connbytes_kb)
  193.                     match_str="$match_str -m connbytes --connbytes $(($option_value*1024)): --connbytes-dir both --connbytes-mode bytes"
  194.                 ;;
  195.             esac
  196.         done
  197.  
  198.  
  199.         if [ -n "$min_pkt_size" ] || [ -n "$max_pkt_size" ] ; then
  200.             if [ -z "$min_pkt_size" ] ; then min_pkt_size=0 ; fi
  201.             if [ -z "$max_pkt_size" ] ; then max_pkt_size=1500 ; fi
  202.             match_str="$match_str -m length --length $min_pkt_size:$max_pkt_size"
  203.         fi
  204.  
  205.         if [ -n "$class" ] ; then
  206.             if [ -n "$proto" ] || [ -n "$match_str" ] ; then
  207.                 next_mark=$(get_classname_mark "$class" "$class_mark_list" )
  208.  
  209.                 #We need to specify both udp and tcp if the user indicated a port
  210.                 #and he did not indiate a protocal.
  211.                 if [ -z "$proto" ] && [ -n "$need_proto" ] ; then
  212.  
  213.                     $echo_on
  214.                     iptables -t $table -I $chain -p tcp $match_str -j MARK --set-mark $next_mark
  215.                     iptables -t $table -I $chain -p udp $match_str -j MARK --set-mark $next_mark
  216.                     $echo_off
  217.  
  218.                 else
  219.  
  220.                     #Otherwise just specify what the user requested (or nothing)
  221.  
  222.                     if [ -n "$proto" ] ; then
  223.                         tmp_proto="-p $proto"
  224.                     else
  225.                         tmp_proto=""
  226.                     fi
  227.  
  228.                     $echo_on
  229.                     iptables -t $table -I $chain $tmp_proto $match_str -j MARK --set-mark $next_mark
  230.                     $echo_off
  231.                 fi
  232.  
  233.             fi
  234.         fi
  235.     done
  236. }
  237.  
  238.  
  239. update_markfile()
  240. {
  241.  
  242.     #initialize mark file in /tmp first, and test md5sum
  243.     #this should speed things up and prevent writing to flash unnecessarily (since /tmp is ramdisk)
  244.  
  245.     tmp_qos_mark_file="/tmp/qos_marks.tmp.tmp"
  246.     rm -rf "$tmp_qos_mark_file"
  247.  
  248.     #re-populate per the QoS setup.
  249.     if [ $total_upload_bandwidth -ge 0 ] ; then
  250.  
  251.         upload_class_list=$(load_all_config_sections "$config_file_name" "upload_class")
  252.  
  253.         next_class_index=2
  254.         for uclass_name in $upload_class_list ; do
  255.             printf "upload $uclass_name %d $upload_mask\n" $next_class_index >> "$tmp_qos_mark_file"
  256.             next_class_index=$(($next_class_index+1))
  257.         done
  258.     fi
  259.  
  260.     if [ $total_download_bandwidth -ge 0 ] ; then
  261.  
  262.         download_class_list=$(load_all_config_sections "$config_file_name" "download_class")
  263.  
  264.         next_class_index=2
  265.         for dclass_name in $download_class_list ; do
  266.             printf "download $dclass_name %d $download_mask\n" $(($next_class_index << 8)) >> "$tmp_qos_mark_file"
  267.             next_class_index=$(($next_class_index+1))
  268.         done
  269.     fi
  270.  
  271.     mark_files_match="0"
  272.     if [ -e "$qos_mark_file" ] ; then
  273.         new_md5=$(md5sum "$tmp_qos_mark_file" | awk '{ print $1 ; } ')
  274.         old_md5=$(md5sum "$qos_mark_file" | awk '{ print $1 ; } ')
  275.         if [ "$new_md5" = "$old_md5" ] ; then
  276.             mark_files_match="1"
  277.         fi
  278.     fi
  279.  
  280.     if [ "$mark_files_match" = "0" ] ; then
  281.         mv "$tmp_qos_mark_file" "$qos_mark_file"
  282.     else
  283.         rm -rf "$tmp_qos_mark_file"
  284.     fi
  285.  
  286. }
  287.  
  288.  
  289.  
  290. initialize_qos()
  291. {
  292.     #initialize layer7_marker if necessary
  293.     create_l7marker_chain
  294.  
  295.     # Now, load/insert necessary kernel modules
  296.     # The following packages are required for the modules:
  297.     rmmod  imq >&- 2>&-
  298.     insmod imq numdevs=1 hook_chains="INPUT,FORWARD" hook_tables="mangle,mangle" >&- 2>&-
  299.     ip link set dev imq0 mtu 1500
  300.  
  301.     insmod cls_fw >&- 2>&-
  302.     insmod cls_flow >&- 2>&-
  303.     insmod sch_hfsc >&- 2>&-
  304.     #insmod sch_sfq >&- 2>&-
  305.     insmod sch_fq_codel >&- 2>&-
  306.  
  307.     #Set the atm parameters if pppoe is being used.
  308.     wan_proto=$(uci get network.wan.proto 2>/dev/null)
  309.     if [ "$wan_proto" = "pppoe" ] ; then
  310.         #On my PPPoE WAN link I see the following overhead
  311.         #MAC Header=14 & PPPoE=8 plus I read that an addition ATM=8 is added by the modem"
  312.         overhead="stab linklayer atm overhead 30 mtu 1492 tsize 128 "
  313.     #else
  314.         #Even for the ethernet cases I do not think the qdisc sees the MAC Header
  315.         #but for now leave this out.
  316.         #overhead="stab linklayer ethernet overhead 14 mpu 64"
  317.     fi
  318.  
  319.     #On low memory routers we need to take it easy on how big the queues can get.
  320.     #When depth is limited to 32 maximum bandwidth through any class will be around 11Mbps.
  321.     #Otherwise it will be around 350Mbps.
  322.     total_mem="$(sed -e '/^MemTotal: /!d; s#MemTotal: *##; s# kB##g' /proc/meminfo)"
  323.     if [ "$total_mem" -lt 16000 ] ; then
  324.         sfq_depth="depth 32";
  325.     else
  326.         sfq_depth="";
  327.     fi
  328.  
  329.     #load upload variables
  330.     load_all_config_options "$config_file_name" "upload"
  331.     if [ -n "$total_bandwidth" ] ; then
  332.         total_upload_bandwidth="$total_bandwidth"
  333.     else
  334.         total_upload_bandwidth=-1
  335.     fi
  336.    
  337.     upload_default_class="$default_class"
  338.  
  339.     if [ $total_upload_bandwidth -ge 0 ] ; then
  340.  
  341.         #load upload classes
  342.         upload_class_list=$(load_all_config_sections "$config_file_name" "upload_class")
  343.         for uclass_name in $upload_class_list ; do
  344.             percent_bandwidth=""
  345.             min_bandwidth=""
  346.             max_bandwidth=""
  347.             load_all_config_options "$config_file_name" "$uclass_name"
  348.             if [ -z "$percent_bandwidth" ] ; then
  349.                 percent_bandwidth="0"
  350.             fi
  351.             if [ -z "$min_bandwidth" ] ; then
  352.                 min_bandwidth="-1"
  353.             fi
  354.             if [ -z "$max_bandwidth" ] ; then
  355.                 max_bandwidth="-1"
  356.             fi
  357.             classdef="$percent_bandwidth $max_bandwidth $min_bandwidth"
  358.             eval $uclass_name=\"\$classdef\"  #"#comment quote here so formatting in editor isn't FUBAR
  359.         done
  360.  
  361.  
  362.         # Attach egress queuing discipline to QoS interface, now with temperary default
  363.         $echo_on
  364.         tc qdisc add dev $qos_interface root handle 1:0 hfsc default 1
  365.  
  366.         # For the root qdisc, only ul is relevant, since there is no link sharing, and rt only applies to leaf qdiscs
  367.         #
  368.         # A detailed explanation of how/why ls is being set is warranted here...
  369.         # This is being set to max bandwidth possible on a connection (right now that's about 1Gbit, we can increase later if necessary)
  370.         # Only ratio of child ls rates is important -- bounding is done by the ul parameter, since hfsc takes min of ls and ul
  371.         # This means we can just multiply percents by 10 in child nodes to get what ls curves for each should be
  372.         # Again, for ls the ratios matter, but the absolute values do not, provided that they are all < than the ul for either the
  373.         # child or any of the parent nodes
  374.         #
  375.         tc class add dev $qos_interface parent 1:0 classid 1:1 hfsc sc rate ${total_upload_bandwidth}kbit ul rate ${total_upload_bandwidth}kbit
  376.         $echo_off
  377.  
  378.         class_mark_list=""
  379.         upload_shift=0
  380.         next_class_index=2
  381.         next_classid=$(printf "0x%X" $(($next_class_index << $upload_shift)) )
  382.         def_upload_idx=$next_class_index
  383.         def_upload_class=$next_classid
  384.  
  385.         for uclass_name in $upload_class_list ; do
  386.  
  387.             class_mark_list="$class_mark_list$uclass_name:$next_classid "
  388.  
  389.             uclass_def=$(eval echo "\$$uclass_name")
  390.  
  391.             #% bandwidth at capacity for this class
  392.             #m2=$((  10 * $(echo $uclass_def | awk ' {print $1}' ) ))
  393.             bandwidth_percent=$(echo $uclass_def | awk ' {print $1}' )
  394.             bandwidth_from_percent=$(( total_upload_bandwidth * bandwidth_percent / 100 ))
  395.  
  396.             #is there a minimum bandwidth specified in kbps?
  397.             min_bandwidth=$( echo $uclass_def | awk ' {print $3}' )
  398.             ll_str=""
  399.             if [ "$min_bandwidth" -gt 0 ] ; then
  400.                 ll_str=" rt m1 $((2*$min_bandwidth))kbit d 20ms m2 ${min_bandwidth}kbit"
  401.             fi
  402.  
  403.             #is there an upper limit specified in kbps?
  404.             max_bandwidth=$( echo $uclass_def | awk ' {print $2}' )
  405.             ul_str=""
  406.             if [ "$max_bandwidth" -ge 0 ] ; then
  407.                 ul_str=" ul m2 ${max_bandwidth}kbit"
  408.             else
  409.                 max_bandwidth="$total_upload_bandwidth"
  410.             fi
  411.  
  412.             #tbw is the Delay x Bandwidth product in bytes per second.  We do not actually know the packet
  413.             #delay so we make an estimate of 150ms here and hope for the best.  max_bandwidth is in kbps
  414.             #we multiply 150ms by 1000 below so the units work out.        
  415.             tbw=$(($max_bandwidth*150/8));
  416.             if [ "$tbw" -lt 3000 ] ; then
  417.                 tbw=3000
  418.             fi
  419.  
  420.             #We will use the SFQ qdisc with flow classifier.  The limit on the depth of our qdisc depends on the upper limit
  421.             #of the bandwidth allocated to this class.  To impliment per IP sharing of the class we use the flow classifier
  422.             #and the 'nfct-src' on the upload side and 'dst' on the download side.  I found a nice man page here
  423.             #https://arndtroide.homelinux.org/cgi-bin/man/man2html?tc-sfq+8
  424.             $echo_on
  425.            
  426.             #Add the leaf class
  427.             tc class add dev $qos_interface parent 1:1 classid 1:$next_class_index hfsc ls m2 ${bandwidth_from_percent}kbit $ll_str $ul_str
  428.             #Add the qdisc to the leaf class, assuming average packet at 500 bytes.
  429.             tc qdisc add dev $qos_interface parent 1:$next_class_index handle $next_class_index:1 fq_codel limit 1001 ecn quantum 300 flows 1024  
  430.             #Add a filter to the root class to direct packets to this leaf class according to the conntrack mark
  431.             tc filter add dev $qos_interface parent 1:0 protocol ip handle $next_classid fw flowid 1:$next_class_index
  432.             #Add a filter to the leaf class to define flows as being the source IP address.
  433.             tc filter add dev $qos_interface parent $next_class_index: handle 1 flow divisor 1024 map key nfct-src and 0xff
  434.             $echo_off
  435.  
  436.             if [ "$upload_default_class" = "$uclass_name" ] ; then
  437.                 def_upload_idx=$next_class_index
  438.                 def_upload_class=$next_classid
  439.             fi
  440.  
  441.             next_class_index=$(($next_class_index+1))
  442.             next_classid=$(printf "0x%X" $(($next_class_index << $upload_shift)) )
  443.         done
  444.  
  445.         $echo_on
  446.  
  447.         #Go back and touch up the root qdisc to have the proper default class
  448.         tc qdisc change dev $qos_interface $overhead root handle 1:0 hfsc default $def_upload_idx
  449.  
  450.         # Set up egress chain
  451.         iptables -t mangle -N qos_egress
  452.         iptables -t mangle -A POSTROUTING -o $qos_interface -j qos_egress
  453.  
  454.         #Next the user entered rules.
  455.         $echo_off
  456.         apply_all_rules "upload_rule" "$class_mark_list" "qos_egress" "mangle"
  457.         $echo_on
  458.  
  459.         #set default class mark first in case we don't match anything
  460.         iptables -t mangle -I qos_egress -j MARK --set-mark $def_upload_class
  461.  
  462.         #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
  463.         iptables -t mangle -I qos_egress -m mark ! --mark 0x0 -j RETURN
  464.         iptables -t mangle -I qos_egress -m mark ! --mark 0x0 -j CONNMARK --save-mark --mask $upload_mask
  465.  
  466.         # save current mark to connmark at end of chain
  467.         iptables -t mangle -A qos_egress -j CONNMARK --save-mark --mask $upload_mask
  468.  
  469.         $echo_off
  470.  
  471.     fi
  472.  
  473.  
  474.     #load download variables
  475.     total_bandwidth=""
  476.     default_class=""
  477.     load_all_config_options "$config_file_name" "download"
  478.     if [ -n "$total_bandwidth" ] ; then
  479.         total_download_bandwidth="$total_bandwidth"
  480.     else
  481.         total_download_bandwidth=-1
  482.     fi
  483.     download_default_class="$default_class"
  484.  
  485.     #Only if both upload and download QoS are enabled can we enable Gargoyle active QoS monitor
  486.     if [ $total_download_bandwidth -eq 0 ] || [ $total_upload_bandwidth -eq 0 ] ; then
  487.         qos_monenabled = "false" ;
  488.     fi
  489.  
  490.     if [ $total_download_bandwidth -ge 0 ] ; then
  491.         # Set up the InterMediate Queuing device (IMQ)
  492.         ip link set imq0 up
  493.  
  494.         # Attach ingress queuing discipline to IMQ interface
  495.         tc qdisc add dev imq0 root handle 1:0 hfsc default 1
  496.  
  497.         tc class add dev imq0 parent 1:0 classid 1:1 hfsc sc rate ${total_download_bandwidth}kbit ul rate ${total_download_bandwidth}kbit
  498.  
  499.         #load download classes
  500.         download_class_list=$(load_all_config_sections "$config_file_name" "download_class")
  501.         for dclass_name in $download_class_list ; do
  502.             percent_bandwidth=""
  503.             min_bandwidth=""
  504.             max_bandwidth=""
  505.             minRTT=""
  506.  
  507.             load_all_config_options "$config_file_name" "$dclass_name"
  508.             if [ -z "$percent_bandwidth" ] ; then
  509.                 percent_bandwidth="0"
  510.             fi
  511.             if [ -z "$min_bandwidth" ] ; then
  512.                 min_bandwidth="-1"
  513.             fi
  514.             if [ -z "$max_bandwidth" ] ; then
  515.                 max_bandwidth="-1"
  516.             fi
  517.            
  518.             classdef="$percent_bandwidth $max_bandwidth $min_bandwidth $minRTT"
  519.             eval $dclass_name=\"\$classdef\"  #"#comment quote here so formatting in editor isn't FUBAR
  520.         done
  521.  
  522.  
  523.         class_mark_list=""
  524.         download_shift=8
  525.         next_class_index=2
  526.         next_classid=$(printf "0x%X" $(($next_class_index << $download_shift)) )
  527.         def_download_idx=$next_class_index
  528.         def_download_class=$next_classid
  529.  
  530.         for dclass_name in $download_class_list ; do
  531.  
  532.  
  533.             class_mark_list="$class_mark_list$dclass_name:$next_classid  "
  534.             dclass_def=$(eval echo "\$$dclass_name")
  535.  
  536.             #bandwidth for this class
  537.             #m2=$(( 10 * $(echo $dclass_def | awk ' {print $1}' ) ))
  538.             bandwidth_percent=$(echo $dclass_def | awk ' {print $1}' )
  539.             bandwidth_from_percent=$(( total_download_bandwidth * bandwidth_percent / 100 ))
  540.  
  541.             #The Gargoyle ACC switches from optimum WAN utilization mode to minimum RTT mode
  542.             #when it detects a class has become active that includes a two part service curve.
  543.             #So to trigger this behaviour we create two parts curves when minRTT is set.
  544.            
  545.             minRTT=$( echo $dclass_def | awk ' {print $4}' )
  546.             if [ "$minRTT" == "Yes" ] ; then
  547.                 ll_str=" ls m1 ${bandwidth_from_percent}kbit d 20ms m2 ${bandwidth_from_percent}kbit"
  548.             else
  549.                 ll_str=" ls m2 ${bandwidth_from_percent}kbit"
  550.             fi
  551.  
  552.             #is there a minimum bandwidth specified?
  553.             min_bandwidth=$( echo $dclass_def | awk ' {print $3}' )
  554.             rt_str=""
  555.             if [ "$min_bandwidth" -gt 0 ] ; then
  556.                 if [ "$minRTT" == "Yes" ] ; then
  557.                     rt_str=" rt m1 $((2*$min_bandwidth))kbit d 20ms m2 ${min_bandwidth}kbit"
  558.                 else
  559.                     rt_str=" rt m2 ${min_bandwidth}kbit"
  560.                 fi
  561.             fi
  562.  
  563.             #is there an upper limit specified?
  564.             max_bandwidth=$( echo $dclass_def | awk ' {print $2}' )
  565.             ul_str=""
  566.             if [ "$max_bandwidth" -ge 0 ] ; then
  567.                 ul_str=" ul m2 ${max_bandwidth}kbit"
  568.             else
  569.                 max_bandwidth="$total_download_bandwidth"
  570.             fi
  571.  
  572.             tbw=$(($max_bandwidth*150/8));
  573.             if [ "$tbw" -lt 5000 ] ; then
  574.                 tbw=5000
  575.             fi
  576.  
  577.             $echo_on
  578.             tc class add dev imq0 parent 1:1 classid 1:$next_class_index hfsc $rt_str $ll_str $ul_str
  579.             #Assume average download packet size is 1000 bytes.
  580.             tc qdisc add dev imq0 parent 1:$next_class_index handle $next_class_index:1 fq_codel limit 1001 ecn quantum 1500 flows 1024  
  581.             tc filter add dev imq0 parent 1:0 prio $next_class_index protocol ip handle $next_classid fw flowid 1:$next_class_index
  582.             tc filter add dev imq0 parent $next_class_index: handle 1 flow divisor 1024 map key dst and 0xff
  583.             $echo_off
  584.  
  585.             if [ "$download_default_class" = "$dclass_name" ] ; then
  586.                 def_download_idx=$next_class_index
  587.                 def_download_class=$next_classid
  588.             fi
  589.  
  590.             next_class_index=$(($next_class_index+1))
  591.             next_classid=$(printf "0x%X" $(($next_class_index << $download_shift)) )
  592.         done
  593.         $echo_on
  594.  
  595.         #Go back and touch up the root qdisc to have the proper default class
  596.         tc qdisc change dev imq0 $overhead root handle 1:0 hfsc default $def_download_idx
  597.  
  598.         # Create ingress chain
  599.         iptables -t mangle -N qos_ingress
  600.  
  601.         # Mark ingress in FORWARD and INPUT chains to make sure any DNAT (virt. server) is taken into account
  602.         iptables -t mangle -A FORWARD -i $qos_interface -j qos_ingress
  603.         iptables -t mangle -A INPUT -i $qos_interface -j qos_ingress
  604.  
  605.         #Now the rest of the user entered rules.
  606.         $echo_off
  607.         apply_all_rules "download_rule" "$class_mark_list" "qos_ingress" "mangle" "$download_mask"
  608.         $echo_on
  609.  
  610.         #set default class mark first in case we don't match anything
  611.         iptables -t mangle -I qos_ingress -j MARK --set-mark $def_download_class
  612.  
  613.         #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
  614.         iptables -t mangle -I qos_ingress -m mark ! --mark 0x0 -j RETURN
  615.         iptables -t mangle -I qos_ingress -m mark ! --mark 0x0 -j CONNMARK --save-mark --mask $download_mask
  616.  
  617.         #make sure all packets get sent through IMQ
  618.         iptables -t mangle -I qos_ingress -j IMQ --todev 0
  619.  
  620.         #save current mark to connmark at end of chain
  621.         iptables -t mangle -A qos_ingress -j CONNMARK --save-mark --mask $download_mask
  622.  
  623.         $echo_off
  624.  
  625.     fi
  626.  
  627.     #Enable Gargoyle active QoS monitor
  628.     if [ $total_upload_bandwidth -ge 0 ] && [ $total_download_bandwidth -ge 0 ] && [ "$qos_monenabled" = "true" ] ; then
  629.  
  630.         $echo_on
  631.  
  632.         #if the user specified a ping target then use that otherwise use the gateway.
  633.         if [ -z "$ptarget_ip" ] ; then
  634.             old_ifs="$IFS"
  635.             IFS=$(printf "\n\r")
  636.             targets=$(traceroute -n -I -w 1 -q 2 -m6 100.100.100.100 | 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')
  637.             ptarget_ip=""
  638.             for t in $targets ; do
  639.                 if [ -z "$ptarget_ip" ] ; then
  640.                     #ip of potential gateway
  641.                     target=$(echo "$t" | awk '{ print $1 ; }')
  642.                     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\.)')
  643.  
  644.  
  645.                     # round or rather ceil() time up to nearest millisecond since bash doesn't like working with decimals
  646.                     time=$(echo "$t" | awk ' { print $3 ; }' | sed 's/\..*$//g')
  647.                     time=$(( $time + 1 ))
  648.    
  649.                     if [ -z "$target_is_local" ] || [ "$time" -gt 5 ] ; then
  650.                         ptarget_ip="$target"
  651.                     fi
  652.                 fi
  653.             done
  654.             IFS="$old_ifs"
  655.            
  656.             #in case ping target is still not defined use default gateway
  657.             if [ -z "$ptarget_ip" ] ; then
  658.                 ptarget_ip=$(gargoyle_header_footer -i gargoyle | sed -n 's/.*currentWanGateway.*"\(.*\)".*/\1/p')
  659.             fi
  660.         fi
  661.  
  662.  
  663.        
  664.         #Ping responses for the ping target never go to the IMQ device.
  665.         iptables -t mangle -I qos_ingress -p icmp --icmp-type 0 -d $wan_ip -s $ptarget_ip -j RETURN
  666.  
  667.         #Make a class to handle the outgoing ping requests from the router.
  668.         #These pings 84 bytes each for ethernet plus 22 more for PPPoE connections, allowing a maximum rate of 200ms we get 2.6kbps
  669.         tc class add dev $qos_interface parent 1:1 classid 1:127 hfsc rt umax 106 dmax 10ms rate 4kbit
  670.         tc qdisc add dev $qos_interface parent 1:127 pfifo
  671.         tc filter add dev $qos_interface parent 1:0 prio 1 protocol ip handle 127 fw flowid 1:127
  672.  
  673.         #Mark all ping requests from the router to the ptarget to the above special class overriding any other mark.
  674.         iptables -t mangle -I qos_egress -p icmp --icmp-type 8 -s $wan_ip -d $ptarget_ip -j MARK --set-mark 127
  675.  
  676.         #Start the monitor
  677.         if [ -n "$pinglimit" ] ; then
  678.             #In manual mode the user selects the active mode pinglimit indirectly.  qosmon always measures the RTT of a ping on an unloaded link.
  679.             #This is called the ping entitlement.  With a manual entry the minRTT ping limit is 110% of this measured ping entitlement
  680.             #and the active mode ping limit is the minRTT limit plus the user entered value.  See the qosmon source code for more details.
  681.             #In summary manaully entered ping times only affect the active mode, not the minRTT mode ping time limits.
  682.             qosmon -a -b 800 $ptarget_ip $total_download_bandwidth $pinglimit
  683.         else
  684.             #In auto mode we calculate transmission delay based on our bandwidth and then ask qosmon
  685.             #to add this value to its measured ping entitlement to form the final ping limit.
  686.             pinglimit=$((1500*10*2/3/$total_download_bandwidth+1500*10/$total_upload_bandwidth+2))
  687.             qosmon -a -b 800 $ptarget_ip $total_download_bandwidth $pinglimit
  688.         fi
  689.  
  690.         $echo_off
  691.     fi
  692.  
  693.     update_markfile
  694.  
  695. }
  696.  
  697.  
  698. define_interface()
  699. {
  700.     #Wait for up to 15 seconds for the wan interface to indicate it is up.
  701.     wait_sec=15
  702.     while [ -z $(uci -P /var/state get network.wan.up 2>/dev/null) ] && [ $wait_sec -gt 0 ] ; do
  703.         sleep 1
  704.         wait_sec=$(($wait_sec - 1))
  705.     done
  706.  
  707.     #The interface name will depend on if pppoe is used or not.  If pppoe is used then
  708.     #the name we are looking for is in network.wan.ifname.  If there is nothing there
  709.     #use the device named by network.wan.device
  710.  
  711.     qos_interface=$(uci -P /var/state get network.wan.ifname 2>/dev/null)
  712.     if [ -z $qos_interface ] ; then
  713.         qos_interface=$(uci -P /var/state get network.wan.device 2>/dev/null)
  714.     fi
  715.  
  716.     wan_ip=$(ifconfig $qos_interface | sed -n 's/.*inet addr:\([0-9/.]*\).*/\1/p')
  717.     local_ip=$(ifconfig br-lan | sed -n 's/.*inet addr:\([0-9/.]*\).*/\1/p')
  718.  
  719. }
  720.  
  721. stop()
  722. {
  723.     #if already in process of being initialized, do not continue
  724.     #until that is finished, and then exit cleanly, without
  725.     #doing anything further
  726.     if [ -e "$lock_file" ] ; then
  727.         while [ -e "$lock_file" ] ; do
  728.             sleep 1
  729.         done
  730.         exit
  731.     fi
  732.  
  733.     #Kill the qos monitor in case it is running
  734.     killall qosmon 2>/dev/null
  735.  
  736.  
  737.     $echo_on
  738.     for iface in $(tc qdisc show | grep hfsc | awk '{print $5}'); do
  739.         tc qdisc del dev "$iface" root
  740.     done
  741.  
  742.     # eliminate existing rules in mangle table
  743.     delete_chain_from_table "mangle" "qos_egress"
  744.     delete_chain_from_table "mangle" "qos_ingress"
  745.     $echo_off
  746.  
  747. }
  748.  
  749.  
  750. start()
  751. {
  752.     test_total_up=$(uci get qos_gargoyle.upload.total_bandwidth 2>/dev/null)
  753.     test_total_down=$(uci get qos_gargoyle.download.total_bandwidth 2>/dev/null)
  754.     if [ -z "$test_total_up" ] && [ -z "$test_total_down" ] ;then
  755.         disable
  756.         exit 0
  757.     fi
  758.  
  759.  
  760.     #This script is called by a hotplug event.  If the WAN comes
  761.     #up fast we could end up trying to run qos_gargoyle while the
  762.     #boot is still in progress which causes issues in low memory
  763.     #routers.  To avoid this we check to see if rcS is still running
  764.     #or not.  If it is we wait until it completes or 60 seconds.
  765.     cnt=0
  766.     while ps | grep '[//]rcS S boot' >/dev/null
  767.         do
  768.             sleep 4
  769.             cnt=`expr $cnt + 1`
  770.             if [ $cnt -ge 15 ] ; then
  771.                 break;
  772.             fi
  773.         done
  774.  
  775.     stop
  776.     touch "$lock_file"
  777.  
  778.     #load qos_interface from global variables
  779.     define_interface
  780.     if [ -n "$qos_interface" ] ; then
  781.  
  782.         load_all_config_options "$config_file_name" "global"
  783.         initialize_qos
  784.     fi
  785.  
  786.     rm -rf "$lock_file"
  787.  
  788. }
  789.  
  790. restart()
  791. {
  792.     echo_on="set -x"
  793.     echo_off="set +x"
  794.     start "$1"
  795. }
  796.  
  797. show()
  798. {
  799.     #load global variables
  800.     load_all_config_options "$config_file_name" "global"
  801.  
  802.     #load qos_interface from global variables
  803.     define_interface
  804.  
  805.     echo "Egress configuration on $qos_interface"
  806.     iptables -t mangle -vnL qos_egress 2>/dev/null
  807.     tc -s qdisc show dev $qos_interface
  808.     tc -s class show dev $qos_interface
  809.     tc -s filter show dev $qos_interface
  810.  
  811.     echo "Ingress configuration in imq0"
  812.     iptables -t mangle -vnL qos_ingress 2>/dev/null
  813.     tc -s qdisc show dev imq0
  814.     tc -s class show dev imq0
  815.     tc -s filter show dev imq0
  816.     tc -s filter show dev imq0 parent 2:
  817.    
  818.  
  819. }
  820.  
  821. boot()
  822. {
  823.     #Do nothing during init.  Start is called by hotplug.
  824.     return
  825. }
Advertisement
Add Comment
Please, Sign In to add comment