0. 数据平面和用户态协议栈

传统基于linux netfilter实现防火墙,虽然方便,但是性能很差。于是pfring/netmap/dpdk等机制,都要bypass掉内核协议栈。

多年来,各大操作系统(Linux/Windows/Freebsd)的网络协议栈一直都在内核中实现。其实网络协议栈只是一个应用,而不是操作系统的范畴,没有人规定网络协议栈一定是要早内核实现的。这些操作系统的开发者们将一切和具体业务无关的东西都塞进了操作系统内核。

其实通信行业的,早就有了数据面,控制面,管理面分离的理念,只是这个理念没有在通用操作系统中推行起来。随着计算机网络技术的发展,通信技术和计算机网络技术逐渐融合。是时候把网络协议栈(数据平面)从操作系统剥离出来了,让操作系统去做控制的事情。

当网络协议栈,从内核态转移到用户态之后。很多之前利用内核协议栈做的功能:比如交换、路由、防火墙、NAT等功能,自然也能转到用户态去做。

DPDK框架本身提供的能力还是比较裸的,要使用DPDK实现防火墙功能,必须重新实现TCP/IP协议栈,这对于任何人来说都不是一件容易的事情。

1. FD.io

FD.io (Fast data – Input/Output) 是许多项目和库的一个集合,基于DPDK并逐渐演化,支持在通用硬件平台上部署灵活和可变的业务。FD.io支持多个平台上(x86、ARM和PowerPC)和部署在不同的环境中(裸机、虚拟机和容器)。

FD.io的一个关键项目是VPP(Vector Packet Processing:矢量报文处理)。VPP是高度模块化的项目,新开发的功能模块很容易被集成进VPP,而不影响VPP底层的代码框架。

2. VPP

VPP其实就是一个用户态协议栈,也就是一个通用的数据平面。

2.1 VPP架构

VPP平台可用于构建任何类型的数据包处理应用程序。它可以用作负载均衡器,防火墙,IDS或主机协议栈的基础架构。

VPP是一个有效且灵活的数据面,它包括一系列按有向图组织的转发图形节点(graph node)和一个软件框架。

VPP从网络IO层读取最大的可用数据包向量。数据包向量称为“矢量“。它利用了数据报文流时间局限性的特点,提升了报文处理性能。

然后,VPP通过“数据包处理”图处理数据包的向量。

1. 输入节点轮询(或中断驱动)接口的接收队列,获取批量报文。
2. 接着把这些报文按照下个节点功能组成一个矢量(vector)或者一帧(frame)。2.1 比如:输入节点收集所有IPv4的报文并把它们传递给ip4-input节点;2.2 输入节点收集所有IPv6的报文并把它们传递给ip6-input节点。2.3 当ip6-input节点处理完当前帧的所有报文后,把报文传递到后续不同的节点。2.4 比如:如果某报文校验失败,就被传送到error-drop节点;2.5 正常报文被传送到ip6-lookup节点。
3. 一帧报文依次通过不同的图形节点,直到它们被interface-output节点发送出去。

VPP采用插件(plugin)架构,插件与直接内嵌于VPP框架中的模块一样被同等对待。

有向图特性和插件架构使它成为一个松耦合、高度一致的软件架构。在有向图中的节点是可替代的。当这个特性和VPP支持动态加载插件节点相结合时,新功能能被快速开发,而不需要新建和编译一个定制的代码版本。

2.2 VPP软件框架

VPP软件框架包含基础设施层VPP INFRA、矢量处理库VLIB、网络层VNET、插件集Plugins。

VPP源码地址:https://github.com/FDio/vpp

2.2.1 VPPINFRA(基础设施)

VPPinfra是基本的c库服务的集合。提供了高性能的数据结构:vectors/Bitmaps/Pools/Hashes以及一些基本库函数。
源码位置: ./src/vppinfra

2.2.2 VLIB(向量处理库)

