VPP (FD.io) 심화 — 고성능 유저스페이스 패킷 처리

FD.io VPP(Vector Packet Processing) 프레임워크: 벡터 패킷 처리 모델, 그래프 노드 아키텍처, DPDK 통합, 플러그인 시스템, TAP/TUN·vhost-user·AF_PACKET·AF_XDP 커널 인터페이스, L2/L3/ACL/NAT/IPsec/SRv6 기능, CLI/API, 성능 최적화, 활용 사례 종합 가이드.

관련 기술: VPP는 커널 네트워크 스택을 우회(bypass)하여 유저스페이스에서 패킷을 처리하는 프레임워크입니다. 커널의 TAP/TUN(drivers/net/tun.c), vhost(drivers/vhost/), AF_PACKET(net/packet/af_packet.c), AF_XDP(net/xdp/xsk.c), UIO/VFIO(drivers/uio/, drivers/vfio/) 서브시스템과 밀접하게 연관됩니다.

VPP 개요

VPP(Vector Packet Processing)는 Linux Foundation 산하 FD.io(Fast Data Input/Output) 프로젝트의 핵심 구성 요소로, 고성능 유저스페이스 패킷 처리 플랫폼입니다. 원래 Cisco에서 개발한 상용 코드를 2016년에 오픈소스로 공개하였으며, 현재 Linux Foundation이 호스팅하는 커뮤니티 주도 프로젝트입니다.

VPP의 핵심 설계 철학은 벡터 패킷 처리(Vector Packet Processing)입니다. 전통적인 커널 네트워크 스택이 패킷 하나씩 전체 처리 경로를 순회하는 scalar 방식인 반면, VPP는 여러 패킷을 하나의 벡터(배열)로 묶어서 동일한 처리 노드를 일괄 통과시킵니다. 이를 통해 CPU의 명령어 캐시(I-cache) 히트율을 극대화하고, 분기 예측(branch prediction) 효율을 높여 범용 x86 CPU에서도 초당 수천만 패킷(수십 Mpps)을 처리할 수 있습니다.

FD.io / Linux Foundation 역사

연도주요 이벤트
2002~Cisco 내부에서 VPP 엔진 개발 시작 (상용 라우터/스위치용)
2016.02Linux Foundation 산하 FD.io 프로젝트로 오픈소스 공개
2017VPP 17.01 릴리스, DPDK 통합 본격화
2018Kubernetes CNI (Contiv-VPP) 지원, vhost-user 안정화
2020AF_XDP 호스트 인터페이스 지원, rdma 플러그인
2022~5G UPF 유스케이스 확대, SRv6 고도화, VPP 24.x 시리즈

패킷 처리 프레임워크 비교

항목커널 스택DPDK (raw)VPPOvS-DPDK
실행 공간커널유저스페이스유저스페이스유저스페이스
처리 모델Scalar (패킷 단위)Poll-mode (배치)Vector (그래프 노드)Flow-based (Megaflow)
성능 (64B, 단일 코어)~1 Mpps~15 Mpps~12 Mpps~8 Mpps
L2/L3 스택완전 내장직접 구현 필요완전 내장L2 + OpenFlow
확장성커널 모듈라이브러리플러그인 시스템OpenFlow 규칙
커널 통합네이티브UIO/VFIOTAP/vhost/AF_XDPvhost-user
라이선스GPL-2.0BSD/LGPLApache 2.0Apache 2.0

벡터 패킷 처리 모델

Scalar 처리 vs Vector 처리

전통적인 커널 네트워크 스택의 Scalar 처리는 패킷 하나를 수신하면 L2 → L3 → L4 → 소켓 전달까지 전체 경로를 완주한 후 다음 패킷을 처리합니다. 각 단계마다 다른 함수 코드가 I-cache에 로드되므로 캐시 미스가 빈번합니다.

VPP의 Vector 처리는 다수의 패킷(벡터)을 동시에 한 노드에서 처리한 후, 다음 노드로 벡터 전체를 넘깁니다. 동일한 코드가 수백 패킷에 반복 적용되므로 I-cache에 상주하며, 분기 예측이 안정되어 파이프라인 버블이 최소화됩니다.

Scalar 처리 vs Vector 처리 Scalar 처리 (커널) Pkt 1 L2 L3 L4 완료 Pkt 2 L2 L3 ... 순차 반복 I-cache miss ↕ Vector 처리 (VPP) Vector (256 pkts) Pkt 1, Pkt 2, ... ... Pkt N L2 Node 모든 패킷 일괄 L3 Node 모든 패킷 일괄 L4 Node 모든 패킷 일괄 완료 ✓ I-cache hot — 동일 코드 반복 실행 Scalar: 패킷마다 전체 경로 순회 → I-cache 냉각 | Vector: 노드별 일괄 처리 → I-cache 상주 VPP 기본 벡터 크기: VLIB_FRAME_SIZE = 256

