L2TP (Layer 2 Tunneling Protocol)

L2TP는 Layer 2 프레임을 IP 네트워크 위에서 터널(Tunnel)링하는 프로토콜입니다. 리눅스 커널은 L2TPv2(PPP 터널링)와 L2TPv3(이더넷/HDLC 등 다양한 Layer 2 프로토콜 터널링)를 모두 지원하며, l2tp_core, l2tp_ppp, l2tp_eth, l2tp_netlink 네 가지 핵심 모듈로 구성됩니다. 본 문서는 프로토콜 구조부터 커널 내부 아키텍처, 패킷(Packet) 포맷, 터널/세션 관리, Netlink 설정 인터페이스, IPsec 연동, 실전 구성 예제, 디버깅(Debugging)까지 종합적으로 다룹니다.

전제 조건: 네트워크 스택(Network Stack)UDP, IPSec & xfrm 문서를 먼저 읽으세요. L2TP는 UDP 또는 IP 위에서 동작하며, 보안을 위해 IPsec과 함께 사용되는 경우가 많습니다.
일상 비유: L2TP는 우편 봉투 안에 또 다른 봉투를 넣는 것과 비슷합니다. 내부 봉투(Layer 2 프레임)는 원래의 발신/수신 주소를 갖고 있고, 외부 봉투(IP/UDP 헤더)는 실제 전달 경로를 결정합니다. 이렇게 하면 원래 네트워크가 물리적으로 떨어져 있어도 마치 같은 LAN처럼 통신할 수 있습니다.

핵심 요약

  • L2TP 터널 --- 두 끝점(LAC/LNS) 사이의 제어/데이터 연결 컨테이너(Container)
  • L2TP 세션 --- 터널 내부에서 실제 데이터를 주고받는 논리적 채널
  • L2TPv2 --- PPP 프레임 전용 터널링 (RFC 2661)
  • L2TPv3 --- 이더넷, HDLC 등 범용 Layer 2 터널링 (RFC 3931)
  • Pseudowire --- L2TPv3에서 세션이 전달하는 가상 회선

단계별 이해

  1. 터널 생성
    두 끝점 간 UDP 또는 IP 소켓(Socket)을 열고 L2TP 터널을 수립합니다.
  2. 세션 생성
    터널 안에 하나 이상의 세션을 만들어 실제 데이터 전달 경로를 구성합니다.
  3. 캡슐화(Encapsulation) 및 전송
    원본 Layer 2 프레임을 L2TP 헤더로 감싸서 IP 네트워크를 통해 전송합니다.
  4. 역캡슐화(Decapsulation) 및 전달
    수신 측에서 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특징
1996PPTPRFC 2637Microsoft, GRE 기반 PPP 터널링
1998L2FRFC 2341Cisco, UDP 기반 PPP 터널링
1999L2TPv2RFC 2661PPTP + L2F 통합, PPP 전용
2005L2TPv3RFC 3931범용 Layer 2 터널링, 이더넷/HDLC/FR 지원

L2TPv2와 L2TPv3 비교

속성L2TPv2L2TPv3
표준RFC 2661RFC 3931
전송 프로토콜UDP만 (포트 1701)UDP 또는 IP (프로토콜 115)
페이로드(Payload) 유형PPP 프레임만이더넷, PPP, HDLC, FR 등
터널 ID 크기16비트32비트
세션 ID 크기16비트32비트
쿠키(인증)없음최대 8바이트
주요 용도원격 접속 VPNL2 VPN, 사이트 간 연결, 이더넷 브리징
커널 버전: 리눅스 커널의 L2TP 서브시스템은 2.6.35부터 도입되었으며, 이후 지속적으로 개선되어 현재 커널(6.x)에서는 L2TPv3 이더넷 터널, Netlink 설정 인터페이스, IP 캡슐화 등을 완전히 지원합니다.

L2TP 프로토콜 구조

L2TP는 제어 메시지데이터 메시지의 두 가지 채널을 사용합니다. 제어 채널은 터널과 세션의 수립/해제를 담당하며 신뢰성 있는 전송(순서 번호, 재전송(Retransmission))을 보장합니다. 데이터 채널은 실제 페이로드를 전달하며 비신뢰성(best-effort)으로 동작합니다.

L2TP 프로토콜 스택

L2TP 프로토콜 스택 (L2TPv2 vs L2TPv3) L2TPv2 (RFC 2661) PPP 프레임 (페이로드) L2TPv2 헤더 (16-bit ID) UDP (포트 1701) IP (IPv4/IPv6) 물리 계층 (Ethernet 등) L2TPv3 (RFC 3931) 이더넷/PPP/HDLC/FR (페이로드) L2TPv3 헤더 (32-bit ID + Cookie) UDP (포트 1701) IP 직접 (Proto 115) IP (IPv4/IPv6) 물리 계층 (Ethernet 등) 제어 채널 (신뢰성) 데이터 채널 (비신뢰성)

L2TPv2는 반드시 UDP(포트 1701) 위에서만 동작하지만, L2TPv3는 UDP 외에도 IP 프로토콜 번호 115를 사용하여 직접 IP 위에서 동작할 수 있습니다. IP 직접 캡슐화는 UDP 헤더 오버헤드(Overhead)를 제거하여 대역폭(Bandwidth) 효율이 높지만, 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 비트 우선순위(Priority) 비트 (데이터 메시지 전용)
  • 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 패킷 전체 구조

L2TPv3 over UDP --- 패킷 구조 Ethernet 14B IP 헤더 20B UDP 8B L2TP 헤더 4-16B Cookie 0/4/8B L2-Sub 4B (opt) 원본 L2 프레임 (이더넷/PPP 등) 외부 전송 헤더 L2TP 캡슐화 영역 물리 매체 전송 방향

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 연결을 나타냅니다.

