この記事はLinuxで作るL2TP/WireGuardネットワークの一部です。

2つの拠点間で同一セグメントのL2ネットワークを構築したい時、一般的にはxl2tpdによるL2TPなどを用いることが多いようですが、IPSec抜きでも少々面倒です。 他にもっと楽なものがないか探してみたところ、Linuxカーネルの機能にL2TPが含まれており、 xl2tpd無しでもL2TPv3のネットワークが構築できるとの情報を見つけました。今回はこれを試してみます。

事前に用意するもの

  • てきとうなLinuxサーバ4台
    • L2TPを話すサーバ2台(vpn01, vpn02)
    • L2ネットワーク共有確認用サーバ2台(member01, member02)
    • 今回はubuntu 22.04(5.15.0-30-generic)とAlpine Linux(5.15.40-0-virt)で確認
    • vpn01とvpn02は相互に通信できる状態としておく
  • iproute2
  • 自由に使えるプライベートIPアドレス範囲
    • WAN側(実験用): 192.168.122.0/24
    • L2共有側: 10.0.2.0/24

ざっくりとした手順

  • 通常の手順でネットワーク設定を行い、vpn01とvpn02との間で相互に疎通できるようにする
  • L2TPのトンネルを相互に設定する
  • ブリッジを設定する
  • member01とmember02を設定(通常の手順でネットワーク設定するだけ)

ネットワーク構成

  • Linuxサーバ(vpn01)
    • eth0(WAN側): 192.168.122.24/24
    • eth1(L2共有ネットワーク側): 10.0.2.11/24
  • Linuxサーバ(vpn02)
    • eth0(WAN側): 192.168.122.32/24
    • eth1(L2共有ネットワーク側): 10.0.2.21/24

L2TP ネットワーク図

カーネルモジュールのロード

双方のLinuxサーバで、l2tp_ethモジュールをロードしておきます。

1
% sudo modprobe l2tp_eth

vpn01側の設定

L2TPの設定

L2TPの設定には、下記の情報が必要です。

  • ローカルトンネルID: 3939
  • リモートトンネルID: 3940
  • ローカルセッションID: 3941
  • リモートセッションID: 3942
  • ローカルIPアドレス: 192.168.122.24
  • リモートIPアドレス: 192.168.122.32
  • ローカルポート: 1701
  • リモートポート: 1701

トンネルIDとセッションIDは、0以外の32bitの値ならなんでもよいですが、 ローカルとリモートのIDがお互いのものを指すようにする必要があります。

これらをip l2tp add tunnelip l2tp add sessionで設定します。

1
2
3
4
5
6
7
8
vpn01% sudo ip l2tp add tunnel \
        tunnel_id 3939 \
        peer_tunnel_id 3940 \
        encap udp \
        local 192.168.122.24 \
        remote 192.168.122.32 \
        udp_sport 1701 \
        udp_dport 1701
1
2
3
4
vpn01% sudo ip l2tp add session \
        tunnel_id 3939 \
        session_id 3941 \
        peer_session_id 3942

トンネル設定を確認する際はshow tunnelです。

1
2
3
4
5
6
vpn01% sudo ip l2tp show tunnel
Tunnel 3939, encap UDP
  From 192.168.122.24 to 192.168.122.32
  Peer tunnel 3940
  UDP source / dest ports: 1701/1701
  UDP checksum: disabled

セッション設定を確認する際はshow sessionです。

1
2
3
4
5
vpn01% sudo ip l2tp show session
Session 3941 in tunnel 3939
  Peer session 3942, tunnel 3940
  interface name: l2tpeth0
  offset 0, peer offset 0

うまく設定できれば、l2tpeth0デバイスができているはずです。

1
2
3
4
5
6
vpn01% ip link show dev l2tpeth0
4: l2tpeth0: <BROADCAST,MULTICAST> mtu 1446 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 6a:02:f6:4d:d3:f0 brd ff:ff:ff:ff:ff:ff
vpn01% ip addr show dev l2tpeth0
4: l2tpeth0: <BROADCAST,MULTICAST> mtu 1446 qdisc noop state DOWN group default qlen 1000
    link/ether 6a:02:f6:4d:d3:f0 brd ff:ff:ff:ff:ff:ff

l2tpeth0ができていることを確認したら、デバイスをupしmtuを設定します。 mtuの値はネットワーク環境によって異なりますので、適宜設定してください。

1
2
3
4
vpn01% sudo ip link set dev l2tpeth0 up mtu 1446
vpn01% ip link show dev l2tpeth0
4: l2tpeth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1446 qdisc pfifo_fast state UNKNOWN mode DEFAULT group default qlen 1000
    link/ether 6a:02:f6:4d:d3:f0 brd ff:ff:ff:ff:ff:ff

