diff --git a/qosmngr/Makefile b/qosmngr/Makefile index 1c39bb73b..f7054aac0 100644 --- a/qosmngr/Makefile +++ b/qosmngr/Makefile @@ -40,11 +40,24 @@ define Build/Prepare endef endif -define Package/qosmngr/install - $(CP) ./files/* $(1)/ +define Package/qosmngr/install/common + $(CP) ./files/common/* $(1)/ $(INSTALL_DIR) $(1)/usr $(INSTALL_DIR) $(1)/usr/sbin $(INSTALL_BIN) $(PKG_BUILD_DIR)/qosmngr $(1)/usr/sbin endef +ifeq ($(CONFIG_TARGET_iopsys_brcm63xx_arm),y) +define Package/qosmngr/install + $(CP) ./files/broadcom/* $(1)/ + $(call Package/qosmngr/install/common,$(1)) +endef +endif + +ifeq ($(CONFIG_TARGET_iopsys_econet),y) +define Package/qosmngr/install + $(CP) ./files/airoha/* $(1)/ + $(call Package/qosmngr/install/common,$(1)) +endef +endif $(eval $(call BuildPackage,qosmngr)) diff --git a/qosmngr/files/airoha/etc/uci-defaults/60-qos_queue_generate b/qosmngr/files/airoha/etc/uci-defaults/60-qos_queue_generate new file mode 100644 index 000000000..ff1c23e68 --- /dev/null +++ b/qosmngr/files/airoha/etc/uci-defaults/60-qos_queue_generate @@ -0,0 +1,48 @@ +#!/bin/sh + +. /lib/functions.sh + +ethwan="$(db -q get hw.board.ethernetWanPort)" + +generate_queue(){ + section="$1" + + config_get ifname "$section" "ifname" + + if [ "$ifname" != "$ethwan" ]; then + return 0 + fi + + # guaranteed number of queues + no_of_q="0 1 2 3" + + i=0 + local total_q=$((${no_of_q##* } + 1)) + for i in $no_of_q; do + order=$((total_q - i)) + uci add qos queue + uci rename qos.@queue[-1]="q_${i}_${ifname}" + uci set qos.@queue[-1].enable="1" + uci set qos.@queue[-1].ifname="$ifname" + uci set qos.@queue[-1].precedence="$order" + uci set qos.@queue[-1].scheduling="SP" + uci set qos.@queue[-1].rate="0" + uci set qos.@queue[-1].burst_size="0" + uci set qos.@queue[-1].weight="1" + done + + uci commit qos +} + +if [ -s "/etc/config/qos" ]; then + if uci -q get qos.@queue[0] >/dev/null; then + # return if there is any valid content + exit + else + rm -f /etc/config/qos + fi +fi +touch /etc/config/qos + +config_load ports +config_foreach generate_queue ethport diff --git a/qosmngr/files/airoha/lib/qos/common/chains.ebtables.sh b/qosmngr/files/airoha/lib/qos/common/chains.ebtables.sh new file mode 100755 index 000000000..e5c9ed540 --- /dev/null +++ b/qosmngr/files/airoha/lib/qos/common/chains.ebtables.sh @@ -0,0 +1,453 @@ +#!/bin/sh +# Install ebtables rules + +BR_RULE="" +BR6_RULE="" + +init_broute_rule() { + BR_RULE="" + BR6_RULE="" +} + +broute_filter_on_src_if() { + BR_RULE="$BR_RULE --in-if $1" +} + +broute_filter_on_src_mac() { + BR_RULE="$BR_RULE --src $1" +} + +broute_filter_on_dst_mac() { + BR_RULE="$BR_RULE --dst $1" +} + +broute_filter_on_pcp() { + case "$BR_RULE" in + *proto*) + BR_RULE="$BR_RULE --vlan-prio $1" + ;; + *) + BR_RULE="$BR_RULE --proto 802_1Q --vlan-prio $1" + ;; + esac +} + +broute_filter_on_ether_type() { + BR_RULE="$BR_RULE --proto $1" +} + +broute_filter_on_ether_type6() { + BR6_RULE="$BR6_RULE --proto IPv6" +} + +ebt_match_src_ip() { + BR_RULE="$BR_RULE --ip-src $1" +} + +ebt_match_dst_ip() { + BR_RULE="$BR_RULE --ip-dst $1" +} + +ebt_match_ipv6_src_ip() { + BR_RULE="$BR_RULE --ip6-src $1" +} + +ebt_match_ipv6_dst_ip() { + BR_RULE="$BR_RULE --ip6-dst $1" +} + +ebt_match_ip_src_port() { + BR_RULE="$BR_RULE --ip-source-port $1" +} + +ebt_match_ip_dst_port() { + BR_RULE="$BR_RULE --ip-destination-port $1" +} + +ebt_match_ipv6_src_port() { + if [ -n "$BR6_RULE" ]; then + BR6_RULE="$BR6_RULE --ip6-source-port $1" + else + BR_RULE="$BR_RULE --ip6-source-port $1" + fi +} + +ebt_match_ipv6_dst_port() { + if [ -n "$BR6_RULE" ]; then + BR6_RULE="$BR6_RULE --ip6-destination-port $1" + else + BR_RULE="$BR_RULE --ip6-destination-port $1" + fi +} + +ebt_match_ip_protocol() { + BR_RULE="$BR_RULE --ip-proto $1" +} + +ebt_match_ipv6_protocol() { + if [ -n "$BR6_RULE" ]; then + BR6_RULE="$BR6_RULE --ip6-proto $1" + else + BR_RULE="$BR_RULE --ip6-proto $1" + fi +} + +ebt_match_ipv6_dscp() { + if [ -n "$BR6_RULE" ]; then + BR6_RULE="$BR6_RULE --ip6-tclass $1" + else + BR_RULE="$BR_RULE --ip6-tclass $1" + fi +} + +broute_filter_on_vid() { + + if [ -z "$1" ] || [ "$1" -lt 0 ]; then + return + fi + + case "$BR_RULE" in + *proto*) + BR_RULE="$BR_RULE --vlan-id $1" + ;; + *) + BR_RULE="$BR_RULE --proto 802_1Q --vlan-id $1" + ;; + esac +} + +broute_rule_set_traffic_class() { + BR_RULE="$BR_RULE -j mark --mark-or 0x$1 --mark-target ACCEPT" + if [ -n "$BR6_RULE" ]; then + BR6_RULE="$BR6_RULE -j mark --mark-or 0x$1 --mark-target ACCEPT" + fi +} + +broute_append_rule() { + echo "ebtables -t broute -A qos $BR_RULE" >> /tmp/qos/classify.ebtables + if [ -n "$BR6_RULE" ]; then + echo "ebtables -t broute -A qos $BR6_RULE" >> /tmp/qos/classify.ebtables + fi +} + +set_ip_addr() +{ + local cid="$1" + local match_src_ip_func="$2" + local match_dst_ip_func="$3" + + config_get src_ip "$cid" "src_ip" + config_get dst_ip "$cid" "dest_ip" + + if [ -n "$src_ip" ]; then + $match_src_ip_func "$src_ip" + fi + + if [ -n "$dst_ip" ]; then + $match_dst_ip_func "$dst_ip" + fi +} + +set_ports() +{ + local cid="$1" + local match_src_port_func="$2" + local match_dst_port_func="$3" + local src_port="" + local dst_port="" + local src_port_range="" + local dst_port_range="" + + config_get src_port "$cid" "src_port" + config_get dst_port "$cid" "dest_port" + config_get src_port_range "$cid" "src_port_range" + config_get dst_port_range "$cid" "dest_port_range" + + if [ -n "$src_port" ] && [ -n "$src_port_range" ] ; then + $match_src_port_func "$src_port:$src_port_range" + elif [ -n "$src_port" ] ; then + $match_src_port_func "$src_port" + fi + + if [ -n "$dst_port" ] && [ -n "$dst_port_range" ] ; then + $match_dst_port_func "$dst_port:$dst_port_range" + elif [ -n "$dst_port" ] ; then + $match_dst_port_func "$dst_port" + fi +} + +protocol_string_to_num() +{ + local value="-1" + + case "$1" in + *[0-9]*) + value="$1" + ;; + TCP|tcp) + value=6 + ;; + UDP|udp) + value=17 + ;; + ICMP|icmp) + value=1 + ;; + ICMPv6|icmpv6) + value=58 + ;; + IGMP|igmp) + value=2 + ;; + SCTP|sctp) + value=132 + ;; + *) + value=-1 + ;; + esac + echo $value +} + +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 [ -n "$dscp_filter" ]; then + echo "DSCP filter is not supporter" + fi + + if [ -n "$protocol" ]; then + local proto_num="$(protocol_string_to_num "$protocol")" + ebt_match_ip_protocol "$proto_num" + + 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 [ -n "$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 [ -n "$protocol" ]; then + local proto_num="$(protocol_string_to_num "$protocol")" + ebt_match_ipv6_protocol "$proto_num" + + 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 +} + +handle_ebtables_rules() { + local sid="$1" + local is_l2_rule=0 + local src_dhcp_options="" + local dst_dhcp_options="" + local protocol="" + local ip_version="" + + init_broute_rule + + config_get src_if "$sid" "ifname" + config_get src_mac "$sid" "src_mac" + config_get dst_mac "$sid" "dst_mac" + config_get dscp_filter "$sid" "dscp_filter" + config_get pcp_check "$sid" "pcp_check" + config_get eth_type "$sid" "ethertype" + config_get vid "$sid" "vid_check" + config_get dhcp_type "$sid" "dhcp_type" # dhcpv4 or v6 + config_get src_vcid "$sid" "src_vendor_class_id" # dhcp option 60 + config_get dst_vcid "$sid" "dst_vendor_class_id" # dhcp option 60 + config_get src_clid "$sid" "src_client_id" # dhcp option 61 + config_get dst_clid "$sid" "dst_client_id" # dhcp option 61 + config_get src_ucid "$sid" "src_user_class_id" # dhcp option 77 + config_get dst_ucid "$sid" "dst_user_class_id" # dhcp option 77 + config_get traffic_class "$sid" "traffic_class" + config_get protocol "$sid" "proto" + + if [ -n "$src_if" ]; then + for interf in $(db -q get hw.board.ethernetPortOrder); do + if [ "$src_if" == "$interf" ]; then + src_if="$src_if+" + broute_filter_on_src_if "$src_if" + is_l2_rule=1 + fi + done + fi + + if [ -n "$src_mac" ]; then + broute_filter_on_src_mac "$src_mac" + is_l2_rule=1 + fi + + if [ -n "$dst_mac" ]; then + broute_filter_on_dst_mac "$dst_mac" + is_l2_rule=1 + fi + + if [ -n "$pcp_check" ]; then + broute_filter_on_pcp "$pcp_check" + is_l2_rule=1 + fi + + if [ -n "$eth_type" ]; then + broute_filter_on_ether_type "$eth_type" + is_l2_rule=1 + fi + + if [ -n "$vid" ]; then + broute_filter_on_vid "$vid" + is_l2_rule=1 + fi + + case $eth_type in + IPv4|IPV4|0800) + ip_version=4 + ;; + IPv6|IPV6|86DD) + ip_version=6 + ;; + *) + if [ -z "$eth_type" ]; then + case "$src_ip$dst_ip" in + *.*) + ip_version=4 + broute_filter_on_ether_type "IPv4" + ;; + *:*) + ip_version=6 + broute_filter_on_ether_type "IPv6" + ;; + *) + if [ -n "$protocol" ] || [ -n "$dscp_filter" ]; then + # Neither ether_type nor ip address used, + # ethertype is not configured by user, so install + # both proto IPv4 and IPv6 rule (version 1) + ip_version=1 + BR6_RULE="$BR_RULE" + broute_filter_on_ether_type "IPv4" + broute_filter_on_ether_type6 "IPv6" + fi + esac + fi + esac + + if [ "$ip_version" == "4" ] || [ "$ip_version" == "1" ]; then + broute_ipv4_rule_options "$sid" + is_l2_rule=1 + fi + + if [ "$ip_version" == "6" ] || [ "$ip_version" == "1" ]; then + broute_ipv6_rule_options "$sid" + is_l2_rule=1 + fi + + # first process options that will help figure our source mac address + # dhcp option for "vendor class id" + if [ -n "$src_vcid" ]; then + src_dhcp_options="$src_dhcp_options vcid=$src_vcid" + is_l2_rule=1 + fi + + # dhcp option for "client id" + if [ -n "$src_clid" ]; then + src_dhcp_options="$src_dhcp_options clid=$src_clid" + is_l2_rule=1 + fi + + # dhcp option for "user class id" + if [ -n "$src_ucid" ]; then + src_dhcp_options="$src_dhcp_options ucid=$src_ucid" + is_l2_rule=1 + fi + + # if src mac is already a classification criteria, then it + # does not really make sense to add it as a criteria to + # filter packets again based on source mac + if [ -n "$src_dhcp_options" ] && [ -z "$src_mac" ]; then + comp="$(grep -i "$src_dhcp_options" /tmp/dhcp.client.options)" + if [ -n "$comp" ]; then + s_mac_add="$(echo $comp | head -n1 | awk '{print $1;}')" + if [ -n "$s_mac_add" ]; then + broute_filter_on_src_mac "$s_mac_add" + fi + fi + fi + + # Now process options that will help figure our destination mac address + # dhcp option for "vendor class id" + if [ -n "$dst_vcid" ]; then + dst_dhcp_options="$dst_dhcp_options vcid=$dst_vcid" + is_l2_rule=1 + fi + + # dhcp option for "client id" + if [ -n "$dst_clid" ]; then + dst_dhcp_options="$dst_dhcp_options clid=$dst_clid" + is_l2_rule=1 + fi + + # dhcp option for "user class id" + if [ -n "$dst_ucid" ]; then + dst_dhcp_options="$dst_dhcp_options ucid=$dst_ucid" + is_l2_rule=1 + fi + + # if dst mac is already a classification criteria, then it + # does not really make sense to add it as a criteria to + # filter packets again based on destination mac + if [ -n "$dst_dhcp_options" ] && [ -z "$dst_mac" ] ; then + comp="$(grep -i "$dst_dhcp_options" /tmp/dhcp.client.options)" + if [ -n "$comp" ]; then + d_mac_add="$(echo $comp | head -n1 | awk '{print $1;}')" + if [ -n "$d_mac_add" ]; then + broute_filter_on_dst_mac "$d_mac_add" + fi + fi + fi + + if [ $is_l2_rule -eq 0 ]; then + return + fi + + [ -n "$traffic_class" ] && broute_rule_set_traffic_class "$traffic_class" + + [ -n "$BR_RULE" ] && broute_append_rule +} + +create_ebtables_chains() { + ebtables -t broute -N qos + ret=$? + if [ $ret -eq 0 ]; then + ebtables -t broute -I BROUTING -j qos + else + ebtables -t broute -D BROUTING -j qos + ebtables -t broute -I BROUTING -j qos + fi +} + +flush_ebtables_chains() { + echo "ebtables -t broute -F qos" > /tmp/qos/classify.ebtables +} + diff --git a/qosmngr/files/airoha/lib/qos/common/chains.iptables.sh b/qosmngr/files/airoha/lib/qos/common/chains.iptables.sh new file mode 100755 index 000000000..0418ac8b0 --- /dev/null +++ b/qosmngr/files/airoha/lib/qos/common/chains.iptables.sh @@ -0,0 +1,240 @@ +#!/bin/sh +# Install iptables rules + +IP_RULE="" + +init_iptables_rule() { + IP_RULE="" +} + +iptables_filter_intf() { + IP_RULE="$IP_RULE -i $1" +} + +iptables_filter_proto() { + IP_RULE="$IP_RULE -p $1" +} + +iptables_filter_ip_src() { + IP_RULE="$IP_RULE -s $1" +} + +iptables_filter_ip_dest() { + IP_RULE="$IP_RULE -d $1" +} + +iptables_filter_port_dest() { + IP_RULE="$IP_RULE --dport $1" +} + +iptables_filter_port_src() { + IP_RULE="$IP_RULE --sport $1" +} + +iptables_filter_port_dest_range() { + IP_RULE="$IP_RULE --dport $1:$2" +} + +iptables_filter_port_src_range() { + IP_RULE="$IP_RULE --sport $1:$2" +} + +iptables_filter_dscp_filter() { + IP_RULE="$IP_RULE -m dscp --dscp $1" +} + +iptables_filter_ip_len_min() { + IP_RULE="$IP_RULE -m length --length $1" +} + +iptables_filter_ip_len_max() { + IP_RULE="$IP_RULE:$1" +} + +iptables_set_dscp_mark() { + IP_RULE="$IP_RULE -j DSCP --set-dscp $1" +} + +iptables_set_traffic_class() { + IP_RULE="$IP_RULE -j MARK --set-xmark 0x${1}0/0xF0" +} + +append_rule_to_mangle_table() { + if [ "$2" == 4 ]; then + echo "iptables -w -t mangle -A $1 $IP_RULE" >> /tmp/qos/classify.iptables + elif [ "$2" == 6 ]; then + echo "ip6tables -w -t mangle -A $1 $IP_RULE" >> /tmp/qos/classify.ip6tables + elif [ "$2" == 1 ]; then + echo "iptables -w -t mangle -A $1 $IP_RULE" >> /tmp/qos/classify.iptables + echo "ip6tables -w -t mangle -A $1 $IP_RULE" >> /tmp/qos/classify.ip6tables + fi +} + +handle_iptables_rules() { + local cid="$1" + local ip_version=0 + local is_l3_rule=0 + + init_iptables_rule + config_get proto "$cid" "proto" + config_get traffic_class "$cid" "traffic_class" + config_get dscp_mark "$cid" "dscp_mark" + config_get dscp_filter "$cid" "dscp_filter" + config_get dest_port "$cid" "dest_port" + config_get dest_port_range "$cid" "dest_port_range" + config_get src_port "$cid" "src_port" + config_get src_port_range "$cid" "src_port_range" + config_get dest_ip "$cid" "dest_ip" + config_get src_ip "$cid" "src_ip" + config_get ip_len_min "$cid" "ip_len_min" + config_get ip_len_max "$cid" "ip_len_max" + config_get ifname "$cid" "ifname" + + #check version of ip + case $src_ip$dest_ip in + *.*) + ip_version=4 + ;; + *:*) + ip_version=6 + ;; + *) + ip_version=1 #ip address not used + esac + + #filter interface + if [ -n "$ifname" ]; then + if [ "$ifname" != "lo" ]; then + iptables_filter_intf "$ifname" + fi + fi + + # filter proto + if [ -n "$proto" ]; then + iptables_filter_proto "$proto" + is_l3_rule=1 + fi + + #filter src. ip + if [ -n "$src_ip" ]; then + iptables_filter_ip_src "$src_ip" + is_l3_rule=1 + fi + + #filter dest. ip + if [ -n "$dest_ip" ]; then + iptables_filter_ip_dest "$dest_ip" + is_l3_rule=1 + fi + + #filter dest. port + if [ -n "$dest_port" ] && [ -z "$dest_port_range" ]; then + iptables_filter_port_dest "$dest_port" + is_l3_rule=1 + fi + + #filter src. port + if [ -n "$src_port" ] && [ -z "$src_port_range" ]; then + iptables_filter_port_src "$src_port" + is_l3_rule=1 + fi + + #filter dest. port range + if [ -n "$dest_port" ] && [ -n "$dest_port_range" ]; then + + if [ "$dest_port_range" == "-1" ] ; then + dest_port_range="$dest_port" + fi + + iptables_filter_port_dest_range "$dest_port" "$dest_port_range" + is_l3_rule=1 + fi + + #filter src. port range + if [ -n "$src_port" ] && [ -n "$src_port_range" ]; then + + if [ "$src_port_range" == "-1" ] ; then + src_port_range="$src_port" + fi + + iptables_filter_port_src_range "$src_port" "$src_port_range" + is_l3_rule=1 + fi + + #filter dscp + if [ -n "$dscp_filter" ]; then + iptables_filter_dscp_filter "$dscp_filter" + is_l3_rule=1 + fi + + #filter min. IP packet len. + if [ -n "$ip_len_min" ]; then + iptables_filter_ip_len_min "$ip_len_min" + is_l3_rule=1 + fi + + #filter max. IP packet len. + if [ -n "$ip_len_max" ]; then + iptables_filter_ip_len_max "$ip_len_max" + is_l3_rule=1 + fi + + if [ -z "$is_l3_rule" ] || [ "$is_l3_rule" -eq 0 ] ; then + return + fi + + #set dscp mark + [ -n "$dscp_mark" ] && iptables_set_dscp_mark "$dscp_mark" + + #set packet queue mark + [ -n "$traffic_class" ] && iptables_set_traffic_class "$traffic_class" + + #write iptables rule for dscp marking + [ -n "$IP_RULE" ] && [ -n "$dscp_mark" ] && append_rule_to_mangle_table "qos_forward" $ip_version + + if [ -n "$IP_RULE" ] && [ -n "$traffic_class" ]; then + if [ "$ifname" == "lo" ]; then + #write iptables rule for putting WAN directed internal packets in different queue + append_rule_to_mangle_table "qos_output" $ip_version + else + #write iptables rule for putting WAN directed LAN packets in different queue + append_rule_to_mangle_table "qos_prerouting" $ip_version + fi + fi +} + +create_iptables_chains() { + iptables -w -t mangle -N qos_forward + ret=$? + [ $ret -eq 0 ] && iptables -w -t mangle -I FORWARD -j qos_forward + + iptables -w -t mangle -N qos_prerouting + ret=$? + [ $ret -eq 0 ] && iptables -w -t mangle -I PREROUTING -j qos_prerouting + + iptables -w -t mangle -N qos_output + ret=$? + [ $ret -eq 0 ] && iptables -w -t mangle -I OUTPUT -j qos_output + + ip6tables -t mangle -N qos_forward + ret=$? + [ $ret -eq 0 ] && ip6tables -t mangle -I FORWARD -j qos_forward + + ip6tables -t mangle -N qos_prerouting + ret=$? + [ $ret -eq 0 ] && ip6tables -t mangle -I PREROUTING -j qos_prerouting + + ip6tables -t mangle -N qos_output + ret=$? + [ $ret -eq 0 ] && ip6tables -t mangle -I OUTPUT -j qos_output +} + +flush_iptables_chains() { + echo "iptables -w -t mangle -F qos_forward" > /tmp/qos/classify.iptables + echo "iptables -w -t mangle -F qos_prerouting" >> /tmp/qos/classify.iptables + echo "iptables -w -t mangle -F qos_output" >> /tmp/qos/classify.iptables + + echo "ip6tables -w -t mangle -F qos_forward" > /tmp/qos/classify.ip6tables + echo "ip6tables -w -t mangle -F qos_prerouting" >> /tmp/qos/classify.ip6tables + echo "ip6tables -w -t mangle -F qos_output" >> /tmp/qos/classify.ip6tables +} diff --git a/qosmngr/files/airoha/lib/qos/common/chains.sh b/qosmngr/files/airoha/lib/qos/common/chains.sh new file mode 100755 index 000000000..d26146ee2 --- /dev/null +++ b/qosmngr/files/airoha/lib/qos/common/chains.sh @@ -0,0 +1,12 @@ +#!/bin/sh +# Set up or flush all chains + +setup_qos() { + create_ebtables_chains + create_iptables_chains +} + +flush_chains() { + flush_ebtables_chains + flush_iptables_chains +} diff --git a/qosmngr/files/airoha/lib/qos/common/classify.sh b/qosmngr/files/airoha/lib/qos/common/classify.sh new file mode 100755 index 000000000..3b4a1d3e3 --- /dev/null +++ b/qosmngr/files/airoha/lib/qos/common/classify.sh @@ -0,0 +1,44 @@ +#!/bin/sh +# Common classifier library + +# Handle classify section +handle_classify() { + cid="$1" #classify section ID + + config_get is_enable "$cid" "enable" + # no need to configure disabled classify rules + if [ -z "$is_enable" ] || [ "$is_enable" == "0" ]; then + return + fi + + handle_ebtables_rules "$cid" + handle_iptables_rules "$cid" + handle_policer_rules "$cid" +} + + +# Configure classifier based on UCI subtree 'qos.classify' +configure_classify() { + # Processing classify section + # First remove old files + rm -f /tmp/qos/classify.ebtables + rm -f /tmp/qos/classify.iptables + rm -f /tmp/qos/classify.ip6tables + + # Create files that will contain the rules if not present already + mkdir -p /tmp/qos/ + touch /tmp/qos/classify.iptables + touch /tmp/qos/classify.ip6tables + touch /tmp/qos/classify.ebtables + + # Add flush chain rules + flush_chains + + # Load UCI file + config_load qos + config_foreach handle_classify classify + + sh /tmp/qos/classify.ebtables + sh /tmp/qos/classify.iptables + sh /tmp/qos/classify.ip6tables +} \ No newline at end of file diff --git a/qosmngr/files/airoha/lib/qos/common/policer.sh b/qosmngr/files/airoha/lib/qos/common/policer.sh new file mode 100755 index 000000000..d62891e61 --- /dev/null +++ b/qosmngr/files/airoha/lib/qos/common/policer.sh @@ -0,0 +1,132 @@ +#!/bin/sh +# Common policer library + +POLICER_COUNT=0 + +# Function invoked +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 [ -z "$pindex" ] || [ "$pindex" -lt 0 ]; then + # policer not found, no need to proceed further + return + fi + + config_ingress_rate_limit "$ifname" "$ingress_rate" "$in_burst_size" + +} + +# Configure ingress rate limit +config_ingress_rate_limit() { + local ifname="$1" + local ingress_rate="$2" + local in_burst_size="$3" + local port="" + + if [ -z "$ingress_rate" ] || [ "$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 + + hw_policer_set_ingress_rate "$ifname" "$port" "$ingress_rate" "$in_burst_size" +} + +# Function invoked for handling policer section in UCI +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 [ -z "$is_enable" ] || [ "$is_enable" == "0" ] ; then + return + fi + + config_get cir "$p_sec" "committed_rate" + config_get cbs "$p_sec" "committed_burst_size" -1 + config_get ebs "$p_sec" "excess_burst_size" 0 + config_get pir "$p_sec" "peak_rate" 0 + config_get pbs "$p_sec" "peak_burst_size" 0 + config_get meter "$p_sec" "meter_type" 0 + + hw_policer_set add "$dir" "$POLICER_COUNT" "$meter" "$cir" "$cbs" "$ebs" "$pir" "$pbs" + + POLICER_COUNT=$((POLICER_COUNT + 1)) +} + +# Configure policer based on UCI subtree 'qos.policer' +configure_policer() { + for intf in $(db get hw.board.ethernetPortOrder); do + if [ -n "${intf}" ] ; then + hw_policer_set_ingress_rate "$intf" 0 0 + fi + done + + # 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 + + while : + do + if [ "$i" -eq "$max_p_inst" ]; then + break + fi + hw_policer_set del 1 $i + i=$((i + 1)) + done + + # 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 +} \ No newline at end of file diff --git a/qosmngr/files/airoha/lib/qos/common/queue.sh b/qosmngr/files/airoha/lib/qos/common/queue.sh new file mode 100755 index 000000000..b765e23c9 --- /dev/null +++ b/qosmngr/files/airoha/lib/qos/common/queue.sh @@ -0,0 +1,50 @@ +#!/bin/sh +# Handle queues and their order + +Q_COUNT=0 + +# Preliminary configuration of a queue +pre_configure_queue() { + # Delete queues + hw_queue_init_all + + for intf in $(db get hw.board.ethernetPortOrder); do + hw_intf_init "${intf}" + done +} + +# UCI queue section handler +handle_queue() { + local qid="$1" #queue section ID + local intf_name="$2" + + config_get is_enable "$qid" "enable" + + # no need to configure disabled queues + if [ -z "${is_enable}" ] || [ "${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 + + 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 + config_get precedence "$qid" "precedence" + + hw_queue_set "${ifname}" "${Q_COUNT}" "${precedence}" "$qsize" "$wgt" "$sc_alg" "$shapingrate" "$rate" "$bs" + Q_COUNT=$((Q_COUNT + 1)) +} + +# Configure all queues specified in UCI tree 'qos.queue' +configure_queue() { + # Load UCI file + config_load qos + config_foreach handle_queue queue +} diff --git a/qosmngr/files/airoha/lib/qos/common/shaper.sh b/qosmngr/files/airoha/lib/qos/common/shaper.sh new file mode 100755 index 000000000..cf2d8b789 --- /dev/null +++ b/qosmngr/files/airoha/lib/qos/common/shaper.sh @@ -0,0 +1,46 @@ +#!/bin/sh +# Common shaper library + +# UCI 'shaper' section handler. +# It will verify shaper configuration sanity and then invoke +# hardware-specific functions +handle_shaper() { + sid="$1" #queue section ID + + config_get is_enable "$sid" "enable" + # no need to configure disabled queues + if [ -z "${is_enable}" ] || [ "${is_enable}" == "0" ] ; then + return + fi + + + config_get ifname "$sid" "ifname" + # if ifname is empty that is good enough to break + if [ -z "$ifname" ] ; then + return + fi + + config_get rate "$sid" "rate" + # Convert the rate from bps to kbps. + if [ -z "${rate}" ] || [ "${rate}" -lt 1000 ] ; then + return + fi + + rate=$((rate / 1000)) + config_get bs "$sid" "burst_size" + + hw_shaper_set "$ifname" add "$rate" "$bs" +} + +# Configure shaper based on options saved to UCI tree 'qos.shaper' +configure_shaper() { + # Delete existing shaper + for intf in $(db get hw.board.ethernetPortOrder); do + hw_shaper_set "$intf" del + done + + # Load UCI file + config_load qos + # Processing shaper section(s) + config_foreach handle_shaper shaper +} diff --git a/qosmngr/files/airoha/lib/qos/econet.sh b/qosmngr/files/airoha/lib/qos/econet.sh new file mode 100755 index 000000000..f71c56d04 --- /dev/null +++ b/qosmngr/files/airoha/lib/qos/econet.sh @@ -0,0 +1,190 @@ +#!/bin/sh +# Implementation of QoS setup for Econet platform. + +errmsg() { + echo "$@" >&2 + return 0 +} + +get_var() { + local varname="$1" + eval echo \$\{${varname}\} + return 0 +} + +# Initialized queues +# (Not needed on Econet) +hw_queue_init_all() { + return 0 +} + +# Initialize all interfaces +# (Not needed on Econet) +hw_intf_init() { + return 0 +} + +# Initialize the hardware setup library +hw_init_all() { + export TMP_HW_QUEUE_LIST="" + export TMP_HW_QUEUE_MASK="0" + return 0 +} + +# Remember selected queue options. They will be committed +# during hw_commit_all() +hw_queue_set() { + local ifname="$1" + local q_count="$2" + local order="$3" + local qsize="$4" + local wgt="$5" + local sc_alg="$6" + local rate="$7" + local burstsize="$8" + local index="$((order - 1))" + local ethwan="$(db -q get hw.board.ethernetWanPort)" + + #if [ "${ifname}" != "${ethwan}" ] ; then + # return 2 + #fi + + export TMP_HW_QUEUE_${order}_no="${q_count}" + export TMP_HW_QUEUE_${order}_ifname="${ifname}" + export TMP_HW_QUEUE_${order}_order="${order}" + export TMP_HW_QUEUE_${order}_qsize="${qsize}" + export TMP_HW_QUEUE_${order}_wgt="${wgt}" + export TMP_HW_QUEUE_${order}_sc_alg="${sc_alg}" + export TMP_HW_QUEUE_${order}_rate="${rate}" + export TMP_HW_QUEUE_${order}_burstsize="${burstsize}" + export TMP_HW_QUEUE_LIST="${TMP_HW_QUEUE_LIST} ${order}" + export TMP_HW_QUEUE_MASK="$((TMP_HW_QUEUE_MASK | 1 << index))" + + return 0 +} + +# Set policer options. In fact, they are not supported. +hw_policer_set() { + local action="$1" + local dir="$2" + local policer_no="$3" + + shift 3 + + case "$action" in + add) + meter="$1" + cir="$2" + cbs="$3" + ebs="$4" + pir="$5" + pbs="$6" + + ;; + del) + ;; + esac + errmsg "Policer (action $action, direction $dir) is not implemented" + return $? +} + +# Set ingress rate. In fact, it is not supported +hw_policer_set_ingress_rate() { + local ifname="$1" + local ingress_rate="$2" + local in_burst_size="$3" + + errmsg "Policer (action set_ingress_rate) is not implemented" +} + +# Configure shaper rate that will be committed during hw_commit_all() +hw_shaper_set() { + local ifname="$1" + local action="$2" + local rate="$3" + local burstsize="$4" + + case "${action}" in + add) + export TMP_HW_SHAPE_RATE="$rate" + export TMP_HW_SHAPE_BURSTSIZE="$burstsize" + ;; + del) + export TMP_HW_SHAPE_RATE="" + export TMP_HW_SHAPE_BURSTSIZE="" + ;; + *) + return 1 + ;; + esac + + return 0 +} + +# Convert shaper in UCI terms to Econet terms +hw_sc_alg2str() { + local sc_alg="$1" + + case "${sc_alg}" in + SP) + echo "PQ" + ;; + WRR) + echo "WRR" + ;; + *) + echo "" + return 1 + esac + + return 0 +} + +# Commit all options preserved during +hw_commit_all() { + local sorted_list="$(echo $TMP_HW_QUEUE_LIST | tr ' ' '\n' | sort | xargs)" + local weight_list="" + local glob_alg="" + local shape_rate="$TMP_HW_SHAPE_RATE" + local queue_mask="$TMP_HW_QUEUE_MASK" + local q_count="0" + + # Reorder queues + for q in ${sorted_list} ; do + local sc_alg="$(get_var TMP_HW_QUEUE_${q}_sc_alg)" + local wgt="$(get_var TMP_HW_QUEUE_${q}_wgt)" + + if [ "$glob_alg" != "" ] && [ "$sc_alg" != "$glob_alg" ] ; then + errmsg "Not matching scheduling algorithm: $sc_alg vs $glob_alg" + return 1 + fi + + glob_alg="$sc_alg" + + case "${sc_alg}" in + WRR) + if [ $(($q_count >= 4)) != 0 ] ; then + errmsg "Too many queues, next queues will be ignored" + else + weight_list="$weight_list $wgt" + fi + q_count=$((q_count + 1)) + ;; + esac + done + + if [ "${glob_alg}" == "WRR" ] ; then + while [ $((q_count < 4)) != 0 ] ; do + weight_list="$weight_list 1" + q_count=$((q_count + 1)) + done + fi + + if [ "${glob_alg}" != "" ] ; then + /userfs/bin/qosrule discpline $(hw_sc_alg2str ${glob_alg}) ${weight_list} \ + ${shape_rate:+uplink-bandwidth} $shape_rate \ + queuemask $queue_mask + else + /userfs/bin/qosrule discpline off + fi +} diff --git a/qosmngr/files/airoha/lib/qos/qos.sh b/qosmngr/files/airoha/lib/qos/qos.sh new file mode 100755 index 000000000..d8222ffc3 --- /dev/null +++ b/qosmngr/files/airoha/lib/qos/qos.sh @@ -0,0 +1,51 @@ +#!/bin/sh +# The entrypoint for the QoS setup library + +. /lib/functions.sh +. /lib/network/port.sh + +. /lib/qos/common/chains.sh +. /lib/qos/common/chains.ebtables.sh +. /lib/qos/common/chains.iptables.sh +. /lib/qos/common/classify.sh +. /lib/qos/common/policer.sh +. /lib/qos/common/queue.sh +. /lib/qos/common/shaper.sh +. /lib/qos/econet.sh + +configure_qos() { + # queue configuration is being done after shaper configuration, + # If port shapingrate configuration on DISC device is called after queue configuration then + # driver overwrites the queue shaping rate with default value of port shaping rate. + pre_configure_queue + setup_qos + configure_shaper + configure_queue + configure_policer + configure_classify +} + +reload_qos() { + local service_name="$1" + + hw_init_all + case "${service_name}" in + shaper) + configure_shaper + ;; + queue) + pre_configure_queue + configure_queue + ;; + classify) + configure_classify + ;; + policer) + configure_policer + ;; + "") + configure_qos + ;; + esac + hw_commit_all +} diff --git a/qosmngr/files/etc/uci-defaults/60-qos_queue_generate b/qosmngr/files/broadcom/etc/uci-defaults/60-qos_queue_generate similarity index 100% rename from qosmngr/files/etc/uci-defaults/60-qos_queue_generate rename to qosmngr/files/broadcom/etc/uci-defaults/60-qos_queue_generate diff --git a/qosmngr/files/lib/qos/broadcom.sh b/qosmngr/files/broadcom/lib/qos/qos.sh similarity index 95% rename from qosmngr/files/lib/qos/broadcom.sh rename to qosmngr/files/broadcom/lib/qos/qos.sh index f35c76f71..58a5ced16 100755 --- a/qosmngr/files/lib/qos/broadcom.sh +++ b/qosmngr/files/broadcom/lib/qos/qos.sh @@ -10,9 +10,6 @@ POLICER_COUNT=0 Q_COUNT=0 SP_Q_PRIO=7 -#counter variable to assign classify order value if not added in config -temp_order=1 - # Function to handle a queue order and # update total number of queues handle_q_order() { @@ -979,43 +976,6 @@ config_ingress_rate_limit() { ethswctl -c rxratectrl -n $unit -p $port -x $ingress_rate -y $in_burst_size } -# Function to handle a classify order -handle_classify_order() { - local cid="$1" #classify section ID - - config_get is_enable "$cid" "enable" 1 - - # No need to configure disabled classify - if [ $is_enable == '0' ]; then - return - fi - - # Create classify file containing classify order - local corder_file="/tmp/qos/classify.order" - - config_get c_order "$cid" "order" - - if [ -z "$c_order" ]; then - c_order=$temp_order; - temp_order=$((temp_order + 1)) - fi - - value=${c_order}_${cid} - echo $value >> $corder_file - -} - -# Sort classify, lower value in uci means higher precedence, so this -# function sorts the classify order in assending order -sort_classify_by_order() { - local corder_file="/tmp/qos/classify.order" - local tmp_corder_file="/tmp/qos/tmp_classify.order" - - sort -n -k1 $corder_file > $tmp_corder_file - cp $tmp_corder_file $corder_file - rm -f $tmp_corder_file -} - #function to handle a classify section handle_classify() { cid="$1" #classify section ID @@ -1026,15 +986,9 @@ handle_classify() { return fi - local corder_file="/tmp/qos/classify.order" - - while read -r line; do - line_cid=${line: 2} - - handle_ebtables_rules $line_cid - handle_iptables_rules $line_cid - handle_policer_rules $line_cid - done < "$corder_file" + handle_ebtables_rules $cid + handle_iptables_rules $cid + handle_policer_rules $cid } configure_shaper() { @@ -1054,25 +1008,19 @@ configure_classify() { rm -f /tmp/qos/classify.ebtables rm -f /tmp/qos/classify.iptables rm -f /tmp/qos/classify.ip6tables - rm -f /tmp/qos/classify.order - rm -f /tmp/qos/tmp_classify.order #create files that will contain the rules if not present already mkdir -p /tmp/qos/ touch /tmp/qos/classify.iptables touch /tmp/qos/classify.ip6tables touch /tmp/qos/classify.ebtables - touch /tmp/qos/classify.order - touch /tmp/qos/tmp_classify.order #add flush chain rules flush_chains # Load UCI file config_load qos - config_foreach handle_classify_order classify - sort_classify_by_order - handle_classify classify + config_foreach handle_classify classify sh /tmp/qos/classify.ebtables sh /tmp/qos/classify.iptables diff --git a/qosmngr/files/etc/init.d/qos b/qosmngr/files/common/etc/init.d/qos similarity index 96% rename from qosmngr/files/etc/init.d/qos rename to qosmngr/files/common/etc/init.d/qos index 4e5c27c60..531043f6a 100755 --- a/qosmngr/files/etc/init.d/qos +++ b/qosmngr/files/common/etc/init.d/qos @@ -11,7 +11,8 @@ NAME=qosmngr PROG=/usr/sbin/qosmngr . /lib/functions.sh -include /lib/qos + +. /lib/qos/qos.sh start_service() { if [ -f "/etc/config/qos" ]; then