#!/bin/bash

###########################################################################
#
# MODULE:       Configurator
# COPYRIGHT:    (C) 2009-2025 by CacheGuard Technologies Ltd (UK)
# COPYRIGHT:    (C) 2026-2026 by CacheGuard Technologies SAS (FR)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program 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 <http://www.gnu.org/licenses/>.
#
###########################################################################

get-qos-logical-interfaces()
{
    local with_ipsec=${1}

    local interfaces

    case ${APL_ROLE} in
	gateway)
	    interfaces="external internal"
	    test -z "${AUXILIARY_IS_ACTIVE}" || interfaces="${interfaces} auxiliary"
	    test -z "${with_ipsec}" -o ${VPN_IPSEC_MODE} == False || interfaces="${interfaces} vpnipsec"
	    ;;
	manager)
	    interfaces="internal"
	    ;;
	*)
	    interfaces="internal"
	    ;;
    esac

    echo ${interfaces}
}

get-sfq-flows-divisor()
{
    test -n "${1}" || return 1
    local connections=${1}

    local ratio=4
    local min_flows=16
    local max_divisor=65536
    local max_flows=$[65536 / ${ratio}]

    local flows=${connections}
    if test ${flows} -lt ${min_flows} ; then
	flows=${min_flows}
    elif test ${flows} -gt ${max_flows} ; then
	flows=${max_flows}
    fi

    local divisor=$[${flows} * ${ratio}]
    divisor=$(get-power2-near-value ${divisor})

    test ${divisor} -le ${max_divisor} || divisor=${max_divisor}

    echo ${flows}:${divisor}
}

