VXLAN/GENEVE 오버레이(Overlay) 네트워크

Linux 커널 VXLAN/GENEVE 오버레이 네트워크 심층 분석: VXLAN(RFC 7348) UDP 터널(Tunnel)링, GENEVE(RFC 8926) 가변 TLV 캡슐화(Encapsulation), VTEP 구성과 FDB 학습, 멀티캐스트/유니캐스트 BUM 복제, BGP EVPN 연동, 커널 내부 데이터 경로와 핵심 자료구조, NIC 오프로드 및 성능 튜닝까지 실무 관점으로 다룹니다.

전제 조건: 네트워크 스택(Network Stack), Linux Bridge 문서를 먼저 읽으세요. VXLAN은 L3 위에 L2 오버레이를 구성하는 터널링 기술이고, GENEVE는 VXLAN의 확장형 차세대 프로토콜입니다.
일상 비유: VXLAN은 서로 다른 건물(데이터센터) 사이를 터널로 연결해 같은 층처럼 보이게 만드는 것과 같습니다. GENEVE는 그 터널에 짐표(TLV 옵션)를 자유롭게 붙일 수 있는 확장형 터널입니다.

핵심 요약

  • VXLAN (24비트 VNI) — 최대 1,677만 개 논리 네트워크, UDP 4789 포트, L2 over L3 오버레이 네트워크의 기반입니다. RFC 7348로 표준화되어 있습니다.
  • GENEVE (RFC 8926) — VXLAN/NVGRE/STT의 장점을 통합하고 가변 길이 TLV 옵션으로 확장성을 극대화한 차세대 오버레이 프로토콜입니다. UDP 6081 포트를 사용합니다.
  • VTEP (Virtual Tunnel Endpoint) — 오버레이 터널의 양 끝점으로, 캡슐화/역캡슐화(Decapsulation)를 수행합니다. Linux에서는 vxlan/geneve 타입 netdev가 VTEP 역할을 합니다.
  • FDB 학습 — VXLAN/GENEVE에서 원격 MAC-VTEP 매핑(Mapping)을 학습하는 방식으로, 동적(data plane), 정적(수동), 컨트롤 플레인(BGP EVPN) 방식이 있습니다.
  • NIC 오프로드 — 최신 NIC(mlx5, ice 등)은 VXLAN/GENEVE 캡슐화/역캡슐화를 하드웨어에서 수행하여 CPU 부하를 크게 줄입니다.

단계별 이해

  1. VXLAN 오버레이 생성
    ip link add vxlan100 type vxlan id 100 dstport 4789 local 10.0.0.1 remote 10.0.0.2로 유니캐스트 VXLAN 터널을 생성합니다.
  2. 오버레이 브리지(Bridge) 구성
    VXLAN 디바이스를 브리지에 연결하고 bridge fdb append 00:00:00:00:00:00 dev vxlan100 dst <VTEP>로 BUM 트래픽 복제 대상을 등록합니다.
  3. GENEVE 터널 생성
    ip link add geneve100 type geneve id 100 remote 10.0.0.2 dstport 6081로 GENEVE 터널을 구성합니다.
  4. MTU 및 오프로드 확인
    물리 NIC MTU를 점보 프레임(9000)으로 설정하고, ethtool --show-tunnels eth0으로 터널 오프로드 상태를 확인합니다.
관련 표준: RFC 7348 (VXLAN), RFC 8926 (GENEVE), RFC 7432 (BGP EVPN), RFC 8365 (EVPN Overlay), RFC 9136 (VXLAN-GPE) — 이 문서에서 다루는 오버레이 네트워크 프로토콜의 핵심 규격입니다. 종합 목록은 참고자료 — 표준 & 규격 섹션을 참고하세요.

오버레이 네트워크 개요

오버레이 네트워크(Overlay Network)는 기존 물리(언더레이) 네트워크 위에 논리적인 가상 네트워크를 구성하는 기술입니다. 원본 이더넷 프레임을 UDP 등으로 캡슐화하여 L3 네트워크를 통해 전달하므로, 물리적 위치와 무관하게 동일한 L2 도메인을 구성할 수 있습니다.

오버레이 네트워크가 필요한 이유

전통적인 VLAN(802.1Q)은 12비트 VID로 최대 4,094개의 논리 네트워크만 지원합니다. 대규모 데이터센터와 클라우드 환경에서는 수만~수백만 테넌트를 격리(Isolation)해야 하므로 VLAN만으로는 한계가 있습니다. 또한 VLAN은 L2 도메인 내에서만 동작하기 때문에 L3 경계를 넘어서는 네트워크 확장이 불가능합니다.

한계VLAN (802.1Q)VXLAN/GENEVE 오버레이
네트워크 ID 공간4,094개 (12비트)16,777,216개 (24비트 VNI)
L3 경계 횡단불가 (L2 브로드캐스트 도메인 한정)가능 (UDP 캡슐화로 IP 라우팅(Routing) 통과)
멀티테넌트 격리수천 규모 한계수백만 규모 지원
데이터센터 간 연결DCI 전용 장비 필요IP 연결만 있으면 가능
확장성STP 도메인 크기 제약IP ECMP로 수평 확장
주요 사용처: VXLAN/GENEVE 오버레이는 Kubernetes/Docker 컨테이너(Container) 네트워크(Flannel, Calico, Cilium), OpenStack Neutron, VMware NSX, AWS VPC, OVN/OVS 기반 SDN에서 핵심 데이터플레인으로 사용됩니다.

Linux 커널은 drivers/net/vxlan/drivers/net/geneve.c에서 각각 VXLAN과 GENEVE를 구현합니다. 두 프로토콜 모두 struct net_device 기반의 가상 네트워크 디바이스로 동작하며, ip link add 명령으로 생성합니다. 물리 NIC의 터널 오프로드를 활용하면 CPU 부담 없이 와이어레이트에 가까운 성능을 달성할 수 있습니다.

오버레이 네트워크 캡슐화 개념 VTEP A (10.0.0.1) VM/컨테이너 오버레이 IP: 10.200.0.1 캡슐화 언더레이 네트워크 (L3) Outer IP + UDP(4789/6081) + VXLAN/GENEVE Header + Inner Ethernet Frame 역캡슐화 VTEP B (10.0.0.2) VM/컨테이너 오버레이 IP: 10.200.0.2 VXLAN (RFC 7348) 고정 8B 헤더, UDP 4789 24비트 VNI, 광범위한 HW 지원 Flannel, EVPN, 대부분의 SDN GENEVE (RFC 8926) 가변 헤더(TLV), UDP 6081 24비트 VNI + 확장 옵션 OVS, Cilium, AWS VPC, NSX
오버레이 네트워크: VTEP 간 캡슐화/역캡슐화로 L3 위에 L2 네트워크 구성

VLAN 기초 — 오버레이 이해를 위한 배경

VLAN(Virtual LAN, IEEE 802.1Q)은 이더넷 프레임에 4바이트 태그를 삽입하여 하나의 물리적 네트워크를 여러 논리적 브로드캐스트 도메인으로 분리하는 기술입니다. 12비트 VLAN ID(VID)로 최대 4,094개의 세그먼트를 지원합니다. VXLAN/GENEVE 오버레이를 이해하려면 VLAN의 기본 개념 — 태깅, 트렁킹, 브로드캐스트 도메인 분리 — 을 먼저 파악하는 것이 중요합니다.

오버레이 네트워크에서 VLAN은 여전히 중요한 역할을 합니다. 언더레이 네트워크의 트래픽 분리, VLAN-aware 브리지를 통한 VID-VNI 매핑(bridge vlan tunnel), 그리고 VTEP이 연결된 물리 네트워크의 트렁크 구성 등에서 VLAN 지식이 필수적입니다. 다만 대규모 멀티테넌트 환경에서는 VLAN의 4,094개 제한이 병목(Bottleneck)이 되므로, 24비트 VNI를 제공하는 VXLAN/GENEVE로 확장합니다.

상세 문서: VLAN(802.1Q) 프레임 구조, 태깅 처리, QoS, Q-in-Q 등의 상세 내용은 802.1Q VLAN 페이지(Page)를 참조하세요.

Bridge VLAN Filtering — 오버레이 연동 기초

VLAN-aware 브리지는 포트별로 허용 VLAN을 필터링하며, 각 포트에 PVID(Port VLAN ID)와 tagged/untagged 설정을 적용합니다. 오버레이 네트워크에서 Bridge VLAN Filtering은 특히 중요한데, VXLAN external 모드에서 VLAN-Tunnel 매핑(bridge vlan tunnel add)을 통해 VID와 VNI를 1:1로 자동 변환할 수 있기 때문입니다. 하나의 VXLAN 디바이스로 여러 VNI를 처리하는 멀티테넌트 구성의 핵심입니다.

Bridge VLAN Filtering의 기본 동작은 물리 스위치의 access/trunk 포트 개념과 동일합니다. Ingress 경로에서 untagged 프레임에 PVID를 할당하고, VLAN 필터(br_allowed_ingress)를 통과한 프레임만 FDB 학습과 포워딩을 진행합니다. Egress 경로에서는 출력 포트의 VLAN 설정에 따라 태그를 유지하거나 제거합니다.

상세 문서: Bridge VLAN Filtering, PVID 설정, STP, IGMP Snooping 등의 상세 내용은 Linux Bridge 페이지를 참조하세요.

VXLAN (Virtual Extensible LAN)

VXLAN(RFC 7348)은 L2 이더넷 프레임을 UDP로 캡슐화하여 L3 네트워크 위에 가상 L2 오버레이를 구성하는 네트워크 가상화(Virtualization) 기술입니다. 기존 VLAN이 12비트 VID로 4,094개의 세그먼트만 지원하는 한계를 극복하여, 24비트 VNI로 최대 16,777,216 (224)개의 논리 네트워크를 지원합니다. 데이터센터의 멀티테넌트 환경, 컨테이너 오버레이 네트워크(Flannel, Calico VXLAN 모드), SDN 솔루션의 핵심 기반 기술입니다.

관련 RFC: RFC 7348 (VXLAN), RFC 7432 (BGP EVPN), RFC 8365 (EVPN Overlay), RFC 9136 (VXLAN-GPE). Linux 커널의 VXLAN 구현은 drivers/net/vxlan/ 디렉토리에 위치하며, vxlan_core.c가 핵심 로직을 담당합니다.

VXLAN 아키텍처와 용어

VXLAN 오버레이/언더레이 아키텍처 Overlay Network (VNI 100: 10.200.0.0/24) VM-A / Container 10.200.0.1 MAC: aa:bb:cc:11:22:33 VM-B / Container 10.200.0.2 MAC: dd:ee:ff:44:55:66 L2 연결 (동일 브로드캐스트 도메인) VTEP-A local: 192.168.1.10 vxlan100 + br-vxlan VTEP-B local: 192.168.1.20 vxlan100 + br-vxlan Underlay Network (L3 IP: 192.168.1.0/24) 물리 스위치 / 라우터 — IP 라우팅으로 VTEP 간 연결 UDP:4789 VXLAN Tunnel Outer IP + UDP + VXLAN Header + Inner Frame
용어설명
VNI (VXLAN Network Identifier)24비트 논리 네트워크 식별자 (0~16,777,215). VLAN ID에 해당하며, 동일 VNI = 동일 L2 도메인
VTEP (VXLAN Tunnel Endpoint)VXLAN 캡슐화/역캡슐화를 수행하는 엔드포인트. Linux에서는 vxlan 타입 net_device
Overlay NetworkVXLAN으로 구성된 가상 L2 네트워크. 테넌트/애플리케이션별 격리
Underlay NetworkVTEP 간 IP 연결을 제공하는 물리적 L3 네트워크
BUM 트래픽Broadcast, Unknown unicast, Multicast — 목적지를 모르는 트래픽. 멀티캐스트 그룹 또는 헤드엔드 복제로 처리
FDB (Forwarding Database)내부 MAC 주소 → 원격 VTEP IP 매핑 테이블. bridge fdb로 관리
Headend Replication멀티캐스트 없이 BUM 트래픽을 알려진 모든 VTEP에 유니캐스트로 복제 전송
오버레이 vs 언더레이: VXLAN의 핵심은 분리(separation)입니다. 오버레이 네트워크의 VM/컨테이너는 물리적 토폴로지(Topology)를 인식하지 않으며, 동일 VNI 내에서 마치 같은 L2 스위치에 연결된 것처럼 통신합니다. 언더레이 네트워크는 순수 IP 라우팅만 처리하면 되므로, 물리 네트워크 설계가 크게 단순화됩니다.

VXLAN 패킷(Packet) 포맷

구간크기핵심 필드설명
Outer Ethernet14 bytesdst/src MAC, EtherType언더레이 다음 홉으로 전달되는 L2 헤더
Outer IP20 bytes (IPv4) / 40 bytes (IPv6)src/dst IP, Protocol=17VTEP 간 L3 경로 식별
UDP8 bytessrc port(해시(Hash)), dst port=4789ECMP 분산을 위한 엔트로피 + VXLAN 식별
VXLAN Header8 bytesFlags, VNI(24)오버레이 네트워크 식별자 전달
Inner Ethernet14 bytes + payload원본 dst/src MAC, EtherType테넌트 L2 프레임 원형 보존 (FCS 제외)
Outer Ethernet 14 bytes Outer IP 20/40 bytes UDP 8 bytes VXLAN Header Flags + VNI(24) Inner Ethernet + Payload 원본 L2 프레임 Outer UDP dst port: 4789 (IANA 기본값), src port: hash 기반(ECMP 분산) VXLAN Header (8 bytes) Byte 0: R|R|R|R|I|R|R|R + Reserved(24) Byte 4~6: VNI (24 bits), Byte 7: Reserved (8 bits) I(bit 4)=1 이어야 VNI 유효. Reserved는 0으로 설정. 총 오버헤드: IPv4 50 bytes / IPv6 70 bytes
소스 포트 엔트로피: VXLAN 외부 UDP 소스 포트는 내부 프레임의 L2/L3/L4 헤더를 해시하여 결정합니다. 이는 언더레이 네트워크의 ECMP(Equal-Cost Multi-Path) 라우팅에서 여러 경로로 트래픽을 분산시키는 데 필수적입니다. Linux에서는 skb_get_hash()를 사용하며, 포트 범위는 커널의 net.ipv4.ip_local_port_range 설정을 따릅니다.

커널 내부 자료구조

/* include/net/vxlan.h — VXLAN 핵심 자료구조 */

/* VXLAN 헤더 (on-wire 형식) */
struct vxlanhdr {
    __be32 vx_flags;   /* VXLAN_HF_VNI (bit 27, 0x08000000) 설정 시 VNI 유효 */
    __be32 vx_vni;     /* 상위 24비트: VNI, 하위 8비트: reserved */
};

/* VXLAN 디바이스 구성 파라미터 */
struct vxlan_config {
    union vxlan_addr  remote_ip;     /* 원격 VTEP IP (유니캐스트 시) */
    union vxlan_addr  saddr;         /* 로컬 VTEP IP */
    __be32            vni;           /* VXLAN Network Identifier */
    int               remote_ifindex;/* 송신 디바이스 ifindex */
    __be16            dst_port;      /* UDP 목적지 포트 (기본 4789) */
    __u16             port_min;      /* 소스 포트 범위 하한 */
    __u16             port_max;      /* 소스 포트 범위 상한 */
    __u8              tos;           /* 외부 IP TOS (1=inherit) */
    __u8              ttl;           /* 외부 IP TTL (0=inherit) */
    __u32             flags;         /* VXLAN_F_* 플래그 */
    __u32             label;         /* IPv6 flow label */
    unsigned short    age_interval;  /* FDB 엔트리 만료 시간 (초) */
    unsigned int      addrmax;       /* FDB 최대 엔트리 수 */
    __u8              df;            /* DF 비트 제어 */
};

/* VXLAN 디바이스 — net_device의 private 데이터 */
struct vxlan_dev {
    struct hlist_node  hlist4;      /* vxlan_net의 해시 테이블 (IPv4) */
    struct hlist_node  hlist6;      /* vxlan_net의 해시 테이블 (IPv6) */
    struct list_head   next;        /* 전역 VXLAN 디바이스 리스트 */
    struct vxlan_sock  __rcu *vn4_sock; /* IPv4 UDP 소켓 */
    struct vxlan_sock  __rcu *vn6_sock; /* IPv6 UDP 소켓 */
    struct net_device  *dev;        /* 연관된 net_device */
    struct vxlan_rdst  default_dst; /* 기본 원격 목적지 */
    struct timer_list  age_timer;   /* FDB 에이징 타이머 */
    struct hlist_head  fdb_head[FDB_HASH_SIZE]; /* FDB 해시 테이블 */
    unsigned int       addrcnt;     /* 현재 FDB 엔트리 수 */
    struct vxlan_config cfg;        /* 디바이스 구성 */
};

/* VXLAN FDB 엔트리 — 내부 MAC → 원격 VTEP 매핑 */
struct vxlan_fdb {
    struct hlist_node hlist;        /* vxlan_dev->fdb_head 해시 체인 */
    struct rcu_head   rcu;          /* RCU 해제 */
    unsigned long     updated;      /* 마지막 갱신 jiffies (에이징 기준) */
    unsigned long     used;         /* 마지막 참조 jiffies */
    struct list_head  remotes;      /* vxlan_rdst 리스트 (다중 VTEP 가능) */
    u8                eth_addr[ETH_ALEN]; /* 내부 MAC 주소 */
    u16               state;        /* NUD_REACHABLE, NUD_STALE 등 */
    __be32            vni;          /* collect_metadata 모드 시 VNI */
    u16               flags;        /* NTF_SELF, NTF_VXLAN_ADDED 등 */
};

/* VXLAN 원격 목적지 (하나의 FDB 엔트리에 여러 VTEP 가능) */
struct vxlan_rdst {
    union vxlan_addr  remote_ip;    /* 원격 VTEP IP */
    __be16            remote_port;  /* 원격 UDP 포트 */
    __be32            remote_vni;   /* 원격 VNI (보통 동일) */
    u32               remote_ifindex; /* 송신 인터페이스 */
    struct list_head  list;         /* vxlan_fdb->remotes 리스트 */
    struct rcu_head   rcu;
};

/* VXLAN UDP 소켓 — 여러 VXLAN 디바이스가 공유 가능 */
struct vxlan_sock {
    struct hlist_node  hlist;      /* 전역 소켓 해시 */
    struct socket      *sock;      /* UDP 소켓 */
    struct hlist_head  vni_list[VNI_HASH_SIZE]; /* VNI별 디바이스 룩업 */
    refcount_t        refcnt;     /* 참조 카운트 */
    u32               flags;      /* VXLAN_F_* 플래그 */
};
소켓(Socket) 공유: 동일 UDP 포트를 사용하는 여러 VXLAN 디바이스는 하나의 vxlan_sock을 공유합니다. 수신 시 vxlan_sock->vni_list를 통해 VNI별로 올바른 vxlan_dev를 찾아 역캡슐화합니다. 이 설계 덕분에 수천 개의 VNI를 하나의 UDP 소켓으로 효율적으로 처리할 수 있습니다.

