Linux的socket reuseport与eBPF

今天晚上和小小一起学习,上个晚自习。


已经是reuseport骨灰级玩家了,所以简单写。

最开始,Linux协议栈并没有完全兼容BSD的reuseport语义,也就是说,Linux 3.9之前,所谓的reuseport仅仅有socket热备份功能,没有socket负载均衡功能,但是很快在3.9内核之后,Linux协议栈的reuseport功能便完善了:

  1. Active-Backup
  2. Active-Active

最开始的 Active-Active 实现非常Low,自己看代码就知道了,必须遍历所有的socket,计算hash,才能选出工作socket,如果系统的Listener(TCP的,或者UDP的,比如DNS)非常多,那么socket lookup将会成为热点,徒耗CPU。

而后,在Linux 4.6,事情起了变化,改成了取模算法,详见:
https://blog.csdn.net/dog250/article/details/80458669

增加了eBPF的支持,看起来很棒,但是好用吗?不一定。

比如,我想实现一致性哈希,Linux 4.6版本的reuseport调用eBPF很难吧。即便实现一个最简单的一致性哈希,为了简洁,我也首选kpatch这类hotfix方案,而不是去写一个eBPF程序。以下是我的一致性哈希的代码:
https://blog.csdn.net/dog250/article/details/89268404
然而,如何用一种更加优雅的方案去促成这件事呢?

等吧。


终于,在eBPF的大潮下,我们迎来了 BPF_PROG_TYPE_SK_REUSEPORT ,这是又一个新增的eBPF的HOOK点,当reuseport逻辑选择socket的时候,该类型的eBPF程序被调用,最终的效果是:

  • 给定一个数据包skb,该HOOK点的eBPF程序会为该skb选择一个处理它的socket。

先花一两分钟浏览下面的链接:
https://lwn.net/Articles/762101/
https://github.molgen.mpg.de/donald/linux/commit/8217ca653ec601246832d562207bc24bdf652d2f
它们交代了这个eBPF的HOOK点的来龙去脉。

reuseport这个HOOK点的eBPF程序依托两层嵌套的map:
在这里插入图片描述

首先,数据包根据元数据map到一个内层的reuseport map,该内层的map以数据包的内容为key,map到特定的socket。

有点意思,不是吗?

若要写代码,也不难,我这里没时间搞了,小小已经睡着了,我也要睡了,不过还是给出一个未完成的demo:

struct {
	__uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS);
	__uint(max_entries, 1);
	__uint(key_size, sizeof(__u32));
	__uint(value_size, sizeof(__u32));
} outer_map SEC(".maps");

struct {
	__uint(type, BPF_MAP_TYPE_ARRAY);
	__uint(max_entries, NR_RESULTS);
	__type(key, __u32);
	__type(value, __u32);
} result_map SEC(".maps");

SEC("select_by_skb_data")
int _select_by_skb_data(struct sk_reuseport_md *reuse_md)
{
	__u32  index = 0, flags = 0, dummy = 0, key = 0;
	void *data, *data_end;
	void *reuseport_array;
	enum result result;
	struct udphdr *uh;
	int err;

	data = reuse_md->data;
	data_end = reuse_md->data_end;

	if (reuse_md->eth_protocol != bpf_htons(ETH_P_IP))
		return SK_PASS;

	if (reuse_md->ip_protocol != IPPROTO_UDP)
		return SK_PASS;

	uh = data;

	if (uh + 1 > data_end)
		return SK_PASS;

	// 仅仅一个reuseport组而已
	reuseport_array = bpf_map_lookup_elem(&outer_map, &dummy);
	if (!reuseport_array)
		return SK_DROP;
	
	// ... key的获取过程,略!
	// key计算自数据包内容
	err = bpf_sk_select_reuseport(reuse_md, reuseport_array, &key, flags);
	if (err)
		return SK_DROP;

	return SK_PASS;
}

char _license[] SEC("license") = "GPL";

如果想知道更加详细的信息,请自行debug内核源码树的下面的文件:

linux-source-5.3.0/tools/testing/selftests/bpf/progs/test_select_reuseport_kern.c
linux-source-5.3.0/tools/testing/selftests/bpf/test_select_reuseport.c

自诩reuseport,Netfilter/iptables,eBPF/XDP骨灰级玩家,欢迎志同道合者周末一起玩耍,然而也有我不会,不擅长的,比如DPDK,FPGA,Golang,数据库等,等等等等。


浙江温州皮鞋湿,下雨进水不会胖!

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值