Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
34
Help us understand the problem. What is going on with this article?
@s_ponta

v6プラスにつなぐルーターをラズパイで自作する

More than 1 year has passed since last update.

モチベーション

 最近家のネットワーク環境がずいぶんと遅くなり、光回線を引いているはずなのに回線速度が<1Mbpsとかになることが頻繁にあり「光ってなんだったっけ」みたいな気持ちになっていたのだが、調べてみるとPPPoEでのボトルネック問題である可能性があるらしく、IPoEにすれば劇的に改善する場合があるとのこと。

 幸い我が家のルーター(WN-AX1167GR2, IO-DATA)はv6プラス対応ということだったのでプロバイダ(ANDLINE)に電話してみると、サクッと2~3時間後には開通して速度が常時200Mbpsオーバー(調子よければ350Mbpsとか)になり、動画サイト閲覧はもちろんのこと、自宅サーバー(CentOS7)のyum updateとかも爆速、gitlab-ceのアップデートファイル700MBとかをものの数十秒でダウンロードできるようになった。
 dockerイメージもpullし放題で実に最高なのだけれど(補足のMTU問題を解決するまでは不通だったが)、このルーター、ポート開放はv6プラスに対応していませんみたいなことが説明書に書いてある。あとIPv6でのアドレス取得にはルーターアドバタイズメント(RA)の機構が重要で、こいつがこれまで使っていたDHCPと競合してローカルネットワーク用DNS情報配布とかに影響するのだけれど、その設定が存在しなかった。ただただv6プラスをとりあえず使えるだけ。つまりとりあえずネットサーフィンして高速通信を楽しむ分には最高だけど、お家でIT系DIYをするとなった途端にいろいろと壁にぶつかってしまう。
 一体他の人々はどうやっているのだろうと調べると、このあたりまで細かく設定する人はYAMAHAのRTX830とかRTX1210とかちゃんとした業務用ルーターを買ってたりしていて、流石にそこまでお金をかける気にはなれない。一方、頑張って自作ルータを作っている人もちらほらいる。自作ルーターで検索すると一番多かったのはPPPoEと併用パターンだったが、もう低速の世界に帰れない体になってしまったので、これらは除外してIPoEだけでなんとかしたい

 今回これらの記事の内容をひっかき集め、Raspberry Piを使って自分なりにv6プラスのルーターを作ったので、その内容をまとめてメモとして残しておく。本当は自宅サーバーへ外部からアクセスするためのポートフォワード設定までやりたかったが、このルーター作成に数週間以上かかってしまって一部忘れつつあるので、一旦v6プラスルーター化するところまでの覚えている範囲を書く。
かなり試行錯誤しながら行ったり来たりしつつ作業したので、もしかするとどこか抜けていてこの通りやっても動かないかもしれない。なのでなにかあればコメントしていただけると、他の人への共有という意味だけでなく自分自身思い出すためにもありがたい。ネットワーク系に手を出したのが初めてということもあるので、いつかの自分のために設定全体の本質的な部分以外の、個々のコマンドのメモとかも記載している。冗長なので知っている人は適宜読み飛ばしてもらいたい。

最終的なゴール

ラズパイルーターネットワーク構成.png

 図に最終的なゴールを示す。v6プラスは登録商標やらなんやら書かれているが、技術的にはMAP-Eという技術を使っている。IPv6はIPv6のまま、IPv4はIPv6にカプセル化してIPv6上の通信として通信を行うのがIPv4 over IPv6(ないしIPoE)で、それを実現する技術MAP-E, DS-Liteの2つの手法のうちMAP-Eを今回の場合は用いる。どちらになるかは契約しているプロバイダ次第。とはいえv6プラスならMAP-Eのはずだ。

ルーター(上図のraspberry pi 4)がやらなければいけないことは次の事項だ。

