BPF进阶 - BPF常用命令

这篇文章主要解析常用的BPF命令
参考文章:https://www.freebsd.org/cgi/man.cgi?query=bpf&sektion=4&manpath=FreeBSD | 4.7-RELEASE

上篇文章 BPF初探 - Android中BPF运用实例解析 介绍了Android源码中对BPF的运用,其中配置的BPF过滤规则看起来很简洁,但类似汇编代码的规则配置让初学者看起来非常难受,下面就对常用的指令进行解释。

基础知识

  在指令解析之前,为了能够更好的理解,我们需要先贯彻以下几点知识:
  
  1. 以太头长度定长,一共14个字节

/** if_ether.h*/
#define ETH_ALEN 6
struct ethhdr {
    unsigned char h_dest[ETH_ALEN];          // 6字节dest mac
    unsigned char h_source[ETH_ALEN];        // 6字节src mac
    __be16 h_proto;                          // 2字节protocol
} 

  2. ip头不定长,需要根据ip头中第一个字节后4位(BIG_ENDIAN)来确定

/** ip.h*/
struct iphdr {
#if defined(__LITTLE_ENDIAN_BITFIELD)
    __u8    ihl:4,
            version:4;
#elif defined (__BIG_ENDIAN_BITFIELD)
    __u8    version:4,                     // IPv4|IPv6
            ihl:4;                         // ip头长度
#else
#error  "Please fix <asm/byteorder.h>"
#endif
    __u8    tos;                           // 服务类型,代表包的优先级
    __be16  tot_len;                       // 整个包分节长度
    __be16  id;                            // 标识 
    __be16  frag_off;                      // 片偏移
    __u8    ttl;                           // 生存时间
    __u8    protocol;                      // 应用协议
    __sum16 check;                         // 校验和
    __be32  saddr;                         // 源地址
    __be32  daddr;                         // 目标地址
    /*The options start here. */           // 上面一共20字节,如果有可选选项,则会导致ip头长度超过20
};

  综上两点,我们可以得出,到ip头的偏移就是ether头的长度,到udp/tcp/icmp头的偏移需要动态计算ip头的长度。
 
BPF常用指令解析

  BPF一共有8种类型的指令,分别是 BPF_LD, BPF_LDX, BPF_ST, BPF_STX, BPF_ALU, BPF_JMP, BPF_RET, BPF_MISC

  BPF_LD  将值复制到累加器(A)

/**BPF_LD */
BPF_STMT(BPF_LD | BPF_W | BPF_ABS, k)   A <- P[k:4]     // 将k字节偏移处往后4个字节存入A中
BPF_STMT(BPF_LD | BPF_H | BPF_ABS, k)   A <- P[k:2]     // 将k字节偏移处往后2个字节存入A中
BPF_STMT(BPF_LD | BPF_B | BPF_ABS, k)   A <- P[k:1]     // 将k字节偏移处往后1个字节存入A中
BPF_STMT(BPF_LD | BPF_W | BPF_IND, k)   A <- P[X+k:4]   // 将(X寄存器值与k的和)偏移处往后4个字节存入A中
BPF_STMT(BPF_LD | BPF_H | BPF_IND, k)   A <- P[X+k:2]   // 将(X寄存器值与k的和)偏移处往后2个字节存入A中
BPF_STMT(BPF_LD | BPF_B | BPF_IND, k)   A <- P[X+k:1]   // 将(X寄存器值与k的和)偏移处往后1个字节存入A中
BPF_STMT(BPF_LD | BPF_W | BPF_LEN)      A <- len        // 将包长度存存入A中
BPF_STMT(BPF_LD | BPF_IMM, k)           A <- k          // 将k值存入A中
BPF_STMT(BPF_LD | BPF_MEM, k)           A <- M[k]       // 将k地址内存的值存入A中

  BPF_LDX 将值复制到寄存器(X)

/**BPF_LDX */
BPF_STMT(BPF_LDX | BPF_W | BPF_IMM, k)  X <- k              // 将k值存入X中
BPF_STMT(BPF_LDX | BPF_W | BPF_MEM, k)  X <- M[k]           // 将k地址内存的值存入X中
BPF_STMT(BPF_LDX | BPF_W | BPF_LEN, k)  X <- len            // 将包长度存入X中

BPF_STMT(BPF_LDX | BPF_B | BPF_MSH, k)  X <- 4*(P[k:1]&0xf) // 用于计算ip头的长度 --->
                                        // ---> 将偏移k处一个字节后4位转换成十进制乘以4

  BPF_ST  将A累加器中的值存入存储器中

/**BPF_ST */
BPF_STMT(BPF_ST, k)                     M[k] <- X       // 将A中的值存入存储器中

  BPF_STX 将X寄存器中的值存入存储器中

/**BPF_STX */
BPF_STMT(BPF_ST, k)                     M[k] <- X       // 将X中的值存入存储器中

  BPF_ALU 将A累加器中的值进行不同方式的计算并存入A中