캐시 효율성과 성능

VPP의 성능 우위는 CPU 마이크로아키텍처 수준의 최적화에서 비롯됩니다:

/* VPP 노드의 전형적인 dual-loop 패턴 (간략화) */
while (n_left_from >= 4) {
    /* 다음 2개 패킷 프리페치 */
    vlib_buffer_t *p2, *p3;
    p2 = vlib_get_buffer(vm, from[2]);
    p3 = vlib_get_buffer(vm, from[3]);
    CLIB_PREFETCH(p2->data, CLIB_CACHE_LINE_BYTES, LOAD);
    CLIB_PREFETCH(p3->data, CLIB_CACHE_LINE_BYTES, LOAD);

    /* 현재 2개 패킷 처리 */
    vlib_buffer_t *b0, *b1;
    b0 = vlib_get_buffer(vm, from[0]);
    b1 = vlib_get_buffer(vm, from[1]);

    /* 패킷 처리 로직... */
    next0 = process_packet(b0);
    next1 = process_packet(b1);

    vlib_validate_buffer_enqueue_x2(vm, node, next_index,
                                     to_next, n_left_to_next,
                                     bi0, bi1, next0, next1);
    from += 2;
    n_left_from -= 2;
}

VPP 아키텍처

VPP 패킷 처리 그래프 아키텍처 NIC DPDK/AF_XDP dpdk-input INPUT node ethernet-input L2 분류 ip4-input IPv4 처리 ip4-lookup FIB 검색 ip4-rewrite MAC 재작성 NAT44 ACL ip6-input IPv6 처리 l2-input L2 브릿징 l2-fwd MAC 학습/포워딩 interface-output TX queue 범례: INPUT/INTERNAL 노드 L3 처리 노드 기능 플러그인 (ACL, NAT...)

패킷 처리 그래프 (Graph Node Architecture)

VPP의 핵심은 방향성 비순환 그래프(DAG) 기반 패킷 처리 엔진입니다. 각 처리 단계는 노드(Node)로 구현되며, 노드 간 연결은 arc로 표현됩니다. 패킷(벡터)은 노드에서 노드로 arc를 따라 이동하며, 각 노드는 패킷의 다음 목적지 노드를 결정합니다.

/* 그래프 노드 등록 예제 — src/vnet/ethernet/node.c */
VLIB_REGISTER_NODE (ethernet_input_node) = {
    .function = ethernet_input,          /* 노드 처리 함수 */
    .name = "ethernet-input",
    .vector_size = sizeof(u32),          /* 벡터 원소 크기 (버퍼 인덱스) */
    .n_errors = ETHERNET_N_ERROR,
    .error_strings = ethernet_error_strings,
    .n_next_nodes = ETHERNET_INPUT_N_NEXT,
    .next_nodes = {
        [ETHERNET_INPUT_NEXT_IP4_INPUT] = "ip4-input",
        [ETHERNET_INPUT_NEXT_IP6_INPUT] = "ip6-input",
        [ETHERNET_INPUT_NEXT_L2_INPUT]  = "l2-input",
        [ETHERNET_INPUT_NEXT_DROP]      = "error-drop",
    },
};

노드 유형 (INTERNAL, INPUT, PROCESS, PRE_INPUT)

노드 유형설명실행 방식대표 예
VLIB_NODE_TYPE_INTERNAL그래프 중간 처리 노드이전 노드가 벡터를 전달할 때 실행ip4-input, ip4-lookup, l2-fwd
VLIB_NODE_TYPE_INPUT패킷 입력 노드매 메인 루프마다 폴링dpdk-input, af-packet-input
VLIB_NODE_TYPE_PROCESS코루틴 기반 프로세스이벤트/타이머 기반 실행dhcp-client-process, arp-reply
VLIB_NODE_TYPE_PRE_INPUT입력 전 처리INPUT 노드보다 먼저 실행epoll-input (이벤트 폴링)

벡터 크기와 처리 흐름

VPP의 벡터 프레임 크기는 VLIB_FRAME_SIZE로 정의되며, 기본값은 256입니다. 메인 루프(vlib_main_loop)는 다음 순서로 동작합니다:

  1. PRE_INPUT 노드 실행 (epoll 이벤트 수집 등)
  2. INPUT 노드 실행 (dpdk-input이 NIC에서 패킷 수집 → 벡터 생성)
  3. INTERNAL 노드: 보류 중인 프레임이 있는 노드를 순회하며 벡터 처리
  4. PROCESS 노드: 타이머/이벤트 만료된 코루틴 실행
  5. 1번으로 돌아감 (busy-loop 또는 sleep)
성능 팁: VPP의 메인 루프는 기본적으로 busy-polling이며, 패킷이 없을 때도 CPU를 100% 사용합니다. startup.confpoll-sleep-usec 옵션으로 유휴 시 절전 모드를 활성화할 수 있지만, 레이턴시가 증가할 수 있습니다.