VXLAN 디바이스 플래그 (VXLAN_F_*)

플래그ip link 옵션설명
VXLAN_F_LEARN(기본 on)수신 패킷의 inner src MAC → outer src IP를 FDB에 자동 학습
VXLAN_F_PROXYproxyARP/NDP proxy — FDB에 있는 MAC의 ARP 요청에 VTEP이 대리 응답
VXLAN_F_L2MISSl2missFDB miss 시 netlink 알림 발생 (외부 컨트롤러 연동)
VXLAN_F_L3MISSl3missARP 테이블 miss 시 netlink 알림 발생
VXLAN_F_RSCrscRoute Short Circuit — ARP 응답의 MAC을 FDB에 학습
VXLAN_F_COLLECT_METADATAexternal메타데이터 모드 — tc/BPF에서 VNI/목적지를 동적 결정
VXLAN_F_UDP_ZERO_CSUM6_TXudp6zerocsumtxIPv6 송신 시 UDP checksum을 0으로 설정
VXLAN_F_UDP_ZERO_CSUM6_RXudp6zerocsumrxIPv6 수신 시 UDP checksum 0을 허용
VXLAN_F_GBPgbpGroup Based Policy — VXLAN 헤더에 정책 태그 삽입
VXLAN_F_GPEgpeGeneric Protocol Extension — 내부 프로토콜 지정 가능

VXLAN 송신 경로 (Tx Path)

VXLAN 패킷 송신 흐름 (vxlan_xmit) 애플리케이션 / socket TCP/IP 스택 → 내부 이더넷 프레임 생성 vxlan_xmit() ndo_start_xmit 콜백 — dst MAC으로 FDB 룩업 FDB 룩업 dst MAC HIT vxlan_rdst 원격 VTEP IP 획득 MISS BUM 처리 default_dst flood vxlan_xmit_one() 목적지 VTEP별 캡슐화 수행 캡슐화 단계 소스 포트 해시 계산 VXLAN 헤더 push (flags I=1 + VNI) UDP 헤더 push (dst_port 4789) 외부 IP 경로 ip_route_output udp_tunnel_xmit_skb() → ip_local_out() → NF_INET_LOCAL_OUT → 언더레이 라우팅 ip6 경로: udp_tunnel6_xmit_skb() / ip6_route_output() 물리 NIC → 와이어 언더레이 네트워크를 통해 원격 VTEP 전송 최종 캡슐화 구조: [ 외부 Ethernet | 외부 IP | UDP (4789) | VXLAN 헤더 (8B, VNI) | 내부 Ethernet | 내부 IP | Payload ]
/* drivers/net/vxlan/vxlan_core.c — VXLAN 송신 메인 */
static netdev_tx_t vxlan_xmit(struct sk_buff *skb,
                               struct net_device *dev)
{
    struct vxlan_dev *vxlan = netdev_priv(dev);
    struct ethhdr *eth;
    struct vxlan_fdb *f;
    struct vxlan_rdst *rdst, *fdst;

    eth = eth_hdr(skb);

    /* 1. 내부 프레임의 dst MAC으로 FDB 룩업 */
    f = vxlan_find_mac(vxlan, eth->h_dest, vni);

    if (f) {
        /* FDB hit — 알려진 유니캐스트 목적지 */
        fdst = vxlan_fdb_find_rdst(f, &remote_ip, port, vni, ifindex);
        vxlan_xmit_one(skb, dev, vni, fdst, did_rsc);
    } else if (is_multicast_ether_addr(eth->h_dest)) {
        /* 멀티캐스트/브로드캐스트 — 모든 원격 VTEP에 복제 전송 */
        vxlan_xmit_one(skb, dev, vni, &vxlan->default_dst, 0);
    } else {
        /* Unknown unicast (FDB miss) */
        if (vxlan->cfg.flags & VXLAN_F_L2MISS)
            vxlan_fdb_miss(vxlan, eth->h_dest); /* netlink 알림 */
        /* default_dst의 remote 리스트로 flood */
        vxlan_xmit_one(skb, dev, vni, &vxlan->default_dst, 0);
    }
    return NETDEV_TX_OK;
}

/* 단일 VTEP으로의 캡슐화 및 송신 */
static void vxlan_xmit_one(
    struct sk_buff *skb,
    struct net_device *dev,
    __be32 default_vni,
    struct vxlan_rdst *rdst,
    bool did_rsc)
{
    struct vxlan_dev *vxlan = netdev_priv(dev);
    struct vxlanhdr *vxh;
    struct rtable *rt;
    __be16 src_port, dst_port;
    __be32 vni;

    /* 소스 포트: 내부 프레임 해시 기반 (ECMP 분산) */
    src_port = udp_flow_src_port(dev_net(dev), skb,
                                  vxlan->cfg.port_min,
                                  vxlan->cfg.port_max, true);
    dst_port = rdst->remote_port ? : vxlan->cfg.dst_port;
    vni = rdst->remote_vni ? : default_vni;

    /* IP 라우팅 조회 */
    rt = vxlan_get_route(vxlan, dev, sock4, skb, ...);

    /* VXLAN 헤더 추가 */
    vxh = (struct vxlanhdr *)__skb_push(skb, sizeof(*vxh));
    vxh->vx_flags = htonl(VXLAN_HF_VNI);
    vxh->vx_vni = vxlan_vni_field(vni);

    /* UDP 터널 캡슐화 + IP 송신 */
    udp_tunnel_xmit_skb(rt, sock4->sk, skb,
                        saddr, rdst->remote_ip.sin.sin_addr.s_addr,
                        tos, ttl, df, src_port, dst_port,
                        xnet, !net_eq(vxlan->net, dev_net(dev)));
}

VXLAN 수신 경로 (Rx Path)

물리 NIC → NAPI → netif_receive_skb()
    ↓
IP 프로토콜 처리 → UDP 디멀티플렉싱
    ↓
udp_rcv() → udp_queue_rcv_skb()
    ↓
vxlan_rcv() ← vxlan_sock의 UDP 소켓 콜백 (encap_rcv)
    ├─ 1. VXLAN 헤더 유효성 검사 (I 플래그, 최소 길이)
    ├─ 2. VNI 추출 → vxlan_vs_find_vni()로 vxlan_dev 룩업
    ├─ 3. VXLAN 헤더 pull (skb->data를 inner frame으로 이동)
    ├─ 4. inner src MAC 학습 (VXLAN_F_LEARN 설정 시)
    │      vxlan_snoop() → FDB에 src MAC → outer src IP 매핑 추가/갱신
    ├─ 5. skb 메타데이터 설정
    │      skb->dev = vxlan_dev->dev
    │      skb->protocol = eth_type_trans()
    └─ 6. netif_rx() → 내부 프레임을 네트워크 스택에 재주입
              ↓
         브리지 처리 (vxlan이 bridge port인 경우)
              ↓
         로컬 VM/컨테이너에 전달
/* drivers/net/vxlan/vxlan_core.c — VXLAN 수신 */
static int vxlan_rcv(struct sock *sk,
                      struct sk_buff *skb)
{
    struct vxlan_dev *vxlan;
    struct vxlan_sock *vs;
    struct vxlanhdr *vxh;
    __be32 vni;

    vs = rcu_dereference_sk_user_data(sk);

    /* VXLAN 헤더 파싱 */
    vxh = (struct vxlanhdr *)(udp_hdr(skb) + 1);

    /* I 플래그 검증 */
    if (!(ntohl(vxh->vx_flags) & VXLAN_HF_VNI)) {
        pr_warn_ratelimited("invalid vxlan flags %#x\\n",
                            ntohl(vxh->vx_flags));
        goto drop;
    }

    /* VNI 추출 및 VXLAN 디바이스 룩업 */
    vni = vxlan_vni(vxh->vx_vni);
    vxlan = vxlan_vs_find_vni(vs, skb->dev->ifindex, vni);
    if (!vxlan)
        goto drop;

    /* VXLAN 헤더 제거, inner frame 노출 */
    skb_pull(skb, VXLAN_HLEN);  /* 8 bytes */

    /* 소스 MAC 학습 (dynamic FDB entry) */
    if (vxlan->cfg.flags & VXLAN_F_LEARN)
        vxlan_snoop(skb->dev, &saddr, eth_hdr(skb)->h_source,
                    skb->ifindex, vni);

    /* inner frame을 네트워크 스택에 재주입 */
    skb->protocol = eth_type_trans(skb, vxlan->dev);
    netif_rx(skb);

    return 0;
drop:
    kfree_skb(skb);
    return 0;
}

FDB (Forwarding Database) 학습과 관리

VXLAN FDB는 내부 MAC 주소를 원격 VTEP IP로 매핑하는 핵심 테이블입니다. 브리지의 FDB와 개념은 같지만, 목적지가 포트 번호가 아닌 원격 VTEP의 IP 주소라는 점이 다릅니다.

방식동작
동적 학습VXLAN_F_LEARN 설정 시, 수신 패킷의 inner src MAC
(Data Plane)→ outer src IP를 자동 학습. 에이징 타이머(Timer) 적용.
정적 설정bridge fdb add로 수동 등록.
(Static)에이징 없음, 재부팅 시 재설정 필요.
nolearning +동적 학습 비활성화, 모든 매핑을 수동 관리.
정적 FDB대규모 환경에서 FDB 폭증 방지, 보안 향상.
L2MISS/L3MISSFDB miss 시 netlink 알림 → 외부 컨트롤러가
+ 컨트롤러FDB 엔트리를 동적으로 주입 (SDN 패턴).
BGP EVPNBGP Type-2 Route로 MAC/IP 바인딩을 분산 학습.
(Control Plane)FRR/BIRD 등이 커널 FDB를 자동 관리.
# --- FDB 관리 명령 ---

# 전체 FDB 조회
bridge fdb show dev vxlan100

# 00:00:00:00:00:00 = BUM 트래픽 대상 (헤드엔드 복제 목적지)
bridge fdb append 00:00:00:00:00:00 dev vxlan100 dst 10.0.0.2
bridge fdb append 00:00:00:00:00:00 dev vxlan100 dst 10.0.0.3
bridge fdb append 00:00:00:00:00:00 dev vxlan100 dst 10.0.0.4

# 특정 MAC → VTEP 정적 매핑
bridge fdb add aa:bb:cc:dd:ee:ff dev vxlan100 dst 10.0.0.2 self permanent
bridge fdb del aa:bb:cc:dd:ee:ff dev vxlan100 dst 10.0.0.2

# 동적 학습된 엔트리 확인 (상태: offload/self/permanent)
bridge fdb show dev vxlan100 | column -t

# FDB 에이징 시간 설정 (기본 300초)
ip link set vxlan100 type vxlan ageing 600

# FDB 최대 엔트리 수 제한
ip link set vxlan100 type vxlan maxaddress 1024

# 동적 FDB 플러시 (정적 엔트리는 유지)
bridge fdb flush dev vxlan100 dynamic
/* drivers/net/vxlan/vxlan_core.c — FDB 동적 학습 (수신 시) */
static bool vxlan_snoop(struct net_device *dev,
                        union vxlan_addr *src_ip,
                        const u8 *src_mac,
                        u32 src_ifindex, __be32 vni)
{
    struct vxlan_dev *vxlan = netdev_priv(dev);
    struct vxlan_fdb *f;

    f = __vxlan_find_mac(vxlan, src_mac, vni);
    if (likely(f)) {
        struct vxlan_rdst *rdst = first_remote_rcu(f);

        /* 이미 알려진 MAC — VTEP IP가 변경되었으면 갱신 */
        if (vxlan_addr_equal(&rdst->remote_ip, src_ip))
            return false;  /* 변경 없음 */

        /* MAC 이동 감지 — VTEP 변경 */
        rdst->remote_ip = *src_ip;
        f->updated = jiffies;
    } else {
        /* 신규 MAC 학습 */
        vxlan_fdb_create(vxlan, src_mac, src_ip, NUD_REACHABLE,
                        NTF_SELF, vxlan->cfg.dst_port,
                        vni, vni, src_ifindex,
                        NTF_VXLAN_ADDED, &f);
    }
    return true;
}

BUM 트래픽 처리

BUM(Broadcast, Unknown unicast, Multicast) 트래픽은 VXLAN 환경에서 가장 까다로운 문제입니다. 물리 네트워크에서는 스위치가 BUM 프레임을 모든 포트로 flood하지만, VXLAN 오버레이에서는 VTEP이 어떤 원격 VTEP에 전송해야 하는지 결정해야 합니다.

방식설정장점단점
멀티캐스트 그룹 group 239.1.1.1 자동 VTEP 디스커버리, 설정 간단 언더레이에 멀티캐스트 지원 필요, WAN 환경 제약
헤드엔드 복제
(Ingress Replication)
nolearning +
FDB 00:00:00:00:00:00 엔트리
멀티캐스트 불필요, L3 라우팅만으로 충분 VTEP 수에 비례하는 복제 오버헤드(Overhead), 수동 관리 필요
BGP EVPN FRR/BIRD + EVPN 설정 자동 VTEP 디스커버리 + MAC 학습, ARP suppression BGP 인프라 필요, 설정 복잡도 높음
SDN 컨트롤러 l2miss + netlink 연동 중앙 집중 제어, 정교한 정책 적용 가능 컨트롤러 가용성 의존, 구현 복잡
# === 방법 1: 멀티캐스트 기반 VXLAN ===
# 동일 멀티캐스트 그룹의 모든 VTEP이 BUM 트래픽을 수신
ip link add vxlan100 type vxlan \
    id 100 \
    group 239.1.1.1 \
    dstport 4789 \
    dev eth0 \
    ttl 10

# 멀티캐스트 그룹 참여 확인
ip maddr show dev eth0

# === 방법 2: 헤드엔드 복제 (유니캐스트 flood) ===
# 멀티캐스트 없이, 알려진 모든 VTEP에 유니캐스트로 복제
ip link add vxlan100 type vxlan \
    id 100 \
    local 10.0.0.1 \
    dstport 4789 \
    nolearning \
    proxy

# BUM 대상 VTEP 등록 (00:00:00:00:00:00 = flood 대상)
bridge fdb append 00:00:00:00:00:00 dev vxlan100 dst 10.0.0.2
bridge fdb append 00:00:00:00:00:00 dev vxlan100 dst 10.0.0.3
bridge fdb append 00:00:00:00:00:00 dev vxlan100 dst 10.0.0.4

# VTEP 제거 (노드 해제 시)
bridge fdb del 00:00:00:00:00:00 dev vxlan100 dst 10.0.0.4

# === 방법 3: L2MISS 알림 기반 (SDN 연동) ===
ip link add vxlan100 type vxlan \
    id 100 \
    local 10.0.0.1 \
    dstport 4789 \
    nolearning \
    l2miss \
    l3miss

# netlink 이벤트 모니터링 (컨트롤러가 수신)
ip monitor neigh dev vxlan100
# 출력 예: miss aa:bb:cc:dd:ee:ff STALE
# → 컨트롤러가 bridge fdb add로 매핑 주입

VXLAN 기본 구성

# --- 유니캐스트 VXLAN (point-to-point) ---
ip link add vxlan100 type vxlan \
    id 100 \
    local 10.0.0.1 \
    remote 10.0.0.2 \
    dstport 4789 \
    dev eth0

ip addr add 192.168.100.1/24 dev vxlan100
ip link set vxlan100 up

# --- 멀티캐스트 VXLAN (BUM 트래픽 자동 학습) ---
ip link add vxlan100 type vxlan \
    id 100 \
    group 239.1.1.1 \
    dstport 4789 \
    dev eth0 \
    ttl 10

# --- 브리지 + VXLAN (L2 확장, 가장 일반적) ---
ip link add br-vxlan type bridge
ip link add vxlan100 type vxlan id 100 local 10.0.0.1 dstport 4789 nolearning
ip link set vxlan100 master br-vxlan
ip link set vxlan100 up
ip link set br-vxlan up

# FDB 엔트리 수동 설정 (헤드엔드 복제)
bridge fdb append 00:00:00:00:00:00 dev vxlan100 dst 10.0.0.2
bridge fdb append 00:00:00:00:00:00 dev vxlan100 dst 10.0.0.3

# 특정 MAC → VTEP 매핑
bridge fdb add aa:bb:cc:dd:ee:ff dev vxlan100 dst 10.0.0.2

# VXLAN 상태 확인
ip -d link show vxlan100
bridge fdb show dev vxlan100

IPv6 언더레이 VXLAN

VXLAN은 IPv6 언더레이를 완전히 지원합니다. IPv6 언더레이를 사용하면 글로벌 주소 공간(Address Space)을 활용한 대규모 오버레이 네트워크 구성이 가능합니다. 단, IPv6 헤더 크기(40바이트)로 인해 오버헤드가 70바이트로 증가합니다.

# IPv6 언더레이 VXLAN 구성
ip link add vxlan100 type vxlan \
    id 100 \
    local fd00::1 \
    remote fd00::2 \
    dstport 4789 \
    dev eth0 \
    udp6zerocsumtx \
    udp6zerocsumrx

ip addr add 10.200.0.1/24 dev vxlan100
ip link set vxlan100 up

# IPv6 멀티캐스트 VXLAN
ip link add vxlan200 type vxlan \
    id 200 \
    group ff05::100 \
    dstport 4789 \
    dev eth0 \
    ttl 10 \
    udp6zerocsumtx \
    udp6zerocsumrx

# IPv6 + 브리지 조합
ip link add br-v6 type bridge
ip link add vxlan300 type vxlan \
    id 300 \
    local fd00::1 \
    dstport 4789 \
    nolearning \
    udp6zerocsumtx \
    udp6zerocsumrx
ip link set vxlan300 master br-v6
ip link set vxlan300 up
ip link set br-v6 up

bridge fdb append 00:00:00:00:00:00 dev vxlan300 dst fd00::2
bridge fdb append 00:00:00:00:00:00 dev vxlan300 dst fd00::3
UDP checksum과 IPv6: IPv4에서 UDP checksum은 선택사항이지만, IPv6에서는 기본적으로 사용이 요구됩니다. udp6zerocsumtx/udp6zerocsumrx 옵션은 RFC 6935/6936에서 정의한 예외 시나리오에 따라 VXLAN 트래픽의 UDP checksum을 생략하여 성능을 향상시킵니다. NIC의 하드웨어 checksum offload가 지원되면 이 옵션 없이도 성능 저하가 미미합니다.

