VXLAN/GENEVE 오버레이(Overlay) 네트워크
Linux 커널 VXLAN/GENEVE 오버레이 네트워크 심층 분석: VXLAN(RFC 7348) UDP 터널(Tunnel)링, GENEVE(RFC 8926) 가변 TLV 캡슐화(Encapsulation), 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) — 오버레이 터널의 양 끝점으로, 캡슐화/역캡슐화(Decapsulation)를 수행합니다. Linux에서는
vxlan/geneve타입 netdev가 VTEP 역할을 합니다. - FDB 학습 — VXLAN/GENEVE에서 원격 MAC-VTEP 매핑(Mapping)을 학습하는 방식으로, 동적(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 터널을 생성합니다. - 오버레이 브리지(Bridge) 구성
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개의 논리 네트워크만 지원합니다. 대규모 데이터센터와 클라우드 환경에서는 수만~수백만 테넌트를 격리(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로 수평 확장 |
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개 제한이 병목(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 솔루션의 핵심 기반 기술입니다.
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 패킷(Packet) 포맷
| 구간 | 크기 | 핵심 필드 | 설명 |
|---|---|---|---|
| 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(해시(Hash)), 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를 자동 학습. 에이징 타이머(Timer) 적용. |
| 정적 설정 | 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 수에 비례하는 복제 오버헤드(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
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 디바이스 생성
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 오버레이 네트워크는 기본적으로 암호화(Encryption)나 인증 메커니즘을 제공하지 않습니다. 언더레이 네트워크의 보안이 오버레이 전체의 보안 기반이 되므로, 적절한 방어 조치가 필수적입니다.
VXLAN/GENEVE 보안
| 위협 | 설명 | 대응 |
|---|---|---|
| 트래픽 도청 | 언더레이에서 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 |
핵심 성능 튜닝 체크리스트
# === 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 수신 버퍼(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 환경에서는 보안과 확장성 측면에서 주의가 필요합니다.
정적 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 Suppression | VTEP이 알려진 MAC/IP의 ARP 요청에 로컬 대리 응답 → ARP 브로드캐스트 감소 | bridge link set dev vxlan0 neigh_suppress on |
| ARP/ND Proxy | VXLAN proxy 모드: FDB에 있는 MAC의 ARP에 VTEP이 응답 | ip link add vxlan0 type vxlan ... proxy |
| nolearning + EVPN | BGP로 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)로 수신 패킷을 분배합니다.
/* 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 정보, 디버깅 메타데이터, 서비스 체이닝 정보 등을 유연하게 전달할 수 있습니다.
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 내 오프셋 */
};
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 트래픽을 크게 개선합니다.
FRR VXLAN+EVPN 설정 상세
FRR(Free Range Routing)에서 VXLAN+EVPN을 구성하려면 bgpd와 zebra의 연동이 핵심입니다. 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 IRB | Symmetric 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의 관계를 이해하는 것이 멀티테넌트 구성의 핵심입니다.
# 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 환경에서 표준 구성으로 자리잡았습니다.
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
ethtool --show-tunnels eth0으로 확인합니다. VXLAN/GENEVE/GRE 각각의 오프로드 포트가 등록되어 있어야 합니다. TSO(TCP Segmentation Offload)가 내부 TCP까지 적용되는지 ethtool -k eth0 | grep tx-udp_tnl로 확인하세요.
NIC별 VTEP 오프로드 지원 비교
| NIC | VXLAN 캡슐화/해제 | GENEVE 캡슐화/해제 | Inner TSO/LRO | TC flower 오프로드 | switchdev | 비고 |
|---|---|---|---|---|---|---|
| NVIDIA CX-6 Dx | O | O | O (TSO/LRO/GRO) | O (conntrack 포함) | O | VF LAG, ECMP 오프로드 |
| NVIDIA CX-7 | O | O | O | O (crypto 포함) | O | 400G, IPsec+VXLAN 결합 |
| Intel E810 | O | O | O (TSO) | O (switchdev 모드) | O (ice) | ADQ 결합 가능 |
| Broadcom P2100 | O | O | O | O | O (bnxt) | TruFlow 엔진 |
| 범용 NIC | Rx Checksum만 | Rx Checksum만 | 외부 TSO만 | X | X | 소프트웨어 처리 |
# === 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) 블랙홀을 방지하려면 정확한 오버헤드 계산이 필수적입니다.
# === 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 frag | df 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 필터링으로 선택적 확장
컨테이너 오버레이 네트워크
Kubernetes CNI 플러그인들은 VXLAN/GENEVE를 활용하여 Pod 간 오버레이 네트워크를 구성합니다. 각 CNI의 아키텍처와 사용하는 오버레이 프로토콜, 특성을 비교합니다.
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 VXLAN | VXLAN | 8472 | 정적 (flanneld) | 없음 (별도 NetworkPolicy 필요) | 자동 (MTU-50) | 최소 설정, 단순, 소규모 적합 |
| Calico VXLAN | VXLAN | 4789 | felix 에이전트 | 없음 (iptables/BPF 정책) | 자동 감지 | CrossSubnet, BGP 대체, IP 라우팅 기반 |
| Cilium GENEVE | GENEVE | 6081 | BPF map | TLV 옵션 (정책 ID) | 자동 (MTU-58) | BPF datapath, L7 정책, 고성능 |
| Cilium VXLAN | VXLAN (GBP) | 8472 | BPF map | GBP 태그 | 자동 (MTU-50) | GBP 확장으로 정책 전달 |
| Weave Net | VXLAN (fastdp) | 6784 | 자체 mesh | 없음 | 자동 | 암호화 내장, mesh 토폴로지 |
| Antrea | GENEVE/VXLAN | 6081/4789 | OVS FDB | OVS 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 엔트리
디버깅
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 vxlan | VNI, local/remote IP, dstport, learning 플래그 |
| 2. FDB 테이블 | bridge fdb show dev vxlan0 | 00: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 vxlan0 | TX/RX 카운터, error/drop |
| 9. NIC 오프로드 | ethtool --show-tunnels eth0 | VXLAN/GENEVE 포트 등록 |
| 10. EVPN 상태 | vtysh -c "show bgp l2vpn evpn summary" | BGP 세션 Established, 경로 수 |
참고자료
- 커널 공식 문서: VXLAN — VXLAN 터널링 공식 문서입니다
- 커널 공식 문서: Switchdev — switchdev 프레임워크 공식 문서입니다
- 커널 공식 문서: Bridge — 리눅스 브리지 VLAN 필터링 관련 공식 문서입니다
- RFC 7348 — VXLAN(Virtual eXtensible Local Area Network) 표준 규격입니다
- RFC 7432 — BGP MPLS-Based Ethernet VPN(EVPN) 표준 규격입니다
- man bridge(8) — bridge vlan 명령어 매뉴얼 페이지입니다
- drivers/net/vxlan/ 소스 디렉터리 — 커널 VXLAN 구현 소스 코드입니다
- LWN: VXLAN for Linux (2012) — 리눅스 VXLAN 구현에 대한 LWN 기사입니다
관련 문서
이 주제와 관련된 다른 문서를 더 깊이 이해하고 싶다면 다음을 참고하세요.