L2TP 터널 / 세션 다중화 구조 LAC (클라이언트) PPP 사용자 A PPP 사용자 B 이더넷 세그먼트 C 이더넷 세그먼트 D 터널 #1 (UDP 1701) 세션 #1 (PPP-A) 세션 #2 (PPP-B) 제어 채널 (Ns/Nr 순서 보장) 터널 #2 (IP Proto 115) 세션 #3 (Eth-C) 세션 #4 (Eth-D) LNS (서버) PPP 인터페이스 A PPP 인터페이스 B l2tpeth0 (Eth-C) l2tpeth1 (Eth-D) 하나의 터널 안에 여러 세션이 다중화되어 독립적인 L2 연결을 제공
LAC와 LNS:
  • 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가 세션 핸들러(Handler)로 동작합니다. l2tp_netlink는 사용자 공간(User Space)과의 Netlink 기반 설정 인터페이스를 담당합니다.

리눅스 커널 L2TP 모듈 아키텍처 사용자 공간 ip l2tp pppd xl2tpd openl2tpd 사용자 앱 Netlink / 소켓 인터페이스 커널 공간 l2tp_netlink l2tp_core (터널/세션 관리) l2tp_ppp (PPP 핸들러) l2tp_eth (이더넷 핸들러) l2tp_ip / l2tp_ip6 UDP / IP 소켓 계층 (네트워크 스택)

커널 소스 파일 구조

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 커널 모듈(Kernel Module) 구조

struct l2tp_tunnel

struct l2tp_tunnel은 L2TP 터널 하나를 표현하는 핵심 구조체(Struct)입니다. 커널 소스(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 터널 내 세션을 빠르게 조회하기 위한 해시 테이블(Hash Table)
  • l2tp_net 네트워크 네임스페이스(Namespace) 참조. 컨테이너 환경 지원

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 커널 자료구조 관계도 struct l2tp_net (per-netns) struct l2tp_tunnel tunnel_id, sock, encap, version struct l2tp_tunnel tunnel_id, sock, encap, version l2tp_session PPP (l2tp_ppp) l2tp_session PPP (l2tp_ppp) l2tp_session Eth (l2tp_eth) tunnel_list tunnel_list session_hlist session_hlist session_hlist

L2TP over UDP 동작 흐름

L2TP over UDP는 가장 일반적인 캡슐화 방식입니다. UDP 포트 1701을 사용하며, NAT 환경에서도 동작할 수 있다는 장점이 있습니다.

L2TP over UDP --- 송신 경로 애플리케이션 데이터 PPP/이더넷 프레임 구성 (l2tp_ppp / l2tp_eth) L2TP 헤더 추가 (l2tp_xmit_skb) UDP 캡슐화 (udp_sendmsg / ip_append_data) IP 라우팅 및 전송 (ip_route_output) NIC 드라이버 (dev_queue_xmit) 수신 경로 NIC -> NAPI ip_rcv -> UDP udp_rcv l2tp_udp_encap_recv l2tp_recv_common 세션 조회 (session_id) session->recv_skb() PPP/Eth 핸들러 핵심 진입점: l2tp_udp_encap_recv() --- UDP 소켓의 encap_rcv 콜백으로 등록됨

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 콜백(Callback)을 호출하도록 함

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 UDPL2TP over IP
오버헤드UDP 8B + L2TP 헤더L2TP 헤더만
NAT 통과가능불가 (프로토콜 115)
방화벽(Firewall) 통과용이 (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 인터페이스 특성

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

리눅스 커널의 L2TP 서브시스템은 Generic Netlink를 통해 사용자 공간에서 터널과 세션을 동적으로 생성/삭제/조회할 수 있는 인터페이스를 제공합니다. iproute2ip 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 자체에는 암호화(Encryption) 기능이 없으므로, 보안이 필요한 환경에서는 반드시 IPsec(특히 ESP 터널/트랜스포트 모드)과 함께 사용해야 합니다. 일반적으로 L2TP/IPsec이라 불리는 조합은 IKE(포트 500/4500)로 SA를 수립한 후, L2TP 트래픽을 ESP로 암호화합니다.

L2TP/IPsec 캡슐화 계층 원본 IP 패킷 (사용자 데이터) PPP 프레임 L2TP 헤더 + UDP (포트 1701) ESP (IPsec 암호화) 외부 IP 헤더 + UDP NAT-T (포트 4500) 암호화 영역

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 연결)

L2TPv3 이더넷 브리징 토폴로지 사이트 A (192.168.1.1) eth0 (물리) l2tpeth0 (가상) br0 (브리지) 10.0.0.0/24 LAN 사이트 B (192.168.2.1) eth0 (물리) l2tpeth0 (가상) br0 (브리지) 10.0.0.0/24 LAN 인터넷 / WAN L2TPv3 이더넷 터널 양쪽 LAN이 동일한 10.0.0.0/24 서브넷을 공유 (Layer 2 확장)
# === 사이트 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 계산

MTU 주의: L2TP 터널 사용 시 오버헤드를 고려하여 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 + 패딩(Padding) + auth 포함)

예제 3: L2TPv3 over IP (Proto 115) 전용 구성

이 예제는 UDP를 사용하지 않고 IP 프로토콜 번호 115로 직접 L2TPv3를 전달합니다. NAT가 없는 전용망/DC 내부 백본에서 오버헤드를 줄이고 지연(Latency) 편차를 최소화할 때 유용합니다. 반대로 NAT/인터넷 경로에서는 권장되지 않습니다.

L2TPv3 over IP(proto 115) 점대점 연결 Site A underlay: 10.10.10.1 tunnel_id 100 / peer 200 session_id 1000 / peer 2000 l2tpeth0 + br0 Site B underlay: 10.10.10.2 tunnel_id 200 / peer 100 session_id 2000 / peer 1000 l2tpeth0 + br0 전용 IP 백본 IP Proto 115 허용 NAT 없음 헤더 오버헤드 감소: UDP 8바이트 제거, 단 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 과대 설정으로 단편화/손실 발생
운영 주의: L2TPv3 over IP는 conntrack/L4 로드밸런서와의 친화도(Affinity)가 낮습니다. 멀티 경로 환경에서는 ECMP 해시(Hash) 편향과 비대칭 라우팅(Routing)을 먼저 점검하고, 필요하면 UDP 캡슐화로 전환하세요.

제어 채널 상태 기계와 재전송

L2TP 데이터 채널은 best-effort로 동작하지만, 제어 채널은 Ns/Nr 순서 번호와 재전송 타이머(Timer)로 신뢰성을 보장합니다. 현장에서 자주 발생하는 문제는 "데이터는 일부 보이는데 세션이 붙지 않음"으로, 대부분 제어 메시지 교환 실패(SCCRQ/SCCRP/ICRQ/ICRP 구간)에서 발생합니다.

