使用XDP(eXpress Data Path)防御DDoS攻击

来源:互联网 发布:cv2.calchist python 编辑:程序博客网 时间:2024/06/06 04:47

前段时间研究了一下bpf,我一直期待有个什么编译器可以把顺手写来的C代码编译成bpf字节码,然后作为iptables的一个match注入到内核…抑或直接用于我的n+1模型。当我把这个需求告诉温州皮鞋厂老板的时候,温州老板研究了几天后,告诉我一个叫做bcc(http://iovisor.github.io/bcc/)的东西,这玩意儿可以把C语言写成的代码编译成bpf字节码…这不正是我想要的吗?…

  这个周六是陌生的一天,凌晨3点起床开始总结这篇文章,这注定是一篇比较轻松的文章。

  本文依旧以DDoS防护为背景来说一种新的方案

欲望

你了解了Linux内核协议栈的运作机理后,你是不是一直都有一种在协议栈的某个部分挂HOOK的欲望,不要说你没有。

  人们总是觉得Linux协议栈实现得不够好,特别是性能方面,所以在这种不信任的基调下,人们当然很自信地觉得把数据包从协议栈里拉出来,自己会处理得比内核协议栈要好,但是,真的是这样吗?我来猜测几点背后的原因。

  首先,这可能是因为Linux协议栈是作为内核一个子系统套件存在的,它无法脱离内核作为一个模块存在,这就意味着如果你改了其实现的细节,就必然要重新编译内核并重启系统,别看这么简单的一个操作,对于很多线上系统是吃不消的,这就跟Windows装完软件要重启系统(重启系统仅仅就是为了重新加载注册表,windows设计者是省事了,用户烦死了!)一样烦人,所以,人们自然而然地需要一种动态HOOK的机制,在里面可以实现一些自己的逻辑。

  其次,Linux内核协议栈说实话真的扛不住高并发,大流量,特别是它是在20世纪90年代作为一个通用操作系统实现的,只是后来从Linux社区迸发的一种文化让其逐渐深入各个专业的领域,比如大型服务器,专用网络设备等,这必然存在一个逐步进化的过程。一句话,Linux的协议栈不是为1Gbps/10Gbps/40Gbps这些网络设计的,要想支持它们,你就必须自己做点什么。

新的分层方法

多人会把Linux协议栈的实现按照OSI模型或者TCP/IP模型分成对应的层次,比如什么链路层,IP层,TCP层。其实这根本不对,Linux协议栈实现从链路层通用处理到IP层路由,并没有经过什么显式的关卡一样的门,仅仅支持一些函数调用而已。记住,OSI模型也好,TCP/IP模型也罢,所谓的分层仅仅是逻辑视图上的分层,好在让人们便于理解以及便于界定软件设计的边界和分工,所以可以说,逻辑上分层这些层次之间都是隐式的门,然而在性能攸关的实现领域,显式的门处在完全不同的位置!

  如果谈优化,我们就必须要找到显式的门,找到了,绕过它便是优化!

  所以说,我之前的那些想法,比如在Netfilter的PREROUTING上做更多的事,优化效果并不明显,就是因为Netfilter并不是门,它也只是一些函数调用。

  那么,什么是门?所谓的门,就是那些开销巨大,你必须花点代价才能过去的点。举几个例子,必须用户态到内核态的系统调用,比如套接字处理的自旋锁,比如内存分配,或者说现实中的深圳罗湖口岸,深圳布吉关,梅林关…

  按照以上的说法,我来重新把Linux协议栈来分层,有了这个新的层次,在哪里做优化就显而易见了(红色区域开销巨大,是为”门“):

这里写图片描述

我们看到数据包从接收一直到用户态,主要经历了两个门,其中一个是skb分配,另一个是套接字锁定,在之前那篇《SYNPROXY抵御DDoS攻击的原理和优化》文章中,我采用的方法显然是绕开了套接字锁定,抗DDoS的性能便得到了很大的提升,然而纵观我几乎所有的文章,基本上都是绕此门而优化的。因为这是一种便宜的方案。

绕过更低层的门

在2014年时,接触过一段时间netmap,当时还调研了基于Tilera做网络处理加速,不管怎样,这些都是跟DPDK类似的方案,DPDK应该都听说过,Intel的一个被吵得火热烫手的专用框架。

  既然大家都一样,Intel是老大,自然就没有Tilera什么事了(它们的方案又贵,又晦涩),这就是DPDK被炒火的原因,Intel之类的公司,放个屁都是香的。

  其实,类似DPDK的加速方案原理都非常简单,那就是完全绕开内核实现的协议栈,把数据包直接从网卡拉到用户态,依靠Intel自身处理器的一些专门优化,来高速处理数据包,你可知道在这类方案中,CPU可是专门处理数据包的,什么内核态,用户态,都无关紧要,采用map机制,专门的处理程序可以非常高效地在任意时间读取并处理数据包,想想CPU的处理速度换算成pps是什么概念吧,如果一个CPU什么都不干,专门处理数据包,那将是非常猛的线速处理了。

  DPDK没什么大不了的,就跟当年的EJB一样,全靠厂商推动,依赖的是一揽子方案,并非一个朴素通用的框架。你给DPDK做个手术跑在ARM上试试,就算能跑,很多功能也都是废的。

  总之,在skb还未分配的网卡驱动层面做一些事情是必要的,至于怎么做,做什么,那花样就多了去了。

附:澄清一个单位概念

注意,线速是一个指标单位,表征处理性能而不是传输性能,并不是像在网线上跑一样,如果那样,直接叫光速或者70%光速好了…同样总是引起混淆的单位是年一遇,百年一遇并不是讲一百年遇到一次,而是百(年一遇),数值是百,单位是年一遇。

XDP

释一个名词,XDP的意思是eXpress Data Path。它能做什么呢?很简单,下图说明:

这里写图片描述

其中,最显而易见的是,竟然可以在如此低的层面上把数据包丢弃或者回弹回去,如果面临DDoS攻击,采用这种方式的话,数据包就没有必要上升到Netfilter层面再被丢弃了。说白了,XDP允许数据包在进入Linux协议栈之前就能受到判决。

  别的不管,我只管DDoS防护,现在的问题是XDP靠什么机制知道这个数据包是不是要被丢弃的呢?

  答案太响亮了,竟然是eBPF!

  事实上,这相当于在网卡驱动层面运行了一个eBPF程序,该程序决定数据包何去何从。最简单的想法是,假设1000个IP地址是已知的异常地址,我们将其封装在一个高效的查找结构中,然后将这个结构包括查找过程编译成eBPF字节码并注入到网卡,网卡收到数据包后,运行该eBPF字节码,如果数据包源IP地址被找到,则丢弃!

这里写图片描述

这不就是我那个n+1模型以及iptables的bpf match中需要的效果吗:
《使用iptables的bpf match来优化规则集-HiPAC/ipset/n+1模型之外的方法》
《iptables高性能前端优化-无压力配置1w+条规则》
更加令人兴奋的是,这一切竟然本来就是存在的现成的东西。推荐几个链接:
https://netdevconf.org/2.1/slides/apr6/bertin_Netdev-XDP.pdf
https://netdevconf.org/2.1/papers/Gilberto_Bertin_XDP_in_practice.pdf
https://github.com/netoptimizer/prototype-kernel
https://www.iovisor.org/technology/xdp

以往,我们认为内核是确定的程序,我们能喂给它的只有数据,虽然Linux内核大部分都跑在冯诺依曼架构为主(如今基本都是混合架构)的机器上,但这种认知反而更像是哈佛架构,冯诺依曼机器本来就是程序和数据统一存储的,现在,eBPF程序可以被灌入网卡驱动了,这简直就跟网卡硬件的Firmware一样为网卡注入了新的功能。不管是你认为程序已经数据化了,还是这种方案真的回归了冯诺依曼模型,都无所谓,重要的是,它提升了性能。

  请注意,XDP并没有对数据包做Kernel bypass,它只是提前做了一点预检而已,目前它也只能有三种Action,继续去挂号,直接杀掉,或者打道回府,看来这只是减少了挂号服务生的负担…这与DPDK这种半道黄牛是完全不同的,DPDK完全可能把你拉到一个黑诊所…不过,XDP思路非常清晰,后续的可能性谁也无法预估,说不定真有一天XDP会直接接管路由查找甚至TCP握手处理呢。

  本节的最后,再一次提一下一个熟悉的朋友,那就是Cisco的ACL,我一直都觉得在Cisco的中低端设备上,ACL的匹配就是按照XDP的方式做的,把用户输入的ACL规则编译成eBPF之类的字节码,然后灌入到需要使能的网卡上,我想象不出除了这种方式,还能有什么更高效的方式,也希望Cisco的朋友能有机会告知究竟…

关于DDoS防御

经,我做过一个模块,就是在内核里记录访问本机次数最多的前几个IP,然后把它们列入黑名单,认为它们是攻击者。然而,这是错的。最终并没有揪出攻击者,反而记录访问次数这件事却差点耗尽CPU…

  收敛点说吧,这件事做的并不全错,记录前n个访问最多的IP并阻止它们确实能起到一定的防御效果,但是在做这件事之前,完全是有办法做到更深层过滤的,那就是,把不请自来的数据包的源IP地址全部加入黑名单,这样,nf_conntrack,iptables以及XDP三者联动,将会是一个完美的DDoS防御方案。

  1. nf_conntrack负责识别不请自来的ACK攻击包
  2. 自研的记录模块负责识别Syn flood攻击包
  3. iptables LOG修改后负责将攻击源加入黑名单
  4. XDP负责阻止黑名单里的IP继续访问

怎么做?

抱歉,什么也做不了!

  目前XDP并不是支持所有的网卡,我能接触到的也就是Intel的网卡,截止到4.10内核,XDP也就支持Mellanox(mlx4和mlx5)和QLogic,另外还有cavium的网卡。

  这个局面让我根本无法去动手实践而只能纸上谈兵…不过这好似卸载了我巨大的负担,让我满怀着些许希望度过一个不用编程的周末,不然我可能又要在周末搞什么XDP测试了…但我仍然满怀希望,等待内核升级后在e1000e的驱动里看到XDP的钩子…

原创粉丝点击