#!/bin/sh /etc/rc.common START=99 USE_PROCD=1 RULE_LIST="/tmp/easy_qos_rule.list" CLIENT_LIST="/tmp/easy_qos_class_client.list" BRIDGE_INTF="" [ -f /etc/profile.d/intel.sh ] && { . /etc/profile.d/intel.sh sh /etc/profile.d/intel.sh } log() { echo "${@}"|logger -t easy_qos_class -p debug } exec_log() { ${@} if [ "${?}" -ne 0 ]; then log "Failed to create ${@}"; fi } exec_class_log() { ${@} |grep -i successful if [ "${?}" -ne 0 ]; then log "Failed to create ${@}"; return 1 fi return 0 } get_priority() { local prio=$(echo $1|tr 'A-Z' 'a-z'); case "${prio}" in "lowest") echo 8;; "low") echo 7;; "besteffort") echo 6;; "normal") echo 5;; "video") echo 4;; "medium") echo 3;; "high") echo 2;; "highest") echo 1;; esac } get_mark() { local prio=$(echo $1|tr 'A-Z' 'a-z'); case "${prio}" in "lowest") echo "0x41/0x3df";; "low") echo "0x82/0x3df";; "besteffort") echo "0xc3/0x3df";; "normal") echo "0x104/0x3df";; "video") echo "0x145/0x3df";; "medium") echo "0x186/0x3df";; "high") echo "0x1c7/0x3df";; "highest") echo "0x208/0x3df";; esac } clean_client_entries() { [ -f ${CLIENT_LIST} ] && rm ${CLIENT_LIST} } map_client_entries() { local clients ip mac host json_load "$(ubus call router.network 'clients')" json_get_keys keys for key in ${keys}; do json_select ${key} json_get_vars ipaddr macaddr hostname clients="${macaddr} ${ipaddr} ${hostname};${clients}" json_select .. done json_init IFS=";" for client in ${clients}; do macaddr=$(echo ${client} | cut -d" " -f1) json_add_object "${macaddr//:/_}" json_add_string "ip" "$(echo ${client} | cut -d" " -f2)" json_add_string "macaddr" "$(echo ${client} | cut -d" " -f1)" json_add_string "host" "$(echo ${client} | cut -d" " -f3)" json_close_object done IFS=' ' echo `json_dump` > ${CLIENT_LIST} json_cleanup } # Find the IP of a corresponding mac from arp table get_ipaddress() { local clients ip mac host json_load "$(cat ${CLIENT_LIST})" json_get_keys keys # jshn seems a bit iffy on having : in key, replace by _ json_select "${1//:/_}" 2 > /dev/null json_get_var ip ip echo "$ip" } check_and_create() { iptables -t mangle -C PREROUTING ${@} 2>/dev/null # Create rule if not exists if [ ${?} -ne 0 ]; then exec_log iptables -t mangle -A PREROUTING ${@} else log "Rule exists for ${@}" fi } create_ip_rule() { local proto=$1; shift local src_ip=$1; shift local mark=$1; shift local ports=$1; local cmd=""; cmd="-j EXTMARK --set-mark ${mark}"; if [ "${proto}" != "icmp" ]; then if [ -n "${ports}" ]; then cmd="--match multiport --dports ${ports} ${cmd}"; fi fi if [ "${proto}" == "icmp" ]; then cmd="-p icmp -m icmp --icmp-type 8 $cmd" elif [ "${proto}" == "all" ]; then cmd="-p all $cmd" else cmd="-p ${proto} -m ${proto} $cmd" fi cmd="-s ${src_ip} $cmd" check_and_create ${cmd} } is_lan_bridge() { local _section=$1 local _type local _is_lan config_get _type "$section" "type" config_get _is_lan "$section" "is_lan" if [ "${_type}" == "bridge" -a "${_is_lan}" == "1" ]; then BRIDGE_INTF="br-${_section}" fi } get_bridge_interface() { config_load network config_foreach is_lan_bridge interface } validate_rule_section() { uci_validate_section easy_qos rule "${1}" \ 'priority:string' \ 'macaddr:string' \ 'proto:string:none' \ 'port:list(uinteger)' \ 'comment:string:none' } # Clear existing rules before applying new rules clear_existing_rules() { # execute the delete rules written onto a file then delete the file [ -f ${RULE_LIST} ] || return 0 while read line do log "Deleting old classification rules" exec_class_log classcfg -D ${line} -i ${BRIDGE_INTF} done <${RULE_LIST} local rule=$(iptables -t mangle -S PREROUTING|grep -m 1 EXTMARK |sed 's/-A/-D/1') while [ -n "${rule}" ]; do exec_log iptables -t mangle ${rule} rule=$(iptables -t mangle -S PREROUTING|grep -m 1 EXTMARK |sed 's/-A/-D/1') done sync [ -f ${RULE_LIST} ] && rm ${RULE_LIST} } # classcfg -M local_dhcp -i lo -p udp --dport 67:67 --dport 68:68 -j mark --mark 1 create_rule() { local proto=$1; shift local mac_addr=$1; shift local mark=$1; shift local ports=$1; local cmd=""; # Rule name is uniqe, so we take hash of all the input as rule_name local rule_name="$(echo ${mac_addr}${proto}${mark}${ports} |md5sum |head -c 30)" cmd="-j mark --mark ${mark}"; if [ "${mac_addr}" != "none" ]; then cmd="--smac ${mac_addr} ${cmd}"; fi if [ "${proto}" != "icmp" ]; then if [ "${ports}" != "none" ]; then IFS="," for port in ${ports}; do cmd="--dport ${port}:${port} ${cmd}"; done IFS=' ' fi fi if [ "${proto}" != "none" ]; then cmd="-p ${proto} $cmd" fi cmd="-i ${BRIDGE_INTF} $cmd" cmd="-A ${rule_name} $cmd" # Store the rule_names for cleanup on reload exec_class_log classcfg ${cmd} [ $? -eq 0 ] && \ echo ${rule_name} >> ${RULE_LIST} } manage_rule() { local cfg="$1" local priority macaddr proto port comment prio_num port_list ip ipmark validate_rule_section "${1}" || { log "Validation of section failed" return 1; } prio_num=$(get_priority ${priority}) port_list=$(echo ${port}|sed 's/ /,/g') ipmark=$(get_mark ${priority}) ip=$(get_ipaddress ${macaddr}) if [ -n "${prio_num}" ]; then if [ "${proto}" == "none" -o "${proto}" == "tcpudp" ]; then create_rule tcp ${macaddr} ${prio_num} ${port_list} create_rule udp ${macaddr} ${prio_num} ${port_list} if [ -n "${ip}" ]; then create_ip_rule tcp ${ip} ${ipmark} ${port_list} create_ip_rule udp ${ip} ${ipmark} ${port_list} fi else create_rule ${proto} ${macaddr} ${prio_num} ${port_list} if [ -n "${ip}" ]; then create_ip_rule ${proto} ${ip} ${ipmark} ${port_list} fi fi fi } reload_service() { get_bridge_interface map_client_entries clear_existing_rules config_load easy_qos config_foreach manage_rule rule clean_client_entries } start_service() { [ -x /opt/intel/usr/sbin/classcfg ] || exit 0 reload_service log "Easy QoS class installed" } service_triggers() { procd_add_reload_trigger "easy_qos" "network" }