GRE (Generic Routing Encapsulation)
Linux 커널 GRE 서브시스템: RFC 2784/2890 프로토콜 구조, ip_gre/ip6_gre 커널 모듈, GRE/GRETAP/ERSPAN 터널 유형, 키·시퀀스·체크섬 옵션, Netfilter 연동, GSO/GRO 오프로드, PPTP, NVGRE, MTU/PMTUD 이슈, 성능 튜닝, 디버깅 종합 가이드. 커널 내부 데이터 경로, 핵심 자료구조/API, 운영 환경 튜닝 포인트와 장애 디버깅 절차까지 실무 관점으로 다룹니다.
핵심 요약
- 외부 헤더 — underlay는 바깥 IP 헤더만 보고 전달하며, 기본 GRE는 IP 프로토콜 번호 47을 사용합니다.
- GRE Key — 32비트 식별자로 다중화에 쓰이며, 암호화나 인증 기능은 아닙니다.
- GRETAP — Protocol Type 0x6558(TEB)로 Ethernet 프레임 전체를 실어 L2 브리징을 제공합니다.
- ERSPAN — GRE 위에 미러링 메타데이터를 추가해 원격 패킷 캡처를 전송합니다.
- 실무 핵심 — 성능과 장애는 대부분 MTU/PMTUD, ECMP 해시, NAT 통과, offload 지원 여부에서 갈립니다.
단계별 이해
- 캡슐화 층 분리
문제를 outer IP, GRE 옵션, inner payload 중 어디에서 보는지 먼저 고정합니다. - 링크 종류 선택
L3면gre, L2면gretap, 미러링이면erspan, 동적 제어면external을 고릅니다. - underlay 제약 확인
NAT, ECMP, 방화벽, 멀티패스 해시가 GRE에 우호적인지 확인합니다. - 크기 계산
추가 헤더만큼 MTU와 MSS를 줄이고, PMTUD/ICMP가 살아 있는지 검증합니다. - 디버깅 표면 확정
tcpdump,ip -d link,conntrack,tracefs중 어디서 상태를 볼지 정합니다.
GRE 개요
GRE(Generic Routing Encapsulation)는 임의의 네트워크 계층 프로토콜을 다른 네트워크 계층 프로토콜 위에 캡슐화하여 전송하는 터널링 프로토콜입니다. IP 프로토콜 번호 47을 사용하며, TCP/UDP와 달리 포트 개념이 없고 순수한 캡슐화 메커니즘으로 동작합니다.
| 특성 | GRE | IPSec (ESP) | VXLAN | WireGuard |
|---|---|---|---|---|
| 계층 | IP 프로토콜 47 | IP 프로토콜 50 | UDP 4789 | UDP 51820 |
| 캡슐화 대상 | L2/L3 모두 가능 | L3 전용 | L2 프레임 | L3 전용 |
| 암호화 | 없음 (IPSec 병용) | 내장 (AES-GCM 등) | 없음 (MACsec 병용) | 내장 (ChaCha20) |
| 오버헤드 | 4~16바이트 | 36~73바이트 | 50바이트 | 60바이트 |
| NAT 통과 | 불가 (포트 없음) | NAT-T (UDP 4500) | 가능 (UDP) | 가능 (UDP) |
| 멀티캐스트 | 지원 | 제한적 | 지원 | 미지원 |
| 커널 모듈 | ip_gre, ip6_gre | xfrm, esp4/6 | vxlan | wireguard |
GRE의 핵심 장점은 프로토콜 불가지론(protocol-agnostic)입니다. EtherType 필드를 통해 IPv4, IPv6, Ethernet, MPLS 등 다양한 페이로드를 캡슐화할 수 있으며, 라우팅 프로토콜의 멀티캐스트 패킷도 터널을 통해 전달할 수 있습니다.
Linux 링크 종류 선택 기준
| 링크 종류 | 내부 페이로드 | 대표 장점 | 주의할 점 |
|---|---|---|---|
gre |
L3 패킷 | 오버헤드가 낮고 라우팅 프로토콜 터널링에 적합 | NAT와 ECMP에 불리하며 포트 기반 식별이 없습니다 |
gretap |
Ethernet 프레임 | 브리지/OVS와 직접 연결되는 L2 오버레이 | 브로드캐스트 도메인이 그대로 확장되어 MTU와 BUM 트래픽 관리가 중요합니다 |
erspan |
미러링된 Ethernet 프레임 | 원격 캡처, IDS, 포렌식 수집에 적합 | 일반 데이터 터널이 아니라 모니터링 전용으로 봐야 합니다 |
gre external |
메타데이터 기반 | TC, OVS, BPF가 패킷별로 dst/key를 결정할 수 있습니다 | remote, local, key와 상호배타적이므로 제어 평면 설계가 먼저 필요합니다 |
gre + encap fou/gue |
L3 또는 L2 | UDP 소스 포트로 ECMP 분산과 중간 장비 통과성이 좋아집니다 | UDP 추가 헤더만큼 오버헤드가 늘고 중간 장비가 UDP 포트 정책을 가질 수 있습니다 |
GRE 패킷 구조
GRE 헤더 포맷 (RFC 2784 / RFC 2890)
RFC 2784는 원본 RFC 1701의 GRE 헤더를 단순화했으며, RFC 2890이 Key와 Sequence Number 확장을 추가했습니다. 커널 구현은 두 RFC를 모두 지원합니다.
/*
* GRE 헤더 구조 (RFC 2784 + RFC 2890 확장)
*
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* |C| |K|S| Reserved0 | Ver | Protocol Type |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Checksum (optional) | Reserved1 (optional) |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Key (optional) |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Sequence Number (optional) |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
* C (bit 0) : Checksum 존재 여부
* K (bit 2) : Key 존재 여부
* S (bit 3) : Sequence Number 존재 여부
* Ver : 버전 (0 = RFC 2784, 1 = PPTP용 Enhanced GRE)
* Protocol Type : 캡슐화된 페이로드의 EtherType
*/
/* 커널 정의: include/net/gre.h */
struct gre_base_hdr {
__be16 flags; /* C, K, S 플래그 + Reserved + Ver */
__be16 protocol; /* 페이로드 EtherType (예: 0x0800=IPv4, 0x86DD=IPv6, 0x6558=TEB) */
};
| 플래그 비트 | 매크로 | 설명 | 추가 헤더 크기 |
|---|---|---|---|
| C (bit 0) | GRE_CSUM (0x8000) |
Checksum + Reserved1 필드 존재 | +4바이트 |
| K (bit 2) | GRE_KEY (0x2000) |
Key 필드 존재 (터널 식별/다중화) | +4바이트 |
| S (bit 3) | GRE_SEQ (0x1000) |
Sequence Number 필드 존재 (순서 보장) | +4바이트 |
| Ver (bits 13-15) | GRE_VERSION |
0 = 표준 GRE, 1 = Enhanced GRE (PPTP) | — |
전체 캡슐화 패킷 구조
커널 GRE 아키텍처
모듈 구조
Linux 커널의 GRE 구현은 여러 모듈로 계층화되어 있습니다:
| 커널 모듈 | 소스 파일 | 역할 |
|---|---|---|
gre |
net/ipv4/gre_demux.c |
IP 프로토콜 47 수신 디먹싱, GRE 핸들러 등록 프레임워크 |
ip_gre |
net/ipv4/ip_gre.c |
IPv4 GRE/GRETAP/ERSPAN 터널 디바이스 구현 |
ip6_gre |
net/ipv6/ip6_gre.c |
IPv6 GRE/GRETAP/ERSPAN 터널 디바이스 구현 |
ip_tunnel |
net/ipv4/ip_tunnel.c |
IPv4 터널 공통 로직 (lookup, xmit, rcv, 통계) |
| — | net/ipv4/ip_tunnel_core.c |
iptunnel_xmit(), iptunnel_pull_header() 등 공통 함수 |
drivers/net/gre/ 계층이 아니라, 실제 송수신 경로는 ip_gre.c/ip6_gre.c를 타고 메타데이터 형식 정의만 include/net/erspan.h에 분리되어 있습니다.
핵심 자료구조: struct ip_tunnel
/* include/net/ip_tunnels.h — 현대 커널에서 중요한 필드만 발췌 */
struct ip_tunnel {
struct ip_tunnel __rcu *next; /* 해시 체인의 다음 터널 */
struct hlist_node hash_node; /* 해시 테이블 연결 */
struct net_device *dev; /* 터널 net_device */
struct net *net; /* 네트워크 네임스페이스 */
unsigned long err_time; /* ICMP 에러 rate limit 타임스탬프 */
int err_count; /* ICMP 에러 카운트 */
/* GRE/ERSPAN 전용 상태 */
u32 i_seqno; /* 마지막으로 본 입력 시퀀스 번호 */
atomic_t o_seqno; /* 마지막 출력 시퀀스 번호 */
int tun_hlen; /* 외부 IP + GRE 길이 */
u8 erspan_ver; /* ERSPAN 버전 (0/1/2) */
struct dst_cache dst_cache; /* underlay 라우팅 캐시 */
struct ip_tunnel_parm parms; /* src, dst, key, flags, TTL/TOS */
int encap_hlen; /* FOU/GUE 같은 2차 UDP encap 길이 */
int hlen; /* tun_hlen + encap_hlen */
struct ip_tunnel_encap encap; /* fou/gue/none 설정 */
struct gro_cells gro_cells; /* GRO 처리용 per-CPU 셀 */
__u32 fwmark; /* underlay 라우팅에 쓰는 fwmark */
bool collect_md; /* metadata 모드 (Collect Metadata) */
bool ignore_df; /* inner DF를 무시할지 여부 */
};
/* 터널 파라미터 — iproute2 'ip tunnel add' 시 설정 */
struct ip_tunnel_parm {
char name[IFNAMSIZ]; /* 터널 디바이스 이름 */
int link; /* 물리 디바이스 ifindex (0=auto) */
__be16 i_flags; /* 입력 GRE 플래그 (GRE_KEY, GRE_CSUM, GRE_SEQ) */
__be16 o_flags; /* 출력 GRE 플래그 */
__be32 i_key; /* 입력 GRE 키 */
__be32 o_key; /* 출력 GRE 키 */
struct iphdr iph; /* 외부 IP 헤더 템플릿 (saddr, daddr, ttl, tos) */
};
커널 버전별로 필드 순서와 보조 멤버는 조금씩 달라지지만, 운영 관점에서 봐야 할 축은 거의 같습니다. 고정 파라미터는 parms, 패킷별 동적 정보는 collect_md, FOU/GUE 같은 2차 캡슐화는 encap/encap_hlen, PMTUD 예외 처리는 ignore_df, 순서 검사는 i_seqno/o_seqno가 담당합니다.
GRE 수신 경로 — 디먹싱
IP 레이어가 프로토콜 번호 47 패킷을 수신하면 gre_demux.c의 핸들러로 전달됩니다:
/* net/ipv4/gre_demux.c — GRE 수신 흐름 */
/* 1. IP 레이어에서 GRE 핸들러 호출 */
static int gre_rcv(struct sk_buff *skb)
{
const struct gre_protocol *proto;
u8 ver;
/* GRE 헤더에서 버전 필드 추출 */
ver = skb->data[1] & 0x7;
if (ver >= GREPROTO_MAX)
goto drop;
/* 등록된 버전별 핸들러 호출
* ver=0: ip_gre.c의 gre_rcv() → 표준 GRE
* ver=1: pptp.c의 pptp_rcv() → PPTP (Enhanced GRE) */
proto = rcu_dereference(gre_proto[ver]);
if (!proto || !proto->handler)
goto drop;
return proto->handler(skb);
...
}
/* 2. ip_gre.c에서의 GRE 패킷 처리 */
static int gre_rcv(struct sk_buff *skb)
{
struct tnl_ptk_info tpi;
/* GRE 헤더 파싱: flags, key, seq, protocol type 추출 */
if (gre_parse_header(skb, &tpi, NULL, htons(ETH_P_IP), 0) < 0)
goto drop;
/* ERSPAN 처리 (Protocol Type 0x88BE) */
if (tpi.proto == htons(ETH_P_ERSPAN) ||
tpi.proto == htons(ETH_P_ERSPAN2))
return erspan_rcv(skb, &tpi, hlen);
/* ip_tunnel_lookup()으로 매칭되는 터널 디바이스 탐색 */
return ip_tunnel_rcv(tunnel, skb, &tpi, tun->hlen);
}
GRE 송신 경로
/* net/ipv4/ip_gre.c — GRE 패킷 송신 */
static netdev_tx_t gre_tap_xmit(struct sk_buff *skb,
struct net_device *dev)
{
struct ip_tunnel *tunnel = netdev_priv(dev);
if (tunnel->collect_md) {
/* Metadata 모드: skb의 tunnel_info에서 dst/key 추출
* → OVS, TC tunnel_key 등 flow-based 터널에서 사용 */
gre_fb_xmit(skb, dev, htons(ETH_P_TEB));
} else {
/* 일반 모드: ip_tunnel_parm에서 dst/key 사용 */
gre_build_header(skb, tunnel->tun_hlen,
tunnel->parms.o_flags,
htons(ETH_P_TEB),
tunnel->parms.o_key,
htonl(tunnel->o_seqno++));
/* ip_tunnel_xmit() → 외부 IP 헤더 추가 + 라우팅 + 송신 */
ip_tunnel_xmit(skb, dev, &tunnel->parms.iph,
tunnel->parms.iph.protocol);
}
return NETDEV_TX_OK;
}
/* GRE 헤더 빌드 — 고속 경로 인라인 함수 */
static void gre_build_header(struct sk_buff *skb, int hdr_len,
__be16 flags, __be16 proto,
__be32 key, __be32 seq)
{
struct gre_base_hdr *greh;
skb_push(skb, hdr_len); /* 헤더 공간 확보 */
skb_reset_transport_header(skb); /* transport 헤더 = GRE 시작점 */
greh = (struct gre_base_hdr *)skb_transport_header(skb);
greh->flags = gre_tnl_flags_to_gre_flags(flags);
greh->protocol = proto;
/* 선택적 필드를 순서대로 채움 */
if (flags & TUNNEL_CSUM)
*ptr++ = 0; /* 체크섬은 나중에 계산 */
if (flags & TUNNEL_KEY)
*ptr++ = key;
if (flags & TUNNEL_SEQ)
*ptr++ = seq;
if (flags & TUNNEL_CSUM)
/* GRE 체크섬 계산 (GRE 헤더 + 페이로드 전체) */
*(__sum16 *)&greh[1] = gre_checksum(skb);
}
송수신 데이터 경로
운영자가 가장 자주 추적하는 흐름은 "어디서 캡슐화되고, 어디서 디캡슐레이션되며, 어느 단계에서 MTU·conntrack·offload가 개입하는가"입니다. 아래 흐름을 기준으로 장애 지점을 잘라내면 디버깅 속도가 크게 빨라집니다.
GRE 터널 유형
GRE 터널 (L3)
기본 GRE 터널은 L3(IP) 패킷을 캡슐화합니다. net_device 타입은 ARPHRD_IPGRE이며, L2 헤더 없이 IP 패킷을 직접 GRE로 감쌉니다.
# 기본 GRE 터널 생성 (L3 — point-to-point)
ip tunnel add gre1 mode gre \
local 10.0.0.1 remote 10.0.0.2 \
ttl 64
ip addr add 192.168.100.1/30 dev gre1
ip link set gre1 up
# 확인
ip tunnel show gre1
ip -d link show gre1
# 커널 내부: ip_gre.c → ipgre_newlink() → alloc_netdev()
# dev→type = ARPHRD_IPGRE
# dev→hard_header_len = 0 (L3 터널이므로 L2 헤더 없음)
# Protocol Type = 0x0800 (IPv4) 또는 0x86DD (IPv6)
GRETAP 터널 (L2)
GRETAP은 Ethernet 프레임 전체를 캡슐화하여 L2 브리징이 가능합니다. Protocol Type은 0x6558(TEB)이며, 브리지에 포트로 추가할 수 있습니다.
# GRETAP 터널 생성 (L2 — Ethernet 프레임 캡슐화)
ip link add gretap1 type gretap \
local 10.0.0.1 remote 10.0.0.2 \
ttl 64
ip link set gretap1 up
# L2 브리징에 참여
ip link add br0 type bridge
ip link set gretap1 master br0
ip link set eth1 master br0
ip link set br0 up
# → eth1과 gretap1이 같은 브로드캐스트 도메인을 공유
# 커널 내부: dev→type = ARPHRD_ETHER
# dev→hard_header_len = ETH_HLEN (14)
# Protocol Type = 0x6558 (Transparent Ethernet Bridging)
| 특성 | GRE (L3) | GRETAP (L2) |
|---|---|---|
| 캡슐화 대상 | IP 패킷 (L3) | Ethernet 프레임 (L2) |
| ARPHRD | ARPHRD_IPGRE | ARPHRD_ETHER |
| Protocol Type | 0x0800 (IPv4) / 0x86DD (IPv6) | 0x6558 (TEB) |
| 브리지 포트 | 불가 | 가능 |
| MAC 주소 | 없음 (point-to-point) | 있음 (자동 생성) |
| ARP | 불필요 (NOARP) | 정상 동작 |
| 오버헤드 | 외부IP(20) + GRE(4~16) | 외부IP(20) + GRE(4~16) + 내부ETH(14) |
| 대표 사용 사례 | site-to-site VPN, 라우팅 프로토콜 터널링 | L2 VPN, 원격 브리징, OVS 오버레이 |
IPv6 GRE (ip6_gre)
IPv6를 외부 전송 프로토콜로 사용하는 GRE 터널입니다. ip6_gre.c 모듈이 처리하며, ip6gre/ip6gretap 두 가지 모드를 지원합니다.
# IPv6 GRE 터널 (L3)
ip link add ip6gre1 type ip6gre \
local 2001:db8::1 remote 2001:db8::2
# IPv6 GRETAP 터널 (L2)
ip link add ip6gretap1 type ip6gretap \
local 2001:db8::1 remote 2001:db8::2
# 커널: net/ipv6/ip6_gre.c → ip6gre_newlink()
# 외부 IP 헤더가 IPv6 (40바이트)이므로 오버헤드 증가
ERSPAN (Encapsulated Remote SPAN)
ERSPAN은 GRE를 이용해 미러링된 트래픽을 원격으로 전송하는 프로토콜입니다. 네트워크 모니터링과 IDS/IPS 배치에 주로 사용됩니다.
| ERSPAN 버전 | Protocol Type | 추가 헤더 | 특징 |
|---|---|---|---|
| Type I | 0x88BE | 없음 | 추가 ERSPAN 메타데이터 없이 원시 미러링 프레임 전달 |
| Type II (v1) | 0x88BE | 8바이트 ERSPAN 헤더 | Session ID, VLAN, COS, 포트 인덱스 |
| Type III (v2) | 0x22EB | 12바이트 ERSPAN 헤더 + 선택적 서브헤더 | 타임스탬프 (100μs 해상도), BSO, FT, SGT |
# ERSPAN Type II 터널 (미러링 소스)
ip link add erspan1 type erspan \
local 10.0.0.1 remote 10.0.0.2 \
seq key 100 \
erspan_ver 1 erspan 100 # session ID = 100
# ERSPAN Type III 터널
ip link add erspan2 type erspan \
local 10.0.0.1 remote 10.0.0.2 \
seq key 200 \
erspan_ver 2 erspan_dir ingress erspan_hwid 0x7
ip link set erspan1 up
# tc mirror 액션으로 트래픽 미러링 연결
tc qdisc add dev eth0 ingress
tc filter add dev eth0 ingress protocol all \
matchall action mirred egress mirror dev erspan1
# 커널 내부: ip_gre.c/ip6_gre.c 경로에서 ERSPAN 헤더를 추가
# Linux iproute2 UAPI는 ERSPAN 링크 생성 시 seq + key 인자를 함께 받습니다
ip link help erspan 기준으로 erspan/ip6erspan 링크는 seq, key, erspan_ver 조합을 사용합니다. Type I 자체는 추가 ERSPAN 헤더가 없지만, Linux 실무에서는 Type II/III 링크를 직접 생성하는 경우가 훨씬 많습니다.
/* include/net/erspan.h — ERSPAN 헤더 구조체 */
struct erspan_base_hdr {
#if defined(__LITTLE_ENDIAN_BITFIELD)
__u8 vlan_upper:4,
ver:4;
__u8 vlan:8;
__u8 session_id_upper:2,
t:1,
en:2,
cos:3;
__u8 session_id:8;
#else
...
#endif
};
/* ERSPAN Type III 확장 */
struct erspan_md2 {
__be32 timestamp; /* 100μs granularity */
__be16 sgt; /* Security Group Tag */
#if defined(__LITTLE_ENDIAN_BITFIELD)
__u8 hwid_upper:2,
ft:5,
p:1;
__u8 o:1,
gra:2,
dir:1,
hwid:4;
#else
...
#endif
};
GRE 키, 시퀀스 번호, 체크섬
GRE Key를 이용한 터널 다중화
GRE Key는 동일한 src/dst IP 쌍에서 여러 논리 터널을 다중화하는 32비트 식별자입니다.
커널은 터널 lookup 시 (remote_ip, local_ip, key) 3-tuple로 매칭합니다.
# 같은 엔드포인트에 서로 다른 키로 다중 터널
ip link add gre-a type gre \
local 10.0.0.1 remote 10.0.0.2 key 100
ip link add gre-b type gre \
local 10.0.0.1 remote 10.0.0.2 key 200
# 입력/출력 키를 별도로 설정 (비대칭 키)
ip link add gre-asym type gre \
local 10.0.0.1 remote 10.0.0.2 \
ikey 100 okey 200
# 커널 내부: ip_tunnel_lookup()
# → hash(remote, local, key)로 O(1) 탐색
# fallback: wildcard remote (0.0.0.0) → 다중 피어 수용 가능
/* net/ipv4/ip_tunnel.c — 터널 lookup 로직 */
struct ip_tunnel *ip_tunnel_lookup(struct ip_tunnel_net *itn,
int link, __be16 flags, __be32 remote, __be32 local, __be32 key)
{
struct ip_tunnel *t, *cand = NULL;
struct hlist_head *head;
unsigned int hash;
/* 1차: (remote, local, key) 정확 매칭 */
hash = ip_tunnel_hash(key, remote);
head = &itn->tunnels[hash];
hlist_for_each_entry_rcu(t, head, hash_node) {
if (local == t->parms.iph.saddr &&
remote == t->parms.iph.daddr &&
key == t->parms.i_key)
return t; /* 정확 매칭 */
}
/* 2차: wildcard remote (0.0.0.0) 매칭 */
hash = ip_tunnel_hash(key, 0);
head = &itn->tunnels[hash];
hlist_for_each_entry_rcu(t, head, hash_node) {
if (local == t->parms.iph.saddr &&
key == t->parms.i_key)
cand = t;
}
/* 3차: fallback 터널 (gre0 등) */
...
return cand;
}
시퀀스 번호
GRE 시퀀스 번호는 RFC 2890에서 "신뢰성은 없지만 순서 있는 전달"을 위해 정의됐습니다. Linux에서도 입력 시퀀스 검사는 순서가 어긋난 패킷을 드롭하므로, 재정렬이 조금이라도 발생할 수 있는 underlay에서는 매우 조심해서 써야 합니다.
# 출력 쪽에만 시퀀스 번호를 붙이는 예시
ip link add gre-txseq type gre \
local 10.0.0.1 remote 10.0.0.2 oseq
# 수신 쪽에서 시퀀스 검증을 요구하는 예시
ip link add gre-rxseq type gre \
local 10.0.0.2 remote 10.0.0.1 iseq
# 주의:
# 1. ip-tunnel(8)은 결합 옵션인 'seq'가 제대로 동작하지 않으므로 사용하지 말라고 경고합니다.
# 2. ECMP, LAG, RSS, 재전송 경로가 있는 underlay에서는 iseq를 쉽게 깨뜨립니다.
/* 수신 측 시퀀스 번호 검증 */
if (flags & TUNNEL_SEQ) {
if (ntohl(tpi->seq) < tunnel->i_seqno) {
/* 이전 시퀀스 → 드롭 (replay 또는 reordering) */
tunnel->dev->stats.rx_fifo_errors++;
tunnel->dev->stats.rx_errors++;
goto drop;
}
tunnel->i_seqno = ntohl(tpi->seq) + 1;
}
체크섬
GRE 체크섬은 GRE 헤더와 캡슐화된 페이로드 전체에 대한 무결성 검증입니다. 외부 IP 헤더의 체크섬과는 별도로 동작합니다.
# 체크섬 활성화
ip link add gre1 type gre \
local 10.0.0.1 remote 10.0.0.2 csum
# 입력/출력 별도 설정
ip link add gre1 type gre \
local 10.0.0.1 remote 10.0.0.2 \
icsum ocsum # 양방향 체크섬
tx-gre-csum-segmentation)를 지원하는 NIC에서는 영향이 적지만, 소프트웨어 전용 환경에서는 처리량이 10~20% 감소할 수 있습니다. 내부 페이로드가 이미 TCP/UDP 체크섬으로 보호되므로 대부분의 환경에서 GRE 체크섬은 불필요합니다.
고급 설정
Collect Metadata 모드 (Flow-Based Tunneling)
Collect Metadata 모드(external)는 터널 파라미터를 디바이스에 고정하지 않고, 각 패킷의 메타데이터에서 동적으로 추출합니다.
OVS(Open vSwitch), TC tunnel_key 액션, BPF 프로그램에서 활용합니다.
# Collect Metadata 모드 GRE 디바이스
ip link add gre-md type gre external
# TC를 이용한 flow-based 터널링
tc qdisc add dev gre-md ingress
tc filter add dev gre-md ingress \
flower enc_dst_ip 10.0.0.2 enc_key_id 100 \
action tunnel_key unset \
action mirred egress redirect dev eth0
# 송신 방향: 패킷에 터널 메타데이터 설정
tc qdisc add dev eth0 ingress
tc filter add dev eth0 ingress \
flower dst_ip 192.168.0.0/24 \
action tunnel_key set id 100 dst_ip 10.0.0.2 \
action mirred egress redirect dev gre-md
/* 커널 내부: collect_md 모드 송신 */
static netdev_tx_t gre_fb_xmit(struct sk_buff *skb,
struct net_device *dev,
__be16 proto)
{
struct ip_tunnel_info *tun_info;
/* skb에 연결된 tunnel metadata 추출 */
tun_info = skb_tunnel_info(skb);
if (!tun_info || !(tun_info->mode & IP_TUNNEL_INFO_TX))
goto err_free_skb;
key = &tun_info->key;
/* key→u.ipv4.dst → 외부 목적지 IP
* key→tun_id → GRE Key
* key→tun_flags → GRE 플래그 (CSUM, KEY, SEQ) */
...
}
external 모드 제약: iproute2 매뉴얼 기준으로 external은 remote, local, key, csum, seq 같은 고정 옵션과 상호배타적입니다. 즉 "고정 목적지 터널"이 아니라 "외부 제어 평면이 패킷별 메타데이터를 채우는 장치"로 이해해야 맞습니다.
다중점 GRE (mGRE)
원격 주소를 지정하지 않은 GRE 터널은 여러 피어와 동적으로 통신할 수 있습니다. NHRP(Next Hop Resolution Protocol)와 결합하면 DMVPN(Dynamic Multipoint VPN) 구현이 가능합니다.
# mGRE 터널 (remote 미지정)
ip tunnel add mgre0 mode gre \
local 10.0.0.1 key 1000 \
ttl 64
# remote를 지정하지 않았으므로 wildcard 터널
# 패킷의 목적지 IP를 기반으로 NHRP가 next-hop 결정
# → 라우팅 테이블 또는 NHRP 캐시에서 외부 목적지 결정
ip addr add 172.16.0.1/24 dev mgre0
ip link set mgre0 up
# NHRP 데몬 (OpenNHRP 또는 FRR)이 필요
# Cisco DMVPN과 상호 운용 가능
피어 생존 감지와 제어 평면
일부 벤더 장비 문서에는 GRE keepalive가 자주 등장하지만, 현재 Linux의 ip tunnel/ip link GRE UAPI에는 그런 설정 노브가 없습니다. 따라서 피어 생존 감지는 GRE 자체가 아니라 GRE 위나 아래에서 따로 설계해야 합니다.
| 방법 | 배치 위치 | 장점 | 주의점 |
|---|---|---|---|
| BFD | GRE 위 또는 underlay | 탐지 시간이 짧고 라우팅과 연계하기 쉽습니다 | 추가 데몬과 라우팅 스택 연동이 필요합니다 |
| OSPF/BGP hello | GRE 위 | 제어 평면과 데이터 경로를 함께 검증합니다 | 단순 L2 브리징만 쓰는 GRETAP에는 직접 적용하기 어렵습니다 |
| ICMP/HTTP 헬스체크 | GRE 위 | 구성은 쉽고 애플리케이션까지 검증 가능합니다 | 장애 감지 속도와 오탐 관리가 약합니다 |
| underlay BFD/ICMP | 외부 IP 경로 | 터널 바깥 경로 불안정을 빨리 감지합니다 | GRE 종단 로직이나 내부 라우팅 상태까지는 보장하지 않습니다 |
GRE와 Netfilter/conntrack
nf_conntrack_proto_gre
GRE는 TCP/UDP와 달리 포트가 없으므로, conntrack은 GRE Key를 포트 대신 사용하여 연결을 추적합니다.
PPTP helper (nf_conntrack_pptp)는 GRE 콜 ID를 이용한 NAT를 지원합니다.
/* net/netfilter/nf_conntrack_proto_gre.c
*
* GRE conntrack 튜플:
* src: (src_ip, gre_key_or_call_id)
* dst: (dst_ip, gre_key_or_call_id)
*
* GRE v0 (표준): Key 필드를 식별자로 사용
* GRE v1 (PPTP): Call ID를 식별자로 사용
*/
/* conntrack 조회 */
conntrack -L -p gre
# 출력 예:
# gre 47 src=10.0.0.1 dst=10.0.0.2 srckey=0x00000064 dstkey=0x00000064
# src=10.0.0.2 dst=10.0.0.1 srckey=0x00000064 dstkey=0x00000064
nftables/iptables GRE 필터링
# nftables: GRE 프로토콜 필터링
nft add rule inet filter forward \
ip protocol gre accept
# GRE 키 기반 필터링 (K=1, C=0, S=0 으로 헤더 길이를 고정한 환경만)
nft add rule inet filter forward \
ip protocol gre \
@th,32,32 0x00000064 accept
# iptables: GRE 프로토콜 허용
iptables -A FORWARD -p gre -j ACCEPT
# PPTP helper 로드 (GRE v1 NAT 지원)
modprobe nf_conntrack_pptp
modprobe nf_nat_pptp
# PPTP 서버로의 DNAT
iptables -t nat -A PREROUTING -p tcp --dport 1723 \
-j DNAT --to-destination 192.168.1.100
# → nf_conntrack_pptp가 GRE 세션을 자동 추적하여 NAT 수행
gre key 파서를 제공하지 않으므로, Key 매칭은 결국 raw 오프셋에 의존합니다. 체크섬(C)이나 시퀀스(S)가 켜지면 Key 오프셋이 달라지므로, 운영 환경에서 GRE 옵션 조합을 고정하지 않았다면 인터페이스 단위 정책이나 TC/OVS 메타데이터 기반 정책이 더 안전합니다.
GRE 하드웨어/소프트웨어 오프로드
GSO (Generic Segmentation Offload)
GSO는 GRE 터널 내부의 대형 TCP 세그먼트를 지연 분할하여 처리량을 극대화합니다:
/* GSO 유형: include/linux/skbuff.h */
SKB_GSO_GRE /* GRE 터널의 GSO — 내부 IP 분할 */
SKB_GSO_GRE_CSUM /* GRE 체크섬 포함 GSO — HW가 GRE csum도 처리 */
/* NIC feature 플래그 */
NETIF_F_GSO_GRE /* tx-gre-segmentation */
NETIF_F_GSO_GRE_CSUM /* tx-gre-csum-segmentation */
# NIC의 GRE 오프로드 기능 확인
ethtool -k eth0 | grep gre
# tx-gre-segmentation: on
# tx-gre-csum-segmentation: on
# GRE 오프로드 비활성화 (디버깅용)
ethtool -K eth0 tx-gre-segmentation off
ethtool -K eth0 tx-gre-csum-segmentation off
GRO (Generic Receive Offload)
수신 측에서는 GRO가 여러 GRE 패킷을 하나의 큰 패킷으로 병합하여 프로토콜 스택 처리 효율을 높입니다:
/* net/ipv4/gre_offload.c — GRE GRO 콜백 */
static struct sk_buff *gre_gro_receive(
struct list_head *head, struct sk_buff *skb)
{
/* GRE 헤더 파싱 후, 동일한 flow (src/dst/key)의 패킷을 병합
* → 내부 TCP 세그먼트를 하나의 큰 skb로 결합
* → 프로토콜 스택을 한 번만 타므로 CPU 절약 */
...
}
/* ip_gre.c에서 GRO 셀 초기화 */
gro_cells_init(&tunnel->gro_cells, dev);
/* → per-CPU GRO 처리로 멀티코어 확장성 확보 */
| 오프로드 기능 | 방향 | ethtool 플래그 | 효과 |
|---|---|---|---|
| GSO (GRE) | TX | tx-gre-segmentation |
64KB 청크를 MTU 크기로 지연 분할 |
| GSO (GRE+csum) | TX | tx-gre-csum-segmentation |
GSO + HW GRE 체크섬 계산 |
| GRO | RX | rx-gro-hw (NIC별) |
수신 패킷 병합으로 인터럽트/스택 처리 감소 |
| RX Checksum | RX | rx-checksum |
내부/외부 체크섬 HW 검증 |
MTU와 단편화 (Path MTU Discovery)
GRE 캡슐화는 패킷 크기를 증가시키므로 MTU 관리가 매우 중요합니다. 잘못된 MTU 설정은 블랙홀, 성능 저하, 연결 실패의 주요 원인입니다.
MTU 계산
| 터널 유형 | 오버헤드 | 터널 MTU (물리 1500) |
|---|---|---|
| GRE (기본) | 24바이트 | 1476 |
| GRE + Key | 28바이트 | 1472 |
| GRE + Key + Seq | 32바이트 | 1468 |
| GRE + Key + Seq + Csum | 36바이트 | 1464 |
| GRETAP (기본) | 38바이트 | 1462 |
| GRETAP + Key | 42바이트 | 1458 |
| IPv6 GRE (기본) | 44바이트 | 1456 |
| ERSPAN Type II | 50바이트 | 1450 |
| ERSPAN Type III | 54바이트 | 1446 |
Path MTU Discovery
# 터널 MTU 명시 설정
ip link set gre1 mtu 1400
# PMTUD 관련 커널 동작:
# 1. 외부 IP 헤더에 DF(Don't Fragment) 비트 설정 (기본값)
# 2. 중간 라우터가 ICMP "Fragmentation Needed" 반환
# 3. 커널이 터널 MTU를 동적으로 조정
# DF 비트 정책 설정
ip tunnel change gre1 pmtudisc # DF 비트 설정 (기본값 — PMTUD 활성화)
ip tunnel change gre1 nopmtudisc # DF 비트 해제 (단편화 허용)
ip link set dev gre1 type gre ignore-df # inner DF를 무시하고 외부 조각화를 허용
# MSS clamping으로 블랙홀 방지
iptables -t mangle -A FORWARD -o gre1 \
-p tcp --tcp-flags SYN,RST SYN \
-j TCPMSS --clamp-mss-to-pmtu
# nftables 동일 설정
nft add rule inet mangle forward \
oifname "gre1" tcp flags syn / syn,rst \
tcp option maxseg size set rt mtu
/* net/ipv4/ip_tunnel.c — 터널 송신 시 MTU 처리 */
void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
const struct iphdr *tnl_params, u8 protocol)
{
...
/* DF 비트가 설정되어 있고 패킷이 MTU를 초과하면 */
if (tnl_params->frag_off & htons(IP_DF)) {
if (skb->len > mtu) {
/* ICMP "Fragmentation Needed" 발신자에게 전송 */
icmp_ndo_send(skb, ICMP_DEST_UNREACH,
ICMP_FRAG_NEEDED,
htonl(mtu));
goto tx_error;
}
}
...
}
nopmtudisc로 단편화 허용 (성능 감소).
ignore-df는 마지막 수단: iproute2는 GRE/GRETAP에 [no]ignore-df를 제공합니다. 이 옵션은 inner 패킷의 DF를 무시해 조각화를 강제로 허용하므로, PMTUD가 완전히 깨진 오래된 경로에서는 임시 처방이 될 수 있지만 CPU 사용량과 재조립 비용이 늘고 중간 장비 호환성도 나빠질 수 있습니다.
GRE 변형 프로토콜
PPTP (Point-to-Point Tunneling Protocol)
PPTP는 Enhanced GRE(버전 1)를 사용하는 VPN 프로토콜입니다. TCP 1723 포트로 제어 채널을 설정하고, GRE v1으로 데이터를 전송합니다.
- Enhanced GRE (Version 1) — PPTP 전용
- 0 1 2 3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- |C|R|K|S|s|Recur|A| Flags | Ver | Protocol Type (880B) |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Key (Payload Length) | Key (Call ID) |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Sequence Number (optional) |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Acknowledgment Number (optional) |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- Ver = 1 (Enhanced GRE)
- K = 1 항상 (Call ID가 Key 역할)
- Protocol Type = 0x880B (PPP)
- A 비트: Acknowledgment 필드 존재
- 커널 모듈: drivers/net/ppp/pptp.c
- conntrack: nf_conntrack_pptp, nf_nat_pptp
# PPTP 관련 커널 모듈
modprobe ppp_generic
modprobe pptp
modprobe nf_conntrack_pptp # GRE v1 conntrack
modprobe nf_nat_pptp # PPTP NAT helper
# PPTP 서버는 pptpd, 클라이언트는 pptp-linux/NetworkManager 사용
# 보안 주의: PPTP의 MS-CHAPv2 인증은 취약 — IPSec/WireGuard 권장
NVGRE (Network Virtualization using GRE)
NVGRE(RFC 7637)는 데이터 센터에서 GRE Key의 상위 24비트를 VSID(Virtual Subnet ID)로 사용하여 멀티테넌트 네트워크를 구현합니다.
# Linux에서 NVGRE 시뮬레이션 (GRETAP + Key)
ip link add nvgre1 type gretap \
local 10.0.0.1 remote 10.0.0.2 \
key 0x000064ff # VSID=100 (0x64), FlowID=0xff
# 실제 NVGRE 구현은 OVS 또는 하드웨어 VTEP에서 주로 사용
GRE-in-UDP (RFC 8086)
GRE를 UDP로 감싸서 NAT 통과와 ECMP 로드 밸런싱 문제를 해결합니다:
# FOU (Foo-over-UDP) 수신 포트 설정
modprobe fou
ip fou add port 4754 ipproto 47 # GRE-in-UDP 수신
# GRE 터널 생성 + FOU 캡슐화
ip link add gre-fou type gre \
local 10.0.0.1 remote 10.0.0.2 \
encap fou encap-sport auto encap-dport 4754
ip link set gre-fou up
실전 활용 시나리오
사이트 간 VPN (GRE + IPSec)
GRE 단독으로는 암호화가 없으므로, 일반적으로 IPSec과 결합하여 사용합니다. GRE가 터널링을, IPSec이 암호화를 담당합니다.
# 1. GRE 터널 생성
ip tunnel add gre-vpn mode gre \
local 203.0.113.1 remote 198.51.100.1 \
ttl 64 key 1000
ip addr add 172.16.0.1/30 dev gre-vpn
ip link set gre-vpn up
ip route add 192.168.2.0/24 dev gre-vpn
# 2. IPSec으로 GRE 트래픽 암호화 (xfrm)
ip xfrm state add \
src 203.0.113.1 dst 198.51.100.1 \
proto esp spi 0x1000 mode transport \
enc "aes" 0x$(openssl rand -hex 16) \
auth "hmac(sha256)" 0x$(openssl rand -hex 32)
ip xfrm state add \
src 198.51.100.1 dst 203.0.113.1 \
proto esp spi 0x1001 mode transport \
enc "aes" 0x$(openssl rand -hex 16) \
auth "hmac(sha256)" 0x$(openssl rand -hex 32)
# 3. IPSec 정책: GRE 트래픽(프로토콜 47)만 암호화
ip xfrm policy add \
src 203.0.113.1 dst 198.51.100.1 \
proto gre dir out \
tmpl src 203.0.113.1 dst 198.51.100.1 \
proto esp mode transport
ip xfrm policy add \
src 198.51.100.1 dst 203.0.113.1 \
proto gre dir in \
tmpl src 198.51.100.1 dst 203.0.113.1 \
proto esp mode transport
# 결과 패킷 구조:
# [외부 IP] [ESP] [GRE] [내부 IP] [페이로드]
# → GRE 터널 전체가 ESP로 암호화됨
멀티캐스트 라우팅 프로토콜 터널링
GRE의 핵심 사용 사례 중 하나는 OSPF, RIP 등의 멀티캐스트/브로드캐스트 라우팅 프로토콜을 인터넷을 통해 전달하는 것입니다:
# OSPF 멀티캐스트를 GRE로 전달
ip tunnel add gre-ospf mode gre \
local 10.0.0.1 remote 10.0.0.2 \
ttl 64
ip addr add 172.16.0.1/30 dev gre-ospf
ip link set gre-ospf up
ip link set gre-ospf multicast on # 멀티캐스트 활성화
# FRRouting OSPF 설정에서 GRE 인터페이스 포함
# interface gre-ospf
# ip ospf area 0.0.0.0
# ip ospf network point-to-point
OVS (Open vSwitch) GRE 오버레이
# OVS에서 GRE 포트 추가
ovs-vsctl add-br br-int
ovs-vsctl add-port br-int gre0 -- \
set interface gre0 type=gre \
options:remote_ip=10.0.0.2 \
options:key=flow # flow-based key (OVS가 동적 결정)
# OVS는 내부적으로 collect_md 모드의 GRE 디바이스를 사용
# → OpenFlow 규칙으로 터널 엔드포인트와 키를 동적 지정
네트워크 네임스페이스 간 GRE
# 네임스페이스 생성
ip netns add ns1
ip netns add ns2
# veth 쌍으로 네임스페이스 연결
ip link add veth1 type veth peer name veth2
ip link set veth1 netns ns1
ip link set veth2 netns ns2
ip netns exec ns1 ip addr add 10.0.0.1/24 dev veth1
ip netns exec ns1 ip link set veth1 up
ip netns exec ns2 ip addr add 10.0.0.2/24 dev veth2
ip netns exec ns2 ip link set veth2 up
# 각 네임스페이스에서 GRE 터널 생성
ip netns exec ns1 ip tunnel add gre1 mode gre \
local 10.0.0.1 remote 10.0.0.2 key 42
ip netns exec ns1 ip addr add 172.16.0.1/30 dev gre1
ip netns exec ns1 ip link set gre1 up
ip netns exec ns2 ip tunnel add gre1 mode gre \
local 10.0.0.2 remote 10.0.0.1 key 42
ip netns exec ns2 ip addr add 172.16.0.2/30 dev gre1
ip netns exec ns2 ip link set gre1 up
# 테스트
ip netns exec ns1 ping -c 3 172.16.0.2
성능 튜닝
성능 최적화 체크리스트
| 항목 | 기본값 | 권장 설정 | 효과 |
|---|---|---|---|
| GSO/GRO | on (NIC 지원 시) | on 유지 | 처리량 2~5배 향상 |
| GRE 체크섬 | off | off 유지 | 체크섬 계산 오버헤드 제거 |
| 시퀀스 번호 | off | off (ECMP 환경) | reorder 드롭 방지 |
| MTU | 자동 계산 | 명시 설정 + MSS clamp | 단편화/블랙홀 방지 |
| TTL | inherit (내부 TTL) | 64 고정 | TTL 소진 루프 방지 |
| txqueuelen | 1000 | 5000~10000 (고대역폭) | 큐 오버플로 감소 |
| RPS/RFS | off | on (멀티코어) | 터널 수신 CPU 분산 |
| nopmtudisc | off (DF 설정) | off 유지 + MSS clamp | 최적 MTU 자동 탐색 |
# 성능 최적화 적용 예시
ip link set gre1 mtu 1400 # 보수적 MTU
ip link set gre1 txqueuelen 5000 # 큐 확장
ethtool -K eth0 tx-gre-segmentation on # GSO 활성화
# MSS clamping (PMTUD 블랙홀 방지)
nft add rule inet mangle forward \
oifname "gre1" tcp flags syn / syn,rst \
tcp option maxseg size set rt mtu
# RPS 활성화 (터널 수신 CPU 분산)
echo ff > /sys/class/net/gre1/queues/rx-0/rps_cpus
# conntrack 비활성화 (순수 L3 터널, NAT 불필요 시)
nft add rule inet raw prerouting ip protocol gre notrack
nft add rule inet raw output ip protocol gre notrack
ECMP 해시와 내부 헤더 활용
순수 GRE는 underlay 관점에서 포트가 없기 때문에 많은 라우터가 outer IP 주소와 프로토콜 47만으로 해시를 계산합니다. 그 결과 대역폭이 충분한 멀티패스 underlay에서도 한 경로로만 몰리는 path polarization이 흔합니다.
Linux host 자체가 multipath next-hop을 선택하는 경우에는 fib_multipath_hash_policy와 fib_multipath_hash_fields로 inner header까지 해시에 반영할 수 있습니다. 다만 이것은 이 Linux 호스트의 라우팅 결정에만 영향을 주며, 중간 하드웨어 라우터의 해시 방식은 바꾸지 못합니다.
# Linux host의 multipath 라우팅이 inner L3까지 보도록 설정
sysctl -w net.ipv4.fib_multipath_hash_policy=2
# 사용자 정의 해시: outer + inner IP/프로토콜/포트 모두 반영
sysctl -w net.ipv4.fib_multipath_hash_policy=3
sysctl -w net.ipv4.fib_multipath_hash_fields=$(( \
0x0001 | 0x0002 | 0x0004 | \
0x0040 | 0x0080 | 0x0100 | 0x0400 | 0x0800 ))
# 핵심 비트:
# 0x0040 inner source IP, 0x0080 inner destination IP
# 0x0100 inner protocol, 0x0400 inner source port, 0x0800 inner destination port
GRE vs VXLAN 성능 비교
| 항목 | GRE | VXLAN |
|---|---|---|
| 캡슐화 오버헤드 | 24~36바이트 | 50바이트 |
| ECMP 해싱 | 불리 (IP proto 47, 포트 없음) | 유리 (UDP 소스 포트 엔트로피) |
| NAT 통과 | 불가 | 가능 (UDP) |
| CPU 사용 | 낮음 (단순 헤더) | 중간 (UDP 처리 추가) |
| HW 오프로드 | 광범위 지원 | 광범위 지원 |
| 멀티캐스트 | 네이티브 지원 | 외부 학습 필요 (FDB) |
디버깅
디버깅 도구
# 1. 터널 상태 확인
ip tunnel show
ip -d link show gre1
ip -s link show gre1 # RX/TX 바이트, 패킷, 에러 통계
ip monitor link # 링크 상태 변화 실시간 관찰
# 2. tcpdump로 GRE 패킷 캡처
tcpdump -i eth0 proto gre -nn -v
# → 외부 IP + GRE 헤더 + 내부 패킷 확인
# 내부 패킷도 디코딩
tcpdump -i eth0 proto gre -nn -v -e
# GRE 터널 인터페이스에서 캡처 (디캡슐레이션된 패킷)
tcpdump -i gre1 -nn -v
# 3. ip route get으로 경로 확인
ip route get 192.168.100.2 dev gre1
# 4. 커널 로그 확인
dmesg | grep -i gre
journalctl -k | grep -i tunnel
# 5. conntrack 확인 (GRE 세션)
conntrack -L -p gre
# 5-1. 드롭 원인 추적
perf trace -e skb:kfree_skb -a sleep 5
dropwatch -l kas
# 6. PMTUD 확인 (ICMP 에러 모니터)
tcpdump -i eth0 'icmp[0] == 3 and icmp[1] == 4' -nn -v
# → ICMP Destination Unreachable, Fragmentation Needed
# 7. 터널/라우팅 통계
nstat -az | grep -E 'InNoRoutes|InHdrErrors|OutFragFails|InCsumErrors'
자주 발생하는 문제
| 증상 | 원인 | 해결 방법 |
|---|---|---|
| 터널 인터페이스 UP이나 ping 불가 | 방화벽에서 프로토콜 47 차단 | iptables -A INPUT -p gre -j ACCEPT |
| 소형 패킷은 통과, 대형 패킷 실패 | MTU 불일치 / PMTUD 블랙홀 | MTU 수동 설정 + MSS clamping |
| RX 패킷 카운터 증가하나 전달 안 됨 | rp_filter (역방향 경로 필터) | sysctl net.ipv4.conf.gre1.rp_filter=0 |
| 터널 생성 시 "RTNETLINK: File exists" | gre0 기본 디바이스 이름 충돌 | ip link 스타일 사용 또는 다른 이름 지정 |
| GRE over NAT 실패 | GRE는 포트 없어 NAT 불가 | GRE-in-UDP (FOU) 사용 |
| TTL 초과 루프 | 재귀 라우팅 (터널 경로가 터널 자체) | TTL 고정, 라우팅 테이블 확인 |
| conntrack 테이블 가득 참 | GRE 세션 누적 | nf_conntrack_max 증가 또는 GRE notrack |
| ECMP 환경에서 seq 드롭 | 시퀀스 번호 순서 어긋남 | 시퀀스 번호 비활성화 |
ftrace를 이용한 커널 내부 추적
# GRE 관련 커널 함수 추적
cd /sys/kernel/debug/tracing
# function_graph 트레이서 활성화
echo function_graph > current_tracer
echo 'ip_tunnel_xmit' >> set_graph_function
echo 'ip_tunnel_rcv' >> set_graph_function
echo 'gre_rcv' >> set_graph_function
echo 'gre_build_header' >> set_graph_function
echo 1 > tracing_on
# 터널을 통해 패킷 전송 후 결과 확인
cat trace
# kprobe로 GRE 터널 lookup 추적
echo 'p:gre_lookup ip_tunnel_lookup remote=%si local=%dx key=%cx' \
> kprobe_events
echo 1 > events/kprobes/gre_lookup/enable
cat trace_pipe
sysctl 파라미터
| 파라미터 | 기본값 | 설명 |
|---|---|---|
net.ipv4.conf.gre1.rp_filter |
0 (커널 기본, 배포판 오버라이드 가능) | 역방향 경로 필터링. 비대칭 라우팅이면 0 또는 2(loose) 검토 |
net.ipv4.conf.gre1.forwarding |
0 | 터널을 통한 IP 포워딩 활성화 필요 |
net.ipv4.conf.gre1.accept_local |
0 | 로컬 소스 주소 패킷 수신 허용. same-host 터널 종단이나 특수 라우팅에서 사용 |
net.ipv4.conf.gre1.disable_policy |
0 | IPSec 정책 무시 (IPSec 미사용 시) |
net.ipv4.ip_forward |
0 | 전역 IP 포워딩 — GRE 라우터 역할 시 1 필수 |
net.ipv4.conf.all.send_redirects |
1 | ICMP 리디렉트 전송. GRE 라우터에서는 0 권장 |
net.ipv4.ip_no_pmtu_disc |
0 | 전역 PMTUD 비활성화 (비권장 — 터널별 설정 사용) |
net.ipv4.fib_multipath_hash_policy |
0 | Linux host의 multipath 해시 기준. 2는 inner L3, 3은 사용자 정의 필드 사용 |
net.ipv4.fib_multipath_hash_fields |
커널/배포판별 | policy=3일 때 outer/inner IP·프로토콜·포트 비트마스크를 선택 |
# GRE 라우터 기본 sysctl 설정
sysctl -w net.ipv4.ip_forward=1
sysctl -w net.ipv4.conf.gre1.rp_filter=0
sysctl -w net.ipv4.conf.gre1.forwarding=1
sysctl -w net.ipv4.conf.all.send_redirects=0
sysctl -w net.ipv4.fib_multipath_hash_policy=2
# 영구 적용 (/etc/sysctl.d/99-gre.conf)
net.ipv4.ip_forward = 1
net.ipv4.conf.default.rp_filter = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.fib_multipath_hash_policy = 2
커널 소스 맵
| 파일 | 역할 |
|---|---|
include/net/gre.h | GRE 헤더 구조체, 플래그 매크로, gre_protocol 등록 API |
include/net/ip_tunnels.h | struct ip_tunnel, ip_tunnel_parm, 터널 API 선언 |
include/net/erspan.h | ERSPAN 헤더 구조체 (Type II/III) |
include/uapi/linux/if_tunnel.h | UAPI: 터널 ioctl/netlink 상수, ip_tunnel_parm |
net/ipv4/gre_demux.c | GRE 프로토콜 47 디먹싱, 버전별 핸들러 디스패치 |
net/ipv4/gre_offload.c | GRE GSO/GRO 오프로드 콜백 |
net/ipv4/ip_gre.c | IPv4 GRE/GRETAP/ERSPAN 터널 디바이스 드라이버 |
net/ipv4/ip_tunnel.c | IPv4 터널 공통 로직 (lookup, xmit, rcv, change, delete) |
net/ipv4/ip_tunnel_core.c | iptunnel_xmit/rcv, tunnel metadata, FOU 캡슐화 |
net/ipv6/ip6_gre.c | IPv6 GRE/GRETAP/ERSPAN 터널 디바이스 드라이버 |
drivers/net/ppp/pptp.c | PPTP (Enhanced GRE v1) 드라이버 |
net/netfilter/nf_conntrack_proto_gre.c | GRE conntrack 프로토콜 핸들러 |
net/ipv4/netfilter/nf_nat_pptp.c | PPTP NAT helper (GRE Call ID NAT) |
표준 및 공식 문서
아래 문서는 이 페이지를 검증하거나 실제 운영 환경에서 세부 동작을 확인할 때 가장 먼저 볼 1차 자료입니다.
관련 문서
이 주제와 관련된 다른 문서를 더 깊이 이해하고 싶다면 다음을 참고하세요.