ルーターの設定事項

  1. WAN側NICでIPv6アドレスを取得する
  2. WAN側NICで受け取ったRAをLAN側に流し、ローカルなネットワークにつながった他の機器もIPv6アドレスを取得させる
  3. WAN側NICにカスタマーエッジ(CE)アドレスを設定し、ボーダーリレー(BR)へとIPv4トンネルを掘り、IPv4の接続を確立する
  4. LAN側NICをデフォルトゲートウェイにするようDHCPでLAN内に情報を流し、LAN側NICに来たパケットをボーダーリレーへのトンネルに転送する

 まず前半1. 2.はシンプルにIPv6のアドレス取得方法に関するもので、これはIPv6一般に成立する話だ。後半3. 4.で出来上がったIPv6のネットワーク上にIPv4のネットワークを作成する。LANのIPv4は通常のIPv4のネットワーク作成となんら変わらないが、これをインターネット上のIPv4につなぐために、ボーダーリレー(BR)と呼ばれるノードに接続する必要がある。BRがIPv4とIPv6のアドレスを持っていて、こちらとしては自前のルーターのWANのIPv6からBRのIPv6に接続し、これをトンネルとしてBRのIPv4からIPv4のインターネットにつなぐことでIPv4に接続できる、という格好になる。なのでトンネルを無視してIPv4だけ見れば、BRまで含めた一つがこれまでのルーターの役割のようになっていて、個々のユーザーに割り振られるIPv4のグローバルIPはBRのアドレスということになる。BRが持っているIPv4のバリエーションはそんなに多くないので複数世帯で共有する必要があり、そのためにIPv4のポート割り当てなどに制約/ルールが生じていて、そのあたりのルールがMAP-Eということのようだ(序盤だけ読んで放置してしまったが、詳しくはRFC 7597が原典)。どう設定するのかは後述。

 そんな感じなのでv6プラスだとポート制約が云々という話もあるが、BR経由のIPv4が原因でv6ネイティブな接続は恐らく影響はなく、ルーター部分で適切にポート開放してDNSにAAAAレコードを設定しておけば自分だけが使う分には問題なさそうだ。トンネルは好きに張ればよさそうなので、どうしてもIPv4でウェルノウンポートが欲しければ、AWSなりGCPなりで適当なサーバーを立ててオレオレBRみたいなことをすれば一応可能ではあるようにみえる。
(実際そういう話もあるみたいだ さくらのVPS で IPv4 over IPv6ルータの構築。とはいえ帯域とか通信量に対するコスト制約とかいろいろ考えないといけないと思うが。)

 長々とごたくを書いたが上の4つが出来ればラズパイがルーターとして動作するはずだ。一応ただ貫通させるのではなく、内から外へは通信を許し、外から内はフィルタリングする程度のことはルーター部で行うことも目標とする。これはセキュリティ対策をそれなりに施してある通常のPCはともかく、一応ネットワークにつながっているIoT的なあれやこれやを保護するためだ。これらを1個ずつ順に見ていく。

IPv6アドレスの取得

 まずWAN側デバイスでのIPアドレスの取得から。そもそもラズパイにはイーサネットのコネクタ(RJ45)が1個しかついていない。これではルーターになりようがないので、USBでもう一本コネクタを生やす。今回購入したのはBuffaloのLUA4-U3-AGTE-BK。購入基準は

  • それなりに安い値段
  • USB3.0/1Gbpsに対応
  • あまり廉価すぎて性能がでないという不安のないもの

を勘で選んでいる。ラズパイは以前から使っているRaspberry Pi 4の4GBモデルを使った。

 まずは一旦USB3.0ポートに差し込む。特になんなくデバイス自体は認識されるはずだ。デバイスが認識されているかどうかはip aでのリストに今までなかったインターフェースがあるかどうか見ればいい。分からなければ抜き差ししてチェック。

 認識されていればこれに光回線終端装置(ONU)からのLANケーブルを指せばIPv6網につながるはずではあるが、いきなりは指さない。というのは、これまで使ってきていたファイアウォールの設定が、繋がっているものはすべてローカルという前提で組んでいるため、単にポートだけ開放していると思ってないところでWAN側から繋がってしまう可能性があるからだ。そこでこれらの設定を先に書き換えておく。

sudo ufw status numbered

で現在自分が設定しているものを確認する。このうち、例えば192.168.X.Xからの接続は許可するといった類はルールにローカルの制約が既に入っているため問題ないが、例えば

