오버레이 · 실전 시나리오

VPP에서 오버레이 네트워크와 실전 시나리오를 구성하는 가이드입니다. Segment Routing(SRv6), QoS, VXLAN/GENEVE 터널, 실전 시나리오 맵, L3 라우터+NAT44 예제, memif 서비스 체이닝 예제를 다룹니다.

선행 문서: 본 페이지는 데이터 경로 (L2~L4)의 L2~L4 노드 심화, DPDK 통합, L3 FIB 구조를 전제로 합니다. 기본 데이터 경로 설명을 먼저 읽으시기 바랍니다.

Segment Routing (SRv6)

VPP는 IPv6 Segment Routing(SRv6)을 데이터 플레인 수준에서 완전히 구현합니다. SRv6는 IPv6 확장 헤더인 Segment Routing Header(SRH)를 사용하여 패킷 경로를 소스에서 명시적으로 지정합니다. SRH에는 128비트 IPv6 주소 형식의 SID(Segment Identifier) 목록이 역순으로 저장되며, Segments Left 카운터가 현재 활성 세그먼트를 가리킵니다. 각 SRv6 노드는 Segments Left 값을 1씩 감소시키고 다음 SID를 IPv6 목적지 주소에 복사하여 패킷을 다음 홉으로 전달합니다.

VPP는 두 가지 SRv6 적용 모드를 지원합니다. 캡슐화 모드는 원본 패킷을 새로운 IPv6 + SRH 외부 헤더로 감싸는 방식이며, 삽입(Insertion) 모드는 기존 IPv6 헤더에 SRH를 직접 삽입합니다. 캡슐화 모드가 기본이며, 삽입 모드는 중간 라우터가 SRv6를 인식하지 못하는 환경에서 유용합니다.

로컬 SID 동작(Local SID Behavior)은 각 노드에서 SID가 자신에게 할당된 것일 때 수행하는 처리를 정의합니다. VPP가 지원하는 주요 동작은 다음과 같습니다:

BSID(Binding SID)는 SRv6 정책 전체를 하나의 SID로 추상화하여 트래픽 엔지니어링을 단순화합니다. 입구 노드는 BSID만 참조하면 되므로 정책 내부의 세그먼트 목록 변경이 투명하게 처리됩니다. VPP에서는 sr policy add bsid 명령으로 BSID를 정책에 바인딩하고, sr steer 명령으로 특정 트래픽을 해당 정책으로 유도합니다.

VPP는 SRv6 Mobile 기능도 제공하여 5G 환경에서 UPF(User Plane Function) 역할을 수행합니다. GTP-U 터널 패킷을 SRv6 세그먼트로 변환(T.M.GTP4.D)하거나, SRv6 패킷을 다시 GTP-U로 복원(End.M.GTP4.E)하는 상호 변환을 지원합니다. 이를 통해 모바일 백홀 네트워크에서 GTP-U 터널링 대신 SRv6 기반의 유연한 트래픽 엔지니어링을 적용할 수 있습니다.

