OpenBSD で MAP-E (IPv4 over IPv6) を動作させる
コンテンツ
OpenBSD で MAP-E 方式の IPv4 over IPv6 を動作させるためのメモ。
自宅ネットワークは OpenBSD をルータとした IPoE 方式の IPv6 と PPPoE 方式の IPv4 のデュアルスタック環境である。 最近、IPv4 接続の速度が劣化していると感じたため、品質が改善する可能性がある MAP-E 方式へ変更した(結局、有意な差はなかったのだが…)。
OSの標準機能や既存のOSSでは MAP-E 動作(MAP CE 機能)を実現できなかったため Kernel にパッチ をあてソースからビルドした。NAT で使用されるポート番号を MAP-E の port set に制限するオプションを pf(4) に追加した。3ヶ月程度、問題なく動作し続けている。
Mapping of Address and Port with Encapsulation (MAP-E) について
IPv4 over IPv6 を実現する方式の一つ。ユーザルータ(MAP Customer Edge (CE))とISPルータ(MAP Border Relay (BR))の間でIPv4 over IPv6トンネルを構築する。MAP CE では NAT によりIPv4プライベートアドレスをグローバルアドレスに変換し、それを IPv6 にカプセル化する。MAP BR でデカプセル化する。MAP BR でグローバルアドレスは複数のユーザで共有され、ポート番号でユーザを識別する。つまり、MAP CE で NAT する際に使用できるポート番号が制限される。正しいポート番号に NAT しないと返りのパケットが別ユーザにルーティングされてしまう。
RFC 7597 で説明されている。解説は次の文献が分かりやすい:
- 徹底解説 v6プラス の第4章
- プロフェッショナルIPv6 第2版 の第24章
Kernel にパッチをあてる理由
NAT で使用するポート番号を MAP ルールに従い制限する上手い方法がないため。
MAP CE に必要な機能
MAP CE として必要な機能は次の3つである。
- IPv4 over IPv6 トンネル
- IPv4 NAT
- NAT で使用する port 番号制限
OpenBSD は 1. は gif(4), 2. は pf.conf(5) で nat-to
で実現できる。3. で使用できるポート番号を制限しつつ、使用可能なポート番号を全て使えるように設定するのが難しい。
pf.conf
のNATポート範囲の指定
例えば pf.conf
で port 2000:2004
のように一つの区間ならば指定できる。しかし、一般に MAP-E で使用されるポート番号は複数の区間となる。実際、私の環境では63個の区間となった(各区間は16個のポート番号で構成されている)。
この方法を使う場合63個の区間から一つ選び、それをNATで使用することになる。ポート番号が16個しかないのでNATセッションが多い状況では使用できるポート番号が枯渇する恐れがある。
pf.conf
の複数NATルール
使用可能なポート数を増やすために、送信元ポート番号などに基づき63個のNATルールを作成し、それぞれ使用可能なポート区間を指定することも考えられる。しかしながら、各ルールが使用可能なポート番号は16個のままなのでマッチルールやクライアントのリクエストによっては、やはり、ポート番号が枯渇する恐れがある。
ラウンドロビンでマッチするアクションを選ぶようなランダムにルールを選択仕組みがあればある程度は緩和可能かもしれないが、現状、そのような仕組みは pf
にはない。
(ただ、うまくマッチルールを作成すれば、例えば、送信元ポート番号の下位6ビットとポート区間を紐付けるなどでランダムに近い挙動でポート区間を選べれば、この方法で問題ないかもしれない)
追記 [2022-08-21 Sun]
ランダムでルールを適用する probability オプションが存在することに気がついた。このオプションを使いうまく確率を設定すれば、複数のルールからいずれかを適用する、という動作が可能である。一部制限はあるが PF ルールで MAP-E 動作が実現できる。
追記おわり
変更箇所
Kernel に手を入れて NAT で使用するポート番号を制限できるように変更した。設定ファイル pf.conf
で MAP ルールで定まる集合から選択するためオプション map-e-portset
を設定できるようにした。例えば次でPSID offset (a bits の長さ), PSID length, PSID をそれぞれ 4, 6, 20 に指定できる。
|
|
パッチは次で公開している。
MAP-E 動作のための設定例
Kernel にパッチをあてビルド、インストールすることで MAP-E 動作が可能となる。
MAP-E 情報の取得
まず、MAP-E設定に必要な情報(MAP BR のアドレスや PSID など)を次から取得する(このサイト非常に便利で助かった)。
必要なのは次の4つ。
- MAP BR のIPv6アドレス
- MAP グローバル IPv4 アドレス
- PSID offset
- PSID length
- PSID
また、MAP CE の IPv6 アドレスも必要である。
以降、それぞれの値は次の表の通りであるとして説明する。
MAP CE IPv6 address | 2001:0db8:dead::1/64 |
MAP BR IPv6 address | 2001:0db8:beef::1/64 |
MAP global IPv4 | 192.0.2.1 |
PSID offset | 4 |
PSID length | 6 |
PSID | 20 |
IPv4 over IPv6 トンネル
あるインターフェイスに 2001:0db8:dead::/64
のアドレスが設定されており、このインターフェイスを使いインターネットへ IPv6 接続されているとする。
MAP BR へ IPv4 over IPv6 トンネルを作成するには、例えば /etc/hostname.gif0
を次のように編集し sh /etc/netstart gif0
を実行する。
|
|
IPv6ヘッダが40バイトなので MTU は 1460 (=1500-40) に設定する。
MAP ルールに従う NAT ポート選択
次を pf.conf
に追加する。
|
|
一行目は MAP-E とは関係ないが、 Windows が MTU 1500 で通信しようとするので TCP の MSS を 1420 に設定する必要がある。 二行目で MAPルールに従うようにポート選択する。
実装上のメモ
- FreeBSD で MAP-E を有効にするパッチ を参考にした
-
NAT処理を行なう
pf
とpf
の設定を読むpfctl
に変更を加えたpf
関連のファイルはディレクトリsys/net/
の中のpf
がプレフィックスになっているものpfctl
関連のファイルはディレクトリsbin/pfctl
,share/man/man5/pf.conf
,regress/sbin/pfctl
に配置されている
-
NATで使用されるポート番号は
sys/net/pf_lb.c
の関数pf_set_sport
で決定される- この関数は
nat-to
とaf-to
のルール処理時に呼び出される - オリジナルの
pf_set_sport
はポート番号の下限low
と上限high
を定めて、そこからランダムに未使用のポート番号を選択する -
新しい関数
pf_set_sport_range
とpf_set_sport_mape
を加えた- 関数
pf_set_sport_range
はオリジナルのpf_set_sport
と同様に下限と上限からランダムに未使用ポートを選択する -
関数
pf_set_sport_mape
は MAP-E の port-set 設定に従い未使用ポートを選択する- ランダムに a bits を選択し、
pf_set_sport_range
を呼びだすことを繰り返す
- ランダムに a bits を選択し、
- 関数
pf_set_sport
設定に応じてpf_set_sport_range
もしくはpf_set_sport_mape
を実行する
- 関数
- MAP-E port-set のルールは
pfvar.h
にstruct pf_mape_port
として新しく定義しstruct pool
のメンバ変数として追加した
- この関数は
-
pfctl
による設定ファイルpf.conf
の読み取り、出力はsbin/pfctl/parse.y
,sbin/pfctl/pfctl_parser.c
で決定される-
parse.y
は yacc で記述されている- まず
%{
と%}
の間に C の struct の定義、関数宣言 - 次に
%token
,%type
が定義され%%
で終端 - そして 文法 が定義され
%%
で終端 - 最後に C の関数が定義
- まず
- トークンとして
map-e-portset
を追加して値をstruct pf_mape_port
に保存 pfctl_parser.c
でmap-e-portset
情報を出力するように変更-
regress/sbin/pfctl
には成功すると失敗するpf.conf
のルールを出力とともに置く- 例えば
pf1.in
とpf1.ok
やpfail2.in
とpfail2.ok
- 例えば
-
おわりに
Kernel と Base system をビルドするのは数時間かかるので大変である。また、IPv4 のゲートウェイが PPPoE と MAP-E の2つできたので policy routing で使い分けができるようになったが、計測してみたところ、私の環境ではどちらの経路も大差ないようで残念であった。
作成者 Toru Mano
最終更新時刻 2023-01-01 (c70d5a1)