To                      Action          From
--                      ------          ----
53/tcp                  ALLOW IN        Anywhere

みたいなざっくりポート開放しているものがあれば、該当するものを削除して

sudo ufw allow in on eth0 to any port 53 proto tcp

としてeth0のネットワークインターフェース(NIC)のみで許可するようにしておく。

ufw allowメモ
 on NIC名でそのNICだけにルールが適応される。inが入力、to XXXXがアドレスXXXXに対して、port numnum番のポートについて、proto ppプロトコルについての意味。出力の設定があればoutを使えばいい。接続元情報が必要であればfrom XXXXto節の前に入れるといい。XXXX192.168.0.0/255.255.255.0等が可能。
 既存のルールを削除するにはsudo ufw status numberedで確認したルール番号を、sudo ufw delete numnumに指定して削除する。削除するたびに番号がずれるので毎回sudo ufw status numberedで確認するか、番号の大きなものから順に削除すれば番号のずれを気にしなくて済む。

 記載し忘れていたが、元々設定済みのほかのアプリケーションに変更を加えたくないので、ラズパイにもともとついていたNICことeth0をLAN側NIC、USB経由で新しく生やしたNICenxAABBCCDDEEFFをWAN側NICとする。もちろんAABBCCDDEEFFは各々変わる文字列。ip anmcli d(インストールされていれば)でチェックする。
あとIPv6を有効化するので、/etc/default/ufwの中のIPV6=yesIPV6=noになっていないかチェックしておく。

 後で転送周りの設定をするが、ここまで出来たら一旦IPv6が取得できることを確認する。既存のルーター(今はIO-DATAのルーター)のWANを引き抜き、ラズパイのWANに接続する。また既存ルーターの設定画面から、ルーターモードではくAPモードで動作するように変更しておく。ip aでアドレスを確認し、WAN側アダプタにfe以外の2から始まるIPv6アドレスが割り振られていればOK。

RAを利用してほかのデバイスにIPv6アドレスを与える

 IPv6ではIPアドレスを取得するためにルーターアドバタイズメント(RA)というものを利用してアドレスの割り当てを行う。ルーターのWAN側もネットワークの側からRAの情報を受け取ってIPアドレスを取得しているのだが、LAN内の他のデバイスがRAの情報を取得するにはルーターがLAN側にRAを流す必要がある。幸い、そのためのアプリケーションであるradvdaptでインストールできる。

sudo apt install radvd

 radvdの設定は/etc/radvd.confに記述する (なければ作る)。 細かい設定の意味はこちら(radvd.conf(5) - Linux man page)を参照。

interface eth0
{
    AdvSendAdvert on;                    # RAを送る
    AdvManagedFlag on;                   # MフラグをON (後述)
    AdvOtherConfigFlag on;               # OフラグをON (後述)
    MinRtrAdvInterval 30;                # UnsolicitedRA送信間隔の最小値 (ルーターから勝手に送り付けるRA)
    MaxRtrAdvInterval 100;               # UnsolicitedRA送信間隔の最大値
    prefix 240b:11:aabb:ccdd::/64 # 配布するIPv6アドレスのプリフィックス
    {
        AdvOnLink on;                    # リンクのOnOff状態の配布。正確な意味はよく分からず。
        AdvAutonomous on;                # ステートレスアドレス自動設定にこのプリフィックスを使う(MフラグonのDHCPv6だから不要?)
        AdvRouterAddr on;                # Router(raspi)のアドレスが配布される
    };
};

 interfaceのところのeth0でLAN側インターフェースを指定すると、このインターフェースからRAを流してくれる。prefixの240b:11:aabb:ccdd::/64の部分はWAN側デバイスに先ほど割り振られたアドレスをip aで確認してその前半分を使えばよい。マスクが/64になるか、/56になるかはひかり電話を契約しているかどうかに依存するとのこと。うちの場合はひかり電話の契約はなかったので/64を指定した。

MフラグとOフラグ

 上に書いたMフラグ、Oフラグはそれぞれ

  • DHCPでアドレスを管理するかRAのみを使うか(Managed)
  • DNS等のその他オプション情報をDHCPから配布するかRAの情報を使うか(Other)

