XDP入门--eBPF程序如何打印log, printf log,打印日志

我们都知道,写任何程序,程序能打印log,让编程人员能通过log了解程序的运行状态是非常重要的。上一篇hello world中,我们只是简单的丢弃报文,没有打印任何log,本文中,我们将来理解一下如何在XDP的BPF程序中打印log。
C语言中,我们常用printf来打印log到标准输出,但在BPF程序里,是不能使用printf函数的,我们使用bpf_helpers.h里的bpf_trace_printk()函数来做为代替格式化打印的输出。本例子在树莓派系统上验证通过。

1、第一步,我们需要安装libbpf-dev, clang, llvm, bpfcc-tools

sudo apt install libbpf-dev clang llvm bpfcc-tools

2、第二步,我们编写代码,解析出收到的报文的源IP地址和目标IP地址


#include <stdio.h>
#include <linux/bpf.h>
#include <net/ethernet.h>
#include <linux/if_vlan.h>
#include <netinet/in.h>
#include <linux/ip.h>
#include <bpf/bpf_helpers.h>

#ifndef __section
# define __section(NAME)                  \
   __attribute__((section(NAME), used))
#endif

__section("prog")
int xdp_ip_filter(struct xdp_md *ctx)
{
    
    
    void *end = (void *)(long)ctx->data_end;
    void *data = (void *)(long)ctx->data;
    int ip_src;
    int ip_dst;
    long int offset;
    short int eth_type;

    char info_fmt1[] = "Dst Addr: %pi4";
    char info_fmt2[] = "Src Addr: %pi4";
    char info_fmt3[] ="-----------------";
    
    static int i = 0;
    static int j = 0;
    unsigned char *saddrpoint = 0;
    unsigned char *daddrpoint = 0;

    struct ethhdr *eth = data;
    offset = sizeof(*eth);

    if (data + offset > end) {
    
    
    return XDP_ABORTED;
    }
    eth_type = eth->h_proto;

    /* 只处理 IPv4 地址*/
    
    if (eth_type == ntohs(ETH_P_IPV6)) {
    
    
        return XDP_PASS;
    }

    struct iphdr *iph = data + offset;
    offset += sizeof(struct iphdr);
    /* make sure the bytes you want to read are within the packet's range before reading them
    * 在读取之前,确保你要读取的子节在数据包的长度范围内
    */
    if (iph + 1 > end) {
    
    
        return XDP_ABORTED;
    }
    ip_src = iph->saddr;
    ip_dst = iph->daddr;

    saddrpoint = (unsigned char*)(&ip_src);
    daddrpoint =(unsigned char*)(&ip_dst);
    
    if(ip_dst == 0x1000000a)
    {
    
    
        i++;
        saddrpoint = saddrpoint +2;
        daddrpoint = daddrpoint +2;
        bpf_trace_printk(info_fmt3, sizeof(info_fmt3));
        bpf_trace_printk(info_fmt2, sizeof(info_fmt2), &ip_src);
        bpf_trace_printk(info_fmt1, sizeof(info_fmt1), &ip_dst);
    }
    else
    {
    
    
        j++;
        saddrpoint = saddrpoint +2;
        daddrpoint = daddrpoint +2;
        bpf_trace_printk(info_fmt2, sizeof(info_fmt2), &ip_src);
        bpf_trace_printk(info_fmt1, sizeof(info_fmt1), &ip_dst);
    }
    return XDP_PASS;
}

char __license[] __section("license") = "GPL";


3、第三步,调用bpf_trace_printk()函数打印每个报文的源IP地址和目标IP地址

见上面代码,注意bpf_trace_printk()最多只能打印3个变量,所以本代码示例中,只打印IP地址后二个字节。

4、第四步,配置bpf程序到eth0,并查看打印出来的log

测试环境如下:

                                                     +- RPi -------+          +- old pc1----+
                                                     |         Eth0+----------+ Eth0        |    
                 +- Router ----+                     |  DHCP server|          | 10.0.0.10   |
                 | Firewall    |                     |   10.0.0.1  |          |             |
(Internet)---WAN-+ DHCP server +-WLAN AP-+-)))   (((-+ WLAN        |          +-------------+
                 | 192.168.3.1 |                     |             |          
                 +-------------+                     |             |          +- old pc2----+
                                                     |         Eth1+----------+ Eth0        |   
                                                     |             |          | 10.0.0.4    |                                                       
                                                     +-------------+          |             |
                                                                              +-------------+

从10.0.0.1(iperf3 -c 10.0.0.10)向10.0.0.10(iperf3 -s)打iperf3的数据流,则打印出来的log放在/sys/kernel/debug/tracing/trace文件中,我们可以用以下命令进行查看

sudo ip link set dev eth0 xdp obj xdp-example.o
sudo tail /sys/kernel/debug/tracing/trace

或者

sudo ip link set dev eth0 xdp obj xdp-example.o
sudo cat /sys/kernel/debug/tracing/trace_pipe

结果如下:


          <idle>-0       [000] d.s.. 28261.939281: bpf_trace_printk: -----------------
          <idle>-0       [000] d.s.. 28261.939318: bpf_trace_printk: Src Addr: 010.000.000.006
          <idle>-0       [000] d.s.. 28261.939335: bpf_trace_printk: Dst Addr: 010.000.000.001
          <idle>-0       [000] d.s.. 28261.949260: bpf_trace_printk: -----------------
          <idle>-0       [000] d.s.. 28261.949297: bpf_trace_printk: Src Addr: 010.000.000.006
          <idle>-0       [000] d.s.. 28261.949309: bpf_trace_printk: Dst Addr: 010.000.000.001
          <idle>-0       [000] d.s.. 28261.959214: bpf_trace_printk: -----------------
          <idle>-0       [000] d.s.. 28261.959253: bpf_trace_printk: Src Addr: 010.000.000.006
          <idle>-0       [000] d.s.. 28261.959262: bpf_trace_printk: Dst Addr: 010.000.000.001
          <idle>-0       [000] d.s.. 28261.969038: bpf_trace_printk: -----------------
          <idle>-0       [000] d.s.. 28261.969072: bpf_trace_printk: Src Addr: 010.000.000.006
          <idle>-0       [000] d.s.. 28261.969083: bpf_trace_printk: Dst Addr: 010.000.000.001
          <idle>-0       [000] d.s.. 28261.979048: bpf_trace_printk: -----------------
          <idle>-0       [000] d.s.. 28261.979084: bpf_trace_printk: Src Addr: 010.000.000.006
          <idle>-0       [000] d.s.. 28261.979094: bpf_trace_printk: Dst Addr: 010.000.000.001
          <idle>-0       [000] d.s.. 28261.988944: bpf_trace_printk: -----------------
          <idle>-0       [000] d.s.. 28261.988980: bpf_trace_printk: Src Addr: 010.000.000.006
          <idle>-0       [000] d.s.. 28261.988990: bpf_trace_printk: Dst Addr: 010.000.000.001



5、中间碰到的问题:

  1. 没有stubs-soft.h文件
sudo ln -s /usr/include/gnu/stubs-hard.h /usr/include/gnu/stubs-soft.h
  1. 编译报警说有太多入口参数问题
    bpf_trace_printk()最多只支持3个变量参数,超过3个则报错

  2. bpf_trace_printk()支持直接打印ip地址的格式化字符串
    %pi4: 带前导0的IPv4地址格式
    %pI4: 不带前导0的IPv4地址格式

猜你喜欢

转载自blog.csdn.net/meihualing/article/details/130809679