#!/bin/sh . /lib/functions.sh #set -x # include common code . /lib/qos/ip_rule.sh . /lib/qos/iptables.sh . /lib/qos/ebtables.sh . /lib/qos/classify.sh MAJOR="" POLICER_COUNT=0 Q_COUNT=0 SP_Q_PRIO=7 get_rate_per_queue() { echo "1000000" } get_burst_size_per_queue() { echo "1500" } # Function to handle a queue order and # update total number of queues handle_q_order() { local qid="$1" #queue section ID config_get is_enable "$qid" "enable" 1 # No need to configure disabled queues if [ $is_enable == '0' ]; then return fi config_get ifname "$qid" "ifname" # If ifname is empty that is good enough to break if [ -z "$ifname" ];then return fi # Create precedence file containing queue order per # interface. local precedence_file="/tmp/qos/$ifname/q_order" local q_no=$(cat /tmp/qos/$ifname/q_idx) config_get precedence "$qid" "precedence" value=${precedence}_q${q_no} echo $value >> $precedence_file # Update the number of queues per interface. q_no=$((q_no + 1)) echo $q_no > /tmp/qos/$ifname/q_idx } # Sort queue, lower value in uci means higher precedence, so this # function sorts the precedence in decending order sort_q_by_precedence() { ifname="$1" local order_file="/tmp/qos/$ifname/q_order" local tmp_order_file="/tmp/qos/$ifname/q_order.tmp" sort -n -k1 $order_file > $tmp_order_file cp $tmp_order_file $order_file rm -f $tmp_order_file } sort_by_precedence() { for interf in $(db -q get hw.board.ethernetPortOrder); do sort_q_by_precedence $interf done } # function to handle a queue section handle_queue() { local qid="$1" #queue section ID local port="$2" local port_bw="$3" local root="$4" local port_bs="$5" local priority=0 config_get is_enable "$qid" "enable" # no need to configure disabled queues if [ "$is_enable" == "0" ]; then return fi config_get ifname "$qid" "ifname" # if ifname is empty that is good enough to break if [ -z "$ifname" ];then return fi # This is to get the qid per interface. if [ "$port" != "$ifname" ]; then return fi local precedence_file="/tmp/qos/$ifname/q_order" local temp_order=0 while read -r line; do line_qid=${line: -1} if [ "$line_qid" == "$Q_COUNT" ]; then break fi temp_order=$((temp_order + 1)) done < "$precedence_file" # precedence_file so the order is calculated accordingly. local order=`expr $SP_Q_PRIO - $temp_order` config_get sc_alg "$qid" "scheduling" config_get wgt "$qid" "weight" 1 config_get rate "$qid" "rate" config_get bs "$qid" "burst_size" config_get qsize "$qid" "queue_size" 1024 [ "$rate" == "0" ] && rate="$port_bw" [ "$bs" == "0" ] && bs="$port_bs" local salg=1 case "$sc_alg" in "SP") salg=1 ;; "WRR") salg=2 ;; "WDRR") salg=3 ;; "WFQ") salg=4 ;; esac # ignore precedence value in case of WRR, broadcom recommends that WRR queue should # always have precedence value set to 0 if [ $salg -ne 2 ]; then priority=$order fi if [ $order -ne 0 ]; then if [ $salg -eq 2 ]; then tc class add dev $port parent ${root}: classid ${root}:$order cbq allot $bs bandwidth ${port_bw}kbit rate ${rate}kbit prio $priority weight $wgt avpkt 1500 bounded isolated else tc class add dev $port parent ${root}: classid ${root}:$order cbq allot $bs bandwidth ${port_bw}kbit rate ${rate}kbit prio $priority avpkt 1500 bounded isolated fi tc filter add dev $port parent ${root}:0 prio $order handle $order fw classid ${root}:$order fi Q_COUNT=$((Q_COUNT + 1)) } iptables_set_traffic_class() { IP_RULE="$IP_RULE -j MARK --set-mark $1" } ebt_match_ipv6_dscp() { #when ethertype is not configured by user then both proto rules of ipv4 #and ipv6 to be installed so update BR6_RULE string as well otherwise #update BR_RULE only for installation of ipv6 proto rule only. if [ ! -z "$BR6_RULE" ]; then BR6_RULE="$BR6_RULE --ip6-tclass $1" else BR_RULE="$BR_RULE --ip6-tclass $1" fi } broute_filter_on_dscp() { # The broadcom option --ip-dscp-extend actually accepts tos # and not dscp and that too in hex, hence, perform the conversion # from dscp in uci to tos first and then convert to hex tos_val=$(($1<<2)) tos_hex=$(printf "%x" $tos_val) BR_RULE="$BR_RULE --ip-dscp-extend $tos_hex" } broute_ipv4_rule_options() { local cid=$1 config_get protocol "$cid" "proto" config_get dscp_filter "$cid" "dscp_filter" set_ip_addr $cid ebt_match_src_ip ebt_match_dst_ip if [ ! -z $dscp_filter ]; then broute_filter_on_dscp "$dscp_filter" fi if [ ! -z $protocol ]; then local proto_num=$(protocol_string_to_num "$protocol") ebt_match_ip_protocol "$proto_num" #port installation for protol tcp/udp/sctp if [ $proto_num = "6" ] || [ $proto_num = "17" ] || [ $proto_num = "132" ] ; then set_ports "$cid" ebt_match_ip_src_port ebt_match_ip_dst_port fi fi } broute_ipv6_rule_options() { local cid=$1 config_get protocol "$cid" "proto" config_get dscp_filter "$cid" "dscp_filter" set_ip_addr $cid ebt_match_ipv6_src_ip ebt_match_ipv6_dst_ip if [ ! -z $dscp_filter ]; then local tos_val local tos_hex tos_val=$(($dscp_filter<<2)) tos_hex=$(printf "%x" $tos_val) ebt_match_ipv6_dscp "$tos_hex" fi if [ ! -z $protocol ]; then local proto_num=$(protocol_string_to_num "$protocol") ebt_match_ipv6_protocol "$proto_num" #port installation for protol tcp/udp/sctp if [ $proto_num = "6" ] || [ $proto_num = "17" ] || [ $proto_num = "132" ]; then set_ports "$cid" ebt_match_ipv6_src_port ebt_match_ipv6_dst_port fi fi } broute_rule_set_traffic_class() { #when ethertype is not configured by user then both proto rules of ipv4 #and ipv6 to be installed so update BR6_RULE string as well otherwise #update BR_RULE only for installation of ipv6 proto rule only. BR_RULE="$BR_RULE -j mark --mark-or 0x$1 --mark-target ACCEPT" if [ ! -z "$BR6_RULE" ]; then BR6_RULE="$BR6_RULE -j mark --mark-or 0x$1 --mark-target ACCEPT" fi } #function to handle a policer section handle_policer() { local p_sec="$1" # policer section ID local dir=1 # default direction, upstream config_get is_enable "$p_sec" "enable" #no need to configure disabled policer if [ $is_enable == '0' ]; then return fi POLICER_COUNT=$((POLICER_COUNT + 1)) } handle_policer_rules() { local c_sec=$1 local policer_name local ifname local pname local pindex=-1 local ingress_rate=0 local in_burst_size=0 config_get policer_name "$c_sec" "policer" if [ -z "$policer_name" ];then # no need to apply policer if policer not present in this # classification rule return fi config_get ifname "$c_sec" "ifname" if [ -z "$ifname" ]; then # cannot associate policer as interface is not mentioned return fi local i=0 local max_policer_inst=$(cat /tmp/qos/max_policer_inst) while : do if [ $i -eq $max_policer_inst ]; then break fi pname="$(uci -q get qos.@policer[$i].name)" if [ "$policer_name" == "$pname" ]; then pindex=$i ingress_rate=$(uci -q get qos.@policer[$i].committed_rate) in_burst_rate=$(uci -q get qos.@policer[$i].committed_burst_size) break fi i=$((i + 1)) done if [ $pindex -lt 0 ]; then # policer not found, no need to proceed further return fi config_ingress_rate_limit $ifname $ingress_rate $in_burst_size $pindex } config_ingress_rate_limit() { local ifname="$1" local ingress_rate=$2 local in_burst_size=$3 local pindex="$4" local wanport="$(db -q get hw.board.ethernetWanPort)" # Unit in uci file is in bps while that accepted by ethswctl is kbits if [ $ingress_rate -lt 1000 ]; then return fi ingress_rate=$((ingress_rate / 1000)) if [ $in_burst_size -eq 0 ]; then in_burst_size=$ingress_rate else in_burst_size=$((in_burst_size / 1000)) fi tc qdisc add dev $ifname ingress tc filter add dev $ifname parent ffff: protocol ip prio $pindex u32 match ip src 0.0.0.0/0 police rate ${ingress_rate}kbit burst $in_burst_size drop flowid :$pindex } pre_configure_queue() { # Delete queues for intf in $(db get hw.board.ethernetPortOrder); do rm -rf /tmp/qos/$intf mkdir -p /tmp/qos/$intf touch /tmp/qos/$intf/q_order touch /tmp/qos/$intf/q_idx echo 0 > /tmp/qos/$intf/q_idx tc qdisc del dev $intf root tc qdisc del dev $intf ingress done } get_link_rate() { intf="$1" speed=0 config_load ports get_speed() { psid="$1" iname="$2" config_load ports config_get ifname "$psid" "ifname" if [ "$ifname" == "$iname" ]; then config_get speed "$psid" "speed" fi } config_foreach get_speed ethport $intf echo "$speed" } configure_queue() { qdisc_idx=0 local bs=1500 local rate=0 # Load UCI file config_load qos config_foreach handle_q_order queue sort_by_precedence get_intf_shaper_config() { local b_size sid="$1" #shaper section ID config_get is_enable "$sid" "enable" # no need to configure disabled queues if [ "$is_enable" == "0" ]; then return fi config_get ifname "$sid" "ifname" # if ifname is empty that is good enough to break if [ -z "$ifname" ] || ! [ "$ifname" == "$2" ];then return fi config_get rate "$sid" "rate" # Convert the rate from bps to kbps. if [ $rate -lt 1000 ];then return fi rate=$(( rate / 1000 )) config_get b_size "$sid" "burst_size" if [ "$b_size" == "0" ]; then bs="$b_size" fi } local wanport="$(db -q get hw.board.ethernetWanPort)" for interf in $(db -q get hw.board.ethernetPortOrder); do Q_COUNT=0 rate=0 # sp queue have max priority value = no. of queue configured on the port # hence read and update SP_Q_PRIO here local q_no=$(cat /tmp/qos/$interf/q_idx) SP_Q_PRIO=`expr $q_no - 1` qdisc_idx=`expr $qdisc_idx + 1` # link_rate is in mbps and rate is in kbp link_rate=$(get_link_rate "$interf") # Read the shaper configuration for interface config_foreach get_intf_shaper_config shaper $interf if [ "$rate" == "0" ]; then rate=$(( link_rate * 1000 )) fi # TODO using 1500 as allot and avpkt, if shaper config exist for interf get burst_size of shaper for actual value tc qdisc add dev $interf root handle ${qdisc_idx}: cbq allot $bs avpkt 1500 bandwidth ${rate}kbit # if qdisc_idx is the index corresponds to WAN port, then # it would be the MAJOR portion of the destination class ID. # under the same qdisc. We are programming for WAN port only. if [ "$interf" == "$wanport" ]; then MAJOR="$qdisc_idx" fi config_foreach handle_queue queue $interf $rate $qdisc_idx $bs done } configure_policer() { # Delete policer local i=0 local max_p_inst=0 if [ -f "/tmp/qos/max_policer_inst" ]; then max_p_inst=$(cat /tmp/qos/max_policer_inst) fi # reset the policer counter echo 0 > /tmp/qos/max_policer_inst # Load UCI file config_load qos config_foreach handle_policer policer echo $POLICER_COUNT > /tmp/qos/max_policer_inst } configure_qos() { pre_configure_queue configure_queue configure_classify configure_policer } reload_qos() { local service_name="$1" if [ -z "$service_name" ]; then configure_qos elif [ "$service_name" == "queue" ]; then pre_configure_queue configure_queue elif [ "$service_name" == "classify" ]; then configure_classify elif [ "$service_name" == "policer" ]; then configure_policer fi } reload_qos_service() { reload_qos }