VLIB提供了向量处理操作,包括:图结点调度、多线程支持、共享内存支持等。
源码位置:./src/{vlib, vlibapi, vlibmemory}

vlib数据包处理应用程序总是定义一组图节点来处理数据包,我们来看看图结点调度相关的代码:
一、vlib_node_registration_t初始化图结点

vlib提供了几种类型的矢量处理图节点,主要用于控制框架的调度行为。
vlib_node_registration_t的类型成员的功能如下:1. VLIB_NODE_TYPE_PRE_INPUT - run before all other node types
2. VLIB_NODE_TYPE_INPUT - run as often as possible, after pre_input nodes
3. VLIB_NODE_TYPE_INTERNAL - only when explicitly made runnable by adding pending frames for processing
4. VLIB_NODE_TYPE_PROCESS - only when explicitly made runnable. “Process” nodes are actually cooperative multi-tasking threads. They must explicitly suspend after a reasonably short period of time.

二、Vlib_main_loop()调度图节点
基本的矢量处理算法非常简单:

/** Input nodes may have added work to the pending vector.* Process pending vector until there is nothing left.* All pending vectors will be processed from input -> output.*/
for (i = 0; i < _vec_len (nm->pending_frames); i++){cpu_time_now = dispatch_pending_node (vm, i, cpu_time_now);/* Reset pending vector for next iteration. */
}

2.2.3 VNET(VPP网络协议栈)

VNET提供矢量化的第2层和3个网络图节点,一个数据包生成器和一个数据包跟踪器。
代码路径: ./src/vnet

一、网络图结点:
比如ethernet-input->ipv4-input,ipv6-input,arp-input-> ipv4-input-no-checksum等。

二、packet tracer
类似linux中的tcpdump

trace add input-node-name count  # start capturing packet traces
trace add dpdk-input 100 # trace 100 packets on a typical x86_64 system running the dpdk plugin
show trace  # display the packet trace

三、VPP数据包生成器
packet-generator

2.2.4 插件

在VPP中,新建一个VPP插件很简单:

一、运行make-plugin.sh自动生成插件

$ cd ./src/plugins
$ ../../extras/emacs/make-plugin.sh
<snip>
Loading /scratch/vpp-docs/extras/emacs/tunnel-c-skel.el (source)...
Loading /scratch/vpp-docs/extras/emacs/tunnel-decap-skel.el (source)...
Loading /scratch/vpp-docs/extras/emacs/tunnel-encap-skel.el (source)...
Loading /scratch/vpp-docs/extras/emacs/tunnel-h-skel.el (source)...
Loading /scratch/vpp-docs/extras/emacs/elog-4-int-skel.el (source)...
Loading /scratch/vpp-docs/extras/emacs/elog-4-int-track-skel.el (source)...
Loading /scratch/vpp-docs/extras/emacs/elog-enum-skel.el (source)...
Loading /scratch/vpp-docs/extras/emacs/elog-one-datum-skel.el (source)...
Plugin name: myplugin
Dispatch type [dual or qs]: dual
(Shell command succeeded with no output)OK...

产生文件如下

$ cd ./myplugin
$ ls
CMakeLists.txt        myplugin.c           myplugin_periodic.c  setup.pg
myplugin_all_api_h.h  myplugin.h           myplugin_test.c
myplugin.api          myplugin_msg_enum.h  node.c

二、重新编译工程

$ cd <top-of-workspace>
$ make rebuild [or rebuild-release]

三、运行VPP,确保插件被加载

$ cd <top-of-workspace>
$ make run
<snip>
load_one_plugin:189: Loaded plugin: myplugin_plugin.so (myplugin description goes here)
<snip>
load_one_vat_plugin:67: Loaded plugin: myplugin_test_plugin.so
<snip>
DBGvpp#

3. vpp安装使用

3.1 VPP安装运行

  1. 安装
deb [trusted=yes] https://nexus.fd.io/content/repositories/fd.io.ubuntu.xenial.main/ ./
# apt-get update
# apt-get install vpp-lib vpp vpp-plugins
  1. 创建startup.conf
