大约PF_RING/Intel 82599/透明VPN一些事

时间:2023-03-08 21:18:29
接近崩溃的边缘,如今,在医院这篇文章地方的想法,小病,我宁愿不吃药瓶。一台笔记本电脑,但无法上网,我不称职。想知道的东西。唯一可用3G,不开的热点。由于没人给我报销流程。这个周末,我只有一天,由于下雨。我有一个晚上。了解PF_RING之后。我拼命地想做个实验。。

事情的起因是这样的。

一共同拥有4个问题

1.关于一个网络加速卡

前些日子,接触到一款网络加速卡,插在PCIe插槽,卡上执行独立的Linux系统,通过PCIe和主机通信。这块卡号称其特点在于处理抓包的性能非常好,抓包?是的,这就是说它适合做DPI或者透明防火墙了。厂商的技术人员亲自为我们演示了一个防火墙的功能,即不能訪问优库站点。其他的都能够。

让我觉得好玩的是,他们的加速卡上有两个万兆光口,一个接一台能够上网的PC,还有一个接出口交换机,PC上除了不能訪问优库。其他的都能够訪问,而两个万兆光口既没有被bridge在一起,又没有IP。这块卡是怎么做到一个口进还有一个口出的,不是网桥,没有IP。这简直就不是一个网络设备。那它是什么?

2.近期公司的一次測评

近期。对公司的设备进行了一次性能測评。结果达标,可是个人觉得还有提升余地,仅仅是測评事件发生在京城。本人没有亲历。仅仅能遗憾,加之測试方并非那种酷爱技术之人。搞的我们的人总是在配合他,结果可想而知,见好就收,他们是绝不会让我们将他们那悍猛的性能測试仪器作为玩具折腾的。
       被測试的机器看起来非常猛。超过100G的内存,超过30核的x86 CPU核心,超过10G的网卡好几块。重得让人不想搬,丑陋的外观让人觉得就像是楚留香打扮成的要饭的,总之。非常猛,非常酷。可是对于软件,就不能说非常猛了。我一向觉得,x86架构加上Linux协议栈的网络性能瓶颈在于串行处理和内核中的锁,协议栈的处理本身就是串行的,因此传统的Linux内核协议栈绝对不可能将上述如此悍猛的硬件发挥到最高性能。这样的组合一般都是提供给虚拟机使用的。假设你真的想搭配这样的硬件,那还是别用Linux内核协议栈了。假设你想在不使用虚拟机且执行WEB服务之类的传统应用的情况下将全部硬件性能发挥到极致,无异于天方夜谭,瓶颈根本就不在硬件,而在协议栈以及应用server。
       有破就有立,这才像话。

3.公司的那个NAS上网行为管理

假设我在公司浏览了一个不太雅的站点,我当然不想让NAS纪录,否则那帮管理员一定能够随时看到,然后背后嘲笑我,实际上管理员都有这样的癖好,毕竟在当代,对于隐私,网管的权力是巨大的。后来我听说这玩意儿是串接在主链路的,作为一个比較懂网络且比較好奇的研发人员。一定想知道这玩意儿是怎么做到二层监控的,假设说是镜像数据,倒是真没啥新意,关键这玩意儿并非旁路设备。确确实实是一个串联的设备,也就是说数据包真的就是全然通过了它的处理逻辑。说实话,假设它不想让你訪问XXYY,而不仅仅是监控,它全然能够作为一个防火墙使用(对于集权的网管来讲。假设是我,我一定会放开全部的訪问,仅仅是监控,这样才干看到很多其他好玩的)。这须要设备具有极强的线速。不是一般的Linux能够做到的,它是怎么做到的,怎么做到如此高性能的转发的,关键是它还是个二层设备。

4.VPN的透明性

