Linuxでv6プラス MAP-Eなルーターをつくる。IPv6, RAも疎通する版

ルーターで遊びたい欲が出てきており、適当なマシンを調達して遊ぶことになりました。 自分の家のルーター(IO DATA WN-AX2033GR)が微妙にIPoE環境だとフルスピードが出ないこと(だいたい200-300Mbpsが限界)や細かい設定ができない不満などがあり、いろいろ代替手段を物色していたんですがちょうどいいマシンを借りれたのでそれでやりました

よくある文献だとv6の設定まで詳しくは書いてなかったりするんですがv6のほうがカプセル化のオーバーヘッドがない分スループット高いはずだし、MAP-Eでセッション(ポート)枯渇することもないので使わない手はないはずなんですよね。

環境

  • 光コラボ回線。NTTフレッツ光ネクスト相当
  • DTI 光。 安いがIPoEが開通するとPPPoEが消滅する
  • IPv4 over IPv6はJPNEのMAP-E
  • おうちに光コンセントが生えているタイプ
  • ひかり電話なし=HGWなし=RAの/64のほう。DHCPv6の/56ではない

ルーター選定

長い道のりがあった。要件としては

  • 回線のスペックを生かしたいので1Gbpsフルで出ること
  • いろいろ細かいところをいじって遊べること
  • 常時運用して邪魔にならないサイズ

コストだけだと市販ルーターにOpenWrtを焼くのが最安なのだが、それだと1Gbps出ないらしい。ハードウェアオフローディングが前提だがOpenWrtだとその辺うまく扱えないとかいろいろな理由により最適化が進んでいないらしい。基本市販ルーターに乗っているチップはスループット要件的には全滅である。そんなこんなでこの辺が候補になった。

  • Raspberry Pi 4 Model B 10000円くらい
  • NanoPi R4S 8000円くらい
  • Celeron J1900を積んだSBC 15000円くらい

価格はすべてケースやらアダプタやらいろいろ込みの概算。NanoPiが最安だがサポート体制もドキュメントもあんまりなさそうなのが難。RasPi 4は結構良いんじゃないかとおもう。Celeron JはいわゆるAtomベースのクアッドコアでx64が動作し互換性やらドライバやらなんやらで悩むことがほとんどないであろうことが約束されているのがうれしい。

とここまで調べたところで、友人がLattePandaを買っていたことを思い出した。Atom Cherry Trailのクアッドコアで性能十分だろうと借りたらLattePanda AlphaでCore m3でオーバースペックすぎるといううれしい誤算がありスタート。

UbuntuにするかOpenWrtにするか悩んだがとりあえずUbuntu Server 20.04でスタート。どっちがいいかは両方は試していないので謎。ルーター用じゃないディストロ使うとよくもわるくもルーターっぽい設定方法はできない

LANポートが1つしかなかったのでUSB 3.0-GbE変換をかった。これはなんでもいい。激安は主にRealtekかASIXらしいがx64 Ubuntuではどっちも無設定で動くし当然1Gbpsでる(OpenWRTの場合はどちらか見極めて別途ドライバインストールが必要らしい)

えらばなかったルーター

最近のはv6やらv4 over v6やらもうまいことハードウェアオフローディングしたりしていてまあまあ高速らしい。完全にオーバーヘッドでなくなっているかは不明ですが手持ちのより速いことは間違いない。機能と安定性はバラバラだったり、JPNE 固定IPは対応していなかったり対応していてもなぜかポート開放できなかったりととにかく機能不足がすごい

  • EdgeRouter X

手持ちで持っているがMAP-E非対応なのと単なるv4 over v6のカプセル化でもハードウェアオフローディングに対応できないのでCPUの能力不足で200-300Mbpsくらいしかでないということで除外

40000円也。富豪~。とはいえ自分がこうやって苦労したようなことが公式ドキュメントみながらササっとできるので金で時間と性能を買えるのはすごい

構成

IPv4, IPv6のデュアルスタックにしたい。したいんだがこれはつまり両方別に設定しつつたまに共通になったりあれこれする魔境である。めんどうったらありゃしない

IPv4

ルーターにDHCPv4サーバーを立ててやります

IPv6

ルーターはRAを中継するだけ。いわゆるL3スイッチに近い(けどちょっと追加で仕事する)

  • radvdでRAを中継
  • radvdで追加で設定できるのでnameserverも書いておく(1.1.1.1サービスのv6アドレスである2606:4700:4700::1111とか)
  • RAのためのICMPv6をいい感じにするのにndppdとかいうやつもいれる
  • DHCPv6は一切使わない

DEV (デバイス名)

