自己动手编译OpenvSwitch-DPDK
一直眼馋三层交换机,而且自己无论使用RouterOS还是OpenWrt配置三层路由进行跨VLAN通信都一直搞不起来,在路由内部每个VLAN都可以访问,然而外部进来的流量就不行。在搜索解决方案的时候无意间发现了OpenvSwitch这个SDN项目,该项目目前主要用在数据中心等地方,完整的方案叫OpenStack,而且囊括进三层之后还有个OpenvNetwork这个项目。经过测试后发现OpenvSwitch完全可以做到我需要的三层功能,而且还便宜不需要其他硬件支持跑在虚拟机上即可。
OpenvSwitch简称OVS,根据数据包不同的转发路径分为普通用户空间转发、内核数据路径转发(kernel datapath)、DPDK驱动转发三种,速度也是由低到高排序的。第一种数据路径的OVS安装较为简单,直接使用系统自带包管理器安装即可,具体参考官方文档。剩下两种里基本都是涉及到内核与驱动的,所以自己编译OVS在所难免。
这里记录下OVS-DPDK的编译流程。我所使用的编译环境是centos-8.5,最小化安装,因为有关内核驱动所以以后的机器也都是这么配置。宿主机是ESXI-6.7。
- update@2022/4/21:
- 基于Ubuntu编译ovs-dpdk,ubuntu部分都是基于vmware workstation 16。
- 基于dpdk-pktgen与dpdk-testpmd发包测试
- bug fix: ovs bridge添加dpdk网卡参数
编译DPDK
DPDK编译其实还是相对较简单的,官方文档跟着做就好了。看RPM repo里有DPDK和DPDK-devel的包,但是DPDK官方没有这些包的编译说明,所以理想的方式包安装因为个人能力问题只好放弃。
环境配置
Centos 8
升级内核版本到5.4。
1 | dnf -y groupinstall "Development Tools" |
Ubuntu 20.04
vmware虚拟机分配4核4G内存。
1 | apt-get install build-essential libnuma-dev python3-pyelftools pkg-config libjansson-dev libpcap-dev libelf-dev libssl-dev |
获取源码
1 | git clone https://dpdk.org/git/dpdk-stable |
dpdk与ovs是有版本匹配需求的,详细的在ovs的faq里有说(Q: What DPDK version does each Open vSwitch release work with?),一般两边都用稳定版最好。
编译、安装
1 | meson build -Denable_kmods=true |
具体说明看官方文档,但是一般情况下默认的就足够了。如果想要一起编译所有example,就在meson build
上加上-Dexamples=all
选项。
测试
centos默认没有这个环境变量,所以pkg-config找不到配置,这里手动添加下:
1 | export PKG_CONFIG_PATH=/usr/local/lib64/pkgconfig |
而Ubuntu安装好了pkg-config就行了。执行pkg-config --modversion libdpdk
查看是否正常链接到:
ok~一切正常,查看下本机网卡:
驱动类型
dpdk支持的驱动大体上有UIO和VFIO两种,主要区别是UIO不支持DMA与中断等底层特性,所以改进而来VFIO。VFIO完全体需要IOMMU的支持,没有IOMMU的话,要使用VFIO就得进行如下配置:
1 | echo 1 > /sys/module/vfio/parameters/enable_unsafe_noiommu_mode |
这种VFIO驱动不能将各个用户空间完全隔离,所以存在安全问题,并且如果开启了IOMMU时这种VFIO驱动是不能启用的,一般推荐IOMMU下的VFIO驱动。
具体可以看看别人的说明,我了解的也不多就不逼逼了:
systemD管理DPDK网卡绑定
目标就是开机自动绑定dpdk驱动,绑定脚本dpdk-init
:
1 |
|
把绑定脚本放在某个目录即可,我这里就和OVS放在一起了,即/usr/local/share/openvswitch/scripts
路径。
dpdk.service
:
参考了apt方式安装的dpdk,用我自己的dpdk-init
替换了官方的(太复杂。。。)1
2
3
4
5
6
7
8
9
10
11
12
13[Unit]
Description=DPDK runtime environment
Documentation=https://dpdk.org/doc/guides/index.html
DefaultDependencies=false
After=network-pre.target local-fs.target
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/local/share/openvswitch/scripts/dpdk-init bind
[Install]
WantedBy=multi-user.target
编译dpdk-pktgen
pktgen是一个流量生成器,有基于linux内核协议栈的pktgen和基于dpdk的pktgen,基于协议栈的可以直接用不再赘述,基于dpdk的需要确保dpdk安装正确。
1 | git clone http://dpdk.org/git/apps/pktgen-dpdk |
如果想将pktgen安装到bin里面去,就到build目录下执行sudo meson install
,否则编译好的二进制文件就在build/app
下。-Dwerror=false
得加,不然有几个warning会过不去。。。
还发现一个百度出的测试工具,没用过就贴一下dperf。
编译OVS
基本跟着官方文档走就可以了。
环境配置
Centos 8
1 | dnf -y install libcap-ng-devel openssl-devel checkpolicy desktop-file-utils python3-devel selinux-policy-devel unbound unbound-devel |
下面两个是文档生成工具,可以不安装
1 | dnf -y python3-sphinx groff |
Ubuntu 20.04
1 | apt-get install libcap-ng-dev openssl unbound libunbound-dev selinux-policy-dev python3-dev libtool checkpolicy |
获取源码
1 | git clone https://github.com/openvswitch/ovs.git |
编译、安装
1 | ./boot.sh |
这个文档插个眼,说可以编译OVS的RPM包,测试不支持DPDK的OVS包可以成功,支持DPDK的因为少了dpdk-devel
这个包会失败。
打包(optional)
debian系参考Debian Packaging for Open vSwitch
安装依赖
1
sudo apt-get install fakeroot graphviz debhelper dh-python dh-autoreconf python3-all python3-sphinx libunwind-dev
检查依赖是否安装完成,在ovs源码根目录运行,无输出即没问题
1
dpkg-checkbuilddeps
没有找到直接支持DPDK的参数,但是可以在
debian/rules
文件里添加1
EXTRA_CONFIGURE_OPTS += --with-dpdk=yes
即给configure传一个参数过去
打包
1
DEB_BUILD_OPTIONS='parallel=4' fakeroot debian/rules binary
清理
1
fakeroot debian/rules clean
生成的deb包与源码目录在同一层级。
测试
测试系统里就有ovs相关命令了,需要对编译结果测试的看文档。
但是此时由于hugepages等设置,DPDK是没有开启的。
systemD管理OVS
默认编译安装的OVS脚本都在/usr/local/share/openvswitch/scripts
目录下,其他目录的话统一替换即可。
对比用apt方式安装的OVS,编译安装的还差ovs-systemd-reload
脚本,把这个放在/usr/local/share/openvswitch/scripts
里。
1 |
|
剩下的4个service直接给出,基本不用修改。
openvswitch-switch.service
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18[Unit]
Description=Open vSwitch
Before=network.target
After=network-pre.target ovsdb-server.service ovs-vswitchd.service
PartOf=network.target
Requires=ovsdb-server.service
Requires=ovs-vswitchd.service
[Service]
Type=oneshot
ExecStart=/bin/true
ExecReload=/usr/local/share/openvswitch/scripts/openvswitch/ovs-systemd-reload
ExecStop=/bin/true
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
Also=ovs-record-hostname.serviceovs-vswitchd.service
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26[Unit]
Description=Open vSwitch Forwarding Unit
After=ovsdb-server.service network-pre.target systemd-udev-settle.service
Before=network.target networking.service
Requires=ovsdb-server.service
ReloadPropagatedFrom=ovsdb-server.service
AssertPathIsReadWrite=/var/run/openvswitch/db.sock
PartOf=openvswitch-switch.service
DefaultDependencies=no
[Service]
LimitNOFILE=1048576
Type=forking
Restart=on-failure
Environment=HOME=/var/run/openvswitch
EnvironmentFile=-/etc/default/openvswitch-switch
ExecStart=/usr/local/share/openvswitch/scripts/ovs-ctl \
--no-ovsdb-server --no-monitor --system-id=random \
--no-record-hostname \
start $OVS_CTL_OPTS
ExecStop=/usr/local/share/openvswitch/scripts/ovs-ctl --no-ovsdb-server stop
ExecReload=/usr/local/share/openvswitch/scripts/ovs-ctl --no-ovsdb-server \
--no-monitor --system-id=random \
--no-record-hostname \
restart $OVS_CTL_OPTS
TimeoutSec=300ovsdb-server.service
这里如果没有dpdk的话,删除dpdk.service
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22[Unit]
Description=Open vSwitch Database Unit
After=syslog.target network-pre.target dpdk.service local-fs.target
Before=network.target networking.service
PartOf=openvswitch-switch.service
DefaultDependencies=no
[Service]
LimitNOFILE=1048576
Type=forking
Restart=on-failure
EnvironmentFile=-/etc/default/openvswitch-switch
ExecStart=/usr/local/share/openvswitch/scripts/ovs-ctl \
--no-ovs-vswitchd --no-monitor --system-id=random \
--no-record-hostname \
start $OVS_CTL_OPTS
ExecStop=/usr/local/share/openvswitch/scripts/ovs-ctl --no-ovs-vswitchd stop
ExecReload=/usr/local/share/openvswitch/scripts/ovs-ctl --no-ovs-vswitchd \
--no-record-hostname \
--no-monitor restart $OVS_CTL_OPTS
RuntimeDirectory=openvswitch
RuntimeDirectoryMode=0755ovs-record-hostname.service
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18[Unit]
Description=Open vSwitch Record Hostname
After=ovsdb-server.service ovs-vswitchd.service network-online.target
Requires=ovsdb-server.service
Requires=ovs-vswitchd.service
Requires=network-online.target
AssertPathIsReadWrite=/var/run/openvswitch/db.sock
[Service]
Type=oneshot
ExecStart=/usr/local/share/openvswitch/scripts/ovs-ctl record-hostname-if-not-set
ExecStop=/bin/true
ExecReload=/usr/local/share/openvswitch/scripts/ovs-ctl record-hostname-if-not-set
TimeoutSec=300
RemainAfterExit=yes
[Install]
RequiredBy=openvswitch-switch.service
把上头四个文件放在/lib/systemd/system/
目录下即可,使能服务
1 | sudo systemctl enable openvswitch-switch.service |
启动
1 | sudo systemctl start openvswitch-switch.service |
平台配置
DPDK需要硬件虚拟化的支持和hugepages内存的支持,且至少需要双核的cpu。
虚拟机虚拟化及IOMMU配置
ESXi
cpu虚拟化种开启硬件辅助,并分配两个及以上核心,内存直接全部预留给虚拟机。网卡选择为vmxnet3。
VMware Workstation
类似ESXi,如图打开。
Workstation不支持直接选择网卡型号,所以修改虚拟机目录下的*.vmx
文件,修改对应网卡类型如下图所示。
linux内核开启虚拟化支持
开启虚拟化支持需要添加两个内核启动参数,即iommu=pt
、intel_iommu=on
,不同系统添加方式不同
修改内核参数后需要重启才能生效。
Centos 8
1 | sudo grubby --update-kernel=ALL --args="iommu=pt" |
Ubuntu 20.04
修改/etc/default/grub
文件,在GRUB_CMDLINE_LINUX
参数里添加intel_iommu=on iommu=pt
这两个,每个之间用空格分隔,最后更新参数并重启。
1 | sudo update-grub && sudo reboot |
开启hugepages
默认2M的hugepage,若有自定义设置自行调整,经过测试无需在/etc/fstab
里手动挂载,只要添加内核启动参数即可:
hugepagesz
: 每个大页的大小,默认2Mhugepages
: 大页的数量
所以总的大小为hugepagesz * hugepages,我这里就是2G的大页。
Centos 8
1 | sudo grubby --update-kernel=ALL --args="transparent_hugepage=never" |
Ubuntu 20.04
类似linux内核开启虚拟化支持->Ubuntu 20.04做法,在GRUB_CMDLINE_LINUX
参数里添加transparent_hugepage=never default_hugepages=2G hugepagesz=2M hugepages=1024
。更新后重启系统。
验证大页内存与IOMMU
Hugepages
1 | grep Huge /proc/meminfo |
IOMMU
1 | dmesg | grep -i dmar |
可以看到有“IOMMU enabled”的信息,这说明此运行时系统打开了IOMMU。
网卡绑定DPDK驱动
1 | modprobe vfio-pci |
测试
单机双网卡回环-pktgen收发
结构如图所示,dpdk-port0与dpdk-port1分别是两个网口,绑定到dpdk驱动。外部使用网线连接到一起,而对于虚拟机,直接连接到同一个交换机/网络。
由于虚拟机只有四个核心,所以选择核心1-3给pktgen使用,将2与3号核心分别绑定到两个dpdk网卡上,-P
参数允许混杂模式,启动pktgen
1 | pktgen -l 1-3 -n 4 -- -P -m "2.0, 3.1" |
测试: 网卡0发出1000个包
1 | set 0 count 1000 |
观察到网卡1收到了1000个包
同样地,网卡1也可以发送数据包让网卡0接收。数据包的mac信息、ip信息也都可以修改
1 | set 0 src mac 00:0c:29:77:d3:89 |
双机双网卡-testpmd转发
使用DPDK自带的testpmd来进行二层转发,测试系统结构如图所示
testpmd依旧使用1-3号核心,直接进入交互模式
1 | dpdk-testpmd -l 1-3 -n 4 -- -i |
show config fwd
用来查看testpmd的转发模式,默认模式为io转发模式,类似于一个二层交换机。可以通过set fwd rxonly
设置为仅接受,或者set fwd io
设置为io转发模式。set promisc all on/off
用于开启/关闭混杂模式。在运行混杂的io转发模式下启动testpmd:
1 | start |
查看下统计信息
1 | show port stats all |
此时因为pktgen还没有发送数据包所以都是0。让pktgen从0号网口发送1000个64k数据包,地址都是默认的。
pktgen的1号网口应该收到1000个数据包,但是我这里只收到994个,丢包估计是因为我的笔记本CPU干到100%的原因。再去testpmd看下信息:
testpmd的0号网口收到了1000个包,1号网口转发出去994个包,数量都对得上~也确实丢了6个包。。更进一步的性能测试就不做了,笔记本不太行而且虚拟机之间走的还是VMware的虚拟网络。
双机双网卡-OVS转发
测试系统结构配置与双机双网卡-testpmd转发基本类似,只是将testpmd换成OVS,流表直接缺省的NORMAL即可。OVS提供了一键启动的脚本,位于/usr/local/share/openvswitch/scripts
目录下,设置初始化dpdk驱动:
1 | export PATH=$PATH:/usr/local/share/openvswitch/scripts |
如果这里没报错就基本稳了,查看下OVS情况:
1 | ovs-vsctl get Open_vSwitch . dpdk_initialized |
完美~
添加网桥:
1 | ovs-vsctl add-br br0 |
需要设置网桥的参数以支持DPDK驱动:
1 | ovs-vsctl set bridge br0 datapath_type=netdev |
- 如果没有这个
datapath_type
的设置,在添加DPDK网卡的时候就会出错,查看/usr/local/var/log/openvswitch/ovs-vswitchd.log
报错日志为:
具体参考文档DPDK Bridges
添加两个DPDK网卡:
1 | ovs-vsctl add-port br0 dpdk-p0 -- set Interface dpdk-p0 type=dpdk options:dpdk-devargs=0000:0b:00.0 |
查看此时的默认流表ovs-ofctl dump-flows br0
,数据包等都是0个。使用pktgrn发送1000个数据包:
基本功能测试完成✅
脚本boy上线
这一顿操作下来还是有点麻烦的,直接上脚本:
(脚本未更新)
PS:写的比较随意
1 |
|