VXLAN-GPE (Generic Protocol Extension)

VXLAN-GPE(RFC 9136)는 표준 VXLAN을 확장하여 내부 페이로드(Payload)의 프로토콜을 명시적으로 지정할 수 있게 합니다. 표준 VXLAN은 항상 이더넷 프레임을 캡슐화하지만, VXLAN-GPE는 IPv4, IPv6, NSH(Network Service Header), MPLS 등을 직접 캡슐화할 수 있어 SFC(Service Function Chaining)와 같은 고급 네트워크 기능에 사용됩니다.

VXLAN-GPE Header (8 bytes, RFC 9136) Flags: R,R,Ver(2),I,P,B,O Reserved (24 bits) Next Protocol (8) VNI (24 bits) Reserved (8 bits) Ver: 버전(현재 0), I: VNI 유효, P: Next Protocol 유효, B: BUM 비트, O: OAM 비트 Next Protocol: 0x01 IPv4, 0x02 IPv6, 0x03 Ethernet, 0x04 NSH, 0x05 MPLS
# VXLAN-GPE 디바이스 생성
ip link add vxlan-gpe0 type vxlan \
    id 500 \
    local 10.0.0.1 \
    remote 10.0.0.2 \
    dstport 4790 \
    gpe

# GPE는 IANA 포트 4790 사용 (표준 VXLAN은 4789)
# collect_metadata(external) 모드와 결합하여 BPF에서 활용 가능
ip link add vxlan-gpe1 type vxlan \
    external \
    dstport 4790 \
    gpe
/* include/net/vxlan.h — GPE 관련 정의 */
struct vxlanhdr_gpe {
    __u8   vx_flags;       /* Ver=0, I, P, B, O 플래그 */
    __u8   reserved[2];
    __u8   np;             /* Next Protocol */
    __be32 vx_vni;         /* VNI << 8 */
};

#define VXLAN_GPE_NP_IPV4   0x01
#define VXLAN_GPE_NP_IPV6   0x02
#define VXLAN_GPE_NP_ETHERNET 0x03
#define VXLAN_GPE_NP_NSH    0x04
#define VXLAN_GPE_NP_MPLS   0x05

VXLAN GBP (Group Based Policy)

VXLAN GBP(draft-smith-vxlan-group-policy)는 VXLAN 헤더의 예약 필드를 활용하여 보안 그룹 태그를 전달합니다. 소스/목적지 그룹 ID를 기반으로 마이크로세그멘테이션 정책을 적용할 수 있으며, Cilium 등의 CNI에서 네트워크 정책 적용에 사용합니다.

VXLAN GBP Header (8 bytes) Flags: G,R,R,R,I,R,R,R,R,D,R,R,A,R,R,R Group Policy ID (16 bits) VNI (24 bits) Reserved (8 bits) G: GBP Extension 활성화, D: Don't Learn, A: Policy Applied Group Policy ID: 16비트 보안 그룹 식별자 (0~65535)
# GBP 활성화된 VXLAN 생성
ip link add vxlan-gbp type vxlan \
    id 100 \
    local 10.0.0.1 \
    dstport 4789 \
    gbp

# iptables/nftables에서 GBP 태그 기반 필터링
iptables -A FORWARD -m set --match-set allowed_groups src \
    -j ACCEPT
iptables -A FORWARD -j DROP

VXLAN + EVPN (BGP Control Plane)

EVPN(Ethernet VPN, RFC 7432)은 BGP를 Control Plane으로 사용하여 VXLAN의 MAC/IP 학습, VTEP 디스커버리, BUM 최적화를 자동화합니다. 데이터 플레인의 flood & learn 방식을 대체하여 확장성과 수렴 시간을 크게 개선합니다.

EVPN Route Types (VXLAN 관련):

Type-2 (MAC/IP Advertisement):
  - MAC 주소 + IP 주소 + VNI를 BGP로 광고
  - 수신 VTEP이 FDB에 자동 등록 (data plane 학습 불필요)
  - ARP suppression: VTEP이 알려진 IP의 ARP 요청에 대리 응답

Type-3 (Inclusive Multicast Ethernet Tag):
  - VTEP의 존재를 광고 (BUM 트래픽 대상 목록 자동 구축)
  - Ingress Replication 리스트를 자동 관리

Type-5 (IP Prefix Route):
  - L3 VXLAN (Symmetric IRB): VNI 간 라우팅을 위한 IP prefix 광고
  - 테넌트별 VRF + L3 VNI를 통한 인터-서브넷 라우팅

EVPN 동작 흐름:
  VM-A boots → VTEP-A가 MAC/IP 학습 → Type-2 BGP Update 전파
      → VTEP-B가 수신 → 커널 FDB에 자동 추가
      → VM-B → VM-A 통신 시 data plane flood 불필요
# === FRR (Free Range Routing)을 이용한 EVPN 설정 예시 ===

# 1. 커널: VXLAN + 브리지 구성 (nolearning 필수)
ip link add br100 type bridge
ip link add vxlan100 type vxlan \
    id 100 \
    local 10.0.0.1 \
    dstport 4789 \
    nolearning

ip link set vxlan100 master br100
ip link set vxlan100 up
ip link set br100 up

# L3 VNI (대칭 IRB 라우팅용)
ip link add br-l3vni type bridge
ip link add vxlan-l3 type vxlan \
    id 9999 \
    local 10.0.0.1 \
    dstport 4789 \
    nolearning

ip link set vxlan-l3 master br-l3vni
ip link set vxlan-l3 up
ip link set br-l3vni up

# VRF 생성
ip link add vrf-tenant1 type vrf table 100
ip link set vrf-tenant1 up
ip link set br100 master vrf-tenant1
ip link set br-l3vni master vrf-tenant1
# 2. FRR 설정 (vtysh / frr.conf)

router bgp 65001
  bgp router-id 10.0.0.1
  no bgp default ipv4-unicast
  neighbor SPINE peer-group
  neighbor SPINE remote-as 65100
  neighbor 10.0.0.254 peer-group SPINE
  !
  address-family l2vpn evpn
    neighbor SPINE activate
    advertise-all-vni
  exit-address-family
!
router bgp 65001 vrf tenant1
  !
  address-family ipv4 unicast
    redistribute connected
  exit-address-family
  !
  address-family l2vpn evpn
    advertise ipv4 unicast
  exit-address-family
!
vni 100
  rd 10.0.0.1:100
  route-target import 65001:100
  route-target export 65001:100
# 3. EVPN 상태 확인 (vtysh)
show bgp l2vpn evpn summary
show bgp l2vpn evpn route type macip
show bgp l2vpn evpn route type multicast
show bgp l2vpn evpn route type prefix
show evpn vni
show evpn mac vni 100
show evpn arp-cache vni 100

# 커널 FDB에 BGP가 주입한 엔트리 확인
bridge fdb show dev vxlan100 | grep offload
# 출력 예: aa:bb:cc:dd:ee:ff dev vxlan100 dst 10.0.0.2 self offload
ARP Suppression: EVPN Type-2 경로에 IP가 포함되면, VTEP은 알려진 MAC/IP에 대한 ARP 요청을 로컬에서 대리 응답합니다. 이로써 오버레이 네트워크의 ARP 브로드캐스트 트래픽이 크게 감소하며, 대규모 환경에서 성능이 향상됩니다. bridge link set dev vxlan100 neigh_suppress on으로 활성화합니다.

collect_metadata (external) 모드

collect_metadata(external) 모드는 단일 VXLAN 디바이스로 여러 VNI를 처리할 수 있게 합니다. VNI와 목적지 VTEP을 패킷별로 동적으로 결정하며, tc-flower, BPF, OVS(Open vSwitch) 등의 프로그래머블 데이터 플레인에서 사용됩니다.

# external 모드 VXLAN (VNI/목적지를 tc/BPF에서 결정)
ip link add vxlan-ext type vxlan \
    external \
    dstport 4789

ip link set vxlan-ext up

# tc-flower로 트래픽 제어 (VNI/목적지 동적 설정)
tc qdisc add dev vxlan-ext clsact
tc filter add dev vxlan-ext egress \
    flower dst_mac aa:bb:cc:dd:ee:ff \
    action tunnel_key set \
        id 100 \
        src_ip 10.0.0.1 \
        dst_ip 10.0.0.2 \
        dst_port 4789 \
    action mirred egress redirect dev vxlan-ext
/* BPF에서 VXLAN 메타데이터 설정 (tc BPF 프로그램) */
SEC("tc")
int vxlan_encap(struct __sk_buff *skb)
{
    struct bpf_tunnel_key key = {};

    key.tunnel_id = 100;          /* VNI */
    key.remote_ipv4 = 0x0A000002; /* 10.0.0.2 */
    key.tunnel_tos = 0;
    key.tunnel_ttl = 64;

    bpf_skb_set_tunnel_key(skb, &key, sizeof(key),
                           BPF_F_ZERO_CSUM_TX);
    return TC_ACT_OK;
}

/* 수신 측: 터널 메타데이터 읽기 */
SEC("tc")
int vxlan_decap(struct __sk_buff *skb)
{
    struct bpf_tunnel_key key = {};

    bpf_skb_get_tunnel_key(skb, &key, sizeof(key), 0);
    /* key.tunnel_id = VNI, key.remote_ipv4 = 소스 VTEP */

    /* VNI 기반 정책 적용 */
    if (key.tunnel_id == 100)
        return TC_ACT_OK;
    return TC_ACT_SHOT;
}

성능 최적화

MTU 설정

MTU 주의: VXLAN은 50바이트(IPv4) 또는 70바이트(IPv6)의 오버헤드를 추가합니다. 내부 MTU가 1500이면 물리 NIC의 MTU는 최소 1550(IPv4) 또는 1570(IPv6) 이상이어야 합니다. 점보 프레임을 강력히 권장합니다.
# 권장: 물리 NIC에 점보 프레임 설정
ip link set eth0 mtu 9000

# VXLAN 디바이스 MTU (자동 계산되지만 명시 설정 권장)
ip link set vxlan100 mtu 8950   # 9000 - 50 (VXLAN overhead)

# Path MTU Discovery 문제 시 DF 비트 제어
ip link add vxlan100 type vxlan id 100 ... df unset  # DF=0 (fragmentation 허용)
ip link add vxlan100 type vxlan id 100 ... df set     # DF=1 (PMTUD 활성화)
ip link add vxlan100 type vxlan id 100 ... df inherit # inner IP의 DF 상속

NIC 오프로드

# NIC의 VXLAN 오프로드 지원 확인
ethtool -k eth0 | grep -E "(tx-udp_tnl|rx-udp_tnl|vxlan)"

# 일반적인 오프로드 기능:
#   tx-udp_tnl-segmentation     : VXLAN TSO (내부 TCP 세그멘테이션)
#   tx-udp_tnl-csum-segmentation: TSO + 외부 UDP checksum
#   rx-udp-gro-forwarding       : UDP GRO (VXLAN 수신 병합)

# 오프로드 포트 등록 확인
ethtool --show-tunnels eth0
# 출력 예:
#   port 4789, VXLAN

# GRO 활성화 (수신 성능 향상)
ethtool -K eth0 rx-udp-gro-forwarding on

# VXLAN 통계 확인
ip -s link show dev vxlan100
# ethtool 카운터 (NIC별 상이)
ethtool -S eth0 | grep -i vxlan
/* drivers/net/vxlan/vxlan_core.c — NIC에 오프로드 포트 등록 */
static void vxlan_push_rx_ports(struct net_device *dev)
{
    struct vxlan_sock *vs;

    /* 물리 NIC에 VXLAN UDP 포트 알림 → NIC이 하드웨어 파싱 수행 */
    rcu_read_lock();
    hlist_for_each_entry_rcu(vs, &vn->sock_list[0], hlist)
        udp_tunnel_push_rx_port(dev, vs->sock,
                                UDP_TUNNEL_TYPE_VXLAN);
    rcu_read_unlock();
}

/* NIC 드라이버 예시: VXLAN 오프로드 포트 등록 콜백 */
static void nic_add_vxlan_port(struct net_device *dev,
                                struct udp_tunnel_info *ti)
{
    /* NIC ASIC에 VXLAN 포트 프로그래밍 */
    /* → 하드웨어가 외부 헤더를 파싱하여 inner payload를 직접 처리 */
    /* → RSS (내부 5-tuple 기반), checksum offload, TSO 가능 */
    hw_register_vxlan_port(adapter, ntohs(ti->port));
}

커널 튜닝 파라미터

# VXLAN 관련 sysctl 튜닝

# UDP 수신 버퍼 확대 (대량 VXLAN 트래픽)
sysctl -w net.core.rmem_max=26214400
sysctl -w net.core.rmem_default=26214400

# conntrack이 필요 없는 경우 VXLAN 인터페이스에서 비활성화
iptables -t raw -A PREROUTING -i vxlan+ -j NOTRACK
iptables -t raw -A OUTPUT -o vxlan+ -j NOTRACK

# VXLAN FDB 에이징 최적화
ip link set vxlan100 type vxlan ageing 300   # 기본값

# 네트워크 네임스페이스간 이동 허용
sysctl -w net.core.netns_local=0

VXLAN 트러블슈팅

# === 1단계: VXLAN 디바이스 상태 확인 ===
ip -d link show vxlan100
# 확인 항목: id(VNI), group/remote, local, dstport, learning, proxy

# === 2단계: FDB 테이블 검증 ===
bridge fdb show dev vxlan100
# 필수 확인: 00:00:00:00:00:00 엔트리 (BUM flood 대상)
# permanent = 정적, offload = EVPN/HW 주입

# === 3단계: 언더레이 연결성 확인 ===
# VTEP IP 간 ping (기본 연결 확인)
ping 10.0.0.2

# UDP 4789 도달 가능 여부
ncat -u 10.0.0.2 4789

# 방화벽 확인 (VXLAN UDP 포트)
iptables -L -n -v | grep 4789
nft list ruleset | grep 4789

# === 4단계: 패킷 캡처 ===
# 외부 캡슐화된 VXLAN 패킷 (언더레이)
tcpdump -i eth0 -nn "udp port 4789" -c 10

# 내부 프레임 (오버레이, VTEP에서 역캡슐화 후)
tcpdump -i vxlan100 -nn -c 10

# VXLAN 헤더까지 상세 출력
tcpdump -i eth0 -nn -e -v "udp port 4789"

# === 5단계: 통계 및 카운터 ===
ip -s link show dev vxlan100
# TX/RX packets, bytes, errors, dropped 확인

# VXLAN 전용 통계
nstat -a | grep -i Vxlan
# VxlanInPkts, VxlanOutPkts, VxlanInErrors 등

# ethtool 확장 통계 (NIC별)
ethtool -S eth0 | grep -i vxlan

# === 6단계: 일반적인 문제와 해결 ===

# 문제: ping은 되지만 TCP 연결 안됨 → MTU 문제
# 해결:
ip link set eth0 mtu 9000
ip link set vxlan100 mtu 8950
# 또는 inner의 MSS 조정:
iptables -A FORWARD -p tcp --tcp-flags SYN,RST SYN \
    -j TCPMSS --clamp-mss-to-pmtu

# 문제: 멀티캐스트 VXLAN에서 BUM 전달 안됨
# 확인: 언더레이 멀티캐스트 라우팅
ip mroute show
ip maddr show dev eth0
# 스위치의 IGMP snooping 설정 확인

# 문제: EVPN FDB 동기화 안됨
# 확인: BGP 세션 상태
vtysh -c "show bgp l2vpn evpn summary"
vtysh -c "show bgp l2vpn evpn route type macip"

# 문제: MAC flapping 감지
dmesg | grep -i "moved from"
# bridge: vxlan100: xx:xx:xx:xx:xx:xx moved from port X to port Y
tcpdump 팁: Wireshark에서 VXLAN 패킷을 분석할 때, udp.port == 4789 필터로 캡처한 후 Decode As → VXLAN을 선택하면 내부 이더넷 프레임까지 파싱됩니다. tshark에서는 tshark -d udp.port==4789,vxlan -r capture.pcap으로 디코딩할 수 있습니다.

GENEVE (Generic Network Virtualization Encapsulation)

GENEVE(RFC 8926)는 IETF NVO3 워킹그룹이 설계한 차세대 네트워크 가상화 캡슐화 프로토콜입니다. VXLAN, NVGRE, STT의 장점을 통합하고, 가변 길이 옵션(TLV)으로 확장성을 극대화한 것이 핵심 특징입니다. Open vSwitch(OVS)의 기본 터널 프로토콜이며, VMware NSX, AWS VPC, Cilium 등에서 채택하고 있습니다.

GENEVE vs VXLAN: GENEVE는 VXLAN의 상위 호환으로 설계되었습니다. VXLAN은 고정 8바이트 헤더이지만, GENEVE는 가변 길이 옵션(TLV)을 통해 보안 태그, QoS 정보, 디버깅(Debugging) 메타데이터 등을 유연하게 전달할 수 있습니다. 새로운 오버레이 네트워크 설계 시 GENEVE를 우선 고려하는 것이 권장됩니다.

GENEVE 패킷 포맷

GENEVE 헤더 포맷 (RFC 8926) Outer Ethernet 14B Outer IP 20/40B UDP 8B (6081) GENEVE Header 8B + Options Options (TLV) 0~252B Inner Frame 원본 L2 GENEVE 고정 헤더 (8 bytes) Ver (2b) 현재: 0 Opt Len (6b) ×4 bytes O|C|Rsvd (8b) OAM, Critical Protocol Type (16b) 0x6558=Ethernet VNI (24b) + Rsvd (8b) 네트워크 식별자 O=OAM 패킷, C=Critical 옵션 포함 (처리 불가 시 드롭 필수) Opt Len: 옵션 길이를 4바이트 단위로 표현 (최대 63 → 252바이트) GENEVE TLV 옵션 구조 (가변 길이) Option Class (16b) 벤더/표준 식별 Type (8b) 옵션 종류 R|R|R|Len (8b) ×4 bytes Option Data (0~124 bytes) 보안 태그, QoS 정보, 디버깅 메타데이터 등 Option Class 예: 0x0100 (Linux), 0x0101 (OVS), 0x0102 (VMware NSX) 여러 TLV를 연속으로 배치 가능 — VXLAN에 없는 핵심 확장성
GENEVE 헤더: 8바이트 고정 헤더 + 가변 길이 TLV 옵션
필드크기설명
Version2비트프로토콜 버전 (현재 0)
Opt Len6비트옵션 길이 (4바이트 단위, 0~63 → 0~252바이트)
O (OAM)1비트OAM 패킷 표시 (관리/진단용)
C (Critical)1비트Critical 옵션 포함 (미지원 시 반드시 드롭)
Protocol Type16비트내부 페이로드 타입 (0x6558=Ethernet, 0x0800=IPv4, 0x86DD=IPv6)
VNI24비트Virtual Network Identifier (VXLAN VNI와 동일 개념)
Reserved8비트예약 (0으로 설정)
Options가변TLV(Type-Length-Value) 옵션 (Class + Type + Length + Data)

