0x00 前言
0x01 Tail Call
尾调用 VS 普通函数调用
例1:XDP
先列举一个简单的例子:
SEC("xdp/icmp")
int handle_icmp(struct xdp_md *ctx) {
bpf_printk("new icmp packet captured (XDP)\n");
return XDP_PASS;
}
SEC("xdp/tcp")
int handle_tcp(struct xdp_md *ctx) {
bpf_printk("new tcp packet captured (XDP)\n");
return XDP_PASS;
}
SEC("xdp/udp")
int handle_udp(struct xdp_md *ctx) {
bpf_printk("new udp packet captured (XDP)\n");
return XDP_PASS;
}
// 在内核态声明一个尾调用的MAP,并且初始化
struct {
__uint(type, BPF_MAP_TYPE_PROG_ARRAY);
__type(key, u32);
__type(value, u32);
__uint(max_entries, 3);
__array(values, int());
} packet_processing_progs SEC(".maps") = {
.values =
{
[0] = &handle_icmp,
[1] = &handle_tcp,
[2] = &handle_udp,
},
};
SEC("xdp_classifier")
int packet_classifier(struct xdp_md *ctx) {
// bpf_printk("new packet_classifier (XDP)\n");
void *data_end = (void *)(long)ctx->data_end;
void *data = (void *)(long)ctx->data;
struct ethhdr *eth = data;
struct iphdr *ip;
// 检查是否有足够的数据空间
if ((void *)(eth + 1) > data_end) {
return XDP_ABORTED;
}
// satisfy with ebpf verifier
if (eth->h_proto != 8) {
return XDP_PASS;
}
ip = (struct iphdr *)(eth + 1);
// 检查IP头部是否完整
if ((void *)(ip + 1) > data_end) {
return XDP_ABORTED;
}
bpf_printk("protocol: %d\n", ip->protocol);
bpf_printk("icmp: %d,tcp:%d,udp:%d\n", IPPROTO_ICMP, IPPROTO_TCP, IPPROTO_UDP);
switch (ip->protocol) {
case IPPROTO_ICMP:
bpf_printk("icmp\n");
bpf_tail_call(ctx, &packet_processing_progs, 0);
break;
case IPPROTO_TCP:
bpf_printk("tcp\n");
bpf_tail_call(ctx, &packet_processing_progs, 1);
break;
case IPPROTO_UDP:
bpf_printk("udp\n");
bpf_tail_call(ctx, &packet_processing_progs, 2);
break;
default:
bpf_printk("unknown protocol\n");
break;
}
return XDP_PASS;
}
例2:tracee
// trace/events/syscalls.h: TP_PROTO(struct pt_regs *regs, long id)
// initial entry for sys_enter syscall logic
SEC("raw_tracepoint/sys_enter")
int tracepoint__raw_syscalls__sys_enter(struct bpf_raw_tracepoint_args *ctx)
{
struct task_struct *task = (struct task_struct *) bpf_get_current_task();
int id = ctx->args[1];
if (is_compat(task)) {
// Translate 32bit syscalls to 64bit syscalls, so we can send to the correct handler
u32 *id_64 = bpf_map_lookup_elem(&sys_32_to_64_map, &id);
if (id_64 == 0)
return 0;
id = *id_64;
}
bpf_tail_call(ctx, &sys_enter_init_tail, id);
return 0;
}
0x0 参考
- bpf_tail_call特性介绍
- 在 ebpf/libbpf 程序中使用尾调用(tail calls)
- 22-tail-calls
- eBPF Talk: eBPF 尾调用简介
- eBPF 入门实践教程二十一: 使用 XDP 进行可编程数据包处理
FEATURED TAGS
Latex
gRPC
负载均衡
OpenSSH
Authentication
Consul
Etcd
Kubernetes
性能优化
Python
分布式锁
WebConsole
后台开发
Golang
OpenSource
Nginx
Vault
网络安全
Perl
分布式理论
Raft
正则表达式
Redis
分布式
限流
go-redis
微服务
反向代理
ReverseProxy
Cache
缓存
连接池
OpenTracing
GOMAXPROCS
GoMicro
微服务框架
日志
zap
Pool
Kratos
Hystrix
熔断
并发
Pipeline
证书
Prometheus
Metrics
PromQL
Breaker
定时器
Timer
Timeout
Kafka
Xorm
MySQL
Fasthttp
bytebufferpool
任务队列
队列
异步队列
GOIM
Pprof
errgroup
consistent-hash
Zinx
网络框架
设计模式
HTTP
Gateway
Queue
Docker
网关
Statefulset
NFS
Machinery
Teleport
Zero Trust
Oxy
存储
Confd
热更新
OAuth
SAML
OpenID
Openssl
AES
微服务网关
IM
KMS
安全
数据结构
hashtable
Sort
Asynq
基数树
Radix
Crontab
热重启
系统编程
sarama
Go-Zero
RDP
VNC
协程池
UDP
hashmap
网络编程
自适应技术
环形队列
Ring Buffer
Circular Buffer
InnoDB
timewheel
GroupCache
Jaeger
GOSSIP
CAP
Bash
websocket
事务
GC
TLS
singleflight
闭包
Helm
network
iptables
MITM
HTTPS
Tap
Tun
路由
wireguard
gvisor
Git
NAT
协议栈
Envoy
FRP
DPI
gopacket
Cgroup
Namespace
DNS
eBPF
GoZero
Gost
Clash
Tracee
gopsutil
Linux
HIDS
ELKEID
XDP
TC
Systemd
netlink
Kernel