• eBPF(三)


    目录

    一、编程接口

    二、命令码的类型

    BPF辅助函数

    三、BPF映射

    四、程序的分类

    跟踪类

    网络类

    1、XDP程序

    2、TC程序

    3、套接字程序

    4、cgroup程序

    其他eBPF程序


    一、编程接口

    1. #include
    2. int bpf(int cmd, union bpf_attr *attr, unsigned int size);

    cmd 类型,attr属性(map)

    本文围绕类型,属性(映射),以及程序的分类进行介绍

    二、命令码的类型

    include/uapi/linux/bpf.h内核头文件中定义

    1. enum bpf_cmd {
    2. BPF_MAP_CREATE,
    3. BPF_MAP_LOOKUP_ELEM,
    4. BPF_MAP_UPDATE_ELEM,
    5. BPF_MAP_DELETE_ELEM,
    6. BPF_MAP_GET_NEXT_KEY,
    7. BPF_PROG_LOAD,
    8. BPF_OBJ_PIN,
    9. BPF_OBJ_GET,
    10. BPF_PROG_ATTACH,
    11. BPF_PROG_DETACH,
    12. BPF_PROG_TEST_RUN,
    13. BPF_PROG_GET_NEXT_ID,
    14. BPF_MAP_GET_NEXT_ID,
    15. BPF_PROG_GET_FD_BY_ID,
    16. BPF_MAP_GET_FD_BY_ID,
    17. BPF_OBJ_GET_INFO_BY_FD,
    18. BPF_PROG_QUERY,
    19. BPF_RAW_TRACEPOINT_OPEN,
    20. BPF_BTF_LOAD,
    21. BPF_BTF_GET_FD_BY_ID,
    22. BPF_TASK_FD_QUERY,
    23. BPF_MAP_LOOKUP_AND_DELETE_ELEM,
    24. BPF_MAP_FREEZE,
    25. BPF_BTF_GET_NEXT_ID,
    26. BPF_MAP_LOOKUP_BATCH,
    27. BPF_MAP_LOOKUP_AND_DELETE_BATCH,
    28. BPF_MAP_UPDATE_BATCH,
    29. BPF_MAP_DELETE_BATCH,
    30. BPF_LINK_CREATE,
    31. BPF_LINK_UPDATE,
    32. BPF_LINK_GET_FD_BY_ID,
    33. BPF_LINK_GET_NEXT_ID,
    34. BPF_ENABLE_STATS,
    35. BPF_ITER_CREATE,
    36. BPF_LINK_DETACH,
    37. BPF_PROG_BIND_MAP,
    38. };

    BPF辅助函数

    可以查询辅助函数

    1. # bpftool feature probe
    2. ...
    3. eBPF helpers supported for program type socket_filter:
    4. - bpf_map_lookup_elem
    5. - bpf_map_update_elem
    6. - bpf_map_delete_elem
    7. - bpf_ktime_get_ns
    8. - bpf_get_prandom_u32
    9. - bpf_get_smp_processor_id
    10. - bpf_tail_call
    11. - bpf_perf_event_output
    12. - bpf_skb_load_bytes
    13. ...

    使用man bpf-helpers或者在内核源码bpf辅助函数中,可以查看其使用说明


    三、BPF映射

    BPF映射用于提供大块的键值存储,这些存储可被用户空间程序访问,进而获取 eBPF 程序的运行状态。 在eBPF 程序需要大块存储时,就不能像常规的内核代码那样去分配内存了,而是必须通过 BPF 映射(BPF Map)来完成。

    bpf_map_type 类型在内核源码 include/uapi/linux/bpf.h

    1. enum bpf_map_type {
    2. BPF_MAP_TYPE_UNSPEC,
    3. BPF_MAP_TYPE_HASH,
    4. BPF_MAP_TYPE_ARRAY,
    5. BPF_MAP_TYPE_PROG_ARRAY,
    6. BPF_MAP_TYPE_PERF_EVENT_ARRAY,
    7. BPF_MAP_TYPE_PERCPU_HASH,
    8. BPF_MAP_TYPE_PERCPU_ARRAY,
    9. BPF_MAP_TYPE_STACK_TRACE,
    10. BPF_MAP_TYPE_CGROUP_ARRAY,
    11. BPF_MAP_TYPE_LRU_HASH,
    12. BPF_MAP_TYPE_LRU_PERCPU_HASH,
    13. BPF_MAP_TYPE_LPM_TRIE,
    14. BPF_MAP_TYPE_ARRAY_OF_MAPS,
    15. BPF_MAP_TYPE_HASH_OF_MAPS,
    16. BPF_MAP_TYPE_DEVMAP,
    17. BPF_MAP_TYPE_SOCKMAP,
    18. BPF_MAP_TYPE_CPUMAP,
    19. BPF_MAP_TYPE_XSKMAP,
    20. BPF_MAP_TYPE_SOCKHASH,
    21. BPF_MAP_TYPE_CGROUP_STORAGE,
    22. BPF_MAP_TYPE_REUSEPORT_SOCKARRAY,
    23. BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE,
    24. BPF_MAP_TYPE_QUEUE,
    25. BPF_MAP_TYPE_STACK,
    26. BPF_MAP_TYPE_SK_STORAGE,
    27. BPF_MAP_TYPE_DEVMAP_HASH,
    28. BPF_MAP_TYPE_STRUCT_OPS,
    29. BPF_MAP_TYPE_RINGBUF,
    30. BPF_MAP_TYPE_INODE_STORAGE,
    31. BPF_MAP_TYPE_TASK_STORAGE,
    32. };

    通过以下代码创建一个BPF映射,通过用户态程序的系统调用来创建

    1. int bpf_create_map(enum bpf_map_type map_type,
    2. unsigned int key_size,
    3. unsigned int value_size, unsigned int max_entries)
    4. {
    5. union bpf_attr attr = {
    6. .map_type = map_type,
    7. .key_size = key_size,
    8. .value_size = value_size,
    9. .max_entries = max_entries
    10. };
    11. return bpf(BPF_MAP_CREATE, &attr, sizeof(attr));
    12. }

    如果eBPF程序使用了BCC库,可以使用预定义的宏简化BPF的映射创建


    四、程序的分类

    bpf_prog_type 

    1. enum bpf_prog_type {
    2. BPF_PROG_TYPE_UNSPEC,
    3. BPF_PROG_TYPE_SOCKET_FILTER,
    4. BPF_PROG_TYPE_KPROBE,
    5. BPF_PROG_TYPE_SCHED_CLS,
    6. BPF_PROG_TYPE_SCHED_ACT,
    7. BPF_PROG_TYPE_TRACEPOINT,
    8. BPF_PROG_TYPE_XDP,
    9. BPF_PROG_TYPE_PERF_EVENT,
    10. BPF_PROG_TYPE_CGROUP_SKB,
    11. BPF_PROG_TYPE_CGROUP_SOCK,
    12. BPF_PROG_TYPE_LWT_IN,
    13. BPF_PROG_TYPE_LWT_OUT,
    14. BPF_PROG_TYPE_LWT_XMIT,
    15. BPF_PROG_TYPE_SOCK_OPS,
    16. BPF_PROG_TYPE_SK_SKB,
    17. BPF_PROG_TYPE_CGROUP_DEVICE,
    18. BPF_PROG_TYPE_SK_MSG,
    19. BPF_PROG_TYPE_RAW_TRACEPOINT,
    20. BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
    21. BPF_PROG_TYPE_LWT_SEG6LOCAL,
    22. BPF_PROG_TYPE_LIRC_MODE2,
    23. BPF_PROG_TYPE_SK_REUSEPORT,
    24. BPF_PROG_TYPE_FLOW_DISSECTOR,
    25. BPF_PROG_TYPE_CGROUP_SYSCTL,
    26. BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE,
    27. BPF_PROG_TYPE_CGROUP_SOCKOPT,
    28. BPF_PROG_TYPE_TRACING,
    29. BPF_PROG_TYPE_STRUCT_OPS,
    30. BPF_PROG_TYPE_EXT,
    31. BPF_PROG_TYPE_LSM,
    32. BPF_PROG_TYPE_SK_LOOKUP,
    33. };

    也可以使用

    bpftool feature probe | grep program_type 

    应用程序类型大致可以分为三类

    1,跟踪,从内核和程序的运行状态中提取跟踪信息

    2,网络,对网络数据包进行过滤和处理

    3、其他,安全,BPF扩展

    跟踪类

    主要用于从系统中提取跟踪信息,为监控、排错,性能优化提供数据支撑;

    程序的类型有

    类型描述
    BPF_PROG_TYPE_KPROBE用于对特定函数进行插装kprobe,uprobe
    BPF_PROG_TYPE_TRACEPOINT用于内核静态跟踪点
    BPF_PROG_TYPE_PERF_EVENT用于性能事件跟踪,内核调用,定时器,硬件等

    网络类

    主要用于对网络数据包进行过滤和处理,进而实现网络的观测、过滤、流量控制等功能

    又可分为XDP,TC,套接字和cgroup程序

    1、XDP程序

            类型 :BPF_PROG_TYPE_XDP

            在网络驱动程序刚收到数据包时触发,无需通过网络协议栈,可用来实现高性能网络处理方案,常用于DDos防御,防火墙,4层负载均衡等场景。

            XDP程序在处理网络包后,有不同处理方式

    命令码含义使用场景
    XDP_DROP丢包防火墙,DDOS攻击防御
    XDP_PASS传递到内核协议栈正常处理

    XDP_TX

    XDP_REDIRECT

    转发到同一/不同网卡负载均衡
    XDP_ABORTED错误XDP处理错误

    命令行使用

    1. # eth1 为网卡名
    2. # xdpgeneric 设置运行模式为通用模式
    3. # xdp-example.o 为编译后的 XDP 字节码
    4. sudo ip link set dev eth1 xdpgeneric object xdp-example.o
    5. #卸载
    6. sudo ip link set veth1 xdpgeneric off

    BCC库函数

    1. from bcc import BPF
    2. # 编译XDP程序
    3. b = BPF(src_file="xdp-example.c")
    4. fn = b.load_func("xdp-example", BPF.XDP)
    5. # 加载XDP程序到eth0网卡
    6. device = "eth0"
    7. b.attach_xdp(device, fn, 0)
    8. ...
    9. # 卸载XDP程序
    10. b.remove_xdp(device)

    2、TC程序

    类型:BPF_PROG_TYPE_SCHED_CLS 和 BPF_PROG_TYPE_SCHED_ACT

    分别作为 Linux 流量控制 的分类器和执行器,Linux 流量控制通过网卡队列、排队规则、分类器、过滤器以及执行器等,实现了对网络流量的整形调度和带宽控制。

    TC可以直接获取内核解析后的网络报文数据结构sk_buff,可以在发送和接收两个方向上执行

    1. 接收网络包,TC程序在网卡接收(GRO)之后、协议栈处理之前
    2. 发送网络包,TC乘坐在协议栈处理之后,数据包发送到网卡队列之前(GOS)执行
    1. # 创建 clsact 类型的排队规则
    2. sudo tc qdisc add dev eth0 clsact
    3. # 加载接收方向的 eBPF 程序
    4. sudo tc filter add dev eth0 ingress bpf da obj tc-example.o sec ingress
    5. # 加载发送方向的 eBPF 程序
    6. sudo tc filter add dev eth0 egress bpf da obj tc-example.o sec egress

    Traffic Control HOWTO (linux-ip.net)

    3、套接字程序

            用于过滤,观测或重定向套接字网络包。

            可以挂载到套接字socket,控制组cgroup以及网络命名空间netns等位置

            类型以及说明

    类型说明

    BPF_PROG_TYPE_SOCKET_FILTER

    套接字过滤和观测

    BPF_PROG_TYPE_SOCK_OPS

    套接字修改重定向

    BPF_PROG_TYPE_SK_SKB

    用于套接字修改或消息流动态解析

    BPF_PROG_TYPE_SK_MSG

    用于控制内核是否发送消息套解析

    BPF_PROG_TYPE_SK_REUSEPORT

    控制端口是否重用
    BPF_PROG_TYPE_SK_LOOKUP用于TCP连接选择监听套接字或为UDP数据包选择未连接的套接字,绕过bind系统调用

    使用例子第一条如:用户态 setsockopt(sock,SOL_SOCKET,SO_ATTACH_BPF,...)绑定BPF到具体的socket上。

    4、cgroup程序

            对cgroup内所有的进程的网络过滤、套接字选项以及转发等进行动态控制

            典型用在容器中多进程进行网络控制

            类型及说明

    类型说明
    BPF_PROG_TYPE_CGROUP_SKB    在入口和出口过滤包,并可以接收或拒绝数据包
    BPF_PROG_TYPE_CGROUP_SOCK在套接字创建、释放和绑定地址时,接受或拒绝操作,也可用于统计套接字信息
    BPF_PROG_TYPE_CGROUP_DEVICE对设备文件访问进行过滤
    BPF_PROG_TYPE_CGROUP_SOCK_ADDR在connect,bind,sendto,recvmsg中修改IP地址和端口
    BPF_PROG_TYPE_CGROUP_SYSCTL对设备文件访问过滤
    BPF_PROG_TYPE_CGROUP_SOCKOPT在setsockopt和getsockopt操作中修改套接字

    这些类型的BPF程序都可以通过BPF系统调用的BPF_PROG_ATTACH进行挂载。如

    1. union bpf_attr attr = {};
    2. attr.target_fd = target_fd; // cgroup文件描述符
    3. attr.attach_bpf_fd = prog_fd; // BPF程序文件描述符
    4. attr.attach_type = BPF_CGROUP_DEVICE; // 挂载类型为BPF_CGROUP_DEVICE
    5. if (bpf(BPF_PROG_ATTACH, &attr, sizeof(attr)) < 0) {
    6. return -errno;
    7. }
    8. ...

    其他eBPF程序

    类型说明
    BPF_PROG_TYPE_LSM  用于安全模块访问控制和审计策略
    BPF_PROG_TYPE_LWT_IN,
    BPF_PROG_TYPE_LWT_OUT,
    BPF_PROG_TYPE_LWT_XMIT,
     用于轻量级的封装或解封装
    BPF_PROG_TYPE_STRUCT_OPS  拥塞控制
    BPF_PROG_TYPE_FLOW_DISSECTOR 流量解析器
    BPF_PROG_TYPE_EXT 扩展程序

    参考

    bpf.h - include/uapi/linux/bpf.h - Linux source code (v5.13) - Bootlin

    XDP - IO Visor Project

    Traffic Control HOWTO (linux-ip.net)


  • 相关阅读:
    Jmeter连接Mysql数据库
    运筹说 第82期 | 算法介绍之图与网络分析(二)
    golang学习笔记系列之复杂数据类型
    爬虫超详细介绍
    jmeter利用bean shell加密解密方法
    攻防世界Check
    go每日一题——内嵌,json.Marshal函数
    easyexecl导出100万行execl报字体错误的解决办法
    K8s Ingress Controller部署指南:轻松实现服务外部访问
    使用React.ts创建一个密码生成器的简单示例
  • 原文地址:https://blog.csdn.net/WANGYONGZIXUE/article/details/126440846