这事得从一次耻辱说起。去年我在实施我的VPN时告知客户我们的产品是一个二层设备,于是客户松了一大口气,由于这会省去他大量的维护时间。由于二层设备不须要配置网络參数,其实我也是这么觉得的,然而他错了。我也错了,实施完成我兴高採烈地回家。在后来的一个月甚至几个月内,我被无数次的“网桥模式下的路由问题”骚扰。于是我这个研发再次冒充实施project师向客户解释,说“我们将感兴趣流量拉到了第三层,不感兴趣流量直接二层通过...”这句诗一样的话简直是在打自己的脸,由于这明显属于事后解释,搪塞,为什么一開始不这么说呢。客户假设懂技术,会觉得这明显在扯嘛...
       从密集实施中抽出身来的间隙,我有点走火入魔。一直思考怎样能够“全然透明”实现二层设备(有一次被客户损的,那叫一个爽,说深圳某公司的VPN就是一个盒子,串到链路就可以。什么都不用配置...)。办法当然有,诸如Linux Netfilter queue/packet socket机制,诸如在内核中巧探測数据包的“上一跳”。返回包自己主动封装MAC机制,总之看我2013年5月份后的博客吧,炫技的东西大大的有,可是没有一个实用的。这就是走火入魔!因此。我须要一个正常的标准方案实现纯二层VPN技术。

关于这些问题的解释

以上是我以前提出的几个问题。对于非常多人来讲,这些都不是问题,实际上对于我来讲。这些也不是什么问题,可是对于非常多人来讲。有必要为他们明个理指条路。
       对于我以下的解释,我觉得首先要先实现一个原型,然后才干涉及性能,因此我先说一般原理。再说性能调优。

一进一出的设备与PF_RING

上面的几个问题中提到的设备也好。卡也罢,除了问题2之外,都是一进一出的设备,问题2并非这样的设备,它能够被看作是一台server,并非转发设备。之所以将它也放在问题中。是想通过它来谈一下性能调优的方方面面。

这样的设备特别适合串接在链路上,由于它们的转发原则非常easy。根本就不须要查表。原则就是从一个口进来的数据包一定从还有一个口出去。你能够把这样的设备看作一根昂贵的全双工网线。这样的设备无疑是能够转发数据的,可是在转发之前。却还是能够做些别的事的,比方过滤掉某些流量,比方记录一些流量...有没有什么办法能够让没有这类实际硬件设备的最普通的人一睹这类设备的行为呢?
       有。使用PF_RING能够非常easy构建一个,那么什么是PF_RING。

最好的资料就是其站点介绍 ,可是我还是要简介一下。

所谓的PF_RING实际上是一种数据包抓取/投放机制。它本质上的行为就是从驱动模块直接抓取一个数据包放在一个环行缓冲区中。或者直接将一个构建好的以太帧投放到网卡驱动的发送队列。实际上它提供了一个数据包的直接获取路径,没有它,依照以往常规的协议栈逻辑,一个数据包必须串行地经过协议栈各层的层层协议头剥离,才干暴露出当前层次的数据。

和基于PACKET套接字的libpcap不同的是,PF_RING机制更为灵活:
1.PF_RING採用mmap的方式将网络裸数据放在一个用户态能够直接access的地方,而不是通过socket read/write机制的内存拷贝。
2.PF_RING支持以下2.1到2.3三种方式将裸数据放到mmap到用户态的环形缓冲区以及2.4的DNA方式:
2.1.依照PACKET套接字的方式从netif_receive_skb函数中抓取数据包,这是一种和PACKET套接字兼容的方式,所不同的是数据包不再通过socket IO进入用户态,而是通过mmap;
2.2.直接在NAPI层次将数据包置入到所谓的环形缓冲区。同一时候NAPI Polling到skb对列,对于这两个路径中的第一个而言。这是一种比2.1介绍的方式更加有效的方式,由于降低了数据包在内核路径的处理长度。可是要求网卡支持NAPI以及PF_RING接口(一般而言,NAPI会将数据包Polling到一个skb队列)。
2.3.和2.2相同。仅仅是不再执行NAPI Polling。这就意味着。数据包将不会进入内核。而是直接被mmap到了用户态,这特别适合于用户态的全然处理而不仅仅是网络审计,既然内核不须要处理网络数据了,那么CPU将被节省下来用于用户态的网络处理。这可能会将内核串行的网络处理变为用户态并行的处理。

以下我会有图示。这里仅仅是点到为止。

