OpenWrt 22.03/23.05 で MAP-E のポートセット全部使う
MAP-E(OCN バーチャルコネクト, v6 プラス) のポートセットを活用するためのルール設定を OpenWrt 22.03 以降の新ファイアウォールで使えるようにする。
また後述する方法は OpenWrt 23.05 でも使えることを確認した。
事の発端
怪レい箱
前から気になっていた SFP+ が付いている怪レい箱 を遂に購入した。
スペックやレビューは既に先駆者がおられるので割愛するが、ネットワークインタフェースは Intel I225(2.5Gbps)3 口と Mellanox ConnectX-3(SFP+, 10Gbps)2 口という、ルーターを作れと言わんばかりの布陣になっている。公式サイトによると 1Gbps x 1 と 2.5Gbps x 2 ということになっているし、Q&A にもなんで全部 2.5Gbps じゃないん?という項があったが、実際は 2.5Gbps x 3 になっているようだ。
新しい箱でルーターを組むにあたり、せっかくなら OpenWrt の新しいバージョン 22.03 で組むことにした。
新しいファイアウォール
OpenWrt 22.03 の目玉の一つに nftables をベースとした新しいファイアウォール(fw4)がある。nftables は iptables よりも性能が向上しているらしい[1] ので、活用できるならしたいところ。
しかし一方で、従来の iptables をベースとしたファイアウォール(fw3)で使用されていた、MAP-E(OCN バーチャルコネクト, v6 プラス)のポートセットを活用するためのルール設定がそのまま通用しない。実際、LuCI から「手動設定ルール」タブが消滅しており、/etc/firewall.user
も削除されている。そのため、fw4 に合わせて何かしらの修正が要る。
OpenWrt のインストールと MAP-E の設定(省略)
OpenWrt のインストールをして MAP-E のトンネルを張り、とりあえず IPv4 と IPv6 それぞれのウェブサイトに繋がる(ただし先頭のポートセットしか使用されない)ようにするまでの手順は、OpenWrt 21.02 の頃と同じなので割愛する。先駆者のブログなどを参照してほしい。ここではその時にハマりそうなポイントなどを紹介する。
- R86S は Intel CPU の PC と同じなので x86_64 用のイメージ
generic-ext4-combined.img.gz
か EFI ブートをするのであればgeneric-ext4-combined-efi.img.gz
をダウンロードする。解凍した後、rufus なりdd
コマンドなりで、eMMC なり TF カード[2]なり NVMe SSD なりに書きこめばブートできる。 -
map
パッケージをインストールした後、直ぐにインタフェースを作成しようとしても、プロトコル一覧にMAP / LW4over6
が現れない。一度再起動する必要がある。 -
map
やluci-i18n-firewall-ja
などのパッケージに iptables のものっぽい dependency がある。この辺りは fw4 対応が追いついてないのかな。 -
Local address not yet configured!
気にしなくてよさそう。 - デフォルトではトンネルデバイスの MTU が 1280 に設定されているので環境によっては変更するとよい(後述)。
-
/lib/netifd/proto/map.sh
のファイアウォールまわりの記述が fw4 に対応してないという噂[3] もあるが、今のところそのままでも問題ない模様。
ポートセットを全部使う設定
先述の通り、従来の fw3 で使用されていた、MAP-E(OCN バーチャルコネクト, v6 プラス)のポートセットを活用するためのルール設定は、そのままでは fw4 では通用しない。とはいえ、「トンネルを通じて出ていこうとするパケットに対し順番にマークをつけ、そのマークに応じてポートセットを振り分ける」というルールを追加すればよいという点に変わりはない。
fw4 では、ルールを記述した nft ファイルを /etc/nftables.d/
以下に置くことで、ルールを登録することができる。だたこれだと inet/fw4
テーブル下のチェーンとして登録されるためか、うまく機能しなかった。また、IPv4 のパケットについて扱えばよいので、ip
のテーブルにしたい。そこで、fw3 の時のような手動設定ルールとしてではなく、同等のルールを書くスクリプトをローカルスタートアップに置くようにした。
要するに以下のどちらか好きなほうのスクリプトを適当なディレクトリに配置して実行パーミッションを付けたうえで、以下のように [System]-[Startup]-[Local Startup] に追記する。
# Put your custom commands here that should be executed once
# the system init finished. By default this file does nothing.
+ /etc/mape_setup_rule.sh
exit 0
肝心のスクリプトは以下の通りだが、生成されるルールは fw3 の頃のルールとはいくつか見栄えが異なる。
まずは、パケットマークを付けるルールが一つだけになっている。これは nftables のルールでは、「ナンバジェネレータの値をポートセットの個数で割った余り(+ オフセット)をそのままパケットマークとして設定する」という記述が可能だからである。
次に、パケットマークごとのジャンプ先チェーンの連想配列を用いていて、ジャンプ先チェーンで SNAT している。従来は、パケットを処理する際にも毎回ルールを舐めていたわけで、このようなところをまとめられるのも nftables の高速化の秘訣の一つなのだろうか。ただ、このようなルールにすると、[Status]-[Firewall] に大量のチェーンが表示されて見づらいので、気になる場合は map 使わない版を使用してほしい。
またルールにヒットしたパケットの数や合計サイズを確認するには、 nftables ではルール中に counter
を明記しておく必要がある。しかし、ポートセットに分散させる処理自体には不要なので、最初にポートセットにパケットがちゃんと分散している様子を確認した後で削除しても構わない。なお、各ルールはセットを用いて異なるプロトコル(TCP, UDP, ICMP)についてひとまとめのルールになっている。プロトコルごとのパケットカウントを観察したい場合は、それぞれのルールに分けてもよい。
#!/bin/sh
IP4=例の計算機で計算した IP アドレス
TUNDEV=プロトコル MAP で作成したインタフェースの名前
PSID=例の計算機で計算した PSID
PREFIX=1024 # v6 プラスなら 4096
BLOCKS=63 # v6 プラスなら 15。
# BLOCKS をあえて小さい値にして残りを自宅サーバなど用ポートにするのもアリ
nft table ip mape_nat
nft delete table ip mape_nat
nft add table ip mape_nat
nft add map ip mape_nat chain_map { type mark : verdict \; }
nft add chain ip mape_nat POSTROUTING { type nat hook postrouting priority 100 \; }
nft add rule mape_nat POSTROUTING oifname map-$TUNDEV meta l4proto { tcp, udp, icmp } mark set numgen inc mod $BLOCKS offset 0x11 counter
nft add rule mape_nat POSTROUTING oifname map-$TUNDEV meta mark vmap @chain_map;
for r in `seq 1 $BLOCKS ` ; do
mark=$(( r + 0x10 ))
port_l=$(( r * PREFIX + PSID * 16 ))
port_r=$((port_l + 15))
nft add chain ip mape_nat mape_ports$r
nft add rule ip mape_nat mape_ports$r meta l4proto { tcp, udp, icmp } counter snat to $IP4:$port_l-$port_r persistent
nft add element ip mape_nat chain_map { $mark : goto mape_ports$r }
done
#!/bin/sh
IP4=例の計算機で計算した IP アドレス
TUNDEV=プロトコル MAP で作成したインタフェースの名前
PSID=例の計算機で計算した PSID
PREFIX=1024 # v6 プラスなら 4096
BLOCKS=63 # v6 プラスなら 15
# BLOCKS をあえて小さい値にして残りを自宅サーバなど用ポートにするのもアリ
nft table ip mape_nat
nft delete table ip mape_nat
nft add table ip mape_nat
nft add chain ip mape_nat POSTROUTING { type nat hook postrouting priority 100 \; }
nft add rule mape_nat POSTROUTING oifname map-$TUNDEV meta l4proto { tcp, udp, icmp } mark set numgen inc mod $BLOCKS offset 0x11 counter
for r in `seq 1 $BLOCKS ` ; do
mark=$(( r + 0x10 ))
port_l=$(( r * PREFIX + PSID * 16 ))
port_r=$((port_l + 15))
nft add rule ip mape_nat POSTROUTING oifname map-$TUNDEV meta l4proto { tcp, udp, icmp } counter mark $mark snat to $IP4:$port_l-$port_r persistent
done
トンネルデバイスの MTU について
LuCI でトンネルインタフェースの [Advanced Settings]-[Use MTU on tunnel interface] 欄に書けばよい。[devices]タブを見に行くとトンネルデバイスの MTU に反映されているのが確認できる。
先述の通り、トンネルデバイスの MTU は /lib/netifd/proto/map.sh
によって 1280 に設定されている。LuCI の [Network]-[Interfaces]-[Devices] から設定するとうまく通信できなくなるようなので、以下の行を編集するとよい。値は書く環境に応じて要調整。
proto_add_tunnel
json_add_string mode ipip6
- json_add_int mtu "${mtu:-1280}"
+ json_add_int mtu "${mtu:-1460}"
json_add_int ttl "${ttl:-64}"
json_add_string local $(eval "echo \$RULE_${k}_IPV6ADDR")
めでたしめでたし
各ポートセットへ流すチェーンに分散しているようだ。ニチバンのページも問題なく開ける。
冒頭の R86S をルータにしてスピードテストした結果。以前よりこのくらい出ていて、R86S のパワーが足りずに足を引っ張るということはなさそう。
追記
2023/08/06:
- 23.05-rc2 でも同様の方法が使えることを追記
- ルール投入スクリプトでトンネルデバイス名(
map-
で始まる)ではなくトンネルインタフェース名を指定すればよいように修正
2024/05/03:
- 23.05 でも同様の方法が使えることを追記
- MTU の設定を修正
関連記事
- OpenWRT 22.03でIPoE(OCNバーチャルコネクト)接続する|solstice|note
- Ubuntu / Debian でIPv4 over IPv6 (OCNバーチャルコネクト, v6プラス), systemdによる設定, ルーター化, VPNおよび自宅サーバー可能な固定グローバルIPv4アドレス - Qiita
- NanoPi NEO3をv6プラスのルーターにする systemd-networkd + nftables - がとらぼ
- man nftables 日本語訳 - Qiita
- 8.6.2. nftables での名前付きマップの使用 Red Hat Enterprise Linux 8 | Red Hat Customer Portal
Discussion