set-qos-constant()
{
    export QOS_LEAF_QDISC=sfq

    export QOS_ADMIN_OR_MON_MARK=0x1
    export QOS_FILE_MARK=0x3

    export QOS_HANDLE_ROOT=1
    export QOS_CLASS_ADMIN=${QOS_HANDLE_ROOT}:1
    export QOS_CLASS_NADMIN=${QOS_HANDLE_ROOT}:2
    export QOS_HANDLE_ADMIN=2
    export QOS_HANDLE_NADMIN=3
    export QOS_HANDLE_SELF=11
    export QOS_HANDLE_WEB_PUBLIC=12
    export QOS_HANDLE_WEB_CLIENT=13
    export QOS_HANDLE_TWEB_CLIENT=14
    export QOS_HANDLE_IPSEC=15
    export QOS_HANDLE_RWEB_BACKEND=16
    export QOS_HANDLE_RWEB_PUBLIC=17
    export QOS_HANDLE_PEER=18
    export QOS_HANDLE_AV=19
    export QOS_HANDLE_ETC=20
    export QOS_HANDLE_FILE=21
    export QOS_HANDLE_SELF_DEFAULT=22
    export QOS_HANDLE_ROUTER=23
    export QOS_HANDLE_ROUTER_DEFAULT=24
    export QOS_HANDLE_PEER_NEXT_INIT=25
    export QOS_HANDLE_INIT=$[${QOS_HANDLE_PEER_NEXT_INIT} + ${MAX_PEERS_NB} + 100]

    export QOS_HANDLE_SELF_MAX=$[${QOS_HANDLE_INIT}
				 + (${MAX_PEERS_NB} * 3)
				 + ${MAX_DHCP_PEER_NB}
				 + ${RWEB_NB}
				 + (${RWEB_NB} * ${MAX_RWEB_HOST_NB})
				 + ${MAX_ACL_WEB_NB}
				 + ${MAX_TRANSPARENT_WEB_NB}
				 + ${MAX_ACL_FILE_NB}
				 + ${MAX_ACL_AV_NB}
				 + ${MAX_QOS_SHAPE_ROUTER_RULES_NB}
	   ]

    export QOS_CLASS_ROUTER_ID=${QOS_HANDLE_NADMIN}:${QOS_HANDLE_ROUTER}
    export QOS_CLASS_ROUTER_DEFAULT_ID=${QOS_HANDLE_NADMIN}:${QOS_HANDLE_ROUTER_DEFAULT}

    export QOS_CHILD_QDISC_HANDLE_OFFSET=2000

    export QOS_CLASS_SELF_ID=${QOS_HANDLE_NADMIN}:${QOS_HANDLE_SELF}
    export QOS_CLASS_WEB_PUBLIC_ID=${QOS_HANDLE_NADMIN}:${QOS_HANDLE_WEB_PUBLIC}
    export QOS_CLASS_WEB_CLIENT_ID=${QOS_HANDLE_NADMIN}:${QOS_HANDLE_WEB_CLIENT}
    export QOS_CLASS_TWEB_CLIENT_ID=${QOS_HANDLE_NADMIN}:${QOS_HANDLE_TWEB_CLIENT}
    export QOS_CLASS_IPSEC_ID=${QOS_HANDLE_NADMIN}:${QOS_HANDLE_IPSEC}
    export QOS_CLASS_RWEB_BACKEND_ID=${QOS_HANDLE_NADMIN}:${QOS_HANDLE_RWEB_BACKEND}
    export QOS_CLASS_RWEB_PUBLIC_ID=${QOS_HANDLE_NADMIN}:${QOS_HANDLE_RWEB_PUBLIC}
    export QOS_CLASS_PEER_ID=${QOS_HANDLE_NADMIN}:${QOS_HANDLE_PEER}
    export QOS_CLASS_AV_ID=${QOS_HANDLE_NADMIN}:${QOS_HANDLE_AV}
    export QOS_CLASS_ETC_ID=${QOS_HANDLE_NADMIN}:${QOS_HANDLE_ETC}
    export QOS_CLASS_FILE_ID=${QOS_HANDLE_NADMIN}:${QOS_HANDLE_FILE}
    export QOS_CLASS_SELF_DEFAULT_ID=${QOS_HANDLE_NADMIN}:${QOS_HANDLE_SELF_DEFAULT}

    export QOS_INGRESS_PRIO=12

    export QOS_CLASS_SELF_PRIO=12
    export QOS_CLASS_ROUTER_PRIO=14

    export QOS_CLASS_ROUTER_RULE_PRIO=12
    export QOS_CLASS_ROUTER_DEFAULT_PRIO=14

    export QOS_CLASS_WEB_PUBLIC_PRIO=12
    export QOS_CLASS_WEB_CLIENT_PRIO=14
    export QOS_CLASS_TWEB_CLIENT_PRIO=16
    export QOS_CLASS_IPSEC_PRIO=18
    export QOS_CLASS_RWEB_BACKEND_PRIO=20
    export QOS_CLASS_RWEB_PUBLIC_PRIO=22
    export QOS_CLASS_PEER_PRIO=24
    export QOS_CLASS_AV_PRIO=26
    export QOS_CLASS_ETC_PRIO=28
    export QOS_CLASS_FILE_PRIO=30
    export QOS_CLASS_SELF_ROUTER_PRIO=32
    export QOS_CLASS_SELF_DEFAULT_PRIO=34

    export QOS_ADMIN_FILTER_PRIO=12
    export QOS_ADMIN_SNMP_TRAP_FILTER_PRIO=14
    export QOS_ADMIN_SYSLOG_FILTER_PRIO=16
    export QOS_ADMIN_SNMP_FILTER_PRIO=18
    export QOS_NADMIN_FILTER_PRIO=20

    export QOS_DNS_FILTER_PRIO=12
    export QOS_PEER_FILTER_PRIO=14
    export QOS_WEB_FILTER_PRIO=16
    export QOS_RWEB_FILTER_PRIO=18
    export QOS_IKE_FILTER_PRIO=20
    export QOS_ESP_FILTER_PRIO=22
    export QOS_AV_FILTER_PRIO=24
    export QOS_ARP_FILTER_PRIO=26
    export QOS_DHCP_FILTER_PRIO=28
    export QOS_CLOUD_FILTER_PRIO=30
    export QOS_VRRP_FILTER_PRIO=32
    export QOS_ICMP_FILTER_PRIO=34
    export QOS_AUTH_FILTER_PRIO=36
    export QOS_NTP_FILTER_PRIO=38
    export QOS_OCSP_FILTER_PRIO=40
    export QOS_EMAIL_FILTER_PRIO=42
    export QOS_IGMP_FILTER_PRIO=44
    export QOS_IPV6_FILTER_PRIO=46
    export QOS_SELF_FIRST_FILTER_PRIO=48
    export QOS_FILE_FILTER_PRIO=$[${QOS_SELF_FIRST_FILTER_PRIO} + ${MAX_QOS_SHAPE_ROUTER_RULES_NB} + 2]
    export QOS_SELF_DEFAULT_FILTER_PRIO=$[${QOS_FILE_FILTER_PRIO} + 2]

    export QOS_GATEWAY_REAL_IP_FILTER_PRIO=12
    export QOS_GATEWAY_VRRP_IP_FILTER_PRIO=14
    export QOS_TWEB_FILTER_PRIO=16
    export QOS_ROUTER_FIRST_FILTER_PRIO=18
    export QOS_ROUTER_DEFAULT_FILTER_PRIO=$[${QOS_ROUTER_FIRST_FILTER_PRIO} + ${MAX_QOS_SHAPE_ROUTER_RULES_NB} + 2]

    export QOS_HTB_QUANTUM="quantum ${NETWORK_MTU}"

    case ${QOS_LEAF_QDISC} in
	sfq)
	    local flows_divisor flows divisor clients

	    local qdisc_sfq="sfq perturb 60 quantum ${NETWORK_MTU}b"

	    local simultaneous_etc_nb=$[(${SIMULTANEOUS_USERS_NB} + ${SIMULTANEOUS_RUSERS_NB}) / 5]
	    test ${simultaneous_etc_nb} -gt 1 || simultaneous_etc_nb=1


	    flows_divisor=$(get-sfq-flows-divisor ${MAX_ADMINISTRATORS_NB})
	    flows=${flows_divisor/:*}
	    divisor=${flows_divisor/*:}
	    export QOS_QDISC_LEAFQ_ADMIN_OR_MON="${qdisc_sfq} flows ${flows} divisor ${divisor}"

	    flows_divisor=$(get-sfq-flows-divisor ${SIMULTANEOUS_USERS_NB})
	    flows=${flows_divisor/:*}
	    divisor=${flows_divisor/*:}
	    export QOS_QDISC_LEAFQ_USERS="${qdisc_sfq} flows ${flows} divisor ${divisor}"

	    flows_divisor=$(get-sfq-flows-divisor ${SIMULTANEOUS_RUSERS_NB})
	    flows=${flows_divisor/:*}
	    divisor=${flows_divisor/*:}
	    export QOS_QDISC_LEAFQ_RUSERS="${qdisc_sfq} flows ${flows} divisor ${divisor}"

	    flows_divisor=$(get-sfq-flows-divisor ${MAX_RWEB_HOST_NB})
	    flows=${flows_divisor/:*}
	    divisor=${flows_divisor/*:}
	    export QOS_QDISC_LEAFQ_RWEB="${qdisc_sfq} flows ${flows} divisor ${divisor}"

	    flows_divisor=$(get-sfq-flows-divisor $[2 * ${MAX_PEERS_NB}])
	    flows=${flows_divisor/:*}
	    divisor=${flows_divisor/*:}
	    export QOS_QDISC_LEAFQ_PEER="${qdisc_sfq} flows ${flows} divisor ${divisor}"
    
	    flows_divisor=$(get-sfq-flows-divisor ${MAX_ACL_AV_NB})
	    flows=${flows_divisor/:*}
	    divisor=${flows_divisor/*:}
	    export QOS_QDISC_LEAFQ_AV="${qdisc_sfq} flows ${flows} divisor ${divisor}"

	    flows_divisor=$(get-sfq-flows-divisor ${simultaneous_etc_nb})
	    flows=${flows_divisor/:*}
	    divisor=${flows_divisor/*:}
	    export QOS_QDISC_LEAFQ_ETC="${qdisc_sfq} flows ${flows} divisor ${divisor}"

	    if test ${VPN_IPSEC_ACCESS_MODE} == True ; then
		clients=${SIMULTANEOUS_USERS_NB}
	    else
		clients=${VPN_IPSEC_SITE_NB}
	    fi
	    flows_divisor=$(get-sfq-flows-divisor ${clients})
	    flows=${flows_divisor/:*}
	    divisor=${flows_divisor/*:}
	    export QOS_QDISC_LEAFQ_IPSEC="${qdisc_sfq} flows ${flows} divisor ${divisor}"
	    ;;
	fq_codel)
	    loca qdisc_fq_codel="fq_codel quantum ${NETWORK_MTU}"
	    export QOS_QDISC_LEAFQ_ADMIN_OR_MON=${qdisc_fq_codel}
	    export QOS_QDISC_LEAFQ_USERS=${qdisc_fq_codel}
	    export QOS_QDISC_LEAFQ_RUSERS=${qdisc_fq_codel}
	    export QOS_QDISC_LEAFQ_RWEB=${qdisc_fq_codel}
	    export QOS_QDISC_LEAFQ_PEER=${qdisc_fq_codel}
	    export QOS_QDISC_LEAFQ_AV=${qdisc_fq_codel}
	    export QOS_QDISC_LEAFQ_ETC=${qdisc_fq_codel}
	    export QOS_QDISC_LEAFQ_IPSEC=${qdisc_fq_codel}
	    ;;
	*)
	    ;;
    esac

    export QOS_FILTER_SYN="match u8 0x02 0xff at 33"
    export QOS_FILTER_NSYN="match u8 0x00 0x02 at 33"
    export QOS_FILTER_ACK="match u8 0x10 0xff at 33"
    export QOS_FILTER_NACK="match u8 0x00 0x10 at 33"

    export QOS_FILTER_ALL="match u32 0 0"
    export QOS_FILTER_ICMP="match ip protocol 1 0xff"
    export QOS_FILTER_IGMP_MULTICAST="match ip protocol 2 0xff"
    export QOS_FILTER_TCP="match ip protocol 6 0xff"
    export QOS_FILTER_UDP="match ip protocol 17 0xff"
    export QOS_FILTER_ESP="match ip protocol 50 0xff"
    export QOS_FILTER_AH="match ip protocol 51 0xff"
    export QOS_FILTER_IPV6="match u8 0 0"

    export QOS_FILTER_TOTAL_LEN_L64="match u16 0x0000 0xffc0 at 2"
    export QOS_FILTER_PROXY="match u32 0xCD040000 0xFFFF0000 at 20"
    export QOS_FILTER_MISS="match u32 0xCD040000 0xFFFFFFFF at 20"
    export QOS_FILTER_HIT="match u32 0xCD04FFFF 0xFFFFFFFF at 20"

    export QOS_SYN_PCT=20
    export QOS_ACK_PCT=20
}

set-qos-parameters()
{
    set-qos-constant

    local rate=50

    if test-auth-state ; then
	local users=$[${SIMULTANEOUS_USERS_NB} + ${SIMULTANEOUS_RUSERS_NB}]
	rate=$[${rate} + ${users} * 5]
    fi

    local ceil=$[${rate} * 5]
    export QOS_ETC_RATE="${rate}kbit ceil ${ceil}kbit"
}

reset-qos-index()
{
    rm -f /tmp/${QOS_INDEX_FILENAME}.${$}
}

commit-qos-index()
{
    test -f /tmp/${QOS_INDEX_FILENAME}.${$} || return 0
    mv -f /tmp/${QOS_INDEX_FILENAME}.${$} ${RUN_DIR}/${QOS_INDEX_FILENAME}
    rm -f ${RUN_DIR}/${QOS_DIFF_TRAFFIC_FILENAME} ${RUN_DIR}/${QOS_TRAFFIC_FILENAME}{,.old}
}

gen-qos-index()
{
    test -n "${1}" || return 1
    test -n "${2}" || return 2
    test -n "${3}" || return 3
    test -n "${4}" || return 4
    test -n "${5}" || return 5
    test -n "${6}" || return 6
    local flow=${1}
    local qdisc=${2}
    local interface=${3}
    local idev=${4}
    local odev=${5}
    local handle=${6}

    echo "${flow} ${qdisc} ${interface} ${idev} ${odev} ${handle}" >> /tmp/${QOS_INDEX_FILENAME}.${$}
}

get-qos-dev()
{
    test -n "${1}" || return 1
    test -n "${2}" || return 2
    local interface=${1}
    local sense=${2}

    case ${interface} in
	internal)
	    case ${sense} in
		ingress)
		    echo ${IF_QOS_INTERNAL_INGRESS}
		    ;;
		egress)
		    if test -n "${3}" ; then
			local flow=${3}
			get-net-dev ${flow}
		    else
			echo ${INTERNAL_DEVICE_LIST}
		    fi
		    ;;
		*)
		    ;;
	    esac
	    ;;

	external)
	    case ${sense} in
		ingress)
		    echo ${IF_QOS_EXTERNAL_INGRESS}
		    ;;
		egress)
		    echo ${IF_QOS_EXTERNAL_EGRESS}
		    ;;
		*)
		    ;;
	    esac
	    ;;

	vpnipsec)
	    case ${sense} in
		ingress)
		    echo ${IF_QOS_EXTERNAL_INGRESS}
		    ;;
		egress)
		    echo ${IF_QOS_EXTERNAL_EGRESS}
		    ;;
		*)
		    ;;
	    esac
	    ;;

	auxiliary)
	    case ${sense} in
		ingress)
		    echo ${IF_QOS_AUXILIARY_INGRESS}
		    ;;
		egress)
		    echo ${IF_QOS_AUXILIARY_EGRESS}
		    ;;
		*)
		    ;;
	    esac
	    ;;
	*)
	    return 11
	    ;;
    esac
}

is-percent-qos()
{
    test -n "${1}" || return 1
    local qos=${1}

    local len=${#qos} ; ((len--))
    if test ${qos:${len}} == '%' ; then
	echo "${qos:0:${len}}"
	return 0
    else
	echo "${qos}"
	return 1
    fi
}

get-qos-percent()
{
    test -n "${1}" || return 1
    test -n "${2}" || return 2
    local in_rate=${1}
    local in_pct=${2}

    local rate=$[${in_rate} * ${in_pct} / 100]
    test ${rate} -gt 0 || rate=1

    echo ${rate}
}

get-basic-rate()
{
    test -n "${1}" || return 1
    test -n "${2}" || return 2
    local in_rate=${1}
    local in_bw=${2}

    local rate

    rate=$(is-percent-qos ${in_rate})
    if test ${?} -eq 0 ; then
	rate=$[${rate} * ${in_bw} / 100]
	test ${rate} -gt 0 || rate=1
    fi
    echo ${rate}
}

get-qos-rate()
{
    test -n "${1}" || return 1
    test -n "${2}" || return 2
    local interface=${1}
    local sense=${2}
    local flow=${3}

    local qos=$(get-qos-shape ${interface} ${sense} ${flow})
    local bw=$(get-qos-bw ${interface} ${sense})

    test -n "${qos}" || return 11
    test -n "${bw}" || return 13

    qos=$(get-basic-rate ${qos} ${bw})
    echo ${qos}
}

get-qos-ceil()
{
    test -n "${1}" || return 1
    test -n "${2}" || return 2
    test -n "${3}" || return 3
    local interface=${1}
    local sense=${2}
    local rate=${3}

    local bw borrow=$(get-qos-borrow ${interface} ${sense})

    if test ${borrow} == False ; then
	bw=${rate}
    else
	bw=$(get-qos-bw ${interface} ${sense})
    fi

    echo ${bw}
}

get-qos-router-rate()
{
    test -n "${1}" || return 1
    test -n "${2}" || return 2
    test -n "${3}" || return 3
    local interface=${1}
    local sense=${2}
    local in_rate=${3}

    local rate flow='default'

    rate=$(is-percent-qos ${in_rate})
    if test ${?} -ne 0 ; then
	echo ${in_rate}
	return 0
    fi

    local default_rate=$(get-qos-rate ${interface} ${sense} ${flow})
    rate=$[${rate} * ${default_rate} / 100]
    test ${rate} -gt 0 || rate=1
    echo ${rate}
}

gen-tc-qos-default()
{
    test -n "${1}" || return 1
    local interface=${1}

    local flow='default'
    local idev=$(get-qos-dev ${interface} ingress)
    local odev odevs1 odevs=$(get-qos-dev ${interface} egress)
    local irate=$(get-qos-rate ${interface} ingress ${flow})
    local orate=$(get-qos-rate ${interface} egress ${flow})
    local iceil=$(get-qos-ceil ${interface} ingress ${irate})
    local oceil=$(get-qos-ceil ${interface} egress ${orate})

    local handle_self="$[${QOS_CHILD_QDISC_HANDLE_OFFSET} + ${QOS_HANDLE_SELF_DEFAULT}]:"
    local handle_router="$[${QOS_CHILD_QDISC_HANDLE_OFFSET} + ${QOS_HANDLE_ROUTER_DEFAULT}]:"

    if nic-exist ${idev} ; then
	echo
	echo "# Default QoS for ingress traffic on the ${interface} (${idev}) interface:"

	echo "class add dev ${idev} parent ${QOS_CLASS_SELF_ID} classid ${QOS_CLASS_SELF_DEFAULT_ID} htb rate ${irate}kbit ceil ${iceil}kbit ${QOS_HTB_QUANTUM} prio ${QOS_CLASS_SELF_DEFAULT_PRIO}"
	echo "qdisc add dev ${idev} parent ${QOS_CLASS_SELF_DEFAULT_ID} handle ${handle_self} ${QOS_QDISC_LEAFQ_USERS}"
	echo "filter add dev ${idev} parent ${QOS_CLASS_SELF_ID} priority ${QOS_SELF_DEFAULT_FILTER_PRIO} matchall flowid ${QOS_CLASS_SELF_DEFAULT_ID}"
	echo
	echo "class add dev ${idev} parent ${QOS_CLASS_ROUTER_ID} classid ${QOS_CLASS_ROUTER_DEFAULT_ID} htb rate ${irate}kbit ceil ${iceil}kbit ${QOS_HTB_QUANTUM} prio ${QOS_CLASS_ROUTER_DEFAULT_PRIO}"
	echo "qdisc add dev ${idev} parent ${QOS_CLASS_ROUTER_DEFAULT_ID} handle ${handle_router} ${QOS_QDISC_LEAFQ_USERS}"
	echo "filter add dev ${idev} parent ${QOS_HANDLE_NADMIN}: priority ${QOS_ROUTER_DEFAULT_FILTER_PRIO} matchall flowid ${QOS_CLASS_ROUTER_DEFAULT_ID}"
    fi

    for odev in ${odevs}
    do
	if nic-exist ${odev} ; then
	    echo
	    echo "# Default QoS for egress traffic on the ${interface} (${odev}) interface:"

	    echo "class add dev ${odev} parent ${QOS_CLASS_SELF_ID} classid ${QOS_CLASS_SELF_DEFAULT_ID} htb rate ${orate}kbit ceil ${oceil}kbit ${QOS_HTB_QUANTUM} prio ${QOS_CLASS_SELF_DEFAULT_PRIO}"
	    echo "qdisc add dev ${odev} parent ${QOS_CLASS_SELF_DEFAULT_ID} handle ${handle_self} ${QOS_QDISC_LEAFQ_USERS}"
	    echo "filter add dev ${odev} parent ${QOS_CLASS_SELF_ID} priority ${QOS_SELF_DEFAULT_FILTER_PRIO} matchall flowid ${QOS_CLASS_SELF_DEFAULT_ID}"
	    echo
	    echo "class add dev ${odev} parent ${QOS_CLASS_ROUTER_ID} classid ${QOS_CLASS_ROUTER_DEFAULT_ID} htb rate ${irate}kbit ceil ${iceil}kbit ${QOS_HTB_QUANTUM} prio ${QOS_CLASS_ROUTER_DEFAULT_PRIO}"
	    echo "qdisc add dev ${odev} parent ${QOS_CLASS_ROUTER_DEFAULT_ID} handle ${handle_router} ${QOS_QDISC_LEAFQ_USERS}"
	    echo "filter add dev ${odev} parent ${QOS_HANDLE_NADMIN}: priority ${QOS_ROUTER_DEFAULT_FILTER_PRIO} matchall flowid ${QOS_CLASS_ROUTER_DEFAULT_ID}"

	    odevs1="${odevs1} ${odev}"
	fi
    done

    odevs1=${odevs1:1}

    gen-qos-index default-steered: ${QOS_LEAF_QDISC} ${interface} ${idev} ${odevs1// /:} ${handle_self}
    gen-qos-index default-routed: ${QOS_LEAF_QDISC} ${interface} ${idev} ${odevs1// /:} ${handle_router}
}

tc-normalise-port()
{
    test -n "${1}" || return 1
    local ports=${1}

    if test ${ports} == ${ports/:} ; then
	echo ${ports}
	return 0
    fi

    local port1=${ports/:*}
    local port2=${ports/*:}

    if test ${port1} == ${port2} ; then
	ports=${port1}
    else
	ports=${port1}-${port2}
    fi

    echo ${ports}
}

is-web-port()
{
    test -n "${1}" || return 1
    local port=${1}

    test ${port} == ${port/-} || return 11
    test ${port} == ${WWW_PORT} -o ${port} == ${HTTPS_PORT}
}

gen-tc-qos-router()
{
    test ${APL_ROLE} == gateway || return 0

    local interface native_interface interfaces=$(get-qos-logical-interfaces)

    for interface in ${interfaces}
    do
	test ${interface} == internal || local ${interface}_odev=$(get-qos-dev ${interface} ingress)
	local ${interface}_idev=$(get-qos-dev ${interface} ingress)
    done

    local internal_odev variable

    local elt range i=0
    local rule_id protocol src_ip src_port dst_ip dst_port
    local ingress_qos egress_qos

    local idev odev vlan
    local qosid irate orate iceil oceil

    local class_parent class_priority filter_parent filter_priority
    local filter_protocol handle
    local filter_router_priority=${QOS_ROUTER_FIRST_FILTER_PRIO} filter_self_priority=${QOS_SELF_FIRST_FILTER_PRIO}
    local filter_forth_src_ip filter_forth_src_port filter_forth_dst_ip filter_forth_dst_port
    local filter_back_src_ip filter_back_src_port filter_back_dst_ip filter_back_dst_port
    local snat proto

    if test -n "${QOS_SHAPE_ROUTER_LIST}" ; then
	echo
	echo "#########################################"
	echo "# QoS for routed traffic via the gateway:"
	echo "#########################################"
    fi

    for elt in ${QOS_SHAPE_ROUTER_LIST}
    do
	range=$[${i} % 10]
	case ${range} in
	    0)
		rule_id=${elt}
		;;
	    1)
		interface=${elt}
		;;
	    2)
		protocol=${elt}
		;;
	    3)
		src_ip=${elt}
		;;
	    4)
		src_port=${elt}
		;;
	    5)
		ingress_qos=${elt}
		;;
	    6)
		dst_ip=${elt}
		;;
	    7)
		dst_port=${elt}
		;;
	    8)
		egress_qos=${elt}
		;;
	    9)
		src_port=$(tc-normalise-port ${src_port})
		dst_port=$(tc-normalise-port ${dst_port})

		if test ${interface} != 'external' ; then
		    unset snat
		else
		    if test ${SNAT_MODE} == False ; then
			unset snat
		    else
			if test ${TNAT_MODE} == True ; then
			    snat=yes
			else
			    if is-web-port ${dst_port} ; then
				unset snat
			    else
				snat=yes
			    fi
			fi
		    fi
		fi

		case ${interface} in
		    external)
			native_interface=${interface}
			;;
		    auxiliary)
			if test -z "${AUXILIARY_IS_ACTIVE}" ; then
			    ((i++))
			    continue
			fi
			native_interface=${interface}
			;;
		    *)
			native_interface=internal
			;;
		esac

		variable=${native_interface}_idev
		idev=$(eval eval 'echo -n \$"${variable}"')

		case ${interface} in
		    web)
			odev=${IF_QOS_WEB_EGRESS}
			;;
		    admin)
			odev=${IF_QOS_ADMIN_EGRESS}
			;;
		    antivirus)
			odev=${IF_QOS_ANTIVIRUS_EGRESS}
			;;
		    file)
			odev=${IF_QOS_FILE_EGRESS}
			;;
		    mon)
			odev=${IF_QOS_MON_EGRESS}
			;;
		    peer)
			odev=${IF_QOS_PEER_EGRESS}
			;;
		    rweb)
			odev=${IF_QOS_RWEB_EGRESS}
			;;
		    *)
			odev=$(get-qos-dev ${interface} egress)
			;;
		esac

		if test -z "${idev}" -o -z "${odev}" ; then
		    ((i++))
		    continue
		fi

		case ${protocol} in
		    tcp|udp)
			filter_protocol=" ip_proto ${protocol}"
			;;
		    any)
			unset filter_protocol
			;;
		    *)
			return 255
			;;
		esac

		case ${src_ip} in
		    any)
			unset filter_forth_src_ip
			unset filter_back_src_ip
			;;
		    *)
			if test -n "${snat_mode}" ; then
			    filter_forth_src_ip=" src_ip ${IP_EXTERNAL_IP}"
			    filter_back_src_ip=" dst_ip ${IP_EXTERNAL_IP}"
			else
			    filter_forth_src_ip=" src_ip ${src_ip}"
			    filter_back_src_ip=" dst_ip ${src_ip}"
			fi
			;;
		esac

		case ${dst_ip} in
		    any)
			unset filter_forth_dst_ip
			unset filter_back_dst_ip
			;;
		    *)
			filter_forth_dst_ip=" dst_ip ${dst_ip}"
			filter_back_dst_ip=" src_ip ${dst_ip}"
			;;
		esac

		case ${src_port} in
		    any)
			unset filter_forth_src_port
			unset filter_back_src_port
			;;
		    *)
			filter_forth_src_port=" src_port ${src_port}"
			filter_back_src_port=" dst_port ${src_port}"
			;;
		esac

		case ${dst_port} in
		    any)
			unset filter_forth_dst_port
			unset filter_back_dst_port
			;;
		    *)
			filter_forth_dst_port=" dst_port ${dst_port}"
			filter_back_dst_port=" src_port ${dst_port}"
			;;
		esac

		irate=$(get-qos-router-rate ${native_interface} ingress ${ingress_qos})
		orate=$(get-qos-router-rate ${native_interface} egress ${egress_qos})		
		iceil=$(get-qos-ceil ${native_interface} ingress ${irate})
		oceil=$(get-qos-ceil ${native_interface} egress ${orate})

		qosid="${QOS_HANDLE_NADMIN}:${QOS_HANDLE}"
		handle="$[${QOS_CHILD_QDISC_HANDLE_OFFSET} + ${QOS_HANDLE}]:"

		if test -n "${snat_mode}" ; then
		    class_parent=${QOS_CLASS_SELF_ID}
		    class_priority=${QOS_CLASS_SELF_ROUTER_PRIO}
		    filter_parent=${QOS_CLASS_SELF_ID}
		    filter_priority=${filter_self_priority}
		    ((filter_self_priority++))
		else
		    class_parent=${QOS_CLASS_ROUTER_ID}
		    class_priority=${QOS_CLASS_ROUTER_RULE_PRIO}
		    filter_parent=${QOS_HANDLE_NADMIN}:
		    filter_priority=${filter_router_priority}
		    ((filter_router_priority++))
		fi

		if nic-exist ${idev} ; then
		    echo
		    echo "# Rule '${rule_id}' on the ${interface} interface for ingress traffic:"
		    echo "class add dev ${idev} parent ${class_parent} classid ${qosid} htb rate ${irate}kbit ceil ${iceil}kbit ${QOS_HTB_QUANTUM} prio ${class_priority}"
		    echo "qdisc add dev ${idev} parent ${qosid} handle ${handle} ${QOS_QDISC_LEAFQ_USERS}"

		    if test -n "${filter_protocol}" ; then
			echo "filter add dev ${idev} parent ${filter_parent} priority ${filter_priority} protocol ip flower${filter_protocol}${filter_back_dst_ip}${filter_back_dst_port}${filter_back_src_ip}${filter_back_src_port} flowid ${qosid}"
		    else
			for proto in tcp udp
			do
			    echo "filter add dev ${idev} parent ${filter_parent} priority ${filter_priority} protocol ip flower ip_proto ${proto}${filter_back_dst_ip}${filter_back_dst_port}${filter_back_src_ip}${filter_back_src_port} flowid ${qosid}"
			done
		    fi
		fi

		if nic-exist ${odev} ; then
		    echo
		    echo "# Rule '${rule_id}' on the ${interface} interface for egress traffic:"
		    echo "class add dev ${odev} parent ${class_parent} classid ${qosid} htb rate ${orate}kbit ceil ${oceil}kbit ${QOS_HTB_QUANTUM} prio ${class_priority}"
		    echo "qdisc add dev ${odev} parent ${qosid} handle ${handle} ${QOS_QDISC_LEAFQ_USERS}"

		    if test -n "${filter_protocol}" ; then
			echo "filter add dev ${odev} parent ${filter_parent} priority ${filter_priority} protocol ip flower${filter_protocol}${filter_forth_src_ip}${filter_forth_src_port}${filter_forth_dst_ip}${filter_forth_dst_port} flowid ${qosid}"
		    else
			for proto in tcp udp
			do
			    echo "filter add dev ${odev} parent ${filter_parent} priority ${filter_priority} protocol ip flower ip_proto ${proto}${filter_forth_src_ip}${filter_forth_src_port}${filter_forth_dst_ip}${filter_forth_dst_port} flowid ${qosid}"
			done
		    fi
		fi

		gen-qos-index routed:${rule_id} ${QOS_LEAF_QDISC} ${native_interface} ${idev} ${odev} ${handle}

		((QOS_HANDLE++))
		;;
	    *)
		return 1
		;;
	esac
	((i++))
    done
}

gen-tc-qos-filter-self-vrrp()
{
    test ${HA_MODE} == True || return 0

    test -n "${1}" || return 1
    test -n "${2}" || return 2
    local idev=${1}
    local odevs=${2}

    local prio=${QOS_GATEWAY_VRRP_IP_FILTER_PRIO}

    local ips="${VRRP_EXTERNAL_IP_LIST} ${VRRP_WEB_IP_LIST}"
    test -z "${AUXILIARY_IS_ACTIVE}" || ips="${ips} ${VRRP_AUXILIARY_IP_LIST}"
    local odev ip

    if test ${VLAN_MODE} == True ; then
	test ${RWEB_MODE} == False || ips="${ips} ${VRRP_RWEB_IP_LIST}"
	test ${AV_MODE} == False -o ${AV_INTERNAL} = False || ips="${ips} ${VRRP_AV_IP_LIST}"
    fi

    if nic-exist ${idev} ; then
	for ip in ${ips}
	do
	    echo "filter add dev ${idev} parent ${QOS_HANDLE_NADMIN}: priority ${prio} protocol ip u32 match ip dst ${ip}/32 flowid ${QOS_CLASS_SELF_ID}"
	done
    fi

    for odev in ${odevs}
    do
	if nic-exist ${odev} ; then
	    for ip in ${ips}
	    do
		echo "filter add dev ${odev} parent ${QOS_HANDLE_NADMIN}: priority ${prio} protocol ip u32 match ip src ${ip}/32 flowid ${QOS_CLASS_SELF_ID}"
	    done
	fi
    done
}

gen-tc-qos-filter-self-fixed-ip()
{
    test -n "${1}" || return 1
    test -n "${2}" || return 2
    local idev=${1}
    local odevs=${2}

    local prio=${QOS_GATEWAY_REAL_IP_FILTER_PRIO}

    local ip ips odev
    ips="${IP_EXTERNAL_IP} ${ALIAS_IP_EXTERNAL_LIST}"
    test -z "${AUXILIARY_IS_ACTIVE}" || ips="${ips} ${IP_AUXILIARY_IP}"

    if test ${VLAN_MODE} == False ; then
	ips="${IP_INTERNAL_IP} ${ips}"
    else
	local i=0 elt range

	for elt in ${IP_VLAN_LIST}
	do
	    range=$[${i} % 3]
	    case ${range} in
		0)
		    ;;
		1)
		    ip=${elt}
		    ;;
		2)
		    ips="${ip} ${ips}"
		    ;;
		*)
		    return 1
		    ;;
	    esac
	    ((i++))
	done
    fi

    if nic-exist ${idev} ; then
	for ip in ${ips}
	do
	    echo "filter add dev ${idev} parent ${QOS_HANDLE_NADMIN}: priority ${prio} protocol ip u32 match ip dst ${ip}/32 flowid ${QOS_CLASS_SELF_ID}"
	done
    fi

    for odev in ${odevs}
    do
	if nic-exist ${odev} ; then
	    for ip in ${ips}
	    do
		echo "filter add dev ${odev} parent ${QOS_HANDLE_NADMIN}: priority ${prio} protocol ip u32 match ip src ${ip}/32 flowid ${QOS_CLASS_SELF_ID}"
	    done
	fi
    done
}

gen-tc-qos-filter-self()
{
    test -n "${1}" || return 1
    local interface=${1}

    local idev=$(get-qos-dev ${interface} ingress)
    local odevs=$(get-qos-dev ${interface} egress)    

    echo
    echo "# QoS for traffic destined to the gateway on the ${interface} interface: #"

    gen-tc-qos-filter-self-vrrp		${idev} "${odevs}"
    gen-tc-qos-filter-self-fixed-ip	${idev} "${odevs}"
}

gen-tc-qos-etc-ntp()
{
    test -n "${NTP_SERVER_LIST}" || return 0

    test -n "${1}" || return 1
    test -n "${2}" || return 2
    local idev=${1}
    local odevs=${2}

    local prio=${QOS_NTP_FILTER_PRIO}
    local odev

    ! nic-exist ${idev} || echo "filter add dev ${idev} parent ${QOS_CLASS_SELF_ID} priority ${prio} protocol ip u32 ${QOS_FILTER_UDP} match ip sport ${NTP_PORT} 0xffff flowid ${QOS_CLASS_ETC_ID}"

    for odev in ${odevs}
    do
	! nic-exist ${odev} || echo "filter add dev ${odev} parent ${QOS_CLASS_SELF_ID} priority ${prio} protocol ip u32 ${QOS_FILTER_UDP} match ip dport ${NTP_PORT} 0xffff flowid ${QOS_CLASS_ETC_ID}"
    done
}

gen-tc-qos-etc-ocsp()
{
    test ${OCSP_MODE} == True || return 0

    test -n "${1}" || return 1
    test -n "${2}" || return 2
    local idev=${1}
    local odevs=${2}

    local prio=${QOS_OCSP_FILTER_PRIO}
    local odev

    ! nic-exist ${idev} || echo "filter add dev ${idev} parent ${QOS_CLASS_SELF_ID} priority ${prio} protocol ip u32 ${QOS_FILTER_TCP} match ip dport ${OCSP_PORT} 0xffff flowid ${QOS_CLASS_ETC_ID}"

    for odev in ${odevs}
    do
	! nic-exist ${odev} || echo "filter add dev ${odev} parent ${QOS_CLASS_SELF_ID} priority ${prio} protocol ip u32 ${QOS_FILTER_TCP} match ip sport ${OCSP_PORT} 0xffff flowid ${QOS_CLASS_ETC_ID}"
    done
}

gen-tc-qos-etc-email()
{
    test -n "${EMAIL_ACCOUNT_SERVER_NR_LIST}" || return 0
    test -n "${EMAIL_ACCOUNT_SERVER_PORT}" || return 0

    test -n "${1}" || return 1
    test -n "${2}" || return 2
    local idev=${1}
    local odevs=${2}

    local prio=${QOS_EMAIL_FILTER_PRIO}
    local ip odev

    for ip in ${EMAIL_ACCOUNT_SERVER_NR_LIST}
    do
	! nic-exist ${idev} || echo "filter add dev ${idev} parent ${QOS_CLASS_SELF_ID} priority ${prio} protocol ip u32 ${QOS_FILTER_TCP} match ip src ${ip}/32 match ip sport ${EMAIL_ACCOUNT_SERVER_PORT} 0xffff flowid ${QOS_CLASS_ETC_ID}"

	for odev in ${odevs}
	do
	    ! nic-exist ${odev} || echo "filter add dev ${odev} parent ${QOS_CLASS_SELF_ID} priority ${prio} protocol ip u32 ${QOS_FILTER_TCP} match ip dst ${ip}/32 match ip dport ${EMAIL_ACCOUNT_SERVER_PORT} 0xffff flowid ${QOS_CLASS_ETC_ID}"
	done
    done
}

gen-tc-qos-etc-igmp()
{
    test -n "${1}" || return 1
    test -n "${2}" || return 2
    local idev=${1}
    local prio=${2}

    ! nic-exist ${idev} || echo "filter add dev ${idev} parent ${QOS_CLASS_SELF_ID} priority ${prio} protocol ip u32 ${QOS_FILTER_IGMP_MULTICAST} match ip dst ${IGMP_MULTICAST_IP}/32 flowid ${QOS_CLASS_ETC_ID}"
}

gen-tc-qos-etc-ipv6()
{
    test -n "${1}" || return 1
    test -n "${2}" || return 2
    local idev=${1}
    local prio=${2}

    ! nic-exist ${idev} || echo "filter add dev ${idev} parent ${QOS_CLASS_SELF_ID} priority ${prio} protocol ipv6 u32 ${QOS_FILTER_IPV6} flowid ${QOS_CLASS_ETC_ID}"
}

gen-tc-qos-etc-arp()
{
    test -n "${1}" || return 1
    test -n "${2}" || return 2
    local idev=${1}
    local odevs=${2}

    local prio=${QOS_ARP_FILTER_PRIO}
    local odev

    ! nic-exist ${idev} || echo "filter add dev ${idev} parent ${QOS_CLASS_SELF_ID} priority ${prio} protocol arp matchall flowid ${QOS_CLASS_ETC_ID}"

    for odev in ${odevs}
    do
	! nic-exist ${odev} || echo "filter add dev ${odev} parent ${QOS_CLASS_SELF_ID} priority ${prio} protocol arp matchall flowid ${QOS_CLASS_ETC_ID}"
    done
}

gen-tc-qos-etc-dhcp()
{
    test -n "${1}" || return 1
    test -n "${2}" || return 2
    local idev=${1}
    local odevs=${2}

    local prio=${QOS_DHCP_FILTER_PRIO}
    local odev

    if test "${DHCP_IS_ACTIVE}" == "True" ; then
	! nic-exist ${idev} || echo "filter add dev ${idev} parent ${QOS_CLASS_SELF_ID} priority ${prio} protocol ip u32 ${QOS_FILTER_UDP} match ip sport ${BOOTPC_PORT} 0xffff match ip dport ${BOOTPS_PORT} 0xffff flowid ${QOS_CLASS_ETC_ID}"

	for odev in ${odevs}
	do
	    ! nic-exist ${odev} || echo "filter add dev ${odev} parent ${QOS_CLASS_SELF_ID} priority ${prio} protocol ip u32 ${QOS_FILTER_UDP} match ip sport ${BOOTPS_PORT} 0xffff match ip dport ${BOOTPC_PORT} 0xffff flowid ${QOS_CLASS_ETC_ID}"
	done
    fi

    local doit
    case ${CLOUD_NAME} in
	aws|azure)
	    doit=yes
	    ;;
	*)
	    ;;
    esac

    test -n "${doit}" || test ${IP_EXTERNAL_MODE} == 'static' || doit=yes

    if test -n "${doit}" ; then
	for odev in ${odevs}
	do
	    if nic-exist ${odev} ; then
		    echo "filter add dev ${odev} parent ${QOS_CLASS_SELF_ID} priority ${prio} protocol ip u32 ${QOS_FILTER_UDP} match ip sport ${BOOTPC_PORT} 0xffff match ip dport ${BOOTPS_PORT} 0xffff flowid ${QOS_CLASS_ETC_ID}"
	    fi
	done

	if nic-exist ${idev} ; then
	    echo "filter add dev ${idev} parent ${QOS_CLASS_SELF_ID} priority ${prio} protocol ip u32 ${QOS_FILTER_UDP} match ip sport ${BOOTPS_PORT} 0xffff match ip dport ${BOOTPC_PORT} 0xffff flowid ${QOS_CLASS_ETC_ID}"
	fi
    fi
}

gen-tc-qos-etc-cloud()
{
    test -n "${1}" || return 1
    test -n "${2}" || return 2
    local idev=${1}
    local odevs=${2}

    local prio=${QOS_CLOUD_FILTER_PRIO}
    local odev

    case ${CLOUD_NAME} in
	azure)
	    for odev in ${odevs}
	    do
		if nic-exist ${odev} ; then
		    echo "filter add dev ${odev} parent ${QOS_CLASS_SELF_ID} priority ${prio} protocol ip u32 ${QOS_FILTER_TCP} match ip dst ${CLOUD_AZURE_FABRIC_IP}/32 match ip dport ${WWW_PORT} 0xffff flowid ${QOS_CLASS_ETC_ID}"

		    echo "filter add dev ${odev} parent ${QOS_CLASS_SELF_ID} priority ${prio} protocol ip u32 ${QOS_FILTER_TCP} match ip dst ${CLOUD_AZURE_FABRIC_IP}/32 match ip dport ${HTTPS_PORT} 0xffff flowid ${QOS_CLASS_ETC_ID}"

		    echo "filter add dev ${odev} parent ${QOS_CLASS_SELF_ID} priority ${prio} protocol ip u32 ${QOS_FILTER_TCP} match ip dst ${CLOUD_AZURE_FABRIC_IP}/32 match ip dport ${CLOUD_AZURE_PORT} 0xffff flowid ${QOS_CLASS_ETC_ID}"
		fi
	    done

	    if nic-exist ${idev} ; then
		echo "filter add dev ${idev} parent ${QOS_CLASS_SELF_ID} priority ${prio} protocol ip u32 ${QOS_FILTER_TCP} match ip src ${CLOUD_AZURE_FABRIC_IP}/32 match ip sport ${WWW_PORT} 0xffff flowid ${QOS_CLASS_ETC_ID}"

		echo "filter add dev ${idev} parent ${QOS_CLASS_SELF_ID} priority ${prio} protocol ip u32 ${QOS_FILTER_TCP} match ip src ${CLOUD_AZURE_FABRIC_IP}/32 match ip sport ${HTTPS_PORT} 0xffff flowid ${QOS_CLASS_ETC_ID}"

		echo "filter add dev ${idev} parent ${QOS_CLASS_SELF_ID} priority ${prio} protocol ip u32 ${QOS_FILTER_TCP} match ip src ${CLOUD_AZURE_FABRIC_IP}/32 match ip sport ${CLOUD_AZURE_PORT} 0xffff flowid ${QOS_CLASS_ETC_ID}"
	    fi
	    ;;
	*)
	    ;;
    esac
}

gen-tc-qos-etc-vrrp()
{
    test ${HA_MODE} == True || return 0

    test -n "${1}" || return 1
    test -n "${2}" || return 2
    test -n "${3}" || return 3
    local idev=${1}
    local odevs=${2}
    local interface=${3}

    local prio=${QOS_VRRP_FILTER_PRIO}
    local net_prf net_prf_list
    local odev

    case ${interface} in
	internal)
	    net_prf_list="${IP_WEB_NET}/${IP_WEB_PRF}"
	    test ${VLAN_MODE} == False || net_prf_list="${net_prf_list} ${IP_RWEB_NET}/${IP_RWEB_PRF}"
	    ;;
	external)
	    net_prf_list="${IP_EXTERNAL_NET}/${IP_EXTERNAL_PRF}"
	    ;;
	auxiliary)
	    test -n "${AUXILIARY_IS_ACTIVE}" || return 0
	    net_prf_list="${IP_AUXILIARY_NET}/${IP_AUXILIARY_PRF}"
	    ;;
	*)
	    return 11
	    ;;
    esac

    if nic-exist ${idev} ; then
	for net_prf in ${net_prf_list}
	do
	    echo "filter add dev ${idev} parent ${QOS_CLASS_SELF_ID} priority ${prio} protocol ip u32 ${QOS_FILTER_AH} match ip src ${net_prf} match ip dst ${VRRP_MULTICAST_IP}/32 flowid ${QOS_CLASS_ETC_ID}"
	done
    fi

    for odev in ${odevs}
    do
	if nic-exist ${odev} ; then
	    for net_prf in ${net_prf_list}
	    do
		echo "filter add dev ${odev} parent ${QOS_CLASS_SELF_ID} priority ${prio} protocol ip u32 ${QOS_FILTER_AH} match ip src ${net_prf} match ip dst ${VRRP_MULTICAST_IP}/32 flowid ${QOS_CLASS_ETC_ID}"
	    done
	fi
    done
}

gen-tc-qos-etc-icmp()
{
    test -n "${1}" || return 1
    test -n "${2}" || return 2
    local idev=${1}
    local odevs=${2}

    local prio=${QOS_ICMP_FILTER_PRIO}
    local odev

    ! nic-exist ${idev} || echo "filter add dev ${idev} parent ${QOS_CLASS_SELF_ID} priority ${prio} protocol ip u32 ${QOS_FILTER_ICMP} flowid ${QOS_CLASS_ETC_ID}"

    for odev in ${odevs}
    do
	! nic-exist ${odev} || echo "filter add dev ${odev} parent ${QOS_CLASS_SELF_ID} priority ${prio} protocol ip u32 ${QOS_FILTER_ICMP} flowid ${QOS_CLASS_ETC_ID}"
    done
}

gen-tc-qos-etc-ldap-auth()
{
    test-ldap-auth-state || return 0

    test -n "${1}" || return 1
    test -n "${2}" || return 2
    test -n "${3}" || return 0
    local idev=${1}
    local odevs=${2}
    local servers=${3}

    local prio=${QOS_AUTH_FILTER_PRIO}
    local elt range i=0
    local ip port
    local odev

    if nic-exist ${idev} ; then
	for elt in ${servers}
	do
	    range=$[${i} % 3]
	    case ${range} in
		0)
		    ;;
		1)
		    ip=${elt}
		    ;;
		2)
		    port=${elt}

		    echo "filter add dev ${idev} parent ${QOS_CLASS_SELF_ID} priority ${prio} protocol ip u32 ${QOS_FILTER_UDP} match ip src ${ip}/32 match ip sport ${port} 0xffff flowid ${QOS_CLASS_ETC_ID}"
		    echo "filter add dev ${idev} parent ${QOS_CLASS_SELF_ID} priority ${prio} protocol ip u32 ${QOS_FILTER_TCP} match ip src ${ip}/32 match ip sport ${port} 0xffff flowid ${QOS_CLASS_ETC_ID}"
		    ;;
		*)
		    ;;
	    esac
	    ((i++))
	done
    fi

    for odev in ${odevs}
    do
	if nic-exist ${odev} ; then
	    i=0
	    for elt in ${servers}
	    do
		range=$[${i} % 3]
		case ${range} in
		    0)
			;;
		    1)
			ip=${elt}
			;;
		    2)
			port=${elt}

			echo "filter add dev ${odev} parent ${QOS_CLASS_SELF_ID} priority ${prio} protocol ip u32 ${QOS_FILTER_UDP} match ip dst ${ip}/32 match ip dport ${port} 0xffff flowid ${QOS_CLASS_ETC_ID}"
			echo "filter add dev ${odev} parent ${QOS_CLASS_SELF_ID} priority ${prio} protocol ip u32 ${QOS_FILTER_TCP} match ip dst ${ip}/32 match ip dport ${port} 0xffff flowid ${QOS_CLASS_ETC_ID}"
			;;
		    *)
			;;
		esac
		((i++))
	    done
	fi
    done
}

gen-tc-qos-etc-kerberos-auth()
{
    test-kerberos-auth-state || return 0

    test -n "${1}" || return 1
    test -n "${2}" || return 2
    test -n "${3}" || return 0
    local idev=${1}
    local odevs=${2}
    local ips=${3}

    local prio=${QOS_AUTH_FILTER_PRIO}
    local ip odev

    if nic-exist ${idev} ; then
	for ip in ${ips}
	do
	    echo "filter add dev ${idev} parent ${QOS_CLASS_SELF_ID} priority ${prio} protocol ip u32 ${QOS_FILTER_TCP} match ip src ${ip}/32 match ip sport ${KERBEROS_PORT} 0xffff flowid ${QOS_CLASS_ETC_ID}"
	    echo "filter add dev ${idev} parent ${QOS_CLASS_SELF_ID} priority ${prio} protocol ip u32 ${QOS_FILTER_TCP} match ip src ${ip}/32 match ip sport ${KERBEROS_ADM_PORT} 0xffff flowid ${QOS_CLASS_ETC_ID}"
	    echo "filter add dev ${idev} parent ${QOS_CLASS_SELF_ID} priority ${prio} protocol ip u32 ${QOS_FILTER_UDP} match ip src ${ip}/32 match ip sport ${KPASSWD_PORT} 0xffff flowid ${QOS_CLASS_ETC_ID}"
	    test ${AUTHENTICATE_AD_MODE} == False || \
		echo "filter add dev ${idev} parent ${QOS_CLASS_SELF_ID} priority ${prio} protocol ip u32 ${QOS_FILTER_TCP} match ip src ${ip}/32 match ip sport ${LDAP_PORT} 0xffff flowid ${QOS_CLASS_ETC_ID}"
	done
    fi

    for odev in ${odevs}
    do
	if nic-exist ${odev} ; then
	    for ip in ${ips}
	    do
		echo "filter add dev ${odev} parent ${QOS_CLASS_SELF_ID} priority ${prio} protocol ip u32 ${QOS_FILTER_TCP} match ip dst ${ip}/32 match ip dport ${KERBEROS_PORT} 0xffff flowid ${QOS_CLASS_ETC_ID}"
		echo "filter add dev ${odev} parent ${QOS_CLASS_SELF_ID} priority ${prio} protocol ip u32 ${QOS_FILTER_TCP} match ip dst ${ip}/32 match ip dport ${KERBEROS_ADM_PORT} 0xffff flowid ${QOS_CLASS_ETC_ID}"
		echo "filter add dev ${odev} parent ${QOS_CLASS_SELF_ID} priority ${prio} protocol ip u32 ${QOS_FILTER_UDP} match ip dst ${ip}/32 match ip dport ${KPASSWD_PORT} 0xffff flowid ${QOS_CLASS_ETC_ID}"
		test ${AUTHENTICATE_AD_MODE} == False || \
		    echo "filter add dev ${odev} parent ${QOS_CLASS_SELF_ID} priority ${prio} protocol ip u32 ${QOS_FILTER_TCP} match ip dst ${ip}/32 match ip dport ${LDAP_PORT} 0xffff flowid ${QOS_CLASS_ETC_ID}"
	    done
	fi
    done
}

gen-tc-qos-etc-auth-nr()
{
    test -n "${1}" || return 1
    local interface=${1}

    local idev=$(get-qos-dev ${interface} ingress)
    local odevs=$(get-qos-dev ${interface} egress)

    gen-tc-qos-etc-ldap-auth ${idev} "${odevs}" "${LDAP_SERVER_NR_LIST}"
    gen-tc-qos-etc-kerberos-auth ${idev} "${odevs}" "${KERBEROS_SERVER_NR_LIST}"
}

gen-tc-qos-etc-auth-ip()
{
    test -n "${1}" || return 1
    local interface=${1}

    local idev=$(get-qos-dev ${interface} ingress)
    local odevs=$(get-qos-dev ${interface} egress)

    gen-tc-qos-etc-ldap-auth ${idev} "${odevs}" "${LDAP_SERVER_IP_LIST}"
    gen-tc-qos-etc-kerberos-auth ${idev} "${odevs}" "${KERBEROS_SERVER_IP_LIST}"
}

gen-tc-qos-etc()
{
    test -n "${1}" || return 1
    local interface=${1}

    local idev=$(get-qos-dev ${interface} ingress)
    local odev odevs1 odevs=$(get-qos-dev ${interface} egress)
    local handle="$[${QOS_CHILD_QDISC_HANDLE_OFFSET} + ${QOS_HANDLE_ETC}]:"

    echo
    echo "# QoS for ETC traffic on the ${interface} interface (ingress:${idev}, egress: ${odevs}):"

    if nic-exist ${idev} ; then
	echo "class add dev ${idev} parent ${QOS_CLASS_SELF_ID} classid ${QOS_CLASS_ETC_ID} htb rate ${QOS_ETC_RATE} ${QOS_HTB_QUANTUM} prio ${QOS_CLASS_ETC_PRIO}"
	echo "qdisc add dev ${idev} parent ${QOS_CLASS_ETC_ID} handle ${handle} ${QOS_QDISC_LEAFQ_ETC}"
    fi

    for odev in ${odevs}
    do
	if nic-exist ${odev} ; then
	    echo "class add dev ${odev} parent ${QOS_CLASS_SELF_ID} classid ${QOS_CLASS_ETC_ID} htb rate ${QOS_ETC_RATE} ${QOS_HTB_QUANTUM} prio ${QOS_CLASS_ETC_PRIO}"
	    echo "qdisc add dev ${odev} parent ${QOS_CLASS_ETC_ID} handle ${handle} ${QOS_QDISC_LEAFQ_ETC}"
	    odevs1="${odevs1} ${odev}"
	fi
    done
    odevs1=${odevs1:1}

    gen-tc-qos-etc-arp      ${idev} "${odevs}"
    gen-tc-qos-etc-dhcp     ${idev} "${odevs}"
    gen-tc-qos-etc-cloud     ${idev} "${odevs}"
    gen-tc-qos-etc-vrrp     ${idev} "${odevs}" ${interface}
    gen-tc-qos-etc-icmp     ${idev} "${odevs}"
    gen-tc-qos-etc-auth-ip  ${idev} "${odevs}"
    gen-tc-qos-etc-ntp      ${idev} "${odevs}"
    gen-tc-qos-etc-ocsp     ${idev} "${odevs}"
    gen-tc-qos-etc-email    ${idev} "${odevs}"

    gen-qos-index etc: ${QOS_LEAF_QDISC} ${interface} ${idev} ${odevs1// /:} ${handle}
}

gen-tc-backend-rweb-top()
{
    test -n "${1}" || return 1
    test -n "${2}" || return 2
    test -n "${3}" || return 3
    test -n "${4}" || return 4
    local interface=${1}
    local idev=${2}
    local odev=${3}
    local class=${4}

    local flow='rweb'

    local irate=$(get-qos-rate ${interface} ingress ${flow})
    local iceil=$(get-qos-ceil ${interface} ingress ${irate})
    local orate=$(get-qos-rate ${interface} egress ${flow})
    local oceil=$(get-qos-ceil ${interface} egress ${orate})

    ! nic-exist ${idev} || echo "class add dev ${idev} parent ${QOS_CLASS_SELF_ID} classid ${class} htb rate ${irate}kbit ceil ${iceil}kbit ${QOS_HTB_QUANTUM} prio ${QOS_CLASS_RWEB_BACKEND_PRIO}"
    ! nic-exist ${odev} || echo "class add dev ${odev} parent ${QOS_CLASS_SELF_ID} classid ${class} htb rate ${orate}kbit ceil ${oceil}kbit ${QOS_HTB_QUANTUM} prio ${QOS_CLASS_RWEB_BACKEND_PRIO}"
}

gen-tc-backend-rweb()
{
    test ${RWEB_MODE} == True || return 0

    local elt range i=0
    local interface ip port qos
    local idev odev rweb_class if_name flow='rweb'
    local qosid irate iceil orate oceil rate ceil
    local handle

    echo
    echo "# QoS for rweb backend web servers traffic:"

    gen-tc-backend-rweb-top internal ${IF_QOS_INTERNAL_INGRESS} ${IF_QOS_RWEB_EGRESS} ${QOS_CLASS_RWEB_BACKEND_ID}

    for elt in ${ACCESS_RWEB_LIST}
    do
	range=$[${i} % 4]
	case ${range} in
	    0)
		interface=${elt}
		;;
	    1)
		ip=${elt}
		;;
	    2)
		port=${elt}
		;;
	    3)
		qos=${elt}

		handle=$[${QOS_CHILD_QDISC_HANDLE_OFFSET} + ${QOS_HANDLE}]:

		case ${interface} in
		    rweb)
			idev=${IF_QOS_INTERNAL_INGRESS}
			odev=${IF_QOS_RWEB_EGRESS}
			rweb_class=${QOS_CLASS_RWEB_BACKEND_ID}
			if_name='internal'
			;;
		    external)
			idev=${IF_QOS_EXTERNAL_INGRESS}
			odev=${IF_QOS_EXTERNAL_EGRESS}
			rweb_class=${QOS_CLASS_RWEB_PUBLIC_ID}
			if_name='external'
			;;
		    vpnipsec)
			((i++))
			continue
			;;
		    *)
			;;
		esac

		qosid="${QOS_HANDLE_NADMIN}:${QOS_HANDLE}"

		irate=$(get-qos-rate ${if_name} ingress ${flow})
		iceil=$(get-qos-ceil ${if_name} ingress ${irate})
		orate=$(get-qos-rate ${if_name} egress ${flow})
		oceil=$(get-qos-ceil ${if_name} egress ${orate})

		if nic-exist ${idev} ; then
		    rate=$(get-qos-percent ${irate} ${qos})
		    ceil=$(get-qos-ceil ${if_name} ingress ${rate})

		    echo "class add dev ${idev} parent ${rweb_class} classid ${qosid} htb rate ${rate}kbit ceil ${ceil}kbit ${QOS_HTB_QUANTUM} prio ${QOS_CLASS_RWEB_BACKEND_PRIO}"
		    echo "qdisc add dev ${idev} parent ${qosid} handle ${handle} ${QOS_QDISC_LEAFQ_RWEB}"
		    echo "filter add dev ${idev} parent ${QOS_CLASS_SELF_ID} priority ${QOS_RWEB_FILTER_PRIO} protocol ip u32 match ip src ${ip}/32 match ip sport ${port} 0xffff flowid ${qosid}"
		fi

		if nic-exist ${odev} ; then
		    rate=$(get-qos-percent ${orate} ${qos})
		    ceil=$(get-qos-ceil ${if_name} egress ${rate})

		    echo "class add dev ${odev} parent ${rweb_class} classid ${qosid} htb rate ${rate}kbit ceil ${ceil}kbit ${QOS_HTB_QUANTUM} prio ${QOS_CLASS_RWEB_BACKEND_PRIO}"
		    echo "qdisc add dev ${odev} parent ${qosid} handle ${handle} ${QOS_QDISC_LEAFQ_RWEB}"
		    echo "filter add dev ${odev} parent ${QOS_CLASS_SELF_ID} priority ${QOS_RWEB_FILTER_PRIO} protocol ip handle $(printf 0x%x/${QOS_MARK_MASK} ${QOS_HANDLE} 2> /dev/null) fw flowid ${qosid}"
		fi

		gen-qos-index rweb:${ip} ${QOS_LEAF_QDISC} ${if_name} ${idev} ${odev} ${handle}
		((QOS_HANDLE++))
		;;
	    *)
		return 255
		;;
	esac
	((i++))
    done
}

gen-tc-ha-share-peer()
{
    test -n "${1}" || return 1
    test -n "${2}" || return 2
    local irate=${1}
    local orate=${2}

    local i=0 elt range
    local ip qos
    local qosid rate ceil

    for elt in ${PEER_SHARE_LIST} ${PEER_HA_LIST}
    do
	range=$[${i} % 2]
	case ${range} in
	    0)
		ip=${elt}
		;;
	    1)
		qos=${elt}

		qosid="${QOS_HANDLE_NADMIN}:${QOS_HANDLE}"

		if nic-exist ${IF_QOS_INTERNAL_INGRESS} ; then
		    rate=$(get-qos-percent ${irate} ${qos})
		    ceil=$(get-qos-ceil internal ingress ${rate})
		    echo "class add dev ${IF_QOS_INTERNAL_INGRESS} parent ${QOS_CLASS_PEER_ID} classid ${qosid} htb rate ${rate}kbit ceil ${ceil}kbit ${QOS_HTB_QUANTUM} prio ${QOS_CLASS_PEER_PRIO}"
		    echo "qdisc add dev ${IF_QOS_INTERNAL_INGRESS} parent ${qosid} handle ${QOS_HANDLE}: ${QOS_QDISC_LEAFQ_PEER}"
		    echo "filter add dev ${IF_QOS_INTERNAL_INGRESS} parent ${QOS_CLASS_SELF_ID} priority ${QOS_PEER_FILTER_PRIO} protocol ip u32 ${QOS_FILTER_UDP} match ip sport ${PEER_HTCP_PORT} 0xffff match ip dport ${PEER_HTCP_PORT} 0xffff flowid ${qosid}"
		    echo "filter add dev ${IF_QOS_INTERNAL_INGRESS} parent ${QOS_CLASS_SELF_ID} priority ${QOS_PEER_FILTER_PRIO} protocol ip u32 ${QOS_FILTER_TCP} match ip sport ${PEER_HTTP_PORT} 0xffff flowid ${qosid}"
		    echo "filter add dev ${IF_QOS_INTERNAL_INGRESS} parent ${QOS_CLASS_SELF_ID} priority ${QOS_PEER_FILTER_PRIO} protocol ip u32 ${QOS_FILTER_TCP} match ip dport ${PEER_HTTP_PORT} 0xffff flowid ${qosid}"
		fi

		if nic-exist ${IF_QOS_PEER_EGRESS} ; then
		    rate=$(get-qos-percent ${orate} ${qos})
		    ceil=$(get-qos-ceil internal egress ${rate})
		    echo "class add dev ${IF_QOS_PEER_EGRESS} parent ${QOS_CLASS_PEER_ID} classid ${qosid} htb rate ${rate}kbit ceil ${ceil}kbit ${QOS_HTB_QUANTUM} prio ${QOS_CLASS_PEER_PRIO}"
		    echo "qdisc add dev ${IF_QOS_PEER_EGRESS} parent ${qosid} handle ${QOS_HANDLE}: ${QOS_QDISC_LEAFQ_PEER}"
		    echo "filter add dev ${IF_QOS_PEER_EGRESS} parent ${QOS_CLASS_SELF_ID} priority ${QOS_PEER_FILTER_PRIO} protocol ip handle $(printf 0x%x/${QOS_MARK_MASK} ${QOS_HANDLE} 2> /dev/null) fw flowid ${qosid}"
		fi

		gen-qos-index peer:${ip} ${QOS_LEAF_QDISC} internal ${IF_QOS_INTERNAL_INGRESS} ${IF_QOS_PEER_EGRESS} ${QOS_HANDLE}:

		((QOS_HANDLE++))
		;;
	    *)
		return 1
		;;
	esac
	((i++))
    done
}

gen-tc-dhcp-peer()
{
    test ${DHCP_IS_ACTIVE} = True -a -n "${DHCP_PEER_LIST}" || return 0

    test -n "${1}" || return 1
    test -n "${2}" || return 2
    test -n "${3}" || return 3
    test -n "${4}" || return 4
    local irate=${1}
    local orate=${2}
    local iceil=${3}
    local oceil=${4}

    local handle="$[${QOS_CHILD_QDISC_HANDLE_OFFSET} + ${QOS_HANDLE}]:"
    local ip=${DHCP_PEER_LIST/* }
    local qosid

    qosid="${QOS_HANDLE_NADMIN}:${QOS_HANDLE}"

    if nic-exist ${IF_QOS_INTERNAL_INGRESS} ; then
	echo "class add dev ${IF_QOS_INTERNAL_INGRESS} parent ${QOS_CLASS_PEER_ID} classid ${qosid} htb rate ${irate}kbit ceil ${iceil}kbit ${QOS_HTB_QUANTUM} prio ${QOS_CLASS_PEER_PRIO}"
	echo "qdisc add dev ${IF_QOS_INTERNAL_INGRESS} parent ${qosid} handle ${handle} pfifo"
	echo "filter add dev ${IF_QOS_INTERNAL_INGRESS} parent ${QOS_CLASS_SELF_ID} priority ${QOS_PEER_FILTER_PRIO} protocol ip u32 match ip src ${ip}/32 flowid ${qosid}"
    fi

    if nic-exist ${IF_QOS_PEER_EGRESS} ; then
	echo "class add dev ${IF_QOS_PEER_EGRESS} parent ${QOS_CLASS_PEER_ID} classid ${qosid} htb rate ${orate}kbit ceil ${oceil}kbit ${QOS_HTB_QUANTUM} prio ${QOS_CLASS_PEER_PRIO}"
	echo "qdisc add dev ${IF_QOS_PEER_EGRESS} parent ${qosid} handle ${handle} pfifo"
	echo "filter add dev ${IF_QOS_PEER_EGRESS} parent ${QOS_CLASS_SELF_ID} priority ${QOS_PEER_FILTER_PRIO} protocol ip handle $(printf 0x%x/${QOS_MARK_MASK} ${QOS_HANDLE} 2> /dev/null) fw flowid ${qosid}"
    fi

    gen-qos-index dhcp: pfifo internal ${IF_QOS_INTERNAL_INGRESS} ${IF_QOS_PEER_EGRESS} ${handle}

    ((QOS_HANDLE++))
}

gen-tc-previous-peer()
{
    test -n "${1}" || return 1
    test -n "${2}" || return 2
    local irate=${1}
    local orate=${2}

    local i=0 elt range
    local ip qos
    local rate ceil
    local qosid handle

    for elt in ${PEER_PREVIOUS_LIST}
    do
	range=$[${i} % 2]
	case ${range} in
	    0)
		ip=${elt}
		;;
	    1)
		qos=${elt}
		handle="$[${QOS_CHILD_QDISC_HANDLE_OFFSET} + ${QOS_HANDLE}]:"
		qosid="${QOS_HANDLE_NADMIN}:${QOS_HANDLE}"

		if nic-exist ${IF_QOS_INTERNAL_INGRESS} ; then
		    echo
		    rate=$(get-qos-percent ${irate} ${qos})
		    ceil=$(get-qos-ceil internal ingress ${rate})
		    echo "class add dev ${IF_QOS_INTERNAL_INGRESS} parent ${QOS_CLASS_PEER_ID} classid ${qosid} htb rate ${rate}kbit ceil ${ceil}kbit ${QOS_HTB_QUANTUM} prio ${QOS_CLASS_PEER_PRIO}"
		    echo "qdisc add dev ${IF_QOS_INTERNAL_INGRESS} parent ${qosid} handle ${handle} ${QOS_QDISC_LEAFQ_PEER}"

		    echo "filter add dev ${IF_QOS_INTERNAL_INGRESS} parent ${QOS_CLASS_SELF_ID} priority ${QOS_PEER_FILTER_PRIO} protocol ip u32 match ip src ${ip}/32 match ip dport ${PEER_HTTP_PORT} 0xffff flowid ${qosid}"
		    echo "filter add dev ${IF_QOS_INTERNAL_INGRESS} parent ${QOS_CLASS_SELF_ID} priority ${QOS_PEER_FILTER_PRIO} protocol ip  u32 match ip src ${ip}/32 match ip dport ${WWW_PORT} 0xffff flowid ${qosid}"
		fi

		if nic-exist ${IF_QOS_WEB_EGRESS} ; then
		    echo
		    rate=$(get-qos-percent ${orate} ${qos})
		    ceil=$(get-qos-ceil internal egress ${rate})
		    echo "class add dev ${IF_QOS_WEB_EGRESS} parent ${QOS_CLASS_PEER_ID} classid ${qosid} htb rate ${rate}kbit ceil ${ceil}kbit ${QOS_HTB_QUANTUM} prio ${QOS_CLASS_PEER_PRIO}"
		    echo "qdisc add dev ${IF_QOS_WEB_EGRESS} parent ${qosid} handle ${handle} ${QOS_QDISC_LEAFQ_PEER}"

		    echo "filter add dev ${IF_QOS_WEB_EGRESS} parent ${QOS_CLASS_SELF_ID} priority ${QOS_PEER_FILTER_PRIO} protocol ip handle $(printf 0x%x/${QOS_MARK_MASK} ${QOS_HANDLE} 2> /dev/null) fw flowid ${qosid}"
		fi

		gen-qos-index peer:${ip} ${QOS_LEAF_QDISC} internal ${IF_QOS_INTERNAL_INGRESS} ${IF_QOS_WEB_EGRESS} ${handle}
		
		((QOS_HANDLE++))
		;;
	    *)
		return 1
		;;
	esac
	((i++))
    done
}

gen-tc-peer()
{
    local go

    if test -n "${PEER_SHARE_LIST}" -o \
	    -n "${PEER_HA_LIST}" -o \
	    -n "${PEER_PREVIOUS_LIST}" ; then
	go='yes'
    fi

    test ${DHCP_IS_ACTIVE} == False -o -z "${DHCP_PEER_LIST}" || go='yes'

    test -n "${go}" || return 0
	
    local irate=$(get-qos-rate internal ingress peer)
    local iceil=$(get-qos-ceil internal ingress ${irate})
    local orate=$(get-qos-rate internal egress peer)
    local oceil=$(get-qos-ceil internal egress ${orate})

    echo
    echo "# QoS for peer gateway traffic:"

    ! nic-exist ${IF_QOS_INTERNAL_INGRESS} || echo "class add dev ${IF_QOS_INTERNAL_INGRESS} parent ${QOS_CLASS_SELF_ID} classid ${QOS_CLASS_PEER_ID} htb rate ${irate}kbit ceil ${iceil}kbit ${QOS_HTB_QUANTUM} prio ${QOS_CLASS_PEER_PRIO}"
    ! nic-exist ${IF_QOS_PEER_EGRESS} || echo "class add dev ${IF_QOS_PEER_EGRESS} parent ${QOS_CLASS_SELF_ID} classid ${QOS_CLASS_PEER_ID} htb rate ${orate}kbit ceil ${oceil}kbit ${QOS_HTB_QUANTUM} prio ${QOS_CLASS_PEER_PRIO}"

    gen-tc-ha-share-peer ${irate} ${orate}
    gen-tc-previous-peer ${irate} ${orate}
    gen-tc-dhcp-peer ${irate} ${orate} ${iceil} ${oceil}
}

gen-tc-web-top()
{
    test -n "${1}" || return 1
    test -n "${2}" || return 2
    test -n "${3}" || return 3
    local interface=${1}
    local idev=${2}
    local odev=${3}

    local flow='web'
    local irate=$(get-qos-rate ${interface} ingress ${flow})
    local iceil=$(get-qos-ceil ${interface} ingress ${irate})
    local orate=$(get-qos-rate ${interface} egress ${flow})
    local oceil=$(get-qos-ceil ${interface} egress ${orate})

    ! nic-exist ${idev} || echo "class add dev ${idev} parent ${QOS_CLASS_SELF_ID} classid ${QOS_CLASS_WEB_CLIENT_ID} htb rate ${irate}kbit ceil ${iceil}kbit ${QOS_HTB_QUANTUM} prio ${QOS_CLASS_WEB_CLIENT_PRIO}"
    ! nic-exist ${odev} || echo "class add dev ${odev} parent ${QOS_CLASS_SELF_ID} classid ${QOS_CLASS_WEB_CLIENT_ID} htb rate ${orate}kbit ceil ${oceil}kbit ${QOS_HTB_QUANTUM} prio ${QOS_CLASS_WEB_CLIENT_PRIO}"
}

gen-tc-web-nolist()
{
    test -n "${1}" || return 1
    test -n "${2}" || return 2
    test -n "${3}" || return 3
    local interface=${1}
    local idev=${2}
    local odev=${3}

    local flow='web'
    local irate=$(get-qos-rate ${interface} ingress ${flow})
    local iceil=$(get-qos-ceil ${interface} ingress ${irate})
    local orate=$(get-qos-rate ${interface} egress ${flow})
    local oceil=$(get-qos-ceil ${interface} egress ${orate})

    local handle="$[${QOS_CHILD_QDISC_HANDLE_OFFSET} + ${QOS_HANDLE}]:"
    local qosid="${QOS_HANDLE_NADMIN}:${QOS_HANDLE}"

    if nic-exist ${idev} ; then
	echo "class add dev ${idev} parent ${QOS_CLASS_WEB_CLIENT_ID} classid ${qosid} htb rate ${irate}kbit ceil ${iceil}kbit ${QOS_HTB_QUANTUM} prio ${QOS_CLASS_WEB_CLIENT_PRIO}"
	echo "qdisc add dev ${idev} parent ${qosid} handle ${handle} ${QOS_QDISC_LEAFQ_USERS}"

	if test ${WEB_MODE} == True ; then
	    echo "filter add dev ${idev} parent ${QOS_CLASS_SELF_ID} priority ${QOS_WEB_FILTER_PRIO} protocol ip u32 ${QOS_FILTER_TCP} match ip dport ${PROXY_PORT} 0xffff flowid ${qosid}"
	    echo "filter add dev ${idev} parent ${QOS_CLASS_SELF_ID} priority ${QOS_WEB_FILTER_PRIO} protocol ip u32 ${QOS_FILTER_TCP} match ip dport ${WWW_PORT} 0xffff flowid ${qosid}"
	fi

	if test ${DNS_MODE} == True ; then
	    echo "filter add dev ${idev} parent ${QOS_CLASS_SELF_ID} priority ${QOS_DNS_FILTER_PRIO} protocol ip u32 ${QOS_FILTER_UDP} match ip dport ${DOMAIN_PORT} 0xffff flowid ${qosid}"
	    echo "filter add dev ${idev} parent ${QOS_CLASS_SELF_ID} priority ${QOS_DNS_FILTER_PRIO} protocol ip u32 ${QOS_FILTER_TCP} match ip dport ${DOMAIN_PORT} 0xffff flowid ${qosid}"
	fi

	# Reverse Websites accessed from internal end-users
	if test ${WEB_MODE} == True -a ${RWEB_MODE} == True ; then
	    local rweb_ip
	    for rweb_ip in ${RWEB_HTTP_IP_LIST}
	    do
		echo "filter add dev ${idev} parent ${QOS_CLASS_SELF_ID} priority ${QOS_WEB_FILTER_PRIO} protocol ip u32 ${QOS_FILTER_TCP} match ip dst ${rweb_ip}/32 match ip dport ${WWW_PORT} 0xffff flowid ${qosid}"
	    done
	    for rweb_ip in ${RWEB_HTTPS_IP_LIST}
	    do
		echo "filter add dev ${idev} parent ${QOS_CLASS_SELF_ID} priority ${QOS_WEB_FILTER_PRIO} protocol ip u32 ${QOS_FILTER_TCP} match ip dst ${rweb_ip}/32 match ip dport ${HTTPS_PORT} 0xffff flowid ${qosid}"
	    done
	fi
    fi

    if nic-exist ${odev} ; then
	echo "class add dev ${odev} parent ${QOS_CLASS_WEB_CLIENT_ID} classid ${qosid} htb rate ${orate}kbit ceil ${oceil}kbit ${QOS_HTB_QUANTUM} prio ${QOS_CLASS_WEB_CLIENT_PRIO}"
	echo "qdisc add dev ${odev} parent ${qosid} handle ${handle} ${QOS_QDISC_LEAFQ_USERS}"
	echo "filter add dev ${odev} parent ${QOS_CLASS_SELF_ID} priority ${QOS_WEB_FILTER_PRIO} protocol ip handle $(printf 0x%x/${QOS_MARK_MASK} ${QOS_HANDLE} 2> /dev/null) fw flowid ${qosid}"
    fi

    gen-qos-index web: ${QOS_LEAF_QDISC} ${interface} ${idev} ${odev} ${handle}
}

gen-tc-web-list()
{
    local elt range i=0
    local interface ip px qos
    local idev odev if_name
    local irate iceil orate oceil rate ceil
    local qosid handle rweb_ip
    local flow='web'

    for elt in ${ACCESS_WEB_IF_IP_PX_LIST}
    do
	range=$[${i} % 4]
	case ${range} in
	    0)
		interface=${elt}
		;;
	    1)
		ip=${elt}
		;;
	    2)
		px=${elt}
		;;
	    3)
		qos=${elt}

		handle="$[${QOS_CHILD_QDISC_HANDLE_OFFSET} + ${QOS_HANDLE}]:"

		case ${interface} in
		    internal|web)
			idev=${IF_QOS_INTERNAL_INGRESS}
			odev=${IF_QOS_WEB_EGRESS}
			if_name=internal
			;;
		    admin)
			idev=${IF_QOS_INTERNAL_INGRESS}
			odev=${IF_QOS_ADMIN_EGRESS}
			if_name=internal
			;;
		    antivirus)
			idev=${IF_QOS_INTERNAL_INGRESS}
			odev=${IF_QOS_AV_EGRESS}
			if_name=internal
			;;
		    file)
			idev=${IF_QOS_INTERNAL_INGRESS}
			odev=${IF_QOS_FILE_EGRESS}
			if_name=internal
			;;
		    mon)
			idev=${IF_QOS_INTERNAL_INGRESS}
			odev=${IF_QOS_MON_EGRESS}
			if_name=internal
			;;
		    rweb)
			idev=${IF_QOS_INTERNAL_INGRESS}
			odev=${IF_QOS_RWEB_EGRESS}
			if_name=internal
			;;
		    auxiliary)
			if test -z "${AUXILIARY_IS_ACTIVE}" ; then
			    ((i++))
			    continue
			fi
			idev=${IF_QOS_AUXILIARY_INGRESS}
			odev=${IF_QOS_AUXILIARY_EGRESS}
			if_name=${interface}
			;;
		    vpnipsec)
			((i++))
			continue
			;;
		    *)
			;;
		esac

		qosid="${QOS_HANDLE_NADMIN}:${QOS_HANDLE}"

		irate=$(get-qos-rate ${if_name} ingress ${flow})
		iceil=$(get-qos-ceil ${if_name} ingress ${irate})
		orate=$(get-qos-rate ${if_name} egress ${flow})
		oceil=$(get-qos-ceil ${if_name} egress ${orate})

		if nic-exist ${idev} ; then
		    rate=$(get-qos-percent ${irate} ${qos})
		    ceil=$(get-qos-ceil ${if_name} ingress ${rate})
		    echo "class add dev ${idev} parent ${QOS_CLASS_WEB_CLIENT_ID} classid ${qosid} htb rate ${rate}kbit ceil ${ceil}kbit ${QOS_HTB_QUANTUM} prio ${QOS_CLASS_WEB_CLIENT_PRIO}"
		    echo "qdisc add dev ${idev} parent ${qosid} handle ${handle} ${QOS_QDISC_LEAFQ_USERS}"

		    if test ${WEB_MODE} == True ; then
			echo "filter add dev ${idev} parent ${QOS_CLASS_SELF_ID} priority ${QOS_WEB_FILTER_PRIO} protocol ip u32 ${QOS_FILTER_TCP} match ip src ${ip}/${px} match ip dport ${PROXY_PORT} 0xffff flowid ${qosid}"
			echo "filter add dev ${idev} parent ${QOS_CLASS_SELF_ID} priority ${QOS_WEB_FILTER_PRIO} protocol ip u32 ${QOS_FILTER_TCP} match ip src ${ip}/${px} match ip dport ${WWW_PORT} 0xffff flowid ${qosid}"
		    fi

		    if test ${DNS_MODE} == True ; then
			echo "filter add dev ${idev} parent ${QOS_CLASS_SELF_ID} priority ${QOS_DNS_FILTER_PRIO} protocol ip u32 ${QOS_FILTER_UDP} match ip src ${ip}/${px} match ip dport ${DOMAIN_PORT} 0xffff flowid ${qosid}"
			echo "filter add dev ${idev} parent ${QOS_CLASS_SELF_ID} priority ${QOS_DNS_FILTER_PRIO} protocol ip u32 ${QOS_FILTER_TCP} match ip src ${ip}/${px} match ip dport ${DOMAIN_PORT} 0xffff flowid ${qosid}"
		    fi

		    # Reverse Websites accessed from internal end-users
		    if test ${WEB_MODE} == True -a ${RWEB_MODE} == True ; then
			for rweb_ip in ${RWEB_HTTP_IP_LIST}
			do
			    echo "filter add dev ${idev} parent ${QOS_CLASS_SELF_ID} priority ${QOS_WEB_FILTER_PRIO} protocol ip u32 ${QOS_FILTER_TCP} match ip src ${ip}/${px} match ip dst ${rweb_ip}/32 match ip dport ${WWW_PORT} 0xffff flowid ${qosid}"
			done
			for rweb_ip in ${RWEB_HTTPS_IP_LIST}
			do
			    echo "filter add dev ${idev} parent ${QOS_CLASS_SELF_ID} priority ${QOS_WEB_FILTER_PRIO} protocol ip u32 ${QOS_FILTER_TCP} match ip src ${ip}/${px} match ip dst ${rweb_ip}/32 match ip dport ${HTTPS_PORT} 0xffff flowid ${qosid}"	    
			done
		    fi
		fi

		if nic-exist ${odev} ; then
		    rate=$(get-qos-percent ${orate} ${qos})
		    ceil=$(get-qos-ceil ${if_name} egress ${rate})
		    echo "class add dev ${odev} parent ${QOS_CLASS_WEB_CLIENT_ID} classid ${qosid} htb rate ${rate}kbit ceil ${ceil}kbit ${QOS_HTB_QUANTUM} prio ${QOS_CLASS_WEB_CLIENT_PRIO}"
		    echo "qdisc add dev ${odev} parent ${qosid} handle ${handle} ${QOS_QDISC_LEAFQ_USERS}"

		    echo "filter add dev ${odev} parent ${QOS_CLASS_SELF_ID} priority ${QOS_WEB_FILTER_PRIO} protocol ip handle $(printf 0x%x/${QOS_MARK_MASK} ${QOS_HANDLE} 2> /dev/null) fw flowid ${qosid}"
		fi

		gen-qos-index web:${ip}/${px} ${QOS_LEAF_QDISC} ${if_name} ${idev} ${odev} ${handle}
		((QOS_HANDLE++))
		;;
	    *)
		return 1
		;;
	esac
	((i++))
    done
}

gen-tc-web()
{
    test ${WEB_MODE} == True -o ${DNS_MODE} == True || return 0

    echo
    echo "# QoS for web traffic between the explicit web proxy and its clients:"

    gen-tc-web-top internal ${IF_QOS_INTERNAL_INGRESS} ${IF_QOS_WEB_EGRESS}
    test -z "${AUXILIARY_IS_ACTIVE}" || gen-tc-web-top auxiliary ${IF_QOS_AUXILIARY_INGRESS} ${IF_QOS_AUXILIARY_EGRESS}

    if test -n "${ACCESS_WEB_IF_IP_PX_LIST}" ; then
	gen-tc-web-list
	return 0
    fi

    gen-tc-web-nolist internal ${IF_QOS_INTERNAL_INGRESS} ${IF_QOS_WEB_EGRESS}
    test -z "${AUXILIARY_IS_ACTIVE}" || gen-tc-web-nolist auxiliary ${IF_QOS_AUXILIARY_INGRESS} ${IF_QOS_AUXILIARY_EGRESS}

    ((QOS_HANDLE++))
}

gen-tc-tweb-nolist()
{
    test -n "${1}" || return 1
    test -n "${2}" || return 2
    test -n "${3}" || return 3
    local interface=${1}
    local idev=${2}
    local odev=${3}

    local flow='tweb'
    local irate=$(get-qos-rate ${interface} ingress ${flow})
    local iceil=$(get-qos-ceil ${interface} ingress ${irate})
    local orate=$(get-qos-rate ${interface} egress ${flow})
    local oceil=$(get-qos-ceil ${interface} egress ${orate})

    local qosid="${QOS_HANDLE_NADMIN}:${QOS_HANDLE}"
    local handle="$[${QOS_CHILD_QDISC_HANDLE_OFFSET} + ${QOS_HANDLE}]:"

    if nic-exist ${idev} ; then
	echo "class add dev ${idev} parent ${QOS_CLASS_TWEB_CLIENT_ID} classid ${qosid} htb rate ${irate}kbit ceil ${iceil}kbit ${QOS_HTB_QUANTUM} prio ${QOS_CLASS_TWEB_CLIENT_PRIO}"
	echo "qdisc add dev ${idev} parent ${qosid} handle ${handle} ${QOS_QDISC_LEAFQ_USERS}"

	echo "filter add dev ${idev} parent ${QOS_HANDLE_NADMIN}: priority ${QOS_TWEB_FILTER_PRIO} protocol ip u32 match ip dport ${WWW_PORT} 0xffff flowid ${qosid}"
	echo "filter add dev ${idev} parent ${QOS_HANDLE_NADMIN}: priority ${QOS_TWEB_FILTER_PRIO} protocol ip u32 match ip dport ${HTTPS_PORT} 0xffff flowid ${qosid}"
    fi

    if nic-exist ${odev} ; then
	echo "class add dev ${odev} parent ${QOS_CLASS_TWEB_CLIENT_ID} classid ${qosid} htb rate ${orate}kbit ceil ${oceil}kbit ${QOS_HTB_QUANTUM} prio ${QOS_CLASS_TWEB_CLIENT_PRIO}"
	echo "qdisc add dev ${odev} parent ${qosid} handle ${handle} ${QOS_QDISC_LEAFQ_USERS}"
	echo "filter add dev ${odev} parent ${QOS_HANDLE_NADMIN}: priority ${QOS_TWEB_FILTER_PRIO} protocol ip handle $(printf 0x%x/${QOS_MARK_MASK} ${QOS_HANDLE} 2> /dev/null) fw flowid ${qosid}"
    fi

    gen-qos-index tweb: ${QOS_LEAF_QDISC} ${interface} ${idev} ${odev} ${handle}
}

gen-tc-tweb-list()
{
    local elt range i=0
    local interface ip px qos
    local idev odev flow='tweb'
    local irate iceil orate oceil rate ceil
    local qosid handle rweb_ip

    for elt in ${TRANSPARENT_IF_IP_PX_LIST}
    do
	range=$[${i} % 4]
	case ${range} in
	    0)
		interface=${elt}
		;;
	    1)
		ip=${elt}
		;;
	    2)
		px=${elt}
		;;
	    3)
		qos=${elt}
		handle="$[${QOS_CHILD_QDISC_HANDLE_OFFSET} + ${QOS_HANDLE}]:"

		case ${interface} in
		    internal)
			idev=${IF_QOS_INTERNAL_INGRESS}
			odev=${IF_QOS_WEB_EGRESS}
			;;
		    auxiliary)
			if test -z "${AUXILIARY_IS_ACTIVE}" ; then
			    ((i++))
			    continue
			fi
			idev=${IF_QOS_AUXILIARY_INGRESS}
			odev=${IF_QOS_AUXILIARY_EGRESS}
			;;
		    vpnipsec)
			((i++))
			continue
			;;
		    *)
			;;
		esac

		qosid="${QOS_HANDLE_NADMIN}:${QOS_HANDLE}"

		irate=$(get-qos-rate ${interface} ingress ${flow})
		iceil=$(get-qos-ceil ${interface} ingress ${irate})
		orate=$(get-qos-rate ${interface} egress ${flow})
		oceil=$(get-qos-ceil ${interface} egress ${orate})

		if nic-exist ${idev} ; then
		    rate=$(get-qos-percent ${irate} ${qos})
		    ceil=$(get-qos-ceil ${interface} ingress ${rate})
		    echo "class add dev ${idev} parent ${QOS_CLASS_TWEB_CLIENT_ID} classid ${qosid} htb rate ${rate}kbit ceil ${ceil}kbit ${QOS_HTB_QUANTUM} prio ${QOS_CLASS_TWEB_CLIENT_PRIO}"
		    echo "qdisc add dev ${idev} parent ${qosid} handle ${handle} ${QOS_QDISC_LEAFQ_USERS}"
		    echo "filter add dev ${idev} parent ${QOS_HANDLE_NADMIN}: priority ${QOS_TWEB_FILTER_PRIO} protocol ip u32 match ip src ${ip}/${px} match ip dport ${WWW_PORT} 0xffff flowid ${qosid}"
		    echo "filter add dev ${idev} parent ${QOS_HANDLE_NADMIN}: priority ${QOS_TWEB_FILTER_PRIO} protocol ip u32 match ip src ${ip}/${px} match ip dport ${HTTPS_PORT} 0xffff flowid ${qosid}"
		fi

		if nic-exist ${odev} ; then
		    rate=$(get-qos-percent ${orate} ${qos})
		    ceil=$(get-qos-ceil ${interface} egress ${rate})
		    echo "class add dev ${odev} parent ${QOS_CLASS_TWEB_CLIENT_ID} classid ${qosid} htb rate ${rate}kbit ceil ${ceil}kbit ${QOS_HTB_QUANTUM} prio ${QOS_CLASS_TWEB_CLIENT_PRIO}"
		    echo "qdisc add dev ${odev} parent ${qosid} handle ${handle} ${QOS_QDISC_LEAFQ_USERS}"
		    echo "filter add dev ${odev} parent ${QOS_HANDLE_NADMIN}: priority ${QOS_TWEB_FILTER_PRIO} protocol ip handle $(printf 0x%x/${QOS_MARK_MASK} ${QOS_HANDLE} 2> /dev/null) fw flowid ${qosid}"
		fi

		gen-qos-index tweb:${ip}/${px} ${QOS_LEAF_QDISC} ${interface} ${idev} ${odev} ${handle}

		((QOS_HANDLE++))
		;;
	    *)
		return 1
		;;
	esac
	((i++))
    done
}

gen-tc-tweb-top()
{
    test -n "${1}" || return 1
    test -n "${2}" || return 2
    test -n "${3}" || return 3
    local interface=${1}
    local idev=${2}
    local odev=${3}

    local flow='tweb'    
    local irate=$(get-qos-rate ${interface} ingress ${flow})
    local iceil=$(get-qos-ceil ${interface} ingress ${irate})
    local orate=$(get-qos-rate ${interface} egress ${flow})
    local oceil=$(get-qos-ceil ${interface} egress ${orate})

    ! nic-exist ${idev} || echo "class add dev ${idev} parent ${QOS_CLASS_SELF_ID} classid ${QOS_CLASS_TWEB_CLIENT_ID} htb rate ${irate}kbit ceil ${iceil}kbit ${QOS_HTB_QUANTUM} prio ${QOS_CLASS_TWEB_CLIENT_PRIO}"
    ! nic-exist ${odev} || echo "class add dev ${odev} parent ${QOS_CLASS_SELF_ID} classid ${QOS_CLASS_TWEB_CLIENT_ID} htb rate ${orate}kbit ceil ${oceil}kbit ${QOS_HTB_QUANTUM} prio ${QOS_CLASS_TWEB_CLIENT_PRIO}"
}

gen-tc-tweb()
{
    test ${TRANSPARENT_MODE} == True -a ${TNAT_MODE} == True || return 0

    echo 
    echo "# QoS for web traffic between the transparent web proxy and its clients:"

    gen-tc-tweb-top internal ${IF_QOS_INTERNAL_INGRESS} ${IF_QOS_WEB_EGRESS}
    test -z "${AUXILIARY_IS_ACTIVE}" || gen-tc-tweb-top auxiliary ${IF_QOS_AUXILIARY_INGRESS} ${IF_QOS_AUXILIARY_EGRESS}

    if test -n "${TRANSPARENT_IF_IP_PX_LIST}" ; then
	gen-tc-tweb-list
	return 0
    fi

    gen-tc-tweb-nolist internal ${IF_QOS_INTERNAL_INGRESS} ${IF_QOS_WEB_EGRESS}
    test -z "${AUXILIARY_IS_ACTIVE}" || gen-tc-tweb-nolist auxiliary ${IF_QOS_AUXILIARY_INGRESS} ${IF_QOS_AUXILIARY_EGRESS}

    ((QOS_HANDLE++))
}

gen-tc-av()
{
    test ${AV_MODE} == True || return 0
    test -n "${ACCESS_IF_IP_PX_AV_LIST}" || return 0

    local elt range i=0
    local ip px qos
    local qosid handle irate iceil orate oceil rate ceil
    local idev odev flow='antivirus'

    local interface interfaces=$(get-qos-logical-interfaces with-ipsec)

    echo "# QoS for AV traffic:"

    for interface in ${interfaces}
    do
	case ${interface} in
	    internal)
		test ${AV_INTERNAL} == True || continue
		idev=${IF_QOS_INTERNAL_INGRESS}
		odev=${IF_QOS_AV_EGRESS}
		;;
	    external)
		test ${AV_EXTERNAL} ==  True || continue
		idev=${IF_QOS_EXTERNAL_INGRESS}
		odev=${IF_QOS_EXTERNAL_EGRESS}
		;;
	    auxiliary)
		test -n "${AUXILIARY_IS_ACTIVE}" || continue
		test ${AV_AUXILIARY} ==  True || continue
		idev=${IF_QOS_AUXILIARY_INGRESS}
		odev=${IF_QOS_AUXILIARY_EGRESS}
		;;
	    vpnipsec)
		continue
		;;
	    *)
		;;
	esac

	if nic-exist ${idev} ; then
	    irate=$(get-qos-rate ${interface} ingress ${flow})
	    iceil=$(get-qos-ceil ${interface} ingress ${irate})

	    echo "class add dev ${idev} parent ${QOS_CLASS_SELF_ID} classid ${QOS_CLASS_AV_ID} htb rate ${irate}kbit ceil ${iceil}kbit ${QOS_HTB_QUANTUM} prio ${QOS_CLASS_AV_PRIO}"
	fi

	if nic-exist ${odev} ; then
	    orate=$(get-qos-rate ${interface} egress ${flow})
	    oceil=$(get-qos-ceil ${interface} egress ${orate})

	    echo "class add dev ${odev} parent ${QOS_CLASS_SELF_ID} classid ${QOS_CLASS_AV_ID} htb rate ${orate}kbit ceil ${oceil}kbit ${QOS_HTB_QUANTUM} prio ${QOS_CLASS_AV_PRIO}"
	fi
    done

    for elt in ${ACCESS_IF_IP_PX_AV_LIST}
    do
	range=$[${i} % 4]
	case ${range} in
	    0)
		interface=${elt}
		;;
	    1)
		ip=${elt}
		;;
	    2)
		px=${elt}
		;;
	    3)
		qos=${elt}

		case ${interface} in
		    internal)
			idev=${IF_QOS_INTERNAL_INGRESS}
			odev=${IF_QOS_AV_EGRESS}
			;;
		    external)
			idev=${IF_QOS_EXTERNAL_INGRESS}
			odev=${IF_QOS_EXTERNAL_EGRESS}
			;;
		    auxiliary)
			if test -z "${AUXILIARY_IS_ACTIVE}" ; then
			    ((i++))
			    continue
			fi
			idev=${IF_QOS_AUXILIARY_INGRESS}
			odev=${IF_QOS_AUXILIARY_EGRESS}
			;;
		    vpnipsec)
			((i++))
			continue
			;;
		    *)
			;;
		esac

		qosid="${QOS_HANDLE_NADMIN}:${QOS_HANDLE}"
		handle="$[${QOS_CHILD_QDISC_HANDLE_OFFSET} + ${QOS_HANDLE}]:"

		if nic-exist ${idev} ; then
		    rate=$(get-qos-percent ${irate} ${qos})
		    ceil=$(get-qos-ceil ${interface} ingress ${rate})
		    echo "class add dev ${idev} parent ${QOS_CLASS_AV_ID} classid ${qosid} htb rate ${rate}kbit ceil ${ceil}kbit ${QOS_HTB_QUANTUM} prio ${QOS_CLASS_AV_PRIO}"
		    echo "qdisc add dev ${idev} parent ${qosid} handle ${handle} ${QOS_QDISC_LEAFQ_AV}"

		    echo "filter add dev ${idev} parent ${QOS_CLASS_SELF_ID} priority ${QOS_AV_FILTER_PRIO} protocol ip u32 match ip src ${ip}/${px} match ip dport ${AV_PORT} 0xffff flowid ${qosid}"
		    echo "filter add dev ${idev} parent ${QOS_CLASS_SELF_ID} priority ${QOS_AV_FILTER_PRIO} protocol ip u32 match ip src ${ip}/${px} match ip dport ${AV_STREAM_MIN_PORT} 0xf000 flowid ${qosid}"

		fi

		if nic-exist ${odev} -a ${interface} != vpnipsec ; then
		    rate=$(get-qos-percent ${orate} ${qos})
		    ceil=$(get-qos-ceil ${interface} egress ${rate})
		    echo "class add dev ${odev} parent ${QOS_CLASS_AV_ID} classid ${qosid} htb rate ${rate}kbit ceil ${ceil}kbit ${QOS_HTB_QUANTUM} prio ${QOS_CLASS_AV_PRIO}"
		    echo "qdisc add dev ${odev} parent ${qosid} handle ${handle} ${QOS_QDISC_LEAFQ_AV}"

		    echo "filter add dev ${odev} parent ${QOS_CLASS_SELF_ID} priority ${QOS_AV_FILTER_PRIO} protocol ip handle $(printf 0x%x/${QOS_MARK_MASK} ${QOS_HANDLE} 2> /dev/null) fw flowid ${qosid}"
		fi

		gen-qos-index antivirus:${ip}/${px} ${QOS_LEAF_QDISC} ${interface} ${idev} ${odev} ${handle}

		((QOS_HANDLE++))
		;;
	    *)
		return 1
		;;
	esac
	((i++))
    done

    echo
}

gen-tc-vpnipsec()
{
    test ${VPN_IPSEC_MODE} == True || return 0

    local idev=$(get-qos-dev external ingress)
    local odev=$(get-qos-dev external egress)

    local irate=$(get-qos-rate external ingress vpnipsec)
    local iceil=$(get-qos-ceil external ingress ${irate})
    local orate=$(get-qos-rate external egress vpnipsec)
    local oceil=$(get-qos-ceil external egress ${orate})
    local handle="$[${QOS_CHILD_QDISC_HANDLE_OFFSET} + ${QOS_HANDLE_IPSEC}]:"

    if nic-exist ${idev} ; then
	echo
	echo "# QoS for IPsec ingress traffic on the external (${idev}) interface:"
	echo "class add dev ${idev} parent ${QOS_CLASS_SELF_ID} classid ${QOS_CLASS_IPSEC_ID} htb rate ${irate}kbit ceil ${iceil}kbit ${QOS_HTB_QUANTUM} prio ${QOS_CLASS_IPSEC_PRIO}"
	echo "qdisc add dev ${idev} parent ${QOS_CLASS_IPSEC_ID} handle ${handle} ${QOS_QDISC_LEAFQ_IPSEC}"

	echo "filter add dev ${idev} parent ${QOS_CLASS_SELF_ID} priority ${QOS_IKE_FILTER_PRIO} protocol ip u32 ${QOS_FILTER_UDP} match ip dport ${ISAKMP_PORT} 0xffff flowid ${QOS_CLASS_IPSEC_ID}"
	echo "filter add dev ${idev} parent ${QOS_CLASS_SELF_ID} priority ${QOS_IKE_FILTER_PRIO} protocol ip u32 ${QOS_FILTER_UDP} match ip dport ${NATT_PORT} 0xffff flowid ${QOS_CLASS_IPSEC_ID}"
	echo "filter add dev ${idev} parent ${QOS_CLASS_SELF_ID} priority ${QOS_ESP_FILTER_PRIO} protocol ip u32 ${QOS_FILTER_ESP} flowid ${QOS_CLASS_IPSEC_ID}"
    fi

    if nic-exist ${odev} ; then
	echo
	echo "# QoS for IPsec egress traffic on the external (${odev}) interface:"
	echo "class add dev ${odev} parent ${QOS_CLASS_SELF_ID} classid ${QOS_CLASS_IPSEC_ID} htb rate ${orate}kbit ceil ${oceil}kbit ${QOS_HTB_QUANTUM} prio ${QOS_CLASS_IPSEC_PRIO}"
	echo "qdisc add dev ${odev} parent ${QOS_CLASS_IPSEC_ID} handle ${handle} ${QOS_QDISC_LEAFQ_IPSEC}"

	echo "filter add dev ${odev} parent ${QOS_CLASS_SELF_ID} priority ${QOS_IKE_FILTER_PRIO} protocol ip u32 ${QOS_FILTER_UDP} match ip sport ${ISAKMP_PORT} 0xffff flowid ${QOS_CLASS_IPSEC_ID}"
	echo "filter add dev ${odev} parent ${QOS_CLASS_SELF_ID} priority ${QOS_IKE_FILTER_PRIO} protocol ip u32 ${QOS_FILTER_UDP} match ip sport ${NATT_PORT} 0xffff flowid ${QOS_CLASS_IPSEC_ID}"
    fi

    gen-qos-index vpnipsec: htb external ${idev} ${odev} ${handle}
}

gen-tc-external-rweb-top()
{
    test ${RWEB_MODE} == True || return 0

    local irate=$(get-qos-rate external ingress rweb)
    local iceil=$(get-qos-ceil external ingress ${irate})
    local orate=$(get-qos-rate external egress rweb)
    local oceil=$(get-qos-ceil external egress ${orate})

    echo
    echo "# Top rules for rWeb traffic on the external interface:"
    ! nic-exist ${IF_QOS_EXTERNAL_INGRESS} || echo "class add dev ${IF_QOS_EXTERNAL_INGRESS} parent ${QOS_CLASS_SELF_ID} classid ${QOS_CLASS_RWEB_PUBLIC_ID} htb rate ${irate}kbit ceil ${iceil}kbit ${QOS_HTB_QUANTUM} prio ${QOS_CLASS_RWEB_PUBLIC_PRIO}"
    ! nic-exist ${IF_QOS_EXTERNAL_EGRESS} || echo "class add dev ${IF_QOS_EXTERNAL_EGRESS} parent ${QOS_CLASS_SELF_ID} classid ${QOS_CLASS_RWEB_PUBLIC_ID} htb rate ${orate}kbit ceil ${oceil}kbit ${QOS_HTB_QUANTUM} prio ${QOS_CLASS_RWEB_PUBLIC_PRIO}"
}

gen-tc-external-rweb()
{
    test ${RWEB_MODE} == True || return 0

    local elt range i=0
    local ip_protocol qos
    local ip protocol

    local irate=$(get-qos-rate external ingress rweb)
    local orate=$(get-qos-rate external egress rweb)

    local qosid handle

    echo
    echo "# QoS for exposed rweb sites traffic on the external interface:"

    for elt in ${RWEB_QOS_LIST}
    do
	range=$[${i} % 2]
	case ${range} in
	    0)
		ip_protocol=${elt}
		ip=${ip_protocol/:*}
		protocol=${ip_protocol/*:}
		;;
	    1)
		qos=${elt}

		qosid="${QOS_HANDLE_NADMIN}:${QOS_HANDLE}"
		handle="$[${QOS_CHILD_QDISC_HANDLE_OFFSET} + ${QOS_HANDLE}]:"

		if nic-exist ${IF_QOS_EXTERNAL_INGRESS} ; then
		    rate=$(get-qos-percent ${irate} ${qos})
		    ceil=$(get-qos-ceil external ingress ${rate})
		    echo "class add dev ${IF_QOS_EXTERNAL_INGRESS} parent ${QOS_CLASS_RWEB_PUBLIC_ID} classid ${qosid} htb rate ${rate}kbit ceil ${ceil}kbit ${QOS_HTB_QUANTUM} prio ${QOS_CLASS_RWEB_PUBLIC_PRIO}"
		    echo "qdisc add dev ${IF_QOS_EXTERNAL_INGRESS} parent ${qosid} handle ${handle} ${QOS_QDISC_LEAFQ_RUSERS}"

		    case ${protocol} in
			http)
			    echo "filter add dev ${IF_QOS_EXTERNAL_INGRESS} parent ${QOS_CLASS_SELF_ID} priority ${QOS_RWEB_FILTER_PRIO} protocol ip u32 match ip dport ${WWW_PORT} 0xffff flowid ${qosid}"
			    ;;
			https)
			    echo "filter add dev ${IF_QOS_EXTERNAL_INGRESS} parent ${QOS_CLASS_SELF_ID} priority ${QOS_RWEB_FILTER_PRIO} protocol ip u32 match ip dport ${HTTPS_PORT} 0xffff flowid ${qosid}"
			    ;;
			*)
			    ;;
		    esac
		fi

		if nic-exist ${IF_QOS_EXTERNAL_EGRESS} ; then
		    rate=$(get-qos-percent ${orate} ${qos})
		    ceil=$(get-qos-ceil external egress ${rate})
		    echo "class add dev ${IF_QOS_EXTERNAL_EGRESS} parent ${QOS_CLASS_RWEB_PUBLIC_ID} classid ${qosid} htb rate ${rate}kbit ceil ${ceil}kbit ${QOS_HTB_QUANTUM} prio ${QOS_CLASS_RWEB_PUBLIC_PRIO}"
		    echo "qdisc add dev ${IF_QOS_EXTERNAL_EGRESS} parent ${qosid} handle ${handle} ${QOS_QDISC_LEAFQ_RUSERS}"

		    echo "filter add dev ${IF_QOS_EXTERNAL_EGRESS} parent ${QOS_CLASS_SELF_ID} priority ${QOS_RWEB_FILTER_PRIO} protocol ip handle $(printf 0x%x/${QOS_MARK_MASK} ${QOS_HANDLE} 2> /dev/null) fw flowid ${qosid}"
		fi

		case ${protocol} in
		    http)
			gen-qos-index rweb:${ip} ${QOS_LEAF_QDISC} external ${IF_QOS_EXTERNAL_INGRESS} ${IF_QOS_EXTERNAL_EGRESS} ${handle}
			;;
		    https)
			gen-qos-index rweb:${ip}[TLS] ${QOS_LEAF_QDISC} external ${IF_QOS_EXTERNAL_INGRESS} ${IF_QOS_EXTERNAL_EGRESS} ${handle}
			;;
		    *)
			;;
		esac

		((QOS_HANDLE++))
		;;
	    *)
		return 1
		;;
	esac
	((i++))
    done

    echo
}

gen-tc-external-web()
{
    test ${WEB_MODE} == True \
	 -o ${TRANSPARENT_MODE} == True \
	 -o ${AV_MODE} == True || return 0

    local elt range i=0
    local ip port qos
    local handle classid

    local irate=$(get-qos-rate external ingress web)
    local iceil=$(get-qos-ceil external ingress ${irate})
    local orate=$(get-qos-rate external egress web)
    local oceil=$(get-qos-ceil external egress ${orate})
    local leafq_handle="$[${QOS_CHILD_QDISC_HANDLE_OFFSET} + ${QOS_HANDLE_WEB_PUBLIC}]:"

    if nic-exist ${IF_QOS_EXTERNAL_INGRESS} ; then
	echo
	echo "# QoS for web ingress traffic on the external interface:"
	echo "class add dev ${IF_QOS_EXTERNAL_INGRESS} parent ${QOS_CLASS_SELF_ID} classid ${QOS_CLASS_WEB_PUBLIC_ID} htb rate ${irate}kbit ceil ${iceil}kbit ${QOS_HTB_QUANTUM} prio ${QOS_CLASS_WEB_PUBLIC_PRIO}"
	echo "qdisc add dev ${IF_QOS_EXTERNAL_INGRESS} parent ${QOS_CLASS_WEB_PUBLIC_ID} handle ${leafq_handle} ${QOS_QDISC_LEAFQ_USERS}"

	echo "filter add dev ${IF_QOS_EXTERNAL_INGRESS} parent ${QOS_CLASS_SELF_ID} priority ${QOS_WEB_FILTER_PRIO} protocol ip u32 ${QOS_FILTER_TCP} match ip sport ${WWW_PORT} 0xffff flowid ${QOS_CLASS_WEB_PUBLIC_ID}"
	echo "filter add dev ${IF_QOS_EXTERNAL_INGRESS} parent ${QOS_CLASS_SELF_ID} priority ${QOS_WEB_FILTER_PRIO} protocol ip u32 ${QOS_FILTER_TCP} match ip sport ${HTTPS_PORT} 0xffff flowid ${QOS_CLASS_WEB_PUBLIC_ID}"
	echo "filter add dev ${IF_QOS_EXTERNAL_INGRESS} parent ${QOS_CLASS_SELF_ID} priority ${QOS_WEB_FILTER_PRIO} protocol ip u32 ${QOS_FILTER_TCP} match ip sport ${DOMAIN_PORT} 0xffff flowid ${QOS_CLASS_WEB_PUBLIC_ID}"
	echo "filter add dev ${IF_QOS_EXTERNAL_INGRESS} parent ${QOS_CLASS_SELF_ID} priority ${QOS_WEB_FILTER_PRIO} protocol ip u32 ${QOS_FILTER_UDP} match ip sport ${DOMAIN_PORT} 0xffff flowid ${QOS_CLASS_WEB_PUBLIC_ID}"
    fi

    if nic-exist ${IF_QOS_EXTERNAL_EGRESS} ; then
	echo
	echo "# QoS for web egress traffic on the external interface:"
	echo "class add dev ${IF_QOS_EXTERNAL_EGRESS} parent ${QOS_CLASS_SELF_ID} classid ${QOS_CLASS_WEB_PUBLIC_ID} htb rate ${orate}kbit ceil ${oceil}kbit ${QOS_HTB_QUANTUM} prio ${QOS_CLASS_WEB_PUBLIC_PRIO}"
	echo "qdisc add dev ${IF_QOS_EXTERNAL_EGRESS} parent ${QOS_CLASS_WEB_PUBLIC_ID} handle ${leafq_handle} ${QOS_QDISC_LEAFQ_USERS}"

	echo "filter add dev ${IF_QOS_EXTERNAL_EGRESS} parent ${QOS_CLASS_SELF_ID} priority ${QOS_WEB_FILTER_PRIO} protocol ip u32 ${QOS_FILTER_TCP} match ip dport ${WWW_PORT} 0xffff flowid ${QOS_CLASS_WEB_PUBLIC_ID}"
	echo "filter add dev ${IF_QOS_EXTERNAL_EGRESS} parent ${QOS_CLASS_SELF_ID} priority ${QOS_WEB_FILTER_PRIO} protocol ip u32 ${QOS_FILTER_TCP} match ip dport ${HTTPS_PORT} 0xffff flowid ${QOS_CLASS_WEB_PUBLIC_ID}"
	echo "filter add dev ${IF_QOS_EXTERNAL_EGRESS} parent ${QOS_CLASS_SELF_ID} priority ${QOS_WEB_FILTER_PRIO} protocol ip u32 ${QOS_FILTER_TCP} match ip dport ${DOMAIN_PORT} 0xffff flowid ${QOS_CLASS_WEB_PUBLIC_ID}"
	echo "filter add dev ${IF_QOS_EXTERNAL_EGRESS} parent ${QOS_CLASS_SELF_ID} priority ${QOS_WEB_FILTER_PRIO} protocol ip u32 ${QOS_FILTER_UDP} match ip dport ${DOMAIN_PORT} 0xffff flowid ${QOS_CLASS_WEB_PUBLIC_ID}"
    fi

    gen-qos-index web: ${QOS_LEAF_QDISC} external ${IF_QOS_EXTERNAL_INGRESS} ${IF_QOS_EXTERNAL_EGRESS} ${leafq_handle}

    if test -n "${PEER_NEXT_LIST}" ; then
	echo
	echo "# QoS for ingress & egress next peer traffic on the external interface:"
    fi

    handle=${QOS_HANDLE_PEER_NEXT_INIT}

    for elt in ${PEER_NEXT_LIST}
    do
	range=$[${i} % 3]
	case ${range} in
	    0)
		ip=${elt}
		;;
	    1)
		port=${elt}
		;;
	    2)
		qos=${elt}

		leafq_handle="$[${QOS_CHILD_QDISC_HANDLE_OFFSET} + ${handle}]:"
		classid=${QOS_HANDLE_NADMIN}:${handle}

		echo
		if nic-exist ${IF_QOS_EXTERNAL_INGRESS} ; then
		    rate=$(get-qos-percent ${irate} ${qos})
		    ceil=$(get-qos-ceil external ingress ${rate})
		    echo "class add dev ${IF_QOS_EXTERNAL_INGRESS} parent ${QOS_CLASS_WEB_PUBLIC_ID} classid ${classid} htb rate ${rate}kbit ceil ${ceil}kbit ${QOS_HTB_QUANTUM} prio ${QOS_CLASS_WEB_PUBLIC_PRIO}"
		    echo "qdisc add dev ${IF_QOS_EXTERNAL_INGRESS} parent ${classid} handle ${leafq_handle} ${QOS_QDISC_LEAFQ_USERS}"
		    echo "filter add dev ${IF_QOS_EXTERNAL_INGRESS} parent ${QOS_CLASS_SELF_ID} priority ${QOS_WEB_FILTER_PRIO} protocol ip u32 ${QOS_FILTER_TCP} match ip sport ${port} 0xffff flowid ${classid}"
		fi

		if nic-exist ${IF_QOS_EXTERNAL_EGRESS} ; then
		    rate=$(get-qos-percent ${orate} ${qos})
		    ceil=$(get-qos-ceil external egress ${rate})
		    echo "class add dev ${IF_QOS_EXTERNAL_EGRESS} parent ${QOS_CLASS_WEB_PUBLIC_ID} classid ${classid} htb rate ${rate}kbit ceil ${ceil}kbit ${QOS_HTB_QUANTUM} prio ${QOS_CLASS_WEB_PUBLIC_PRIO}"
		    echo "qdisc add dev ${IF_QOS_EXTERNAL_EGRESS} parent ${classid} handle ${leafq_handle} ${QOS_QDISC_LEAFQ_USERS}"
		    echo "filter add dev ${IF_QOS_EXTERNAL_EGRESS} parent ${QOS_CLASS_SELF_ID} priority ${QOS_WEB_FILTER_PRIO} protocol ip u32 ${QOS_FILTER_TCP} match ip dport ${port} 0xffff flowid ${classid}"
		fi

		gen-qos-index peer:${ip}:${port} ${QOS_LEAF_QDISC} external ${IF_QOS_EXTERNAL_INGRESS} ${IF_QOS_EXTERNAL_EGRESS} ${leafq_handle}

		((handle++))
		;;
	    *)
		;;
	esac
	((i++))
    done
}

gen-tc-qos-file-top()
{
    local interface interfaces=$(get-qos-logical-interfaces with-ipsec)
    local flow='file'

    echo
    echo "# QoS for file traffic between the gateway and trusted file servers:"

    for interface in ${interfaces}
    do
	case ${interface} in
	    vpnipsec)
		continue
		;;
	    internal|external|auxiliary)
		;;
	    *)
		;;
	esac

	local idev=$(get-qos-dev ${interface} ingress)
	local odev odevs1 odevs=$(get-qos-dev ${interface} egress)
	local irate=$(get-qos-rate ${interface} ingress ${flow})
	local iceil=$(get-qos-ceil ${interface} ingress ${irate})
	local orate=$(get-qos-rate ${interface} egress ${flow})
	local oceil=$(get-qos-ceil ${interface} egress ${orate})
	local handle="$[${QOS_CHILD_QDISC_HANDLE_OFFSET} + ${QOS_HANDLE_FILE}]:"

	if nic-exist ${idev} ; then
	    echo "class add dev ${idev} parent ${QOS_CLASS_SELF_ID} classid ${QOS_CLASS_FILE_ID} htb rate ${irate}kbit ceil ${iceil}kbit ${QOS_HTB_QUANTUM} prio ${QOS_CLASS_FILE_PRIO}"
	    echo "qdisc add dev ${idev} parent ${QOS_CLASS_FILE_ID} handle ${handle} pfifo"
	fi

	for odev in ${odevs}
	do
	    if nic-exist ${odev} -a ${interface} != vpnipsec ; then
		echo "class add dev ${odev} parent ${QOS_CLASS_SELF_ID} classid ${QOS_CLASS_FILE_ID} htb rate ${orate}kbit ceil ${oceil}kbit ${QOS_HTB_QUANTUM} prio ${QOS_CLASS_FILE_PRIO} "
		echo "qdisc add dev ${odev} parent ${QOS_CLASS_FILE_ID} handle ${handle} pfifo"
		odevs1="${odevs1} ${odev}"
	    fi
	done
	odevs1=${odevs1:1}

	gen-qos-index file: pfifo ${interface} ${idev} ${odevs1// /:} ${handle}
    done
}

gen-tc-qos-file-filter()
{
    test -n "${1}" || return 1
    test -n "${2}" || return 0
    local resolution_type=${1}
    local access_file_if_ip_list=${2}

    local interfaces interface
    local if_ip ip idev odev odevs

    case ${resolution_type} in
	ip)
	    interfaces=${ACCESS_FILE_USED_IF_LIST}
	    ;;
	nr)
	    ;;
	*)
	    ;;
    esac

    for if_ip in ${access_file_if_ip_list}
    do
	interface=${if_ip/:*}
	ip=${if_ip#*:}

	case ${interface} in
	    auxiliary)
		test -n "${AUXILIARY_IS_ACTIVE}" || continue
		;;
	    vpnipsec)
		continue
		;;
	    internal|external)
		;;
	    *)
		continue
		;;
	esac

	test ${resolution_type} == ip || member "${ACCESS_FILE_USED_IF_LIST}" ${interface} || interfaces="${interfaces} ${interface}"
	idev=$(get-qos-dev ${interface} ingress)

	! nic-exist ${idev} || echo "filter add dev ${idev} parent ${QOS_CLASS_SELF_ID} priority ${QOS_FILE_FILTER_PRIO} protocol ip u32 match ip src ${ip}/32 flowid ${QOS_CLASS_FILE_ID}"
    done

    for interface in ${interfaces}
    do
	case ${interface} in
	    auxiliary)
		test -n "${AUXILIARY_IS_ACTIVE}" || continue
		;;
	    vpnipsec)
		continue
		;;
	    internal|external)
		;;

	    *)
		continue
		;;
	esac

	odevs=$(get-qos-dev ${interface} egress)

	for odev in ${odevs}
	do
	    ! nic-exist ${odev} || echo "filter add dev ${odev} parent ${QOS_CLASS_SELF_ID} priority ${QOS_FILE_FILTER_PRIO} protocol ip handle ${QOS_FILE_MARK}/${QOS_MARK_MASK} fw flowid ${QOS_CLASS_FILE_ID}"
	done
    done
}

gen-tc-qos-file-filter-ip()
{
    gen-tc-qos-file-filter ip "${ACCESS_FILE_IF_IP_LIST}"
}

gen-tc-qos-file-filter-nr()
{
    gen-tc-qos-file-filter nr "${ACCESS_FILE_IF_NR_LIST}"
}

gen-tc-ingress-admin()
{
    test -n "${1}" || return 1
    test -n "${2}" || return 2
    local dev=${1}
    local apl_ip=${2}

    test ${ADMIN_SSH} == False || echo "filter add dev ${dev} parent ${QOS_HANDLE_ROOT}: priority ${QOS_ADMIN_FILTER_PRIO} protocol ip u32 ${QOS_FILTER_TCP} match ip dst ${apl_ip}/32 match ip dport ${SSH_PORT} 0xffff flowid ${QOS_CLASS_ADMIN}"
    test ${ADMIN_WADMIN} == False || echo "filter add dev ${dev} parent ${QOS_HANDLE_ROOT}: priority ${QOS_ADMIN_FILTER_PRIO} protocol ip u32 ${QOS_FILTER_TCP} match ip dst ${apl_ip}/32 match ip dport ${WADMIN_PORT} 0xffff flowid ${QOS_CLASS_ADMIN}"
    test ${ADMIN_WAUDIT} == False || echo "filter add dev ${dev} parent ${QOS_HANDLE_ROOT}: priority ${QOS_ADMIN_FILTER_PRIO} protocol ip u32 ${QOS_FILTER_TCP} match ip dst ${apl_ip}/32 match ip dport ${WAUDIT_PORT} 0xffff flowid ${QOS_CLASS_ADMIN}"
}

gen-tc-ingress-snmp-trap()
{
    test -n "${1}" || return 1
    test -n "${2}" || return 0

    local dev=${1}
    local ip_ports=${2}

    local ip port
    
    for ip_port in ${ip_ports}
    do
	ip=${ip_port/:*/}
	port=${ip_port/*:/}
	echo "filter add dev ${dev} parent ${QOS_HANDLE_ROOT}: priority ${QOS_ADMIN_SNMP_TRAP_FILTER_PRIO} protocol ip u32 ${QOS_FILTER_TCP} match ip src ${ip}/32 match ip sport ${port} 0xffff flowid ${QOS_CLASS_ADMIN}"
    done
}

gen-tc-ingress-snmp-trap-ip()
{
    test ${ADMIN_SNMP} == True || return 0

    test -n "${1}" || return 1
    local dev=${1}

    gen-tc-ingress-snmp-trap ${dev} "${SNMP_TRAP_SERVER_IP_LIST}"
}

gen-tc-ingress-snmp-trap-nr()
{
    test -n "${1}" || return 1
    local dev=${1}

    ! nic-exist ${dev} || gen-tc-ingress-snmp-trap ${dev} "${SNMP_TRAP_SERVER_IP_LIST}"
}

gen-tc-ingress-syslog()
{
    test -n "${1}" || return 1
    test -n "${2}" || return 0

    local dev=${1}
    local syslog_server_ip_list=${2}

    local elt range i=0
    local protocol ip port
    local filter

    for elt in ${syslog_server_ip_list}
    do
	range=$[${i} % 3]
	case ${range} in
	    0)
		protocol=${elt}
		;;
	    1)
		ip=${elt}
		;;
	    2)
		port=${elt}

		case ${protocol} in
		    udp)
			filter=${QOS_FILTER_UDP}
			;;
		    tcp)
			filter=${QOS_FILTER_TCP}
			;;
		    *)
			;;
		esac
	    
		echo "filter add dev ${dev} parent ${QOS_HANDLE_ROOT}: priority ${QOS_ADMIN_SYSLOG_FILTER_PRIO} protocol ip u32 ${filter} match ip src ${ip}/32 match ip sport ${port} 0xffff flowid ${QOS_CLASS_ADMIN}"

		;;
	    *)
		return 255
		;;
	esac
	((i++))
    done
}

gen-tc-ingress-syslog-ip()
{
    test ${REMOTE_SYSLOG_MODE} == True || return 0

    test -n "${1}" || return 1
    local dev=${1}

    ! nic-exist ${dev} || gen-tc-ingress-syslog ${dev} "${SYSLOG_SERVER_IP_LIST}"
}

gen-tc-ingress-syslog-nr()
{
    test -n "${1}" || return 1
    local dev=${1}

    ! nic-exist ${dev} || gen-tc-ingress-syslog ${dev} "${SYSLOG_SERVER_NR_LIST}"
}

gen-tc-ingress-snmp-agent()
{
    test ${ADMIN_SNMP} == True || return 0

    test -n "${1}" || return 1
    test -n "${2}" || return 2
    local dev=${1}
    local apl_ip=${2}

    test ${SNMP_UDP} == False || echo "filter add dev ${dev} parent ${QOS_HANDLE_ROOT}: priority ${QOS_ADMIN_SNMP_FILTER_PRIO} protocol ip u32 ${QOS_FILTER_UDP} match ip dst ${apl_ip}/32 match ip dport ${SNMP_PORT} 0xffff flowid ${QOS_CLASS_ADMIN}"
    test ${SNMP_TCP} == False || echo "filter add dev ${dev} parent ${QOS_HANDLE_ROOT}: priority ${QOS_ADMIN_SNMP_FILTER_PRIO} protocol ip u32 ${QOS_FILTER_TCP} match ip dst ${apl_ip}/32 match ip dport ${SNMP_PORT} 0xffff flowid ${QOS_CLASS_ADMIN}"
    test ${SNMP_TLS} == False || echo "filter add dev ${dev} parent ${QOS_HANDLE_ROOT}: priority ${QOS_ADMIN_SNMP_FILTER_PRIO} protocol ip u32 ${QOS_FILTER_TCP} match ip dst ${apl_ip}/32 match ip dport ${SNMP_TLS_PORT} 0xffff flowid ${QOS_CLASS_ADMIN}"
}

gen-tc-qos-nr()
{
    local interface interfaces=$(get-qos-logical-interfaces with-ipsec)

    echo
    echo "# QoS for various traffic after name resolutions:"

    for interface in ${interfaces}
    do
	case ${interface} in
	    internal)
		idev=${IF_QOS_INTERNAL_INGRESS}
		;;
	    external)
		idev=${IF_QOS_EXTERNAL_INGRESS}
		;;
	    auxiliary)
		idev=${IF_QOS_AUXILIARY_INGRESS}
		;;
	    vpnipsec)
		idev=${IF_QOS_EXTERNAL_INGRESS}

		;;
	    *)
		return 11
		;;
	esac

	test ${ADMIN_SNMP} == False || gen-tc-ingress-snmp-trap-nr ${idev}
	test ${REMOTE_SYSLOG_MODE} == False || gen-tc-ingress-syslog-nr ${idev}
	test ${QOS_MODE} == False || gen-tc-qos-etc-auth-nr ${interface}
    done

    test ${QOS_MODE} == False || gen-tc-qos-file-filter-nr
}

gen-tc-ingress()
{
    local dev if_qos

    local interface interfaces=$(get-qos-logical-interfaces)

    echo "#########################################################"
    echo "### Begin of Ingress Redirection Rules to IFB Devices ###"
    echo "#########################################################"
    echo

    for interface in ${interfaces}
    do
	case ${interface} in
	    internal)
		dev=${IF_INTERNAL}
		if_qos=${IF_QOS_INTERNAL_INGRESS}
		;;
	    external)
		dev=${IF_LOGICAL_EXTERNAL}
		if_qos=${IF_QOS_EXTERNAL_INGRESS}
		;;
	    auxiliary)
		dev=${IF_AUXILIARY}
		if_qos=${IF_QOS_AUXILIARY_INGRESS}
		;;
	    *)
		return 11
		;;
	esac

	nic-exist ${dev} || continue

	echo "# Ingress redirection of the ${interface} (${dev}) interface to ${if_qos}:"

	case ${interface} in
	    external|auxiliary)
		echo "qdisc add dev ${dev} ingress handle ffff:"
		echo "filter add dev ${dev} parent ffff: priority ${QOS_INGRESS_PRIO} matchall flowid ${QOS_HANDLE_ROOT}: action mirred egress redirect dev ${if_qos}"
		;;
	    internal)
		if test ${VLAN_MODE} == False ; then
		    echo "qdisc add dev ${dev} ingress handle ffff:"
		    echo "filter add dev ${dev} parent ffff: priority ${QOS_INGRESS_PRIO} matchall flowid ${QOS_HANDLE_ROOT}: action mirred egress redirect dev ${if_qos}"
		else
		    local i=0 elt range
		    local vlan
    
		    for elt in ${VLAN_FLOW_LIST}
		    do
			range=$[${i} % 2]
			case ${range} in
			    0)
				vlan=${elt}
				;;
			    1)
				echo "qdisc add dev ${dev}.${vlan} ingress handle ffff:"
				echo "filter add dev ${dev}.${vlan} parent ffff: priority ${QOS_INGRESS_PRIO} matchall flowid ${QOS_HANDLE_ROOT}: action mirred egress redirect dev ${if_qos}"
				;;
			    *)
				return 1
				;;
			esac
			((i++))
		    done
		fi
		;;
	    *)
		;;
	esac
	echo
    done
}

get-interface-admin-ip()
{
    test -n "${1}" || return 1
    local interface=${1}

    local ip='127.0.0.1'

    case ${interface} in
	    internal)
		ip=${IP_ADMIN_IP}
		;;
	    external)
		ip=${IP_EXTERNAL_IP}
		;;
	    auxiliary)
		ip=${IP_AUXILIARY_IP}
		;;
	    *)
		echo ${ip}
		return 11
		;;
    esac

    echo ${ip}
}

get-interface-mon-ip()
{
    test -n "${1}" || return 1
    local interface=${1}

    local ip='127.0.0.1'

    case ${interface} in
	    internal)
		ip=${IP_MON_IP}
		;;
	    external)
		ip=${IP_EXTERNAL_IP}
		;;
	    auxiliary)
		ip=${IP_AUXILIARY_IP}
		;;
	    *)
		echo ${ip}
		return 11
		;;
    esac

    echo ${ip}
}

is-qos-admin-or-mon-interface()
{
    test -n "${1}" || return 1
    test -n "${2}" || return 2
    local interface=${1}
    local dev=${2}

    test ${interface} == internal || return 0
    test ${VLAN_MODE} == True || return 0
    test ${dev} == ${IF_ADMIN} -o ${dev} == ${IF_MON}
}

get-admin-mon-flow-name()
{
    test -n "${1}" || return 1
    test -n "${2}" || return 2
    local interface=${1}
    local dev=${2}

    local admin_name='admin'
    local mon_name='mon'

    local result

    case ${interface} in
	external|auxiliary)
	    result=${admin_name}
	    ;;
	internal)
	    if test ${VLAN_MODE} == False ; then
		result=${admin_name}
	    else
		if test ${IF_ADMIN} == ${IF_MON} ; then
		    result=${admin_name}
		else
		    case ${dev} in
			${IF_ADMIN})
			    result=${admin_name}
			    ;;
			${IF_MON})
			    result=${mon_name}
			    ;;
			*)
			    result=nil
			    ;;
		    esac
		fi
	    fi
	    ;;
	*)
	    result=nil
	    ;;
    esac

    echo ${result}
}

gen-tc-top()
{
    local idev odev odevs
    local admin_ip mon_ip
    local ibw obw
    local flow_name
    local nonadmin_odevs
    local qdisc

    local interface interfaces=$(get-qos-logical-interfaces)

    if test ${QOS_MODE} == True ; then
	qdisc='htb'
    else
	qdisc='prio'
    fi
    
    echo "##########################"
    echo "### Begin of Top Rules ###"
    echo "##########################"
    echo

    for interface in ${interfaces}
    do
	idev=$(get-qos-dev ${interface} ingress)
	odevs=$(get-qos-dev ${interface} egress)
	admin_ip=$(get-interface-admin-ip ${interface})
	mon_ip=$(get-interface-mon-ip ${interface})

	if nic-exist ${idev} ; then

	    echo "# Ingress priority for administration traffic on the ${interface} (${idev}) interface:"
	    echo "qdisc add dev ${idev} root handle ${QOS_HANDLE_ROOT}: prio bands 2 priomap 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1"
	    echo "qdisc add dev ${idev} parent ${QOS_CLASS_ADMIN} handle ${QOS_HANDLE_ADMIN}: ${QOS_QDISC_LEAFQ_ADMIN_OR_MON}"

	    gen-tc-ingress-admin ${idev} ${admin_ip}
	    gen-tc-ingress-snmp-trap-ip ${idev}
	    gen-tc-ingress-syslog-ip ${idev}
	    gen-tc-ingress-snmp-agent ${idev} ${mon_ip}

	    echo "# Non administration ingress traffic goes to the flow (${QOS_CLASS_NADMIN}):"
	    echo "filter add dev ${idev} parent ${QOS_HANDLE_ROOT}: priority ${QOS_NADMIN_FILTER_PRIO} matchall flowid ${QOS_CLASS_NADMIN}"

	    echo

	    if test ${QOS_MODE} == False ; then
		echo "# No QoS for non administration ingress traffic on the ${interface} (${idev}) interface:"
		echo "qdisc add dev ${idev} parent ${QOS_CLASS_NADMIN} handle ${QOS_HANDLE_NADMIN}: prio bands 3 priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1"
	    else
		echo "# QoS for non administration ingress traffic on the ${interface} (${idev}) interface:"
		ibw=$(get-qos-bw ${interface} ingress)
		echo "qdisc add dev ${idev} parent ${QOS_CLASS_NADMIN} handle ${QOS_HANDLE_NADMIN}: htb default ${QOS_HANDLE_ROUTER}"
		echo "class add dev ${idev} parent ${QOS_HANDLE_NADMIN}: classid ${QOS_CLASS_SELF_ID} htb rate ${ibw}kbit ceil ${ibw}kbit ${QOS_HTB_QUANTUM} prio ${QOS_CLASS_SELF_PRIO}"
		echo "class add dev ${idev} parent ${QOS_HANDLE_NADMIN}: classid ${QOS_CLASS_ROUTER_ID} htb rate ${ibw}kbit ceil ${ibw}kbit ${QOS_HTB_QUANTUM} prio ${QOS_CLASS_ROUTER_PRIO}"
	    fi
	fi

	echo

	unset nonadmin_odevs

	for odev in ${odevs}
	do
	    if nic-exist ${odev} ; then
		if is-qos-admin-or-mon-interface ${interface} ${odev} ; then

		    obw=$(get-qos-bw ${interface} egress)

		    echo "# Egress priority for administration traffic on the ${interface} (${odev}) interface:"
		    echo "qdisc add dev ${odev} root handle ${QOS_HANDLE_ROOT}: prio bands 2 priomap 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1"
		    echo "qdisc add dev ${odev} parent ${QOS_CLASS_ADMIN} handle ${QOS_HANDLE_ADMIN}: ${QOS_QDISC_LEAFQ_ADMIN_OR_MON}"
		    echo "filter add dev ${odev} parent ${QOS_HANDLE_ROOT}: priority ${QOS_ADMIN_FILTER_PRIO} protocol ip handle ${QOS_ADMIN_OR_MON_MARK}/${QOS_MARK_MASK} fw flowid ${QOS_CLASS_ADMIN}"
		    echo "# Non administration egress traffic goes to the flow (${QOS_CLASS_NADMIN}):"
		    echo "filter add dev ${odev} parent ${QOS_HANDLE_ROOT}: priority ${QOS_NADMIN_FILTER_PRIO} matchall flowid ${QOS_CLASS_NADMIN}"
		    flow_name=$(get-admin-mon-flow-name ${interface} ${odev})
		    gen-qos-index ${flow_name}: ${QOS_LEAF_QDISC} ${interface} ${idev} ${odev} ${QOS_HANDLE_ADMIN}:

		    echo

		    if test ${QOS_MODE} == False ; then
			echo "# No QoS for non administration egress traffic on the ${interface} (${odev}) interface:"
			echo "qdisc add dev ${odev} parent ${QOS_CLASS_NADMIN} handle ${QOS_HANDLE_NADMIN}: prio bands 3 priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1"
		    else
			echo "# QoS for non administration egress traffic on the ${interface} (${odev}) interface:"
			echo "qdisc add dev ${odev} parent ${QOS_CLASS_NADMIN} handle ${QOS_HANDLE_NADMIN}: htb default ${QOS_HANDLE_ROUTER}"
			echo "class add dev ${odev} parent ${QOS_HANDLE_NADMIN}: classid ${QOS_CLASS_SELF_ID} htb rate ${obw}kbit ceil ${obw}kbit ${QOS_HTB_QUANTUM} prio ${QOS_CLASS_SELF_PRIO}"
			echo "class add dev ${odev} parent ${QOS_HANDLE_NADMIN}: classid ${QOS_CLASS_ROUTER_ID} htb rate ${obw}kbit ceil ${obw}kbit ${QOS_HTB_QUANTUM} prio ${QOS_CLASS_ROUTER_PRIO}"
		    fi

		    if test ${interface} != internal ; then
			gen-qos-index nonadmin: ${qdisc} ${interface} ${idev} ${odev} ${QOS_HANDLE_NADMIN}:
			gen-qos-index steered: class ${interface} ${idev} ${odev} ${QOS_CLASS_SELF_ID}
		    fi
		    test ${interface} != internal -o ${VLAN_MODE} == False || nonadmin_odevs="${nonadmin_odevs} ${odev}"

		else
		    if test ${QOS_MODE} == False ; then
			echo "# No QoS for non administration egress traffic on the ${interface} (${odev}) interface:"
			echo "qdisc add dev ${odev} root handle ${QOS_HANDLE_NADMIN}: prio bands 3 priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1"
		    else
			obw=$(get-qos-bw ${interface} egress)
			echo "# QoS for non administration egress traffic on the ${interface} (${odev}) interface:"
			echo "qdisc add dev ${odev} root handle ${QOS_HANDLE_NADMIN}: htb default ${QOS_HANDLE_ROUTER}"
			echo "class add dev ${odev} parent ${QOS_HANDLE_NADMIN}: classid ${QOS_CLASS_SELF_ID} htb rate ${obw}kbit ceil ${obw}kbit ${QOS_HTB_QUANTUM} prio ${QOS_CLASS_SELF_PRIO}"
			echo "qdisc add dev ${odev} parent ${QOS_CLASS_SELF_ID} handle ${QOS_HANDLE_SELF}: htb default ${QOS_HANDLE_SELF_DEFAULT}"
			echo "class add dev ${odev} parent ${QOS_HANDLE_NADMIN}: classid ${QOS_CLASS_ROUTER_ID} htb rate ${obw}kbit ceil ${obw}kbit ${QOS_HTB_QUANTUM} prio ${QOS_CLASS_ROUTER_PRIO}"
			echo "qdisc add dev ${odev} parent ${QOS_CLASS_ROUTER_ID} handle ${QOS_HANDLE_ROUTER}: htb default ${QOS_HANDLE_ROUTER_DEFAULT}"
		    fi
		    nonadmin_odevs="${nonadmin_odevs} ${odev}"
		fi
		echo
	    fi
	done

	nonadmin_odevs=${nonadmin_odevs:1}

	if test -n "${nonadmin_odevs}" ; then
	    gen-qos-index nonadmin: ${qdisc} internal ${IF_QOS_INTERNAL_INGRESS} ${nonadmin_odevs// /:} ${QOS_HANDLE_NADMIN}:
	    gen-qos-index steered: class internal ${IF_QOS_INTERNAL_INGRESS} ${nonadmin_odevs// /:} ${QOS_CLASS_SELF_ID}
	fi

	gen-qos-index all: prio ${interface} ${idev} ${odevs// /:} ${QOS_HANDLE_ROOT}:
    done
}

gen-tc-no-error()
{
    if test -z "${AUXILIARY_IS_ACTIVE}" ; then
	echo "# To avoid an error during the tc rules activation if the auxiliary interface is disabled:"
	echo "qdisc add dev ifb${IFB_AUXILIARY_INGRESS} root handle 1: pfifo"
	echo
    fi
}

qos-handle-comment()
{
    local handle_comment="### The incremental handle begins from ${QOS_HANDLE_INIT}. ###"

    echo
    printf -- '#%.0s' {1..47} ; echo
    echo ${handle_comment}
    printf -- '#%.0s' {1..47} ; echo
}

gen-tc-qos()
{
    test ${QOS_MODE} == True || return 0

    echo "#####################################"
    echo "### Begin of Flow based QoS Rules ###"
    echo "#####################################"

    local interface interfaces=$(get-qos-logical-interfaces)

    for interface in ${interfaces}
    do
	gen-tc-qos-default ${interface}
	gen-tc-qos-filter-self ${interface}
	gen-tc-qos-etc ${interface}
    done

    gen-tc-external-rweb-top
    gen-tc-external-web
    gen-tc-vpnipsec

    ########################################################################################################
    # Mangled traffic should be treated in the same order as in apl_fw:gen-iptables-mangle-qos-non-admin() #
    ########################################################################################################

    QOS_HANDLE=${QOS_HANDLE_INIT}
    qos-handle-comment

    gen-tc-external-rweb
    gen-tc-backend-rweb
    gen-tc-peer
    gen-tc-av
    gen-tc-web
    gen-tc-tweb
    gen-tc-qos-file-top
    gen-tc-qos-file-filter-ip
    gen-tc-qos-router
}

################################################################################

gen-tc()
{
    reset-qos-index

    gen-tc-ingress
    gen-tc-top
    gen-tc-no-error
    gen-tc-qos
}

gen-tc-resolved()
{
    cat sysconfig.tc
    gen-tc-qos-nr
}

LIB_APL_QOS=Yes
