使用eBPF将网络功能Offload到网卡

安得广厦千万间,大庇天下寒士俱欢颜,风雨不动安如山!

近五六年,经济突飞猛进,互联网行业更是宇宙无敌,又是AI又是区块链的,如火如荼…人们纷纷从国外回到国内,希望过上神仙般的日子,出国瞬间就成了zz不正确的事情…

然而…

大图景展翅,细微之屏风安在?

糟糠裂缝,屎尿连珠,practitioners are mostly docile,安土重迁。

About in system,you all are, and I’m retrograde.

梦里不知身是客,我知我倦客。

本文是一篇随笔。技术可以抚平情绪。


说起Offload,作为搞网络的,首先能想到的就是TCP Checksum的Offload,这算是比较经典的Offload案例了。

它的原理很简单,TCP Checksum Offload将本应该由CPU承担的TCP校验和计算这件事交给网卡来完成,带来的收益是CPU的发包路径更短,并且和网卡形成两极流水线,总的吞吐率增加。

落实到实现,无非就两类:

  1. 网卡芯片上雕刻一个“校验码计算电路”。
  2. 网卡芯片上安装一个小处理器。

对于上述第一类实现,直接雕刻一个电路到芯片上,这种实现非常直接,类似的还可以offload路由查找,TCP分段等等,我照猫画虎写过以下三篇文章:
https://blog.csdn.net/dog250/article/details/47194247
https://blog.csdn.net/dog250/article/details/47055121
https://blog.csdn.net/dog250/article/details/46947959

但是问题是,每Offload一个功能,就要设计一个专门的电路,片上空间就那么大,总的电路规模存在极限,怎么办?

学习CPU的做法啊!直接在网卡芯片上部署一个 “通用的小型CPU(或者叫网络协处理器)” ,这样你就可以将网卡芯片看成是一个和主机CPU并列等同的设备了,并且:

  • 它是可编程的。

以往,数据包到达网卡后,网卡随即中断CPU,告诉CPU,“有数据到了,赶紧来处理”,这样CPU不得不停下手头的工作,赶紧过来处理到达的数据包。充其量,考虑到CPU的响应速度,网卡会自带一个不大的缓存队列,但如果CPU反应太慢,数据包很快就会溢出被丢弃。

现在,网卡部署了网络协处理器后,数据包到达后,网卡的协处理器可以先对该数据包进行一些预处理,然后根据处理结果再考虑是不是要把数据包接力给主机CPU,即根据预处理结果判断是否发送中断给CPU。这种自带处理功能的网卡相比仅仅携带一个小缓存的网卡确实太智能了,所以一般被叫做智能网卡,不得了!

这些预处理包括但不限于:

  • 判断数据包是否应该被丢弃或者被审计,这是防火墙的功能。
  • 判断数据包是否可以直接被转发到另一个网卡,这是路由器/交换机的功能。
  • 修改数据包内容。

那么,最后一个问题,如何对协处理器编程,让其可以运行实现上述的预处理功能。

按照以往的观点,我们必须一些专业的手段对专业的网卡可编程电路进行编程,从而实现一定的功能卸载,比如FPGA。这种方式很难通用化,因为FPGA存在学习成本,且需要专门的工具,不是每个程序员都能马上部署上线的。

按照现在的观点,如果把网卡芯片上的协处理器看成和CPU一样的玩意儿的话,CPU可以用C编程,网卡芯片自然也可以。

这个为网卡编程的C程序便是eBPF的C程序。网卡芯片和CPU均可以看到同样的C代码,不同的是,同样的C代码会被JIT(Just In Time)实时编译成对应CPU或网卡的不同指令供它们执行。

当加载通用的、CPU 无关的字节码之后,内核会接管,验证它的合法性,然后将它编译成 CPU 相关的代码。

这意味着eBPF的编译运行机制需要需要以下的组成部分:

  1. 同样的C语言(以后还会有其它语言)语法,这便于任何C程序员均可以编写eBPF程序。
  2. 一个eBPF编译器前端,将eBPF的C代码编译成一个特定的BPF体系结构的字节码。
  3. 一个软件虚拟机,被部署在需要执行eBPF字节码的位置,执行eBPF指令。
  4. 一个eBPF编译器后端,将eBPF字节码编译成特定体系结构的汇编指令,直接在机器上运行。

如果我们不需要让机器直接运行eBPF程序,那么以上的第4部分就不需要,直接让eBPF虚拟机解释执行它即可,类似传统的tcpdump抓包那样。

然而考虑到性能问题,直接将eBPF编译成机器码,就可以摆脱对eBPF虚拟机的依赖,直接由处理器来运行了。

下面是我针对上面的分析自己讲给自己时画的图:

在这里插入图片描述

事实上,LLVM就实现以上这么一个架构,在设计上,LLVM要比gcc先进很多,只是gcc实在是应用太广泛了,还是希望gcc早日可以支持eBPF,进而支持其offload到特定网卡。以下这个图来自Netronome:
在这里插入图片描述

回到智能网卡,可以针对智能网卡上携带的网络协处理器单独实现一个后端,将中间的eBPF字节码编译成该协处理器体系结构的指令码,载入网卡ram,岂不是就可以让网卡执行我们编写的eBPF程序了吗?

幸运的是,这件事已经是现实了,Netronome将其变成了现实,以下是智能网卡的全貌,图示演示了eBPF程序加载和运行的位置:
在这里插入图片描述
其实你就将它看成一个通用的CPU就好了,CPU如何从Ram里载入程序并运行,网卡就如何从网卡Ram里载入eBPF程序并运行。

以下是数据流:
在这里插入图片描述

在理解了以上说的原理之后,下面我们看看如何来做,首先,先看第一篇:
https://www.netronome.com/m/documents/UG_Getting_Started_with_eBPF_Offload.pdf

该文档step by step演示了howto,然而如果你想总览后观看细节的话,open nfp就是绝对避不开的了:
https://open-nfp.org/dataplanes-ebpf/
https://open-nfp.org/the-classroom/【包含非常多的视频和ppt】

下面的PDF和PPT一定要看:
https://open-nfp.org/media/documents/BPF_webinar_AUzgDY4.pdf
https://open-nfp.org/media/documents/Open_NFP_P4_EBPF_Linux_TC_Offload_FINAL_5JHLETS.pdf
https://open-nfp.org/media/documents/BPF_HW_offload_deep_dive.pdf
https://www.netronome.com/m/documents/eBPF_HW_OFFLOAD_HNiMne8_2_.pdf

如果想试一下,如何做呢?

我前面写了两个极其简单的例子,一个例子用eBPF实现了一个网桥的转发路径,另一个例子用eBPF实现了一个路由器的转发路径:
https://blog.csdn.net/dog250/article/details/102982948
https://blog.csdn.net/dog250/article/details/103014526

这两篇文章中,加载eBPF程序到网卡的API是这么调用的:

bpf_set_link_xdp_fd(ifindex_list[i], -1, flags)

注意那个flags参数,如果加上 XDP_FLAGS_HW_MODE 这个标志,就可以将eBPF字节码给JIT编译到网卡硬件了,同样,如果你用iproute2,那么你该使用 xdpoffload 这个option。


安得广厦千万间,大庇天下寒士俱欢颜,风雨不动安如山!

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

发布了1545 篇原创文章 · 获赞 4728 · 访问量 1055万+

猜你喜欢

转载自blog.csdn.net/dog250/article/details/103094759
今日推荐