#!/bin/sh
# Install iptables rules

IP_RULE=""

init_iptables_rule() {
    IP_RULE=""
}

iptables_filter_intf() {
    IP_RULE="$IP_RULE -i $1"
}

iptables_filter_proto() {
    IP_RULE="$IP_RULE -p $1"
}

iptables_filter_icmp() {
    IP_RULE="$IP_RULE --icmp-type $1"
}

iptables_filter_icmpv6() {
    IP_RULE="$IP_RULE --icmpv6-type $1"
}

iptables_filter_ip_src() {
    IP_RULE="$IP_RULE -s $1"
}

iptables_filter_ip_dest() {
    IP_RULE="$IP_RULE -d $1"
}

iptables_filter_port_dest() {
    IP_RULE="$IP_RULE --dport $1"
}

iptables_filter_port_src() {
    IP_RULE="$IP_RULE --sport $1"
}

iptables_filter_port_dest_range() {
    IP_RULE="$IP_RULE --dport $1:$2"
}

iptables_filter_port_src_range() {
    IP_RULE="$IP_RULE --sport $1:$2"
}

iptables_filter_dscp_filter() {
    IP_RULE="$IP_RULE -m dscp --dscp $1"
}

iptables_filter_ip_len_min() {
    IP_RULE="$IP_RULE -m length --length $1"
}

iptables_filter_ip_len_max() {
    IP_RULE="$IP_RULE:$1"
}

iptables_set_dscp_mark() {
    IP_RULE="$IP_RULE -j DSCP --set-dscp $1"
}

append_rule_to_mangle_table() {
    if [ "$2" == 4 ]; then
        echo "iptables -w -t mangle -A $1 $IP_RULE"  >> /tmp/qos/classify.iptables
    elif [ "$2" == 6 ]; then
        echo "ip6tables -w -t mangle -A $1 $IP_RULE" >> /tmp/qos/classify.ip6tables
    elif [ "$2" == 1 ]; then
        echo "iptables -w -t mangle -A $1 $IP_RULE"  >> /tmp/qos/classify.iptables
        echo "ip6tables -w -t mangle -A $1 $IP_RULE" >> /tmp/qos/classify.ip6tables
    fi
}

protocol_string_to_num()
{
    local value="-1"

    case "$1" in
    TCP|tcp)
        value=6
        ;;
    UDP|udp)
        value=17
        ;;
    ICMP|icmp)
        value=1
        ;;
    ICMPv6|icmpv6)
        value=58
        ;;
    IGMP|igmp)
        value=2
        ;;
    SCTP|sctp)
        value=132
        ;;
    *[0-9]*)
        value="$1"
        ;;
    *)
        value=-1
        ;;
    esac
    echo $value
}