DPDK 통합

VPP는 DPDK(Data Plane Development Kit)를 기본 패킷 I/O 백엔드로 사용합니다. DPDK는 커널의 네트워크 드라이버를 우회하여 NIC 하드웨어에 직접 접근하는 Poll Mode Driver(PMD)를 제공합니다.

DPDK 드라이버 바인딩

DPDK PMD를 사용하려면 NIC를 커널 드라이버에서 분리(unbind)하고 UIO 또는 VFIO 드라이버에 바인딩해야 합니다:

# 현재 NIC 드라이버 확인
$ lspci -k -s 0000:03:00.0
03:00.0 Ethernet controller: Intel Corporation 82599ES ...
    Kernel driver in use: ixgbe

# VFIO-PCI 드라이버로 바인딩 (IOMMU 필요)
$ modprobe vfio-pci
$ dpdk-devbind --bind=vfio-pci 0000:03:00.0

# 또는 UIO 드라이버 (IOMMU 불필요, 보안 취약)
$ modprobe uio_pci_generic
$ dpdk-devbind --bind=uio_pci_generic 0000:03:00.0

# 상태 확인
$ dpdk-devbind --status
커널 관점: VFIO(drivers/vfio/)는 IOMMU를 통한 디바이스 격리를 제공하여 DMA 공격을 방지합니다. UIO(drivers/uio/)는 IOMMU 없이 동작하지만, 잘못된 DMA 요청이 임의 메모리를 덮어쓸 수 있어 프로덕션에서는 VFIO를 권장합니다.

Hugepages 설정

DPDK와 VPP는 Hugepages를 사용하여 TLB 미스를 줄이고 메모리 접근 성능을 높입니다. 커널의 hugetlbfs(fs/hugetlbfs/)를 활용합니다:

# 부팅 시 1GB hugepage 할당 (GRUB)
GRUB_CMDLINE_LINUX="default_hugepagesz=1G hugepagesz=1G hugepages=4 iommu=pt intel_iommu=on"

# 런타임 2MB hugepage 할당
$ echo 1024 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages

# hugetlbfs 마운트 (VPP가 자동으로 사용)
$ mount -t hugetlbfs none /dev/hugepages

# 할당 상태 확인
$ cat /proc/meminfo | grep -i huge
HugePages_Total:    1024
HugePages_Free:      512
Hugepagesize:       2048 kB

멀티큐와 RSS

최신 NIC은 여러 개의 RX/TX 큐를 제공하며, RSS(Receive Side Scaling)를 통해 패킷을 큐에 분산합니다. VPP는 각 워커 스레드에 큐를 할당하여 병렬 처리합니다:

/* startup.conf — DPDK 멀티큐 설정 */
dpdk {
    dev 0000:03:00.0 {
        num-rx-queues 4        /* RX 큐 4개 */
        num-tx-queues 4        /* TX 큐 4개 */
    }
}

cpu {
    main-core 0                /* 메인 스레드: 코어 0 */
    corelist-workers 1-3       /* 워커 스레드: 코어 1,2,3 */
}

플러그인 시스템

VPP는 모듈식 플러그인 아키텍처를 채택하여 기능을 동적으로 확장할 수 있습니다. 각 플러그인은 공유 라이브러리(.so)로 빌드되며, VPP 시작 시 /usr/lib/vpp_plugins/에서 자동 로드됩니다.

플러그인 구조

VPP 플러그인의 기본 디렉터리 구조:

src/plugins/my_plugin/
├── CMakeLists.txt           /* 빌드 설정 */
├── my_plugin.h              /* 공용 헤더 */
├── my_plugin.c              /* 플러그인 초기화 */
├── my_plugin.api            /* Binary API 정의 */
├── node.c                   /* 그래프 노드 구현 */
├── cli.c                    /* CLI 커맨드 */
└── setup.pg                 /* 패킷 생성기 스크립트 (테스트) */

커스텀 플러그인 개발

/* my_plugin.c — 플러그인 초기화 */
#include <vnet/vnet.h>
#include <vlib/vlib.h>
#include <vpp/app/version.h>

typedef struct {
    u32 sw_if_index;
    u32 next_index;
} my_plugin_main_t;

my_plugin_main_t my_plugin_main;

static clib_error_t *
my_plugin_init (vlib_main_t *vm) {
    my_plugin_main_t *mpm = &my_plugin_main;
    mpm->sw_if_index = ~0;
    return 0;
}

VLIB_INIT_FUNCTION(my_plugin_init);

/* 플러그인 등록 매크로 */
VLIB_PLUGIN_REGISTER() = {
    .version = VPP_BUILD_VER,
    .description = "My Custom Plugin",
};
/* node.c — 커스텀 그래프 노드 */
#include <vlib/vlib.h>
#include <vnet/vnet.h>