のフラグ。(参考1 参考2 参考3)
 このラズパイにはisc-dhcpをインストールしていてこれまでこちらで管理していたので、ipv6情報もすべてこちらで管理するためにどちらのフラグもonにしておく。DHCPを使うつもりがなければ両方offで問題ない。仮にDHCPを使わなくてもRAの機構にアドレス自動割り当てが入っているので、DHCPを用意しないからと言って静的に割り当てる必要はない。

IPv6のためのフォワード関係の設定

 このradvdを使うにはIPv6のフォワーディングが必要になる。(と見かけたが、後の挙動を見るにそれ自体は必要ない気もする。とはいえどのみちルーター化で必要なのでONにしておこう)。
これを許可するために/etc/sysctl.confnet.ipv6.conf.all.forwarding=1をコメントアウトする。永続化でなく暫定対応として行うならばsudo sysctl -w net.ipv6.conf.all.forwarding=1 (参考)

恐らく不要なはずだがいろいろと苦戦した結果/etc/sysctl.confにはその他下記の記述が今はついていた。(これの参考にした文献が思い出せない...)

net.ipv4.conf.eth0.arp_ignore = 1
net.ipv6.conf.default.accept_ra = 1
net.ipv6.conf.all.accept_ra = 1
net.ipv6.conf.eth0.accept_ra = 1
net.ipv6.conf.all.disable_ipv6 = 0
net.ipv6.conf.default.disable_ipv6 = 0

これとは別にファイアーウォールもフォワードを設定しないとネットワーク的には繋がらないが、ルーティングは後で細かい設定を加えることにし、ufw自体は/etc/default/ufwで、

DEFAULT_INPUT_POLICY="DROP"
DEFAULT_OUTPUT_POLICY="ACCEPT"
DEFAULT_FORWARD_POLICY="DROP"

にしておく。つまりこの段階ではufw的にはフォワードは許可されない。

 またファイアーウォール関係で、RAはアドレス解決などに使われるICMPv6の1種になっているらしく、これが通らないとうまくいかないようなので、ICMPv6が通るように設定しておく。ICMPv6のポートは58とのことなので(参考)

sudo ufw allow in on eth0 to any port 58

を追加する。ちなみにルーターの話ではないが、CentOSfirewalldでデフォルトポリシーをDROPにして設定する場合、fe80::/10ff00::/8の入力を許可するようにしないとIPv6アドレスの取得に影響をきたすようなので注意(【CentOS7】CentOS7でIPv6をFirewalldで真面目に使う【Firewalld】)。

 ここまでやればIPv6のアドレスがLAN側のeth0やその他デバイスにも払い出されているはず。

ndppd

 どこかの記事でradvd以外にもndppdというものも必要だと記載があったので、こちらもインストールしてある。
 ローカルの接続情報をWAN側に教えるプロキシらしく、これがないとWANより先のインターネット側のネットワークから見たときに、LAN側に接続が形成されていることが分からないため、インターネット側からのアクセスが出来なくなるそうだ。sudo systemctl stop ndppdで止めておいてもLANからのブラウジング等には影響がなかったので、恐らく自宅サーバーなどを開放する云々がなければ要らないのだと思う。今回の話では必要ないが、今後のため/etc/ndppd.conf

proxy WAN側NIC名 {
    rule IPv6プリフィックス/64 {
        iface eth0
        autovia no
    }
}

として設定してある。

IPv4のトンネルを張る

 ここの仕組みがなかなか分からず苦戦した。いくつかのサイト、特にがとらぼ NanoPi NEO2をv6プラスのルーターにする 前編と一連のシリーズ(?)に特に感謝したい。

 IPv4のトンネルを掘るためには、BRのアドレスなどいろいろな情報を調べる必要がある。また調べるだけでなく上記のサイトに記載のルールを当てはめていくつか計算する必要もある。あるいは自動計算してくれるサイトを使って計算してもよい。BRアドレス以外だけが手に入るように見えるが、注釈の下に記載されていた情報にBRアドレスも載っている。ただし、BRアドレス以降の値は場合によって異なる値を表示しているようだ。

