L2TP (Layer 2 Tunneling Protocol) 심화
L2TP는 Layer 2 프레임을 IP 네트워크 위에서 터널링하는 프로토콜입니다.
리눅스 커널은 L2TPv2(PPP 터널링)와 L2TPv3(이더넷/HDLC 등 다양한 Layer 2 프로토콜 터널링)를
모두 지원하며, l2tp_core, l2tp_ppp, l2tp_eth,
l2tp_netlink 네 가지 핵심 모듈로 구성됩니다.
본 문서는 프로토콜 구조부터 커널 내부 아키텍처, 패킷 포맷, 터널/세션 관리,
Netlink 설정 인터페이스, IPsec 연동, 실전 구성 예제, 디버깅까지 종합적으로 다룹니다.
핵심 요약
- L2TP 터널 --- 두 끝점(LAC/LNS) 사이의 제어/데이터 연결 컨테이너
- L2TP 세션 --- 터널 내부에서 실제 데이터를 주고받는 논리적 채널
- L2TPv2 --- PPP 프레임 전용 터널링 (RFC 2661)
- L2TPv3 --- 이더넷, HDLC 등 범용 Layer 2 터널링 (RFC 3931)
- Pseudowire --- L2TPv3에서 세션이 전달하는 가상 회선
단계별 이해
- 터널 생성
두 끝점 간 UDP 또는 IP 소켓을 열고 L2TP 터널을 수립합니다. - 세션 생성
터널 안에 하나 이상의 세션을 만들어 실제 데이터 전달 경로를 구성합니다. - 캡슐화 및 전송
원본 Layer 2 프레임을 L2TP 헤더로 감싸서 IP 네트워크를 통해 전송합니다. - 역캡슐화 및 전달
수신 측에서 L2TP 헤더를 벗기고 원본 프레임을 로컬 인터페이스로 전달합니다.
L2TP 개요
L2TP란?
L2TP(Layer 2 Tunneling Protocol)는 데이터 링크 계층(Layer 2) 프레임을 IP 네트워크를 통해 터널링하는 프로토콜입니다. Microsoft의 PPTP(Point-to-Point Tunneling Protocol)와 Cisco의 L2F(Layer 2 Forwarding)를 결합하여 IETF에서 표준화했습니다.
VPN 터널링의 역사
| 연도 | 프로토콜 | RFC | 특징 |
|---|---|---|---|
| 1996 | PPTP | RFC 2637 | Microsoft, GRE 기반 PPP 터널링 |
| 1998 | L2F | RFC 2341 | Cisco, UDP 기반 PPP 터널링 |
| 1999 | L2TPv2 | RFC 2661 | PPTP + L2F 통합, PPP 전용 |
| 2005 | L2TPv3 | RFC 3931 | 범용 Layer 2 터널링, 이더넷/HDLC/FR 지원 |
L2TPv2와 L2TPv3 비교
| 속성 | L2TPv2 | L2TPv3 |
|---|---|---|
| 표준 | RFC 2661 | RFC 3931 |
| 전송 프로토콜 | UDP만 (포트 1701) | UDP 또는 IP (프로토콜 115) |
| 페이로드 유형 | PPP 프레임만 | 이더넷, PPP, HDLC, FR 등 |
| 터널 ID 크기 | 16비트 | 32비트 |
| 세션 ID 크기 | 16비트 | 32비트 |
| 쿠키(인증) | 없음 | 최대 8바이트 |
| 주요 용도 | 원격 접속 VPN | L2 VPN, 사이트 간 연결, 이더넷 브리징 |
L2TP 프로토콜 구조
L2TP는 제어 메시지와 데이터 메시지의 두 가지 채널을 사용합니다. 제어 채널은 터널과 세션의 수립/해제를 담당하며 신뢰성 있는 전송(순서 번호, 재전송)을 보장합니다. 데이터 채널은 실제 페이로드를 전달하며 비신뢰성(best-effort)으로 동작합니다.
L2TP 프로토콜 스택
L2TPv2는 반드시 UDP(포트 1701) 위에서만 동작하지만, L2TPv3는 UDP 외에도 IP 프로토콜 번호 115를 사용하여 직접 IP 위에서 동작할 수 있습니다. IP 직접 캡슐화는 UDP 헤더 오버헤드를 제거하여 대역폭 효율이 높지만, NAT 환경에서는 사용이 어렵습니다.
L2TP 패킷 포맷
L2TPv2 제어 메시지 헤더
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|T|L|x|x|S|x|O|P|x|x|x|x| Ver | Length (opt) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Tunnel ID | Session ID |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Ns (opt) | Nr (opt) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Offset Size (opt) | Offset Padding (opt) ... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
헤더 필드 설명
- T 비트 메시지 유형: 1=제어, 0=데이터
- L 비트 Length 필드 존재 여부 (제어 메시지에서 필수)
- S 비트 Ns/Nr 순서 번호 필드 존재 여부
- O 비트 Offset 필드 존재 여부 (데이터 메시지 전용)
- P 비트 우선순위 비트 (데이터 메시지 전용)
- Ver 프로토콜 버전: L2TPv2=2
- Tunnel ID 16비트 터널 식별자 (수신 측이 할당)
- Session ID 16비트 세션 식별자 (수신 측이 할당)
- Ns/Nr 보내기/받기 순서 번호 (제어 메시지 신뢰성 보장용)
L2TPv3 데이터 메시지 헤더 (UDP 캡슐화)
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|T|L|x|x|S|x|O|P|x|x|x|x| Ver | Length (opt) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Session ID (32-bit) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Cookie (optional, 0/4/8 bytes) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| L2-Specific Sublayer (opt) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
L2TPv3 데이터 메시지 헤더 (IP 직접 캡슐화)
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Session ID (32-bit) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Cookie (optional, 0/4/8 bytes) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| L2-Specific Sublayer (opt) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
L2TPv3 over UDP 패킷 전체 구조
AVP (Attribute Value Pair)
L2TP 제어 메시지의 파라미터는 AVP 형태로 인코딩됩니다. 각 AVP는 다음 구조를 갖습니다.
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|M|H| rsvd | Length | Vendor ID |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Attribute Type | Attribute Value ... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
M 비트: 필수(Mandatory) AVP 표시. H 비트: 은닉(Hidden) AVP 표시. 주요 AVP 유형: Message Type(0), Protocol Version(2), Framing Capabilities(3), Bearer Capabilities(4), Tunnel ID(9), Session ID(14), Call Serial Number(15) 등.
L2TP 터널과 세션 개념
L2TP의 핵심 추상화는 터널(Tunnel)과 세션(Session)입니다. 하나의 터널 안에 여러 세션이 다중화(multiplexing)될 수 있으며, 각 세션은 독립적인 Layer 2 연결을 나타냅니다.
- LAC (L2TP Access Concentrator): 원격 사용자의 PPP 연결을 수신하여 L2TP 터널로 전달하는 장비
- LNS (L2TP Network Server): L2TP 터널을 종단하고 PPP 세션을 처리하는 서버
- L2TPv3에서는 LAC/LNS 대신 LCCE (L2TP Control Connection Endpoint)라는 용어를 사용
리눅스 커널 L2TP 아키텍처
리눅스 커널의 L2TP 서브시스템은 네 개의 핵심 모듈로 구성됩니다.
l2tp_core가 터널/세션 관리의 기반을 제공하고,
페이로드 유형에 따라 l2tp_ppp 또는 l2tp_eth가 세션 핸들러로 동작합니다.
l2tp_netlink는 사용자 공간과의 Netlink 기반 설정 인터페이스를 담당합니다.
커널 소스 파일 구조
net/l2tp/
l2tp_core.c /* 터널/세션 코어 로직, 캡슐화/역캡슐화 */
l2tp_core.h /* 핵심 자료구조 선언 */
l2tp_netlink.c /* Generic Netlink 인터페이스 */
l2tp_ppp.c /* PPPoL2TP 세션 핸들러 */
l2tp_eth.c /* L2TPv3 이더넷 세션 핸들러 */
l2tp_ip.c /* L2TPv3 over IPv4 소켓 */
l2tp_ip6.c /* L2TPv3 over IPv6 소켓 */
l2tp_debugfs.c /* debugfs 진단 인터페이스 */
L2TP 커널 모듈 구조
struct l2tp_tunnel
struct l2tp_tunnel은 L2TP 터널 하나를 표현하는 핵심 구조체입니다.
커널 소스(net/l2tp/l2tp_core.h)에서 발췌한 주요 필드는 다음과 같습니다.
struct l2tp_tunnel {
int magic; /* 매직 넘버 검증 */
struct rcu_head rcu;
int fd; /* 소켓 파일 디스크립터 */
struct sock *sock; /* 터널 소켓 */
int version; /* 2=L2TPv2, 3=L2TPv3 */
u32 tunnel_id; /* 로컬 터널 ID */
u32 peer_tunnel_id; /* 원격 터널 ID */
enum l2tp_encap_type encap; /* UDP 또는 IP */
struct l2tp_tunnel_cfg cfg; /* 설정 파라미터 */
struct hlist_head session_hlist[L2TP_HASH_SIZE];
/* 세션 해시 테이블 */
struct list_head list; /* 전역 터널 리스트 */
struct net *l2tp_net; /* 네트워크 네임스페이스 */
refcount_t ref_count; /* 참조 카운트 */
struct l2tp_stats stats; /* 통계 카운터 */
struct dentry *l2tp_dentry; /* debugfs 엔트리 */
};
코드 설명
- magic 구조체 유효성 검증용 매직 넘버.
L2TP_TUNNEL_MAGIC(0x42114DDA)과 비교 - sock 터널에 바인딩된 커널 소켓. UDP 또는 IP 소켓이 올 수 있음
- version L2TP 프로토콜 버전. 2(L2TPv2) 또는 3(L2TPv3)
- encap 캡슐화 유형:
L2TP_ENCAPTYPE_UDP또는L2TP_ENCAPTYPE_IP - session_hlist 터널 내 세션을 빠르게 조회하기 위한 해시 테이블
- l2tp_net 네트워크 네임스페이스 참조. 컨테이너 환경 지원
struct l2tp_session
struct l2tp_session {
int magic; /* 매직 넘버 검증 */
struct l2tp_tunnel *tunnel; /* 소속 터널 */
u32 session_id; /* 로컬 세션 ID */
u32 peer_session_id;/* 원격 세션 ID */
u8 cookie[8]; /* 세션 쿠키 (L2TPv3) */
int cookie_len;
u8 peer_cookie[8];
int peer_cookie_len;
u16 nr; /* 예상 수신 순서 번호 */
u16 ns; /* 다음 송신 순서 번호 */
enum l2tp_pwtype pwtype; /* pseudowire 유형 */
struct l2tp_session_cfg cfg; /* 세션 설정 */
struct l2tp_stats stats; /* 통계 */
void (*recv_skb)(struct l2tp_session *,
struct sk_buff *, int);
void (*session_close)(struct l2tp_session *);
struct hlist_node hlist; /* 터널 내 해시 노드 */
struct hlist_node global_hlist; /* 전역 해시 노드 */
refcount_t ref_count;
};
L2TP over UDP 동작 흐름
L2TP over UDP는 가장 일반적인 캡슐화 방식입니다. UDP 포트 1701을 사용하며, NAT 환경에서도 동작할 수 있다는 장점이 있습니다.
UDP 소켓에 L2TP 핸들러 등록
/* l2tp_core.c: 터널 생성 시 UDP 소켓에 콜백 등록 */
static int l2tp_tunnel_sock_create(struct net *net,
u32 tunnel_id,
struct l2tp_tunnel_cfg *cfg,
struct socket **sockp)
{
struct socket *sock;
struct udp_tunnel_sock_cfg udp_cfg = { };
/* UDP 소켓 생성 또는 기존 소켓 사용 */
udp_cfg.encap_type = UDP_ENCAP_L2TPINUDP;
udp_cfg.encap_rcv = l2tp_udp_encap_recv;
udp_cfg.encap_destroy = l2tp_udp_encap_destroy;
setup_udp_tunnel_sock(net, sock, &udp_cfg);
return 0;
}
코드 설명
- encap_type
UDP_ENCAP_L2TPINUDP는 UDP 소켓이 L2TP 패킷을 수신하도록 설정하는 캡슐화 유형 - encap_rcv
l2tp_udp_encap_recv()는 UDP 패킷이 도착할 때 호출되는 L2TP 수신 핸들러 - setup_udp_tunnel_sock UDP 터널 소켓 설정 헬퍼 함수.
inet_sk(sk)->encap_type을 설정하여 UDP 계층이 L2TP 콜백을 호출하도록 함
L2TP over IP 동작 흐름
L2TPv3는 UDP 외에도 IP 프로토콜 번호 115를 사용하여 직접 IP 위에서 동작할 수 있습니다. 이 방식은 UDP 헤더(8바이트)를 제거하여 오버헤드를 줄이지만, NAT 환경에서 동작하지 않습니다.
/* l2tp_ip.c: L2TP over IP 프로토콜 핸들러 등록 */
static const struct net_protocol l2tp_ip_protocol = {
.handler = l2tp_ip_recv,
.err_handler = l2tp_ip_err,
};
static int __init l2tp_ip_init(void)
{
int err;
err = inet_add_protocol(&l2tp_ip_protocol, IPPROTO_L2TP);
if (err)
return err;
err = inet_register_protosw(&l2tp_ip_protosw);
if (err) {
inet_del_protocol(&l2tp_ip_protocol, IPPROTO_L2TP);
return err;
}
return 0;
}
코드 설명
- net_protocol IP 프로토콜 핸들러 구조체.
handler가 패킷 수신 시 호출됨 - IPPROTO_L2TP IP 프로토콜 번호 115 (L2TP)
- inet_add_protocol 커널 IP 스택에 L2TP 프로토콜 핸들러를 등록
- inet_register_protosw L2TP 소켓 유형을 소켓 스위치에 등록하여
socket(AF_INET, SOCK_DGRAM, IPPROTO_L2TP)호출 가능
| 속성 | L2TP over UDP | L2TP over IP |
|---|---|---|
| 오버헤드 | UDP 8B + L2TP 헤더 | L2TP 헤더만 |
| NAT 통과 | 가능 | 불가 (프로토콜 115) |
| 방화벽 통과 | 용이 (UDP 포트 기반) | 어려움 (IP 프로토콜 기반) |
| 커널 모듈 | l2tp_core (내장) | l2tp_ip / l2tp_ip6 |
| 주요 용도 | 원격 접속, NAT 환경 | 데이터센터 간 전용선 |
L2TPv3 이더넷 터널 (l2tp_eth)
l2tp_eth 모듈은 L2TPv3 세션 위에 가상 이더넷 인터페이스(l2tpethN)를
생성합니다. 이 인터페이스를 통해 원격 사이트 간 투명한 Layer 2 연결이 가능합니다.
/* l2tp_eth.c: 이더넷 세션 생성 시 네트워크 디바이스 등록 */
static int l2tp_eth_create(struct net *net,
struct l2tp_tunnel *tunnel,
u32 session_id, u32 peer_session_id,
struct l2tp_session_cfg *cfg)
{
struct net_device *dev;
struct l2tp_session *session;
struct l2tp_eth *priv;
dev = alloc_netdev(sizeof(*priv), "l2tpeth%d",
NET_NAME_UNKNOWN, l2tp_eth_dev_setup);
if (!dev)
return -ENOMEM;
session = l2tp_session_create(sizeof(*spriv),
tunnel, session_id,
peer_session_id, cfg);
session->recv_skb = l2tp_eth_dev_recv;
register_netdevice(dev);
return 0;
}
코드 설명
- alloc_netdev
l2tpethN이름 패턴으로 가상 네트워크 디바이스 할당 - l2tp_session_create
l2tp_core의 세션 생성 함수 호출 - recv_skb 수신 콜백을
l2tp_eth_dev_recv()로 설정하여 수신된 이더넷 프레임을 가상 인터페이스로 전달
l2tpeth 인터페이스 특성
- 브리지 가능:
brctl addif br0 l2tpeth0으로 브리지에 추가 가능 - VLAN 지원: 802.1Q VLAN 태그가 투명하게 전달됨
- MTU 조정: 터널 오버헤드를 고려하여 적절한 MTU 설정 필요
- MAC 주소: 랜덤 할당되며,
ip link set으로 변경 가능
PPP over L2TP (l2tp_ppp + pppd)
l2tp_ppp 모듈(PPPoL2TP)은 L2TP 세션 위에 PPP 인터페이스를 생성합니다.
사용자 공간의 pppd와 함께 동작하며, 전통적인 원격 접속 VPN에 사용됩니다.
/* l2tp_ppp.c: PPPoL2TP 소켓 패밀리 정의 */
static const struct proto_ops pppol2tp_ops = {
.family = AF_PPPOX,
.owner = THIS_MODULE,
.release = pppol2tp_release,
.bind = sock_no_bind,
.connect = pppol2tp_connect,
.getname = pppol2tp_getname,
.ioctl = pppol2tp_ioctl,
.sendmsg = pppol2tp_sendmsg,
.recvmsg = pppol2tp_recvmsg,
};
/* PPPoL2TP 소켓 주소 구조체 */
struct pppol2tp_addr {
__kernel_pid_t pid; /* pppd PID */
int fd; /* 터널 소켓 FD */
struct sockaddr_in addr; /* IP 주소 */
__u16 s_tunnel; /* 로컬 터널 ID */
__u16 s_session; /* 로컬 세션 ID */
__u16 d_tunnel; /* 원격 터널 ID */
__u16 d_session; /* 원격 세션 ID */
};
xl2tpd 설정 예제
; /etc/xl2tpd/xl2tpd.conf - LNS 설정
[global]
listen-addr = 0.0.0.0
port = 1701
access control = no
[lns default]
ip range = 10.10.10.128-10.10.10.254
local ip = 10.10.10.1
require chap = yes
refuse pap = yes
require authentication = yes
name = l2tp-lns
pppoptfile = /etc/ppp/options.xl2tpd
length bit = yes
# /etc/ppp/options.xl2tpd - PPP 옵션
ipcp-accept-local
ipcp-accept-remote
ms-dns 8.8.8.8
ms-dns 8.8.4.4
noccp
auth
mtu 1410
mru 1410
nodefaultroute
debug
lock
proxyarp
connect-delay 5000
Netlink 설정 인터페이스 (ip l2tp 명령)
리눅스 커널의 L2TP 서브시스템은 Generic Netlink를 통해 사용자 공간에서 터널과 세션을
동적으로 생성/삭제/조회할 수 있는 인터페이스를 제공합니다.
iproute2의 ip l2tp 명령이 이 인터페이스를 사용합니다.
Netlink 명령 체계
/* include/uapi/linux/l2tp.h: Netlink 명령 정의 */
enum {
L2TP_CMD_NOOP,
L2TP_CMD_TUNNEL_CREATE, /* 터널 생성 */
L2TP_CMD_TUNNEL_DELETE, /* 터널 삭제 */
L2TP_CMD_TUNNEL_MODIFY, /* 터널 수정 */
L2TP_CMD_TUNNEL_GET, /* 터널 조회 */
L2TP_CMD_SESSION_CREATE, /* 세션 생성 */
L2TP_CMD_SESSION_DELETE, /* 세션 삭제 */
L2TP_CMD_SESSION_MODIFY, /* 세션 수정 */
L2TP_CMD_SESSION_GET, /* 세션 조회 */
__L2TP_CMD_MAX,
};
/* Netlink 속성 정의 (주요 항목) */
enum {
L2TP_ATTR_NONE,
L2TP_ATTR_PW_TYPE, /* pseudowire 유형 */
L2TP_ATTR_ENCAP_TYPE, /* 캡슐화 유형 (UDP/IP) */
L2TP_ATTR_PROTO_VERSION, /* 프로토콜 버전 (2/3) */
L2TP_ATTR_CONN_ID, /* 로컬 터널 ID */
L2TP_ATTR_PEER_CONN_ID, /* 원격 터널 ID */
L2TP_ATTR_SESSION_ID, /* 로컬 세션 ID */
L2TP_ATTR_PEER_SESSION_ID, /* 원격 세션 ID */
L2TP_ATTR_UDP_CSUM, /* UDP 체크섬 사용 */
L2TP_ATTR_COOKIE, /* 세션 쿠키 */
L2TP_ATTR_PEER_COOKIE, /* 원격 쿠키 */
/* ... */
};
ip l2tp 명령 예제
# L2TPv3 over UDP 터널 생성
ip l2tp add tunnel \
tunnel_id 100 peer_tunnel_id 200 \
encap udp \
local 192.168.1.1 remote 192.168.1.2 \
udp_sport 5000 udp_dport 5000
# 이더넷 세션 생성
ip l2tp add session \
tunnel_id 100 \
session_id 1000 peer_session_id 2000 \
cookie 0011223344556677 peer_cookie 7766554433221100
# 생성된 l2tpeth 인터페이스 활성화
ip link set l2tpeth0 up
ip addr add 10.0.0.1/24 dev l2tpeth0
# 터널/세션 목록 조회
ip l2tp show tunnel
ip l2tp show session
# 터널/세션 삭제
ip l2tp del session tunnel_id 100 session_id 1000
ip l2tp del tunnel tunnel_id 100
커널 설정 (CONFIG_L2TP, CONFIG_L2TP_V3, CONFIG_L2TP_ETH)
관련 커널 설정 옵션
# 핵심 L2TP 지원
CONFIG_L2TP=m # L2TP 코어 (l2tp_core)
CONFIG_L2TP_V3=y # L2TPv3 지원 (l2tp_core에 포함)
# 세션 핸들러
CONFIG_L2TP_ETH=m # L2TPv3 이더넷 터널 (l2tp_eth)
CONFIG_PPPOL2TP=m # PPP over L2TP (l2tp_ppp)
# IP 캡슐화
CONFIG_L2TP_IP=m # L2TPv3 over IPv4 (l2tp_ip)
CONFIG_L2TP_DEBUGFS=m # debugfs 디버그 인터페이스 (선택)
# 의존성
CONFIG_NET=y # 네트워킹 지원 (필수)
CONFIG_INET=y # IPv4 지원 (필수)
CONFIG_PPP=m # PPP 지원 (l2tp_ppp 사용 시)
CONFIG_PPPOX=m # PPPoX 소켓 (l2tp_ppp 사용 시)
CONFIG_IPV6=m # IPv6 지원 (l2tp_ip6 사용 시)
모듈 로딩 및 의존성
# 모듈 수동 로딩
modprobe l2tp_core
modprobe l2tp_netlink
modprobe l2tp_eth # 이더넷 터널 사용 시
modprobe l2tp_ppp # PPP 터널 사용 시
modprobe l2tp_ip # IP 캡슐화 사용 시
# 모듈 의존성 확인
modinfo l2tp_eth
# depends: l2tp_core,l2tp_netlink
# 현재 로딩된 L2TP 모듈 확인
lsmod | grep l2tp
ip l2tp 명령 사용 시 필요한 모듈이 자동으로 로딩됩니다.
Netlink Generic Family 이름(l2tp)으로 l2tp_netlink가 먼저 로딩되고,
세션 유형에 따라 l2tp_eth 또는 l2tp_ppp가 추가 로딩됩니다.
보안과 성능
IPsec 연동
L2TP 자체에는 암호화 기능이 없으므로, 보안이 필요한 환경에서는 반드시 IPsec(특히 ESP 터널/트랜스포트 모드)과 함께 사용해야 합니다. 일반적으로 L2TP/IPsec이라 불리는 조합은 IKE(포트 500/4500)로 SA를 수립한 후, L2TP 트래픽을 ESP로 암호화합니다.
strongSwan 연동 설정
# /etc/ipsec.conf - strongSwan L2TP/IPsec 설정
conn l2tp-psk
authby=secret
type=transport
left=%defaultroute
leftprotoport=17/1701
right=%any
rightprotoport=17/%any
keyexchange=ikev1
ike=aes256-sha256-modp2048
esp=aes256-sha256
auto=add
rekey=no
UDP 캡슐화 (NAT Traversal)
NAT 환경에서 IPsec ESP 패킷이 차단되는 문제를 해결하기 위해,
ESP를 UDP 포트 4500으로 캡슐화하는 NAT-T(NAT Traversal)를 사용합니다.
커널은 XFRM 프레임워크를 통해 이를 자동으로 처리합니다.
성능 고려사항
| 항목 | 영향 | 최적화 방법 |
|---|---|---|
| 캡슐화 오버헤드 | 패킷당 20-60바이트 추가 | MTU 조정, Path MTU Discovery 활성화 |
| 암호화 부하 | IPsec ESP 처리 CPU 사용 | AES-NI 하드웨어 가속 활용 |
| 이중 캡슐화 | L2TP + IPsec 중첩 오버헤드 | L2TPv3 over IP 사용 (NAT 불필요 시) |
| 세그먼테이션 오프로드 | 터널 내부 TSO/GSO 미동작 가능 | GRO 활성화, 터널 디바이스 GSO 확인 |
실전 구성 예제
예제 1: L2TPv3 이더넷 브리징 (사이트 간 L2 연결)
# === 사이트 A (192.168.1.1) ===
# L2TPv3 over UDP 터널 생성
ip l2tp add tunnel \
tunnel_id 1 peer_tunnel_id 2 \
encap udp \
local 192.168.1.1 remote 192.168.2.1 \
udp_sport 5000 udp_dport 5000
# 이더넷 세션 생성
ip l2tp add session \
tunnel_id 1 \
session_id 10 peer_session_id 20
# l2tpeth0 인터페이스 활성화
ip link set l2tpeth0 up mtu 1446
# 브리지 생성 및 인터페이스 추가
ip link add br0 type bridge
ip link set eth1 master br0 # 로컬 LAN 인터페이스
ip link set l2tpeth0 master br0 # L2TP 가상 인터페이스
ip link set br0 up
ip addr add 10.0.0.1/24 dev br0
# === 사이트 B (192.168.2.1) ===
# L2TPv3 over UDP 터널 생성 (ID 반대)
ip l2tp add tunnel \
tunnel_id 2 peer_tunnel_id 1 \
encap udp \
local 192.168.2.1 remote 192.168.1.1 \
udp_sport 5000 udp_dport 5000
# 이더넷 세션 생성 (ID 반대)
ip l2tp add session \
tunnel_id 2 \
session_id 20 peer_session_id 10
# l2tpeth0 인터페이스 활성화
ip link set l2tpeth0 up mtu 1446
# 브리지 생성 및 인터페이스 추가
ip link add br0 type bridge
ip link set eth1 master br0
ip link set l2tpeth0 master br0
ip link set br0 up
ip addr add 10.0.0.2/24 dev br0
예제 2: L2TPv2 LAC-LNS 구성 (PPP VPN)
# LNS 서버 - xl2tpd 시작
systemctl start xl2tpd
# LAC 클라이언트 - xl2tpd 설정
cat << 'EOF' > /etc/xl2tpd/xl2tpd.conf
[global]
access control = no
[lac vpn-server]
lns = 203.0.113.1
pppoptfile = /etc/ppp/options.l2tpd.client
length bit = yes
redial = yes
redial timeout = 5
max redials = 5
EOF
# LAC 연결 시작
echo "c vpn-server" > /var/run/xl2tpd/l2tp-control
# 연결 확인
ip addr show ppp0
ip route show
MTU 계산
- L2TPv3/UDP: 1500 - 20(IP) - 8(UDP) - 12(L2TP) - 14(inner Eth) = 1446
- L2TPv3/IP: 1500 - 20(IP) - 4(L2TP) - 14(inner Eth) = 1462
- L2TP/IPsec: 약 1300-1380 (ESP + IV + 패딩 + auth 포함)
예제 3: L2TPv3 over IP (Proto 115) 전용 구성
이 예제는 UDP를 사용하지 않고 IP 프로토콜 번호 115로 직접 L2TPv3를 전달합니다. NAT가 없는 전용망/DC 내부 백본에서 오버헤드를 줄이고 지연 편차를 최소화할 때 유용합니다. 반대로 NAT/인터넷 경로에서는 권장되지 않습니다.
# === Site A ===
ip l2tp add tunnel \
tunnel_id 100 peer_tunnel_id 200 \
encap ip \
local 10.10.10.1 remote 10.10.10.2 \
version 3
ip l2tp add session \
tunnel_id 100 \
session_id 1000 peer_session_id 2000 \
cookie 0x11112222 peer_cookie 0x33334444 \
l2spec_type none
ip link set l2tpeth0 up mtu 1462
ip link add br0 type bridge
ip link set eth1 master br0
ip link set l2tpeth0 master br0
ip link set br0 up
# === Site B ===
ip l2tp add tunnel \
tunnel_id 200 peer_tunnel_id 100 \
encap ip \
local 10.10.10.2 remote 10.10.10.1 \
version 3
ip l2tp add session \
tunnel_id 200 \
session_id 2000 peer_session_id 1000 \
cookie 0x33334444 peer_cookie 0x11112222 \
l2spec_type none
ip link set l2tpeth0 up mtu 1462
ip link add br0 type bridge
ip link set eth1 master br0
ip link set l2tpeth0 master br0
ip link set br0 up
# 검증 절차
ip l2tp show tunnel
ip l2tp show session
ip -s link show l2tpeth0
tcpdump -ni eth0 proto 115
# 실패 시 점검
# 1) 방화벽이 proto 115 차단
# 2) session_id / peer_session_id 교차 불일치
# 3) cookie / peer_cookie 불일치
# 4) MTU 과대 설정으로 단편화/손실 발생
제어 채널 상태 기계와 재전송
L2TP 데이터 채널은 best-effort로 동작하지만, 제어 채널은 Ns/Nr 순서 번호와 재전송 타이머로 신뢰성을 보장합니다.
현장에서 자주 발생하는 문제는 "데이터는 일부 보이는데 세션이 붙지 않음"으로, 대부분 제어 메시지 교환 실패(SCCRQ/SCCRP/ICRQ/ICRP 구간)에서 발생합니다.
L2TPv2 제어 메시지 핸드셰이크
| 단계 | 메시지 | 역할 | 실패 시 증상 |
|---|---|---|---|
| 1 | SCCRQ | 터널 연결 요청 시작 | 상대가 응답하지 않음 |
| 2 | SCCRP | 터널 연결 응답 (Tunnel ID 제시) | 터널 ID 충돌/거부 |
| 3 | SCCCN | 제어 연결 확정 | 제어 연결 수립 직전 종료 |
| 4 | ICRQ/ICRP/ICCN | 세션(호출) 생성 | ppp 인터페이스가 생기지 않음 |
| 5 | SLI/ZLB ACK | 링크 상태/응답 확인 | 주기적 재전송 증가 |
| 종료 | CDN/StopCCN | 세션/터널 해제 | 고아 세션 누적 |
재전송과 타이머 튜닝 포인트
# 운영 관찰 포인트 (도구별)
- xl2tpd: retransmit 횟수, hello timeout, control connection restart
- pppd: LCP Echo-Request/Echo-Reply 지연
- 커널: l2tp_core debug=0x0f 에서 제어 메시지 왕복 추적
# 장애 징후
- SCCRQ만 반복되고 SCCRP 없음: 방화벽/포트 정책 문제
- SCCRP 후 SCCCN 없음: AVP 협상 불일치(인증/프로파일)
- 세션 수립 후 CDN 빈발: MTU/MRU 불일치 또는 PPP LCP 협상 실패
NAT/방화벽/IPsec 설계 포인트
L2TP 단독은 암호화를 제공하지 않으므로 실제 운영에서는 L2TP/IPsec 조합이 일반적입니다. 특히 NAT 구간이 하나라도 있으면 L2TPv3 over IP(proto 115)보다 UDP 캡슐화가 현실적으로 유리합니다.
전송 방식별 네트워크 적합성
| 방식 | 장점 | 제약 | 권장 환경 |
|---|---|---|---|
| L2TPv3 over UDP | NAT 통과 용이, 운영 단순 | UDP 헤더 오버헤드 | 인터넷, 멀티 NAT |
| L2TPv3 over IP(115) | 헤더 오버헤드 최소 | NAT/방화벽 통과 어려움 | 전용망, 폐쇄망 |
| L2TP/IPsec (UDP 1701 + ESP) | 기밀성/무결성 확보 | IKE/정책 운영 복잡성 | 원격 접속 VPN |
| L2TP/IPsec NAT-T (UDP 4500) | NAT 환경에서도 IPsec 가능 | 캡슐화 단계 증가 | 클라우드/재택 환경 |
운영 방화벽 체크리스트
# nftables 예시 (IPv4/IPv6 공통 정책은 환경 맞게 확장)
nft add rule inet filter input udp dport { 500, 1701, 4500 } accept
nft add rule inet filter input ip protocol esp accept
# conntrack 확인: NAT-T 흐름 확인
conntrack -L -p udp | grep -E 'dport=(500|1701|4500)'
# xfrm 정책/상태 확인
ip xfrm state
ip xfrm policy
대규모 운영과 고가용성 설계
수백~수천 세션 환경에서는 단일 노드의 CPU, softirq, 메모리 단편화, conntrack 용량이 병목이 됩니다. L2TP는 세션이 많아질수록 제어 채널 안정성과 데이터 경로 분산(IRQ/RPS/XPS)이 성능의 핵심입니다.
규모별 운영 모델
| 규모 | 권장 구조 | 핵심 튜닝 포인트 |
|---|---|---|
| 소규모 (< 100 세션) | 단일 LNS | MTU/keepalive 정합성 |
| 중규모 (100~1000 세션) | LNS + 전용 모니터링 | RPS/XPS, conntrack, IRQ 분산 |
| 대규모 (1000+ 세션) | 다중 LNS 풀 + Anycast/LB | 세션 스티키 정책, 장애 전이 시간 |
| 이중화 | active-standby 또는 active-active | 터널 재수립 시간과 라우팅 수렴 시간 분리 측정 |
커널/시스템 튜닝 예시
# 소켓/큐 용량 (환경별 검증 후 적용)
sysctl -w net.core.rmem_max=33554432
sysctl -w net.core.wmem_max=33554432
sysctl -w net.core.netdev_max_backlog=100000
# conntrack 여유 확보 (NAT 환경)
sysctl -w net.netfilter.nf_conntrack_max=1048576
# UDP 메모리 임계값
sysctl -w net.ipv4.udp_mem='262144 524288 1048576'
# NIC 큐/IRQ 확인
ethtool -l eth0
cat /proc/interrupts | grep -E 'eth0|mlx|ixgbe'
디버깅과 모니터링
커널 디버그 메시지 활성화
# L2TP 디버그 플래그 활성화 (비트마스크)
# 0x01: 터널 제어 0x02: 터널 데이터
# 0x04: 세션 제어 0x08: 세션 데이터
echo 0x0f > /sys/module/l2tp_core/parameters/debug
# 또는 모듈 로딩 시
modprobe l2tp_core debug=0x0f
# 커널 로그 확인
dmesg | grep l2tp
journalctl -k | grep l2tp
debugfs 인터페이스
# debugfs 마운트 (이미 마운트되지 않은 경우)
mount -t debugfs none /sys/kernel/debug
# L2TP 터널 정보 확인
cat /sys/kernel/debug/l2tp/tunnels
# 출력 예:
# Tunnel 1, encap UDP, ver 3, fd 8
# peer tunnel 2
# from 192.168.1.1:5000 to 192.168.2.1:5000
# Session 10 (peer 20)
# interface l2tpeth0
# TX: 12345 bytes 67 packets
# RX: 54321 bytes 89 packets
tcpdump로 L2TP 패킷 캡처
# L2TP over UDP 캡처 (포트 1701)
tcpdump -i eth0 -nn udp port 1701 -vv
# L2TP over IP 캡처 (프로토콜 115)
tcpdump -i eth0 -nn proto 115 -vv
# L2TP/IPsec 캡처 (ESP + IKE)
tcpdump -i eth0 -nn '(udp port 500 or udp port 4500 or esp)' -vv
# 터널 내부 패킷 캡처 (l2tpeth 인터페이스에서)
tcpdump -i l2tpeth0 -nn -e
통계 및 모니터링
# ip l2tp 통계 확인
ip l2tp show tunnel
ip l2tp show session
# 인터페이스 통계
ip -s link show l2tpeth0
# 네트워크 네임스페이스 내 L2TP 확인
ip netns exec ns1 ip l2tp show tunnel
# 소켓 상태 확인
ss -unp | grep l2tp
일반적인 문제 해결
| 증상 | 원인 | 해결 방법 |
|---|---|---|
| 터널 생성 실패 | 모듈 미로딩 또는 소켓 바인딩 실패 | modprobe l2tp_netlink, 포트 충돌 확인 |
| 패킷 손실 | MTU 불일치로 단편화 발생 | 양쪽 MTU 동일하게 조정, PMTUD 확인 |
| l2tpeth0 생성 안 됨 | l2tp_eth 모듈 미로딩 | modprobe l2tp_eth |
| 세션 ID 불일치 | 양쪽 session_id/peer_session_id가 교차 설정되지 않음 | A의 session_id = B의 peer_session_id 확인 |
| IPsec 연동 안 됨 | IKE SA 수립 실패 또는 정책 불일치 | ipsec statusall, xfrm 정책 확인 |
| NAT 환경에서 연결 불가 | IP 캡슐화 사용 중 (NAT 미지원) | UDP 캡슐화로 전환 |
ftrace/perf/eBPF 심화 추적
패킷 손실이나 지연 급증을 "증상"이 아니라 "경로 지연"으로 분해하려면 커널 이벤트 추적이 필요합니다. L2TP는 수신 softirq 경로와 세션 디캡슐화 경로가 분리되므로, NIC IRQ부터 L2TP 세션 전달까지 단계별 시간을 측정해야 합니다.
# 1) 네트워크 수신 경로 기본 이벤트
trace-cmd record \
-e napi:napi_poll \
-e net:netif_receive_skb \
-e skb:kfree_skb
# 2) 소프트IRQ 지연 관찰
trace-cmd record -e irq:softirq_entry -e irq:softirq_exit -e irq:irq_handler_entry -e irq:irq_handler_exit
# 3) 결과 확인
trace-cmd report | less
# 4) perf로 CPU 핫스팟 확인
perf top -g --call-graph dwarf
perf record -g -a -- sleep 20
perf report
# bpftrace 예시: UDP 1701 수신량/지연 경향 관찰
bpftrace -e '
kprobe:udp_queue_rcv_skb /((struct sock *)arg0)->__sk_common.skc_dport == htons(1701)/ {
@rx++;
}
interval:s:5 {
printf("udp1701_rx=%d\\n", @rx);
clear(@rx);
}'
# 주의: 커널 버전별 심볼/구조체가 다르므로 환경 맞춤 수정 필요
운영 점검 체크리스트 (배포 전/장애 후 공통)
| 항목 | 확인 명령 | 정상 기준 |
|---|---|---|
| 터널/세션 수 | ip l2tp show tunnel, ip l2tp show session | 기대 세션 수와 일치 |
| MTU/MRU | ip link show l2tpeth0, ppp 옵션 | 양 끝단 값 일치 |
| 재전송 증가 | xl2tpd 로그, dmesg | grep l2tp | 지속 증가 없음 |
| NAT 상태 | conntrack -L | 주기적 만료/재생성 과다 없음 |
| IPsec SA | ip xfrm state | replay/error 카운터 안정 |
| CPU softirq | mpstat -P ALL 1, top -H | 특정 코어 과점유 없음 |
참고자료
- RFC 2661 - Layer Two Tunneling Protocol "L2TP" (L2TPv2 표준)
- RFC 3931 - Layer Two Tunneling Protocol - Version 3 (L2TPv3)
- RFC 3193 - Securing L2TP using IPsec
- RFC 4719 - Transport of Ethernet Frames over L2TPv3
- Linux Kernel Documentation - L2TP
- 커널 소스: net/l2tp/
- ip-l2tp(8) 매뉴얼 페이지
- xl2tpd - L2TP 데몬
- GRE (Generic Routing Encapsulation) - 또 다른 터널링 프로토콜
- IPSec & xfrm - L2TP 보안 연동의 핵심
- WireGuard - 현대적인 VPN 프로토콜
- TUN/TAP - 가상 네트워크 디바이스
- Bridge/VLAN/Bonding - L2TP 이더넷 브리징의 기반