ブリッジインタフェースの設定

l2tpeth0のL2ネットワークをL2共有ネットワーク側インタフェース(eth1)と共有するためのブリッジ(br-l2tp0)を作成し、 共有L2ネットワークにおけるvpn01のIPアドレス(10.0.2.11/24)を割り当てます。

1
2
3
vpn01% sudo ip link add br-l2tp0 type bridge
vpn01% sudo ip addr add 10.0.2.11/24 dev br-l2tp0
vpn01% sudo ip link set dev br-l2tp0 up

作成したブリッジインタフェースにl2tpeth0eth1を追加します。

1
2
3
vpn01% sudo ip link set dev l2tpeth0 master br-l2tp0
vpn01% sudo ip link set dev eth1 master br-l2tp0
vpn01% sudo ip link set dev eth1 up

ブリッジインタフェースの確認はbridge link showです。

1
2
3
vpn01% sudo bridge link show
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master br-l2tp0 state forwarding priority 32 cost 100 
4: l2tpeth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1446 master br-l2tp0 state forwarding priority 32 cost 100

これでvpn01側の設定は完了です。

ここまでの作業をまとめると、下記のようになります。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/bin/sh

# L2TP
ip l2tp add tunnel \
        tunnel_id 3939 \
        peer_tunnel_id 3940 \
        encap udp \
        local 192.168.122.24 \
        remote 192.168.122.32 \
        udp_sport 1701 \
        udp_dport 1701
ip l2tp add session \
        tunnel_id 3939 \
        session_id 3941 \
        peer_session_id 3942
ip link set dev l2tpeth0 up mtu 1446

# Bridge
ip link add br-l2tp0 type bridge
ip addr add 10.0.2.11/24 dev br-l2tp0
ip link set dev br-l2tp0 up
ip link set dev l2tpeth0 master br-l2tp0
ip link set dev eth1 master br-l2tp0
ip link set dev eth1 up

L2TP設定(vpn02側)

vpn02側の設定はvpn01側と同様で、アドレスやIDが入れ替わるだけです。

  • ローカルトンネルID: 3940
  • リモートトンネルID: 3939
  • ローカルセッションID: 3942
  • リモートセッションID: 3941
  • ローカルIPアドレス: 192.168.122.32
  • リモートIPアドレス: 192.168.122.24
  • ローカルポート: 1701
  • リモートポート: 1701
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/bin/sh

# L2TP
ip l2tp add tunnel \
        tunnel_id 3940 \
        peer_tunnel_id 3939 \
        encap udp \
        local 192.168.122.32 \
        remote 192.168.122.24 \
        udp_sport 1701 \
        udp_dport 1701
ip l2tp add session \
        tunnel_id 3940 \
        session_id 3942 \
        peer_session_id 3941
ip link set dev l2tpeth0 up mtu 1446

# Bridge
ip link add br-l2tp0 type bridge
ip addr add 10.0.2.21/24 dev br-l2tp0
ip link set dev br-l2tp0 up
ip link set dev l2tpeth0 master br-l2tp0
ip link set dev eth1 master br-l2tp0
ip link set dev eth1 up

vpn01-vpn02間の疎通確認

インタフェース設定やルーティングテーブルを確認しつつ、てきとうにpingなどで疎通確認します。

vpn01側

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
vpn01% ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 52:54:00:ad:86:73 brd ff:ff:ff:ff:ff:ff
    inet 192.168.122.24/24 brd 192.168.122.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::5054:ff:fead:8673/64 scope link 
       valid_lft forever preferred_lft forever
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master br-l2tp0 state UP group default qlen 1000
    link/ether 52:54:00:54:ac:58 brd ff:ff:ff:ff:ff:ff
    inet6 fe80::5054:ff:fe54:ac58/64 scope link 
       valid_lft forever preferred_lft forever
4: l2tpeth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1446 qdisc pfifo_fast master br-l2tp0 state UNKNOWN group default qlen 1000
    link/ether 52:b1:7b:58:22:9c brd ff:ff:ff:ff:ff:ff
    inet6 fe80::50b1:7bff:fe58:229c/64 scope link 
       valid_lft forever preferred_lft forever
5: br-l2tp0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1446 qdisc noqueue state UP group default qlen 1000
    link/ether 52:54:00:54:ac:58 brd ff:ff:ff:ff:ff:ff
    inet 10.0.2.11/24 scope global br-l2tp0
       valid_lft forever preferred_lft forever
    inet6 fe80::e862:2eff:fe1d:568b/64 scope link 
       valid_lft forever preferred_lft forever