MAP-Eに必要な情報の計算

 ここからしばらくMAP-Eに必要な情報の計算方法を記載する。既存のルーターでv6プラスが使えて上記の自動計算サイトで情報を入手できる状態にあれば読み飛ばしてしまえばよいが、本当に0からルーターを自作するならこの段階でラズパイからcurlなどを駆使して下記の情報を得るしかないだろう。

 計算に必要な情報は、

  • 割り当てられたWAN側NICのIPv6プリフィックス
  • MAP情報配信サーバーから得られる情報

の大きく分けて2種類だ。このMAP情報配信サーバーの情報が一般にどこにあるか分からなかったが、v6プラス(JPNEという業者が提供している)の場合、https://api.enabler.ne.jp/6a4a89a8639b7546793041643f5da608/get_rules?callback=v6plusから得ることが出来る。最初このURIの中間部分が各々独自のトークンか何かかと思って調べていたのだが、特にユーザーによって変わるわけではなさそうだ。ここに対してラズパイからcurlすると、

$curl https://api.enabler.ne.jp/6a4a89a8639b7546793041643f5da608/get_rules?callback=v6plus
v6plus({"dmr":"2404:9200:225:100::64","id":"hogehogehugahugafoobar","ipv6_fixlen":56,"fmr":[{"ipv6":"240b:10::/32","ipv4":"106.72.0.0/16","psid_offset":4,"ea_length":24},{"ipv6":"240b:11::/32","ipv4":"106.73.0.0/16","psid_offset":4,"ea_length":24},{"ipv6":"240b:12::/32","ipv4":"14.8.0.0/16","psid_offset":4,"ea_length":24},{"ipv6":"240b:13::/32","ipv4":"14.9.0.0/16","psid_offset":4,"ea_length":24},{"ipv6":"240b:250::/32","ipv4":"14.10.0.0/16","psid_offset":4,"ea_length":24},{"ipv6":"240b:251::/32","ipv4":"14.11.0.0/16","psid_offset":4,"ea_length":24},{"ipv6":"240b:252::/32","ipv4":"14.12.0.0/16","psid_offset":4,"ea_length":24},{"ipv6":"240b:253::/32","ipv4":"14.13.0.0/16","psid_offset":4,"ea_length":24}]})

こんな感じでいろいろな情報が降ってくる。"id"のところはなにかよくわからなかったので念のため適当な文字列に差し替えた。
まず最初の"dmr"の部分だが、ここが後で設定するBRアドレスになっている。このBRアドレスがまるで分らなくてあちこち探しまわっていたのだが、上記計算サイトの場合peeraddrにしれっと書いてあるアドレスがこの値と一致した。

 これらの情報と割り振られたWAN側NICのIPv6アドレスから必要な情報を計算する。割り振られたアドレスのプリフィックスを仮に
    240b:11:aabb:ccdd::/64
としよう。ただしaa等々には何か適当な16進数がはいる。ここから自作ルーターのWAN側NICに割り振るべきCEアドレスと、グローバルIPとして利用可能なIPv4アドレスを計算する。上のコマンドの結果の"fmr"部分を見ると、ipv6, ipv4, psid_offset, ea_lengthの組が繰り返し現れていることが分かる。自分のWAN側NICに割り振られたIPv6プリフィックスと頭が一致する組に着目する。今の場合240b:11:aabb:ccdd::/64が割り振られたと仮定しているので、{"ipv6":"240b:11::/32","ipv4":"106.73.0.0/16","psid_offset":4,"ea_length":24}を見ればいい。
 ここでipv6のマスクが32bit, ipv4は16bit, オフセットが4bit, ea長が24bitとあるが、これが変わると以降のルールも変わるようだ。これらが同じなら以降のルールでいいが、もし違っていたらこちらのスライドの12枚目と以降のルールをつき合わせれば恐らく分かるだろう。あるいは上記に記載したRFCの正確な情報を見てもいい。同じスライドにdmrとかfmrが何なのかも書いてある。後日時間があれば別途まとめておきたい。