ネットワーク周りではdevはdeviceの略称です。最初何?ってなった

  • enp2s0: LAN側NIC
  • enx18ece795bb0b: WAN側NIC
  • ip6tnl0: 使うとうまく動作しない呪われたdev。勝手にできるしなんなのかわからない。ネット情報だとこの名前使ってるやつあるが使うとはまるのでやめましょう
  • ipip6t0: ip4ip6トンネル。↑ではまったので名前変えたやつ

設定

設定はsystemd-networkdでやり、カバーできないところをipとiptablesでやりました。これがベストなのかは謎

apt purge network-manager netplan.io ifupdown

どっかで見かけてビビったんですがこれは初手としてやったほうがいいです。Ubuntuネットワーク管理するやつが多すぎてこいつらが勝手に動作するとわけわからなくなるのでpurgeは基本。systemd-networkd一本でいきましょう

apt install radvd npdpp isc-dhcp-server
$ cat /etc/systemd/network/10-enp2s0.network
[Match]
Name=enp2s0

[Network]
LinkLocalAddressing=ipv6
Address=192.168.0.4/24
DHCP=no
DNS=1.1.1.1
DNS=8.8.8.8

$ cat /etc/systemd/network/10-enx18ece795bb0b.network
[Match]
Name=enx18ece795bb0b

[Link]
RequiredForOnline=yes

[Network]
DNS=2606:4700:4700::1111
IPv6AcceptRA=yes
DHCP=no
Address=240b:10:xxxxxxxxxxxxxxx/64
Tunnel=ipip6t0

$ cat /etc/systemd/network/12-ipip6t0.network
[Match]
Name=ipip6t0

[Network]
IPForward=ipv4

[Route]
Destination=0.0.0.0/0

$ cat /etc/systemd/network/ipip6t0.netdev
[NetDev]
Name=ipip6t0
Kind=ip6tnl

[Tunnel]
Mode=ipip6
Local=240b:10:xxxxxxxxxxxxxxxxx
Remote=2404:9200:225:100::64
DiscoverPathMTU=yes
EncapsulationLimit=none

$ cat /etc/radvd.conf
interface enp2s0
{
    AdvSendAdvert on;
    prefix 240b:10:xxxxxxxxxxx::/64
    {
        AdvOnLink on;
        AdvAutonomous on;
    };
    RDNSS 2606:4700:4700::1111 2001:4860:4860::8888
    {
    };
};

$ cat /etc/ndppd.conf
proxy enx18ece795bb0b {
   router no
   rule 240b:10:xxxxxxxxxxx::/64 {
      iface enp2s0
   }
}

$ cat /etc/dhcp/dhcpd.conf
# default
default-lease-time 600;
max-lease-time 7200;
ddns-update-style none;

authoritative;

subnet 192.168.0.0 netmask 255.255.255.0 {
  option routers 192.168.0.4;
  option domain-name-servers 1.1.1.1, 8.8.8.8;
  range 192.168.0.100 192.168.0.199;
}

$ cat /etc/sysctl.conf
net.ipv6.conf.all.accept_ra = 1
net.ipv6.conf.enx18ece795bb0b.accept_ra = 2
net.ipv4.conf.all.forwarding=1
net.ipv6.conf.all.forwarding=1
net.netfilter.nf_conntrack_tcp_timeout_established = 3600

設定は試行錯誤しながらやったのでなんか結構不要な設定が混じってそうな感がある。参考程度にお考えください。あとMAP-Eのアドレス計算はググってください。

以下はシェルスクリプト。systemdとかで起動時に自動設定されるようにしたほうがいいやつですね。まだやってない

#!/bin/sh
set -ux

# https://qiita.com/s_ponta/items/5652a7be49198288ae61
RA_RANGE='240b:10:xxxx:xxxx::/64'
BR='2404:9200:225:100::64'
CE='240b:10:xxxxxxxxxxxxxxxxxxx'
IP4='xx.xx.xx.xx'
PSID='xxx'
LANDEV='enp2s0'
WANDEV='enx18ece795bb0b'
TUNDEV='ipip6t0'

# udevadm info --export-db
echo 'on' > /sys/bus/usb/devices/1-7/power/control

# no way to config this from systemd-networkd https://github.com/systemd/systemd/issues/928
ip -6 r replace $RA_RANGE dev enp2s0 tab 1
ip -6 rule add from all tab 1 priority 1000

iptables -t filter -F
iptables -t nat -F
iptables -t mangle -F

rule=1
while [ $rule -le 15  ] ; do
  mark=`expr $rule + 16`
  pn=`expr $rule - 1`
  portl=`expr $rule \* 4096 + $PSID \* 16`
  portr=`expr $portl + 15`
  iptables -t nat -A PREROUTING -m statistic --mode nth --every 15 --packet $pn -j MARK --set-mark $mark
  iptables -t nat -A OUTPUT -m statistic --mode nth --every 15 --packet $pn -j MARK --set-mark $mark

  iptables -t nat -A POSTROUTING -p icmp -o $TUNDEV -m mark --mark $mark -j SNAT --to $IP4:$portl-$portr
  iptables -t nat -A POSTROUTING -p tcp -o $TUNDEV -m mark --mark $mark -j SNAT --to $IP4:$portl-$portr
  iptables -t nat -A POSTROUTING -p udp -o $TUNDEV -m mark --mark $mark -j SNAT --to $IP4:$portl-$portr
  rule=`expr $rule + 1`