GENEVE Linux 커널 구현

/* include/net/geneve.h — GENEVE 핵심 자료구조 */

struct genevehdr {
#if defined(__LITTLE_ENDIAN_BITFIELD)
    u8 opt_len:6;     /* 옵션 길이 (×4 bytes) */
    u8 ver:2;          /* 버전 (0) */
    u8 rsvd1:6;
    u8 critical:1;     /* C 비트 */
    u8 oam:1;          /* O 비트 */
#else
    u8 ver:2;
    u8 opt_len:6;
    u8 oam:1;
    u8 critical:1;
    u8 rsvd1:6;
#endif
    __be16 proto_type;  /* ETH_P_TEB(0x6558)=Ethernet */
    u8     vni[3];      /* Virtual Network Identifier */
    u8     rsvd2;       /* 예약 */
    struct geneve_opt options[]; /* 가변 길이 TLV 옵션 */
};

/* TLV 옵션 구조 */
struct geneve_opt {
    __be16 opt_class;   /* 벤더/표준 식별자 */
    u8     type;        /* 옵션 타입 */
#if defined(__LITTLE_ENDIAN_BITFIELD)
    u8     length:5;    /* 데이터 길이 (×4 bytes) */
    u8     r3:1;
    u8     r2:1;
    u8     r1:1;
#else
    u8     r1:1;
    u8     r2:1;
    u8     r3:1;
    u8     length:5;
#endif
    u8     opt_data[];  /* 옵션 데이터 */
};

/* GENEVE 디바이스 구조 */
struct geneve_dev {
    struct hlist_node  hlist;     /* geneve_net 해시 */
    struct net         *net;      /* 네트워크 네임스페이스 */
    struct net_device  *dev;      /* 연관 netdev */
    struct geneve_sock __rcu *sock4; /* IPv4 UDP 소켓 */
    struct geneve_sock __rcu *sock6; /* IPv6 UDP 소켓 */
    struct list_head   next;      /* 전역 디바이스 리스트 */
    struct ip_tunnel_info info;   /* 터널 정보 */
    bool               collect_md; /* metadata 모드 */
    bool               use_udp6_rx_checksums;
    bool               ttl_inherit;
    enum ifla_geneve_df df;      /* DF 비트 설정 */
    bool               inner_proto_inherit;
};

GENEVE 구성

# === GENEVE 기본 구성 ===

# 포인트-투-포인트 GENEVE 터널
ip link add geneve100 type geneve \
    id 100 \
    remote 10.0.0.2 \
    dstport 6081       # IANA 표준 포트

ip addr add 10.200.0.1/24 dev geneve100
ip link set geneve100 up

# === GENEVE + 브리지 (L2 확장) ===
ip link add br-geneve type bridge
ip link add geneve200 type geneve \
    id 200 \
    remote 10.0.0.2 \
    dstport 6081

ip link set geneve200 master br-geneve
ip link set geneve200 up
ip link set br-geneve up

# === external 모드 (OVS/tc/BPF 연동) ===
ip link add geneve-ext type geneve \
    external \
    dstport 6081

# IPv6 언더레이
ip link add geneve300 type geneve \
    id 300 \
    remote fd00::2 \
    dstport 6081

# === TLV 옵션과 함께 사용 (tc/BPF에서 설정) ===
# external 모드 GENEVE에서 BPF로 TLV 옵션 추가
ip link add geneve-tlv type geneve external dstport 6081
ip link set geneve-tlv up

# tc filter에서 tunnel 옵션 설정
tc qdisc add dev geneve-tlv clsact
tc filter add dev geneve-tlv egress \
    flower dst_mac aa:bb:cc:dd:ee:ff \
    action tunnel_key set \
        id 100 \
        src_ip 10.0.0.1 \
        dst_ip 10.0.0.2 \
        dst_port 6081 \
        geneve_opts 0102:1:0a0b0c0d \
    action mirred egress redirect dev geneve-tlv

# GENEVE 디바이스 상세 확인
ip -d link show geneve100
# geneve id 100 remote 10.0.0.2 dstport 6081 ...

# GENEVE 통계
ip -s link show dev geneve100
/* BPF에서 GENEVE TLV 옵션 설정/읽기 */
SEC("tc")
int geneve_encap_with_opts(struct __sk_buff *skb)
{
    struct bpf_tunnel_key key = {};
    struct {
        struct geneve_opt opt;
        __be32 data;
    } geneve_opts;

    key.tunnel_id = 100;
    key.remote_ipv4 = 0x0A000002;  /* 10.0.0.2 */
    key.tunnel_ttl = 64;

    bpf_skb_set_tunnel_key(skb, &key, sizeof(key), 0);

    /* GENEVE TLV 옵션 추가 */
    geneve_opts.opt.opt_class = bpf_htons(0x0102); /* 커스텀 class */
    geneve_opts.opt.type = 1;
    geneve_opts.opt.length = 1;  /* 4 bytes */
    geneve_opts.data = bpf_htonl(0xDEADBEEF);

    bpf_skb_set_tunnel_opt(skb, &geneve_opts,
                           sizeof(geneve_opts));
    return TC_ACT_OK;
}

오버레이 프로토콜 비교

Linux 커널이 지원하는 주요 네트워크 오버레이 프로토콜의 특성을 비교합니다.

특성VXLANGENEVEGRE/NVGRESTT
RFCRFC 7348RFC 8926RFC 2890/7637비표준 (RFC 없음)
캡슐화UDP (4789)UDP (6081)IP Protocol 47TCP-like (7471)
헤더 크기8B 고정8B + 옵션(가변)4~8B18B
네트워크 ID24비트 VNI24비트 VNI24비트 VSID/Key64비트 Context ID
확장성GBP/GPE로 제한적TLV 옵션으로 무한 확장없음없음
ECMP 지원UDP src port 해시UDP src port 해시GRE Key 해시TCP-like src port
NIC 오프로드광범위 지원점점 확대 중대부분 지원제한적
멀티캐스트그룹 기반 BUM유니캐스트 권장그룹 기반유니캐스트만
IPv4 오버헤드50B50B + 옵션38~42B76B
IPv6 오버헤드70B70B + 옵션58~62B96B
주요 사용처데이터센터, 컨테이너OVS, NSX, AWS VPC레거시, 사이트간 VPNVMware (레거시)
커널 소스drivers/net/vxlan/drivers/net/geneve.cnet/ipv4/ip_gre.c미지원
프로토콜 선택 가이드:
  • VXLAN — 가장 넓은 HW 오프로드 지원, BGP EVPN 생태계 성숙. 물리 스위치 기반 데이터센터에 최적.
  • GENEVE — TLV 확장성, OVS 기본 프로토콜. 소프트웨어 정의 네트워킹(SDN)에 최적. 새 프로젝트에 권장.
  • GRE — 단순 사이트 간 터널, 레거시 호환성. 새 오버레이 네트워크에는 비권장.
# === 오버레이 프로토콜별 성능 비교 테스트 ===

# VXLAN 처리량 테스트
iperf3 -c 10.200.0.2 -t 30 -P 4  # VXLAN 인터페이스 통해

# GENEVE 처리량 테스트
iperf3 -c 10.201.0.2 -t 30 -P 4  # GENEVE 인터페이스 통해

# GRE 처리량 테스트
iperf3 -c 10.202.0.2 -t 30 -P 4  # GRE 인터페이스 통해

# 오버헤드 측정: 각 프로토콜의 실제 MTU 확인
ping -M do -s 1450 10.200.0.2  # VXLAN (MTU 1450 + 50 = 1500)
ping -M do -s 1450 10.201.0.2  # GENEVE (옵션 없이 동일)
ping -M do -s 1462 10.202.0.2  # GRE (MTU 1462 + 38 = 1500)

# NIC 오프로드 지원 확인
ethtool --show-tunnels eth0
# Tunnel information for eth0:
#   port 4789, VXLAN    ← VXLAN 오프로드 활성화
#   port 6081, GENEVE   ← GENEVE 오프로드 활성화

switchdev 오버레이 오프로드 개요

switchdev 프레임워크와 eSwitch는 VXLAN/GENEVE 오버레이 네트워크의 성능을 극대화하는 핵심 기술입니다. NVIDIA/Mellanox ConnectX-5/6/7(mlx5), Intel E810(ice) 등의 SmartNIC은 VXLAN/GENEVE 캡슐화/역캡슐화를 하드웨어에서 직접 수행하여 CPU 부하를 크게 줄입니다. TC flower 규칙으로 tunnel_key set 액션을 설치하면 eSwitch가 패킷을 와이어레이트로 터널링합니다.

eSwitch switchdev 모드에서는 VF/SF별 Representor 포트가 생성되어, OVS나 TC flower를 통해 오버레이 터널 규칙을 하드웨어에 오프로드할 수 있습니다. 특히 conntrack offload와 결합하면 설정된 연결(ESTABLISHED)의 VXLAN/GENEVE 패킷이 커널을 완전히 우회하여 NIC에서 직접 처리됩니다.

상세 문서: switchdev 프레임워크, eSwitch 모드 전환, Representor 포트, TC Flower 오프로드 등의 상세 내용은 eSwitch 페이지를 참조하세요.

실전 구성 예제

VXLAN 멀티노드 오버레이

여러 노드에 걸쳐 VXLAN 오버레이 네트워크를 구성하는 기본 예제입니다.

# --- 노드 A (10.0.0.1) ---
ip link add br-overlay type bridge
ip link add vxlan42 type vxlan \
    id 42 local 10.0.0.1 dstport 4789 nolearning

ip link set vxlan42 master br-overlay
ip link set vxlan42 up
ip link set br-overlay up

# 원격 VTEP 등록 (BUM 복제 대상)
bridge fdb append 00:00:00:00:00:00 dev vxlan42 dst 10.0.0.2
bridge fdb append 00:00:00:00:00:00 dev vxlan42 dst 10.0.0.3

# 컨테이너 veth를 오버레이 브리지에 연결
ip link add veth-a type veth peer name veth-a-br
ip link set veth-a-br master br-overlay
ip link set veth-a-br up

ip netns add overlay-ns
ip link set veth-a netns overlay-ns
ip netns exec overlay-ns ip addr add 10.200.0.1/24 dev veth-a
ip netns exec overlay-ns ip link set veth-a up

# --- 노드 B (10.0.0.2) ---
# 동일 구성, local=10.0.0.2, IP=10.200.0.2/24

# 결과: 10.200.0.1 ↔ 10.200.0.2가 L3 네트워크를 통해 L2 통신

GENEVE 오버레이 구성

# --- GENEVE 기본 구성 ---
ip link add geneve100 type geneve id 100 \
    remote 10.0.0.2 dstport 6081

ip link add br-geneve type bridge
ip link set geneve100 master br-geneve
ip link set geneve100 up
ip link set br-geneve up

# 로컬 엔드포인트 연결
ip link add veth-g type veth peer name veth-g-br
ip link set veth-g-br master br-geneve
ip link set veth-g-br up

ip netns add geneve-ns
ip link set veth-g netns geneve-ns
ip netns exec geneve-ns ip addr add 10.201.0.1/24 dev veth-g
ip netns exec geneve-ns ip link set veth-g up

VXLAN + VLAN-Tunnel 매핑 (멀티테넌트)

# VLAN-aware 브리지 + external VXLAN으로 다중 VNI 처리
ip link add br0 type bridge vlan_filtering 1 vlan_default_pvid 0
ip link add vxlan0 type vxlan external dstport 4789 local 10.0.0.1
ip link set vxlan0 master br0
ip link set vxlan0 up
ip link set br0 up

# VLAN ↔ VNI 매핑
bridge vlan add vid 10 dev vxlan0
bridge vlan tunnel add dev vxlan0 vid 10 tunnel_id 100010
bridge vlan add vid 20 dev vxlan0
bridge vlan tunnel add dev vxlan0 vid 20 tunnel_id 100020

# 원격 VTEP FDB
bridge fdb append 00:00:00:00:00:00 dev vxlan0 dst 10.0.0.2 self
bridge fdb append 00:00:00:00:00:00 dev vxlan0 dst 10.0.0.3 self

# VM/컨테이너 access 포트
ip link set tap-vm1 master br0
bridge vlan add vid 10 dev tap-vm1 pvid untagged

오버레이 진단 명령

# VXLAN/GENEVE 디바이스 목록
ip -d link show type vxlan
ip -d link show type geneve

# VXLAN FDB (원격 VTEP 매핑)
bridge fdb show dev vxlan100

# VXLAN/GENEVE 통계
ip -s link show dev vxlan100
nstat -a | grep -i vxlan

# 터널 오프로드 확인
ethtool --show-tunnels eth0

# VLAN-Tunnel 매핑 확인
bridge vlan tunnel show dev vxlan0

# 패킷 캡처 (VXLAN 내부 프레임 확인)
tcpdump -i eth0 -nn udp port 4789 -s 0
tcpdump -i eth0 -nn udp port 6081 -s 0

# VTEP IP 연결 확인
ping -c 3 10.0.0.2    # 언더레이 연결

# MTU 검증 (Path MTU Discovery)
ping -M do -s 1450 10.200.0.2  # 오버레이 IP, VXLAN MTU 확인

# ip monitor로 실시간 이벤트 추적
ip monitor link | grep -E "vxlan|geneve"

관련 커널 설정

# VXLAN
CONFIG_VXLAN=m

# GENEVE
CONFIG_GENEVE=m

# Bridge (오버레이 브리지용)
CONFIG_BRIDGE=m
CONFIG_BRIDGE_VLAN_FILTERING=y

# UDP 터널 지원
CONFIG_NET_UDP_TUNNEL=m

# switchdev (오프로드)
CONFIG_NET_SWITCHDEV=y

보안 고려사항

VXLAN/GENEVE 오버레이 네트워크는 기본적으로 암호화(Encryption)나 인증 메커니즘을 제공하지 않습니다. 언더레이 네트워크의 보안이 오버레이 전체의 보안 기반이 되므로, 적절한 방어 조치가 필수적입니다.

VXLAN/GENEVE 보안

오버레이 보안 경고: VXLAN과 GENEVE는 기본적으로 암호화/인증을 제공하지 않습니다. 언더레이 네트워크에서 UDP 4789/6081 포트에 접근할 수 있는 공격자는 오버레이 트래픽을 도청, 변조, 주입할 수 있습니다. 반드시 추가적인 보안 조치가 필요합니다.
위협설명대응
트래픽 도청언더레이에서 VXLAN UDP 패킷을 캡처하면 내부 프레임이 평문으로 노출IPsec 터널로 언더레이 암호화, MACsec, WireGuard
VNI 스캐닝공격자가 다양한 VNI로 패킷을 전송하여 활성 VNI를 탐색방화벽(Firewall)에서 VTEP IP 화이트리스트, nolearning
VTEP 스푸핑공격자가 VTEP IP를 위조하여 FDB를 오염시키고 트래픽을 가로챔nolearning + 정적 FDB, BGP EVPN (인증된 경로만 학습)
BUM 증폭대량 unknown unicast로 flood 트래픽 증폭FDB 크기 제한 (maxaddress), ARP suppression
MAC 이동 공격MAC 주소를 위조하여 트래픽을 자신의 VTEP으로 리디렉션nolearning, 정적 FDB, MAC flapping 감지
# === VXLAN 보안 강화 설정 ===

# 1. 동적 학습 비활성화 (FDB 오염 방지)
ip link add vxlan100 type vxlan \
    id 100 local 10.0.0.1 dstport 4789 \
    nolearning  # ← 핵심! 수신 패킷에서 MAC 학습 안함

# 2. FDB 크기 제한
ip link set vxlan100 type vxlan maxaddress 256

# 3. 방화벽: VXLAN 포트를 알려진 VTEP만 허용
nft add rule inet filter input \
    udp dport 4789 \
    ip saddr { 10.0.0.2, 10.0.0.3, 10.0.0.4 } \
    accept
nft add rule inet filter input udp dport 4789 drop

# 4. IPsec으로 언더레이 암호화 (ESP 터널)
# VTEP 간 IPsec SA 설정
ip xfrm state add src 10.0.0.1 dst 10.0.0.2 \
    proto esp spi 0x1001 mode transport \
    enc "aes" 0x$(openssl rand -hex 16) \
    auth "hmac(sha256)" 0x$(openssl rand -hex 32)

ip xfrm policy add src 10.0.0.1 dst 10.0.0.2 \
    proto udp dport 4789 dir out \
    tmpl src 10.0.0.1 dst 10.0.0.2 proto esp mode transport

# 5. MACsec (Layer 2 암호화 — 같은 L2 세그먼트에서)
ip link add macsec0 link eth0 type macsec \
    encrypt on
# MACsec 위에 VXLAN/GENEVE 구성

# 6. MAC flapping 감지 (dmesg 모니터링)
dmesg -w | grep "moved from"
# bridge: vxlan100: xx:xx:xx:xx:xx:xx moved from port X to port Y

성능 벤치마킹

VXLAN/GENEVE 오버레이의 캡슐화 오버헤드를 정량적으로 파악하기 위한 벤치마킹 방법론과 주요 튜닝 포인트입니다.

오버레이 계층별 성능 오버헤드

구성상대 처리량(Throughput)상대 지연(Latency)CPU 사용주요 병목
물리 NIC 직접 (베이스라인)100%1x낮음NIC 하드웨어 한계
VLAN 서브인터페이스~99%~1.0x매우 낮음HW 오프로드 시 거의 무시
Linux Bridge (VLAN-aware)~95-98%~1.1x낮음FDB 룩업, VLAN 처리
VXLAN (HW 오프로드)~90-95%~1.2x낮음캡슐화 오버헤드
VXLAN (SW 처리)~70-85%~1.5x중간CPU 캡슐화/역캡슐화
GENEVE (SW 처리)~70-85%~1.5x중간VXLAN과 유사
OVS + VXLAN~65-80%~1.5-2x중~높음OVS 매칭 + 캡슐화
OVS + GENEVE + CT offload~85-95%~1.2x낮음초기 연결만 SW
주의: 위 수치는 일반적인 참고값이며, 실제 성능은 NIC 모델, 드라이버 버전, 커널 버전, 워크로드 패턴(패킷 크기, 연결 수 등)에 따라 크게 달라집니다. 반드시 실제 환경에서 벤치마킹해야 합니다.

