VPP编译命令与案例

一、编译安装

1. 克隆源码
复制代码
git clone -b stable/1801 https://github.com/FDio/vpp.git
cd vpp
2. 执行编译脚本
复制代码
./extras/vagrant/build.sh && make

这个脚本会自动处理依赖、配置编译环境,然后开始编译。编译完成后会在 build-root 目录生成 .deb 安装包。

3. 进入编译输出目录
复制代码
cd build-root
4. 安装生成的 .deb

按以下顺序执行安装命令:

复制代码
dpkg -i vpp-lib_18.01.2-1~g9b554f3_amd64.deb
dpkg -i vpp_18.01.2-1~g9b554f3_amd64.deb
dpkg -i vpp-dev_18.01.2-1~g9b554f3_amd64.deb
dpkg -i vpp-plugins_18.01.2-1~g9b554f3_amd64.deb
5. 验证与配置

安装完成后,VPP 的主配置文件会生成在 /etc/vpp/startup.conf,你可以根据需要修改这个文件来配置接口、IP 等参数。

二、启动与命令

1. VPP 服务启动 / 停止 / 状态管理(系统层面)

这些命令用于控制 VPP 服务的启停,需要 root 权限执行:

功能 命令 说明
启动 VPP 服务 systemctl start vpp 常规启动(推荐)
设置开机自启 systemctl enable vpp 服务器重启后自动启动 VPP
停止 VPP 服务 systemctl stop vpp 优雅停止服务
重启 VPP 服务 systemctl restart vpp 配置修改后生效常用
查看 VPP 运行状态 systemctl status vpp 查看是否运行、报错信息等
手动前台启动(调试) vpp -c /etc/vpp/startup.conf 直接启动,日志输出到终端,适合调试
手动后台启动 vpp -c /etc/vpp/startup.conf -d 后台启动,进程脱离终端

示例:查看 VPP 状态

复制代码
systemctl status vpp
# 正常输出会显示 "active (running)"

2. VPP 交互命令行(CLI)核心操作

VPP 提供专属的交互命令行,是日常操作的核心入口:

2.1 进入 VPP 交互终端
复制代码
vppctl  # 最简方式(推荐)
# 或
vpp -c /etc/vpp/startup.conf  # 启动并直接进入CLI

进入后会看到提示符 vpp#,所有操作都在这个提示符下执行。

2.2 基础信息查看(高频,基于 help 输出)
功能 VPP CLI 命令 说明
查看所有接口 show interface 列出所有网口、状态、MAC、MTU 等
查看接口详细信息 show interface <接口名> 例:show interface GigabitEthernet0/0/0
查看 IP 地址配置 show ip address 列出所有接口的 IP 配置(IPv4/IPv6 通用)
查看路由表 show ip route 查看静态 / 动态路由
查看 VPP 版本 show version 确认安装的 VPP 版本
查看运行状态 / 统计 show run 查看 VPP 运行时配置和状态
查看数据包统计 show interface counters 查看各接口收发包、丢包、错误数
查看内存使用 show memory 查看 VPP 内存分配情况(含 DPDK 相关)
查看 DPDK 端口 show dpdk ports 查看 DPDK 管理的网卡(核心关联)
查看错误日志 show errors 查看 VPP 运行中的错误信息
查看命令历史 history 显示当前 CLI 会话的命令执行历史
2.3 接口配置(核心操作,基于 help 输出)
功能 VPP CLI 命令 说明
启用接口 set interface state <接口名> up 例:set interface state GigabitEthernet0/0/0 up
禁用接口 set interface state <接口名> down 停用指定接口
配置 IP 地址 set ip address <接口名> <IP / 掩码> 例:set ip address GigabitEthernet0/0/0 192.168.1.1/24
清除接口 IP clear ip address <接口名> <IP / 掩码> 移除指定接口的 IP
配置 MTU set interface mtu <接口名> <数值> 例:set interface mtu GigabitEthernet0/0/0 1500
开启 IP4 转发 set ip forwarding enable 通用语法(替代旧版本专属语法)
关闭 IP4 转发 set ip forwarding disable 关闭 IPv4 转发功能
2.4 路由配置(基于 help 输出)
功能 VPP CLI 命令 说明
添加静态路由 ip route add <目标网段> via <下一跳> <出接口> 例:ip route add 10.0.0.0/8 via 192.168.1.254 GigabitEthernet0/0/0
删除静态路由 ip route del <目标网段> 例:ip route del 10.0.0.0/8
添加默认路由 ip route add 0.0.0.0/0 via <下一跳> <出接口> 例:ip route add 0.0.0.0/0 via 192.168.1.254 GigabitEthernet0/0/0
2.5 调试 / 排障命令(基于 help 输出)
功能 VPP CLI 命令 说明
开启接口抓包 pcap trace add <接口名> rx/tx both 例:pcap trace add GigabitEthernet0/0/0 both(捕获收发包)
查看抓包内容 show pcap trace 查看捕获的数据包
清除抓包缓存 clear pcap trace 清空抓包数据
数据包追踪 trace <相关参数> 启动 / 查看数据包追踪(替代旧版抓包)
测试接口连通性 ping <ip-addr> [repeat <cnt>] 例:ping 192.168.1.254 repeat 5(repeat 指定包数)
进阶 ping 测试 ping ipv4 192.168.1.254 size 1500 interval 1 repeat 10 verbose 指定包大小、间隔、次数、详细输出
2.6 退出 CLI
复制代码
quit  # 或 exit / Ctrl+D(均支持)