typedef enum {
    MY_NODE_NEXT_DROP,
    MY_NODE_NEXT_IFACE_OUTPUT,
    MY_NODE_N_NEXT,
} my_node_next_t;

static uword
my_node_fn (vlib_main_t *vm, vlib_node_runtime_t *node,
            vlib_frame_t *frame) {
    u32 n_left_from, *from, *to_next;
    my_node_next_t next_index;

    from = vlib_frame_vector_args(frame);
    n_left_from = frame->n_vectors;
    next_index = node->cached_next_index;

    while (n_left_from > 0) {
        u32 n_left_to_next;
        vlib_get_next_frame(vm, node, next_index,
                           to_next, n_left_to_next);

        while (n_left_from > 0 && n_left_to_next > 0) {
            u32 bi0 = from[0];
            vlib_buffer_t *b0 = vlib_get_buffer(vm, bi0);
            u32 next0 = MY_NODE_NEXT_IFACE_OUTPUT;

            /* 패킷 처리 로직 */
            /* ... */

            to_next[0] = bi0;
            from += 1;
            to_next += 1;
            n_left_from -= 1;
            n_left_to_next -= 1;

            vlib_validate_buffer_enqueue_x1(vm, node, next_index,
                to_next, n_left_to_next, bi0, next0);
        }
        vlib_put_next_frame(vm, node, next_index, n_left_to_next);
    }
    return frame->n_vectors;
}

VLIB_REGISTER_NODE(my_node) = {
    .function = my_node_fn,
    .name = "my-custom-node",
    .vector_size = sizeof(u32),
    .type = VLIB_NODE_TYPE_INTERNAL,
    .n_next_nodes = MY_NODE_N_NEXT,
    .next_nodes = {
        [MY_NODE_NEXT_DROP] = "error-drop",
        [MY_NODE_NEXT_IFACE_OUTPUT] = "interface-output",
    },
};

커널 인터페이스

VPP는 유저스페이스에서 동작하지만, 커널 네트워크 스택과의 연동이 필수적인 경우가 많습니다. VPP는 다양한 커널 인터페이스를 통해 커널과 패킷을 교환합니다.

커널 네트워크 스택 vs VPP 데이터패스 커널 공간 (Kernel Space) 커널 네트워크 스택 NIC 드라이버 (ixgbe...) TAP/TUN vhost AF_PACKET AF_XDP UIO / VFIO (커널 bypass) 유저 공간 (User Space) VPP Process 그래프 노드 엔진 DPDK PMD 플러그인 Binary API CLI (vppctl) Shared Memory Physical NIC (10GbE / 25GbE / 100GbE)

TAP/TUN 인터페이스

TAP 인터페이스는 VPP와 커널 네트워크 스택 간 L2/L3 패킷을 교환하는 가장 일반적인 방법입니다. 커널의 drivers/net/tun.c가 구현합니다.

/* VPP CLI: TAP 인터페이스 생성 */
vpp# create tap id 0 host-if-name vpp-tap0 host-ip4-addr 192.168.1.1/24

/* 호스트에서 확인 */
$ ip addr show vpp-tap0
vpp-tap0: <BROADCAST,MULTICAST,UP> mtu 1500 ...
    inet 192.168.1.1/24 scope global vpp-tap0

/* VPP 측 IP 할당 */
vpp# set interface ip address tap0 192.168.1.2/24
vpp# set interface state tap0 up
virtio 기반 TAP: VPP의 TAP은 내부적으로 virtio 링을 사용합니다. /dev/net/tun + IFF_VNET_HDR 플래그로 virtio 헤더를 포함하여 체크섬 오프로드와 GSO를 지원합니다. 이는 전통적인 TAP보다 성능이 우수합니다.

vhost-user / virtio

vhost-user는 VM과 VPP 간 고성능 패킷 교환을 위한 메커니즘입니다. 공유 메모리와 virtio 링을 사용하여 데이터 복사 없이 패킷을 전달합니다. 커널의 drivers/vhost/ 서브시스템과 유사한 개념이지만, VPP는 유저스페이스에서 vhost 백엔드를 직접 구현합니다.

/* VPP: vhost-user 인터페이스 생성 (서버 모드) */
vpp# create vhost-user socket /var/run/vpp/sock0.sock server
vpp# set interface state VirtualEthernet0/0/0 up

/* QEMU VM 연결 */
$ qemu-system-x86_64 \
    -chardev socket,id=char0,path=/var/run/vpp/sock0.sock \
    -netdev vhost-user,id=net0,chardev=char0 \
    -device virtio-net-pci,netdev=net0

AF_PACKET

AF_PACKET은 커널의 기존 네트워크 인터페이스를 통해 raw 패킷을 송수신하는 소켓 인터페이스입니다(net/packet/af_packet.c). DPDK처럼 NIC를 독점하지 않으므로 기존 커널 스택과 공존 가능합니다.