handle_iptables_rules() {
    local cid="$1"
    local ip_version=0
    local is_l3_rule=0
    local proto_num=-1
    traffic_class=$2

    init_iptables_rule

    # If traffic class non empty/zero then function call for handling fowrwarding
    # policy in the case of tos greater than 0x10.
    # In this case, traffic class should not be read from config and use value in arg
    if [ -z "$traffic_class" ]; then
	    config_get traffic_class "$cid" "traffic_class"
    fi

    config_get proto "$cid" "proto"
    config_get dscp_mark "$cid" "dscp_mark"
    config_get dscp_filter "$cid" "dscp_filter"
    config_get dest_port "$cid" "dest_port"
    config_get dest_port_range "$cid" "dest_port_range"
    config_get src_port "$cid" "src_port"
    config_get src_port_range "$cid" "src_port_range"
    config_get dest_ip "$cid" "dest_ip"
    config_get src_ip "$cid" "src_ip"
    config_get ip_len_min "$cid" "ip_len_min"
    config_get ip_len_max "$cid" "ip_len_max"
    config_get ifname "$cid" "ifname"
    config_get all_interfaces "$cid" "all_interfaces"
    config_get icmp_type "$cid" "icmp_type"
    config_get l3_ifname "$cid" "l3_ifname"

    #check version of ip
    case $src_ip$dest_ip in
        *.*)
            ip_version=4
            ;;
        *:*)
            ip_version=6
            ;;
        *)
            ip_version=1 #ip address not used
    esac

    #filter interface
    if ! [ "$all_interfaces" == "1" ]; then
	if [ -n "$ifname" -a "$ifname" != "lo" ]; then
	    iptables_filter_intf "$ifname"
	elif [ -n "$l3_ifname" -a "$l3_ifname" != "lo" ]; then
	    iptables_filter_intf "$l3_ifname"
	fi
    fi

    # filter proto
    if [ -n "$proto" ]; then
        proto_num=$(protocol_string_to_num "$proto")
        iptables_filter_proto "$proto_num"
        if [ -n "$icmp_type" -a \( "$proto_num" = "58" -o "$proto_num" = "1" \) ]; then
            if [ "$proto_num" = "1" ]; then
                iptables_filter_icmp "$icmp_type"
                ip_version=4
            else
                iptables_filter_icmpv6 "$icmp_type"
                ip_version=6
            fi
        fi
        is_l3_rule=1
    fi

    #filter src. ip
    if [ -n "$src_ip" ]; then
        iptables_filter_ip_src "$src_ip"
        is_l3_rule=1
    fi

    #filter dest. ip
    if [ -n "$dest_ip" ]; then
        iptables_filter_ip_dest "$dest_ip"
        is_l3_rule=1
    fi

    #filter dest. port
    if [ -n "$dest_port" ] && [ -z "$dest_port_range" ]; then
        iptables_filter_port_dest "$dest_port"
        is_l3_rule=1
    fi

    #filter src. port
    if [ -n "$src_port" ] && [ -z "$src_port_range" ]; then
        iptables_filter_port_src "$src_port"
        is_l3_rule=1
    fi

    #filter dest. port range
    if [ -n "$dest_port" ] && [ -n "$dest_port_range" ]; then

        if [ "$dest_port_range" == "-1" ] ; then
            dest_port_range="$dest_port"
        fi

        iptables_filter_port_dest_range "$dest_port" "$dest_port_range"
        is_l3_rule=1
    fi

    #filter src. port range
    if [ -n "$src_port" ] && [ -n "$src_port_range" ]; then

        if [ "$src_port_range" == "-1" ] ; then
            src_port_range="$src_port"
        fi

        iptables_filter_port_src_range "$src_port" "$src_port_range"
        is_l3_rule=1
    fi

    #filter dscp
    if [ -n "$dscp_filter" ]; then
        iptables_filter_dscp_filter "$dscp_filter"
        is_l3_rule=1
    fi

    #filter min. IP packet len.
    if [ -n "$ip_len_min" ]; then
        iptables_filter_ip_len_min "$ip_len_min"
        is_l3_rule=1
    fi

    #filter max. IP packet len.
    if [ -n "$ip_len_max" ]; then
        iptables_filter_ip_len_max "$ip_len_max"
        is_l3_rule=1
    fi

    if [ -z "$is_l3_rule" ] || [ "$is_l3_rule" -eq 0 ] ; then
        return
    fi

    #set dscp mark
    [ -n "$dscp_mark" ] && iptables_set_dscp_mark "$dscp_mark"

    #set packet queue mark
    [ -n "$traffic_class" ] && iptables_set_traffic_class  "$traffic_class"

    #write iptables rule for dscp marking
    [ -n "$IP_RULE" ] && [ -n "$dscp_mark" ] && append_rule_to_mangle_table "qos_forward" $ip_version

    if [ -n "$IP_RULE" ] && [ -n "$traffic_class" ]; then
	if [ "$all_interfaces" == "1" ]; then
	    append_rule_to_mangle_table "qos_output" $ip_version
	elif [ "$ifname" == "lo" ]; then
            #write iptables rule for putting WAN directed internal packets in different queue
            append_rule_to_mangle_table "qos_output" $ip_version
        else
            #write iptables rule for putting WAN directed LAN packets in different queue
            append_rule_to_mangle_table "qos_prerouting" $ip_version
        fi
    fi
}

create_iptables_chains() {
    iptables -w -t mangle -N qos_forward 2> /dev/null
    ret=$?
    [ $ret -eq 0 ] && iptables -w -t mangle -I FORWARD -j qos_forward

    iptables -w -t mangle -N qos_prerouting 2> /dev/null
    ret=$?
    [ $ret -eq 0 ] && iptables -w -t mangle -I PREROUTING -j qos_prerouting

    iptables -w -t mangle -N qos_output 2> /dev/null
    ret=$?
    [ $ret -eq 0 ] && iptables -w -t mangle -I OUTPUT -j qos_output

    ip6tables -w -t mangle -N qos_forward 2> /dev/null
    ret=$?
    [ $ret -eq 0 ] && ip6tables -w -t mangle -I FORWARD -j qos_forward

    ip6tables -w -t mangle -N qos_prerouting 2> /dev/null
    ret=$?
    [ $ret -eq 0 ] && ip6tables -w -t mangle -I PREROUTING -j qos_prerouting

    ip6tables -w -t mangle -N qos_output 2> /dev/null
    ret=$?
    [ $ret -eq 0 ] && ip6tables -w -t mangle -I OUTPUT -j qos_output
}

flush_iptables_chains() {
    echo "iptables -w -t mangle -F qos_forward" > /tmp/qos/classify.iptables
    echo "iptables -w -t mangle -F qos_prerouting" >> /tmp/qos/classify.iptables
    echo "iptables -w -t mangle -F qos_output" >> /tmp/qos/classify.iptables

    echo "ip6tables -w -t mangle -F qos_forward" > /tmp/qos/classify.ip6tables
    echo "ip6tables -w -t mangle -F qos_prerouting" >> /tmp/qos/classify.ip6tables
    echo "ip6tables -w -t mangle -F qos_output" >> /tmp/qos/classify.ip6tables
}