IPv4アドレスの決定

 先ほどの組の中のipv4アドレスがBRで割り振られるipv4アドレスのプリフィックスだ。プリフィックス以外の部分はWAN側NICのIPv6プリフィックスのaabb部分をaa(16進数)→x(10進数)、bbyと変換して、ホスト部分を決定すればよい。今の場合106.73.x.yがIPv4アドレスとなる。

PSIDの決定

 MAP-Eで割り振られたIPv4のうち、使えるポートを決定するための情報がPort-Set ID (PSID)だ。これはIPv6アドレスのプリフィックスのうちcc部分を10進数に変換すればいい。

CEアドレスの決定

 BRへの接続はWAN側NICで自動取得したIPv6アドレスではなくて、別途計算したカスタマーエッジ(CE)アドレスを指定する必要がある。これまでにまだ出てきていない計算としては、先ほどのIPv4プリフィックス部分だけをコンマ区切りで16進数変換したvv.wwの値が必要になる。今回は"106.73.0.0/16"を例にしているので、106(10進数)→vv=6a(16進数)、73ww=49となる。これらを以下のルールでマッピングする。
   CE address=ipv6prefix:vv:wwaa:bb00:cc00
要はipv4アドレスを16進数にマッピングした後前後に00を付け加え、ipv6プリフィックス16進数版ipv4cc00とすればよい。(もしかするとcc00じゃなくてccddなのだがddが常に00なのでこうなっているだけかもしれない。後でRFCをちゃんと読んでみたい。あと先ほどのスライドのルールと若干異なっているような気がする。)

今回の場合だとCEアドレスは240b:11:aabb:ccdd:vv:wwaa:bb00:cc00となる。(ipv6の記述省略ルールで00vv=vvになるが、別に00vvと書いてしまっても問題ないはず)

利用可能なIPv4ポート番号

 IPv4で利用できるポート番号は、同じIPv4アドレスを複数人で共有する都合上制約が生じる。個々人が使用可能なポートとして割り当てられているポートは先ほどから何度も使っているccを使って計算できる。kは0を除く1文字の16進数、lを0も含む16進数として
   kccl
が利用可能なポート番号の16進数表記だ(kが0を除いているのは恐らくpsid_offset=4だからだと思われる)。なので15×16=240個のポートが利用可能だ。普通はポート番号は10進数表記で割り当てるので、これを10進数に変換すればよい。

ここまでのIPv4トンネルを張るのに必要な情報のまとめ

JPNEの場合の必要情報の取得方法のここまでのまとめを表にしておく。

取得方法
WAN側NCIのIPv6プリフィックス ip aで確認 240b:11:aabb:ccdd::/64
BRアドレス APIレスポンスのdmr 2404:9200:225:100::64
IPv4アドレス IPv6プリフィックスとAPIレスポンスのfmrから合成 106.73.x.y
$(x)_{10}= (aa) _{16} $ $ (y) _{10} = (bb) _{16} $
PSID IPv6プリフィックスから計算 10進数変換したcc
CEアドレス IPv6プリフィックスとIPv4アドレスから合成 240b:11:aabb:ccdd:vv:wwaa:bb00:cc00
利用可能ポート IPv6プリフィックスから合成 10進数変換したkccl (k $\in$ [1,f] , l $\in$ [0, f])

計算した情報をもとにIPv4設定

 先の参考にしたサイトの一連の情報の中にMAP-Eルーターとしての設定をしてしまうスクリプトの記述がある。原典は2chのスレ経由のこちらのサイトのようだ。先のサイトの中では原典に加えて戻りパケットが通るようにencaplimit noneの記述の追加をしているとのことだ。
 ただこのスクリプトそのままでは最終行でエラーが出てしまったので、ちょっとだけ変更を加えた下記のスクリプトを用意した。(コメントに理解した限りの目的を書いたが、理解が正しいかはあまり自信がない)

#!/bin/sh

BR='BRのIPv6アドレス'
CE='CEのIPv6アドレス'
IP4='IPv4アドレス'
PSID='PSIDを10進数に変換したもの'
WANDEV='enxXXXXXXXXXXXX'
TUNDEV='ip6tnl1'