SRv6 패킷 처리 흐름 (캡슐화 모드) 원본 패킷 IPv4/IPv6 Payload SR 캡슐화 (입구 노드) SID 목록 + SRH 부착 BSID → 정책 매핑 중간 노드 (End) SL 감소, 다음 SID 전달 최종 SID (출구 노드) End.DT4/DT6: 탈캡슐화 VRF 라우팅 테이블 조회 SRH(Segment Routing Header) 구조 Next Header 확장 헤더 타입 Hdr Ext Len 헤더 길이 Routing Type 4 (SRH) Segments Left 활성 세그먼트 인덱스 Last Entry 마지막 SID 인덱스 Flags HMAC 등 Tag 패킷 그룹 SID 목록 (역순 저장, 하위부터 처리) Segment List[0]: fc00::1 (최종) Segment List[1]: fc00::2 (중간) Segment List[2]: fc00::3 (입구) Segments Left 카운터 변화 입구 노드 (캡슐화) SL = 2, DA = fc00::3 SL-- 중간 노드 (End) SL = 1, DA = fc00::2 SL-- 최종 노드 (End.DT4/DT6) SL = 0, DA = fc00::1 → 탈캡슐화 SRv6 Mobile (5G UPF 연동) T.M.GTP4.D: GTP-U 패킷 → SRv6 세그먼트 변환 (GTP TEID를 SID에 인코딩) End.M.GTP4.E: SRv6 패킷 → GTP-U 터널 복원 (SID에서 TEID 디코딩)
/* 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
알고리즘유형nativeipsecmbopensslQAT HW
AES-GCM-128/256AEADOOOO
AES-CBC-128/256암호화OOO
ChaCha20-Poly1305AEADOO
HMAC-SHA-256/512인증OOO
AES-CTR암호화OOO
NULL테스트OOO

QoS (Quality of Service)

VPP는 패킷 마킹, 폴리싱, 스케줄링 기능을 제공합니다:

VPP QoS 파이프라인과 2r3c 폴리서 동작 원리 인입 (RX) IP DSCP / MPLS TC VLAN PCP qos-record 헤더에서 QoS 값 추출 → opaque 저장 classify ACL / 5-tuple 폴리서 선택 policer-input 2r3c 버킷 검사 color = G/Y/R L3 처리 경로 fwd / redirect / drop 색상별 분기 qos-mark → TX 아웃바운드 헤더에 DSCP 기록 2r3c (RFC 2698) — 두 버킷으로 Green/Yellow/Red 분류 CIR 버킷 (C) Committed Info Rate 토큰 ≥ size? crate=100000 kbps PIR 버킷 (P) Peak Info Rate 토큰 ≥ size? prate=200000 kbps GREEN: 통과 (C,P 모두 OK) YELLOW: 마킹/강등 (C 초과, P OK) RED: 폐기 (P 초과) 토큰 채우기 매 tick마다: C += cir × Δt P += pir × Δt C ≤ cb, P ≤ eb로 캡 qos-record → policer-input(색상 결정) → L3/L2 포워딩 → qos-mark의 4단계로 End-to-End DSCP 관리를 완결합니다. Color-aware 모드에서는 인입 DSCP가 이미 yellow이면 green으로 승격하지 않습니다.
# 폴리서 생성 (2r3c: 2-rate 3-color marker)
vpp# configure policer name rate-limiter cir 100000 cb 10000 \
     eir 200000 eb 20000 rate kbps color-aware

# 인터페이스에 폴리서 적용
vpp# set policer classify interface GigabitEthernet0/8/0 ip4-table 0
vpp# policer input rate-limiter GigabitEthernet0/8/0

# QoS 마킹 (DSCP 설정)
vpp# set qos record interface GigabitEthernet0/8/0 input ip
vpp# set qos mark interface GigabitEthernet0/9/0 output ip table 0

# 상태 확인
vpp# show policer
vpp# show qos record
vpp# show qos mark
QoS 기능설명노드
Policer2r3c (RFC 2698) 기반 트래픽 폴리싱policer-input
QoS Record인입 패킷의 QoS 값(DSCP/MPLS TC) 기록qos-record
QoS Mark송출 패킷에 QoS 값 마킹qos-mark
QoS Store패킷에 고정 QoS 값 저장qos-store

엔드투엔드 QoS 흐름 — Ingress 분류 → Policing → Marking → Egress Shaping

위에서 다룬 폴리서/마킹은 개별 기능이지만, 운영에서는 4단계가 한 흐름으로 묶여야 의미가 있습니다. 패킷이 들어와서 나갈 때까지 어떤 결정이 어디서 일어나는지 정리합니다.

  1. Ingress 분류 (qos-record + classify) — 외부 헤더의 DSCP/PCP/MPLS TC 또는 ACL 5-tuple 매칭으로 트래픽 클래스를 결정합니다. 결과는 버퍼의 opaque 영역에 저장되어 이후 노드에 전달됩니다.
  2. Policing (policer-input) — 클래스별 토큰 버킷에서 색상(green/yellow/red)을 부여합니다. red는 즉시 드롭, yellow는 강등 마킹 또는 통과, green은 통과합니다. 이는 contract enforcement로, 송신단의 burst를 평탄화하지는 않습니다.
  3. Egress Marking (qos-mark) — 송신 헤더에 결정된 DSCP/EXP 값을 기록합니다. 다음 hop이 이 마킹을 보고 자신의 큐로 분류하므로, 도메인 전체에서 DSCP 매핑 일관성이 핵심입니다.
  4. Egress Shaping — VPP 자체에는 본격적인 HTB/HFSC 같은 계층적 스케줄러가 없습니다. 대신 다음 두 가지로 shaping을 구현합니다:
    • Output policer — 정확히 말하면 shaping이 아닌 policing이지만, 송신 인터페이스에 polica를 걸어 average rate를 제한할 수 있습니다. burst가 짧으면 충분합니다.
    • NIC 하드웨어 큐 — DPDK/Mellanox PMD가 지원하는 NIC TX rate limit 또는 priority queue를 사용합니다. show hardware-interfaces detail에서 지원 여부를 확인합니다.
    • 외부 스케줄러로 위임 — 컨테이너 환경에서는 VPP TX → 커널 tap → tc HTB로 넘기는 구조도 흔합니다. 단, tap을 거치면 zero-copy 이점은 사라집니다.
왜 VPP에는 본격 shaper가 없는가: VPP는 라인레이트 포워더로 설계되어 있어, 큐잉으로 의도적 지연을 만드는 shaping은 철학적으로 어울리지 않습니다. 트래픽 contract 강제가 필요하면 policer를, 큐잉/스케줄링이 필요하면 NIC 하드웨어나 인접 노드(라우터/L7 프록시)에 위임하는 것이 일반적인 패턴입니다.

QoS 트러블슈팅

증상별 진단:
  • polic가 적용되지 않음show policer에서 카운터가 0인지 확인합니다. set policer classify의 classify table이 인터페이스에 바인딩되어 있는지(show classify table verbose)를 함께 봅니다.
  • green 트래픽이 모두 yellow로 마킹됨 — color-aware 모드에서 인입 DSCP가 이미 yellow로 인식되는 경우입니다. show qos record로 입력 DSCP 매핑 테이블을 확인합니다. color-blind 모드로 전환하면 모든 입력을 green으로 가정합니다.
  • 마킹이 다음 hop에 보존되지 않음 — 캡슐화/디캡슐화 시 외부 헤더로 DSCP를 복사하는 옵션이 있습니다. VXLAN/IPsec은 기본적으로 inner DSCP를 outer로 복사하지 않으므로, 도메인 정책에 따라 명시적으로 설정해야 합니다.
  • polic 카운터는 증가하는데 라인 sat에서 트래픽이 줄지 않음 — policer는 평균 rate를 강제하지만 burst를 부드럽게 하지 않습니다. 짧은 burst가 반복되면 평균만 만족하고 line은 saturate될 수 있습니다. 이때는 NIC 하드웨어 큐 또는 외부 shaper를 추가해야 합니다.

VXLAN / GENEVE 터널

VXLAN(Virtual Extensible LAN, RFC 7348)은 L2 프레임을 UDP 패킷으로 캡슐화하여 L3 네트워크 위에 가상의 L2 오버레이 네트워크를 구성하는 터널링 프로토콜입니다. 원본 이더넷 프레임 앞에 외부 이더넷 헤더(Outer Ethernet), 외부 IP 헤더(Outer IP), 외부 UDP 헤더(목적지 포트 4789), 그리고 8바이트 VXLAN 헤더가 추가됩니다. VXLAN 헤더에는 24비트 VNI(VXLAN Network Identifier)가 포함되어 최대 약 1,600만 개의 논리적 네트워크 세그먼트를 지원합니다. BUM(Broadcast, Unknown unicast, Multicast) 트래픽 처리에는 멀티캐스트 그룹을 사용한 복제 방식과 헤드엔드 유니캐스트 복제(Head-End Replication) 방식이 있으며, VPP는 두 가지 모두 지원합니다.

GENEVE(Generic Network Virtualization Encapsulation, RFC 8926)는 VXLAN의 후속 프로토콜로, 고정 헤더 뒤에 가변 길이 TLV(Type-Length-Value) 옵션 필드를 추가할 수 있습니다. 이를 통해 OAM(Operations, Administration, Maintenance) 정보, 보안 태그, 텔레메트리 메타데이터 등을 터널 헤더에 직접 포함할 수 있어 확장성이 크게 향상됩니다. UDP 목적지 포트는 6081을 사용하며, VXLAN과 동일하게 24비트 VNI를 지원합니다.

VPP의 패킷 그래프에서 VXLAN 디캡슐화는 ip4-inputip4-lookupip4-localudp-localvxlan4-input 경로를 거치며, vxlan4-input 노드에서 VNI를 기반으로 해당 터널 인터페이스를 식별하고 내부 프레임을 추출합니다. 캡슐화 경로는 l2-outputvxlan4-encapip4-rewriteinterface-output 순서로 진행됩니다. 최신 NIC(Intel X710, Mellanox ConnectX-5 이상 등)은 VTEP(VXLAN Tunnel End Point) 오프로드를 지원하여 외부 헤더의 추가·제거를 하드웨어에서 처리함으로써 CPU 부담을 줄이고 처리량을 높일 수 있습니다.

VXLAN 캡슐화 패킷 구조 및 VPP 그래프 노드 경로 캡슐화 패킷 구조 Outer Ethernet 14 bytes Outer IP 20 bytes Outer UDP dst port: 4789 VXLAN Header VNI (24-bit) · 8 bytes Inner Ethernet 14 bytes Inner IP 20 bytes Inner Payload 가변 길이 오버레이 헤더 (Outer + VXLAN) — 50 bytes 오버헤드 원본 프레임 (Inner Frame) 디캡슐화 경로 (Decap) ip4-input ip4-lookup ip4-local udp-local vxlan4-input l2-input / ip4-input VNI 매칭 → 터널 인터페이스 식별 캡슐화 경로 (Encap) l2-input l2-fwd / l2-flood l2-output vxlan4-encap ip4-rewrite interface-output VXLAN 헤더 + Outer UDP/IP 추가 GENEVE 확장 헤더 Outer Eth+IP+UDP GENEVE Header VNI · port 6081 TLV Options OAM · 보안 태그 · 메타데이터 Inner Frame (원본) GENEVE TLV: 가변 길이 메타데이터로 VXLAN 대비 확장성 우수
# VXLAN 터널 생성
vpp# create vxlan tunnel src 10.0.0.1 dst 10.0.0.2 vni 100
vpp# set interface state vxlan_tunnel0 up

# 브릿지 도메인에 추가 (L2 오버레이)
vpp# set interface l2 bridge vxlan_tunnel0 100
vpp# set interface l2 bridge GigabitEthernet0/9/0 100

# GENEVE 터널 생성
vpp# create geneve tunnel src 10.0.0.1 dst 10.0.0.2 vni 200
vpp# set interface state geneve_tunnel0 up

# 터널 상태 확인
vpp# show vxlan tunnel
vpp# show geneve tunnel
VXLAN GPE vs 표준 VXLAN: 표준 VXLAN(RFC 7348)은 L2 프레임만 캡슐화하지만, VXLAN-GPE(Generic Protocol Extension, draft-ietf-nvo3-vxlan-gpe)는 L3/NSH/MPLS 등 다양한 프로토콜을 캡슐화합니다. VPP는 VXLAN-GPE + SRv6 조합으로 SFC(Service Function Chaining)를 구현합니다.

VXLAN/GENEVE 트러블슈팅

VXLAN/GENEVE 터널은 정상 구성 후에도 캡슐화 오버헤드, MAC 학습, BUM(Broadcast/Unknown-unicast/Multicast) 처리 때문에 여러 함정이 있습니다. 증상별 진단 흐름을 정리합니다.

증상 1: 터널은 up인데 트래픽이 흐르지 않음
  • MTU 부족 — VXLAN은 50바이트(GENEVE는 가변, 보통 50~74) 오버헤드를 추가합니다. 언더레이 MTU가 1500이면 페이로드 MTU는 1450 이하여야 합니다. 게스트가 1500 그대로 보내면 외부 IP 단편화가 발생하거나 DF 비트가 켜진 패킷이 드롭됩니다. ip4-input fragmentation needed 또는 vxlan4-encap drop 카운터가 증가합니다.
  • 해결 — 언더레이를 점보프레임(1600+ 권장, 9000 이상 이상적)으로 올리거나, 게스트 MTU를 1450으로 강제. show interface로 양단 MTU 일치 확인.
증상 2: 한 방향만 통신됨 (반대 방향 unknown unicast)
  • MAC 학습 실패 — VXLAN을 브릿지 도메인에 넣을 때 learn 옵션이 꺼져 있으면 원격 MAC을 학습하지 않습니다. show bridge-domain <id> detail에서 learn 플래그를 확인합니다.
  • 해결set bridge-domain learn <bd> 1로 활성화. EVPN 컨트롤 평면을 쓰는 경우는 학습을 끄고 정적 MAC을 주입합니다.
  • show l2fib bd_id <bd>로 학습된 MAC 테이블 직접 확인.
증상 3: BUM 트래픽 폭주 / 미수신
  • flood 모드 미설정 — VXLAN BUM은 두 가지로 처리합니다: head-end replication(VTEP가 모든 원격에 유니캐스트 복제) 또는 multicast underlay(언더레이 멀티캐스트 그룹 사용). VPP는 기본적으로 head-end replication이며, create vxlan tunnel에 원격을 명시적으로 등록해야 합니다.
  • 증상 — ARP/DHCP 같은 브로드캐스트가 일부 VTEP에만 도달하거나, 멀티캐스트 그룹이 동기화되지 않아 BUM이 모두 드롭됩니다.
  • 해결 — 모든 원격 VTEP을 같은 BD에 명시적으로 등록(create vxlan tunnel ... decap-next l2)하거나, multicast underlay 모드로 전환합니다.
증상 4: 디캡슐화 실패 — 패킷이 외부 라우팅으로 빠짐
  • UDP 포트/VNI 미매칭vxlan4-input 노드가 패킷을 인식하려면 dst port 4789(GENEVE는 6081) + 등록된 VNI가 일치해야 합니다. 한쪽이 다른 VNI를 쓰면 디캡 노드가 무시하고 ip4-local drop으로 진행합니다.
  • 진단show trace로 vxlan4-input 노드에 도달했는지 확인. 도달했지만 드롭이면 show vxlan tunnel의 VNI 목록과 비교.
  • NIC VTEP 오프로드와의 충돌 — VTEP 오프로드 활성화 시 일부 NIC은 디캡을 NIC에서 수행합니다. VPP가 디캡 패킷을 또 디캡하려고 시도하면 실패합니다. show hardware-interfaces detail로 오프로드 상태를 확인하고 필요하면 비활성화합니다.
TLS/QUIC 네트워킹: VCL 세션 레이어, TLS 아키텍처, 인증서 관리, QUIC 프로토콜, TLS 성능 최적화, TLS 종단 프록시 구성은 VPP 호스트 스택 개요를 참고하세요.
전제 조건: VPP의 벡터 처리 모델과 그래프 노드 아키텍처는 기초와 아키텍처에서 먼저 확인하세요. 이 문서의 L2~L4 노드 심화·DPDK 통합·커널 인터페이스 섹션은 실전 시나리오의 직접적 전제가 됩니다.

VXLAN-GPE · GTP-U · PPPoE — 전송 오버레이

VXLAN/GENEVE 외에도 VPP는 서비스별 특화 터널 프로토콜을 다수 지원합니다. 각각 다른 제어 평면 / 도메인에서 쓰이지만 VPP 안에서는 모두 그래프 노드 한 쌍(input/output)으로 구현됩니다.

VXLAN-GPE — Generic Protocol Extension

기본 VXLAN(RFC 7348)은 이더넷 프레임만 운반할 수 있습니다. VXLAN-GPE는 헤더에 next-protocol 필드를 추가해 IPv4/IPv6/이더넷/NSH 같은 다양한 페이로드를 식별할 수 있게 확장합니다. 서비스 체인 헤더(NSH)를 운반하는 주된 transport 중 하나입니다.

vpp# create vxlan-gpe tunnel local 10.0.0.1 remote 10.0.0.2 vni 100      next-protocol ip4 encap-vrf-id 0
vpp# set interface state vxlan_gpe_tunnel0 up
vpp# set interface ip address vxlan_gpe_tunnel0 192.168.100.1/24
vpp# show vxlan-gpe tunnel

GTP-U — GPRS Tunneling Protocol User Plane

GTP-U(3GPP TS 29.281)는 4G/5G 모바일 코어의 사용자 평면 터널입니다. 단말의 IP 패킷을 TEID(Tunnel Endpoint Identifier)로 식별해 UDP 2152 포트로 운반합니다. VPP의 gtpu 플러그인은 UPF(User Plane Function) 구현의 기반이 됩니다.

vpp# create gtpu tunnel src 10.0.0.1 dst 10.0.0.2 teid 1000 encap-vrf-id 0
vpp# set interface ip address gtpu_tunnel0 172.16.0.1/24
vpp# show gtpu tunnel

5G UPF 실전 구성에서는 활용 사례 — 5G UPF와 함께 PFCP 플러그인(N4 인터페이스)이 필요합니다.

PPPoE — 가입자망 터널

PPPoE(RFC 2516)는 ADSL·VDSL·GPON 가입자망에서 이더넷 위에 PPP 세션을 만드는 프로토콜입니다. VPP의 pppoe 플러그인은 BRAS(Broadband Remote Access Server) 역할로 수천 개 가입자 세션을 종단할 수 있습니다.

vpp# create pppoe session client-ip 10.0.0.2 session-id 100      client-mac aa:bb:cc:dd:ee:ff decap-vrf-id 0
vpp# show pppoe session

NSH — Network Service Header

NSH(RFC 8300)는 서비스 체이닝의 메타데이터 캐리어입니다. 트래픽이 방화벽·IDS·WAF 같은 서비스 노드를 거쳐야 할 때, NSH 헤더가 경로(Service Path)와 현재 단계(Service Index)를 기록합니다. VPP는 nsh 플러그인으로 인코딩/디코딩 노드를 제공하며, VXLAN-GPE나 SRv6 위에 실어 전달합니다.

실전 시나리오 맵

VPP의 대표적인 실전 시나리오 5종을 한눈에 비교합니다. 각 시나리오는 startup.conf 설정부터 인터페이스 구성, 데이터 경로 분석, 성능 확인까지 엔드-투-엔드 절차를 제공하며, 상세 구성은 아래 표의 "상세 위치" 열에서 해당 Part로 이동하여 확인할 수 있습니다.

시나리오주요 기능인터페이스적합한 환경상세 위치
L3 라우터 + NAT44IP 포워딩, 주소 변환(Address Translation)DPDK PMD엣지 라우터, 게이트웨이본 문서
memif 서비스 체이닝제로카피 패킷(Packet) 전달, SFCmemif (공유 메모리)NFV, 다단계 패킷 처리본 문서
IPsec VPN터널(Tunnel) 암호화(Encryption), IKEv2DPDK + ipsec사이트 간 VPN, 원격 접속보안과 터널링
SSL/TLS InspectionTLS 종단·복호화(Decryption)·재암호화VCL + TLS 플러그인NGFW, 보안 게이트웨이, DLPTCP/TLS 프록시 · SSL Inspection
TPROXY (투명 프록시)원본 IP 보존 투명 프록시VCL + session layer / memifL7 보안 게이트웨이, CDN, 서비스 메시보안과 터널링
공통 전제: 모든 시나리오는 Hugepage 2MB x 1024 이상, CPU 격리(Isolation)(isolcpus), DPDK 호환 NIC를 가정합니다. 설치 및 설정을 참고하여 기본 환경을 먼저 구성하세요.
VPP 실전 시나리오 맵 — 무엇을 어디에 붙일지 한눈에 공통 기반 (모든 시나리오에서 동일) Hugepages 2MB/1GB isolcpus 워커 핀닝 DPDK / VFIO PMD 바인딩 startup.conf cpu/buffers/dpdk vppctl / API 제어 평면 stats segment 관측성 L2/L3 파이프라인 (벡터 그래프 노드) dpdk-input → ethernet-input → ip4-input → ip4-lookup → ip4-rewrite → interface-tx L3 + NAT44 엣지 게이트웨이 nat44-in2out nat44-out2in 세션 기반 fast path DPDK PMD memif SFC 서비스 체이닝 zero-copy ring 공유 메모리 VPP ↔ 컨테이너 VNF 파이프라인 memif IPsec VPN Site-to-Site ESP 터널 IKEv2 / 정책 QAT 오프로드 anti-replay DPDK + crypto SSL Inspection NGFW / DLP 이중 TLS 세션 동적 ckpair SNI 바이패스 정책 엔진 VCL + TLS TPROXY 투명 프록시 원본 IP 보존 L7 검사 인라인/외부 프록시 VCL / memif 공통 기반 위에 L2/L3 벡터 파이프라인을 깔고, 필요한 기능 블록(플러그인 + 인터페이스)만 조립해 각 시나리오를 만듭니다.

실전 예제: L3 라우터 + NAT44 구성

VPP를 이용하여 DPDK 기반 L3 라우터NAT44를 결합한 실제 운영 환경 구성입니다. 물리 NIC 2개를 DPDK에 바인딩하고, LAN → WAN 트래픽에 NAT을 적용하는 전형적인 엣지 라우터 시나리오입니다.

VPP L3 라우터 + NAT44 데이터 흐름 LAN (192.168.1.0/24) 호스트 192.168.1.10 호스트 192.168.1.20 VPP L3 라우터 GigabitEthernet0/8/0 192.168.1.1/24 dpdk-input → ethernet-input → ip4-input ip4-lookup → nat44-in2out-slowpath ip4-rewrite → GigabitEthernet0/9/0-output GigabitEthernet0/9/0 203.0.113.1/24 NAT44: 192.168.1.x → 203.0.113.1 WAN / Internet 203.0.113.0/24 LAN 호스트 → DPDK 수신 → IP 룩업 → NAT 변환 → WAN 송신

startup.conf 구성

# /etc/vpp/startup.conf — L3 라우터 + NAT44 실전 구성
unix {
    cli-listen /run/vpp/cli.sock
    log /var/log/vpp/vpp.log
    full-coredump
    gid vpp
}

api-trace { on }
api-segment { gid vpp }

dpdk {
    dev 0000:00:08.0 {                 /* LAN NIC */
        name GigabitEthernet0/8/0
        num-rx-queues 2
    }
    dev 0000:00:09.0 {                 /* WAN NIC */
        name GigabitEthernet0/9/0
        num-rx-queues 2
    }
    no-multi-seg                        /* 단일 세그먼트 버퍼 (성능 향상) */
    no-tx-checksum-offload
}