L2TPv2 제어 메시지 핸드셰이크

단계메시지역할실패 시 증상
1SCCRQ터널 연결 요청 시작상대가 응답하지 않음
2SCCRP터널 연결 응답 (Tunnel ID 제시)터널 ID 충돌/거부
3SCCCN제어 연결 확정제어 연결 수립 직전 종료
4ICRQ/ICRP/ICCN세션(호출) 생성ppp 인터페이스가 생기지 않음
5SLI/ZLB ACK링크 상태/응답 확인주기적 재전송 증가
종료CDN/StopCCN세션/터널 해제고아 세션 누적
L2TP 제어 채널 상태 흐름 (터널 + 세션 수립) LAC LNS SCCRQ (Ns=0) SCCRP (Nr=1) SCCCN + AVP 협상 완료 ICRQ (세션 요청) ICRP / ICCN 데이터 채널 활성화 PPP/이더넷 프레임이 Session ID 기반으로 다중화 제어 채널은 Keepalive(ZLB ACK)로 생존성 감시

재전송과 타이머 튜닝 포인트

# 운영 관찰 포인트 (도구별)
- 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 UDPNAT 통과 용이, 운영 단순UDP 헤더 오버헤드인터넷, 멀티 NAT
L2TPv3 over IP(115)헤더 오버헤드 최소NAT/방화벽 통과 어려움전용망, 폐쇄망
L2TP/IPsec (UDP 1701 + ESP)기밀성/무결성(Integrity) 확보IKE/정책 운영 복잡성원격 접속 VPN
L2TP/IPsec NAT-T (UDP 4500)NAT 환경에서도 IPsec 가능캡슐화 단계 증가클라우드/재택 환경
NAT 환경의 L2TP/IPsec 패킷 경로 클라이언트 L2TP(UDP 1701) IKE(UDP 500) / NAT-T(4500) NAT/방화벽 포트 변환 및 상태 추적 ESP 직접 통과 실패 가능 VPN 서버 xfrm 정책/SA 처리 L2TP 터널 종단 필수 허용 포트/프로토콜 UDP 500(IKE), UDP 4500(NAT-T), UDP 1701(L2TP) NAT-T 사용 시 ESP는 UDP 4500 내부로 캡슐화되어 전달 방화벽에서 상태 추적 타임아웃이 짧으면 세션이 주기적으로 끊김

운영 방화벽 체크리스트

# 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
설계 주의: NAT 장비의 UDP 세션 타임아웃이 짧은 환경에서 keepalive 주기가 길면 터널이 간헐적으로 끊깁니다. L2TP keepalive, IKE DPD 주기, NAT 장비 타임아웃 값을 함께 맞춰야 안정성이 확보됩니다.

대규모 운영과 고가용성 설계

수백~수천 세션 환경에서는 단일 노드의 CPU, softirq, 메모리 단편화(Fragmentation), conntrack 용량이 병목(Bottleneck)이 됩니다. L2TP는 세션이 많아질수록 제어 채널 안정성과 데이터 경로 분산(IRQ/RPS/XPS)이 성능의 핵심입니다.

규모별 운영 모델

규모권장 구조핵심 튜닝 포인트
소규모 (< 100 세션)단일 LNSMTU/keepalive 정합성
중규모 (100~1000 세션)LNS + 전용 모니터링RPS/XPS, conntrack, IRQ 분산
대규모 (1000+ 세션)다중 LNS 풀 + Anycast/LB세션 스티키 정책, 장애 전이 시간
이중화active-standby 또는 active-active터널 재수립 시간과 라우팅 수렴 시간 분리 측정
L2TP 서비스 풀의 고가용성 구성 예시 클라이언트 그룹 A 원격 지사/재택 클라이언트 그룹 B IoT/브랜치 라우터 L4 LB / Anycast VIP 세션 스티키(5-tuple) 헬스체크: UDP1701 + IKE 응답 장애 시 신규 세션만 우회 LNS-1 active CPU set / IRQ 분리 LNS-2 active 독립 터널 풀 장애 복구 시 주의 기존 세션 마이그레이션은 어려우므로 재수립 시간 단축이 핵심 DPD/LCP echo/재접속 백오프를 함께 조정

커널/시스템 튜닝 예시

# 소켓/큐 용량 (환경별 검증 후 적용)
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/MRUip link show l2tpeth0, ppp 옵션양 끝단 값 일치
재전송 증가xl2tpd 로그, dmesg | grep l2tp지속 증가 없음
NAT 상태conntrack -L주기적 만료/재생성 과다 없음
IPsec SAip xfrm statereplay/error 카운터 안정
CPU softirqmpstat -P ALL 1, top -H특정 코어 과점유 없음

L2TPv3 Pseudowire 유형별 상세와 L3VPN 통합

L2TPv3의 핵심 기능은 pseudowire(PW)를 통해 다양한 Layer 2 프로토콜을 IP 네트워크 위에서 투명하게 전달하는 것입니다. 커널의 l2tp_session 구조체에서 pwtype 필드가 pseudowire 유형을 결정하며, 각 유형에 따라 캡슐화/역캡슐화 동작이 달라집니다.

Pseudowire 유형 분류

PW 유형L2TP_PWTYPE 상수RFC용도커널 모듈
EthernetL2TP_PWTYPE_ETH (0x0005)RFC 4719L2 브리징, VPLSl2tp_eth
Ethernet VLANL2TP_PWTYPE_ETH_VLAN (0x0004)RFC 4719VLAN 태그 보존 전달l2tp_eth
PPPL2TP_PWTYPE_PPP (0x0007)RFC 2661원격 접속 VPNl2tp_ppp
HDLCL2TP_PWTYPE_PPP_AC (0x0001)RFC 4349시리얼 WAN 연결사용자 공간
Frame RelayL2TP_PWTYPE_IP (0x000B)RFC 4591레거시 FR 마이그레이션사용자 공간

Ethernet PW의 내부 동작

