Linux ルータで OCN 光の IPoE + MAP-E 接続を行う
目次
Linux マシンをルータとして自宅の OCN 光の IPoE + MAP-E 接続を行うようにしたので、その過程を記録します。
利用したマシン
ルータ用のマシンとして MINISFORUM GK41 を使いました。 ギガビットの有線ポートが2つあり、マシン自体が中古で安かったので選びました。
OS は慣れている Ubuntu Server 24.04 にしました。
ネットワーク構成
大雑把な図ですが、次のようなネットワーク構成にしました。
OCN 光の IPv6 IPoE と IPv4 MAP-E を利用しました。 OCN 光は1契約で IPoE だけでなく PPPoE も利用できますが、今回は利用していません。
ネットワーク設計
ネットワーク設計はパズルのように決まっていきます。
ネットワークの分離について
ひかり電話は未契約のため、フレッツ網側からは Router Advertisement で /64 のプレフィックスが配られてきます。 VDSL モデムの下にL2スイッチを置いてPCなどの機器を繋げば、それだけで IPv6 インターネットにアクセスできるようになります。 しかし、それだとファイアウォールもない状態で機器をインターネットに曝すことになるので、一般的にはファイアウォールの役割を持つルータを置くことになります。
ルータの内側と外側とでネットワークを分けるか分けないかは1つの考えどころで、分けるのが一般的なはずです。 ネットワークを分けない場合、ルータの内側と外側のネットワークインターフェースをブリッジ接続しつつ、ebtables や br_netfilter + netfilter でフィルタリングする感じになるかと思います。 ただ、そのようにすると後々困ることは明白なので、ネットワークを分ける構成としました。
自宅内へのIPv6アドレスの配布について
さて、ネットワークを分けるということは、フレッツ網側からマルチキャストで送信されてくる Router Advertisement はルータの内側には届かず、ルータの内側からマルチキャストで送信される Router Solicitation もフレッツ網側に届きません。 そのため、自宅内に IPv6 アドレスを配る仕組みをルータに持たせる必要があります。 自宅内に Router Advertisement を送信するようにするか DHCPv6 を使うかといったことも設計ポイントですが、やはり一般的というか標準的な前者を採用しました。
次にどの IPv6 アドレスを配るかという点ですが、選択肢としては、フレッツ網から貰えるグローバルアドレスをそのまま配るか、ユニークローカルアドレスを配るか、といったものがあります。 後者の方法は、ユニークローカルアドレスを IPv4 におけるプライベートアドレスのように利用しようというものですが、ユニークローカルアドレスはそういった用途で利用するものではないですし、ルータで NAT も必要となってしまいます。 なので、グローバルアドレスを自宅内に配っていきます。
また、自宅内の機器がグローバルアドレスを利用して IPv6 インターネットにアクセスしようとすると、フレッツ網側からパケットが返ってこないという問題が発生します。 フレッツ網側は自宅内機器のグローバルアドレスを同一セグメントにいるものとして扱うため、グローバルアドレスに対応するMACアドレスを知るためにフレッツ網側からは Neighbor Solicitation がマルチキャストで送信されてきます。 しかし、Neighbor Solicitation は自宅内機器に届かず誰も応答しないので、フレッツ網側からパケットが返ってこないということになります。
この問題を解決するには、ルータは自身のMACアドレスを入れた Neighbor Advertisement をフレッツ網側に返す必要があります。 自宅内機器では動的なアドレス設定を行うようにしたいので、ルータでは NDP Proxy を動かし、フレッツ網側から NS が来たら自宅内に対して NS を送信し、自宅内から NA が返ってきたらフレッツ網側に NA を返すようにします。
IPv6グローバルアドレスを付けるインターフェースについて
ルータの内側と外側のどちらのインターフェースに IPv6 グローバルアドレスを付けるかで悩みました。 両方のインターフェースに付けるという選択肢もあります。
必ず実現したいことは、ルータがインターネットにアクセスする際に Temporary アドレスを利用するようにしたいということです。 そのためにはルータは対象のインターフェースで RA を受信して Temporary アドレスを自動設定する必要があります。
内側のインターフェースに Temporary アドレスを付けるには、ルータ自身が送信した RA を自分自身が受信して利用することになり、RA はそういう用途で利用するものではないはずです。 また、NDP Proxy として利用するソフトウェア (ndppd) を利用した際、ターゲットのアドレスを内側のインターフェースで持っていても、適切に NA を返してくれませんでした。
ゆえに、外側のインターフェースに Temporary アドレスを付けることとしました。 そう決めてしまえば、内側のインターフェースにグローバルアドレスを付ける必要はないという発想に至り、RDNSS や DHCPv6 を利用する必要もなくなる (自宅内の DNS は IPv4 で用意すればいい) ので、構成がシンプルになりました。
ルータの構築作業
0. 事前準備
IPoE + MAP-E 接続するルータを構築するにあたり、事前に次の情報を調べておきましょう。
- フレッツ網側から払い出されるグローバルアドレスのプレフィックス
- MAP-E 接続に関する情報
- CE と BR の IPv6 グローバルアドレス
- CE で利用できる IPv4 グローバルアドレスとポート番号のセット (および PSID)
- ※MAP-E に関する基礎知識は RFC 7597 を読んで把握してください。
既に何らかのルータ機器を使って接続をしていれば、そのルータの設定を見ればわかるでしょう。
わからなくても Linux マシンをフレッツ網に繋いで rdisc6 -1 enp2s0
のようなコマンドを叩けばプレフィックスは得られますし、プレフィックスがわかれば 例のサイト で MAP-E 接続に関する情報も得られます。
上記情報を動的に取得・計算して後述の各種設定を行うようにしてもいいですが、ISP 変更や引っ越しなどをしない限り変わらないため、情報を静的に利用すると楽です。
Linux マシンは最小限にセットアップした Ubuntu Server 24.04 を用意し、ログインできれば後はどうにでもなりますが、後々の作業で必要となるパッケージのインストールは事前に行っておくと良いです。
1$ sudo apt install radvd ndppd isc-dhcp-server iptables-persistent
2
3$ sudo systemctl disable --now radvd.service
4$ sudo systemctl disable --now ndppd.service
5$ sudo systemctl disable --now isc-dhcp-server.service
6$ sudo systemctl disable --now isc-dhcp-server6.service
7$ sudo systemctl disable --now netfilter-persistent.service
1. フレッツ網に接続する
まず、ルータの Linux マシンをフレッツ網に接続し、ルータだけ IPv6 インターネットに接続してみます。
Linux マシンの外側のネットワークインターフェース (enp2s0) と VDSL モデムを接続しましょう。 Ubuntu は標準では netplan でインターフェースが管理されていますので、その設定次第では自動で IPv6 グローバルアドレスが付くかもしれません。 ただ、netplan では設定できない項目が後で出てきますので、systemd-networkd を直接利用してネットワーク設定をしていきます。
systemd.network の man を見ながら設定を作成します。
1## netplan の設定を削除 (どこかに退避しても良い)
2$ sudo rm /etc/netplan/*
3
4
5## systemd-networkd の設定を作成
6$ sudo vim /etc/systemd/network/10-enp2s0.network
7$ cat /etc/systemd/network/10-enp2s0.network
8----------
9[Match]
10Name=enp2s0
11
12[Network]
13LinkLocalAddressing=ipv6
14IPv6AcceptRA=true
15IPv6PrivacyExtensions=true
16----------
先述の通り、外側のインターフェースには Temporary アドレスが自動で付与されるようにしています。
ネットワーク設定を反映すると、インターフェースやルーティングテーブルは次のようになりました。
1## ネットワーク設定を反映
2$ sudo systemctl restart systemd-networkd.service
3
4
5## インターフェースの確認
6$ ip addr show enp2s0
7----------
82: enp2s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
9 link/ether YY:YY:YY:YY:YY:YY brd ff:ff:ff:ff:ff:ff
10 inet6 2400:XXXX:XXXX:XXXX:5d8:91b3:bc62:14cb/64 scope global temporary dynamic
11 valid_lft 604758sec preferred_lft 85803sec
12 inet6 2400:XXXX:XXXX:XXXX:YYYY:YYYY:YYYY:YYYY/64 scope global dynamic mngtmpaddr noprefixroute
13 valid_lft 2591958sec preferred_lft 604758sec
14 inet6 fe80::YYYY:YYYY:YYYY:YYYY/64 scope link
15 valid_lft forever preferred_lft forever
16----------
17
18
19## ルーティングテーブルの確認
20$ ip -6 route
21----------
222400:XXXX:XXXX:XXXX::/64 dev enp2s0 proto ra metric 1024 expires 2591940sec hoplimit 64 pref medium
23fe80::/64 dev enp2s0 proto kernel metric 256 pref medium
24default via fe80::TTTT:TTTT:TTTT dev enp2s0 proto ra metric 1024 expires 1740sec hoplimit 64 pref medium
25----------
26
27
28## DNS リゾルバの確認
29$ cat /run/systemd/resolve/resolv.conf
30----------
31(コメント省略)
32nameserver 2404:1a8:7f01:b::3
33nameserver 2404:1a8:7f01:a::3
34search .
35----------
36
37
38## NTP クライアントの確認
39$ timedatectl show-timesync
40----------
41LinkNTPServers=2404:1a8:1102::b 2404:1a8:1102::a
42FallbackNTPServers=ntp.ubuntu.com
43ServerName=2404:1a8:1102::b
44ServerAddress=2404:1a8:1102::b
45(以下略)
46----------
47
48
49## 疎通確認
50$ ping dns.google -c 2
51----------
52PING dns.google (2001:4860:4860::8844) 56 data bytes
5364 bytes from dns.google (2001:4860:4860::8844): icmp_seq=1 ttl=116 time=4.53 ms
5464 bytes from dns.google (2001:4860:4860::8844): icmp_seq=2 ttl=116 time=4.71 ms
55
56--- dns.google ping statistics ---
572 packets transmitted, 2 received, 0% packet loss, time 1002ms
58rtt min/avg/max/mdev = 4.531/4.619/4.708/0.088 ms
59----------
RA で設定されるもの以外に、フレッツ網から DHCPv6 で配られる DNS サーバや NTP サーバも設定されました。 IPv6 インターネットへの疎通性や名前解決も確認できています。
2. IPv6フィルタリングルールを設定する
この時点ではルータが IPv6 インターネットに曝されている状態ですし、安全のため自宅内の機器を繋ぐ前にフィルタリングルールを設定しておきましょう。
私は慣れている ip6tables を利用し、次のような内容にしました。
※ip6tables-save
コマンドの出力 (コメント除去済)
1*filter
2:INPUT DROP [0:0]
3:FORWARD DROP [0:0]
4:OUTPUT ACCEPT [0:0]
5-A INPUT -p ipv6-icmp -j ACCEPT
6-A INPUT -i lo -j ACCEPT
7-A INPUT -i enp3s0 -j ACCEPT
8-A INPUT -s fe80::/64 -d fe80::/64 -i enp2s0 -p udp -m udp --sport 547 --dport 546 -j ACCEPT
9-A INPUT -i enp2s0 -m state --state RELATED,ESTABLISHED -j ACCEPT
10-A INPUT -j DROP
11-A FORWARD -d fc00::/7 -o enp2s0 -j REJECT --reject-with icmp6-addr-unreachable
12-A FORWARD -d fec0::/10 -o enp2s0 -j REJECT --reject-with icmp6-addr-unreachable
13-A FORWARD -p ipv6-icmp -j ACCEPT
14-A FORWARD -i enp3s0 -j ACCEPT
15-A FORWARD -i enp2s0 -m state --state RELATED,ESTABLISHED -j ACCEPT
16-A FORWARD -j DROP
17-A OUTPUT -d fc00::/7 -o enp2s0 -j REJECT --reject-with icmp6-addr-unreachable
18-A OUTPUT -d fec0::/10 -o enp2s0 -j REJECT --reject-with icmp6-addr-unreachable
19-A OUTPUT -j ACCEPT
20COMMIT
基本的な方針として、インターネットとの通信については、外向きは全て許可し、内向きは ICMPv6 と戻りのみ許可しています。 自宅内とルータとの通信については全て許可しています。
ルータはフレッツ網内にいる DHCPv6 サーバと通信を行うため、DHCPv6 サーバから来る通信だけは限定して明示的に許可しています(INPUT 4行目)。 ただし、戻りを許可しており、DHCPv6 サーバから通信が行われることはないはずなので、明示的な許可は無くても良いと思います。
また、ユニークローカルアドレスやサイトローカルアドレスのようにインターネットに存在しないことが明白な宛先の通信は、インターネットに出て行かないようにしています(OUTPUT と FORWARD のそれぞれ1,2行目)。 なお、サイトローカルアドレス宛の REJECT ルールは、次項の Windows の動作を考慮して後から設定を追加しました。
3. 自宅内機器がIPv6インターネットと通信できるようにする
ルータのネットワーク設定
フィルタリングルールを設定したら、次は自宅内の機器に IPv6 グローバルアドレスを配布し、インターネットと通信できるようにしていきます。
まず、ルータで IPv6 転送の有効化と内側のネットワークインターフェース (enp3s0) のネットワーク設定をしておきます。
1## IPv6 転送を有効化
2$ sudo vim /etc/sysctl.d/90-router.conf
3$ cat /etc/sysctl.d/90-router.conf
4----------
5net.ipv6.conf.all.forwarding=1
6----------
7
8$ sudo sysctl --system
9
10
11## systemd-networkd の設定を作成
12$ sudo vim /etc/systemd/network/10-enp3s0.network
13$ cat /etc/systemd/network/10-enp3s0.network
14----------
15[Match]
16Name=enp3s0
17
18[Network]
19LinkLocalAddressing=ipv6
20IPv6AcceptRA=false
21
22[Route]
23Destination=2400:XXXX:XXXX:XXXX::/64
24Metric=1
25----------
26
27## ネットワーク設定を反映
28$ sudo systemctl restart systemd-networkd.service
29
30
31## インターフェースの確認
32$ ip addr show enp3s0
33----------
343: enp3s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
35 link/ether ZZ:ZZ:ZZ:ZZ:ZZ:ZZ brd ff:ff:ff:ff:ff:ff
36 inet6 fe80::ZZZZ:ZZZZ:ZZZZ:ZZZZ/64 scope link
37 valid_lft forever preferred_lft forever
38----------
39
40
41## ルーティングテーブルの確認
42$ ip -6 route
43----------
442400:XXXX:XXXX:XXXX::/64 dev enp3s0 proto static metric 1 pref medium
452400:XXXX:XXXX:XXXX::/64 dev enp2s0 proto ra metric 1024 expires 2591762sec hoplimit 64 pref medium
46fe80::/64 dev enp2s0 proto kernel metric 256 pref medium
47fe80::/64 dev enp3s0 proto kernel metric 256 pref medium
48default via fe80::TTTT:TTTT:TTTT dev enp2s0 proto ra metric 1024 expires 1562sec hoplimit 64 pref medium
49----------
内側のインターフェースにはグローバルアドレスを持たせないこととしたので、上記設定ではリンクローカルアドレスだけが設定されます。
また、自宅内のプレフィックス宛の静的経路を metric 1 で追加するようにしています。 enp2s0 で自動的に設定された経路よりも優先度を高くしないと、インターネット側から来たパケットをルータは自宅内に転送してくれませんので。
RA の送信設定
次に、radvd を利用して自宅内に RA を送信するようにします。 radvd.conf の man を読んで設定を作成し、サービスとして起動させました。
1## radvd の設定作成
2$ sudo vim /etc/radvd.conf
3$ cat /etc/radvd.conf
4----------
5interface enp3s0 {
6 AdvSendAdvert on;
7 AdvManagedFlag off;
8 AdvOtherConfigFlag off;
9
10 prefix 2400:XXXX:XXXX:XXXX::/64 {
11 AdvOnLink on;
12 AdvAutonomous on;
13 };
14};
15----------
16
17## radvd の起動・サービス有効化
18$ sudo systemctl restart radvd.service
19$ sudo systemctl enable radvd.service
ここで試しに、クライアントマシンとして Windows PC をルータの内側インターフェースに接続してみました。
1C:\Users\ymstmsys>ipconfig /all
2----------
3イーサネット アダプター イーサネット:
4
5 接続固有の DNS サフィックス . . . . .:
6 説明. . . . . . . . . . . . . . . . .: Realtek PCIe GbE Family Controller
7 物理アドレス. . . . . . . . . . . . .: WW-WW-WW-WW-WW-WW
8 DHCP 有効 . . . . . . . . . . . . . .: はい
9 自動構成有効. . . . . . . . . . . . .: はい
10 IPv6 アドレス . . . . . . . . . . . .: 2400:XXXX:XXXX:XXXX:MMMM:MMMM:MMMM:MMMM(優先)
11 一時 IPv6 アドレス. . . . . . . . . .: 2400:XXXX:XXXX:XXXX:4869:d2d8:3869:77c3(優先)
12 リンクローカル IPv6 アドレス. . . . .: fe80::WWWW:WWWW:WWWW:WWWW%7(優先)
13 自動構成 IPv4 アドレス. . . . . . . .: 169.254.132.101(優先)
14 サブネット マスク . . . . . . . . . .: 255.255.0.0
15 デフォルト ゲートウェイ . . . . . . .: fe80::ZZZZ:ZZZZ:ZZZZ:ZZZZ%7
16 DHCPv6 IAID . . . . . . . . . . . . .: 178023168
17 DHCPv6 クライアント DUID. . . . . . .: 00-01-00-01-2B-ED-33-23-WW-WW-WW-WW-WW-WW
18 DNS サーバー. . . . . . . . . . . . .: fec0:0:0:ffff::1%1
19 fec0:0:0:ffff::2%1
20 fec0:0:0:ffff::3%1
21 NetBIOS over TCP/IP . . . . . . . . .: 有効
22----------
グローバルアドレスは期待通り設定され、デフォルトゲートウェイにはルータのリンクローカルアドレスが設定されていて期待通りです。
しかし、DNS サーバとしてサイトローカルアドレスが自動で設定されているのは厄介ですね…。 クライアントに IPv6 で DNS サーバを配布しないようにした影響がここで出てきました。 ただ、悪影響が多いわけではないため、いったんそのままにしておきます。 (実際はこのときにルータにてサイトローカルアドレス宛の通信が外に出て行かないように設定を追加しました。)
NDP Proxy の設定
そして次は、NDPPD を利用して NDP Proxy を行わせます。 ndppd.conf の man を読んで設定を作成し、サービスとして起動させました。
1## NDPPD の設定作成
2$ sudo vim /etc/ndppd.conf
3$ cat /etc/ndppd.conf
4----------
5proxy enp2s0 {
6 router no
7 rule 2400:XXXX:XXXX:XXXX::/64 {
8 iface enp3s0
9 }
10}
11----------
12
13## NDPPD の起動・サービス有効化
14$ sudo systemctl start ndppd.service
15$ sudo systemctl enable ndppd.service
これで自宅内の機器も IPv6 インターネットと通信できるようになりました。
1C:\Users\ymstmsys>ping 2001:4860:4860::8844 -n 2
2----------
32001:4860:4860::8844 に ping を送信しています 32 バイトのデータ:
42001:4860:4860::8844 からの応答: 時間 =5ms
52001:4860:4860::8844 からの応答: 時間 =5ms
6
72001:4860:4860::8844 の ping 統計:
8 パケット数: 送信 = 2、受信 = 2、損失 = 0 (0% の損失)、
9ラウンド トリップの概算時間 (ミリ秒):
10 最小 = 5ms、最大 = 5ms、平均 = 5ms
11----------
クライアントの Windows PC からの疎通確認も問題なしです。 この時点では名前解決を行えないため、グローバルアドレスを直接指定する必要がありますが。
4. MAP-Eトンネルを設定する
MAP-E を利用し、まずはルータだけ IPv4 インターネットと通信できるように設定していきます。
具体的に実施することとしては次の通りです。
- ルータに MAP CE アドレスを設定する
- フレッツ網側にある MAP BR との間で IPv4 over IPv6 トンネルを張る
- MAP CE と BR の間の IPv4 over IPv6 の通信を許可する
- IPv4 インターネット宛の経路をトンネルに向ける
- トンネルに向けて送信・転送する際、グローバルIPおよびポート番号をソース NAT する
まず、MAP CE アドレスの設定は、フレッツ網から指定されたアドレスを systemd-networkd で enp2s0 に設定するだけです。 また、この後作成するトンネルを enp2s0 に紐づける設定もしておきます。
1## MAP CE アドレスを追加
2$ sudo vim /etc/systemd/network/10-enp2s0.network
3$ cat /etc/systemd/network/10-enp2s0.network
4----------
5[Match]
6Name=enp2s0
7
8[Network]
9LinkLocalAddressing=ipv6
10IPv6AcceptRA=true
11IPv6PrivacyExtensions=true
12Address=2400:XXXX:XXXX:XXXX:SSSS:SSSS:SSSS:SSSS/64 # MAP CEのアドレスを追加
13Tunnel=tun-mape # トンネルインターフェースの名前を追加
14----------
15
16
17## ネットワーク設定を反映
18$ sudo systemctl restart systemd-networkd.service
MAP BR との間のトンネルインターフェースの作成も systemd-networkd で行うようにします。
1## トンネルインターフェースの作成
2$ sudo vim /etc/systemd/network/20-tun-mape.netdev
3$ cat /etc/systemd/network/20-tun-mape.netdev
4----------
5[NetDev]
6Name=tun-mape
7Kind=ip6tnl
8
9[Tunnel]
10Mode=ipip6
11Local=2400:XXXX:XXXX:XXXX:SSSS:SSSS:SSSS:SSSS # MAP CEのアドレス
12Remote=2001:380:a120::9 # MAP BRのアドレス
13EncapsulationLimit=none
14----------
15
16
17## ネットワーク設定を反映
18$ sudo systemctl restart systemd-networkd.service
19
20
21## インターフェースを確認
22$ ip addr show tun-mape
23----------
245: tun-mape@enp2s0: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1460 qdisc noqueue state UNKNOWN group default qlen 1000
25 link/tunnel6 2400:XXXX:XXXX:XXXX:SSSS:SSSS:SSSS:SSSS peer 2001:380:a120::9 permaddr PPPP:PPPP:PPPP::
26----------
MAP CE と BR の間の IPv4 over IPv6 通信のうち、BR 側からは来るものは Drop するようにしていましたので、限定的に許可します。
ipencap
は IPv4 がカプセル化されていることを示すプロトコルです。
1$ sudo ip6tables -I INPUT 5 -s 2001:380:a120::9/128 -d 2400:XXXX:XXXX:XXXX:SSSS:SSSS:SSSS:SSSS/128 -i enp2s0 -p ipencap -j ACCEPT
次に、IPv4 インターネットへの経路を設定する前に、MAP-E で行うべきソース NAT やフィルタリングの設定を入れていきます。 そうしないと、送信すべきでないパケットが BR やインターネットに流れて行ってしまいますので。
OCN 光の場合、割り当てられた共有の IPv4 グローバルアドレスのうち、1008個のポート番号 (16個のポート番号の範囲 × 63ブロック) を利用できます。 しかし、単純に以下のような SNAT のルールを書くと1つ目のルールしか適用されず、16個のポート番号しか利用されずにポート番号が不足する問題が起きます。
1*nat
2-A POSTROUTING -o tun-mape -p tcp -j SNAT --to-source 153.xx.xx.xx:1856-1871
3-A POSTROUTING -o tun-mape -p tcp -j SNAT --to-source 153.xx.xx.xx:2880-2895
4-A POSTROUTING -o tun-mape -p tcp -j SNAT --to-source 153.xx.xx.xx:3904-3919
5(以下略)
ポート番号を適切に利用する SNAT のルールの作成は、以下のページを参考にしました。
IPv4 のフィルタリングルールなどと合わせて、最終的には次の内容にしました。
※iptables-save
コマンドの出力 (コメント除去済)
1*mangle
2:PREROUTING ACCEPT [0:0]
3:INPUT ACCEPT [0:0]
4:FORWARD ACCEPT [0:0]
5:OUTPUT ACCEPT [0:0]
6:POSTROUTING ACCEPT [0:0]
7-A FORWARD -o tun-mape -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
8-A POSTROUTING -o tun-mape -j HMARK --hmark-sport-mask 0xffff --hmark-rnd 0x00000000 --hmark-mod 63 --hmark-offset 0
9COMMIT
10
11*filter
12:INPUT DROP [0:0]
13:FORWARD DROP [0:0]
14:OUTPUT ACCEPT [0:0]
15-A INPUT -i lo -j ACCEPT
16-A INPUT -i enp3s0 -j ACCEPT
17-A INPUT -i tun-mape -m state --state RELATED,ESTABLISHED -j ACCEPT
18-A INPUT -j DROP
19-A FORWARD -d 10.0.0.0/8 -o tun-mape -j REJECT --reject-with icmp-net-unreachable
20-A FORWARD -d 172.16.0.0/12 -o tun-mape -j REJECT --reject-with icmp-net-unreachable
21-A FORWARD -d 192.168.0.0/16 -o tun-mape -j REJECT --reject-with icmp-net-unreachable
22-A FORWARD -i enp3s0 -j ACCEPT
23-A FORWARD -i tun-mape -m state --state RELATED,ESTABLISHED -j ACCEPT
24-A FORWARD -j DROP
25-A OUTPUT -d 10.0.0.0/8 -o tun-mape -j REJECT --reject-with icmp-net-unreachable
26-A OUTPUT -d 172.16.0.0/12 -o tun-mape -j REJECT --reject-with icmp-net-unreachable
27-A OUTPUT -d 192.168.0.0/16 -o tun-mape -j REJECT --reject-with icmp-net-unreachable
28-A OUTPUT -j ACCEPT
29COMMIT
30
31*nat
32:PREROUTING ACCEPT [0:0]
33:INPUT ACCEPT [0:0]
34:OUTPUT ACCEPT [0:0]
35:POSTROUTING ACCEPT [0:0]
36-A POSTROUTING -o tun-mape -p tcp -m mark --mark 0x0 -j SNAT --to-source 153.xx.xx.xx:1856-1871
37-A POSTROUTING -o tun-mape -p udp -m mark --mark 0x0 -j SNAT --to-source 153.xx.xx.xx:1856-1871
38-A POSTROUTING -o tun-mape -p icmp -m mark --mark 0x0 -j SNAT --to-source 153.xx.xx.xx:1856-1871
39-A POSTROUTING -o tun-mape -p tcp -m mark --mark 0x1 -j SNAT --to-source 153.xx.xx.xx:2880-2895
40-A POSTROUTING -o tun-mape -p udp -m mark --mark 0x1 -j SNAT --to-source 153.xx.xx.xx:2880-2895
41-A POSTROUTING -o tun-mape -p icmp -m mark --mark 0x1 -j SNAT --to-source 153.xx.xx.xx:2880-2895
42(以下プロトコルごとに63行のSNAT、合計189行のSNAT)
43COMMIT
mangle テーブルにて、トンネルインターフェースに送信・転送する際に、 HMARK ルールにより通信のソースポート番号から計算されるハッシュをもとに 0~62 のマークを付けています。 また、nat テーブルでは、ポートセットのポート範囲ごとにマーク付けした SNAT ルールを用意し、マークが一致するルールが適用されるようにしています。 これにより、偏りがなるべく発生しないようにポート範囲を利用しつつ、Port Restricted Cone NAT になっているのでスプラトゥーンのような P2P のゲームもプレイできるはずです。
フィルタリングルールは IPv6 と同じ方針で、インターネットとの通信については、外向きは全て許可し、内向きは戻りのみ許可しています。 自宅内とルータとの通信については全て許可しています。
あと、mangle テーブルではトンネルインターフェースに転送する際に、TCP 接続時に MSS 調整を行うようにしています。 自宅内の MTU は1500ですが、トンネルインターフェースの MTU は1460なので MSS 調整を入れておかないと上手く通信できません。
ソース NAT やフィルタリングの設定を入れたら、IPv4 アドレスやトンネルインターフェースを利用するデフォルト経路を設定し、IPv4 インターネットと通信できるようにします。
1## 内側のネットワークインターフェースにプライベートアドレスを設定
2$ sudo vim /etc/systemd/network/10-enp3s0.network
3$ cat /etc/systemd/network/10-enp3s0.network
4----------
5[Match]
6Name=enp3s0
7
8[Network]
9LinkLocalAddressing=ipv6
10Address=192.168.1.1/24 # ルータ用のプライベートアドレスを追加
11IPv6AcceptRA=false
12
13[Route]
14Destination=2400:XXXX:XXXX:XXXX::/64
15Metric=1
16----------
17
18
19## トンネルインターフェースのネットワーク設定を作成
20$ sudo vim /etc/systemd/network/20-tun-mape.network
21$ cat /etc/systemd/network/20-tun-mape.network
22----------
23[Match]
24Name=tun-mape
25
26[Network]
27LinkLocalAddressing=no
28
29[Route]
30Destination=0.0.0.0/0
31----------
32
33
34## ネットワーク設定を反映
35$ sudo systemctl restart systemd-networkd.service
36
37
38## ルーティングテーブルの確認
39$ ip route
40----------
41default dev tun-mape proto static scope link
42192.168.1.0/24 dev enp3s0 proto kernel scope link src 192.168.1.1
43----------
IPv4 グローバルアドレスは専有のものではないため、トンネルインターフェースには設定せず、あくまでもソース NAT 用として利用するようにしました。 仮にトンネルインターフェースに設定したとしてもソース NAT は行われますし、フィルタリングルールが消えてしまったときに変な通信が着信する可能性も無くしておくためです。
また、トンネルインターフェースにはリンクローカルアドレスも付かないように LinkLocalAddressing=no
としています。
そうしないと、OS起動時にこのインターフェースに他のアドレスが付くまで待機する処理のタイムアウトまで待たされて時間がかかってしまいますし、networkctl
コマンドで見た時に degraded / configuring になってしまいます。
この設定で IPv4 インターネットへの疎通確認も問題なしです。
1## 疎通確認
2$ ping 8.8.8.8 -c 2
3----------
4PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
564 bytes from 8.8.8.8: icmp_seq=1 ttl=118 time=4.85 ms
664 bytes from 8.8.8.8: icmp_seq=2 ttl=118 time=4.80 ms
7
8--- 8.8.8.8 ping statistics ---
92 packets transmitted, 2 received, 0% packet loss, time 1001ms
10rtt min/avg/max/mdev = 4.801/4.826/4.851/0.025 ms
11----------
5. 自宅内機器がIPv4インターネットと通信できるようにする
自宅内の機器が IPv4 インターネットと通信できるようにしていきます。
まず、ルータで IPv4 転送を有効化しておきます。
1## IPv4 転送を有効化
2$ sudo vim /etc/sysctl.d/90-router.conf
3$ cat /etc/sysctl.d/90-router.conf
4----------
5net.ipv6.conf.all.forwarding=1
6net.ipv4.ip_forward=1 # この設定を追加
7----------
8
9$ sudo sysctl --system
次に、ISC DHCP server を利用して自宅内に DHCP でIPアドレスを配布するようにします。 dhcpd.conf の man を読んで設定を作成し、サービスとして起動させました。
1## ISC DHCP Server の設定作成
2$ sudo vim /etc/dhcp/dhcpd.conf
3$ cat /etc/dhcp/dhcpd.conf
4----------
5subnet 192.168.1.0 netmask 255.255.255.0 {
6 range 192.168.1.101 192.168.1.254;
7 option routers 192.168.1.1;
8 option subnet-mask 255.255.255.0;
9 default-lease-time 172800;
10 max-lease-time 172800;
11 option dhcp-renewal-time 86400;
12 option dhcp-rebinding-time 151200;
13}
14----------
15
16
17$ sudo vim /etc/default/isc-dhcp-server
18$ cat /etc/default/isc-dhcp-server
19----------
20(コメント省略)
21INTERFACESv4="enp3s0" # インターフェースを指定しておかないと、サービス起動時に警告ログが出る
22INTERFACESv6=""
23----------
24
25
26## ISC DHCP Server の起動・サービス有効化
27$ sudo systemctl restart isc-dhcp-server.service
28$ sudo systemctl enable isc-dhcp-server.service
これでクライアントの Windows PC にも IPV4 アドレスが付与され、IPv4 インターネットへの疎通も問題なしです。
1C:\Users\ymstmsys>ipconfig /all
2----------
3イーサネット アダプター イーサネット:
4
5 接続固有の DNS サフィックス . . . . .:
6 説明. . . . . . . . . . . . . . . . .: Realtek PCIe GbE Family Controller
7 物理アドレス. . . . . . . . . . . . .: WW-WW-WW-WW-WW-WW
8 DHCP 有効 . . . . . . . . . . . . . .: はい
9 自動構成有効. . . . . . . . . . . . .: はい
10 IPv6 アドレス . . . . . . . . . . . .: 2400:XXXX:XXXX:XXXX:MMMM:MMMM:MMMM:MMMM(優先)
11 一時 IPv6 アドレス. . . . . . . . . .: 2400:XXXX:XXXX:XXXX:4869:d2d8:3869:77c3(優先)
12 リンクローカル IPv6 アドレス. . . . .: fe80::WWWW:WWWW:WWWW:WWWW%7(優先)
13 IPv4 アドレス . . . . . . . . . . . .: 192.168.1.101(優先)
14 サブネット マスク . . . . . . . . . .: 255.255.255.0
15 リース取得. . . . . . . . . . . . . .: 2025年2月2日 11:29:45
16 リースの有効期限. . . . . . . . . . .: 2025年2月4日 11:29:45
17 デフォルト ゲートウェイ . . . . . . .: fe80::ZZZZ:ZZZZ:ZZZZ:ZZZZ%7
18 192.168.1.1
19 DHCP サーバー . . . . . . . . . . . .: 192.168.1.1
20 DHCPv6 IAID . . . . . . . . . . . . .: 178023168
21 DHCPv6 クライアント DUID. . . . . . .: 00-01-00-01-2B-ED-33-23-WW-WW-WW-WW-WW-WW
22 DNS サーバー. . . . . . . . . . . . .: fec0:0:0:ffff::1%1
23 fec0:0:0:ffff::2%1
24 fec0:0:0:ffff::3%1
25 NetBIOS over TCP/IP . . . . . . . . .: 有効
26----------
27
28
29C:\Users\ymstmsys>ping 8.8.8.8 -n 2
30----------
318.8.8.8 に ping を送信しています 32 バイトのデータ:
328.8.8.8 からの応答: バイト数 =32 時間 =5ms TTL=117
338.8.8.8 からの応答: バイト数 =32 時間 =5ms TTL=117
34
358.8.8.8 の ping 統計:
36 パケット数: 送信 = 2、受信 = 2、損失 = 0 (0% の損失)、
37ラウンド トリップの概算時間 (ミリ秒):
38 最小 = 5ms、最大 = 5ms、平均 = 5ms
39----------
6. iptablesルールを保存・自動リストアする
iptables や ip6tables コマンドで設定したルールは OS 再起動すると消えてしまいます。 それでは困るので、iptables-persistent で対応します。
まず、iptables-persistent パッケージを入れて netfilter-persistent save
コマンドを実行すれば、/etc/iptables/
ディレクトリ内に現在の iptables / ip6tables ルールが保存されます。
そして netfilter-persistent
サービスを自動起動するようにすれば、保存したルールがOS起動時にリストアされます。
1## iptables / ip6tables ルールを保存
2$ sudo netfilter-persistent save
3
4
5## netfilter-persistent サービスの有効化
6$ sudo systemctl enable netfilter-persistent.service
netfilter-persistent save
を自動で実行するようにしても良いですが、誤った設定が保存されて自宅内からリモートでログインできなくなると困るので、ルールを変更した場合は手動で save するという運用にしておきます。
7. DNS キャッシュサーバ機能を構築する
ルータの機能として持つべきものとされている DNS キャッシュサーバとしての機能も構築しましょう。
今回は dnsmasq を利用して簡易的にキャッシュサーバを構築しました。
まず、Ubuntu 標準でセットアップされている systemd-resolved のスタブリスナーは、DNS サーバを構築する際に邪魔で相性が悪いので無効化しておきます。
1## スタブリスナーを無効化する設定を作成
2$ sudo mkdir /etc/systemd/resolved.conf.d
3$ sudo vim /etc/systemd/resolved.conf.d/resolved.conf
4$ cat /etc/systemd/resolved.conf.d/resolved.conf
5----------
6[Resolve]
7DNSStubListener=no
8----------
9
10
11## 設定を反映
12$ systemctl restart systemd-resolved.service
13
14
15## DNS リゾルバの確認
16$ cat /etc/resolv.conf
17(コメント省略)
18nameserver 2404:1a8:7f01:b::3
19nameserver 2404:1a8:7f01:a::3
20search .
/etc/resolv.conf
にはフレッツ網から DHCPv6 で配られている DNS サーバが設定されており、これでOKです。
dnsmasq はそこに問い合わせるようにします。
次に、dnsmasq の設定を作成して起動します。
1## パッケージインストール
2$ sudo apt install dnsmasq
3
4
5## dnsmasqの設定作成
6$ sudo vim /etc/dnsmasq.d/dnsmasq.conf
7$ cat /etc/dnsmasq.d/dnsmasq.conf
8----------
9strict-order
10cache-size=10000
11local-ttl=86400
12----------
13
14
15$ sudo vim /etc/default/dnsmasq
16$ cat /etc/default/dnsmasq
17----------
18(ここでは設定変更したものだけを記載しておく)
19IGNORE_RESOLVCONF=yes
20DNSMASQ_EXCEPT="lo"
21----------
22
23
24## dnsmasq の起動・サービス有効化
25$ sudo systemctl restart dnsmasq.service
26$ sudo systemctl enable dnsmasq.service
/etc/default/dnsmasq
の設定変更についてですが、/etc/resolv.conf
記載されている DNS サーバをアップストリームとして dnsmasq に利用させるには、IGNORE_RESOLVCONF=yes
を指定する必要がありました。
また、dnsmasq の起動時に Failed to revert interface configuration: Link lo is loopback device.
というエラーが出力されており、動作上は問題なさそうですが、エラーが出ないようにするために DNSMASQ_EXCEPT="lo"
を指定しました。
/etc/resolv.conf
に複数の DNS サーバが記載されている場合、dnsmasq は上から順ではなく任意の順にクエリを投げるようです。
さらに、同じクエリを複数の DNS サーバに同時に投げることもあり、後から回答してきた DNS サーバには ICMP6 で unreachable port を返してしまっていました。
せっかく回答してくれた DNS サーバに ICMP エラーを返すのは何となく失礼なので、strict-order
の設定を入れて /etc/resolv.conf
の上から順にクエリを投げるようにしています。
それでも1つ目の DNS サーバからの回答が少し遅れたら2つ目にもクエリを投げてしまいますが、頻度は減っているので良しとしました。
自宅側以外のインターフェースへの DNS クエリに応答しないように interface=enp3s0
と bind-interfaces
の設定も入れておこうとしましたが、dnsmasq は標準で起動オプションで --local-service
が指定されており、そもそもローカルネットワーク以外からのクエリには応答しないようになっていました。
将来的に自宅内に別のネットワークを追加したときに問題が生じますが、とりあえず今のところはこの設定で問題ないので追加設定はしないでおきました。
dnsmasq がちゃんと動作しているかどうかも確認しておきましょう。
1C:\Users\ymstmsys>nslookup dns.google 192.168.1.1
2----------
3サーバー: UnKnown
4Address: 192.168.1.1
5
6権限のない回答:
7名前: dns.google
8Addresses: 2001:4860:4860::8844
9 2001:4860:4860::8888
10 8.8.8.8
11 8.8.4.4
12----------
dnsmasq が動作していることを確認したら、それを DHCP で配るようにしましょう。
1## ISC DHCP Server の設定変更
2$ sudo vim /etc/dhcp/dhcpd.conf
3$ cat /etc/dhcp/dhcpd.conf
4----------
5subnet 192.168.1.0 netmask 255.255.255.0 {
6 range 192.168.1.101 192.168.1.254;
7 option routers 192.168.1.1;
8 option subnet-mask 255.255.255.0;
9 default-lease-time 172800;
10 max-lease-time 172800;
11 option dhcp-renewal-time 86400;
12 option dhcp-rebinding-time 151200;
13 option domain-name-servers 192.168.1.1; # 設定を追加
14}
15----------
16
17
18## 設定変更を反映
19$ sudo systemctl restart isc-dhcp-server.service
後はクライアントのネットワークインターフェースを再起動して回ればOKですね。
8. NTP サーバ機能も構築する
これまで利用していたルータでは NTP サーバ機能も兼ねていたので、同じように構築しておきます。
Ubuntu 標準で動作している systemd-timesyncd は NTP クライアントの機能しかないため、NTP サーバとして chrony を利用することにしました。
1## chrony をインストール
2$ sudo apt install chrony
3
4
5## 設定作成
6$ sudo vim /etc/chrony/conf.d/chrony.conf
7$ cat /etc/chrony/conf.d/chrony.conf
8----------
9allow 192.168.1.0/24
10----------
11
12
13## フレッツ網の NTP サーバを参照するように設定
14$ sudo vim /etc/chrony/sources.d/chrony.sources
15$ cat /etc/chrony/sources.d/chrony.sources
16----------
17server 2404:1a8:1102::b iburst
18server 2404:1a8:1102::a iburst
19----------
20
21
22## デフォルトで参照する NTP サーバの pool を削除
23$ sudo sed -i -e "/^pool /d" /etc/chrony/chrony.conf
24
25
26## ネットワークがオンラインになってからサービスが起動するようにする
27$ sudo mkdir /etc/systemd/system/chrony.service.d
28$ sudo vim /etc/systemd/system/chrony.service.d/network-online.conf
29$ cat /etc/systemd/system/chrony.service.d/network-online.conf
30----------
31[Unit]
32Wants=network-online.target
33After=network-online.target
34----------
35
36$ sudo systemctl daemon-reload
37
38
39## chrony の起動・サービス有効化
40$ sudo systemctl restart chrony.service
41$ sudo systemctl enable chrony.service
Ubuntu では chrony をインストールすると systemd-timesyncd は削除されてしまうのですね…。 本当は systemd-timesyncd を NTP クライアントとしつつ、chrony には NTP サーバの機能だけを持たせようとしていましたが、それをやるには少し手間がかかるので諦めました。
フレッツ網内の NTP サーバはこの記事の 「1. フレッツ網に接続する」 で確認していましたし、systemd-networkd が作成する /run/systemd/netif/state
ファイルにも記載されていました。
スクリプトを書けばこのファイルの内容を自動的に chrony に反映させることもできますが、そこまでする理由がないので chrony.sources
に明示的に記載するようにしています。
chrony の標準の systemd のスクリプトでは、ネットワークがオンラインになるのを待たずにサービスとして起動するようになっています。 また、chrony は起動時に NTP サーバに繋がらなかった場合、その NTP サーバをオフラインであるものとし、手動でオンラインにしない限り再度繋ぎに行くことは無いようです。 そのため、そのままでは OS 起動時に NTP サーバに接続できず時刻同期もされないため、ネットワークがオンラインになるのを待ってから起動するようにしています。
一応、chrony の同期状況も確認しておきましょう。
1$ sudo chronyc sources
2----------
3MS Name/IP address Stratum Poll Reach LastRx Last sample
4===============================================================================
5^* 2404:1a8:1102::a 2 7 377 94 +230us[ +236us] +/- 6013us
6^+ 2404:1a8:1102::b 2 7 377 93 -259us[ -259us] +/- 5810us
7----------
問題なさそうなので、NTP サーバの情報を DHCP で配るようにします。
1## ISC DHCP Server の設定変更
2$ sudo vim /etc/dhcp/dhcpd.conf
3$ cat /etc/dhcp/dhcpd.conf
4----------
5subnet 192.168.1.0 netmask 255.255.255.0 {
6 range 192.168.1.101 192.168.1.254;
7 option routers 192.168.1.1;
8 option subnet-mask 255.255.255.0;
9 default-lease-time 172800;
10 max-lease-time 172800;
11 option dhcp-renewal-time 86400;
12 option dhcp-rebinding-time 151200;
13 option domain-name-servers 192.168.1.1;
14 option ntp-servers 192.168.1.1; # 設定を追加
15}
16----------
17
18
19## 設定変更を反映
20$ sudo systemctl restart isc-dhcp-server.service
クライアントの設定が切り替わるかどうかはクライアント次第ですが、ひとまずこれで良いでしょう。
まとめ
ルータのどこに IPv6 グローバルアドレスを付けるかという点と、MAP-E 接続のためのソース NAT をどうすべきかという点に悩みましたが、最も妥当な構成で構築できていると思います。
1週間以上稼働させていますが大きな問題は起きていません。 冬だからか MINISFORUM GK41 の CPU 温度は35度前後を保っており、ファンの音はほとんど気にならず、LED の明るさは控えめで良い感じです。
tcpdump や conntrack で気軽にパケットやコネクションを確認できるようになったのは非常に楽しくて嬉しいです!
接続元を限定して自宅内に入れるようにしたり、Raspberry PI で担っている WireGuard ホストの機能をルータで巻き取ったり、やることはまだまだありますが、それらはまた別の記事とします。