2.4.这是一种更猛的方式,唤作DNA支持的模式,直接绕过内核协议栈的全部路径,也就是说直接在网卡的芯片中将数据包传输到(DMA的方式)所谓环形缓冲区,内核将看不到不论什么数据包。这样的方式和Intel的万兆猛卡结合将是多么令人激动的事啊;
以上2.1-2.3和2.4的方式,依照PF_RING的站点介绍。有以下图示所看到的差别:

大约PF_RING/Intel 82599/透明VPN一些事

让人多少有点遗憾的是,2.4的方式并非能够任意使用的。依据其License,提供免费下载,可是以二进制形式提供測试版本号的库,假设须要长期使用,须要购买解锁的代码。说来挺可怜的,由于人家也须要钱来继续将研究进行下去。

PF_RING的背后

非常多人都仅仅是觉得PF_RING仅仅是一个高性能的抓包机制,提供本机的数据包镜像分析。实现网络审计,这仅仅是依照传统的思路来解释的。更进一步,PF_RING机制颠覆了网络中间节点解释数据包的方式。

依照传统的观念,中间网络节点仅仅能依照协议栈的层次一层一层地解析数据包,所谓路由器是三层设备。交换机是二层设备。防火墙分为二层防火墙和三层防火墙...使用PF_RING的设备。它能够将数据包直接从网卡的芯片DMA到你机器上的内存。仅此而已,然后你通过一个应用程序而不是内核协议栈来处理数据包,至于说你的应用程序怎么处置数据包。我来列举几个:
1.深度解析数据包,依照各种你能够想到的粒度来解析会话。然后记录审计信息;
2.提供高性能的入侵检測功能。
3.转发数据包。依照路由器的方式。可是不再仅仅通过查询路由表的方式进行IP路由,而是能够通过各种各样的方式,转发表全然由你自定义,比方实现一个通用的SDN流表。
4.依据上面第2点的含义,你能够决定哪些包被丢弃,这就是一个高性能的防火墙。

相比协议栈的串行解决方式。使用PF_RING是一个更加高效的方案,不但高效,并且灵活。假设你拥有多核心的处理器。你甚至能够能够在用户态并行处理数据包的各个层信息,例如以下图所看到的:

大约PF_RING/Intel 82599/透明VPN一些事

PF_RING迎合了现代高端网卡的多队列特性。这是实话,可是即便是不支持多队列的网卡,假设依照现现在的Linux内核协议栈来处理,假设中断特定的CPU(实际上Linux的中断balance实在不敢恭维)。咱就假定用的是一块不支持中断均衡的网卡。一般而言触发的软中断也会在该CPU上执行,你丝毫没有办法,即使你有8核心CPU。你又能怎样。可是对于PF_RING而言。由于能够在一块网卡上相应多个Ring,就能够将不同的流置于不同的Ring,甚至不同的数据包置于不同Ring,然后每个Ring由绑在特定CPU上的应用程序处理,这实际上就是将所谓的多队列上推了一个层次。例如以下图所看到的:

大约PF_RING/Intel 82599/透明VPN一些事

对于非转发设备,比方一个APP Server,也就是说流量到本地终止的情形,未来的架构或许是这个样子,依照下图所看到的:

大约PF_RING/Intel 82599/透明VPN一些事

看上面的图。或许你会问,设备怎么就知道数据包就是发到本地的呢?其实这不是这个设备的职责,你怎么知道我DMA到Ring buffer的是一个以太帧而不是一个纯粹的HTTP数据包呢?总之,没有现形的东西。总让人不可思议。

PF_RING最让人爽的不是它实现了什么。而是它提供了让你实现某种事情的机制。至于你能在PF_RING的框架内实现什么,全然受限于你的想象力,这也是为何非常人人仅仅是觉得PF_RING仅仅能抓包的原因。

传统的误区