Ethernet pseudowire(L2TP_PWTYPE_ETH)는 가장 널리 사용되는 유형으로, 원본 이더넷 프레임 전체(MAC 헤더 포함)를 L2TP 세션 안에 캡슐화합니다. 수신 측에서는 L2TP 헤더를 제거한 후 l2tpethN 가상 인터페이스의 netif_rx()를 통해 커널 네트워크 스택에 전달합니다.

/* l2tp_eth.c: 이더넷 프레임 수신 처리 */
static void l2tp_eth_dev_recv(struct l2tp_session *session,
                              struct sk_buff *skb, int data_len)
{
    struct l2tp_eth_sess *spriv = l2tp_session_priv(session);
    struct net_device *dev;

    /* L2TP 헤더 제거, 이더넷 프레임만 남김 */
    skb_pull(skb, data_len);

    dev = spriv->dev;
    skb->dev = dev;
    skb->protocol = eth_type_trans(skb, dev);

    /* 통계 업데이트 */
    dev_sw_netstats_rx_add(dev, skb->len);

    /* 커널 네트워크 스택으로 전달 (브리지/라우팅 처리) */
    netif_rx(skb);
}

L3VPN 구성 (L2TPv3 + IP 라우팅)

L2TPv3 이더넷 pseudowire를 사용하면 L2 수준의 연결이 가능하지만, 대규모 환경에서는 L3VPN 방식이 더 효율적입니다. l2tpeth 인터페이스에 IP 주소를 직접 할당하고 라우팅으로 트래픽을 제어하면 브로드캐스트 도메인을 분리하면서도 사이트 간 IP 통신이 가능합니다.

# L3VPN 모드: l2tpeth에 직접 IP 주소 할당 (브리지 없이)
ip l2tp add tunnel tunnel_id 300 peer_tunnel_id 400 \
    encap udp local 10.1.1.1 remote 10.1.1.2 \
    udp_sport 5000 udp_dport 5000

ip l2tp add session tunnel_id 300 \
    session_id 3000 peer_session_id 4000

# 브리지 없이 직접 라우팅
ip link set l2tpeth0 up mtu 1446
ip addr add 172.16.0.1/30 dev l2tpeth0

# 원격 사이트의 서브넷으로 정적 라우팅
ip route add 192.168.100.0/24 via 172.16.0.2 dev l2tpeth0
ip route add 192.168.200.0/24 via 172.16.0.2 dev l2tpeth0
L2VPN vs L3VPN 선택 기준:
  • L2VPN (브리지 모드): 동일 서브넷 공유 필요, ARP/DHCP 투명 전달, 소규모 사이트
  • L3VPN (라우팅 모드): 서브넷 분리, 브로드캐스트 격리(Isolation), 대규모/멀티사이트

xfrm/IPsec 연동

L2TP/IPsec 조합에서 커널의 xfrm 프레임워크는 SA(Security Association)와 SP(Security Policy)를 관리합니다. ESP(Encapsulating Security Payload)가 L2TP 트래픽을 암호화하며, NAT 환경에서는 NAT-T(NAT Traversal)를 통해 ESP를 UDP 4500으로 캡슐화합니다.

IPsec + L2TP 패킷 중첩 구조

IPsec + L2TP 패킷 중첩 구조 Transport 모드 (L2TP/IPsec VPN) 원본 IP ESP Header UDP 1701 L2TP Header PPP Frame 내부 IP 페이로드 ESP Trailer ESP 암호화 영역 (UDP + L2TP + PPP + 페이로드) NAT-T 모드 (ESP over UDP 4500) 원본 IP UDP 4500 (NAT-T) ESP Header UDP 1701 L2TP Header PPP + 데이터 ESP Tail Tunnel 모드 (사이트 간 IPsec + L2TPv3) 외부 IP ESP 내부 IP (원본) UDP L2TPv3 이더넷 프레임 ESP Tail 모드 비교 Transport: 원본 IP 헤더 유지, 오버헤드 작음, 원격 접속 VPN 표준 NAT-T: ESP를 UDP 4500으로 감싸 NAT 통과, 가장 범용적 Tunnel: 새 외부 IP 헤더 추가, 사이트 간 VPN, 오버헤드 최대 MTU 영향: Transport ~1380B, NAT-T ~1350B, Tunnel ~1320B (AES-256 기준)

Transport/Tunnel 모드 조합별 SA/SP 설정

# Transport 모드: L2TP 트래픽(UDP 1701)만 ESP로 암호화
ip xfrm state add \
    src 192.168.1.1 dst 203.0.113.1 \
    proto esp spi 0x12345678 reqid 1 \
    mode transport \
    enc 'cbc(aes)' 0x$(openssl rand -hex 32) \
    auth 'hmac(sha256)' 0x$(openssl rand -hex 32)

ip xfrm policy add \
    src 192.168.1.1 dst 203.0.113.1 \
    proto udp sport 1701 \
    dir out \
    tmpl src 192.168.1.1 dst 203.0.113.1 \
    proto esp reqid 1 mode transport

# Tunnel 모드: 사이트 간 전체 L2TP 트래픽 암호화
ip xfrm state add \
    src 10.0.0.1 dst 10.0.0.2 \
    proto esp spi 0xABCD1234 reqid 2 \
    mode tunnel \
    enc 'cbc(aes)' 0x$(openssl rand -hex 32) \
    auth 'hmac(sha256)' 0x$(openssl rand -hex 32)

ip xfrm policy add \
    src 10.0.0.0/24 dst 10.0.1.0/24 \
    dir out \
    tmpl src 10.0.0.1 dst 10.0.0.2 \
    proto esp reqid 2 mode tunnel

NAT-T 자동 감지 흐름

IKE(Internet Key Exchange) 협상 단계에서 NAT 감지(NAT Detection)가 이루어집니다. 양쪽 IKE 피어가 NAT-D(NAT Detection) 페이로드를 교환하여 경로에 NAT가 있는지 확인하고, NAT가 감지되면 자동으로 ESP를 UDP 4500으로 캡슐화합니다. 커널의 xfrm 프레임워크가 encap 속성을 통해 이를 처리합니다.