/* VPP: AF_PACKET 인터페이스 (커널의 eth1에 연결) */
vpp# create host-interface name eth1
vpp# set interface state host-eth1 up
vpp# set interface ip address host-eth1 10.0.0.1/24
용도: AF_PACKET은 DPDK보다 성능이 낮지만(커널 경유), NIC를 커널에서 분리할 필요가 없어 개발/테스트 환경에 적합합니다. PACKET_MMAP 링 버퍼를 사용하여 시스템 콜 오버헤드를 줄입니다.

VPP의 linux-cp(Linux Control Plane) 플러그인은 Netlink를 통해 커널 라우팅 테이블, ARP 엔트리, 인터페이스 상태를 VPP와 동기화합니다. 이를 통해 커널의 ip route, ip neigh 등의 명령이 VPP에도 반영됩니다.

/* linux-cp 플러그인 활성화 (startup.conf) */
plugins {
    plugin linux_cp_plugin.so { enable }
    plugin linux_cp_unittest_plugin.so { enable }
}

linux-cp {
    default netns dataplane
}

/* VPP CLI: 리눅스 인터페이스 미러링 */
vpp# lcp create tap0 host-if lcp-tap0

host-interface (AF_XDP)

AF_XDP(net/xdp/xsk.c)는 커널 5.4+에서 사용 가능한 고성능 패킷 I/O 인터페이스입니다. eBPF/XDP 프로그램과 연계하여 커널 네트워크 스택을 우회하면서도 NIC 드라이버를 교체할 필요가 없습니다.

/* VPP: AF_XDP 인터페이스 생성 */
vpp# create interface af_xdp host-if eth0 num-rx-queues 4
vpp# set interface state af_xdp-eth0 up
DPDK vs AF_XDP: DPDK는 NIC를 커널에서 완전히 분리(UIO/VFIO)하여 최고 성능을 제공하지만 커널 스택과 공존이 불가합니다. AF_XDP는 NIC 드라이버를 유지하면서 XDP 후킹으로 VPP에 패킷을 전달하므로, 일부 트래픽은 커널 스택이 처리하고 특정 트래픽만 VPP로 보낼 수 있습니다.

주요 기능

L2 브릿징/스위칭

VPP는 커널의 net/bridge/와 유사한 L2 브릿지 도메인을 제공합니다. MAC 학습, 플러딩, BUM(Broadcast/Unknown unicast/Multicast) 트래픽 처리를 지원합니다.

/* 브릿지 도메인 생성 및 인터페이스 추가 */
vpp# create bridge-domain 1 learn 1 forward 1 flood 1
vpp# set interface l2 bridge GigabitEthernet0/8/0 1
vpp# set interface l2 bridge GigabitEthernet0/9/0 1
vpp# set interface l2 bridge tap0 1 bvi
vpp# show bridge-domain 1 detail

L3 라우팅 (FIB)

VPP의 FIB(Forwarding Information Base)는 mtrie(multi-way trie) 구조를 사용하여 O(1) 시간에 longest-prefix match를 수행합니다:

/* IP 라우팅 설정 */
vpp# ip route add 10.0.0.0/8 via 192.168.1.1
vpp# ip route add 0.0.0.0/0 via 192.168.1.1

/* ECMP (Equal-Cost Multi-Path) */
vpp# ip route add 10.0.0.0/8 via 192.168.1.1 via 192.168.2.1

/* FIB 테이블 확인 */
vpp# show ip fib
vpp# show ip fib table 0 summary

ACL

VPP의 ACL 플러그인은 stateful/stateless 패킷 필터링을 제공합니다. 커널의 Netfilter/nftables에 대응합니다:

/* ACL 규칙 생성 */
vpp# set acl-plugin acl permit src 192.168.1.0/24 dst 10.0.0.0/8 \
     proto 6 sport 1024-65535 dport 80

/* 인터페이스에 적용 */
vpp# set acl-plugin interface GigabitEthernet0/8/0 input acl 0
vpp# show acl-plugin acl

NAT44/NAT64

/* NAT44: 내부 → 외부 주소 변환 */
vpp# nat44 add interface address GigabitEthernet0/8/0
vpp# set interface nat44 in GigabitEthernet0/9/0 out GigabitEthernet0/8/0

/* 정적 매핑 (DNAT) */
vpp# nat44 add static mapping local 192.168.1.100 22 \
     external GigabitEthernet0/8/0 2222 tcp

/* 세션 확인 */
vpp# show nat44 sessions

IPsec

/* IPsec 터널 모드 설정 */
vpp# ipsec sa add 10 spi 1001 esp crypto-alg aes-gcm-256 \
     crypto-key 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef \
     tunnel src 10.0.0.1 dst 10.0.0.2

