この記事のディレクトリ
プログラムを作成する際に、プログラムはログを出力して、プログラマーがログを通じてプログラムの実行ステータスを理解できるようにすることが非常に重要であることは誰もが知っています。前回の hello world では、ログを出力せずに単にメッセージを破棄しましたが、この記事では、XDP BPF プログラムでログを出力する方法を理解します。
C言語ではログを標準出力に出力するにはprintf関数を使用することが多いですが、BPFプログラムではprintf関数が使用できないため、定型出力の代わりにbpf_helpers.hのbpf_trace_printk()関数を使用して出力します。この例は Raspberry Pi システムで検証されています。
1. 最初のステップでは、libbpf-dev、clang、llvm、bpfcc-tools をインストールする必要があります。
sudo apt install libbpf-dev clang llvm bpfcc-tools
2. 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. 3 番目のステップでは、bpf_trace_printk() 関数を呼び出して、各メッセージの送信元 IP アドレスと宛先 IP アドレスを出力します。
上記のコードを参照してください。bpf_trace_printk() は最大 3 つの変数しか出力できないことに注意してください。そのため、このコード例では、IP アドレスの最後の 2 バイトのみが出力されます。
4. 4 番目のステップでは、bpf プログラムを eth0 に設定し、出力されたログを確認します。
テスト環境は以下の通りです。
+- 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 データ フローを再生すると、出力されたログは /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 アドレス形式