/* xfrm_state에 NAT-T 캡슐화 설정 */
struct xfrm_encap_tmpl {
    __u16   encap_type;     /* UDP_ENCAP_ESPINUDP */
    __be16  encap_sport;    /* 로컬 포트 (4500) */
    __be16  encap_dport;    /* 원격 포트 (4500) */
    xfrm_address_t encap_oa; /* 원본 주소 (NAT 전) */
};

커널 트레이싱

L2TP 서브시스템의 성능 문제나 패킷 손실을 정밀 분석하려면 커널 수준의 트레이싱이 필수입니다. ftrace tracepoint, perf probe, BPF 프로그램을 활용하여 패킷 경로의 각 단계별 지연과 드롭 지점을 정확히 파악할 수 있습니다.

ftrace L2TP 관련 이벤트

# L2TP 관련 커널 함수 목록 확인
cat /sys/kernel/debug/tracing/available_filter_functions | grep l2tp

# function_graph 트레이서로 L2TP 수신 경로 추적
echo function_graph > /sys/kernel/debug/tracing/current_tracer
echo 'l2tp_udp_encap_recv' > /sys/kernel/debug/tracing/set_graph_function
echo 1 > /sys/kernel/debug/tracing/tracing_on

# 일정 시간 후 트레이스 확인
cat /sys/kernel/debug/tracing/trace

# 종료
echo 0 > /sys/kernel/debug/tracing/tracing_on
echo nop > /sys/kernel/debug/tracing/current_tracer

perf probe를 이용한 동적 트레이싱

# l2tp_recv_common 진입점에 프로브 등록
perf probe -a 'l2tp_recv_common session=%di:x64'

# l2tp_xmit_skb 송신 경로 프로브
perf probe -a 'l2tp_xmit_skb session=%di:x64 skb=%si:x64'

# 프로브 기반 기록 수집
perf record -e 'probe:l2tp_recv_common' \
            -e 'probe:l2tp_xmit_skb' \
            -a -- sleep 30

# 결과 분석
perf script | head -100

# 프로브 제거
perf probe -d 'l2tp_recv_common'
perf probe -d 'l2tp_xmit_skb'

BPF 기반 L2TP 세션별 통계 수집

# bpftrace: L2TP 세션별 수신 패킷 수와 바이트 집계
bpftrace -e '
kprobe:l2tp_recv_common {
    @session_rx[arg0] = count();
}

kprobe:l2tp_xmit_skb {
    @session_tx[arg0] = count();
}

interval:s:10 {
    print(@session_rx);
    print(@session_tx);
    clear(@session_rx);
    clear(@session_tx);
}'

# bpftrace: L2TP 수신 처리 지연 히스토그램
bpftrace -e '
kprobe:l2tp_udp_encap_recv {
    @start[tid] = nsecs;
}
kretprobe:l2tp_udp_encap_recv /@start[tid]/ {
    @latency_us = hist((nsecs - @start[tid]) / 1000);
    delete(@start[tid]);
}'

skb 드롭 추적

# 커널의 kfree_skb tracepoint로 L2TP 관련 드롭 추적
perf record -e skb:kfree_skb -a -- sleep 60
perf script | grep -i l2tp

# 또는 dropwatch 도구 활용
dropwatch -l kas
# 활성화 후 L2TP 터널 트래픽 발생시키면 드롭 위치 표시

L2TP 데이터 경로 최적화

대량의 L2TP 세션을 처리할 때 데이터 경로의 효율성이 전체 성능을 결정합니다. UDP encap 콜백 구조, GRO/GSO 지원 여부, 시퀀스 번호 처리 오버헤드가 핵심 요소입니다.

UDP encap 콜백 아키텍처

L2TP over UDP에서 패킷 수신은 일반 UDP 수신 경로를 통해 진행되다가, udp_queue_rcv_skb() 단계에서 encap_rcv 콜백이 등록되어 있으면 L2TP 핸들러로 분기됩니다. 이 콜백 기반 구조 덕분에 L2TP 트래픽은 일반 UDP 소켓 처리 없이 바로 커널 내부에서 처리됩니다.

/* net/ipv4/udp.c: UDP encap 콜백 호출 경로 */
static int udp_queue_rcv_one_skb(struct sock *sk,
                                  struct sk_buff *skb)
{
    struct udp_sock *up = udp_sk(sk);

    /* encap_rcv 콜백이 등록되어 있으면 L2TP 핸들러 호출 */
    if (up->encap_type && up->encap_rcv) {
        int ret = up->encap_rcv(sk, skb);
        if (ret <= 0)
            return -ret;  /* L2TP가 처리 완료 */
    }
    /* fallback: 일반 UDP 소켓 큐에 전달 */
    ...
}

GRO/GSO 지원 현황

기능L2TP over UDPL2TP over IP비고
GRO (수신 병합)UDP GRO 경유 지원미지원커널 5.0+ UDP GRO
GSO (송신 분할)UDP GSO 경유 가능제한적l2tp_xmit_skb에서 segmentation 처리
TSO터널 내부 미동작미동작NIC HW offload 한계
Checksum offloadUDP csum offload 가능IP csum만ethtool -K로 확인

시퀀스 번호 처리 성능

L2TP 데이터 메시지에서 시퀀스 번호(Ns/Nr)는 선택 사항이지만, 활성화하면 패킷 순서 보장(Ordering)과 중복 감지가 가능합니다. 다만 고속 환경에서는 시퀀스 번호 처리가 성능 오버헤드를 유발할 수 있습니다.

# 시퀀스 번호 비활성화 (성능 우선)
ip l2tp add session tunnel_id 1 \
    session_id 100 peer_session_id 200 \
    seq none

# 시퀀스 번호 수신만 체크 (절충안)
ip l2tp add session tunnel_id 1 \
    session_id 100 peer_session_id 200 \
    seq recv

# 시퀀스 번호 송수신 모두 활성화 (안정성 우선)
ip l2tp add session tunnel_id 1 \
    session_id 100 peer_session_id 200 \
    seq both
성능 튜닝 포인트:
  • 대역폭 중시 환경: seq none + GRO 활성화 + jumbo MTU
  • 안정성 중시 환경: seq both + cookie 활성화 + IPsec
  • 수신 큐 최적화: net.core.netdev_budget=600으로 NAPI 처리량(Throughput) 증가

멀티세션 아키텍처