ip -6 addr add $CE dev $WANDEV                    #WAN側NICにCEアドレス割り当て
ip -6 tunnel add $TUNDEV mode ip4ip6 remote $BR local $CE dev $WANDEV encaplimit none     #BRまでトンネルを掘る
ip link set dev $TUNDEV mtu 1460                  #トンネルのMTUを設定
ip link set dev $TUNDEV up                        #トンネルデバイスをONにする

route delete default                              # ラズパイ上のデフォルトゲートウェイの情報を消す
route add default dev $TUNDEV                     # デフォルトゲートウェイをトンネルデバイスに設定する

iptables -t nat -F                                # 既存のNATを削除 以下while中でポートマッピングのルールを設定

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

# 一部通常のMTU設定が効かない(?)パケットのMTUを決める???
iptables -t mangle -o $TUNDEV --insert FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu

 エラーの原因はどうもiptablesでの-m tcpmss --mss 1400:65495の部分で何か読み込もうとしているファイルが足りない(パッケージがインストールされていない?)ことが原因のようだったが、一部のパケットに対するmtuの大きさ(?)を制約するのが重要であり、それは--clamp-mss-to-pmtuで達成できるようだったので消してしまった。あとFORWARD直後に1と記載があったがこれもよくわからなかったので消してしまっている。とりあえず動いているがiptablesを直に触ったのは初めてであまりよくわかっておらず、必要な部分だったのかもしれない。

 このスクリプトを適当な場所に配置して、chmod +x ファイルパスで実行権限を付与した後、実行することでIPv4接続のトンネル自体は掘られる。後はufwのtransferをONにすれば基本的にはインターネットにつながるが、単にデフォルトポリシーをDROPからACCEPTにすると何もかもが筒抜けになってしまうので、最後に簡単なルーティング制御を行う。

IPv4パケットをルーティングする

 まずはラズパイがIPv4パケットを転送できるようにするために、/etc/sysctl.confを開きnet.ipv4.ip_forward=1を設定する。ufwだけでの細かい転送設定のやり方がわからなかったので、iptablesを使って転送許可の設定する。せっかくなので先ほどのスクリプトに下記のようにマージした。

#!/bin/bash

BR='BRのIPv6アドレス'
CE='CEのIPv6アドレス'
IP4='IPv4アドレス'
PSID='PSIDを10進数に変換したもの'
WANDEV='enxXXXXXXXXXXXX'
LANDEV='eth0'                            # LAN側NICの追記
TUNDEV='ip6tnl1'

ip -6 addr add $CE dev $WANDEV
ip -6 tunnel add $TUNDEV mode ip4ip6 remote $BR local $CE dev $WANDEV encaplimit none
ip link set dev $TUNDEV mtu 1460
ip link set dev $TUNDEV up

route delete default
route add default dev $TUNDEV