cpu {
    main-core 0
    corelist-workers 1-3               /* 워커 3개 (RSS 큐 분산) */
}

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

plugins {
    plugin default { disable }
    plugin dpdk_plugin.so { enable }
    plugin nat_plugin.so { enable }
    plugin acl_plugin.so { enable }
    plugin ping_plugin.so { enable }
}

L3 라우팅(Routing) + NAT44 설정

# 인터페이스 설정
vpp# set interface ip address GigabitEthernet0/8/0 192.168.1.1/24
vpp# set interface ip address GigabitEthernet0/9/0 203.0.113.1/24
vpp# set interface state GigabitEthernet0/8/0 up
vpp# set interface state GigabitEthernet0/9/0 up

# 기본 라우팅 (WAN 게이트웨이)
vpp# ip route add 0.0.0.0/0 via 203.0.113.254 GigabitEthernet0/9/0

# NAT44 활성화
vpp# nat44 plugin enable sessions 65536
vpp# nat44 add interface address GigabitEthernet0/9/0
vpp# set interface nat44 in GigabitEthernet0/8/0 out GigabitEthernet0/9/0

# 포트 포워딩: 외부 TCP 8080 → 내부 서버 192.168.1.100:80
vpp# nat44 add static mapping local 192.168.1.100 80 \
     external GigabitEthernet0/9/0 8080 tcp