L2TP의 핵심 설계 원칙 중 하나는 단일 터널 위에 여러 세션을 다중화하는 것입니다. 커널은 세션 ID를 기반으로 한 해시 테이블을 사용하여 수신 패킷을 빠르게 올바른 세션으로 라우팅합니다.

세션 ID 해시 및 조회

/* l2tp_core.h: 세션 해시 테이블 크기 */
#define L2TP_HASH_BITS   4
#define L2TP_HASH_SIZE   (1 << L2TP_HASH_BITS)  /* = 16 버킷 */

/* l2tp_core.c: 세션 조회 (수신 경로에서 호출) */
struct l2tp_session *l2tp_tunnel_get_session(
    struct l2tp_tunnel *tunnel, u32 session_id)
{
    struct hlist_head *session_list;
    struct l2tp_session *session;

    session_list = l2tp_session_id_hash(tunnel, session_id);

    hlist_for_each_entry_rcu(session, session_list, hlist) {
        if (session->session_id == session_id) {
            l2tp_session_inc_refcount(session);
            return session;
        }
    }
    return NULL;
}

/* 전역 세션 해시: 터널 없이 session_id만으로 조회 (L2TPv3) */
struct l2tp_session *l2tp_session_get(
    struct net *net, u32 session_id)
{
    struct l2tp_net *pn = l2tp_pernet(net);
    struct hlist_head *session_list;

    session_list = l2tp_session_id_hash_2(pn, session_id);
    /* RCU 보호 하에 해시 순회 */
    ...
}

세션별 통계 구조

/* l2tp_core.h: 세션/터널 통계 구조체 */
struct l2tp_stats {
    atomic_long_t   tx_packets;
    atomic_long_t   tx_bytes;
    atomic_long_t   tx_errors;
    atomic_long_t   rx_packets;
    atomic_long_t   rx_bytes;
    atomic_long_t   rx_errors;
    atomic_long_t   rx_seq_discards;    /* 순서 번호 불일치 드롭 */
    atomic_long_t   rx_oos_packets;     /* out-of-sequence 패킷 */
    atomic_long_t   rx_cookie_discards; /* 쿠키 불일치 드롭 */
};
# 멀티세션 환경 통계 확인
ip l2tp show session
# 출력 예:
# Session 10 in tunnel 1
#   Peer session 20, peer tunnel 2
#   interface name: l2tpeth0
#   offset 0, peer offset 0
#   Using IP, 4 byte cookies
#   Sequence: send=no recv=no
# Session 11 in tunnel 1
#   Peer session 21, peer tunnel 2
#   interface name: l2tpeth1
#   ...

# 개별 세션 인터페이스 통계
for dev in l2tpeth0 l2tpeth1 l2tpeth2; do
    echo "=== $dev ==="
    ip -s link show $dev
done

L2TP + Bridge/VLAN 연동

l2tpeth 인터페이스는 일반 이더넷 디바이스와 동일한 인터페이스를 제공하므로, 리눅스 브리지의 포트로 추가하거나 802.1Q VLAN 하위 인터페이스를 생성할 수 있습니다. 이를 통해 L2TP 터널 위에 복잡한 Layer 2 토폴로지(Topology)를 구성할 수 있습니다.

L2TP + Bridge/VLAN 연동 토폴로지 사이트 A eth0 (WAN) l2tpeth0 l2tpeth1 l2tpeth0.100 l2tpeth0.200 eth1 (LAN) br0 (VLAN-aware Bridge) VLAN 100: 사무실 VLAN 200: 서버실 10.100.0.0/24 10.200.0.0/24 사이트 B (원격) l2tpeth0 eth1 (LAN) br0 (VLAN-aware Bridge) 동일 VLAN 100/200 L2TP 터널 VLAN 태그가 L2TP 터널을 통해 투명하게 전달됨 양쪽 br0의 VLAN 필터링이 동일하게 설정되어야 함

VLAN-aware 브리지 + L2TP 구성

# VLAN-aware 브리지 생성
ip link add br0 type bridge vlan_filtering 1
ip link set br0 up

# L2TP 이더넷 인터페이스를 브리지에 추가
ip link set l2tpeth0 master br0
ip link set eth1 master br0

# VLAN 설정 (포트별 VLAN 할당)
bridge vlan add vid 100 dev l2tpeth0
bridge vlan add vid 200 dev l2tpeth0
bridge vlan add vid 100 dev eth1
bridge vlan add vid 200 dev eth1

# PVID(기본 VLAN) 설정
bridge vlan add vid 100 dev l2tpeth0 pvid untagged
bridge vlan add vid 100 dev eth1 pvid untagged

# VLAN 하위 인터페이스 생성 (관리 IP용)
ip link add link br0 name br0.100 type vlan id 100
ip addr add 10.100.0.1/24 dev br0.100
ip link set br0.100 up

ip link add link br0 name br0.200 type vlan id 200
ip addr add 10.200.0.1/24 dev br0.200
ip link set br0.200 up

# 확인
bridge vlan show
bridge fdb show br br0

STP(Spanning Tree) 고려사항

STP 주의: L2TP 브리지 환경에서 STP를 활성화하면 터널 양쪽에서 루프 감지가 작동하여 포트가 차단될 수 있습니다. 단일 L2TP 링크에서는 STP를 비활성화하거나, 복수 경로가 있는 경우 RSTP를 사용하고 L2TP 포트의 비용을 물리 포트보다 높게 설정하세요.
# STP 비활성화 (단일 링크)
ip link set br0 type bridge stp_state 0

# 또는 RSTP에서 L2TP 포트 비용 조정
ip link set l2tpeth0 type bridge_slave cost 1000

ISP/통신사 대규모 배포 아키텍처

ISP와 통신사에서 L2TP는 가입자 접속(Subscriber Access)의 핵심 프로토콜입니다. LAC(L2TP Access Concentrator)가 가입자의 PPP 세션을 수집하고, LNS(L2TP Network Server)에서 인증/정책 적용 후 인터넷이나 VPN으로 라우팅합니다. 대규모 환경에서는 RADIUS 연동과 BNG(Broadband Network Gateway) 아키텍처가 필수입니다.