3. 常用运维快捷操作(Linux 终端,适配最新版本)

除了 CLI,日常还会用到这些终端命令:

复制代码
# 查看 VPP 进程
ps -ef | grep vpp

# 查看 VPP 日志(核心排障)
tail -f /var/log/vpp/vpp.log

# 重载 VPP 配置(无需重启)
vppctl reload

# 批量执行 CLI 命令(脚本化)
# 将命令写入文件,比如 cmds.txt
echo -e "show interface\nshow ip route" > cmds.txt
# 批量执行
vppctl -f cmds.txt

# 重启 VPP 进程(CLI 内操作)
vpp# restart process

三、代码案例

在vpp/src/plugin目录下创建一个hdrcap目录,里面创建三个文件,这个案例的任务就是打印从dpdk网卡接收的数据的头部:

1.hdrcap.h

cpp 复制代码
#ifndef __HDRCAP_H__
#define __HDRCAP_H__

// 仅引入VPP 18.01核心头文件(按原生插件顺序)
#include <vlib/vlib.h>
#include <vnet/vnet.h>
#include <vnet/ip/ip4_packet.h>
#include <vnet/ethernet/ethernet.h>
#include <vnet/feature/feature.h>
#include <vlib/cli.h>

// 极简全局结构(仅保留vnet_main)
typedef struct {
    vnet_main_t *vnet_main;
} hdrcap_main_t;

// 全局实例声明
extern hdrcap_main_t hdrcap_main;

#endif /* __HDRCAP_H__ */

2.hdrcap.c

cpp 复制代码
#include <vlib/vlib.h>
#include <vnet/plugin/plugin.h>
#include <vnet/interface.h>   // ← 关键:提供 unformat_vnet_sw_interface
#include "hdrcap.h"

hdrcap_main_t hdrcap_main;

static clib_error_t *
hdrcap_enable_command_fn(struct vlib_main_t *vm,
                         unformat_input_t *input,
                         vlib_cli_command_t *cmd)
{
    vnet_main_t *vnm = vnet_get_main();
    u32 sw_if_index = ~0;
    int disable = 0;

    while (unformat_check_input(input) != UNFORMAT_END_OF_INPUT)
    {
        if (unformat(input, "disable"))
        {
            disable = 1;
        }
        else if (unformat(input, "%U", unformat_vnet_sw_interface, vnm, &sw_if_index))
        {
            // OK
        }
        else
        {
            return clib_error_return(0, "参数错误!用法: hdr cap <interface> [disable]");
        }
    }

    if (sw_if_index == ~0)
    {
        return clib_error_return(0, "缺少接口名或索引!");
    }

    vnet_feature_enable_disable("ip4-unicast", "hdrcap", sw_if_index, !disable, 0, 0);
    vnet_feature_enable_disable("ip4-local",   "hdrcap", sw_if_index, !disable, 0, 0);

    vlib_cli_output(vm, "hdrcap插件%s成功(接口索引:%d)", disable ? "禁用" : "启用", sw_if_index);
    return NULL;
}

