使用eBPF sk_skb_stream_verdict时数据包永久排队问题求助
eBPF sk_skb_stream_verdict 实现TCP重定向时的数据包排队问题
架构与环境
拓扑结构:
client <--[key=0]--> proxy <--[key=1]--> server
- 服务器:Go编写的TCP回显服务器,收到数据包原样返回
- 代理:Go协程实现的TCP代理,负责客户端与服务器间的数据包转发
- 环境:Linux archlinux 6.1.52,客户端与服务器通过Docker host网络启动
BPF程序代码
//go:build ignore #include <linux/bpf.h> #include <bpf/bpf_helpers.h> #include <bpf/bpf_endian.h> #define SERVER_PORT 8080 #define PROXY_PORT 8081 // client <--[key=0]--> proxy <--[key=1]--> server struct { __uint(type, BPF_MAP_TYPE_SOCKMAP); __uint(max_entries, 32); __type(key, __u32); __type(value, __u32); // socket FD } sockmap SEC(".maps"); SEC("sockops/prog") int sockops_prog(struct bpf_sock_ops *skops) { switch (skops->op) { case BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB: // SYN-ACK if (skops->local_port == PROXY_PORT) { __u32 key = 0; bpf_sock_map_update(skops, &sockmap, &key, BPF_ANY); } break; case BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB: // SYN if (bpf_ntohl(skops->remote_port) == SERVER_PORT) { __u32 key = 1; bpf_sock_map_update(skops, &sockmap, &key, BPF_ANY); } break; } return 0; } SEC("sk_skb/stream_verdict/prog") int sk_skb_stream_verdict_prog(struct __sk_buff *skb) { int res; if (skb->local_port == PROXY_PORT) { // 不工作 // res = bpf_sk_redirect_map(skb, &sockmap, 0, BPF_F_INGRESS); // 正常工作 res = bpf_sk_redirect_map(skb, &sockmap, 1, 0); return res; } if (bpf_ntohl(skb->remote_port) == SERVER_PORT) { // 不工作 // res = bpf_sk_redirect_map(skb, &sockmap, 1, BPF_F_INGRESS); // 正常工作 res = bpf_sk_redirect_map(skb, &sockmap, 0, 0); return res; } return SK_DROP; } char _license[] SEC("license") = "GPL";
问题现象
当调用bpf_sk_redirect_map时使用BPF_F_INGRESS标志,让代理应用处理数据包时出现异常;而使用flag=0直接转发时一切正常。
用nc作为客户端连接代理时:
- 发送第一个数据包"aaaaa",客户端立即收到回显;
- 发送第二个数据包"bbbbb",数据包永久排队;
- 发送第三个数据包"ccccc",客户端立即收到"bbbbb"和"ccccc"的回显。
追踪结果
使用cilium/pwru追踪数据包发现:
- 第一个数据包发送时会调用
sk_psock_verdict_recv函数; - 第二个数据包发送时该函数未被调用,直到第三个数据包发送才触发。
请求帮助
需要排查上述异常的原因,并给出修复建议。
内容的提问来源于stack exchange,提问作者Justin Chen
相关产品推荐
相关产品推荐

