VXLAN/GENEVE 오버레이 네트워크
Linux 커널 VXLAN/GENEVE 오버레이 네트워크 심층 분석: VXLAN(RFC 7348) UDP 터널링, GENEVE(RFC 8926) 가변 TLV 캡슐화, VTEP 구성과 FDB 학습, 멀티캐스트/유니캐스트 BUM 복제, BGP EVPN 연동, 커널 내부 데이터 경로와 핵심 자료구조, NIC 오프로드 및 성능 튜닝까지 실무 관점으로 다룹니다.
핵심 요약
- 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) — 오버레이 터널의 양 끝점으로, 캡슐화/역캡슐화를 수행합니다. Linux에서는
vxlan/geneve타입 netdev가 VTEP 역할을 합니다. - FDB 학습 — VXLAN/GENEVE에서 원격 MAC-VTEP 매핑을 학습하는 방식으로, 동적(data plane), 정적(수동), 컨트롤 플레인(BGP EVPN) 방식이 있습니다.
- NIC 오프로드 — 최신 NIC(mlx5, ice 등)은 VXLAN/GENEVE 캡슐화/역캡슐화를 하드웨어에서 수행하여 CPU 부하를 크게 줄입니다.
단계별 이해
- VXLAN 오버레이 생성
ip link add vxlan100 type vxlan id 100 dstport 4789 local 10.0.0.1 remote 10.0.0.2로 유니캐스트 VXLAN 터널을 생성합니다. - 오버레이 브리지 구성
VXLAN 디바이스를 브리지에 연결하고bridge fdb append 00:00:00:00:00:00 dev vxlan100 dst <VTEP>로 BUM 트래픽 복제 대상을 등록합니다. - GENEVE 터널 생성
ip link add geneve100 type geneve id 100 remote 10.0.0.2 dstport 6081로 GENEVE 터널을 구성합니다. - MTU 및 오프로드 확인
물리 NIC MTU를 점보 프레임(9000)으로 설정하고,ethtool --show-tunnels eth0으로 터널 오프로드 상태를 확인합니다.
오버레이 네트워크 개요
오버레이 네트워크(Overlay Network)는 기존 물리(언더레이) 네트워크 위에 논리적인 가상 네트워크를 구성하는 기술입니다. 원본 이더넷 프레임을 UDP 등으로 캡슐화하여 L3 네트워크를 통해 전달하므로, 물리적 위치와 무관하게 동일한 L2 도메인을 구성할 수 있습니다.
오버레이 네트워크가 필요한 이유
전통적인 VLAN(802.1Q)은 12비트 VID로 최대 4,094개의 논리 네트워크만 지원합니다. 대규모 데이터센터와 클라우드 환경에서는 수만~수백만 테넌트를 격리해야 하므로 VLAN만으로는 한계가 있습니다. 또한 VLAN은 L2 도메인 내에서만 동작하기 때문에 L3 경계를 넘어서는 네트워크 확장이 불가능합니다.
| 한계 | VLAN (802.1Q) | VXLAN/GENEVE 오버레이 |
|---|---|---|
| 네트워크 ID 공간 | 4,094개 (12비트) | 16,777,216개 (24비트 VNI) |
| L3 경계 횡단 | 불가 (L2 브로드캐스트 도메인 한정) | 가능 (UDP 캡슐화로 IP 라우팅 통과) |
| 멀티테넌트 격리 | 수천 규모 한계 | 수백만 규모 지원 |
| 데이터센터 간 연결 | DCI 전용 장비 필요 | IP 연결만 있으면 가능 |
| 확장성 | STP 도메인 크기 제약 | IP ECMP로 수평 확장 |
Linux 커널은 drivers/net/vxlan/과 drivers/net/geneve.c에서 각각 VXLAN과 GENEVE를 구현합니다. 두 프로토콜 모두 struct net_device 기반의 가상 네트워크 디바이스로 동작하며, ip link add 명령으로 생성합니다. 물리 NIC의 터널 오프로드를 활용하면 CPU 부담 없이 와이어레이트에 가까운 성능을 달성할 수 있습니다.
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개 제한이 병목이 되므로, 24비트 VNI를 제공하는 VXLAN/GENEVE로 확장합니다.
상세 문서: VLAN(802.1Q) 프레임 구조, 태깅 처리, QoS, Q-in-Q 등의 상세 내용은 802.1Q VLAN 심화 페이지를 참조하세요.
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 오버레이를 구성하는 네트워크 가상화 기술입니다. 기존 VLAN이 12비트 VID로 4,094개의 세그먼트만 지원하는 한계를 극복하여, 24비트 VNI로 최대 16,777,216 (224)개의 논리 네트워크를 지원합니다. 데이터센터의 멀티테넌트 환경, 컨테이너 오버레이 네트워크(Flannel, Calico VXLAN 모드), SDN 솔루션의 핵심 기반 기술입니다.
drivers/net/vxlan/ 디렉토리에 위치하며, vxlan_core.c가 핵심 로직을 담당합니다.
VXLAN 아키텍처와 용어
| 용어 | 설명 |
|---|---|
| VNI (VXLAN Network Identifier) | 24비트 논리 네트워크 식별자 (0~16,777,215). VLAN ID에 해당하며, 동일 VNI = 동일 L2 도메인 |
| VTEP (VXLAN Tunnel Endpoint) | VXLAN 캡슐화/역캡슐화를 수행하는 엔드포인트. Linux에서는 vxlan 타입 net_device |
| Overlay Network | VXLAN으로 구성된 가상 L2 네트워크. 테넌트/애플리케이션별 격리 |
| Underlay Network | VTEP 간 IP 연결을 제공하는 물리적 L3 네트워크 |
| BUM 트래픽 | Broadcast, Unknown unicast, Multicast — 목적지를 모르는 트래픽. 멀티캐스트 그룹 또는 헤드엔드 복제로 처리 |
| FDB (Forwarding Database) | 내부 MAC 주소 → 원격 VTEP IP 매핑 테이블. bridge fdb로 관리 |
| Headend Replication | 멀티캐스트 없이 BUM 트래픽을 알려진 모든 VTEP에 유니캐스트로 복제 전송 |
VXLAN 패킷 포맷
| 구간 | 크기 | 핵심 필드 | 설명 |
|---|---|---|---|
| Outer Ethernet | 14 bytes | dst/src MAC, EtherType | 언더레이 다음 홉으로 전달되는 L2 헤더 |
| Outer IP | 20 bytes (IPv4) / 40 bytes (IPv6) | src/dst IP, Protocol=17 | VTEP 간 L3 경로 식별 |
| UDP | 8 bytes | src port(해시), dst port=4789 | ECMP 분산을 위한 엔트로피 + VXLAN 식별 |
| VXLAN Header | 8 bytes | Flags, VNI(24) | 오버레이 네트워크 식별자 전달 |
| Inner Ethernet | 14 bytes + payload | 원본 dst/src MAC, EtherType | 테넌트 L2 프레임 원형 보존 (FCS 제외) |
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_* 플래그 */
};
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_PROXY | proxy | ARP/NDP proxy — FDB에 있는 MAC의 ARP 요청에 VTEP이 대리 응답 |
VXLAN_F_L2MISS | l2miss | FDB miss 시 netlink 알림 발생 (외부 컨트롤러 연동) |
VXLAN_F_L3MISS | l3miss | ARP 테이블 miss 시 netlink 알림 발생 |
VXLAN_F_RSC | rsc | Route Short Circuit — ARP 응답의 MAC을 FDB에 학습 |
VXLAN_F_COLLECT_METADATA | external | 메타데이터 모드 — tc/BPF에서 VNI/목적지를 동적 결정 |
VXLAN_F_UDP_ZERO_CSUM6_TX | udp6zerocsumtx | IPv6 송신 시 UDP checksum을 0으로 설정 |
VXLAN_F_UDP_ZERO_CSUM6_RX | udp6zerocsumrx | IPv6 수신 시 UDP checksum 0을 허용 |
VXLAN_F_GBP | gbp | Group Based Policy — VXLAN 헤더에 정책 태그 삽입 |
VXLAN_F_GPE | gpe | Generic Protocol Extension — 내부 프로토콜 지정 가능 |
VXLAN 송신 경로 (Tx Path)
/* 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를 자동 학습. 에이징 타이머 적용. |
| 정적 설정 | bridge fdb add로 수동 등록. |
| (Static) | 에이징 없음, 재부팅 시 재설정 필요. |
| nolearning + | 동적 학습 비활성화, 모든 매핑을 수동 관리. |
| 정적 FDB | 대규모 환경에서 FDB 폭증 방지, 보안 향상. |
| L2MISS/L3MISS | FDB miss 시 netlink 알림 → 외부 컨트롤러가 |
| + 컨트롤러 | FDB 엔트리를 동적으로 주입 (SDN 패턴). |
| BGP EVPN | BGP 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 수에 비례하는 복제 오버헤드, 수동 관리 필요 |
| 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 언더레이를 사용하면 글로벌 주소 공간을 활용한 대규모 오버레이 네트워크 구성이 가능합니다. 단, 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
udp6zerocsumtx/udp6zerocsumrx 옵션은 RFC 6935/6936에서 정의한 예외 시나리오에 따라 VXLAN 트래픽의 UDP checksum을 생략하여 성능을 향상시킵니다. NIC의 하드웨어 checksum offload가 지원되면 이 옵션 없이도 성능 저하가 미미합니다.
VXLAN-GPE (Generic Protocol Extension)
VXLAN-GPE(RFC 9136)는 표준 VXLAN을 확장하여 내부 페이로드의 프로토콜을 명시적으로 지정할 수 있게 합니다. 표준 VXLAN은 항상 이더넷 프레임을 캡슐화하지만, VXLAN-GPE는 IPv4, IPv6, NSH(Network Service Header), MPLS 등을 직접 캡슐화할 수 있어 SFC(Service Function Chaining)와 같은 고급 네트워크 기능에 사용됩니다.
# 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에서 네트워크 정책 적용에 사용합니다.
# 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
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 설정
# 권장: 물리 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
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 패킷 포맷
| 필드 | 크기 | 설명 |
|---|---|---|
| Version | 2비트 | 프로토콜 버전 (현재 0) |
| Opt Len | 6비트 | 옵션 길이 (4바이트 단위, 0~63 → 0~252바이트) |
| O (OAM) | 1비트 | OAM 패킷 표시 (관리/진단용) |
| C (Critical) | 1비트 | Critical 옵션 포함 (미지원 시 반드시 드롭) |
| Protocol Type | 16비트 | 내부 페이로드 타입 (0x6558=Ethernet, 0x0800=IPv4, 0x86DD=IPv6) |
| VNI | 24비트 | Virtual Network Identifier (VXLAN VNI와 동일 개념) |
| Reserved | 8비트 | 예약 (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 커널이 지원하는 주요 네트워크 오버레이 프로토콜의 특성을 비교합니다.
| 특성 | VXLAN | GENEVE | GRE/NVGRE | STT |
|---|---|---|---|---|
| RFC | RFC 7348 | RFC 8926 | RFC 2890/7637 | 비표준 (RFC 없음) |
| 캡슐화 | UDP (4789) | UDP (6081) | IP Protocol 47 | TCP-like (7471) |
| 헤더 크기 | 8B 고정 | 8B + 옵션(가변) | 4~8B | 18B |
| 네트워크 ID | 24비트 VNI | 24비트 VNI | 24비트 VSID/Key | 64비트 Context ID |
| 확장성 | GBP/GPE로 제한적 | TLV 옵션으로 무한 확장 | 없음 | 없음 |
| ECMP 지원 | UDP src port 해시 | UDP src port 해시 | GRE Key 해시 | TCP-like src port |
| NIC 오프로드 | 광범위 지원 | 점점 확대 중 | 대부분 지원 | 제한적 |
| 멀티캐스트 | 그룹 기반 BUM | 유니캐스트 권장 | 그룹 기반 | 유니캐스트만 |
| IPv4 오버헤드 | 50B | 50B + 옵션 | 38~42B | 76B |
| IPv6 오버헤드 | 70B | 70B + 옵션 | 58~62B | 96B |
| 주요 사용처 | 데이터센터, 컨테이너 | OVS, NSX, AWS VPC | 레거시, 사이트간 VPN | VMware (레거시) |
| 커널 소스 | drivers/net/vxlan/ | drivers/net/geneve.c | net/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 오버레이 네트워크는 기본적으로 암호화나 인증 메커니즘을 제공하지 않습니다. 언더레이 네트워크의 보안이 오버레이 전체의 보안 기반이 되므로, 적절한 방어 조치가 필수적입니다.
VXLAN/GENEVE 보안
| 위협 | 설명 | 대응 |
|---|---|---|
| 트래픽 도청 | 언더레이에서 VXLAN UDP 패킷을 캡처하면 내부 프레임이 평문으로 노출 | IPsec 터널로 언더레이 암호화, MACsec, WireGuard |
| VNI 스캐닝 | 공격자가 다양한 VNI로 패킷을 전송하여 활성 VNI를 탐색 | 방화벽에서 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 오버레이의 캡슐화 오버헤드를 정량적으로 파악하기 위한 벤치마킹 방법론과 주요 튜닝 포인트입니다.
오버레이 계층별 성능 오버헤드
| 구성 | 상대 처리량 | 상대 지연 | 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 |
핵심 성능 튜닝 체크리스트
# === 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 |
모범 사례 요약
- 물리 NIC에 점보 프레임(MTU 9000) 설정 → 오버레이 MTU 8950
- EVPN 사용 시 반드시
nolearning— 데이터 플레인 학습 비활성화 - 보안:
nolearning+ 정적 FDB + 방화벽 VTEP 화이트리스트 - NIC 오프로드 확인:
ethtool --show-tunnels eth0 - conntrack이 불필요한 오버레이 인터페이스에서는 NOTRACK 적용
- 새 프로젝트에서는 GENEVE를 우선 검토 (TLV 확장성)
- UDP 수신 버퍼를 충분히 확보:
sysctl -w net.core.rmem_max=26214400
관련 문서
이 주제와 관련된 다른 문서를 더 깊이 이해하고 싶다면 다음을 참고하세요.