本文目录
我们都知道,写任何程序,程序能打印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、中间碰到的问题:
- 没有stubs-soft.h文件
sudo ln -s /usr/include/gnu/stubs-hard.h /usr/include/gnu/stubs-soft.h
-
编译报警说有太多入口参数问题
bpf_trace_printk()最多只支持3个变量参数,超过3个则报错 -
bpf_trace_printk()支持直接打印ip地址的格式化字符串
%pi4: 带前导0的IPv4地址格式
%pI4: 不带前导0的IPv4地址格式