Skip to content

BPF ​

BPF 的组件名称为 bpf, 相关内核选项为

ini
# General setup
# -> Enable bpf() system call
CONFIG_BPF_SYSCALL=y

# General setup
# -> Control Group support
CONFIG_CGROUPS=y

# General setup
# -> Control Group support
# -> Support for eBPF programs attached to cgroups
CONFIG_CGROUP_BPF=y

# Networking support
# -> Networking options
# -> enable BPF STREAM_PARSER
CONFIG_BPF_STREAM_PARSER=y

检查工具为

bash
#!/bin/bash

source ./kernel_config_utils.sh
source ./bpf_utils.sh

bpf_check() {
    is_error=false
    if ! kernel_config_check CONFIG_BPF_SYSCALL; then
        is_error=true
    fi
    if ! kernel_config_check CONFIG_CGROUPS; then
        is_error=true
    fi
    if ! kernel_config_check CONFIG_CGROUP_BPF; then
        is_error=true
    fi
    if ! kernel_config_check CONFIG_BPF_STREAM_PARSER; then
        is_error=true
    fi

    map_types=$(cat << EOF
    hash
    array
    sockmap
    sockhash
EOF
)
    prog_types=$(cat << EOF
    socket_filter
    perf_event
    sock_ops
    sched_cls
EOF
)
    helpers=$(cat << EOF
    bpf_map_lookup_elem
EOF
)
    declare -A progs_spec_helpers
    progs_spec_helpers=(
        ["socket_filter"]=$(cat << EOF
            bpf_skb_load_bytes
EOF
)
        ["sched_cls"]=$(cat << EOF
            bpf_clone_redirect
            bpf_redirect
EOF
)
    )

    for map_type in $map_types; do
        if ! bpf_map_type_check $map_type; then
            is_error=true
        fi
    done
    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

            local prog_spec_helpers="${progs_spec_helpers[$prog_type]}"

            for helper in $prog_spec_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

    return 0
}

内核选项 BPF_STREAM_PARSER 在 Linux v4.14 中引入, 相关提交为 08848246639218ae58acdf3321bc7b693062f31c.

bpf 系统调用 ​

bpf 系统调用原型如下:

c
int bpf(int cmd, union bpf_attr *attr, unsigned int size);

在内核源码的 kernel/bpf/syscall.c 文件中 SYSCALL_DEFINE3 宏调用处实现. cmd 对应的 C 类型为 enum bpf_cmd, 在内核源码的 include/uapi/linux/bpf.h 文件中定义. 引入 cmd 的 Linux 版本和提交如下表所示:

cmd用途版本提交号
BPF_TOKEN_CREATEv6.935f96de04127d332a5c5e8a155d31f452f88c76d
BPF_LINK_GET_FD_BY_IDBPF link 查找v5.82d602c8cf40d65d4a7ac34fe18648d8778e6e594
BPF_LINK_GET_NEXT_IDBPF link 遍历v5.82d602c8cf40d65d4a7ac34fe18648d8778e6e594
BPF_OBJ_GET_INFO_BY_FD通过 fd 获取 BPF object 信息v4.131e270976908686ec25fb91b8a34145be54137976
BPF_PROG_QUERYBPF 程序查询v4.15468e2f64d220fe2dc11caa2bcb9b3a1e50fc7321
BPF_PROG_GET_NEXT_IDBPF 程序遍历v4.1334ad5580f8f9c86cb273ebea25c149613cd1667e
BPF_MAP_GET_NEXT_IDBPF map 遍历v4.1334ad5580f8f9c86cb273ebea25c149613cd1667e
BPF_TASK_FD_QUERY进程相关 BPF 程序查询v4.1841bdc4b40ed6fb26c6acc655ed9a243a348709c9

BPF 程序类型 ​

BPF 程序类型在内核源码对应标识符的格式为 BPF_PROG_TYPE_*, 对应的 C 类型是 include/uapi/linux/bpf.h 文件中的 enum bpf_prog_type 类型, 对应的名称和上下文定义在文件 include/linux/bpf_types.h 中.

BPF tracing 程序 ​

BPF_PROG_TYPE版本提交号
BPF_PROG_TYPE_TRACINGv5.5f1b9509c2fb0ef4db8d22dac9aef8e856a5d81f6

BPF socket 程序 ​

BPF_PROG_TYPE版本提交号
BPF_PROG_TYPE_SOCKET_FILTERv3.1989aa075832b0da4402acebd698d0411dcc82d03e

引入 BPF helper 函数的版本和提交号如下表所示:

helper 函数版本提交号
bpf_skb_load_bytesv4.112492d3b867043f6880708d095a7a5d65debcfc32

BPF xdp 程序 ​

BPF_PROG_TYPE版本提交号
BPF_PROG_TYPE_XDPv4.86a773a15a1e8874e5eccd2f29190c31085912c95