vpp# ipsec sa add 20 spi 1002 esp crypto-alg aes-gcm-256 \
     crypto-key fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210 \
     tunnel src 10.0.0.2 dst 10.0.0.1

vpp# ipsec policy add spd 1 priority 100 outbound action protect \
     sa 10 local-ip-range 192.168.1.0 - 192.168.1.255 \
     remote-ip-range 192.168.2.0 - 192.168.2.255

Segment Routing (SRv6)

/* SRv6 정책 설정 */
vpp# sr localsid address fc00::1 behavior end
vpp# sr policy add bsid fc00::999 next fc00::2 next fc00::3 encap
vpp# sr steer l3 10.0.0.0/8 via bsid fc00::999

VPP CLI 및 API

VPP CLI (vppctl)

VPP는 유닉스 도메인 소켓(/run/vpp/cli.sock)을 통한 CLI를 제공합니다:

# VPP CLI 접속
$ vppctl
    _______    _        _   _____  ___
 __/ __/ _ \  (_)__    | | / / _ \/ _ \
 _/ _// // / / / _ \   | |/ / ___/ ___/
 /_/ /____(_)_/\___/   |___/_/  /_/

vpp# show version
vpp# show interface
vpp# show hardware-interfaces
vpp# show runtime               /* 노드별 성능 통계 */
vpp# show errors                /* 에러 카운터 */
vpp# show trace                 /* 패킷 트레이스 */

# 패킷 트레이싱 (디버깅)
vpp# trace add dpdk-input 100   /* DPDK 입력 100 패킷 추적 */
vpp# show trace                 /* 추적 결과 확인 */

# 비대화형 실행
$ vppctl show interface

Binary API

VPP Binary API는 공유 메모리를 통한 고성능 프로그래밍 인터페이스입니다. .api 파일에서 메시지를 정의하고, 코드 생성기가 C/Python/Go 바인딩을 자동 생성합니다:

/* my_plugin.api — API 메시지 정의 */
autoreply define my_plugin_enable_disable {
    u32 client_index;
    u32 context;
    bool enable_disable;
    vl_api_interface_index_t sw_if_index;
};

define my_plugin_details {
    u32 context;
    vl_api_interface_index_t sw_if_index;
    u64 packet_count;
};

VAPI (VPP API)

VAPI는 C/C++용 고수준 API 라이브러리로, Binary API 위에서 동기/비동기 호출, 콜백, 자동 직렬화를 제공합니다:

/* VAPI 사용 예제 (C) */
#include <vapi/vapi.h>
#include <vapi/vpe.api.vapi.h>

vapi_ctx_t ctx;
vapi_ctx_alloc(&ctx);
vapi_connect(ctx, "my_app", NULL,
             64,  /* max outstanding requests */
             32,  /* response queue depth */
             VAPI_MODE_BLOCKING, true);

/* 인터페이스 목록 조회 */
vapi_msg_sw_interface_dump *msg = vapi_alloc_sw_interface_dump(ctx);
msg->payload.name_filter_valid = false;
vapi_sw_interface_dump(ctx, msg, callback_fn, NULL);

vapi_disconnect(ctx);
vapi_ctx_free(ctx);

GoVPP / Python bindings

# Python (vpp_papi) 예제
from vpp_papi import VPPApiClient

vpp = VPPApiClient(apifiles=['/usr/share/vpp/api/core/*.api.json'])
vpp.connect('my-python-app')

# 인터페이스 목록
ifaces = vpp.api.sw_interface_dump()
for iface in ifaces:
    print(f"{iface.interface_name}: {iface.sw_if_index}")

# TAP 생성
rv = vpp.api.tap_create_v2(id=0, host_if_name='vpp-tap0',
                            host_ip4_prefix='192.168.1.1/24')

vpp.disconnect()

성능 최적화

CPU 핀닝과 워커 스레드

VPP 성능의 핵심은 CPU 코어 격리와 적절한 워커 스레드 배치입니다:

/* startup.conf — CPU 핀닝 */
cpu {
    main-core 0                    /* 메인 스레드 (API, CLI 처리) */
    corelist-workers 2,4,6,8       /* 워커: 물리 코어만 (HT 제외) */
    skip-cores 1                   /* 코어 1 건너뛰기 (OS용) */
}

# 커널 부팅 파라미터: VPP 코어를 OS 스케줄러에서 격리
GRUB_CMDLINE_LINUX="isolcpus=2,4,6,8 nohz_full=2,4,6,8 rcu_nocbs=2,4,6,8"
커널 관점: isolcpus는 지정 코어를 CFS 로드 밸런싱에서 제외합니다. nohz_full은 해당 코어의 스케줄러 틱을 비활성화하여 인터럽트를 최소화합니다. rcu_nocbs는 RCU 콜백을 다른 코어로 오프로드합니다. 이 조합이 VPP의 busy-polling 성능을 극대화합니다.