# ACL: 외부에서 들어오는 SSH 차단
vpp# set acl-plugin acl deny proto 6 dport 22
vpp# set acl-plugin interface GigabitEthernet0/9/0 input acl 0

# 검증
vpp# show ip fib
vpp# show nat44 sessions
vpp# show interface addr
vpp# show acl-plugin acl
linux-cp 연동: VPP 라우터에서 호스트 OS의 라우팅 테이블(Routing Table)과 동기화하려면 linux-cp 플러그인을 활성화하세요. create linux-cp lcp GigabitEthernet0/8/0 host-if vpp-lan으로 미러 인터페이스를 생성하면 FRR/BIRD 같은 라우팅 데몬과 연동할 수 있습니다.

첫 플로우 생성과 fast path 전환

NAT44를 실무에서 이해할 때 핵심은 첫 패킷그다음 패킷을 분리해서 보는 것입니다. 첫 패킷은 BIB(Basic Information Base)와 세션을 만들기 위해 상대적으로 무거운 경로를 거치고, 이후 동일 플로우는 이미 만들어진 상태를 조회하는 빠른 경로로 내려갑니다.

NAT44는 첫 패킷에서 상태를 만들고, 이후 패킷은 그 상태를 바로 재사용합니다. 첫 패킷: slow path LAN 클라이언트 192.168.1.10:45000 nat44-in2out-slowpath 정책 확인, 주소 선택 상태 테이블 생성 BIB: 192.168.1.10:45000 → 203.0.113.1:61000 세션과 타이머 생성 ip4-lookup 다음 홉 결정 WAN 송신 203.0.113.1 이후 패킷: fast path nat44-in2out / nat44-out2in 기존 세션 키로 바로 검색 slow path 재진입 없이 주소와 포트만 재작성 운영에서 봐야 할 관찰 포인트 첫 요청 직후 세션 수가 1 증가하고, 이후 부하에서는 fast path 호출 비중이 커져야 합니다. 계속 slow path만 늘어나면 세션 재사용 실패, 비대칭 경로, 짧은 타이머를 의심해야 합니다.
# 1. 상태를 깨끗하게 초기화
vpp# clear runtime
vpp# clear trace
vpp# trace add dpdk-input 10
vpp# show nat44 sessions detail