和在SDN环境下问“SDN交换机怎样处理IP层的路由”这样的问题一样,我也向问题1里面提到的网络加速卡提供商索要所谓的”用户态的协议栈“,正是由于以往网络协议都是在所谓的协议栈中被处理的,所以即使对网络的处理移到了用户态。我仍然须要有一个用户态的协议栈才安心。网络在用户态被处理也好。在内核态被处理也罢,关键要有一个栈。这样才算是网络的正常处理方式。然而我得到的答复就是:假设你们须要自行开发协议栈,我们会全力配合和支持...天啊。自行开发协议栈,难道我要研读RFC,然后依照强制,建议等规程去写代码?去处理复杂的IP路由,处理TCP状态机?处理TLS...难道没有现成的用户态协议栈?
       实际上。PF_RING的思想不但颠覆了网络中间节点解释数据包的方式,也颠覆了数据包被处理的方式,它使网卡不再是一个网卡,它在协议栈上撕开一个口子,即数据包不一定必须在栈里面被处理(其实,早在PF_RING之前。非常多二层防火墙就是这么做的,可是基于的技术并非什么通用的技术,也没有通用接口),栈本身就是一个串行的东西,一摞子盘子。假设上面的不拿掉,直接抽掉以下的,你能够试试。基于PF_RING实现的设备非常多。比方网络审计系统,比方防火墙,都没有使用什么用户态协议栈,由于它们根本就不须要协议栈。试想。为何须要实现一个栈,是为了分层解释数据包的各种逻辑,是一个解耦合的详细实现,仅仅有在异构网络互操作的时候,分层才是王道,假设说整个数据包你都拿到了,谁能规定你必须依照某种方式去解析去处理呢?你仅仅要在接口层面保持和外界网络一致就可以。比方数据包在从你的设备出去的时候,一定是一个IEEE802.3的帧格式。

厂商没有给我用户态协议栈,实际上它们也没有,我也不想去实现一个什么协议栈,即便有现成的开源协议栈,我也不想去移植,即使这样。也照样能实现一些实用的东西,实际上。忘掉协议栈吧。对于以太网而已,你能保证你能够正确接收和处理一个以太帧(IN接口)。你能保证你发出的数据是一个以太帧(OUT接口)。这就够了。至于你是怎么处理数据的,没有什么标准。

BTW,上面我说的那一大堆关于用户态协议栈的问题以及关于那块网络加速卡的内容,都和PF_RING本身无关,可是思想一样。无论你有没有听说过。世界上都存在一种Tilera-GX架构的超多核心处理板,而Tilera提供的NetLib套件则提供了一套非常便捷的开发接口,能够让你高速开发执行在用户态的关于网络处理逻辑的一切,这个NetLib开发套件能够直接和其底层的Tilera核心完美契合。

Tilera的思想是这样的,其板载的网络处理芯片拿到数据包以后,不是交给内核协议栈(管它是Linux还是别的)。而是放到一个环形缓冲区中,这个缓冲区能够直接被用户态訪问到,到此为止,它基本就是PF_RING DNA的翻版,可是它有一套NetLib。而PF_RING机制与之相应的就是libpfring。然而看看文档和sample就知道,NetLib提供的东西比libpfring很多其他。假设你使用libpfring。你须要有非常好的想象力与构建能力,反之你使用NetLib,非常多东西都是现成的(包含IP路由查找逻辑),可是仍然须要你有一定的构建能力。无论怎样,二者都是相通的。

动手实现一个透明转发设备

以上扯了非常多的理论和感悟,外加一些PF_RING高性能的理由,其实非常多时候我们更关心应该怎么做。也就是说,更关心接口方面的东西。知道了怎么用,加上你信任底层的实现没有问题,外带你对原理深入的理解。基本就无敌了(可是现实中大量的人仅仅满足接口怎么用)。

这个小节我尝试实现一个最简单的透明转发设备。即一根昂贵的网线,最简单这个词意味着它仅仅有转发功能以及一点点的过滤功能。目的在于展示接口的使用。

拓扑结构非常easy,我的iMac关掉WIFI。网线连接我的Macbook,我的Macbook通过WIFI连接路由器。Macbook内置一个Linux虚拟机。加入两块网卡,同为Bridge模式,一块Bridge到WIFI,一块Bridge到以太网,例如以下图所看到的:

大约PF_RING/Intel 82599/透明VPN一些事

假设仅仅有一台电脑,这个拓扑还真不好搭建。别告诉我说使用VMWare的LAN Segment,我非常讨厌那种东西。