NUMA 인식

NIC과 VPP 워커 스레드를 동일 NUMA 노드에 배치하여 원격 메모리 접근(remote memory access)을 방지합니다:

# NIC의 NUMA 노드 확인
$ cat /sys/bus/pci/devices/0000:03:00.0/numa_node
0

# NUMA 노드 0의 CPU 확인
$ lscpu | grep "NUMA node0"
NUMA node0 CPU(s): 0-7

/* startup.conf — NUMA 인식 설정 */
buffers {
    buffers-per-numa 16384        /* NUMA 노드당 버퍼 수 */
    default data-size 2048
}

dpdk {
    dev 0000:03:00.0 {            /* NUMA 0에 위치한 NIC */
        num-rx-queues 4
    }
}

cpu {
    main-core 0                   /* NUMA 0 코어 */
    corelist-workers 2,4,6        /* NUMA 0 코어 */
}

인터럽트 모드 vs 폴링 모드

모드장점단점적용 시나리오
Polling (기본)최소 레이턴시, 최대 throughputCPU 100% 사용고성능 필수 환경
Interrupt유휴 시 CPU 절약인터럽트 오버헤드트래픽 변동이 큰 환경
Adaptive부하에 따라 자동 전환전환 지연 발생 가능범용 환경
/* startup.conf — 인터럽트 모드 활성화 */
dpdk {
    dev 0000:03:00.0 {
        num-rx-queues 4
    }
}

/* 적응형 모드: 유휴 시 sleep */
unix {
    poll-sleep-usec 100           /* 유휴 시 100μs sleep */
}

성능 벤치마킹

# VPP 내장 패킷 생성기로 벤치마크
vpp# packet-generator new {
    name pg0
    limit 10000000
    size 64-64
    interface pg0
    node ethernet-input
    data { IP4: 1.2.3 -> 4.5.6
           UDP: 192.168.1.1 -> 192.168.2.1
           UDP: 1234 -> 5678
           incrementing 100
    }
}
vpp# packet-generator enable

# 런타임 통계 확인
vpp# show runtime
# 출력:
# Name                 State    Calls   Vectors  Suspends  Clocks   Vectors/Call
# dpdk-input           polling  1000000 10000000 0         24.50    10.00
# ethernet-input       active   1000000 10000000 0         18.30    10.00
# ip4-input            active   1000000 10000000 0         15.20    10.00
# ip4-lookup           active   1000000 10000000 0         12.80    10.00

# TRex 외부 트래픽 생성기 사용
$ ./t-rex-64 -f stl/bench.py -m 10mpps -d 60

설치 및 설정

패키지 설치

# Ubuntu/Debian — FD.io 공식 저장소
$ curl -s https://packagecloud.io/install/repositories/fdio/release/script.deb.sh | sudo bash
$ sudo apt-get install vpp vpp-plugin-core vpp-plugin-dpdk vpp-dev

# CentOS/RHEL
$ curl -s https://packagecloud.io/install/repositories/fdio/release/script.rpm.sh | sudo bash
$ sudo yum install vpp vpp-plugins vpp-devel

# 서비스 시작
$ sudo systemctl start vpp
$ sudo systemctl enable vpp

소스 빌드

# 소스 클론
$ git clone https://gerrit.fd.io/r/vpp
$ cd vpp

# 의존성 설치 (Ubuntu)
$ make install-dep
$ make install-ext-deps

# 빌드
$ make build             /* 디버그 빌드 */
$ make build-release     /* 릴리스 빌드 */

# 실행
$ make run               /* 빌드 디렉터리에서 직접 실행 */

startup.conf 설정

/* /etc/vpp/startup.conf — 프로덕션 설정 예제 */

unix {
    nodaemon                       /* 포그라운드 실행 (디버깅 시) */
    cli-listen /run/vpp/cli.sock   /* CLI 소켓 경로 */
    log /var/log/vpp/vpp.log       /* 로그 파일 */
    full-coredump                  /* 코어 덤프 활성화 */
    exec /etc/vpp/setup.gate       /* 시작 시 실행할 CLI 스크립트 */
}

api-trace {
    on                             /* API 추적 활성화 */
}

api-segment {
    gid vpp                        /* API 소켓 그룹 */
}

cpu {
    main-core 0
    corelist-workers 2,4,6,8
}

dpdk {
    dev default {
        num-rx-queues 4
        num-tx-queues 4
        num-rx-desc 1024
        num-tx-desc 1024
    }
    dev 0000:03:00.0
    dev 0000:03:00.1
    uio-driver vfio-pci            /* VFIO 사용 */
}

buffers {
    buffers-per-numa 32768
    default data-size 2048
}

plugins {
    plugin default { disable }     /* 기본 비활성화 */
    plugin dpdk_plugin.so { enable }
    plugin acl_plugin.so { enable }
    plugin nat_plugin.so { enable }
    plugin linux_cp_plugin.so { enable }
}