# 2. 첫 플로우 생성
# LAN 측 클라이언트에서 외부 HTTP 서버로 연결 1개를 생성합니다.
$ curl http://198.51.100.10/

# 3. VPP에서 상태 확인
vpp# show nat44 sessions detail
vpp# show runtime
vpp# show trace

# 4. 같은 연결을 반복해서 부하를 줍니다.
$ for i in $(seq 1 1000); do curl -s http://203.0.113.1:8080/ > /dev/null; done

# 5. fast path 전환 여부 확인
vpp# show nat44 sessions detail
vpp# show runtime
vpp# show errors
시점관찰해야 할 출력해석
트래픽 전show nat44 sessions detail에 세션이 거의 없습니다.기준선입니다. 이전 테스트 세션이 남아 있으면 결과 해석이 흐려집니다.
첫 요청 직후세션이 1개 이상 생기고, trace에 nat44-in2out-slowpath가 보일 수 있습니다.상태 생성이 정상입니다. 여기서 세션이 안 생기면 인터페이스 방향, 주소 풀, ACL 차단을 먼저 의심해야 합니다.
반복 부하 후show runtime에서 fast path 노드 호출이 꾸준히 증가합니다.이미 생성된 세션을 재사용하고 있음을 뜻합니다. 처리량(Throughput)이 올라가도 slow path 비중이 높지 않아야 합니다.
에러 증가 시show errors에 NAT 드롭 또는 no translation 관련 카운터가 보입니다.세션 용량, 번역 주소 고갈, 비대칭 응답 경로를 함께 봐야 합니다.
실전 해석: 첫 요청에서만 느리고 이후 빨라지는 것은 정상입니다. 반대로 모든 패킷이 계속 느리다면 NAT 자체보다도 세션 재사용이 깨지는 경로 비대칭, 너무 짧은 타이머(Timer), 또는 플로우가 매번 다른 큐와 워커로 갈라지는 문제를 먼저 봐야 합니다.

NAT44 세션 테이블 내부 구조

VPP NAT44의 성능을 이해하려면 세션 테이블이 어떻게 구성되는지 알아야 합니다. NAT44는 내부적으로 BIHash(Bounded-index Extensible Hash) 기반의 세션 테이블을 사용하며, 각 세션은 5-튜플(src IP, dst IP, src port, dst port, protocol)을 키로 합니다.

/* src/plugins/nat/nat44-ed/nat44_ed.h — NAT44 ED 세션 구조체 */
typedef struct {
  /* in2out 키 (LAN → WAN) */
  nat_6t_flow_t i2o;
  /* out2in 키 (WAN → LAN) — 역방향 매핑 */
  nat_6t_flow_t o2i;

  u32 flags;              /* 정적/동적, TCP 상태, FIN 감지 등 */
  u32 thread_index;       /* 이 세션을 소유한 워커 스레드 */
  u32 per_user_index;     /* 사용자별 세션 목록 인덱스 */

  /* 타이머: 세션 만료 관리 */
  f64 last_heard;         /* 마지막 패킷 수신 시각 (vlib_time_now) */
  u32 lru_head_index;     /* LRU 리스트 위치 (GC 우선순위) */

  /* 카운터 */
  u64 total_pkts;
  u64 total_bytes;
} snat_session_t;