unix {cli-listen /run/vpp/cli-vpp1.sock}
api-segment { prefix vpp1 }
plugins { plugin dpdk_plugin.so { disable } }

VPP在用户空间中运行。在生产环境中,通常会使用DPDK来运行它以连接到实际的NIC或使用vhost来连接到VM。
3. 启动VPP

$ sudo /usr/bin/vpp -c startup1.conf
vlib_plugin_early_init:361: plugin path /usr/lib/vpp_plugins:/usr/lib/vpp_plugins
load_one_plugin:189: Loaded plugin: abf_plugin.so (ACL based Forwarding)
load_one_plugin:189: Loaded plugin: acl_plugin.so (Access Control Lists)
load_one_plugin:189: Loaded plugin: avf_plugin.so (Intel Adaptive Virtual Function (AVF) Device Plugin)
  1. 命令vppctl将启动一个VPP Shell,您可以使用它交互地运行VPP命令。
 sudo vppctl -s /run/vpp/cli-vpp1.sock_______    _        _   _____  _____/ __/ _ \  (_)__    | | / / _ \/ _ \_/ _// // / / / _ \   | |/ / ___/ ___//_/ /____(_)_/\___/   |___/_/  /_/vpp# show version
vpp v18.07-release built by root on c469eba2a593 at Mon Jul 30 23:27:03 UTC 2018
vpp#

3.2 网络接口环境搭建

  1. linux veth接口创建
$ sudo ip link add name vpp1out type veth peer name vpp1host
$ sudo ip link set dev vpp1out up
$ sudo ip link set dev vpp1host up
$ sudo ip addr add 10.10.1.1/24 dev vpp1host
  1. VPP连接到VPP1out
vpp# create host-interface name vpp1out
vpp# set int state host-vpp1out up
vpp# set int ip address host-vpp1out 10.10.1.2/24

3.3 VPP 路由

  1. 我们现在启动第二个VPP实例
$ sudo /usr/bin/vpp -c startup2.conf
....
$ sudo vppctl -s /run/vpp/cli-vpp2.sock_______    _        _   _____  _____/ __/ _ \  (_)__    | | / / _ \/ _ \_/ _// // / / / _ \   | |/ / ___/ ___//_/ /____(_)_/\___/   |___/_/  /_/vpp# show version
vpp v18.07-release built by root on c469eba2a593 at Mon Jul 30 23:27:03 UTC 2018
vpp# quit
  1. 在vpp1上创建一个memif
$ sudo vppctl -s /run/vpp/cli-vpp1.sock
vpp# create interface memif id 0 master
  1. 在vpp2上创建一个memif
vpp# create interface memif id 0 slave

让10.10.1.1和10.10.2.2之间通过路由连通
4. 添加主机路由

$ sudo ip route add 10.10.2.0/24 via 10.10.1.2
$ ip route
default via 10.0.2.2 dev enp0s3
10.0.2.0/24 dev enp0s3  proto kernel  scope link  src 10.0.2.15
10.10.1.0/24 dev vpp1host  proto kernel  scope link  src 10.10.1.1
10.10.2.0/24 via 10.10.1.2 dev vpp1host
  1. vpp2添加路由
$ sudo vppctl -s /run/vpp/cli-vpp2.sockvpp# ip route add 10.10.1.0/24  via 10.10.2.1

3.4 交换

  1. 清除之前的配置
$ ps -ef | grep vpp | awk '{print $2}'| xargs sudo kill
$ sudo ip link del dev vpp1host
$ # do the next command if you are cleaning up from this example
$ sudo ip link del dev vpp1vpp2
  1. 启动VPP1和VPP2
  2. VPP1连host
Create a veth with one end named vpp1host and the other named vpp1out.
Connect vpp1out to vpp1
Add ip address 10.10.1.1/24 on vpp1host
  1. VPP1连VPP2