ISP LAC/LNS 대규모 배포 아키텍처 가입자 CPE 가입자 CPE 가입자 CPE PPPoE / PPP DSLAM / OLT L2 집선 LAC (BNG / BRAS) PPP 세션 종단 L2TP 터널 생성 RADIUS 인증 트리거 QoS 정책 적용 IP 백본 LNS 풀 LNS-1 (active) LNS-2 (active) LNS-3 (standby) RADIUS 서버 인증 (Authentication) 과금 (Accounting) 인터넷 / VPN L2TP 터널 1) CPE에서 PPPoE 세션 시작 2) LAC가 RADIUS 조회 후 L2TP 터널 결정 3) LNS에서 PPP 인증/IP 할당 4) RADIUS accounting 시작 5) 인터넷 라우팅

RADIUS 연동 흐름

대규모 ISP 환경에서 LAC는 가입자의 PPP 인증 요청을 RADIUS 서버로 전달합니다. RADIUS 응답에 Tunnel-Type, Tunnel-Medium-Type, Tunnel-Server-Endpoint 등의 속성이 포함되면 LAC가 자동으로 해당 LNS에 L2TP 터널을 수립합니다.

주요 RADIUS 터널 속성

RADIUS 속성설명
Tunnel-Type (64)3 (L2TP)터널 프로토콜 유형
Tunnel-Medium-Type (65)1 (IPv4)터널 전송 매체
Tunnel-Server-Endpoint (67)LNS IP 주소터널 종단점
Tunnel-Password (69)공유 비밀터널 인증용 비밀
Tunnel-Assignment-Id (82)그룹 이름터널 그룹 지정
Tunnel-Preference (83)우선순위다중 LNS 선택 순서
# FreeRADIUS users 파일 예시
subscriber1  Cleartext-Password := "pass123"
    Tunnel-Type = L2TP,
    Tunnel-Medium-Type = IPv4,
    Tunnel-Server-Endpoint = "10.0.0.100",
    Tunnel-Password = "l2tp-secret",
    Tunnel-Assignment-Id = "corp-vpn",
    Framed-IP-Address = 172.16.10.1,
    Framed-IP-Netmask = 255.255.255.0

# 복수 LNS 로드밸런싱 (Tunnel-Preference 사용)
subscriber2  Cleartext-Password := "pass456"
    Tunnel-Type:0 = L2TP,
    Tunnel-Server-Endpoint:0 = "10.0.0.100",
    Tunnel-Preference:0 = 1,
    Tunnel-Type:1 = L2TP,
    Tunnel-Server-Endpoint:1 = "10.0.0.101",
    Tunnel-Preference:1 = 2

L2TPv2 vs L2TPv3 비교

L2TPv2와 L2TPv3는 같은 L2TP 계열이지만 설계 철학과 적용 범위가 크게 다릅니다. L2TPv2는 PPP 원격 접속에 특화되어 있고, L2TPv3는 범용 Layer 2 터널링을 위해 프로토콜 구조를 근본적으로 재설계했습니다.

L2TPv2 vs L2TPv3 프로토콜 스택 비교 L2TPv2 (RFC 2661) PPP 프레임 전용 L2TPv2 헤더 (16-bit TID/SID) UDP 포트 1701 (필수) IP (IPv4) 주요 특성 - 터널/세션 ID: 16비트 (최대 65535) - 인증 쿠키: 없음 - 전송: UDP만 지원 - 용도: 원격 접속 VPN (PPP) - 역할: LAC (클라이언트) / LNS (서버) - 제어: 인밴드 (같은 UDP 소켓) - 커널: l2tp_ppp + pppd - NAT 통과: 가능 (UDP 기반) L2TPv3 (RFC 3931) Ethernet / PPP / HDLC / FR L2TPv3 헤더 (32-bit SID + Cookie) UDP 1701 IP Proto 115 IP (IPv4 / IPv6) 주요 특성 - 터널/세션 ID: 32비트 (최대 2^32) - 인증 쿠키: 최대 8바이트 - 전송: UDP 또는 IP 직접 - 용도: L2 VPN, 이더넷 브리징, PW - 역할: LCCE (대칭, 피어 간 동등) - 제어: 분리 가능 (데이터만 커널) - 커널: l2tp_eth / l2tp_ip / l2tp_ip6 - NAT 통과: UDP시 가능, IP시 불가

프로토콜 차이 상세

구분L2TPv2L2TPv3
헤더 크기6-20 바이트 (가변)4-16 바이트 (가변, 쿠키 포함)
제어 채널 분리동일 UDP 소켓에서 T비트로 구분별도 제어 연결 가능
IPv6 지원미지원완전 지원 (l2tp_ip6)
다중 pseudowirePPP만Ethernet, VLAN, PPP, HDLC, FR 등
터널 인증CHAP 기반 (선택)쿠키 + IPsec 권장
최대 세션 수65535 (16비트)약 43억 (32비트)
데이터 경로 효율PPP 프레임 오버헤드이더넷 직접 전달
대규모 배포ISP 원격 접속 (감소 추세)DC 간 L2VPN (증가 추세)

마이그레이션 가이드: L2TPv2에서 L2TPv3로

마이그레이션 체크리스트:
  1. 기존 PPP 기반 세션이 이더넷 모드로 전환 가능한지 평가
  2. 양쪽 장비가 L2TPv3를 지원하는지 확인 (CONFIG_L2TP_V3=y)
  3. 터널 ID를 16비트에서 32비트로 확장 (기존 ID 유지 가능)
  4. 쿠키 인증 설정 추가 (보안 강화)
  5. IP 캡슐화 전환 시 방화벽에서 프로토콜 115 허용 확인
  6. 브리지 모드 전환 시 STP/VLAN 구성 계획 수립
  7. RADIUS 속성을 L2TPv3 호환으로 갱신

커널 소스 구조 상세

net/l2tp/ 디렉토리의 각 파일이 담당하는 역할과 주요 함수 호출 경로를 상세히 살펴봅니다. 소스 분석 시 이 구조를 먼저 파악하면 디버깅과 패치(Patch) 작성이 수월해집니다.

파일별 상세 역할