핵심 성능 튜닝 체크리스트

# === 1. MTU 최적화 (가장 중요) ===
# 점보 프레임으로 오버레이 오버헤드 흡수
ip link set eth0 mtu 9000
ip link set vxlan100 mtu 8950  # 9000 - 50 (VXLAN IPv4)
ip link set br0 mtu 8950

# === 2. NIC 오프로드 확인 ===
ethtool -k eth0 | grep -E "(tx-udp_tnl|rx-gro|tx-checksum|scatter)"
# 모든 오프로드 활성화 확인

# === 3. RSS/RPS 튜닝 (멀티코어 분산) ===
# NIC RSS가 VXLAN inner 해시를 지원하는지 확인
ethtool -n eth0 rx-flow-hash udp4
# 미지원 시 RPS로 소프트웨어 분산
echo "f" > /sys/class/net/vxlan100/queues/rx-0/rps_cpus

# === 4. conntrack 비활성화 (오버레이에서 불필요 시) ===
nft add rule inet raw prerouting iifname "vxlan*" notrack
nft add rule inet raw output oifname "vxlan*" notrack

# === 5. UDP 버퍼 확대 ===
sysctl -w net.core.rmem_max=26214400
sysctl -w net.core.rmem_default=26214400
sysctl -w net.core.wmem_max=26214400

# === 6. GRO (Generic Receive Offload) 활성화 ===
ethtool -K eth0 rx-udp-gro-forwarding on

# === 7. VXLAN FDB 최적화 ===
# nolearning으로 데이터 플레인 FDB 학습 비활성화
# → 컨트롤 플레인(EVPN 등)에서만 FDB 관리
ip link set vxlan100 type vxlan nolearning

# === 8. Busy polling (지연 민감 워크로드) ===
sysctl -w net.core.busy_poll=50
sysctl -w net.core.busy_read=50

# === 벤치마킹 명령 ===
# 처리량 테스트
iperf3 -c 10.200.0.2 -t 30 -P 8 --bidir

# 지연 시간 테스트
ping -c 100 -i 0.01 10.200.0.2 | tail -1

# PPS(Packets Per Second) 테스트
iperf3 -c 10.200.0.2 -t 30 -u -b 10G -l 64

# CPU 사용률 모니터링
mpstat -P ALL 1

흔한 실수와 모범 사례

실수증상해결
VXLAN MTU 미조정 ping은 되는데 TCP(대용량) 전송 실패, Path MTU black hole 물리 MTU 9000 + VXLAN MTU 8950 또는 MSS clamping
VXLAN FDB 00:00:00:00:00:00 누락 BUM 트래픽(ARP 등) 미전달, 초기 ARP 실패로 연결 불가 bridge fdb append 00:00:00:00:00:00 dev vxlan100 dst <VTEP>
방화벽에서 VXLAN/GENEVE UDP 차단 VTEP 간 ping은 되는데 오버레이 통신 불가 nft add rule inet filter input udp dport { 4789, 6081 } accept
VXLAN learning + EVPN 충돌 MAC flapping, FDB 불일치, 트래픽 루프 EVPN 사용 시 반드시 nolearning 설정
GENEVE 포트 혼동 VXLAN(4789)과 GENEVE(6081) 포트를 혼용 프로토콜에 맞는 IANA 포트 사용 확인
언더레이 라우팅 미설정 VTEP 간 UDP 패킷이 전달되지 않음 ip route로 VTEP 간 IP 연결 확인, traceroute로 경로 검증
NIC 오프로드 미확인 CPU 사용률 높음, 처리량 저하 ethtool --show-tunnels eth0으로 VXLAN/GENEVE 오프로드 확인
VLAN-Tunnel 매핑 누락 external VXLAN에서 VNI가 0으로 전송됨 bridge vlan tunnel add dev vxlan0 vid 10 tunnel_id 100010
conntrack 오버헤드 오버레이 인터페이스에서 불필요한 conntrack 처리로 성능 저하 nft add rule inet raw prerouting iifname "vxlan*" notrack

모범 사례 요약

VXLAN/GENEVE 구성:
  • 물리 NIC에 점보 프레임(MTU 9000) 설정 → 오버레이 MTU 8950
  • EVPN 사용 시 반드시 nolearning — 데이터 플레인 학습 비활성화
  • 보안: nolearning + 정적 FDB + 방화벽 VTEP 화이트리스트
  • NIC 오프로드 확인: ethtool --show-tunnels eth0
  • conntrack이 불필요한 오버레이 인터페이스에서는 NOTRACK 적용
  • 새 프로젝트에서는 GENEVE를 우선 검토 (TLV 확장성)
  • UDP 수신 버퍼(Buffer)를 충분히 확보: sysctl -w net.core.rmem_max=26214400

VXLAN FDB 학습

VXLAN FDB(Forwarding Database)는 오버레이 네트워크의 MAC 주소를 원격 VTEP IP로 매핑하는 핵심 데이터 플레인 구성요소입니다. 물리 브리지의 FDB와 달리, VXLAN FDB는 포트 번호가 아닌 원격 VTEP의 IP 주소를 목적지로 사용합니다. FDB 학습 방식의 선택은 오버레이 네트워크의 확장성, 수렴 시간, 보안에 직접적인 영향을 미칩니다.

동적 FDB 학습 (Data Plane Learning)

동적 학습(VXLAN_F_LEARN)은 수신 패킷의 inner source MAC과 outer source IP를 자동으로 FDB에 등록하는 방식입니다. 물리 스위치의 MAC 학습과 동일한 원리이지만, VXLAN 환경에서는 보안과 확장성 측면에서 주의가 필요합니다.

VXLAN FDB 동적 학습 과정 VM-A (VTEP-A) MAC: aa:11, IP: 10.0.0.1 Inner src: aa:11 1. 패킷 전송 캡슐화된 VXLAN 패킷 Outer src: 10.0.0.1 → Outer dst: 10.0.0.2 Inner src MAC: aa:11, VNI: 100 2. UDP 전달 VTEP-B (10.0.0.2) vxlan_rcv() 호출 VXLAN_F_LEARN 활성 3. vxlan_snoop() FDB 엔트리 생성 MAC aa:11 → VTEP 10.0.0.1 state: NUD_REACHABLE, flags: NTF_SELF 에이징 타이머 (기본 300초) age_timer: updated 이후 미사용 시 삭제 FDB 엔트리 상태 전이 신규 학습 NUD_REACHABLE NUD_STALE 에이징: age_interval 경과 시 REACHABLE → STALE STALE 후 추가 미사용 → 자동 삭제 삭제 MAC 이동: VTEP 변경 감지 시 rdst->remote_ip 갱신 동적 학습의 보안 위험 공격자가 위조된 inner src MAC으로 패킷을 전송하면 FDB가 오염됨 (MAC hijacking) 대응: nolearning + 정적 FDB 또는 BGP EVPN으로 컨트롤 플레인 학습 maxaddress로 FDB 크기 제한, MAC flapping 모니터링 (dmesg "moved from")
VXLAN FDB 동적 학습: 수신 패킷의 inner src MAC → outer src IP 매핑을 자동 등록

정적 FDB 엔트리와 00:00:00:00:00:00

정적 FDB 엔트리는 에이징 없이 영구적으로 유지되며, 수동으로 관리됩니다. 특히 00:00:00:00:00:00 MAC 주소는 BUM 트래픽의 flood 대상을 지정하는 특수 엔트리입니다.

엔트리 유형설정 방법특성용도
BUM flood 대상bridge fdb append 00:00:00:00:00:00 dev vxlan0 dst <VTEP>여러 VTEP을 append로 추가, 삭제는 del헤드엔드 복제: 브로드캐스트/unknown unicast를 모든 등록 VTEP에 전송
유니캐스트 매핑bridge fdb add <MAC> dev vxlan0 dst <VTEP> self permanent에이징 없음, permanent 플래그알려진 MAC의 정확한 VTEP 매핑
동적 학습자동 (VXLAN_F_LEARN)에이징 적용, NUD_REACHABLE/STALE소규모 환경에서 자동 학습
EVPN 주입BGP 데몬(FRR)이 자동 추가offload 플래그, BGP 경로와 연동대규모 환경의 컨트롤 플레인 학습
# BUM 트래픽 처리: 헤드엔드 복제 목록 구축
# 각 원격 VTEP을 00:00:00:00:00:00 엔트리로 등록
bridge fdb append 00:00:00:00:00:00 dev vxlan100 dst 10.0.0.2
bridge fdb append 00:00:00:00:00:00 dev vxlan100 dst 10.0.0.3
bridge fdb append 00:00:00:00:00:00 dev vxlan100 dst 10.0.0.4

# BUM 전송 흐름:
# 1. ARP 요청(브로드캐스트) 또는 FDB miss(unknown unicast) 발생
# 2. vxlan_xmit() → default_dst의 remote 리스트 조회
# 3. 각 VTEP에 대해 vxlan_xmit_one() 호출 (유니캐스트 복제)
# 4. skb_clone()으로 패킷 복사 → 각 VTEP에 독립 전송

# 멀티캐스트 vs 유니캐스트 복제 비교:
# - 멀티캐스트: group 239.x.x.x → IGMP join으로 자동 수신
#   장점: VTEP 추가/제거 시 FDB 수정 불필요
#   단점: 언더레이에 멀티캐스트 라우팅 필수, WAN 불가
# - 유니캐스트: 00:00:00:00:00:00 엔트리 수동 관리
#   장점: L3 라우팅만으로 충분, WAN/클라우드 가능
#   단점: VTEP 수 ×N 복제 오버헤드, 수동 관리

# FDB 상세 조회 (플래그 해석)
bridge fdb show dev vxlan100
# 출력 예:
# 00:00:00:00:00:00 dev vxlan100 dst 10.0.0.2 self permanent
# 00:00:00:00:00:00 dev vxlan100 dst 10.0.0.3 self permanent
# aa:bb:cc:11:22:33 dev vxlan100 dst 10.0.0.2 self          ← 동적 학습
# dd:ee:ff:44:55:66 dev vxlan100 dst 10.0.0.3 self offload  ← EVPN 주입

BUM 트래픽 최적화 전략

대규모 VXLAN 환경에서 BUM 트래픽은 가장 큰 확장성 병목입니다. VTEP 수가 증가하면 헤드엔드 복제의 복제 팬아웃(fan-out)이 선형으로 증가하고, ARP 브로드캐스트가 모든 VTEP으로 전파됩니다. 다음 전략으로 BUM 트래픽을 최소화합니다.

전략효과설정
ARP SuppressionVTEP이 알려진 MAC/IP의 ARP 요청에 로컬 대리 응답 → ARP 브로드캐스트 감소bridge link set dev vxlan0 neigh_suppress on
ARP/ND ProxyVXLAN proxy 모드: FDB에 있는 MAC의 ARP에 VTEP이 응답ip link add vxlan0 type vxlan ... proxy
nolearning + EVPNBGP로 MAC/IP를 사전 배포 → 최초 패킷부터 유니캐스트nolearning + FRR EVPN
FDB 크기 제한FDB 폭증 방지 (DDoS 상황에서 메모리 보호)ip link set vxlan0 type vxlan maxaddress 2048
IGMP/MLD Snooping멀티캐스트 트래픽을 관심 있는 포트에만 전달브리지 레벨: echo 1 > /sys/class/net/br0/bridge/multicast_snooping

FDB 통지 (RTM_NEWNEIGH)

VXLAN FDB 엔트리가 추가/삭제/변경되면 커널은 RTM_NEWNEIGH 또는 RTM_DELNEIGH Netlink 메시지를 브로드캐스트합니다. 컨트롤 플레인 데몬(FRR, flannel, calico-node 등)은 이 통지를 구독하여 FDB 변화를 실시간(Real-time)으로 감지하고 반응합니다.

/* drivers/net/vxlan/vxlan_core.c — FDB 변경 통지 */
/* vxlan_fdb_notify()는 FDB 엔트리 변경 시 호출됨 */

static void vxlan_fdb_notify(
    struct vxlan_dev *vxlan,
    struct vxlan_fdb *fdb,
    struct vxlan_rdst *rd,
    int type,             /* RTM_NEWNEIGH or RTM_DELNEIGH */
    bool swdev_notify,
    struct netlink_ext_ack *extack)
{
    /* Netlink 메시지 구성: MAC, VTEP IP, VNI, state, flags */
    struct sk_buff *skb;
    struct net *net = dev_net(vxlan->dev);

    skb = nlmsg_new(vxlan_nlmsg_size(), GFP_ATOMIC);
    vxlan_fdb_info(skb, vxlan, fdb, 0, 0, type, 0, rd);

    /* RTNLGRP_NEIGH 그룹에 브로드캐스트 */
    rtnl_notify(skb, net, 0, RTNLGRP_NEIGH,
                NULL, GFP_ATOMIC);

    /* switchdev 통지 (HW 오프로드용) */
    if (swdev_notify)
        vxlan_fdb_switchdev_notifier_info(vxlan, fdb, rd, ...);
}

/* l2miss 통지: FDB miss 발생 시 userspace에 알림 */
/* → vxlan 생성 시 l2miss 옵션 활성화 필요 */
/* ip link add vxlan0 type vxlan ... l2miss */
/* 데몬이 RTM_GETNEIGH 수신 → 외부 DB 조회 → FDB 주입 */
# FDB 변경 실시간 모니터링
ip monitor neigh dev vxlan100
# 출력 예:
# aa:bb:cc:11:22:33 dev vxlan100 lladdr 10.0.0.2 REACHABLE  ← 새 학습
# aa:bb:cc:11:22:33 dev vxlan100 lladdr 10.0.0.2 STALE      ← 에이징
# Deleted aa:bb:cc:11:22:33 dev vxlan100 STALE               ← 삭제

# l2miss/l3miss 통지 활성화 (on-demand 학습용)
ip link add vxlan100 type vxlan id 100 dstport 4789 \
    local 10.0.0.1 nolearning l2miss l3miss

# l2miss 이벤트 (MAC 미스):
# → 커널이 RTM_GETNEIGH 전송, 데몬이 수신하여 FDB 추가
# l3miss 이벤트 (ARP 미스):
# → 커널이 RTM_GETNEIGH 전송, 데몬이 수신하여 neigh 추가

# FDB 에이징 시간 조정 (기본 300초)
ip link set vxlan100 type vxlan ageing 600  # 10분으로 연장
ip link set vxlan100 type vxlan ageing 0    # 에이징 비활성화 (정적 전용)

MAC 이동 감지와 처리

VM 라이브 마이그레이션이나 컨테이너 재시작(Reboot) 시 동일 MAC이 다른 VTEP에서 나타날 수 있습니다. VXLAN은 이를 MAC 이동(MAC mobility)으로 감지하고 FDB를 갱신합니다. EVPN 환경에서는 BGP Extended Community의 MAC Mobility 시퀀스 번호로 최신 위치를 판별합니다.

# MAC 이동 감지 (dmesg 로그)
dmesg | grep "moved from"
# [vxlan100] aa:bb:cc:11:22:33 moved from 10.0.0.2 to 10.0.0.3

# EVPN MAC Mobility 시퀀스 확인 (FRR)
vtysh -c "show bgp l2vpn evpn route type macip"
# Route [2]:[0]:[48]:[aa:bb:cc:11:22:33] ... MM: 3
# → 시퀀스 3: 이 MAC이 3번 이동했음을 의미

# MAC flapping 방지: 최대 이동 횟수 제한
vtysh -c "configure terminal"
vtysh -c "router bgp 65001"
vtysh -c " address-family l2vpn evpn"
vtysh -c "  mac-ip-dup-addr-detection max-moves 5 time 180"
# → 180초 내 5번 이상 이동 시 해당 MAC 고정(frozen)

VXLAN 커널 구현

Linux 커널의 VXLAN 구현(drivers/net/vxlan/)은 UDP 터널 인프라 위에 구축됩니다. VXLAN 디바이스의 생성부터 패킷 송수신까지의 내부 동작을 심층적으로 분석합니다.

소켓 관리와 공유

VXLAN은 UDP 소켓을 통해 캡슐화된 패킷을 송수신합니다. 동일 UDP 포트를 사용하는 여러 VXLAN 디바이스는 하나의 vxlan_sock을 공유하며, VNI 기반 해시 테이블(Hash Table)로 수신 패킷을 분배합니다.

VXLAN 소켓 공유 및 VNI 디멀티플렉싱 물리 NIC (eth0) UDP 수신 vxlan_sock UDP port: 4789 encap_rcv = vxlan_rcv refcnt: 3 (3개 디바이스 공유) VNI 추출 vni_list[VNI_HASH_SIZE] 해시 테이블 룩업 vxlan_vs_find_vni() vxlan_dev (VNI 100) fdb_head[], cfg br-tenant1 vxlan_dev (VNI 200) fdb_head[], cfg br-tenant2 vxlan_dev (VNI 300) fdb_head[], cfg br-tenant3 소켓 공유 메커니즘 vxlan_sock_add() → 동일 포트의 기존 소켓이 있으면 refcnt++로 공유 vxlan_sock_release() → refcnt-- → 0이면 소켓 해제 수천 개 VNI를 단일 UDP 소켓으로 효율적 처리 — 파일 디스크립터/메모리 절약
다수의 VXLAN 디바이스가 하나의 UDP 소켓을 공유하고 VNI로 분배
/* drivers/net/vxlan/vxlan_core.c — 소켓 공유 로직 */
static struct vxlan_sock *vxlan_socket_create(
    struct net *net, bool ipv6,
    __be16 port, u32 flags)
{
    struct vxlan_net *vn = net_generic(net, vxlan_net_id);
    struct vxlan_sock *vs;

    /* 동일 포트/플래그의 기존 소켓이 있으면 참조만 증가 */
    vs = vxlan_find_sock(net, ipv6 ? AF_INET6 : AF_INET,
                         port, flags, 0);
    if (vs) {
        if (!refcount_inc_not_zero(&vs->refcnt))
            return ERR_PTR(-EBUSY);
        return vs;
    }

    /* 신규 UDP 소켓 생성 */
    vs = kzalloc(sizeof(*vs), GFP_KERNEL);
    udp_sock_create(net, &udp_cfg, &vs->sock);

    /* encap_rcv 콜백 등록 → UDP 수신 시 vxlan_rcv() 호출 */
    setup_udp_tunnel_sock(net, vs->sock, &tunnel_cfg);
    refcount_set(&vs->refcnt, 1);

    hlist_add_head_rcu(&vs->hlist, &vn->sock_list[ipv6]);
    return vs;
}

vxlan_xmit 송신 경로 상세