iptables -t nat -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`

    # PREROUTINGでのマーク付けをLAN側NICからの入力のみとする
    iptables -t nat -A PREROUTING -i $LANDEV -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

    # MARKがついているものは転送を許可する
    iptables -t filter -A FORWARD -p icmp -m mark --mark $mark -j ACCEPT
    iptables -t filter -A FORWARD -p tcp -m mark --mark $mark -j ACCEPT
    iptables -t filter -A FORWARD -p udp -m mark --mark $mark -j ACCEPT

    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



#####################
#####################
# For ipv6
#####################
#####################

ip6tables -t nat -A PREROUTING -i $LANDEV -j MARK --set-mark 17  #LAN側NICからのパケットにipv4マークと競合しないマークをつける。iptablesと独立しているようなので競合してもいいのかもしれない。
ip6tables -t filter -A FORWARD -p tcp -m mark --mark 17 -j ACCEPT #tcpかつマーク付きなら転送する
ip6tables -t filter -A FORWARD -p udp -m mark --mark 17 -j ACCEPT #udpかつマーク付きなら転送する

 これでLAN側からのコネクションだけ通る。あとはほかのデバイスにデフォルトゲートウェイの情報を配るため、isc-dhcpの設定/etc/dhcp/dhcpd.confsubnet記述の中で

subnet 192.168.0.0 netmask 255.255.255.0 {
  range ...
  ...
  option routers ラズパイのLAN側IPv4アドレス
  ...
  }
}

として配布しておく(...で省略部分を表記)。
 一方、ラズパイ自身のLAN側がこのdhcp情報を反映するとデフォルトゲートウェイの情報が競合してしまうようなので、ラズパイ自身のLAN側NICの設定は静的に処理したほうがよいようだ。/etc/network/interfaceを開き、

auto eth0
iface eth0 inet static
    address 192.168.0.XXX
    network 192.168.0.0
    netmask 255.255.255.0
    broadcast 192.168.0.255
    dns-nameservers 192.168.0.XXX
    mtu 1460

iface eth0 inet6 static
    address 今現在割り振られていたLAN側NICのipv6アドレス
    netmask 64
    dns-nameservers 今現在割り振られていたLAN側NICのipv6アドレス
    accept_ra 1

という形の記述にしておいた。XXXはdhcpのレンジ外になるようにしておく。これでゲートウェイ情報は先ほどのスクリプトと競合しないはずだ。

 ここまで出来たらこれらの設定が有効であるかどうかを確認する。まずはIPv6, IPv4の両方がLAN内から使えることをこちらのサイトで確認。またIPv6のポート開放状況のチェックができるサイトIPv4のポート開放状況のチェックができるサイトを利用して、LAN内の自宅サーバーなどに今の設定のままではアクセスできないことを調べておく。
 これらの結果として、内からはIPv4, IPv6のサイトが自由に閲覧出来て、外からのアクセスはブロックされていれば、当初の目的が達成できたということになる。

まとめ

 当初想定よりもかなり長い作業になったが、これでポート設定などを柔軟に変更可能なv6プラス用のルーターのベースが出来上がった。まだポート制御はざっくりセキュア程度の設定で、いろいろな攻撃への対応などはきちんとできていないので、今後ちまちま更新していきたい。
 あと散々苦労した結果、市販のルーターってよく出来てるなあ、などと今更ながら思った次第。もちろん市販のルーターに不満があったからこんなことに手を出してしまった訳ではあるのだけれども。世の中のインフラがどうやって支えられているか、みたいなことに思いを馳せて少し優しい気持ちになれるのも、得られたものとして悪くはないんじゃないだろうか。

 補足

MTUについて

 今回のラズパイルーターは別として、元々使っていたIO-DATAのルーターでv6プラスを使っていた時から、なぜかdocker pullが出来ないという症状がネットワーク内のlinux OSのPCで生じていた。dockerに表示されているエラーをいろいろ調べてみると、どうやらMTUが原因らしいということが最終的に判明した。
 (もともとMTUが何かよく分かっていなかったのだが、)IPv4の場合にはパケットのデータサイズが大きすぎる場合フラグメンテーションによって適当にパケットサイズを分割する機構が入っており、MTUが多少変な値になっていても速度が劣化するだけで接続が出来ないことはないらしい。しかしながらどうもIPv6においてはフラグメンテーションがおこなわれず、大きすぎるパケットは途中で破棄されてしまうようだ。しかもIPv4 over IPv6ではv4パケットをカプセル化してv6にマッピングするためにIPv6視点からみたパケットサイズがデカくなってしまい、デフォルトのMTU=1500ではあっけなくパケット破棄されてしまうため、docker pullが出来なかったようだ。MTUを下げながらdocker pullを繰り返していくとMTU=1400くらいまで下げたところでエラーがなくなった。
 いくらなんでも1400は低すぎる気がするのだが、とりあえず動くので1400にしている。ラズパイルーターに変えてから変更してないので、もしかするとIO-DATAルーターが原因で、今はもうちょっとあげてもいいのかもしれない。

34
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
s_ponta
個人的な備忘録として記録をつけてます。 DIYとか趣味プログラミング的なものONLY。 仕事のことはOpenにするとややこしいので書かない方針。

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
34
Help us understand the problem. What is going on with this article?