세션이 생성되면 i2oo2i 양방향 키가 동시에 해시 테이블(Hash Table)에 삽입됩니다. 이후 패킷은 키 조회 한 번으로 변환 정보를 가져오므로, 세션 수가 늘어나도 조회 시간은 O(1)에 근접합니다.

/* src/plugins/nat/nat44-ed/nat44_ed_in2out.c — fast path 핵심 로직 */
static_always_inline int
nat44_ed_in2out_fast_path (snat_main_t *sm, vlib_buffer_t *b,
                           ip4_header_t *ip, u32 rx_fib_index)
{
  clib_bihash_kv_16_8_t kv, value;

  /* 5-튜플로 해시 키 생성 */
  init_ed_k (&kv, ip->src_address, src_port,
             ip->dst_address, dst_port, rx_fib_index, ip->protocol);

  /* BIHash 조회 — 히트하면 바로 변환 */
  if (clib_bihash_search_16_8 (&sm->flow_hash, &kv, &value) == 0)
    {
      snat_session_t *s = pool_elt_at_index (tsm->sessions, value.value);
      s->last_heard = vlib_time_now (vm);
      s->total_pkts++;
      s->total_bytes += vlib_buffer_length_in_chain (vm, b);

      /* IP 주소와 포트 재작성 */
      nat_6t_flow_ip4_translate (sm, b, ip, &s->i2o);
      return 0;  /* fast path 성공 */
    }

  return 1;  /* miss → slow path로 전환 */
}
BIHash 특성: clib_bihash_16_8은 16바이트 키, 8바이트 값의 bounded-index 해시(Hash)로, 버킷 수가 2의 거듭제곱이며 충돌 시 체인이 아닌 페이지(Page) 단위 확장을 합니다. 세션 수가 수백만에 도달해도 캐시(Cache) 미스율이 낮아 일정한 조회 성능을 유지합니다.

NAT44 ED BIHash 충돌 처리와 성능 영향

BIHash는 "bounded-index"라는 이름처럼, 하나의 버킷이 들고 있을 수 있는 KV 페어 수에 상한이 있는 해시입니다. 일반 체이닝 해시맵과 달리 버킷 자체가 (log2_pages, offset) 디스크립터이며 실제 페어는 별도 페이지 풀에 저장됩니다. 충돌이 발생하면 clib_bihash_add_del_16_8가 해당 버킷의 페이지 수를 2배로 키우고(log2_pages++) 페어들을 새 페이지 슬롯에 재분산합니다. 이 구조는 평균 조회는 빠르지만, 충돌이 집중될 때 특정 버킷의 재할당 비용이 튀는 꼬리 지연(tail latency)을 만들어냅니다.

NAT44 ED에서 충돌이 문제되는 시나리오는 세 가지입니다. ① DDoS SYN flood: 공격자가 동일 dst/sport를 유지하며 src IP를 무작위화하면, 일부 버킷이 극단적으로 편중됩니다. ② 대형 CDN 클라이언트: 수만 개 클라이언트가 동일 VIP로 접속하면 (any, vip, *, 443) 조합이 동일 버킷에 몰립니다. ③ 해시 시드 고정: 기본 crc32c 해시는 시드를 바꾸지 않으면 공격자가 충돌 키를 예측해 의도적으로 특정 버킷을 폭파시킬 수 있습니다.

/* BIHash 버킷 충돌과 페이지 확장 — 의사 코드 */
int clib_bihash_add_del_16_8 (clib_bihash_16_8_t *h,
                              clib_bihash_kv_16_8_t *kv, int is_add)
{
  u32 hash = crc32c_u64 (kv->key);          /* 해시 계산 */
  u32 bucket_idx = hash & (h->nbuckets - 1);
  clib_bihash_bucket_t *b = &h->buckets[bucket_idx];

  clib_spinlock_lock (&b->lock);             /* 버킷 단위 잠금 */

  if (b->n_entries >= b->page_size) {        /* 페이지 가득 참 */
    /* → 페이지 수 2배로 확장 (log2_pages++) */
    if (b->log2_pages >= BIHASH_MAX_LOG2_PAGES) {
      clib_spinlock_unlock (&b->lock);
      return -1;  /* 포기 — 세션 생성 실패 */
    }
    bihash_split_page (h, b);               /* O(page_size) 복사 */
  }

  bihash_insert_into_page (h, b, kv);
  clib_spinlock_unlock (&b->lock);
  return 0;
}

위 의사 코드에서 주목할 점은 두 가지입니다. ① 버킷 단위 spinlock은 일반적으로 경합이 적지만, 충돌이 집중된 핫 버킷에서는 경합(Contention)으로 실질적으로 직렬화(Serialization)됩니다. ② 페이지 분할(split_page)은 드문 이벤트지만 발생 시 수백 ns 단위의 지연이 튀고, 동시에 다른 워커가 동일 버킷을 만지면 긴 대기가 발생합니다.

시나리오평균 세션 생성 지연p99.9실효 CPS대응
균등 트래픽180 ns450 ns5.5 M CPS
SYN flood (단일 핫 버킷)220 ns18 μs1.2 M CPS해시 시드 랜덤화
대형 CDN (VIP 편중)190 ns2.4 μs3.8 M CPSdst-hashing 활성화
시드 랜덤화 + dst-hashing175 ns520 ns5.3 M CPS
# 충돌 진단 — BIHash 통계 덤프
vpp# show bihash nat44-ed-sessions verbose
  Heap: base 0x7f..  size 1024M  free 612M
  Bucket util: avg_entries_per_bucket 3.2  max 41  ← 최대값이 크면 편중
  Splits: 1204 (0.3% of inserts)
  Page depth distribution:
    1-page buckets: 65530
    2-page buckets: 6
    3-page buckets: 0

# 대응 1: 해시 시드 재설정 (충돌 공격 완화)
vpp# set nat44 hash-seed random

# 대응 2: 버킷 수 증가 (충돌 확률 ↓, 메모리 ↑)
$ cat /etc/vpp/startup.conf
nat {
  translation hash buckets 524288    # 기본 65536 → 8배
  translation hash memory 2147483648 # 2GB
}

# 대응 3: 워커별 독립 테이블 — 버킷 공유 자체 제거
nat {
  max-translations-per-thread 524288
}

진단 팁: show bihash ... verbosemax_entries_per_bucket이 버킷 상한(기본 4~8)을 자주 초과하면 편중이 심한 것입니다. show errorsnat44-ed-in2out-slowpath bihash add failed가 증가하면 페이지 분할도 실패한 상황이므로, 버킷 수 증가와 시드 랜덤화를 동시에 적용해야 합니다.