VXLAN 송신 경로는 vxlan_xmit()vxlan_xmit_one()udp_tunnel_xmit_skb()의 단계를 거칩니다. 핵심은 FDB 룩업으로 목적지 VTEP을 결정하고, IP 라우팅 조회로 실제 출력 경로를 찾는 것입니다.

/* vxlan_xmit_one() 내부의 핵심 단계 (간략화) */

/* 1단계: 소스 포트 계산 — ECMP 분산의 핵심 */
/*   내부 프레임의 L2/L3/L4 해시로 소스 포트 결정 */
/*   동일 플로우 = 동일 소스 포트 = 동일 ECMP 경로 */
src_port = udp_flow_src_port(dev_net(dev), skb,
                              vxlan->cfg.port_min,
                              vxlan->cfg.port_max, true);

/* 2단계: 외부 IP 라우팅 조회 */
/*   VTEP IP를 목적지로 FIB 룩업 → 출력 디바이스/넥스트홉 결정 */
memset(&fl4, 0, sizeof(fl4));
fl4.daddr = rdst->remote_ip.sin.sin_addr.s_addr;
fl4.saddr = vxlan->cfg.saddr.sin.sin_addr.s_addr;
fl4.flowi4_proto = IPPROTO_UDP;
rt = ip_route_output_key(vxlan->net, &fl4);

/* 3단계: DF 비트 결정 */
/*   df=set: DF=1 (PMTUD 필수), df=unset: DF=0, df=inherit: 내부 IP 상속 */
if (vxlan->cfg.df == VXLAN_DF_SET)
    df = htons(IP_DF);

/* 4단계: VXLAN 헤더 push */
vxh = (struct vxlanhdr *)__skb_push(skb, sizeof(*vxh));
vxh->vx_flags = htonl(VXLAN_HF_VNI);
vxh->vx_vni = vxlan_vni_field(vni);

/* 5단계: UDP 터널 캡슐화 + IP 송신 */
udp_tunnel_xmit_skb(rt, sock4->sk, skb,
    fl4.saddr, fl4.daddr,
    tos, ttl, df, src_port, dst_port, xnet, !udp_sum);

vxlan_rcv 수신 경로 상세

VXLAN 수신은 UDP 레이어의 encap_rcv 콜백(Callback)으로 시작됩니다. vxlan_rcv()는 VXLAN 헤더를 파싱하고, VNI로 대상 디바이스를 찾고, 내부 프레임을 추출하여 네트워크 스택에 재주입합니다.

/* vxlan_rcv() 수신 경로 핵심 단계 (간략화) */

/* 1단계: VXLAN 헤더 검증 및 VNI 추출 */
vxh = (struct vxlanhdr *)(udp_hdr(skb) + 1);
if (!(vxh->vx_flags & htonl(VXLAN_HF_VNI))) {
    /* VNI 플래그 없음 → 유효하지 않은 VXLAN 패킷 → 드롭 */
    goto drop;
}
vni = vxlan_vni(vxh->vx_vni);

/* 2단계: VNI → vxlan_dev 디바이스 매핑 */
vxlan = vxlan_vs_find_vni(vs, skb->dev->ifindex, &vni);
if (!vxlan) {
    /* 매칭되는 VNI 없음 → drop, VXLAN_VNI_STATS 증가 */
    goto drop;
}

/* 3단계: 동적 학습 (VXLAN_F_LEARN 활성 시) */
if (vxlan->cfg.flags & VXLAN_F_LEARN)
    vxlan_snoop(skb->dev, &saddr, eth_hdr(skb)->h_source,
                skb->dev->ifindex, vni);

/* 4단계: 내부 프레임 추출 및 프로토콜 설정 */
skb_pull(skb, sizeof(*vxh));  /* VXLAN 헤더 제거 */
skb->protocol = eth_type_trans(skb, vxlan->dev);

/* 5단계: GRO(Generic Receive Offload) 처리 */
/* UDP GRO가 활성화된 경우 여러 VXLAN 패킷을 병합 */

/* 6단계: 네트워크 스택으로 전달 */
gro_cells_receive(&vxlan->gro_cells, skb);
/* → 내부 프레임이 브리지 또는 IP 스택으로 진행 */

vxlan_dev 핵심 구조체(Struct)

vxlan_dev는 VXLAN 디바이스의 모든 상태를 관리하는 핵심 구조체입니다. FDB 해시 테이블, 설정(cfg), 소켓 참조, 통계 등을 포함합니다.

/* include/net/vxlan.h — vxlan_dev 구조체 (주요 필드만) */
struct vxlan_dev {
    struct net_device     *dev;        /* 네트워크 디바이스 */
    struct net            *net;        /* 네트워크 네임스페이스 */
    struct vxlan_config   cfg;         /* 디바이스 설정 */

    /* FDB: MAC → VTEP IP 매핑 해시 테이블 */
    struct hlist_head    fdb_head[FDB_HASH_SIZE];
    unsigned int         addrcnt;     /* 현재 FDB 엔트리 수 */

    /* UDP 소켓 (IPv4/IPv6) */
    struct vxlan_sock __rcu *vn4_sock;
    struct vxlan_sock __rcu *vn6_sock;

    /* GRO 셀 (수신 오프로드) */
    struct gro_cells     gro_cells;

    /* default 원격 목적지 (BUM 트래픽 처리) */
    struct vxlan_rdst    default_dst;

    struct timer_list    age_timer;   /* FDB 에이징 타이머 */
    spinlock_t           hash_lock[FDB_HASH_SIZE];
};

/* vxlan_config: ip link add 시 설정되는 항목들 */
struct vxlan_config {
    union vxlan_addr   saddr;       /* local IP */
    union vxlan_addr   remote_ip;   /* remote IP (유니캐스트) */
    __be32             vni;         /* VNI (24비트) */
    __be16             dst_port;    /* UDP 목적지 포트 */
    struct ip_tunnel_port_range port_range; /* 소스 포트 범위 */
    u32                flags;       /* VXLAN_F_* 플래그들 */
    u8                 df;          /* DF 비트 정책 */
    u32                age_interval;/* FDB 에이징 간격 (초) */
    u32                addrmax;     /* 최대 FDB 엔트리 수 */
    int                remote_ifindex; /* 출력 인터페이스 인덱스 */
};

/* 주요 VXLAN_F_* 플래그 */
/* VXLAN_F_LEARN     — 동적 FDB 학습 활성화 */
/* VXLAN_F_PROXY     — ARP 프록시 모드 */
/* VXLAN_F_L2MISS    — L2 miss 통지 (l2miss) */
/* VXLAN_F_L3MISS    — L3 miss 통지 (l3miss) */
/* VXLAN_F_COLLECT_METADATA — external 모드 (flow-based) */
/* VXLAN_F_GPE       — VXLAN-GPE 모드 */
/* VXLAN_F_GBP       — Group-Based Policy 확장 */

VXLAN-GPE 확장 상세

VXLAN-GPE(RFC 9136)는 Next Protocol 필드를 통해 이더넷 외의 프로토콜을 직접 캡슐화할 수 있어, SFC(Service Function Chaining)와 같은 고급 네트워크 서비스에 사용됩니다. Linux 커널에서는 VXLAN_F_GPE 플래그로 활성화합니다.

/* GPE 수신 처리 — vxlan_rcv() 내부 */
if (vxlan->cfg.flags & VXLAN_F_GPE) {
    struct vxlanhdr_gpe *gpe = (struct vxlanhdr_gpe *)vxh;

    /* Next Protocol에 따라 내부 페이로드 해석 */
    switch (gpe->np) {
    case VXLAN_GPE_NP_IPV4:
        skb->protocol = htons(ETH_P_IP);
        break;
    case VXLAN_GPE_NP_IPV6:
        skb->protocol = htons(ETH_P_IPV6);
        break;
    case VXLAN_GPE_NP_ETHERNET:
        skb->protocol = htons(ETH_P_TEB);
        break;
    case VXLAN_GPE_NP_NSH:
        skb->protocol = htons(ETH_P_NSH);
        break;
    }
    /* GPE는 이더넷 헤더를 포함하지 않을 수 있으므로 */
    /* eth_type_trans() 대신 직접 protocol 설정 */
}

GENEVE TLV 옵션

GENEVE의 가장 큰 차별점은 가변 길이 TLV(Type-Length-Value) 옵션입니다. 각 옵션은 Option Class(16비트 벤더/표준 식별자), Type(8비트), Length(5비트, ×4바이트), Data로 구성됩니다. 이 구조 덕분에 보안 태그, QoS 정보, 디버깅 메타데이터, 서비스 체이닝 정보 등을 유연하게 전달할 수 있습니다.

GENEVE TLV 옵션 상세 구조 단일 TLV 옵션 (4+N*4 bytes) Option Class (16b) IANA 할당 벤더 ID Type (8b) bit7=C (critical) R|R|R|Len(5b) ×4 bytes (0~31) Option Data (0~124 bytes) 4바이트 정렬, 가변 길이 주요 Option Class 할당 0x0100 — Linux (커널 내부용) 0x0101 — OVS (Open vSwitch) 0x0102 — VMware NSX 0x0103 — AWS VPC 0x0104~0xFEFF — 벤더/사용자 정의 TLV 옵션 활용 사례 보안 그룹 태그 (SNAT/DNAT 정책) QoS 우선순위/대역폭 정보 서비스 체이닝 컨텍스트 (SFC) 디버깅 트레이스 ID (분산 추적) 멀티 테넌트 메타데이터 (VPC ID 등) 다중 TLV 체이닝 예시 (GENEVE 헤더 내) TLV #1: 보안 태그 Class:0x0101 Type:1 Len:1 Data: 0x00000042 TLV #2: QoS 마킹 Class:0x0101 Type:2 Len:1 Data: 0x00000007 TLV #3: 트레이스 ID Class:0x0102 Type:5 Len:2 Data: 8-byte trace-id Inner Frame 원본 L2 프레임 GENEVE Opt Len = (4+4) + (4+4) + (4+8) = 28 bytes → Opt Len 필드 = 7 (28÷4) 총 GENEVE 헤더: 8(고정) + 28(옵션) = 36 bytes, IPv4 오버헤드: 50+28 = 78 bytes
GENEVE TLV 옵션: 벤더별 Class로 구분되는 다중 메타데이터 전달

OVS (Open vSwitch) GENEVE 연동

Open vSwitch는 GENEVE를 기본 터널 프로토콜로 사용하며, TLV 옵션을 통해 OVS 파이프라인(Pipeline)의 메타데이터를 원격 노드로 전달합니다. OVS는 GENEVE의 external 모드를 사용하여 단일 터널 디바이스로 모든 VNI를 처리합니다.

# === OVS + GENEVE 구성 예시 ===

# OVS에서 GENEVE 터널 포트 추가
ovs-vsctl add-br br-int
ovs-vsctl add-port br-int geneve0 -- \
    set interface geneve0 type=geneve \
    options:remote_ip=flow \
    options:key=flow \
    options:dst_port=6081

# TLV 옵션 매핑 (OVS 메타데이터 ↔ GENEVE TLV)
# Class 0x0101 Type 0 = OVS 보안 그룹 ID
ovs-vsctl set Open_vSwitch . \
    other_config:geneve_opts='{class=0x0101,type=0,len=4}'

# OpenFlow에서 GENEVE 옵션 기반 매칭
ovs-ofctl add-flow br-int \
    "table=0,tun_metadata0=0x42,actions=output:LOCAL"

# OVS GENEVE 터널 상태 확인
ovs-vsctl show
ovs-appctl dpif/show
ovs-appctl tnl/ports/show
/* OVS에서 GENEVE TLV 옵션 읽기/쓰기 (커널 datapath) */
/* net/openvswitch/flow_netlink.c */

/* tun_metadata0~63: OVS가 GENEVE 옵션을 64개 슬롯에 매핑 */
/* 각 슬롯은 (class, type) 조합으로 식별 */
struct geneve_opt_map {
    __be16 opt_class;  /* GENEVE Option Class */
    u8     type;       /* GENEVE Type */
    u8     len;        /* 옵션 데이터 길이 (bytes) */
    u32    offset;     /* tun_metadata 내 오프셋 */
};
Critical 비트: GENEVE Type 필드의 최상위 비트(bit 7)는 Critical 플래그입니다. 이 비트가 설정된 옵션을 수신 VTEP이 이해하지 못하면, 해당 패킷을 반드시 드롭해야 합니다. 이는 보안/정책 옵션이 무시되는 것을 방지하는 안전장치입니다. GENEVE 헤더의 C 비트는 패킷에 Critical 옵션이 하나라도 포함되어 있음을 나타냅니다.

tc flower tunnel_key를 통한 TLV 옵션 설정

TC(Traffic Control)의 flower classifier와 tunnel_key set 액션을 결합하면 커널 데이터 경로에서 GENEVE TLV 옵션을 직접 설정할 수 있습니다. 이 방식은 OVS 없이도 GENEVE 메타데이터를 활용할 수 있어 경량 오버레이에 유용합니다.

# === TC flower + GENEVE TLV 옵션 설정 ===

# external 모드 GENEVE 디바이스 생성
ip link add geneve0 type geneve external dstport 6081
ip link set geneve0 up

# TC qdisc 설정
tc qdisc add dev eth0 clsact

# GENEVE 캡슐화 + TLV 옵션 삽입
# geneve_opts 형식: class:type:data (16진수)
tc filter add dev eth0 ingress \
    flower \
    src_mac 52:54:00:aa:bb:01 \
    action tunnel_key set \
        id 1000 \
        src_ip 10.0.0.1 \
        dst_ip 10.0.0.2 \
        dst_port 6081 \
        geneve_opts 0101:01:00000042 \
    action mirred egress redirect dev geneve0

# 다중 TLV 옵션 설정 (콤마로 구분)
tc filter add dev eth0 ingress \
    flower \
    src_mac 52:54:00:cc:dd:02 \
    action tunnel_key set \
        id 2000 \
        src_ip 10.0.0.1 \
        dst_ip 10.0.0.3 \
        dst_port 6081 \
        geneve_opts 0101:01:00000042,0101:02:00000007 \
    action mirred egress redirect dev geneve0

# GENEVE TLV 옵션 기반 매칭 (역캡슐화 측)
tc filter add dev geneve0 ingress \
    flower \
    enc_key_id 1000 \
    enc_dst_port 6081 \
    geneve_opts 0101:01:00000042 \
    action mirred egress redirect dev veth-vm1

# 설정 확인
tc -s filter show dev eth0 ingress

GENEVE 커널 내부 구조체

/* drivers/net/geneve.c — 핵심 구조체 */

struct geneve_dev {
    struct net_device     *dev;
    struct net            *net;
    struct geneve_sock __rcu *sock4;
    struct geneve_sock __rcu *sock6;
    struct ip_tunnel_info info;     /* 터널 메타데이터 */
    bool                  collect_md; /* external 모드 */
    bool                  use_udp6_rx_checksums;
};

/* GENEVE 옵션은 ip_tunnel_info의 옵션 배열에 저장 */
/* struct geneve_opt: 단일 TLV 옵션 헤더 */
struct geneve_opt {
    __be16 opt_class;   /* Option Class (벤더/표준 ID) */
    u8     type;        /* bit7=critical, bit0-6=type */
#ifdef __LITTLE_ENDIAN_BITFIELD
    u8     length:5;   /* 데이터 길이 (×4 bytes) */
    u8     r3:1;
    u8     r2:1;
    u8     r1:1;
#endif
    u8     opt_data[];  /* 가변 길이 옵션 데이터 */
};

EVPN 연동

BGP EVPN(RFC 7432, RFC 8365)은 VXLAN 오버레이의 컨트롤 플레인으로, MAC/IP 주소를 BGP 라우팅 프로토콜을 통해 분산 학습합니다. 데이터 플레인의 flood & learn 방식을 대체하여, 대규모 환경에서 수렴 시간과 BUM 트래픽을 크게 개선합니다.

EVPN Type-2 MAC/IP 광고 흐름 VTEP-A (Leaf-1) 10.0.0.1, AS 65001 VNI 100, br100 + vxlan100 VM-A 부팅 MAC: aa:11, IP: 10.200.0.1 1. 로컬 MAC 학습 FRR (zebra → bgpd) Type-2 Route 생성 Route Reflector / Spine BGP l2vpn evpn 세션 RT(Route Target) 기반 필터링 Type-2: [MAC aa:11, IP 10.200.0.1, VNI 100] 2. BGP Update VTEP-B (Leaf-2) 10.0.0.2, AS 65002 VNI 100, br100 + vxlan100 3. BGP Update 전파 FRR (bgpd → zebra) Type-2 수신 → FDB 주입 4. netlink 커널 FDB 엔트리 aa:11 → 10.0.0.1 [offload] EVPN Route Types Type-2 (MAC/IP) MAC + IP + VNI 광고 FDB + ARP 테이블 자동 구축 ARP suppression 활성화 Type-3 (Inclusive Multicast) VTEP 존재 광고 BUM 복제 리스트 자동 구축 Ingress Replication 자동화 Type-5 (IP Prefix) L3 VNI + IP prefix 광고 Symmetric IRB 라우팅 VRF 간 서브넷 연결
EVPN Type-2: VM 부팅 시 MAC/IP가 BGP로 광고되어 원격 VTEP의 FDB에 자동 등록

FRR VXLAN+EVPN 설정 상세

FRR(Free Range Routing)에서 VXLAN+EVPN을 구성하려면 bgpdzebra의 연동이 핵심입니다. zebra가 VXLAN 디바이스를 감지하여 VNI를 BGP에 통보하고, bgpd가 Type-2/3/5 경로를 광고/수신합니다.

# === FRR 완전 설정 예시 (vtysh) ===

# Step 1: BGP 기본 설정
router bgp 65001
  bgp router-id 10.0.0.1
  no bgp default ipv4-unicast
  neighbor SPINE peer-group
  neighbor SPINE remote-as 65000
  neighbor SPINE update-source lo
  neighbor 10.255.0.1 peer-group SPINE
  neighbor 10.255.0.2 peer-group SPINE
  !
  address-family l2vpn evpn
    neighbor SPINE activate
    advertise-all-vni
  exit-address-family

# Step 2: VRF 설정 (Symmetric IRB용)
router bgp 65001 vrf tenant1
  !
  address-family ipv4 unicast
    redistribute connected
  exit-address-family
  !
  address-family l2vpn evpn
    advertise ipv4 unicast
  exit-address-family

# Step 3: 상태 확인 명령
show bgp l2vpn evpn summary
# Neighbor  V  AS  Up/Down  State  PfxRcd  PfxSnt
# 10.255.0.1 4 65000 01:23:45  14      12