1
2
3
4
vpn01% ip r
default via 192.168.122.1 dev eth0 metric 202 
10.0.2.0/24 dev br-l2tp0 proto kernel scope link src 10.0.2.11 
192.168.122.0/24 dev eth0 proto kernel scope link src 192.168.122.24
1
2
3
4
5
6
7
8
9
10
11
vpn01% ping -c 5 10.0.2.21
PING 10.0.2.21 (10.0.2.21): 56 data bytes
64 bytes from 10.0.2.21: seq=0 ttl=64 time=1.024 ms
64 bytes from 10.0.2.21: seq=1 ttl=64 time=0.999 ms
64 bytes from 10.0.2.21: seq=2 ttl=64 time=1.046 ms
64 bytes from 10.0.2.21: seq=3 ttl=64 time=0.631 ms
64 bytes from 10.0.2.21: seq=4 ttl=64 time=0.965 ms

--- 10.0.2.21 ping statistics ---
5 packets transmitted, 5 packets received, 0% packet loss
round-trip min/avg/max = 0.631/0.933/1.046 ms

vpn02側

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
vpn02% ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 52:54:00:e3:36:13 brd ff:ff:ff:ff:ff:ff
    inet 192.168.122.32/24 brd 192.168.122.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::5054:ff:fee3:3613/64 scope link 
       valid_lft forever preferred_lft forever
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master br-l2tp0 state UP group default qlen 1000
    link/ether 52:54:00:fe:ad:9d brd ff:ff:ff:ff:ff:ff
    inet6 fe80::5054:ff:fefe:ad9d/64 scope link 
       valid_lft forever preferred_lft forever
4: l2tpeth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1446 qdisc pfifo_fast master br-l2tp0 state UNKNOWN group default qlen 1000
    link/ether 4a:4f:41:92:a9:b1 brd ff:ff:ff:ff:ff:ff
    inet6 fe80::484f:41ff:fe92:a9b1/64 scope link 
       valid_lft forever preferred_lft forever
5: br-l2tp0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1446 qdisc noqueue state UP group default qlen 1000
    link/ether 4a:4f:41:92:a9:b1 brd ff:ff:ff:ff:ff:ff
    inet 10.0.2.21/24 scope global br-l2tp0
       valid_lft forever preferred_lft forever
    inet6 fe80::9822:3ff:fea5:7bd1/64 scope link 
       valid_lft forever preferred_lft forever
1
2
3
4
vpn02% ip r
default via 192.168.122.1 dev eth0 metric 202 
10.0.2.0/24 dev br-l2tp0 proto kernel scope link src 10.0.2.21 
192.168.122.0/24 dev eth0 proto kernel scope link src 192.168.122.32
1
2
3
4
5
6
7
8
9
10
11
vpn02% ping -c 5 10.0.2.11
PING 10.0.2.11 (10.0.2.11): 56 data bytes
64 bytes from 10.0.2.11: seq=0 ttl=64 time=0.790 ms
64 bytes from 10.0.2.11: seq=1 ttl=64 time=0.987 ms
64 bytes from 10.0.2.11: seq=2 ttl=64 time=0.922 ms
64 bytes from 10.0.2.11: seq=3 ttl=64 time=0.910 ms
64 bytes from 10.0.2.11: seq=4 ttl=64 time=1.176 ms

--- 10.0.2.11 ping statistics ---
5 packets transmitted, 5 packets received, 0% packet loss
round-trip min/avg/max = 0.790/0.957/1.176 ms

member01の設定

member01のeth0は、vpn01のeth1と同じL2セグメントに接続し、 MTUをL2TPインタフェースのものと同じにした上で、IPアドレスを設定します。

1
2
3
member01% sudo ip addr add 10.0.2.12/24 dev eth0
member01% sudo ip link set mtu 1446
member01% sudo ip link set dev eth0 up

member02の設定

member02はmember01と同様です。

1
2
3
member02% sudo ip addr add 10.0.2.22/24 dev eth0
member01% sudo ip link set mtu 1446
member02% sudo ip link set dev eth0 up

member01-member02間の疎通確認

最後にmember01-member02間で疎通確認を行います。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
member01% ping -c 3 10.0.2.11
PING 10.0.2.11 (10.0.2.11): 56 data bytes
64 bytes from 10.0.2.11: seq=0 ttl=64 time=1.014 ms
64 bytes from 10.0.2.11: seq=1 ttl=64 time=0.906 ms
64 bytes from 10.0.2.11: seq=2 ttl=64 time=0.853 ms