/**BPF_ALU */
BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, k)  A <- A + k      // A + k 后存入A中
BPF_STMT(BPF_ALU | BPF_SUB | BPF_K, k)  A <- A - k      // ..
BPF_STMT(BPF_ALU | BPF_MUL | BPF_K, k)  A <- A * k      
BPF_STMT(BPF_ALU | BPF_DIV | BPF_K, k)  A <- A / k
BPF_STMT(BPF_ALU | BPF_AND | BPF_K, k)  A <- A & k
BPF_STMT(BPF_ALU | BPF_OR | BPF_K, k)   A <- A | k
BPF_STMT(BPF_ALU | BPF_LSH | BPF_K, k)  A <- A << k
BPF_STMT(BPF_ALU | BPF_RSH | BPF_K, k)  A <- A >> k
BPF_STMT(BPF_ALU | BPF_ADD | BPF_X)     A <- A + X
BPF_STMT(BPF_ALU | BPF_SUB | BPF_X)     A <- A - X
BPF_STMT(BPF_ALU | BPF_MUL | BPF_X)     A <- A * X
BPF_STMT(BPF_ALU | BPF_DIV | BPF_X)     A <- A / X
BPF_STMT(BPF_ALU | BPF_AND | BPF_X)     A <- A & X
BPF_STMT(BPF_ALU | BPF_OR | BPF_X)      A <- A | X
BPF_STMT(BPF_ALU | BPF_LSH | BPF_X)     A <- A << X
BPF_STMT(BPF_ALU | BPF_RSH | BPF_X)     A <- A >> X
BPF_STMT(BPF_ALU | BPF_NEG)             A <- -A

  BPF_JMP 条件跳转,根据条件跳转到不同偏移的命令

/**BPF_JMP */
BPF_JUMP(BPF_JMP | BPF_JA, k)           pc += k         // 永远跳转到这条命令后偏移k的命令
// 如果A>k,则跳转到偏移jt的命令,否则跳转到偏移为jf的命令  
BPF_JUMP(BPF_JMP | BPF_JGT | BPF_K, k)  pc += (A > k) ? jt : jf 
BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, k)  pc += (A >= k) ? jt : jf
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, k)  pc += (A == k) ? jt : jf
BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K, k) pc += (A & k) ? jt : jf
BPF_JUMP(BPF_JMP | BPF_JGT | BPF_X)     pc += (A > X) ? jt : jf
BPF_JUMP(BPF_JMP | BPF_JGE | BPF_X)     pc += (A >= X) ? jt : jf
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_X)     pc += (A == X) ? jt : jf
BPF_JUMP(BPF_JMP | BPF_JSET | BPF_X)    pc += (A & X) ? jt : jf

  BPF_RET 结束指令,设定接收的包的长度

/** BPF_RET*/
BPF_STMT(BPF_RET | BPF_A),                          // 接收长度为A累加器值的包
BPF_STMT(BPF_RET | BPF_K, k)                        // 接收长度为k的包

  BPF_MISC 将A中的值存入X中,或将X中的值存入A中

/** BPF_MISC*/
BPF_STMT(BPF_MISC | BPF_TAX)                X <- A      // 将A中的值存入X中
BPF_STMT(BPF_MISC | BPF_TXA)                A <- X      // 将X中的值存入A中

使用BPF_LDBPF_LDX将k值存入A或者X中,使用BPF_JUMP将A中的值与k或者X进行比较,实现指令跳转,可以跳转到下一步的过滤指令,或者跳转到BPF_RET进行截取包长度的限定,如果截取包的长度为0,则代表未匹配。

已标记关键词 清除标记
相关推荐
BPF and related observability tools give software professionals unprecedented visibility into software, helping them analyze operating system and application performance, troubleshoot code, and strengthen security. BPF Performance Tools: Linux System and Application Observability is the industry’s most comprehensive guide to using these tools for observability. Brendan Gregg, author of the industry’s definitive guide to system performance, introduces powerful new methods and tools for doing analysis that leads to more robust, reliable, and safer code. This authoritative guide: Explores a wide spectrum of software and hardware targets Thoroughly covers open source BPF tools from the Linux Foundation iovisor project’s bcc and bpftrace repositories Summarizes performance engineering and kernel internals you need to understand Provides and discusses 150+ bpftrace tools, including 80 written specifically for this book: tools you can run as-is, without programming — or customize and develop further, using diverse interfaces and the bpftrace front-end You’ll learn how to use BPF (eBPF) tracing tools to analyze CPUs, memory, disks, file systems, networking, languages, applications, containers, hypervisors, security, and the Linux kernel. You’ll move from basic to advanced tools and techniques, producing new metrics, stack traces, custom latency histograms, and more. It’s like having a superpower: with Gregg’s guidance and tools, you can analyze virtually everything that impacts system performance, so you can improve virtually any Linux operating system or application.
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页