Create a veth with one end named vpp1vpp2 and the other named vpp2vpp1.
Connect vpp1vpp2 to vpp1.
Connect vpp2vpp1 to vpp2.
  1. 在VPP1上配置桥
vpp# show bridge-domainID   Index   Learning   U-Forwrd   UU-Flood   Flooding   ARP-Term     BVI-Intf0      0        off        off        off        off        off        local0
vpp# set int l2 bridge host-vpp1out 0
vpp# set int l2 bridge host-vpp1vpp2 0
vpp# show bridge-domain 0 detail
show bridge-domain: No operations on the default bridge domain are supported
vpp# set int l2 bridge host-vpp1out 1
vpp# set int l2 bridge host-vpp1vpp2  1
vpp# show bridge-domain 1 detail
BD-ID   Index   BSN  Age(min)  Learning  U-Forwrd  UU-Flood  Flooding  ARP-Term  BVI-Intf
1       1      0     off        on        on        on        on       off       N/AInterface           If-idx ISN  SHG  BVI  TxFlood        VLAN-Tag-Rewritehost-vpp1out            1     1    0    -      *                 nonehost-vpp1vpp2           2     1    0    -      *                 none
  1. 在VPP2上配置loopback和桥
vpp# create loopback interface
loop0
vpp# set int l2 bridge loop0 1 bvi
vpp# set int l2 bridge host-vpp2vpp1  1
  1. 检查交换表
10.10.1.1ping10.10.2.2

VPP1

vpp# show l2fib verbose
Mac Address     BD Idx           Interface           Index  static  filter  bvi   Mac Age (min)
de:ad:00:00:00:00    1            host-vpp1vpp2           2       0       0     0      disabled
c2:f6:88:31:7b:8e    1            host-vpp1out            1       0       0     0      disabled
2 l2fib entries

VPP2

vpp# show l2fib verbose
Mac Address     BD Idx           Interface           Index  static  filter  bvi   Mac Age (min)
de:ad:00:00:00:00    1                loop0               2       1       0     1      disabled
c2:f6:88:31:7b:8e    1            host-vpp2vpp1           1       0       0     0      disabled
2 l2fib entries

4. 基于DPDK+VPP开发高性能防火墙

4.1 ACL vs Netfilter

我们知道VPP是思科开源的,思科一般通过ACL实现防火墙功能。

在思科ACL中,数据包的通过路径如下:

相比Linux的Netfilter,数据包的通过路径如下:

  • Cisco的acl配置很灵活,甚至“配置到接口”,“指明方向”这一类信息都是外部的,十分符合UNIX哲学的KISS原则,但是在具体的配置上对工程师的要求更高一些,他们不仅仅要考虑匹配项等信息,而且还要考虑接口的规划。

  • Netfilter的设计更加集成化,它将接口和方向都统一地集成在了“匹配项”中,工程师只需要知道ip信息或者传输层信息就可以配置了,如果他们不关心接口,甚至不需要指明接口信息,实际上在iptables中,不使用-i和-o选项的有很多。

4.1 VPP ACL插件

Access Control Lists (ACLs) with FD.io VPP
基于VPP有很多方法实现ACL,其中 VPP ACL Plugin是其中一个。

  • 支持针对MAC、IP、UDP PORT、TCP PORT/flag、ICMP Message的ACL
  • 支持在IP flow classicaication之前做input ACL
  • 支持在interface output之前做ACL
  • 支持有状态的ACL:Permit+Reflelct动作、使用流机制(流匹配更快)、使用更多的内存
  • 支持无状态的ACL: pERMIT+Drop动作、无流机制(每个报文都匹配ACL)、内存使用少

有状态配置:

$ sudo vppctl ip_add_del_route 20.20.20.0/24 via 1.1.1.2  sw_if_index 1 resolve-attempts 10 count 1
$ sudo vppctl acl_add_replace  ipv4 permit src 30.30.30.1/32 dst 40.40.40.1/32 sport 1000 dport 1000, ipv4 permit+reflect src 10.10.10.0/24, ipv4 permit+reflect src 20.20.20.0/24
$ sudo vppctl acl_interface_set_acl_list sw_if_index 2 input 0
$ sudo vppctl acl_interface_set_acl_list sw_if_index 1 input 0

无状态配置:

$ sudo vppctl ip_add_del_route 20.20.20.0/24 via 1.1.1.2  sw_if_index 1 resolve-attempts 10 count 1
$ sudo vppctl acl_add_replace  ipv4 permit src 30.30.30.1/32 dst 40.40.40.1/32 sport 1000 dport 1000, ipv4 permit src 10.10.10.0/24, ipv4 permit src 20.20.20.0/24
$ sudo vppctl acl_interface_set_acl_list sw_if_index 2 input 0
$ sudo vppctl acl_interface_set_acl_list sw_if_index 1 input 0

4.2 VPP COP

COP 在FIB 2.0实现中重用了FD.io VPP。从本质上说,在FIB中成功查找表明该数据包已被列入白名单并可以转发。

  • cop-input:确定帧是IPv4还是IPv6,并转发到ipN-copwhitelist图形节点。
  • ipN-copwhitelist:使用ip4_fib_ [mtrie,lookup]函数来确认数据包的ip与白名单fib中的路由匹配。
  • 匹配:如果匹配,则将其发送到下一个白名单或ip层。
  • 不匹配:如果不匹配,则将其发送到错误丢弃。
$ sudo vppctl ip_add_del_route 10.10.10.0/24 via 1.1.1.1  sw_if_index 2 resolve-attempts 10 count 1
$ sudo vppctl ip_table_add_del table 1
$ sudo vppctl ip_add_del_route 20.20.20.0/24  vrf 1  resolve-attempts 10 count 1    local
$ sudo vppctl cop_whitelist_enable_disable sw_if_index 1 ip4 fib-id 1
$ sudo vppctl cop_interface_enable_disable sw_if_index 1

4.3 tsnr一款基于vpp和dpdk的防火墙

tsnr是netgate的一款高性能防火墙产品。
是netgate对之前freebsd的pfsense防火墙的重构,参考:https://www.netgate.com/blog/further-a-roadmap-for-pfsense.html

数据平面采用dpdk+vpp,控制平面采用strongswan+FRR。

参考

VPP如何运作
解密FD.io——处理数据包的开源项目
FD.io——助你创新更高效、更灵活的报文处理方案
VPP英文文档
什么是FD.IO/VPP?
Linux的Netfilter框架深度思考-对比Cisco的ACL
渐进式VPP教程
FD.io通用数据平面
VPP/SecurityGroups