done

iptables -t mangle -o $TUNDEV --insert FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu

いろんなサイトのコピペになってる

ip -6 r replace $RA_RANGE dev enp2s0 tab 1
ip -6 rule add from all tab 1 priority 1000

こはちょっと大事でして、systemd-networkdだと現状こういうサブネット全体のmetric (ルーティング優先度、低いほうが優先される)を指定する方法がないのでRAなどでつけられたデフォルト優先度に負けないよう上書きしたい場合は手動でやる必要があります。この例だとこのsubnetは標準だとWAN側NICアサインされてしまうのをLAN側NICに戻しています。このサブネットWAN側LAN側両方で使われているのがややこしいというかv6っぽいというか。しかし自分とこでレンジ切り出して再配布できるDHCPv6のほうがらくってのはよくわかりますね。NTT~。ということで最初はmetric変更したrouteを追加していたんですが上位概念としてtable/ruleというのがあるのでそっちで設定したほうが確実そうということでそれをやってます。priorityはtableのpriorityですね。標準のmainは32000くらい。

設定後はこうなる

$ ip -6 rule
0:      from all lookup local
1000:   from all lookup 1
32766:  from all lookup main

$ ip -6 route list tab 1
240b:10:8ac1:e800::/64 dev enp2s0 metric 1024 pref medium

$ ip -6 route list tab main
::1 dev lo proto kernel metric 256 pref medium
240b:10:xxxxxxxxx::/64 dev enx18ece795bb0b proto kernel metric 256 pref medium
240b:10:xxxxxxxxx::/64 dev enx18ece795bb0b proto ra metric 1024 expires 2591640sec pref medium
240b:10:xxxxxxxxx::/64 dev enp2s0 proto ra metric 1024 expires 86177sec pref medium
fe80::/64 dev ipip6t0 proto kernel metric 256 pref medium
fe80::/64 dev enx18ece795bb0b proto kernel metric 256 pref medium
fe80::/64 dev enp2s0 proto kernel metric 256 pref medium
default via fe80::xxxx:xxxx:xxxx:xxxx dev enx18ece795bb0b proto ra metric 1024 expires 1440sec pref medium

余談ですがipもiptablesもv6とv4で別々に表示されるしv6はオプションつけないと表示されないから注意だ!

速度

適当にはかったとき、v4で600Mbpsくらいは出ている。いままでは200-300Mbpsが限度だったのでルーターの性能によるオーバーヘッドはほぼ消えたといっていいんじゃないだろうか f:id:yayugu:20210217012511p:plain

課題

  • ↑で手動で設定したmetricがなにかのはずみに消えてしまうことがあり、謎。なくなるとv6は疎通しなくなるがv4は通じるので気づきにくかったり
  • セキュリティー。ザルというか無設定なんですがどうなんだろう。v6アドレスの設定方法がどうなっているか調査したりとか、パケット解析してなんか弾くとかやったほうがいいのかね。でもv6では全部グローバルアドレスもっててincoming受け付けられるというのはまっとうなことでは
  • UPnPとか。MAP-Eでどこまで対応する価値あるかはおいといてやりたいときに。可能なportであればできたほうがいい

  • enひかりっていう光コラボのサービスがあるんですが、MAP-Eと固定IPオプションをつけると月3300+180+700=4180円でフルのv4アドレスを持つことができます。これで80やら443やらでサーバー公開できるね!!ということでそれをやりたいと思います。MAP-E + 固定IPオプションは実際にはJPNEの固定IPサービスになるのでごちゃごちゃやる必要があるMAP-EともおさらばできDS-Liteくらいの設定でいけますしセッションが増えた時のポート枯渇にも悩まされなくなります。よさそう!
  • めちゃくちゃ高性能なやつでやってしまったのでどこまで低性能、低価格なルーターで同じスループット出せるかはチャレンジしてみたい。あと一分のチップは最近OpenWrtでハードウェアオフローディング対応したらしいので、市販ルーターのソフトウェアでやっているv4用のハードウェアオフローディングをv6で無理やり使えるようにするハックとかを自分でやるのはおもしろそう(大変そう)

参考文献

https://qiita.com/kakinaguru_zo/items/2764dd8e83e54a6605f2 https://qiita.com/s_ponta/items/5652a7be49198288ae61 https://quintrokk.subness.net/?p=2003 https://gato.intaa.net/archives/25972