以上拓扑的配置例如以下:
iMac-en0:192.168.1.200/24    default gateway:192.168.1.1
Macbook-en0:无IP地址
Linux VM-eth0:up 无IP地址
Linux VM-eth1:up 无IP地址
Macbook-en1:192.168.1.100/24(WIFI分配的。没办法)    default gateway:无
TP-Link LAN IP:192.168.1.1/24
非常清楚了吧。
接下来贴上代码:

#include <stdio.h>
#include <stdlib.h>
#include <pfring.h>
#include <string.h>
#include <getopt.h>
int main(int argc, char *argv[])
{
        pfring *pfring_net1, *pfring_net2;
        unsigned char *dev1 = NULL;
        unsigned char *dev2 = NULL;
        char c;
        struct option opts[] = {
                {.name = "net1", .has_arg = 1, .val = 'i'},
                {.name = "net2", .has_arg = 1, .val = 'o'},
                {NULL}
        };
        while((c = getopt_long(argc, argv, "i:o:", opts, NULL)) != -1) {
                switch(c) {
                case 'i':
                dev1 = strdup(optarg);
                break;
                case 'o':
                dev2 = strdup(optarg);
                break;
                }
        }

        if(dev1 == NULL || dev2 == NULL) {
                goto end;
        }

        pfring_net1 = pfring_open(dev1, 1518, PF_RING_PROMISC);
        pfring_net2 = pfring_open(dev2, 1518, PF_RING_PROMISC);
        if(pfring_net1 == NULL || pfring_net2 == NULL) {
                goto end;
        }
        if (pfring_set_bpf_filter(pfring_net1, "arp or tcp or udp")) {
                goto end;
        }

        if (pfring_set_direction(pfring_net1, rx_only_direction) ||
                pfring_set_direction(pfring_net2, rx_only_direction)) {
                goto end;
        }

        if (pfring_enable_ring(pfring_net1) ||
                pfring_enable_ring(pfring_net2)) {
                goto end;
        }

        while(1) {
                unsigned char *pkt;
                struct pfring_pkthdr ring_hdr;

                if(pfring_recv(pfring_net1, &pkt, 0, &ring_hdr, 0)) {
                        pfring_send(pfring_net2, pkt, ring_hdr.caplen, 1);
                }
                if(pfring_recv(pfring_net2, &pkt, 0, &ring_hdr, 0)) {
                        pfring_send(pfring_net1, pkt, ring_hdr.caplen, 1);
                }
        }
end:
        if (pfring_net1) {
                pfring_close(pfring_net1);
        }
        if (pfring_net2) {
                pfring_close(pfring_net2);
        }
        return 0;
}

将其编译一下:
gcc test.c -o test -lpcap -lpfring -lrt
注意以上的libpcap不是apt-get的那个,而是PF_RING软件包中userland文件夹下的那个libpcap,总之本文不是PF_RING的文档。所以不深入这些细节,另外要感谢的是CSDN资源频道上传《PF_RING用户指南.V5.4.4.pdf》的那位,一冊在手别无所求。关键是其资源分是0分!

编译好的程序执行./test -i eth0 -o eth1,然后在iMac上打开网页吧,全然OK。然后ping一下百度呢?不行!为什么?由于这一句:

pfring_set_bpf_filter(pfring_net1, "arp or tcp or udp")

仅仅同意arp,tcp,udp通过,icmp不在此列。因此不让通过。
       依照上面的代码,你仅仅须要稍作改动,就能把你的机器打造成一个透明设备了,可是要记住,你使用的是PF_RING技术,为何要强调这一点呢?由于就效果来看,使用libpcap/PACKET套接字和Tilera的NetLib相同能做到,可是PF_RING与众不同。和PACKET套接字相比,PF_RING的原理让其性能更高,和NetLib相比,PF_RING更加通过,接口设计得更加好。

要想学习PF_RING,下载最新版本号,解压编译吧,里面内容非常丰富。有定制的Driver。实用户态的库,有内核模块。更要强调的是有超级多的样例,另外每个README都值得一读。

现代千兆/万兆以太网卡-以Intel 82599为例

假设说NetLib是Tilera架构专有的解决方式,那么PF_RING就是x86架构上相应的通用解决方式。

对于Intel 82599万兆卡。提供了非常多新的机制以及对于老机制有了非常多新的扩展,全部新老机制中,最让人激动的莫过于多队列的细化,例如以下图所看到的:

大约PF_RING/Intel 82599/透明VPN一些事

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZG9nMjUw/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" />

那假设说这个多队列和PF_RING相结合。则会变成以下这幅图所表现的:

大约PF_RING/Intel 82599/透明VPN一些事
如此的看这幅图。内核协议栈好像有点多余,确实是。假设我们不用PF_RING机制,Linux内核协议栈怎样来应对相互之间独立的RX Queues呢?假设全部这些Queue由不同的CPU核心处理,那么是否能提升性能呢?答案当然是肯定的,可是,别忘了主角是协议栈,传统的Linux内核协议栈会使用大量的全局链表,比方skb。路由cache。socket,仅仅要操作这些数据结构,就要lock,除了相互排斥的开销之外,内核协议栈本质上是串行处理的,即在处理MAC的时候,无法预处理IP层,或者说在处理IP层的时候。无法预处理TCP,这就导致了Linux内核面对如此的Intel 82599网卡时的无所适从,即便你拥有超过60个CPU核心,在Linux内核看来也是没有什么用武之地。实际上尽管我没有实測过。我觉得Windows也一样的结果,甚至厂商优化过的UNIX也好不到哪去。
       要想将庞大的串行内核协议栈切分成多个以解放lock开销,方法就是切分协议栈本身,对于Linux而言,轻量级的方法就是划分多个Net Namespace。每个ns都有一套网络协议栈数据结构,不同的ns之间网络操作无需相互排斥。

但令人倍感不爽的是,Linux的ns不能分割Intel 82599的Multi RX Queue,唉...对于仅仅有一块82599卡的设备来讲,它仅仅能属于一个ns。这其实也就是堵住了这条路!可是好在Linux内核和Intel驱动能够随便改...重量级的方式就是使用虚拟机。Intel的官方驱动程序全然支持虚拟机。
       这一切都太麻烦了,假设有一个用户态协议栈。直接接在PF_RING的上面,岂不更好,每个CPU核心执行一个也未尝不可。

我怎么又扯到协议栈了啊,之前不是说要忘掉协议栈吗?是的。在一进一出的设备中,协议栈确实没实用。可是在应用server,仅仅要TC/IP还在,TCP的逻辑还是要处理的。比方你就要处理TCP状态机,处理流控,拥控等。而这一切并非说由于Linux的协议栈在内核就一定要在内核被处理,既然数据包依次经过了网卡芯片,多队列逻辑,PF_RING一路无损地到达了用户态,为何不直接进一步让它进入一个协议栈呢?对于应用server,假设你用PF_RING,有一个无锁的用户态协议栈是比較好的。

现代猛卡的软件应对-PF_RING DNA(Derict NIC Access)

网卡芯片,CPU,芯片组,总线的性能都在迅猛提升,那么软件呢?非常遗憾,软件已经显得步履有点踉跄了。然而PF_RING为你做了更进一步的让步。那就是连唯一的一次内存拷贝也省了,即根本不须要将数据包从网卡复制到mmap到用户态的内存,而是在物理层收到包的时候。直接将数据包放到用户指定的地方。详细来讲。就是将网卡上存储器map到了地址空间的某处。虚拟内存真的是个好东西啊。
       装上支持DNA网卡的驱动,加载DNA用户库和NDA应用。网卡将不再是一块网卡,而纯粹变成了从远端延伸到了本机的内存。

基于PF_RING的VPN设备

我能够回答那个关于VPN的问题了,假设使用PF_RING,我会将数据包抓取到用户态的进程。取出其MAC头。IP头备用。加密整个IP数据报,然后用取出的IP头,MAC头又一次封装加密后的数据(传输模式),或者用新的IP头,备用的MAC封装数据(隧道模式)。甚至能够直接加密TCP/UDP的载荷而保留TCP/UDP的头,这样的话。我的VPN设备真的连一个IP地址都不用配置却能实现超级灵活的加解密措施,真的成了一根昂贵的网线。

现在时间2014年6月22日1点11分,阿根廷和伊朗依旧1:1,良好的致命攻击伊朗...从小型的生日有5天。从我的噩梦,在不到到底4天。

版权声明:本文博客原创文章,博客,未经同意,不得转载。