#!/bin/bash

source ./kernel_config_utils.sh
source ./bpf_utils.sh
source ./cmd_utils.sh
source ./net_utils.sh

bpf_tc_object_check() {
    local ifname=$1
    local bpf_tc_object=$2
    local dir=$3
    local handle="0x1"
    local pref="0x66"
    local a="dev $ifname $dir protocol all handle $handle pref $pref bpf"

    if ${TC_CMD} filter get $a > /dev/null 2>&1; then
        echo "find before attach: ifname '$ifname', bpf_tc_object '$bpf_tc_object', dir '$dir'"
        return 1
    fi
    if ! ${TC_CMD} filter add $a object-file $bpf_tc_object section tc; then
        echo "attach failed: ifname '$ifname', object '$bpf_tc_object', dir '$dir'"
        is_error=true
        return 1
    fi
    if ! ${TC_CMD} filter get $a > /dev/null 2>&1; then
        echo "not find after attach: ifname '$ifname', bpf_tc_object '$bpf_tc_object', dir '$dir'"
        return 1
    fi
    if ! ${TC_CMD} filter delete $a; then
        echo "detach failed: ifname '$ifname', object '$bpf_tc_object', dir '$dir'"
        is_error=true
        return 1
    fi
    if ${TC_CMD} filter get $a > /dev/null 2>&1; then
        echo "find after detach: ifname '$ifname', bpf_tc_object '$bpf_tc_object', dir '$dir'"
        return 1
    fi
}
bpf_tc_check() {
    is_error=false
    if ! kernel_config_check CONFIG_NET_SCH_INGRESS sch_ingress; then
        is_error=true
    fi
    if ! kernel_config_check CONFIG_NET_CLS_ACT; then
        is_error=true
    fi
    if ! kernel_config_check CONFIG_NET_ACT_BPF act_bpf; then
        is_error=true
    fi
    if ! kernel_config_check CONFIG_NET_CLS_BPF cls_bpf; then
        is_error=true
    fi
    if [ "$is_error" == "true" ]; then
        return 1
    fi

    prog_types="sched_act sched_cls"
    helpers=$(cat << EOF
    bpf_map_lookup_elem
    bpf_redirect
    bpf_clone_redirect
EOF
)

    for prog_type in $prog_types; do
        if bpf_prog_type_check $prog_type; then
            for helper in $helpers; do
                if ! bpf_prog_helper_check $prog_type $helper; then
                    is_error=true
                fi
            done
        else
            is_error=true
        fi
    done
    if [ "$is_error" == "true" ]; then
        return 1
    fi

    local ifname="dodo"

    if ! netif_new $ifname; then
        return 1
    fi
    while :
    do
        if ! ${TC_CMD} qdisc add dev $ifname clsact; then
            echo "add qdisc clsact failed: ifname '$ifname'"
            is_error=true
            break
        fi

        local bpf_tc_object="/opt/sumports/lib/bpf_tc.o"

        if ! bpf_tc_object_check $ifname $bpf_tc_object ingress; then
            is_error=true
            break
        fi
        if ! bpf_tc_object_check $ifname $bpf_tc_object egress; then
            is_error=true
            break
        fi

        break
    done
    netif_delete $ifname
    if [ "$is_error" == "true" ]; then
        return 1
    fi

    return 0
}

