https://cloud.tencent.com/developer/user/2577825
文章涉及的实验环境和代码可以到这个git repo获取: https://github.com/nevermosby/linux-bpf-learning
当停止了上篇文章实验中的XDP ingress hook,只保留TC egress hook时,使用命令curl localhost
也是无法访问Nginx容器服务的?这是为什么呢?
第一种思路理论上是比较容易实现的,就是在适当的位置添加printf
函数,但由于这个函数需要在内核运行,而BPF中没有实现它,因此无法使用。事实上,BPF程序能的使用的C语言库数量有限,并且不支持调用外部库。
为了克服这个限制,最常用的一种方法是定义和使用BPF辅助函数,即helper function。比如可以使用bpf_trace_printk()
辅助函数,这个函数可以根据用户定义的输出,将BPF程序产生的对应日志消息保存在用来跟踪内核的文件夹(/sys/kernel/debug/tracing/
),这样,我们就可以通过这些日志信息,分析和发现BPF程序执行过程中可能出现的错误。
BPF默认定义的辅助函数有很多,它们都是非常有用的,可谓是「能玩转辅助函数,就能玩转BPF编程」。可以在这里找到全量的辅助函数清单。
bpf_trace_printk()
辅助函数添加日志这个函数的入门使用方法和输出说明可以在这篇文章中找到,现在我们把它加到BPF程序里。老规矩,直接上代码:
#include <stdbool.h>
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/in.h>
#include <linux/pkt_cls.h>
#include <stdio.h>
#include "bpf_endian.h"
#include "bpf_helpers.h"
typedef unsigned int u32;
#define bpfprint(fmt, ...) \
({ \
char ____fmt[] = fmt; \
bpf_trace_printk(____fmt, sizeof(____fmt), \
##__VA_ARGS__); \
})
/*
check whether the packet is of TCP protocol
*/
static __inline bool is_TCP(void *data_begin, void *data_end){
bpfprint("Entering is_TCP\n");
struct ethhdr *eth = data_begin;
// Check packet's size
// the pointer arithmetic is based on the size of data type, current_address plus int(1) means:
// new_address= current_address + size_of(data type)
if ((void *)(eth + 1) > data_end) //
return false;
// Check if Ethernet frame has IP packet
if (eth->h_proto == bpf_htons(ETH_P_IP))
{
struct iphdr *iph = (struct iphdr *)(eth + 1); // or (struct iphdr *)( ((void*)eth) + ETH_HLEN );
if ((void *)(iph + 1) > data_end)
return false;
// extract src ip and destination ip
u32 ip_src = iph->saddr;
u32 ip_dst = iph->daddr;
//
bpfprint("src ip addr1: %d.%d.%d\n",(ip_src) & 0xFF,(ip_src >> 8) & 0xFF,(ip_src >> 16) & 0xFF);
bpfprint("src ip addr2:.%d\n",(ip_src >> 24) & 0xFF);
bpfprint("dest ip addr1: %d.%d.%d\n",(ip_dst) & 0xFF,(ip_dst >> 8) & 0xFF,(ip_dst >> 16) & 0xFF);
bpfprint("dest ip addr2: .%d\n",(ip_dst >> 24) & 0xFF);
// Check if IP packet contains a TCP segment
if (iph->protocol == IPPROTO_TCP)
return true;
}
return false;
}
SEC("xdp")
int xdp_drop_tcp(struct xdp_md *ctx)
{
void *data_end = (void *