基于DPDK+VPP实现高性能防火墙相关推荐

  1. 一个高速lvs-dr替代系统设计 -- 基于dpdk的高性能负载均衡器

    # LVS DR 原理 LVS-DR不同于普通的haproxy代理机制,它在网络中的作用层级更加底层.haproxy一般代理应用层的应用数据,所有的数据都会通过haproxy收发,导致了haproxy ...

  2. 20个基于DPDK开源项目,建议收藏

    一.ANS – DPDK 原生加速网络堆栈 ANS(加速网络堆栈)是DPDK本地TCP/IP堆栈,也参考FreeBSD实现.ANS提供了一个与Intel DPDK一起使用的用户空间TCP/IP堆栈. ...

  3. 《深入浅出DPDK》读书笔记(十六):DPDK应用篇(基于DPDK的存储软件优化:SPDK)

    Table of Contents 基于DPDK的存储软件优化 180.基于以太网的存储系统 181.以太网存储系统的优化 SPDK介绍 182.SPDK介绍 (1)兼容性 (2)性能 183.用户态 ...

  4. 基于DPDK的Open vSwitch概述

    作者简介 Robin Giller   英特尔网络平台集团的项目经理 本文是关于基于DPDK(数据面开发套件)的Open vSwitch[1](OvS-DPDK)的概述,这是一种高性能,开源的虚拟交换 ...

  5. Go-Proxy-Checker,一款基于Go编写的高性能代理服务器验证工具

    简介 Go-Proxy-Checker是一款基于Go编写的高性能HTTP/HTTPS代理服务器验证工具 能够快速的验证你提供的代理列表中有哪些代理可用(是否高匿.是否支持HTTPS),仅需要简单的一条 ...

  6. 悟空活动中台 - 基于 WebP 的图片高性能加载方案

    本文首发于 vivo互联网技术 微信公众号  链接: https://mp.weixin.qq.com/s/rSpWorfNTajtqq_pd7H-nw 作者:悟空中台研发团队 一.背景 移动端网页的 ...

  7. Lontium 的 LT8619C 是一款基于 ClearEdge 技术的高性能 HDMI/双模 DP 接收器芯片

    1. 描述 Lontium 的 LT8619C 是一款基于 ClearEdge 技术的高性能 HDMI/双模 DP 接收器芯片,符合 HDMI 1.4 规范.TTL输出可支持RGB.BT656.BT ...

  8. KU060板卡设计资料原理图第636篇:基于FMC的KU060高性能 PCIe 载板

    基于FMC的KU060高性能 PCIe 载板 一.板卡概述 板卡主控芯片采用Xilinx 公司的 Kintex UltraScale系列FPGA XCKU060-2FFVA1156.板载 2 组 64 ...

  9. websocket系列:基于netty-websocket-spring-boot-starter轻松实现高性能websocket

    前言 本着死磕websocket的精神继续鼓捣websocket的实现方式.本文通过介绍如何基于netty-websocket-spring-boot-starter轻松实现高性能websocket. ...

最新文章

  1. 将二叉查找树转化为链表的代码实现
  2. 每日一练:Python代码绘制航海王草帽路飞,打饭阿姨也能跟着学会的Turtle海龟绘图系列
  3. Linux开发常见问题:GCC:链接器输入文件未使用,因为链接尚未完成
  4. error:1407742E:SSL routines:SSL23_GET_SERVER_HELLO:tlsv1 alert protocol version
  5. python使用缩进来体现-python使用缩进来体现代码之间的逻辑关系吗?
  6. 解决ubuntu下arduino IDE的Serial Port无法选择问题
  7. 虚拟软驱_文伟_新浪博客
  8. 流程图软件Visio的使用笔记
  9. app支付宝验证登录
  10. Robotcup2D学习记录四
  11. Thingworx- 创建一个网络
  12. python程序设计机械工业出版社课后答案-Python 3程序设计基础
  13. mysql 存储过程
  14. 20个关于可视化图表设计的技巧
  15. win server服务器 关闭危险端口 135,137,138,139,445的方法
  16. String类型转json格式
  17. BGP------BGP工作原理、BGP属性及选路原则
  18. K-均值聚类算法(K-Means)
  19. [Offer收割]编程练习赛2 hihocoder 1272 买零食 (DFS 或 dp 水题)
  20. MySQL8.0安装教程,在Linux环境安装MySQL8.0教程,最新教程 超详细

热门文章

  1. python中的switch语句_python技巧 switch case语句
  2. iphonex如何关机_iPhoneX怎么关机 iPhoneX如何强制重启【详细步骤】
  3. java三元组的快速转置_矩阵压缩——三元组以及矩阵快速转置程序实现
  4. Grafana WebHook 报警通知踩坑系列,支持报警截图发钉钉卡片报警!!!
  5. linux tcp cork,Socket选项系列之TCP_CORK(转)
  6. python标准数据库接口是_Python 数据库接口模块_python 数据库模块_python 数据库接口...
  7. Python网络爬虫使用代理proxy
  8. nor flash和nand flash
  9. 平安性格测试题及答案_性格趣味小测试题 有趣的心理测试题大全及答案
  10. Linux zip加密压缩