NAT44 성능 튜닝 파라미터

운영 환경에서 NAT44의 처리량과 안정성을 좌우하는 핵심 파라미터입니다. 기본값은 소규모 테스트에 맞춰져 있어, 실제 트래픽에서는 반드시 조정해야 합니다.

파라미터CLI 명령기본값권장 (10Gbps급)영향
최대 세션 수nat44 plugin enable sessions N655361048576세션 고갈 시 신규 연결 차단
사용자당 세션nat44 plugin enable user-sessions N1000050000단일 내부 IP의 세션 상한
TCP established 타이머nat set timeouts tcp-established N7440초3600초너무 길면 세션 낭비, 짧으면 끊김
TCP transitory 타이머nat set timeouts tcp-transitory N240초120초SYN/FIN 상태 세션 유지 시간
UDP 타이머nat set timeouts udp N300초120초DNS 등 짧은 플로우에 영향
주소 풀 크기nat44 add address1개가능한 많이포트 고갈 방지 (IP당 ~64K 포트)
# 실전 튜닝 예시: 10Gbps 엣지 라우터
vpp# nat44 plugin enable sessions 1048576
vpp# nat set timeouts tcp-established 3600
vpp# nat set timeouts tcp-transitory 120
vpp# nat set timeouts udp 120

# 주소 풀 확장: WAN IP 여러 개 등록
vpp# nat44 add address 203.0.113.1 - 203.0.113.4

# 워커별 세션 분포 확인 (불균형 → RSS 설정 점검)
vpp# show nat44 sessions count
vpp# show nat44 summary

# 세션 GC(Garbage Collection) 동작 확인
vpp# show nat44 sessions detail | grep "last heard"
포트 고갈 주의: NAT44는 IP당 최대 약 64,000개의 포트를 사용할 수 있습니다. 단일 외부 IP로 100,000 세션 이상을 처리해야 한다면 반드시 주소 풀을 확장하세요. show errors에서 nat44-ed-in2out no free external addr 카운터가 증가하면 풀 고갈 징후입니다.

실전 예제: memif 서비스 체이닝

VPP의 memif(memory interface)는 공유 메모리 기반 인터페이스로, VPP 인스턴스 간 또는 VPP-DPDK 앱 간 제로카피 패킷 전달을 제공합니다. 이를 활용하면 여러 네트워크 기능(방화벽(Firewall), DPI, NAT 등)을 체이닝하여 서비스 펑션 체인(SFC)을 구성할 수 있습니다.

memif 기반 서비스 펑션 체이닝 (SFC) NIC RX DPDK VPP #1 (분류기) dpdk-input classify-table memif1/0 → tx memif2/0 → tx VPP #2 (방화벽) memif1/0 → ACL memif3/0 → tx VPP #3 (DPI+NAT) memif2/0 → DPI nat44 → memif4/0 VPP #4 (이그레스) memif3/0 + memif4/0 ip4-rewrite dpdk-output NIC TX DPDK memif1 memif2 memif3 memif4 memif: 공유 메모리 기반 제로카피 인터페이스 — VPP 인스턴스 간 최대 100Gbps+ 전달 가능 각 VPP 인스턴스는 독립 프로세스로 실행되며, 장애 격리와 독립 스케일링 가능

memif 소켓(Socket) 및 인터페이스 구성

# VPP #1 (분류기) — memif master 역할
vpp1# create memif socket id 1 filename /run/vpp/memif-fw.sock
vpp1# create memif socket id 2 filename /run/vpp/memif-dpi.sock
vpp1# create interface memif id 0 socket-id 1 master
vpp1# create interface memif id 0 socket-id 2 master
vpp1# set interface state memif1/0 up
vpp1# set interface state memif2/0 up

# VPP #2 (방화벽) — memif slave 역할
vpp2# create memif socket id 1 filename /run/vpp/memif-fw.sock
vpp2# create memif socket id 3 filename /run/vpp/memif-egress-fw.sock
vpp2# create interface memif id 0 socket-id 1 slave
vpp2# create interface memif id 0 socket-id 3 master
vpp2# set interface state memif1/0 up
vpp2# set interface state memif3/0 up

# L2 cross-connect (방화벽 통과 후 이그레스로)
vpp2# set interface l2 xconnect memif1/0 memif3/0
vpp2# set interface l2 xconnect memif3/0 memif1/0

# VPP #3 (DPI+NAT) — 유사하게 memif slave로 구성
vpp3# create memif socket id 2 filename /run/vpp/memif-dpi.sock
vpp3# create interface memif id 0 socket-id 2 slave
# ... NAT 설정 생략 (L3 NAT 예제 참조)

# VPP #4 (이그레스) — 여러 memif에서 수신, DPDK로 송신
vpp4# create memif socket id 3 filename /run/vpp/memif-egress-fw.sock
vpp4# create memif socket id 4 filename /run/vpp/memif-egress-dpi.sock
vpp4# create interface memif id 0 socket-id 3 slave
vpp4# create interface memif id 0 socket-id 4 slave
소켓 권한: memif 소켓 파일은 양쪽 VPP 프로세스(Process)가 모두 접근할 수 있어야 합니다. 서로 다른 사용자로 실행 시 chmod 770 또는 공통 그룹(gid vpp) 설정이 필요합니다. 소켓 경로의 디렉터리에도 실행 권한이 있어야 합니다.
memif 성능 팁: ring-size 2048buffer-size 2048로 생성하면 64B 패킷 기준 단일 memif 쌍에서 30Mpps 이상의 처리량을 달성할 수 있습니다. show memif로 링 사용률과 오류 카운터를 확인하세요.

memif 링 동작과 병목(Bottleneck) 확인 방법

memif는 단순한 "가상 케이블"이 아니라, 두 프로세스가 공유 메모리 위에서 서술자 링(descriptor ring)을 교환하는 구조입니다. 성능 병목이 생기면 대부분 패킷 처리 로직보다 링 고갈, peer 지연(Latency), 큐 배치 불일치에서 먼저 징후가 나타납니다.

memif는 공유 메모리의 두 개 링을 통해 동작합니다. VPP #1 (master) memif1/0 RX queue memif1/0 TX queue 공유 메모리 영역 S2M ring (slave → master) M2S ring (master → slave) 각 슬롯은 buffer offset, length, flags를 가리킵니다. 패킷 자체를 다시 복사하지 않고, 어느 버퍼를 읽을지 기술합니다. VPP #2 (slave) memif1/0 RX queue memif1/0 TX queue 한쪽 프로세스가 링을 제때 비우지 못하면 다른 쪽은 no free tx slots나 큐 정체로 바로 드러납니다.
# 보다 구체적인 생성 예시
vpp1# create memif socket id 1 filename /run/vpp/memif-sfc.sock
vpp1# create interface memif id 0 socket-id 1 master ring-size 1024 buffer-size 2048
vpp1# set interface state memif1/0 up