활용 사례

가상 스위치 (vSwitch)

VPP는 데이터센터의 가상 스위치로 활용됩니다. VM 간 트래픽을 vhost-user 인터페이스로 고성능 전달하며, OvS-DPDK 대비 더 높은 pps 성능을 제공합니다. Kubernetes 환경에서는 Calico/VPP CNI 플러그인으로 Pod 네트워킹을 가속합니다.

CPE/VNF

통신사의 고객 구내 장비(CPE)를 소프트웨어로 구현하는 VNF(Virtual Network Function)에 VPP가 사용됩니다. 라우팅, NAT, IPsec, QoS를 단일 VPP 인스턴스에서 처리하여 전용 하드웨어를 대체합니다.

5G UPF

5G 코어 네트워크의 UPF(User Plane Function)에서 GTP-U 터널 종단, PDU 세션 관리, SRv6 기반 모바일 유저플레인을 VPP로 구현합니다. Travelping, free5GC 등의 오픈소스 5G 프로젝트가 VPP를 UPF 엔진으로 사용합니다.

로드 밸런서

VPP의 lb 플러그인은 Maglev 해시 기반 L3/L4 로드 밸런서를 제공합니다. GRE 또는 VXLAN으로 백엔드 서버에 패킷을 분산합니다:

vpp# lb vip 10.0.0.100/32 protocol tcp port 80 encap gre4
vpp# lb as 10.0.0.100/32 protocol tcp port 80 192.168.1.10
vpp# lb as 10.0.0.100/32 protocol tcp port 80 192.168.1.11

커널 네트워크 스택과의 비교

성능 비교

시나리오커널 스택VPP (DPDK)VPP (AF_XDP)
L3 포워딩 (64B, 10GbE)~1.2 Mpps~14.8 Mpps (라인레이트)~11 Mpps
L2 브릿징 (64B, 10GbE)~2 Mpps~14.8 Mpps (라인레이트)~12 Mpps
IPsec (AES-GCM-256, 1500B)~2 Gbps~20 Gbps (AES-NI)
NAT44 (64B)~800 Kpps~10 Mpps~8 Mpps
TCP 연결 처리우수 (네이티브)제한적 (VCL)제한적
주의: 성능 수치는 하드웨어, 패킷 크기, 규칙 수에 따라 크게 달라집니다. 위 표는 단일 코어, 10GbE NIC 기준의 대략적인 참고값입니다.

적용 시나리오 선택 기준

기준커널 스택 적합VPP 적합
TCP 애플리케이션웹 서버, DB, 범용 서비스L3/L4 포워딩, 터널링
성능 요구< 5 Gbps, 범용 처리> 10 Gbps, 라인레이트 포워딩
기능 복잡도소켓 API, iptables, tcL2/L3/VPN/NAT 고성능 처리
운영 편의성표준 Linux 도구 사용별도 CLI/API 학습 필요
NIC 공유여러 앱이 NIC 공유NIC 독점(DPDK) 또는 AF_XDP
에코시스템모든 Linux 도구 호환linux-cp로 일부 통합

커널 관련 소스 및 참고 코드

VPP가 활용하는 커널 서브시스템과 관련 소스 파일 매핑:

커널 서브시스템소스 파일VPP 연관
TUN/TAPdrivers/net/tun.cVPP TAP 인터페이스, /dev/net/tun
vhostdrivers/vhost/net.c, drivers/vhost/vhost.cvhost-user 백엔드, virtio 링
virtiodrivers/virtio/virtio_ring.cTAP virtio, VM 네트워킹
AF_PACKETnet/packet/af_packet.chost-interface, PACKET_MMAP
AF_XDPnet/xdp/xsk.c, net/xdp/xsk_buff_pool.cAF_XDP 인터페이스
XDPnet/core/dev.c (XDP hooks)AF_XDP용 XDP 프로그램
UIOdrivers/uio/uio_pci_generic.cDPDK UIO 드라이버
VFIOdrivers/vfio/pci/vfio_pci_core.cDPDK VFIO 드라이버 (IOMMU)
Hugepagesmm/hugetlb.c, fs/hugetlbfs/DPDK/VPP 메모리 할당
IOMMUdrivers/iommu/intel/iommu.c, drivers/iommu/amd/iommu.cVFIO DMA 격리
Netlinknet/netlink/af_netlink.clinux-cp 플러그인, 라우팅 동기화
Namespacenet/core/net_namespace.cTAP 네임스페이스 격리
소스 탐색: VPP 소스 코드는 https://gerrit.fd.io/r/gitweb?p=vpp.git에서 확인할 수 있습니다. 주요 디렉터리: src/vnet/(네트워킹 코어), src/vlib/(벡터 처리 엔진), src/plugins/(플러그인), src/vpp/(메인 프로세스).