파일줄 수 (6.x 기준)핵심 역할주요 함수
l2tp_core.c~1800 터널/세션 생성/삭제, 캡슐화/역캡슐화, 통계 l2tp_tunnel_create(), l2tp_session_create(), l2tp_recv_common(), l2tp_xmit_skb()
l2tp_core.h~250 구조체 선언 (l2tp_tunnel, l2tp_session, l2tp_stats) 매크로(Macro), 인라인 헬퍼
l2tp_netlink.c~900 Generic Netlink 핸들러 (ip l2tp 명령 처리) l2tp_nl_cmd_tunnel_create(), l2tp_nl_cmd_session_create()
l2tp_ppp.c~1400 PPPoL2TP 소켓, PPP 프레임 송수신 pppol2tp_connect(), pppol2tp_sendmsg(), pppol2tp_recv()
l2tp_eth.c~400 L2TPv3 이더넷 가상 디바이스 (l2tpethN) l2tp_eth_create(), l2tp_eth_dev_recv(), l2tp_eth_dev_xmit()
l2tp_ip.c~700 L2TPv3 over IPv4 (프로토콜 115) l2tp_ip_recv(), l2tp_ip_sendmsg()
l2tp_ip6.c~750 L2TPv3 over IPv6 l2tp_ip6_recv(), l2tp_ip6_sendmsg()
l2tp_debugfs.c~300 debugfs 진단 인터페이스 (/sys/kernel/debug/l2tp/) l2tp_dfs_seq_show()

주요 함수 호출 경로

/* === 수신 경로 (L2TP over UDP) === */
NIC IRQ -> napi_poll()
  -> ip_rcv() -> ip_local_deliver()
    -> udp_rcv() -> udp_queue_rcv_skb()
      -> udp_queue_rcv_one_skb()
        -> l2tp_udp_encap_recv()          /* l2tp_core.c */
          -> l2tp_recv_common()            /* 헤더 파싱, 세션 조회 */
            -> l2tp_session_get()         /* 해시 테이블에서 세션 찾기 */
            -> session->recv_skb()        /* 콜백 호출 */
              -> l2tp_eth_dev_recv()      /* 이더넷 세션 */
                -> netif_rx()             /* 커널 네트워크 스택 전달 */
              -> pppol2tp_recv()          /* PPP 세션 */
                -> ppp_input()            /* PPP 프레임 처리 */

/* === 송신 경로 === */
dev_queue_xmit(l2tpethN)
  -> l2tp_eth_dev_xmit()               /* l2tp_eth.c */
    -> l2tp_xmit_skb()                  /* l2tp_core.c: L2TP 헤더 추가 */
      -> l2tp_xmit_core()               /* 쿠키/시퀀스 번호 추가 */
      -> ip_queue_xmit() / udp_sendmsg() /* IP/UDP 전송 */

/* === Netlink 설정 경로 === */
사용자: ip l2tp add tunnel ...
  -> genl_rcv_msg()
    -> l2tp_nl_cmd_tunnel_create()     /* l2tp_netlink.c */
      -> l2tp_tunnel_create()            /* l2tp_core.c */
      -> l2tp_tunnel_register()          /* 소켓 바인딩, 전역 등록 */

사용자: ip l2tp add session ...
  -> genl_rcv_msg()
    -> l2tp_nl_cmd_session_create()    /* l2tp_netlink.c */
      -> l2tp_session_create()           /* l2tp_core.c */
      -> l2tp_eth_create() 또는          /* l2tp_eth.c (이더넷) */
         pppol2tp_session_create()       /* l2tp_ppp.c (PPP) */

RCU 보호 패턴

L2TP 서브시스템은 세션 조회 경로에서 rcu_read_lock()을 사용하여 락 경합(Contention) 없이 고속 패킷 처리를 달성합니다. 세션/터널 삭제 시에는 synchronize_rcu()를 통해 안전한 해제를 보장합니다.

/* 수신 경로에서의 RCU 보호 */
rcu_read_lock();
session = l2tp_tunnel_get_session(tunnel, session_id);
if (session) {
    session->recv_skb(session, skb, data_len);
    l2tp_session_dec_refcount(session);
}
rcu_read_unlock();

/* 세션 삭제 시 안전한 해제 */
hlist_del_init_rcu(&session->hlist);
synchronize_rcu();
l2tp_session_free(session);

네트워크 네임스페이스와 컨테이너 환경

L2TP 서브시스템은 네트워크 네임스페이스를 완전히 지원합니다. 각 네임스페이스는 독립적인 struct l2tp_net을 가지며, 터널/세션이 네임스페이스별로 격리됩니다. 이는 컨테이너 환경이나 멀티테넌트 VPN 서비스에서 필수적인 기능입니다.

# 네트워크 네임스페이스에서 L2TP 터널 생성
ip netns add tenant1
ip netns add tenant2

# veth 쌍으로 네임스페이스 연결
ip link add veth-t1 type veth peer name veth-t1-ns
ip link set veth-t1-ns netns tenant1

# 네임스페이스 내부에서 L2TP 구성
ip netns exec tenant1 ip l2tp add tunnel \
    tunnel_id 1 peer_tunnel_id 2 \
    encap udp local 10.0.1.1 remote 10.0.1.2 \
    udp_sport 5000 udp_dport 5000

ip netns exec tenant1 ip l2tp add session \
    tunnel_id 1 session_id 100 peer_session_id 200

# 격리 확인: 다른 네임스페이스에서는 보이지 않음
ip netns exec tenant2 ip l2tp show tunnel
# (출력 없음 - 터널이 tenant1에 격리됨)
/* l2tp_core.c: per-netns L2TP 데이터 */
struct l2tp_net {
    struct list_head  l2tp_tunnel_list;
                                    /* 이 네임스페이스의 터널 목록 */
    struct hlist_head l2tp_session_hlist[L2TP_HASH_SIZE];
                                    /* 전역 세션 해시 */
    spinlock_t        l2tp_tunnel_list_lock;
    struct dentry     *l2tp_dentry;
                                    /* debugfs 루트 엔트리 */
};

/* 네트워크 네임스페이스 초기화/정리 */
static struct pernet_operations l2tp_net_ops = {
    .init = l2tp_init_net,
    .exit = l2tp_exit_net,
    .id   = &l2tp_net_id,
    .size = sizeof(struct l2tp_net),
};

참고자료

다음 학습: