1. VPP安装
- 安装Ubuntu:版本:Ubuntu 20.04.4
- 保证ubuntu可上网(虚拟机网络配置)
- 更新apt,sudo apt-get update -y
- 安装git ,sudo apt install git
- 安装vim, sudo apt install vim
- 下载vpp源码 :git clone http://gerrit.fd.io/r/vpp 很慢很慢(git到的路径为当前路径)
- 安装一些必要的软件:sudo apt install make , sudo apt install make-guile ,sudo apt install openssh-server
- 下载关系依赖:make install-dep , make dpdk-install-dev (下载时没成功,不影响),make install-ext-deps #时间较长
- 编译:make build (也可执行:./extras/vagrant/build.sh)、make pkg-deb
- 自动配置大页:dpkg -i build-root/*.deb
- 安装DPDK
1
2
3
4
5sudo apt install meson
sudo apt install -y python3-pyelftools python-pyelftools
git clone https://gitee.com/yaozuluo/dpdk.git
cd dpdk
meson -Dexamples=all build - 启动VPP,暂且不用配置其他文件,只是看到VPP运行
1
2
3systemctl enable vpp
systemctl start vpp
systemctl status vpp.service - 测试是否安装成功:sudo vppctl
2. 实例
2.1 运行多个VPP
1 | sudo rm -rf /usr/lib/x86_64-linux-gnu/vpp_plugins/dpdk_plugin.so # 删除dpdk插件 |
2.2 创建虚拟网口(主机与VPP之间)
让VPP简单地跑通,使用命令创建网口,开启主机到VPP的通道,一般采用veth技术。
创建虚拟网口连接到vpp内部建立通讯:
1 | ip link add vhost type veth peer name vpp # 创建成对的虚拟网口vhost及vpp |
VPP指令:
1 | create host-interface name vpp # 创建主机接口,用vpp |
在主机用ping 10.0.0.2应该能通了,并且vpp里面通过show int能看到统计数据在变化;
主机通过tcpdump应该能抓到包。
VPP中使用trace跟踪:
1 | trace add af-packet-input 10 |
关闭vpp进程,删除虚拟接口
1 | ps -ef | grep vpp | awk '{print $2}'| xargs sudo kill |
2.3 连接两个VPP实例
MEMIF 是一种性能非常高的直接内存接口类型,可以在 VPP 实例之间用于形成拓扑。它使用控制通道的文件套接字来设置该共享内存。
在两个 vpp 实例之间创建内存接口:
在vpp1和vpp2上各创建一个 memif 接口:
1 | create interface memif id 0 master |
2.4 路由
- 将路由添加到 Linux 主机路由表
- 将路由添加到 vpp 路由表
设置路由:
1 | ip route add 10.10.1.0/24 via 10.10.2.1 # via下一跳IP地址 |
2.5 切换
- 将接口与桥接域关联
- 创建环回接口
- 为桥接域创建 BVI(网桥虚拟接口)
- 检查桥接域
1 | # 显示桥 |
2.6 源NAT
- 滥用网络命名空间以获取乐趣和利润
- 配置抢断地址
- 配置 snat 内部和外部接口
1 | # 创建一个inside网络空间 |
3. 源码
3.1 源码目录
目录名称 | 描述 |
---|---|
build-data | 构建元数据 |
build-root | 构建输出目录 |
doxygen | 文档生成器配置 |
dpdk | DPDK补丁与构建基础设施 |
extras/libmemif | memif的客户端库 |
src/examples | VPP示例代码 |
src/svm | VPP的捆绑插件目录 |
src/tests | 共享虚拟内存分配库(Shared virtual memory) |
src/vat | 独立测试(不属于测试套件) |
src/vlib | VPP应用程序库 |
src/vlibapi | VPP API库 |
src/vlibsocket | VPP Socket I/O |
src/vnet | VPP网络 |
src/vpp | VPP应用程序 |
src/vpp-api | VPP应用程序API绑定 |
src/vppinfra | VPP核心库 |
src/vpp/api | 尚未重新放置在新地方的API绑定 |
test | 单元测试和Python测试工具 |
3.2 源码分类
vpp数据平面分为四个不同的层:
- 基础架构层:包括vppinfra,vlib,svm和二进制api库。源码:/src/{vppinfra, vlib, svm, vlibapi, vlibmemory}
- 通用网络协议栈层:vnet。源码:/src/vnet
- 应用程序shell:vpp。源码:/src/vpp
- 日益丰富的数据平面插件。源码:/src/plugins
3.2.1 Vppinfra:
Vppinfra是一个基本的c-library服务集合,可以构建直接在裸机上运行的独立程序。它提供高性能动态数组,hash表,位图,高精度实时时钟支持,精细的日志记录和数据结构序列化。
Vectors
Vppinfra向量是无处不在的动态调整大小的数组,具有用户定义的“头”。许多vpppinfra数据结构(例如hash,堆,池)是具有各种不同头的向量。
1
2
3
4
5
6
7
8内存布局如下所示:
User header (optional, uword aligned)
Alignment padding (if needed)
Vector length in elements
User's pointer -> Vector element 0
Vector element 1
...
Vector element N-1如上所示,向量API处理指向向量第0个元素的指针。 如果指为空针则说明向量的有效长度为0。
为了避免内存分配器的分配失败,通常在保留内存分配的同时将向量的长度重置为零。通过vec_reset_length(v)宏将向量长度字段设置为零。
通常情况下不存在用户标头。用户头允许在vppinfra向量上构建其他数据结构。用户可以通过vec * _aligned宏指定数据元素的对齐方式。
向量元素可以是任何C语言数据结构类型,例如 (int,double,struct )。许多宏都用_a类型的变体支持向量数据的对齐,而_h类型的变体支持非零长度向量头。_ha类型的变体支持以上两者。
头或对齐相关宏类型的变体使用不一致将导致延迟以及各种失败问题。
Bitmaps
Vppinfra位图是动态的,使用vppinfra向量API来构建。
Pools
Vppinfra池结合了Vectors和Bitmaps,可以快速分配和释放具有独立生命周期的固定大小的数据结构。池非常适合给每个会话分配结构。
Hashes
Vppinfra提供了几种不通类型的哈希。
在涉及数据包分类/会话查找相关的数据平面问题是通常使用/src/vppinfra/bihash_templat.[ch] 有界索引可扩展哈希值。Bihashes是线程安全的。,不需要读取锁。只需要一个简单的自旋锁确保在同一时刻一次只有一个线程写入一个entry。
在涉及需要字符串精确匹配的控制平面代码中通常使用/src/vppinfra/hash.[ch]中的原始vppinfra哈希。
Format
Vppinfra中的Format大致相当于c语言中的printf。
Format的第一个参数是(u8 *)向量,它附加当前格式化操作的结果。
1
2
3
4
5调用示例:
u8 * result;
result = format (0, "junk = %d, ", junk);
result = format (result, "more junk = %d\n", more_junk);NULL指针是完全正确的0长度向量。格式返回(u8 *)向量,而不是C字符串。如果要打印(u8 *)向量,需要使用“%v”格式字符串。
如果想要一个(u8 *)向量同时也是一个正确的C字符串,可以使用以下任一方案:
1
2
3
4
5
6
7
8u8 * format_junk (u8 * s, va_list *va)
{
junk = va_arg (va, u32);
s = format (s, "%s", junk);
return s;
}
result = format (0, "junk = %U, format_junk, "This is some junk");Unformat
Vppinfra的unformat与scanf有点类似,但是更加的通用。
从C-string或(u8 *)向量初始化unformat_input_t,然后通过unformat()进行解析,如下所示:
1
2
3
4
5unformat_input_t input;
unformat_init_string (&input, "<some-C-string>");
/* or */
unformat_init_vector (&input, <u8-vector>);然后循环解析各个元素:
1
2
3
4
5
6
7
8
9
10while (unformat_check_input (&input) != UNFORMAT_END_OF_INPUT)
{
if (unformat (&input, "value1 %d", &value1))
;/* unformat sets value1 */
else if (unformat (&input, "value2 %d", &value2)
;/* unformat sets value2 */
else
return clib_error_return (0, "unknown input '%U'", format_unformat_error,
input);
}Vppinfra errors and warnings
vpp数据平面中的许多函数都具有clib_error_t *类型的返回值。如果返回NULL则clib_error_t *表示“正确,没有错误”。
Clib_warning(
)是一种添加调试的便捷方式;clib警告预置的功能:自动添加行信息以明确定位消息源。 Serialization
Vppinfra序列化功能支持允许程序员轻松地序列化和反序列化复杂的数据结构。底层的原始序列化/反序列化函数使用网络字节序,因此在小端主机上序列化并在大端主机上反序列化不会存在结构问题。
Event-logger, graphical event log viewer
vppinfra事件记录器提供轻量级的精确时间戳(低于100ns)事件记录服务。代码:/src/vppinfra/{elog.c, elog.h}
序列化支持可以轻松保存并合并一组事件日志。
典型的事件定义和日志记录调用如下所示:
1
2
3
4
5
6
7ELOG_TYPE_DECLARE (e) =
{
.format = "get_or_create: %s",
.format_args = "t4",
.n_enum_strings = 2,
.enum_strings = { "old", "new", },
};如下所示:“.format”字段可能包含以下一个或多个实例:
1
2
3
4
5
6
7
8
9
10i1 - 8-bit unsigned integer
i2 - 16-bit unsigned integer
i4 - 32-bit unsigned integer
i8 - 64-bit unsigned integer
f4 - float
f8 - double
s - 以NULL结尾的字符串
sN - N字节字符数组
t1,2,4 - 每一个事件枚举ID
T4 - 事件日志字符串表的偏移量vpp引擎有几个事件日志的调试CLI:
1
2
3
4
5vpp
vpp
# <filename> must not contain '.' or '/' characters
vpp
事件日志默认为128K条entry。 命令行参数“vlib {elog-events
}”配置事件日志的entry大小。
3.2.2 VLIB:
Vlib提供向量处理支持,包括图形节点调度,可靠多播支持,超轻量级协作多任务线程,CLI,.DLL插件支持,物理内存和Linux epoll支持。
Node Graph Initialization
vlib数据包处理应用程序总是定义一组图节点来处理数据包。通过VLIB_REGISTER_NODE宏来构造vlib_node_registration_t数据结构。在运行时,框架将这样的注册集处理成有向图。在运行时向图添加节点很容易,该框架不支持删除节点。
1
2
3
4
5
6
7vlib_node_registration_t的类型成员函数如下:
VLIB_NODE_TYPE_PRE_INPUT:在所有其他节点类型之前运行
VLIB_NODE_TYPE_INPUT:在pre_input节点之后尽可能的经常运行
VLIB_NODE_TYPE_INTERNAL:仅当通过添加待处理帧进行处理而明确地使其可运行时
VLIB_NODE_TYPE_PROCESS:只有在明确地设置为runnable时。“进程”节点实际上是协作的多任务线程。
他们必须在相当短的时间后明确暂停。要准确理解图节点调度程序,请阅读:/src/vlib/main.c:vlib_main_loop
Graph node dispatcher
Vlib_main_loop()调度图节点。