show bgp l2vpn evpn route type macip
# Route [2]:[0]:[48]:[aa:bb:cc:11:22:33]:[32]:[10.200.0.1]
#   Paths: 10.0.0.2, VNI 100, label 100
#   Extended Community: RT:65001:100 ET:8

show bgp l2vpn evpn route type prefix
# Route [5]:[0]:[24]:[10.200.0.0]
#   Paths: 10.0.0.2, VNI 9999
#   Extended Community: RT:65001:9999 Router MAC:00:00:5e:00:01:01

show evpn vni
# VNI    Type  VxLAN IF  VTEP IP    # MACs  # ARPs
# 100    L2    vxlan100  10.0.0.1   5       3
# 9999   L3    vxlan-l3  10.0.0.1

Asymmetric vs Symmetric IRB 비교

항목Asymmetric IRBSymmetric IRB
라우팅 위치Ingress VTEP에서만 라우팅Ingress + Egress VTEP 모두 라우팅
L3 VNI불필요테넌트별 L3 VNI 필수 (예: VNI 9999)
VNI 요구사항모든 서브넷 VNI가 모든 VTEP에 존재관련 서브넷 VNI만 해당 VTEP에 존재
패킷 전달 VNI목적지 서브넷의 L2 VNI로 전달L3 VNI(VRF)로 전달
확장성모든 VTEP에 모든 서브넷 필요 → 비효율VTEP별 필요 서브넷만 → 확장 우수
구현 복잡도단순 (L3 VNI 불필요)복잡 (VRF + L3 VNI + Anycast GW)
권장 환경소규모, 모든 VTEP이 모든 서브넷 사용대규모 데이터센터, 선택적 서브넷 배치

분산 게이트웨이 (Symmetric IRB)

EVPN Symmetric IRB(Integrated Routing and Bridging)은 모든 Leaf 스위치(VTEP)가 동일한 Anycast Gateway IP/MAC으로 L3 라우팅을 수행하는 아키텍처입니다. 테넌트별 VRF와 L3 VNI를 사용하여 오버레이 네트워크 간 라우팅을 분산 처리합니다.

# === Symmetric IRB 구성 (FRR + Linux) ===

# 1. VRF 생성 (테넌트별)
ip link add vrf-tenant1 type vrf table 100
ip link set vrf-tenant1 up

# 2. L3 VNI 브리지 + VXLAN (라우팅용)
ip link add br-l3vni type bridge
ip link add vxlan-l3 type vxlan id 9999 local 10.0.0.1 dstport 4789 nolearning
ip link set vxlan-l3 master br-l3vni
ip link set br-l3vni master vrf-tenant1
ip link set vxlan-l3 up
ip link set br-l3vni up

# 3. L2 VNI 브리지 + VXLAN (브리징용)
ip link add br100 type bridge
ip link add vxlan100 type vxlan id 100 local 10.0.0.1 dstport 4789 nolearning
ip link set vxlan100 master br100
ip link set br100 master vrf-tenant1
ip link set vxlan100 up
ip link set br100 up

# 4. Anycast Gateway (모든 Leaf에서 동일 IP/MAC)
ip link set br100 address 00:00:5e:00:01:01
ip addr add 10.200.0.254/24 dev br100

# 5. FRR 설정
# router bgp 65001
#   address-family l2vpn evpn
#     advertise-all-vni
#   !
# router bgp 65001 vrf tenant1
#   address-family ipv4 unicast
#     redistribute connected
#   address-family l2vpn evpn
#     advertise ipv4 unicast

# 패킷 흐름 (Symmetric IRB):
# VM-A(10.200.0.1/VNI 100) → VM-C(10.201.0.1/VNI 200)
# 1. VM-A → Anycast GW(br100) → VRF 라우팅
# 2. L3 VNI 9999로 캡슐화 → VTEP-B 전송
# 3. VTEP-B: L3 VNI 역캡슐화 → VRF 라우팅 → VNI 200 → VM-C

VXLAN + Bridge 통합

Linux Bridge와 VXLAN 디바이스의 통합은 오버레이 네트워크 구성의 핵심 패턴입니다. VXLAN 디바이스를 브리지 포트로 연결하면 로컬 VM/컨테이너와 원격 오버레이 네트워크가 하나의 L2 도메인으로 통합됩니다.

master/self FDB 관계

VXLAN 디바이스가 브리지에 연결되면 두 가지 FDB가 존재합니다. master FDB는 브리지가 관리하는 포트별 MAC 테이블이고, self FDB는 VXLAN 디바이스 자체의 MAC→VTEP 매핑입니다. 이 두 FDB의 관계를 이해하는 것이 멀티테넌트 구성의 핵심입니다.

VXLAN + Bridge: master/self FDB 관계 Linux Bridge (br0) master FDB: MAC → 포트 번호 매핑 bridge fdb show: "aa:11 dev vxlan0 master br0" bridge fdb show: "bb:22 dev veth-vm1 master br0" → 프레임을 어느 포트로 보낼지 결정 veth-vm1 (로컬 포트) MAC bb:22 → 로컬 VM/컨테이너 master FDB만 참여 vxlan0 (터널 포트) master + self FDB 모두 참여 self FDB: MAC → VTEP IP VXLAN self FDB aa:11 dst 10.0.0.2 self ← 유니캐스트 매핑 00:00:00:00:00:00 dst 10.0.0.2 self ← BUM flood 00:00:00:00:00:00 dst 10.0.0.3 self ← BUM flood 패킷 흐름: br0 master FDB → dst MAC이 vxlan0 포트 → vxlan0 self FDB → VTEP IP → 캡슐화 → 송신 bridge fdb add 시 "self" 플래그 = VXLAN FDB, "master" 플래그 = Bridge FDB, 둘 다 생략 = 둘 다
master FDB(브리지)가 포트를 결정하고, self FDB(VXLAN)가 VTEP IP를 결정하는 2단계 룩업
# master vs self FDB 엔트리 제어

# self: VXLAN 디바이스 자체의 MAC→VTEP 매핑에만 추가
bridge fdb add aa:bb:cc:dd:ee:ff dev vxlan0 dst 10.0.0.2 self

# master: 브리지의 포트 매핑에만 추가
bridge fdb add aa:bb:cc:dd:ee:ff dev vxlan0 master

# 둘 다 (EVPN이 사용하는 방식)
bridge fdb add aa:bb:cc:dd:ee:ff dev vxlan0 dst 10.0.0.2

# FDB 조회 시 플래그 해석
bridge fdb show dev vxlan0
# aa:bb:cc:dd:ee:ff dev vxlan0 master br0     ← 브리지 FDB
# aa:bb:cc:dd:ee:ff dev vxlan0 dst 10.0.0.2 self ← VXLAN FDB
# dd:ee:ff:11:22:33 dev vxlan0 dst 10.0.0.2 self offload ← HW 오프로드됨

VXLAN-Aware Bridge (per-VNI)

VXLAN-aware 브리지는 vlan_filtering + external VXLAN + bridge vlan tunnel 명령으로 구성하며, 하나의 VXLAN 디바이스로 여러 VNI를 처리합니다. 각 VLAN ID를 VNI에 매핑하여 멀티테넌트 환경을 효율적으로 구현합니다.

# === VXLAN-Aware Bridge 구성 ===

# 1. VLAN-aware 브리지 (기본 PVID 없음)
ip link add br0 type bridge vlan_filtering 1 vlan_default_pvid 0
ip link set br0 up

# 2. external 모드 VXLAN (단일 디바이스로 모든 VNI 처리)
ip link add vxlan0 type vxlan \
    external \
    dstport 4789 \
    local 10.0.0.1
ip link set vxlan0 master br0
ip link set vxlan0 up

# 3. VLAN ↔ VNI 매핑
# VID 10 → VNI 100010 (테넌트 A)
bridge vlan add vid 10 dev vxlan0
bridge vlan tunnel add dev vxlan0 vid 10 tunnel_id 100010

# VID 20 → VNI 100020 (테넌트 B)
bridge vlan add vid 20 dev vxlan0
bridge vlan tunnel add dev vxlan0 vid 20 tunnel_id 100020

# VID 30 → VNI 100030 (테넌트 C)
bridge vlan add vid 30 dev vxlan0
bridge vlan tunnel add dev vxlan0 vid 30 tunnel_id 100030

# 4. VM/컨테이너 access 포트
ip link set tap-vm1 master br0
bridge vlan add vid 10 dev tap-vm1 pvid untagged  # 테넌트 A

ip link set tap-vm2 master br0
bridge vlan add vid 20 dev tap-vm2 pvid untagged  # 테넌트 B

# 5. BUM flood 대상 VTEP (per-VNI FDB)
bridge fdb append 00:00:00:00:00:00 dev vxlan0 dst 10.0.0.2 self
bridge fdb append 00:00:00:00:00:00 dev vxlan0 dst 10.0.0.3 self

# 6. 매핑 확인
bridge vlan tunnel show dev vxlan0
# port    vlan ids tunnel id
# vxlan0  10       100010
# vxlan0  20       100020
# vxlan0  30       100030

Single-Device VXLAN 아키텍처

Single-device VXLAN은 하나의 VXLAN 디바이스(external 모드)로 모든 VNI를 처리하는 구성입니다. 기존의 VNI별 VXLAN 디바이스 방식보다 시스템 리소스를 절약하고 관리 복잡도를 크게 낮춥니다. EVPN 환경에서 표준 구성으로 자리잡았습니다.

Single-Device vs Multi-Device VXLAN Multi-Device (기존 방식) vxlan100 (VNI 100) br100 vxlan200 (VNI 200) br200 vxlan300 (VNI 300) br300 VNI당 1개 vxlan + 1개 bridge 100 VNI = 200개 netdev 리소스 낭비, 복잡한 관리 UDP 소켓 공유는 되지만 디바이스 오버헤드 큼 Single-Device (권장) vxlan0 (external) 단일 디바이스, 모든 VNI 처리 br0 (vlan_filtering=1) VID 10↔VNI 100, VID 20↔VNI 200, ... 100 VNI = 2개 netdev (vxlan0 + br0) 효율적, EVPN 표준 구성 비교 요약 netdev 수 (100 VNI): Multi: 200개 Single: 2개 FDB 관리: 디바이스별 분산 통합 FDB + VLAN 필터 EVPN 연동: VNI당 FDB 동기화 bridge vlan tunnel 자동 매핑 HW 오프로드: 디바이스별 규칙 switchdev 통합 오프로드 구성 난이도: 자동화 필수 선언적 VID↔VNI 매핑
Single-device VXLAN: 하나의 external VXLAN + VLAN-aware 브리지로 전체 VNI 통합 관리

per-VNI 학습과 FDB 분리

VXLAN-aware 브리지 구성에서도 FDB 학습은 VNI별로 분리됩니다. VLAN ID가 다르면 다른 FDB 네임스페이스(Namespace)에 속하므로, 동일 MAC 주소라도 다른 VNI에서 독립적으로 학습됩니다. 이는 멀티테넌트 격리의 핵심입니다.

# per-VNI 학습 동작 확인

# VLAN 10 (VNI 100) 소속 MAC 학습
bridge fdb show dev vxlan0 vlan 10
# aa:11:22:33:44:55 dev vxlan0 vlan 10 master br0
# aa:11:22:33:44:55 dev vxlan0 dst 10.0.0.2 self

# VLAN 20 (VNI 200) 소속 MAC 학습 — 동일 MAC도 가능
bridge fdb show dev vxlan0 vlan 20
# bb:22:33:44:55:66 dev vxlan0 vlan 20 master br0

# VLAN-aware bridge에서 neigh_suppress 설정
bridge link set dev vxlan0 neigh_suppress on
# → ARP suppression이 VNI별로 동작

# bridge vlan 통계 확인
bridge -s vlan show dev vxlan0
# → VID별 RX/TX 바이트/패킷 확인

SmartNIC VTEP 오프로드

최신 SmartNIC(NVIDIA ConnectX-6/7, Intel E810, Broadcom Stingray)은 VXLAN/GENEVE 캡슐화를 하드웨어에서 수행하여 CPU 부하를 거의 없앱니다. TC flower의 tunnel_key 액션과 eSwitch switchdev 모드를 결합하면 오버레이 패킷이 커널을 완전히 우회합니다.

# === TC Flower + tunnel_key 오프로드 예시 ===

# 1. eSwitch switchdev 모드 전환 (mlx5)
devlink dev eswitch set pci/0000:03:00.0 mode switchdev

# 2. external VXLAN 디바이스 생성
ip link add vxlan-offload type vxlan external dstport 4789
ip link set vxlan-offload up

# 3. VF Representor에서 오프로드 규칙 설치
# VM(VF)에서 나가는 트래픽 → VXLAN 캡슐화 (NIC이 수행)
tc qdisc add dev enp3s0f0_0 clsact
tc filter add dev enp3s0f0_0 ingress \
    flower \
    src_mac 52:54:00:aa:bb:01 \
    dst_mac 52:54:00:cc:dd:02 \
    action tunnel_key set \
        id 100 \
        src_ip 10.0.0.1 \
        dst_ip 10.0.0.2 \
        dst_port 4789 \
    action mirred egress redirect dev vxlan-offload

# 4. 원격 VTEP에서 오는 VXLAN 트래픽 → VF로 전달 (NIC이 역캡슐화)
tc qdisc add dev vxlan-offload clsact
tc filter add dev vxlan-offload ingress \
    flower \
    enc_src_ip 10.0.0.2 \
    enc_dst_ip 10.0.0.1 \
    enc_key_id 100 \
    enc_dst_port 4789 \
    dst_mac 52:54:00:aa:bb:01 \
    action tunnel_key unset \
    action mirred egress redirect dev enp3s0f0_0

# 5. 오프로드 확인
tc -s filter show dev enp3s0f0_0 ingress
# in_hw in_hw_count 1 ← 하드웨어 오프로드 확인
# pkts: 1234567  bytes: 987654321 ← NIC 카운터

# 6. conntrack offload와 결합 (ESTABLISHED 연결은 완전 HW 처리)
tc filter add dev enp3s0f0_0 ingress \
    flower \
    ct_state +trk+est \
    action tunnel_key set id 100 ... \
    action mirred egress redirect dev vxlan-offload
오프로드 대상 확인: NIC이 지원하는 터널 오프로드 유형은 ethtool --show-tunnels eth0으로 확인합니다. VXLAN/GENEVE/GRE 각각의 오프로드 포트가 등록되어 있어야 합니다. TSO(TCP Segmentation Offload)가 내부 TCP까지 적용되는지 ethtool -k eth0 | grep tx-udp_tnl로 확인하세요.

NIC별 VTEP 오프로드 지원 비교

NICVXLAN 캡슐화/해제GENEVE 캡슐화/해제Inner TSO/LROTC flower 오프로드switchdev비고
NVIDIA CX-6 DxOOO (TSO/LRO/GRO)O (conntrack 포함)OVF LAG, ECMP 오프로드
NVIDIA CX-7OOOO (crypto 포함)O400G, IPsec+VXLAN 결합
Intel E810OOO (TSO)O (switchdev 모드)O (ice)ADQ 결합 가능
Broadcom P2100OOOOO (bnxt)TruFlow 엔진
범용 NICRx Checksum만Rx Checksum만외부 TSO만XX소프트웨어 처리
VTEP 오프로드 성능 비교 (100Gbps NIC, VXLAN TCP) 100 Gbps 75 Gbps 50 Gbps 25 Gbps ~98 Gbps Full HW Offload (TC flower+switchdev) ~88 Gbps TSO+Checksum (Inner TSO offload) ~60 Gbps Checksum Only (Rx checksum offload) ~30 Gbps Software Only (CPU 캡슐화/해제)
오프로드 수준에 따른 VXLAN 처리량 차이: Full HW offload는 SW 대비 3배 이상 성능
# === NIC 오프로드 상태 상세 확인 ===

# 터널 오프로드 등록 포트 확인
ethtool --show-tunnels eth0
# Tunnel offload information for eth0:
#   rx-udp-tunnel-port-table 0:
#     entry 0: type vxlan, port 4789
#     entry 1: type geneve, port 6081

# Inner TSO 오프로드 확인
ethtool -k eth0 | grep tnl
# tx-udp_tnl-segmentation: on        ← VXLAN/GENEVE 내부 TSO
# tx-udp_tnl-csum-segmentation: on   ← + 체크섬 포함

# GRO(Generic Receive Offload) 내부 패킷 처리
ethtool -k eth0 | grep gro
# rx-udp-gro-forwarding: on          ← UDP 터널 GRO

# TC flower 오프로드 규칙의 HW 카운터 확인
tc -s filter show dev enp3s0f0_0 ingress
# filter ... in_hw in_hw_count 1
#   action ... hw_stats immediate
#   Sent 98765432 bytes 654321 pkt (dropped 0)

MTU 관리

VXLAN/GENEVE 오버레이의 캡슐화 오버헤드로 인해 MTU 관리는 오버레이 네트워크에서 가장 빈번한 문제의 원인입니다. 패킷 단편화(fragmentation)나 Path MTU Discovery(PMTUD) 블랙홀을 방지하려면 정확한 오버헤드 계산이 필수적입니다.

VXLAN/GENEVE 캡슐화 오버헤드 계산 VXLAN over IPv4: 50 bytes Outer Eth 14B Outer IP 20B UDP 8B VXLAN 8B = 50B 물리 MTU 1500 → 내부 MTU = 1450 물리 MTU 9000 → 내부 MTU = 8950 권장: 점보 프레임 9000 + 내부 8950 VXLAN over IPv6: 70 bytes Outer Eth 14B Outer IP 40B UDP 8B VXLAN 8B = 70B 물리 MTU 1500 → 내부 MTU = 1430 물리 MTU 9000 → 내부 MTU = 8930 IPv6: 20바이트 추가 오버헤드 주의 GENEVE: 50B(IPv4) / 70B(IPv6) + TLV 옵션 크기 Opt Len = 0: VXLAN과 동일. TLV 옵션 추가 시 4바이트 단위로 증가 (최대 +252바이트) 예: 3개 TLV(28B) 사용 시 → IPv4 오버헤드 = 50 + 28 = 78B → 내부 MTU = 9000 - 78 = 8922 DF 비트와 PMTUD df=set: DF=1 → 외부 IP에서 단편화 금지, ICMP "need to frag" 수신 필요 (PMTUD) df=unset: DF=0 → 단편화 허용 (성능 저하 가능), df=inherit: 내부 IP의 DF 상속 PMTUD 블랙홀 주의: 방화벽이 ICMP를 차단하면 TCP 대용량 전송이 멈춤 → MSS clamping 권장
오버헤드 계산: 프로토콜, IP 버전, TLV 옵션에 따라 내부 MTU가 달라짐
# === MTU 설정 가이드 ===