引入 BPF helper 函数的版本和提交号如下表所示:

helper 函数版本提交号
bpf_redirect_mapv4.1497f91a7cf04ff605845c20948b8a80e54cbd3376

BPF sched_cls 程序 ​

BPF_PROG_TYPE版本提交号
BPF_PROG_TYPE_SCHED_CLSv4.196be4325f443dbbfeb37d2a157675ac0736531a1

引入 BPF helper 函数的版本和提交号如下表所示:

helper 函数版本提交号
bpf_clone_redirectv4.23896d655f4d491c67d669a15f275a39f713410f8
bpf_redirectv4.427b29f63058d26c6c1742f1993338280d5a41dc6

BPF perf_event 程序 ​

BPF_PROG_TYPE版本提交号
BPF_PROG_TYPE_PERF_EVENTv4.90515e5999a466dfe6e1924f460da599bb6821487

BPF sock_ops 程序 ​

BPF_PROG_TYPE版本提交号
BPF_PROG_TYPE_SOCK_OPSv4.1340304b2a1567fecc321f640ee4239556dd0f3ee0

BPF map 类型 ​

BPF 程序类型在内核源码对应标识符的格式为 BPF_MAP_TYPE_*, 对应的 C 类型是 include/uapi/linux/bpf.h 文件中的 enum bpf_map_type 类型, 对应的操作定义在文件 include/linux/bpf_types.h 中.

BPF array map ​

BPF_MAP_TYPE版本提交号
BPF_MAP_TYPE_ARRAYv3.1928fbcfa08d8ed7c5a50d41a0433aad222835e8e3

引入 BPF helper 函数的版本和提交号如下表所示:

helper 函数版本提交号
bpf_map_lookup_elemv3.1928fbcfa08d8ed7c5a50d41a0433aad222835e8e3
bpf_map_update_elemv3.1928fbcfa08d8ed7c5a50d41a0433aad222835e8e3

BPF xskmap map ​

BPF_MAP_TYPE版本提交号
BPF_MAP_TYPE_XSKMAPv4.18fbfc504a24f53f7ebe128ab55cb5dba634f4ece8

引入 BPF helper 函数的版本和提交号如下表所示:

helper 函数版本提交号
bpf_map_lookup_elemv4.18fbfc504a24f53f7ebe128ab55cb5dba634f4ece8
bpf_map_update_elemv4.18fbfc504a24f53f7ebe128ab55cb5dba634f4ece8
bpf_redirect_mapv4.181b1a251c83d29ec18b4a2191cce5c5b7117529ff

网口多队列的重定向支持在 Linux v5.3 版本中引入, 相关提交为 fada7fdc83c0bf8755956bff707c42b609223301.

BPF 程序类型在内核源码对应标识符的格式为 BPF_LINK_TYPE_*, 对应的 C 类型是 include/uapi/linux/bpf.h 文件中的 enum bpf_link_type 类型, 对应的名称定义在文件 include/linux/bpf_types.h 中.

BPF helper 函数 ​

内核源码的 include/uapi/linux/bpf.h:___BPF_FUNC_MAPPER 列举了 BPF helper 函数和对应编号, enum bpf_func_id 为对应的 C 类型. 引入 BPF helper 函数的编号, Linux 版本和提交号如下表所示:

BPF helper 函数编号版本提交号
bpf_trace_vprintk177v5.1610aceb629e198429c849d5e995c3bb1ba7a9aaa3
bpf_skb_load_bytes26v4.505c74e5e53f6cb07502c3e6a820f33e2777b6605
bpf_map_lookup_elem1v3.19d0003ec01c667b731c139e23de3306a8b328ccf5
bpf_map_update_elem2v3.19d0003ec01c667b731c139e23de3306a8b328ccf5
bpf_redirect_map51v4.1497f91a7cf04ff605845c20948b8a80e54cbd3376
bpf_clone_redirect13v4.23896d655f4d491c67d669a15f275a39f713410f8
bpf_redirect23v4.427b29f63058d26c6c1742f1993338280d5a41dc6

BPF TC 挂载点 ​

BPF TC 挂载点的组件名称为 bpf_tc, 相关内核选项为

ini
# Networking support
# -> Networking options
# -> QoS and/or fair queueing
# -> Ingress/classifier-action Qdisc
CONFIG_NET_SCH_INGRESS=y

# Networking support
# -> Networking options
# -> QoS and/or fair queueing
# -> Actions
CONFIG_NET_CLS_ACT=y

# Networking support
# -> Networking options
# -> QoS and/or fair queueing
# -> Actions
# -> BPF based action
CONFIG_NET_ACT_BPF=y

# Networking support
# -> Networking options
# -> QoS and/or fair queueing
# -> BPF-based classifier
CONFIG_NET_CLS_BPF=y

检查工具为

bash
#!/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
}

BPF TC 挂载点上挂载的是 BPF sched_cls 程序.