Getting started with XDP--how eBPF programs print log, printf log, print log

We all know that it is very important to write any program, the program can print the log, so that the programmer can understand the running status of the program through the log. In the previous hello world, we simply discarded the message without printing any log. In this article, we will understand how to print the log in the XDP BPF program.
In the C language, we often use printf to print the log to the standard output, but in the BPF program, the printf function cannot be used. We use the bpf_trace_printk() function in bpf_helpers.h as an output instead of formatted printing. This example is verified on the Raspberry Pi system.

1. In the first step, we need to install libbpf-dev, clang, llvm, bpfcc-tools

sudo apt install libbpf-dev clang llvm bpfcc-tools

2. In the second step, we write code to parse out the source IP address and destination IP address of the received message


#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. In the third step, call the bpf_trace_printk() function to print the source IP address and destination IP address of each message

See the above code, note that bpf_trace_printk() can only print up to 3 variables, so in this code example, only the last two bytes of the IP address are printed.

4. In the fourth step, configure the bpf program to eth0, and check the printed log

The test environment is as follows:

                                                     +- 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    |                                                       
                                                     +-------------+          |             |
                                                                              +-------------+

From 10.0.0.1 (iperf3 -c 10.0.0.10) to 10.0.0.10 (iperf3 -s) to play iperf3 data flow, the printed log is placed in the /sys/kernel/debug/tracing/trace file, we can use The following command to view

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

or

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

The result is as follows:


          <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. Problems encountered in the middle:

  1. No stubs-soft.h file
sudo ln -s /usr/include/gnu/stubs-hard.h /usr/include/gnu/stubs-soft.h
  1. The compilation alarm says that there are too many entry parameters.
    bpf_trace_printk() only supports up to 3 variable parameters, and an error will be reported if more than 3

  2. bpf_trace_printk() supports directly printing the format string of the ip address
    %pi4 : IPv4 address format with leading 0
    %pI4 : IPv4 address format without leading 0

Guess you like

Origin blog.csdn.net/meihualing/article/details/130809679