# 1. 물리 NIC 점보 프레임 (모든 VTEP과 중간 스위치에 동일 적용)
ip link set eth0 mtu 9000

# 2. VXLAN/GENEVE 디바이스 MTU
ip link set vxlan100 mtu 8950   # 9000 - 50 (VXLAN IPv4)
ip link set geneve100 mtu 8950  # 9000 - 50 (GENEVE, 옵션 없이)

# 3. 브리지 MTU (VXLAN MTU와 동일)
ip link set br0 mtu 8950

# 4. DF 비트 설정
ip link add vxlan100 type vxlan id 100 ... df set     # PMTUD 활성화
ip link add vxlan100 type vxlan id 100 ... df unset   # 단편화 허용
ip link add vxlan100 type vxlan id 100 ... df inherit # 내부 DF 상속

# 5. MSS clamping (PMTUD 블랙홀 방지)
iptables -A FORWARD -p tcp --tcp-flags SYN,RST SYN \
    -j TCPMSS --clamp-mss-to-pmtu

# 6. MTU 검증
# 오버레이 네트워크에서 최대 패킷 크기 테스트
ping -M do -s 8922 10.200.0.2  # 8922 + 28(IP+ICMP) = 8950

# PMTUD 확인
ip route get 10.200.0.2
# 10.200.0.2 via ... mtu 8950 ← PMTUD 결과 확인

MTU 관련 문제 진단

MTU 불일치는 오버레이 네트워크에서 가장 흔한 문제입니다. 증상은 ping은 되지만 대용량 TCP 전송(SSH, HTTP 다운로드)이 멈추는 현상입니다. PMTUD 블랙홀과 단편화 문제를 체계적으로 진단하는 방법을 정리합니다.

증상원인진단 방법해결
ping OK, TCP 멈춤PMTUD 블랙홀 (방화벽이 ICMP 차단)ping -M do -s 1450 <dst> 실패 확인MSS clamping 또는 ICMP 허용
간헐적 패킷 손실중간 홉 MTU 불일치 (단편화 + 재조합 실패)tracepath <dst>로 경로별 MTU 확인전체 경로 MTU 통일
성능 저하 (CPU 높음)DF=0으로 단편화 발생, CPU가 재조합nstat -s | grep -i fragdf set으로 변경 + MTU 조정
vxlan TX error 증가VXLAN MTU > 물리 MTU - 오버헤드ip -s link show vxlan0 TX errors 확인VXLAN MTU 하향 조정
TLV 추가 후 장애GENEVE 옵션 크기만큼 MTU 추가 감소 필요옵션 크기 계산 후 MTU 재설정내부 MTU = 물리 MTU - 50 - TLV 크기
# MTU 문제 종합 진단 스크립트

# 1. 전 경로 MTU 확인
tracepath -n 10.200.0.2
# Resume: pmtu 1500 hops 3 back 3

# 2. 단편화 통계 확인
nstat -s | grep -i -E "frag|reasm"
# IpFragOKs   0      ← 정상: 단편화 없음
# IpFragFails 0      ← 0이어야 함
# IpReasmReqds 0     ← 재조합 요청 없어야 함

# 3. VXLAN 디바이스 통계에서 에러 확인
ip -s link show dev vxlan100
# TX: ... errors N ← N > 0이면 MTU 문제 가능

# 4. 전체 체인 MTU 일관성 검증
# 물리 NIC → 스위치 → VXLAN → 브리지 → VM/Pod
for dev in eth0 vxlan100 br0 veth-vm1; do
    echo "$dev: $(cat /sys/class/net/$dev/mtu)"
done
# eth0: 9000
# vxlan100: 8950
# br0: 8950
# veth-vm1: 8950  ← 모두 일치해야 함

멀티사이트 VXLAN (DCI)

DCI(Data Center Interconnect)는 지리적으로 분리된 데이터센터 간에 L2/L3 오버레이를 확장하는 기술입니다. VXLAN 오버레이를 WAN을 넘어 확장할 때는 추가적인 아키텍처 고려사항이 필요합니다.

VXLAN Stitching

VXLAN stitching은 두 데이터센터의 VXLAN 도메인을 Border Gateway(경계 라우터)에서 연결하는 기술입니다. 각 데이터센터는 독립적인 언더레이와 VNI 공간을 유지하면서, Border Gateway가 VNI 변환과 패킷 중계를 수행합니다.

# === 멀티사이트 VXLAN 구성 개요 ===

# 시나리오: DC-A(10.0.0.0/8) ↔ WAN ↔ DC-B(172.16.0.0/12)
# Border Gateway가 양쪽 VXLAN 도메인을 연결

# DC-A Border Gateway (10.0.0.254)
# VXLAN 디바이스: DC-A 내부 VNI 100
ip link add vxlan-dc-a type vxlan id 100 local 10.0.0.254 dstport 4789

# VXLAN 디바이스: DC-B 방향 VNI 100 (WAN 터널)
ip link add vxlan-dc-b type vxlan id 100 local 203.0.113.1 \
    remote 203.0.113.2 dstport 4789

# 브리지로 양쪽 VXLAN 연결 (L2 stitching)
ip link add br-stitch type bridge
ip link set vxlan-dc-a master br-stitch
ip link set vxlan-dc-b master br-stitch
ip link set vxlan-dc-a up
ip link set vxlan-dc-b up
ip link set br-stitch up

# WAN 구간 암호화 (IPsec 필수)
# → DCI 트래픽은 반드시 IPsec/WireGuard로 보호

# EVPN 멀티사이트 (FRR)
# router bgp 65001
#   address-family l2vpn evpn
#     neighbor DC-B-BORDER activate
#     neighbor DC-B-BORDER route-map DCI-FILTER in
# → RT(Route Target) 기반 VNI 필터링으로 선택적 확장
DCI 주의사항: WAN 구간에서는 멀티캐스트가 불가하므로 반드시 유니캐스트 BUM 복제(헤드엔드 또는 EVPN Type-3)를 사용해야 합니다. WAN 지연(latency)이 높으므로 FDB 에이징 시간을 충분히 길게 설정하고, BGP EVPN의 수렴 시간을 고려해야 합니다. WAN 대역폭(Bandwidth)이 제한적이므로 BUM 트래픽 최적화(ARP suppression)가 특히 중요합니다.

컨테이너 오버레이 네트워크

Kubernetes CNI 플러그인들은 VXLAN/GENEVE를 활용하여 Pod 간 오버레이 네트워크를 구성합니다. 각 CNI의 아키텍처와 사용하는 오버레이 프로토콜, 특성을 비교합니다.

Kubernetes CNI 오버레이 네트워크 아키텍처 Node A (10.0.0.1) Pod-1 10.244.0.2 Pod-2 10.244.0.3 cni0 bridge / cilium_host / cali+ interfaces flannel.1 (VXLAN) / cilium_geneve (GENEVE) VNI: 1 (Flannel) / pod-id (Cilium) Node B (10.0.0.2) Pod-3 10.244.1.2 Pod-4 10.244.1.3 cni0 bridge / cilium_host / cali+ interfaces flannel.1 (VXLAN) / cilium_geneve (GENEVE) VNI: 1 (Flannel) / pod-id (Cilium) VXLAN(4789) / GENEVE(6081) Tunnel Pod-1(10.244.0.2) → Pod-3(10.244.1.2): 캡슐화 → 언더레이 → 역캡슐화 Flannel VXLAN cni0 bridge + flannel.1 VNI 1, nolearning, 정적 FDB Calico VXLAN vxlan.calico + IP 라우팅 FDB 자동 관리, CrossSubnet Cilium GENEVE cilium_geneve + BPF datapath TLV로 정책 ID, GBP 지원 Cilium VXLAN cilium_vxlan + BPF GBP 태그로 정책 전달
Kubernetes 노드 간 Pod 통신: CNI가 VXLAN/GENEVE 오버레이를 자동 구성

Flannel VXLAN

Flannel은 가장 단순한 Kubernetes 오버레이 네트워크 CNI입니다. 각 노드에 VXLAN 디바이스(flannel.1)와 브리지(cni0)를 생성하고, etcd/API 서버의 노드 정보를 기반으로 FDB 엔트리를 자동 관리합니다.

# Flannel이 생성하는 VXLAN 구성 (자동)
# 각 노드에서 확인:
ip -d link show flannel.1
# flannel.1:  mtu 1450 ...
#   vxlan id 1 local 10.0.0.1 dev eth0 srcport 0 0
#   dstport 8472 nolearning ttl auto ...

# Flannel FDB (노드별 정적 엔트리, flanneld가 관리)
bridge fdb show dev flannel.1
# Node-B MAC dev flannel.1 dst 10.0.0.2 self permanent
# Node-C MAC dev flannel.1 dst 10.0.0.3 self permanent

# Flannel은 노드 서브넷 단위로 라우팅:
ip route show
# 10.244.1.0/24 via 10.244.1.0 dev flannel.1 onlink  ← Node-B 서브넷
# 10.244.2.0/24 via 10.244.2.0 dev flannel.1 onlink  ← Node-C 서브넷

# 주의: Flannel 기본 포트는 8472 (IANA 4789가 아님!)

Calico VXLAN 모드

Calico의 VXLAN 모드는 BGP 없이 순수 VXLAN 오버레이로 동작합니다. Flannel과 달리 노드별 IP 라우팅을 VXLAN 디바이스의 넥스트홉으로 설정하며, CrossSubnet 모드에서는 같은 서브넷 노드 간에는 직접 라우팅, 다른 서브넷에만 VXLAN을 사용합니다.

# Calico VXLAN 디바이스 확인
ip -d link show vxlan.calico
# vxlan.calico: vxlan id 4096 local 10.0.0.1 dev eth0
#   dstport 4789 nolearning ...

# Calico는 per-node FDB + ARP 엔트리를 자동 관리
bridge fdb show dev vxlan.calico
ip neigh show dev vxlan.calico

# CrossSubnet 모드: 같은 서브넷이면 직접 라우팅
# → VXLAN 오버헤드 최소화

Cilium GENEVE/VXLAN

Cilium은 BPF 기반 데이터플레인으로 GENEVE 또는 VXLAN 오버레이를 사용합니다. GENEVE 모드에서는 TLV 옵션을 통해 보안 정책 ID를 전달하고, VXLAN 모드에서는 GBP 확장을 활용합니다. BPF가 모든 패킷 처리(캡슐화, 정책 적용, 라우팅)를 수행하므로 iptables 체인을 우회하여 높은 성능을 달성합니다.

# Cilium GENEVE 모드 확인
ip -d link show cilium_geneve
# cilium_geneve: geneve external dstport 6081 ...

# Cilium VXLAN + GBP 모드 확인
ip -d link show cilium_vxlan
# cilium_vxlan: vxlan external gbp dstport 8472 ...

# Cilium은 BPF 프로그램이 터널 메타데이터를 직접 설정:
# bpf_skb_set_tunnel_key() → VNI, 목적지 VTEP
# bpf_skb_set_tunnel_opt() → GENEVE TLV (정책 ID)

# Cilium 상태 확인
cilium status
cilium bpf tunnel list

CNI 플러그인 오버레이 비교

CNI프로토콜UDP 포트FDB 관리정책 전달MTU특징
Flannel VXLANVXLAN8472정적 (flanneld)없음 (별도 NetworkPolicy 필요)자동 (MTU-50)최소 설정, 단순, 소규모 적합
Calico VXLANVXLAN4789felix 에이전트없음 (iptables/BPF 정책)자동 감지CrossSubnet, BGP 대체, IP 라우팅 기반
Cilium GENEVEGENEVE6081BPF mapTLV 옵션 (정책 ID)자동 (MTU-58)BPF datapath, L7 정책, 고성능
Cilium VXLANVXLAN (GBP)8472BPF mapGBP 태그자동 (MTU-50)GBP 확장으로 정책 전달
Weave NetVXLAN (fastdp)6784자체 mesh없음자동암호화 내장, mesh 토폴로지
AntreaGENEVE/VXLAN6081/4789OVS FDBOVS flow설정 가능OVS 기반, Windows 지원

노드 간 Pod 통신 경로 상세

Pod-A(Node-A)에서 Pod-B(Node-B)로의 패킷 흐름을 단계별로 추적합니다. CNI별로 약간 다르지만 기본 패턴은 동일합니다.

# === Pod 간 통신 경로 추적 (Flannel VXLAN 예시) ===

# Pod-A (10.244.0.2) → Pod-B (10.244.1.3) 전송

# 1단계: Pod-A 네임스페이스에서 출발
# eth0(veth) → cni0 브리지 → flannel.1
nsenter -t $(crictl inspect POD_A | jq .info.pid) -n \
    ip route get 10.244.1.3
# 10.244.1.3 via 10.244.0.1 dev eth0  ← 기본 게이트웨이

# 2단계: 호스트 네임스페이스 라우팅
ip route get 10.244.1.3
# 10.244.1.3 via 10.244.1.0 dev flannel.1 onlink

# 3단계: FDB 조회 → VTEP 결정
bridge fdb show dev flannel.1 | grep "10.244.1"
# Node-B-VTEP-MAC dev flannel.1 dst 10.0.0.2 self permanent

# 4단계: VXLAN 캡슐화
# Inner: src=Pod-A-MAC, dst=Node-B-VTEP-MAC, IP: 10.244.0.2→10.244.1.3
# Outer: src=10.0.0.1, dst=10.0.0.2, UDP:8472, VNI:1

# 5단계: Node-B 수신 → 역캡슐화 → cni0 → Pod-B

# 경로 추적 확인 명령
tcpdump -i eth0 -nn "udp port 8472" -c 5  # 캡슐화 패킷
tcpdump -i flannel.1 -nn -c 5              # 역캡슐화 패킷
conntrack -L | grep 10.244.1.3             # conntrack 엔트리
CNI 선택 가이드: 단순한 오버레이만 필요하면 Flannel, L3 라우팅과 보안 정책이 중요하면 Calico, BPF 기반 고성능과 L7 정책이 필요하면 Cilium을 권장합니다. Cilium은 GENEVE 기본 모드에서 TLV로 정책 메타데이터를 전달하여 네트워크 정책 적용이 가장 효율적입니다. 클라우드 환경에서는 클라우드 네이티브 CNI(AWS VPC CNI, Azure CNI)가 오버레이 오버헤드 없이 더 나은 성능을 제공합니다.

디버깅

VXLAN/GENEVE 오버레이 네트워크의 문제를 체계적으로 진단하기 위한 디버깅 기법입니다.

tcpdump VXLAN/GENEVE 필터

# === VXLAN 캡슐화 패킷 캡처 ===

# 기본: UDP 4789 포트 캡처
tcpdump -i eth0 -nn "udp port 4789" -c 20

# 특정 VNI 필터 (VXLAN 헤더 오프셋 계산)
# UDP 페이로드의 바이트 4~6이 VNI (24비트, 빅엔디안)
# VNI 100 = 0x000064 → byte[4]=0x00, byte[5]=0x00, byte[6]=0x64
tcpdump -i eth0 -nn "udp port 4789 and udp[12:4] = 0x08000064"
# 0x08 = VXLAN I 플래그, 0x000064 = VNI 100 (상위 24비트)

# GENEVE 캡처
tcpdump -i eth0 -nn "udp port 6081" -c 20

# 내부 프레임 (역캡슐화 후) 캡처
tcpdump -i vxlan100 -nn -c 20

# Wireshark용 캡처 파일 저장
tcpdump -i eth0 -nn "udp port 4789 or udp port 6081" \
    -w /tmp/overlay.pcap -c 1000

# tshark로 VXLAN 내부 디코딩
tshark -r /tmp/overlay.pcap \
    -d udp.port==4789,vxlan \
    -T fields \
    -e vxlan.vni \
    -e eth.src \
    -e eth.dst \
    -e ip.src \
    -e ip.dst

ftrace / perf를 활용한 커널 경로 추적

# === ftrace로 VXLAN 송수신 함수 추적 ===

# 1. VXLAN 송신 경로 추적
echo 0 > /sys/kernel/debug/tracing/tracing_on
echo "vxlan_xmit" > /sys/kernel/debug/tracing/set_ftrace_filter
echo "vxlan_xmit_one" >> /sys/kernel/debug/tracing/set_ftrace_filter
echo "vxlan_find_mac" >> /sys/kernel/debug/tracing/set_ftrace_filter
echo function > /sys/kernel/debug/tracing/current_tracer
echo 1 > /sys/kernel/debug/tracing/tracing_on
# → 오버레이 트래픽 발생 → trace 확인
cat /sys/kernel/debug/tracing/trace

# 2. VXLAN 수신 경로 추적
echo "vxlan_rcv" > /sys/kernel/debug/tracing/set_ftrace_filter
echo "vxlan_snoop" >> /sys/kernel/debug/tracing/set_ftrace_filter
echo "vxlan_vs_find_vni" >> /sys/kernel/debug/tracing/set_ftrace_filter

# 3. perf로 VXLAN 핫스팟 분석
perf record -a -g -- sleep 10
perf report --stdio | grep -A 5 vxlan

# 4. tracepoint 활용
perf trace -e 'net:*' --filter 'dev == "vxlan100"' -- sleep 5

# 5. FDB 변경 이벤트 모니터링
ip monitor neigh dev vxlan100
# → FDB 추가/삭제/변경 실시간 확인

# 6. Bridge FDB 이벤트
bridge monitor fdb
# → master/self FDB 변경 추적

오버레이 트러블슈팅 체크리스트

단계검증 명령확인 항목
1. VXLAN 디바이스 상태ip -d link show type vxlanVNI, local/remote IP, dstport, learning 플래그
2. FDB 테이블bridge fdb show dev vxlan000:00:00:00:00:00 엔트리, 유니캐스트 매핑, offload 상태
3. 언더레이 연결ping <remote VTEP>VTEP IP 간 IP 연결성
4. UDP 포트 도달ncat -u <VTEP> 4789 / 방화벽 확인UDP 4789/6081 열림
5. 패킷 캡처tcpdump -i eth0 "udp port 4789"캡슐화된 패킷 수신/송신
6. 내부 프레임tcpdump -i vxlan0역캡슐화된 내부 트래픽
7. MTU 검증ping -M do -s 1450 <overlay IP>PMTUD 동작, 단편화 없음
8. 통계ip -s link show dev vxlan0TX/RX 카운터, error/drop
9. NIC 오프로드ethtool --show-tunnels eth0VXLAN/GENEVE 포트 등록
10. EVPN 상태vtysh -c "show bgp l2vpn evpn summary"BGP 세션 Established, 경로 수

참고자료

이 주제와 관련된 다른 문서를 더 깊이 이해하고 싶다면 다음을 참고하세요.