--- 10.0.2.11 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.853/0.924/1.014 ms

member01% ping -c 3 10.0.2.21
PING 10.0.2.21 (10.0.2.21): 56 data bytes
64 bytes from 10.0.2.21: seq=0 ttl=64 time=1.701 ms
64 bytes from 10.0.2.21: seq=1 ttl=64 time=1.363 ms
64 bytes from 10.0.2.21: seq=2 ttl=64 time=1.575 ms

--- 10.0.2.21 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 1.363/1.546/1.701 ms

member01% ping -c 3 10.0.2.22
PING 10.0.2.22 (10.0.2.22): 56 data bytes
64 bytes from 10.0.2.22: seq=0 ttl=64 time=1.911 ms
64 bytes from 10.0.2.22: seq=1 ttl=64 time=2.362 ms
64 bytes from 10.0.2.22: seq=2 ttl=64 time=2.086 ms

--- 10.0.2.22 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 1.911/2.119/2.362 ms
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
member02% ping -c 3 10.0.2.21
PING 10.0.2.21 (10.0.2.21): 56 data bytes
64 bytes from 10.0.2.21: seq=0 ttl=64 time=1.026 ms
64 bytes from 10.0.2.21: seq=1 ttl=64 time=0.983 ms
64 bytes from 10.0.2.21: seq=2 ttl=64 time=1.294 ms

--- 10.0.2.21 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.983/1.101/1.294 ms

member02% ping -c 3 10.0.2.11
PING 10.0.2.11 (10.0.2.11): 56 data bytes
64 bytes from 10.0.2.11: seq=0 ttl=64 time=1.674 ms
64 bytes from 10.0.2.11: seq=1 ttl=64 time=1.577 ms
64 bytes from 10.0.2.11: seq=2 ttl=64 time=1.798 ms

--- 10.0.2.11 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 1.577/1.683/1.798 ms

member02% ping -c 3 10.0.2.12
PING 10.0.2.12 (10.0.2.12): 56 data bytes
64 bytes from 10.0.2.12: seq=0 ttl=64 time=2.497 ms
64 bytes from 10.0.2.12: seq=1 ttl=64 time=2.042 ms
64 bytes from 10.0.2.12: seq=2 ttl=64 time=2.601 ms

--- 10.0.2.12 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 2.042/2.380/2.601 ms

ip neighで確認してみると、問題無くL2で到達できていることが確認できます。

1
2
3
4
5
6
vpn01% ip neigh
...
10.0.2.12 dev br-l2tp0 lladdr 52:54:00:b2:c6:86 REACHABLE
10.0.2.22 dev br-l2tp0 lladdr 52:54:00:4c:22:9d REACHABLE
10.0.2.21 dev br-l2tp0 lladdr 4a:4f:41:92:a9:b1 REACHABLE
...
1
2
3
4
5
6
vpn02% ip neigh
...
10.0.2.22 dev br-l2tp0 lladdr 52:54:00:4c:22:9d REACHABLE
10.0.2.12 dev br-l2tp0 lladdr 52:54:00:b2:c6:86 REACHABLE
10.0.2.11 dev br-l2tp0 lladdr 52:54:00:54:ac:58 REACHABLE
...

tcpdumpでも確認してみます。

1
2
3
4
5
6
7
8
9
10
11
vpn01% sudo tcpdump -i br-l2tp0 arp
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on br-l2tp0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
20:24:33.785791 ARP, Request who-has 10.0.2.22 tell 10.0.2.12, length 28
20:24:33.786446 ARP, Reply 10.0.2.22 is-at 52:54:00:4c:22:9d (oui Unknown), length 28
20:24:39.785816 ARP, Request who-has 10.0.2.12 tell 10.0.2.22, length 28
20:24:39.786113 ARP, Reply 10.0.2.12 is-at 52:54:00:b2:c6:86 (oui Unknown), length 28
^C
4 packets captured
4 packets received by filter
0 packets dropped by kernel

当然ながらL2TPによる通信は暗号化されていないため、容易に盗聴可能です。 そのため、なんらかの手段で経路をセキュアにする必要があります。

MTUサイズの計算

WireSharkを眺めた感じでは 1500 - 20(IPv4) - 8(UDP) - 8(L2TPv3) - 4(HDLC) - 14(L2TPv3上のEthernet) = 1446 となる模様です。 フレッツ光PPPoEなどのMTUは1454との事なので、ここから46が引かれ1400となる…かもしれません(要検証)。

ハマりポイント

  • L2TP通信のローカルIPアドレスがNATなどで書き換えられると通信できなくなる
    • tcpdumpするとudp port l2f unreachable

参考文献