eBPF入门系列:实现快速响应Ping包
XDP
学习项目
编译及运行
# 使用bpf2go生成对应的golang类型的eBPF数据结构及函数
$ go generate
# 编译
$ go build
# 运行,加载eBPF程序到enp0s3网卡
$ sudo ./xdpping --dev enp0s3源码
//go:build ignore
#include "../headers/bpf_all.h"
// 挂载点为xdp,函数名xdp_ping(可自定义),参数类型xdp_md(由内核xdp函数参数决定)
SEC("xdp")
int xdp_ping(struct xdp_md *ctx)
{
bpf_printk("xdpping starting\n");
void *data = ctx_ptr(ctx, data);
void *data_end = ctx_ptr(ctx, data_end);
// 根据原始数据包解析出以太网包头信息
struct ethhdr *eth;
eth = (typeof(eth))data;
// struct ethhdr *eth = data;
if ((void *)(eth + 1) > data_end)
return XDP_PASS;
// 判断是否为IP协议
if (eth->h_proto != bpf_htons(ETH_P_IP))
return XDP_PASS;
// 解析IP包头信息
struct iphdr *iph;
iph = (typeof(iph))(eth + 1);
// struct iphdr *iph = (void *)(eth + 1);
if ((void *)(iph + 1) > data_end)
return XDP_PASS;
// 判断是否为ICMP协议
if (iph->protocol != IPPROTO_ICMP)
return XDP_PASS;
// CSUM_SIZE用于判断ICMP包长度合法性和计算ICMP包头checksum信息
#define CSUM_SIZE 40
int csum_size = CSUM_SIZE;
// 解析ICMP数据包头信息
struct icmphdr *icmph;
// icmph = (typeof(icmph))((void *)iph + (iph->ihl * 4)); // FAILED: R3 offset is outside of the packet
icmph = (typeof(icmph))(iph + 1);
// struct icmphdr *icmph = (void *)(iph + 1);
if ((void *)(icmph) + csum_size > data_end)
return XDP_PASS;
// 判断是否为ICMP request类型包
if (icmph->type != ICMP_ECHO)
return XDP_PASS;
// FAILED: int csum_size = iph->tot_len - sizeof(*iph); // R3 offset is outside of the packet
// FAILED: int csum_size = data_end - (void *)icmph; // R4 unbounded memory access, use 'var &= const' or 'if (var < const)'
// 构造ICMP reply包头
icmph->type = ICMP_ECHOREPLY;
icmph->checksum = 0; // Note: reset and then checksum
icmph->checksum = ipv4_csum(icmph, csum_size);
// 构造IP包头
__be32 daddr = iph->daddr;
iph->daddr = iph->saddr;
iph->saddr = daddr;
iph->ttl = 64;
iph->check = 0; // Note: reset and then checksum
iph->check = ipv4_csum(iph, sizeof(*iph));
// 构造以太网包头
char dmac[ETH_ALEN];
__builtin_memcpy(dmac, eth->h_dest, ETH_ALEN);
__builtin_memcpy(eth->h_dest, eth->h_source, ETH_ALEN);
__builtin_memcpy(eth->h_source, dmac, ETH_ALEN);
bpf_printk("xdpping replay icmp echo reply\n");
// 从当前网卡发送相应数据包,数据包不再进入内核协议栈处理
return XDP_TX;
}TC
学习项目
编译及运行
源码
小结
Last updated