VLIB_CLI_COMMAND(hdrcap_command, static) = {
    .path = "hdr cap",
    .short_help = "hdr cap <interface> [disable] - 启用/禁用IP4报文捕获",
    .function = hdrcap_enable_command_fn,
};

static clib_error_t *
hdrcap_init(struct vlib_main_t *vm)
{
    hdrcap_main.vnet_main = vnet_get_main();
    vlib_cli_output(vm, "hdrcap插件初始化完成!");
    return NULL;
}

VLIB_INIT_FUNCTION(hdrcap_init);

VNET_FEATURE_INIT(hdrcap, static) = {
    .arc_name = "ip4-unicast",
    .node_name = "hdrcap",
    .runs_before = VNET_FEATURES("ip4-lookup"),
};

VNET_FEATURE_INIT(hdrcap_local, static) = {
    .arc_name = "ip4-local",
    .node_name = "hdrcap",
};

/* *INDENT-OFF* */
VLIB_PLUGIN_REGISTER () = {
    .version = "1.0",
    .description = "Header Capture Plugin",
};
/* *INDENT-ON* */

/*
DBGvpp# set interface state GigabitEthernetb/0/0 up
DBGvpp# set interface ip address GigabitEthernetb/0/0 192.168.3.100/24
DBGvpp# hdr cap GigabitEthernetb/0/0
*/

3.hdrcap_node.c

cpp 复制代码
#include "hdrcap.h"

// Trace格式化函数(极简)
static u8 *
format_hdrcap_trace(u8 *s, va_list *args)
{
    return format(s, "HDRCAP: IP4报文捕获节点");
}

// 节点处理函数(VPP 18.01原生参数类型)
static uword
hdrcap_node_fn(struct vlib_main_t *vm,
               struct vlib_node_runtime_t *node,
               struct vlib_frame_t *frame)
{
    // 帧数据指针
    u32 n_left_from = frame->n_vectors;
    u32 *from = vlib_frame_vector_args(frame);
    u32 *to_next;
    u32 n_left_to_next;

    // 获取下一跳节点(IP4_LOOKUP)
    vlib_get_next_frame(vm, node, node->cached_next_index, to_next, n_left_to_next);

    // 遍历所有报文
    while (n_left_from > 0) {
        // 取单个报文缓冲区
        u32 bi0 = from[0];
        vlib_buffer_t *b0 = vlib_get_buffer(vm, bi0);
        u8 *data = vlib_buffer_get_current(b0);

        // 传递报文到下一跳(不修改报文,仅捕获)
        to_next[0] = bi0;
        from++;
        to_next++;
        n_left_from--;
        n_left_to_next--;

        // 解析以太网头和IP4头(极简格式化,避免复杂函数)
        ethernet_header_t *eth = (ethernet_header_t *)data;
        ip4_header_t *ip4 = (ip4_header_t *)(data + sizeof(ethernet_header_t));
        
        // 打印核心报文信息(VPP 18.01兼容写法)
        vlib_cli_output(vm, "\n=== IP4报文捕获 ===");
        vlib_cli_output(vm, "源MAC: %02x:%02x:%02x:%02x:%02x:%02x",
                        eth->src_address[0], eth->src_address[1], eth->src_address[2],
                        eth->src_address[3], eth->src_address[4], eth->src_address[5]);
        vlib_cli_output(vm, "目的MAC: %02x:%02x:%02x:%02x:%02x:%02x",
                        eth->dst_address[0], eth->dst_address[1], eth->dst_address[2],
                        eth->dst_address[3], eth->dst_address[4], eth->dst_address[5]);
        vlib_cli_output(vm, "源IP: %d.%d.%d.%d",
                        ip4->src_address.as_u8[0], ip4->src_address.as_u8[1],
                        ip4->src_address.as_u8[2], ip4->src_address.as_u8[3]);
        vlib_cli_output(vm, "目的IP: %d.%d.%d.%d",
                        ip4->dst_address.as_u8[0], ip4->dst_address.as_u8[1],
                        ip4->dst_address.as_u8[2], ip4->dst_address.as_u8[3]);
    }