vpp2# create memif socket id 1 filename /run/vpp/memif-sfc.sock
vpp2# create interface memif id 0 socket-id 1 slave ring-size 1024 buffer-size 2048
vpp2# set interface state memif1/0 up

# 병목 확인
vpp1# show memif
vpp1# show hardware-interfaces memif1/0
vpp1# show interface rx-placement
vpp1# show runtime
vpp1# show errors
관찰 값의미우선 점검 항목
connected 플래그 미표시핸드셰이크 실패소켓 경로, 권한, master/slave 역할
RX ring 사용률만 높음수신 측이 처리 속도를 따라가지 못함워커 배치, downstream 노드 clocks/call
TX 슬롯 부족peer가 링을 제때 비우지 못함상대 프로세스 CPU 핀닝, 큐 정체
에러는 없지만 처리량 저하링 자체보다 feature/lookup 비용 문제show runtime의 핫 노드 확인
디버깅(Debugging) 순서: memif 장애는 먼저 연결 상태와 링 고갈 여부를 보고, 그다음에 그래프 노드 비용을 봐야 합니다. 곧바로 패킷 페이로드(Payload)를 캡처하는 것보다 show memifshow runtime을 함께 보는 편이 훨씬 빠릅니다.

memif 분류기(Classifier) 구성

서비스 체이닝에서 핵심은 어떤 트래픽을 어느 체인으로 보낼지 결정하는 분류기입니다. VPP의 classify 테이블은 ACL과 달리 패킷 헤더의 임의 오프셋(Offset)을 마스크 매칭할 수 있어 유연한 분류가 가능합니다.

# 분류 테이블 생성: TCP 트래픽(proto=6)을 방화벽 체인으로
# mask: IP 프로토콜 필드(offset 23)만 검사
vpp1# classify table mask l3 ip4 proto

# 매치 규칙: TCP(0x06) → memif1/0 (방화벽 체인)
vpp1# classify session acl-hit-next permit table-index 0 \
      match l3 ip4 proto 6 action set-ip4-fib-id 0
vpp1# set interface input acl intfc GigabitEthernet0/8/0 ip4-table 0

# 비TCP 트래픽 → memif2/0 (DPI+NAT 체인)으로 L2 xconnect
vpp1# set interface l2 xconnect GigabitEthernet0/8/0 memif2/0
vpp1# set interface l2 xconnect memif2/0 GigabitEthernet0/8/0

보다 세밀한 분류가 필요하면 다단계 테이블 체인을 구성할 수 있습니다.

# 1단계: 프로토콜 분류 (TCP vs UDP vs 기타)
vpp1# classify table mask l3 ip4 proto

# 2단계: TCP 중 대상 포트별 세분화 (80/443 → 웹, 나머지 → 기본)
vpp1# classify table mask l4 dst_port next-table 0
vpp1# classify session table-index 1 match l4 dst_port 80 \
      action set-ip4-fib-id 1
vpp1# classify session table-index 1 match l4 dst_port 443 \
      action set-ip4-fib-id 1

# FIB 1은 memif1/0(웹 방화벽)으로 라우팅,
# FIB 0(기본)은 memif2/0(DPI)으로 라우팅

memif vs 다른 가상 인터페이스 비교

VPP에서 인스턴스 간 패킷을 전달하는 방법은 여러 가지가 있습니다. 각 방법의 특성과 적합한 용도를 비교합니다.

인터페이스카피 횟수64B 처리량지연적합한 환경
memif제로카피30+ Mpps~1μsVPP ↔ VPP, VPP ↔ DPDK 앱
veth pair2회 (커널 경유)~2 Mpps~10μsVPP ↔ 커널 네임스페이스(Namespace)
TAP v21회 (virtio)~5 Mpps~5μsVPP ↔ 호스트 OS
AF_XDP제로카피 가능~15 Mpps~2μsVPP ↔ XDP 프로그램
vhost-user1회 (virtio)~8 Mpps~3μsVPP ↔ VM (QEMU)
memif 선택 기준: 동일 호스트에서 VPP 인스턴스끼리 연결하거나, libmemif를 사용하는 커스텀 DPDK 앱과 연결할 때는 memif가 압도적으로 유리합니다. 반면, 커널 네트워크 스택(Network Stack)이나 컨테이너(Container) 네임스페이스와 연동해야 한다면 TAP v2나 AF_XDP를 고려하세요.

memif 공유 메모리 내부 구현

memif의 제로카피가 실제로 어떻게 동작하는지, 핵심 자료구조를 살펴봅니다.

/* src/plugins/memif/memif.h — 서술자 링 구조 */
typedef struct {
  u16 flags;           /* MEMIF_DESC_FLAG_NEXT: 체인 다음 버퍼 존재 */
  u32 region;          /* 공유 메모리 영역 인덱스 */
  u32 offset;          /* 영역 내 버퍼 시작 오프셋 */
  u32 length;          /* 패킷 데이터 길이 */
  u8  metadata[0];     /* 가변 길이 메타데이터 */
} memif_desc_t;

/* 링 구조: head/tail 포인터로 생산자/소비자 패턴 구현 */
typedef struct {
  volatile u16 head;   /* 생산자가 전진 (새 서술자 추가) */
  volatile u16 tail;   /* 소비자가 전진 (서술자 소비) */
  u16 cookie;           /* 버전 검증용 매직 넘버 */
  u16 flags;            /* MEMIF_RING_FLAG_MASK_INT */
  memif_desc_t desc[0]; /* ring-size 개의 서술자 배열 */
} memif_ring_t;
memif 서술자 링: head/tail 기반 생산자-소비자 패턴 M2S ring (master → slave 방향, ring-size=8 예시) desc[0] desc[1] desc[2] desc[3] desc[4] desc[5] desc[6] desc[7] tail=2 (소비자: slave) head=5 (생산자: master) 이미 소비된 슬롯 (재사용 가능) 대기 중인 패킷 (tail ≤ i < head) 빈 슬롯 동작 원리: master가 head를 전진시키며 서술자를 채우고, slave가 tail을 전진시키며 소비합니다. head == tail이면 링이 비었고, (head+1) % size == tail이면 가득 찼습니다. 서술자는 실제 패킷 데이터가 아닌 공유 메모리 영역 내 offset만 가리키므로 복사가 없습니다.
ring-size 선택: 링 크기가 너무 작으면 burst 트래픽에서 바로 가득 차서 드롭이 발생하고, 너무 크면 메모리를 낭비합니다. 일반적으로 ring-size 1024(기본값)는 10Gbps 이하에서 충분하며, 25Gbps 이상에서는 ring-size 2048 또는 ring-size 4096을 고려하세요.