    // 释放下一跳节点
    vlib_put_next_frame(vm, node, node->cached_next_index, n_left_to_next);

    // 返回处理的报文数
    return frame->n_vectors;
}

// 注册节点(VPP 18.01极简写法,无多余字段)
VLIB_REGISTER_NODE(hdrcap_node) = {
    .name = "hdrcap",                // 节点名(必需)
    .function = hdrcap_node_fn,      // 处理函数(必需)
    .vector_size = sizeof(u32),      // 向量大小(固定为u32)
    .format_trace = format_hdrcap_trace, // Trace函数(可选)
    .type = VLIB_NODE_TYPE_INTERNAL, // 节点类型(必需)
    .next_nodes = {                  // 下一跳节点(必需)
        [0] = "ip4-lookup",
    },
};

4.编译

编译的过程需要改三个文件:

先把plugin目录下的sixrd.am复制一份为hdrcap.am

再编辑,包含上面的三个文件:

再在src目录下修改configure.am文件增加:

再修改src/plugin目录下的Makefile.am:

最后编译:

复制代码
# 1. 清理旧的构建文件(可选,确保干净)
make wipe  # 对应你提到的 make wipe

# 2. 重新生成自动构建文件(让 Makefile 修改生效)
autoreconf -if

# 3. 生成最终的 Makefile
./configure

# 4. 编译构建
make build  # 或直接 make

# 5. 运行
make run

5.运行

在make run之前需要 启动大页:

复制代码
# 1. 创建大页内存挂载目录(如果不存在)
mkdir -p /run/vpp/hugepages

# 2. 挂载 2MB 大页内存(临时生效,重启后失效)
mount -t hugetlbfs nodev /run/vpp/hugepages -o pagesize=2M

# 3. 预留 512 个 2MB 大页(匹配 DPDK 要求)
echo 512 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages

启动make run之后,执行下面两个命令设置网卡:

执行命令:

最后ping就可以看到现象:

当然因为上面实例代码没有处理回显,所以ping的终端是100%丢包的。

0voice · GitHub

相关推荐
安科士andxe13 小时前
深入解析|安科士1.25G CWDM SFP光模块核心技术,破解中长距离传输痛点
服务器·网络·5g
YJlio16 小时前
1.7 通过 Sysinternals Live 在线运行工具:不下载也能用的“云端工具箱”
c语言·网络·python·数码相机·ios·django·iphone
CTRA王大大16 小时前
【网络】FRP实战之frpc全套配置 - fnos飞牛os内网穿透(全网最通俗易懂)
网络
小白同学_C16 小时前
Lab4-Lab: traps && MIT6.1810操作系统工程【持续更新】 _
linux·c/c++·操作系统os
今天只学一颗糖16 小时前
1、《深入理解计算机系统》--计算机系统介绍
linux·笔记·学习·系统架构
儒雅的晴天17 小时前
大模型幻觉问题
运维·服务器
testpassportcn17 小时前
AWS DOP-C02 認證完整解析|AWS DevOps Engineer Professional 考試
网络·学习·改行学it
通信大师18 小时前
深度解析PCC策略计费控制:核心网产品与应用价值
运维·服务器·网络·5g
不做无法实现的梦~18 小时前
ros2实现路径规划---nav2部分
linux·stm32·嵌入式硬件·机器人·自动驾驶
Tony Bai19 小时前
告别 Flaky Tests:Go 官方拟引入 testing/nettest,重塑内存网络测试标准
开发语言·网络·后端·golang·php