以下で物理ブリッジポートを条件にするin-bridge-portを使うには、ブリッジのuse-ip-firewallを有効にしておく必要があります。デフォルトはnoです。有効にするとブリッジ経由の通信がIPファイアウォールを通り、Bridge FastPathやCPU負荷にも影響するため、機器の処理能力を確認してから設定してください。後述の送信元IPアドレスリストを使う方法では、このブリッジポートの条件は不要です。詳しくはMikroTikのCommon Firewall Matchers and ActionsとBridging and Switchingを参照してください。
以前、自宅のネットワークをあれこれいじっていた頃は、OpenWrtをメインルーターにして、WAN接続からプロキシまで全部任せていました。ただ、この構成は安定性に難があり、何か試すたびに家中のインターネットが落ちてしまいます。何も触っていなくても、プロキシ側の接続に問題が起きると、本来なら直接アクセスできるサイトまで開けなくなりました。ゲーム関連のサービスなど、そもそもプロキシを使う必要のない端末や通信も多く、それらをプロキシ経由にしないための除外ルールまで個別に設定していました。
その後、SurgeのApple TV版が登場したので、Surgeを補助ゲートウェイとして使い、プロキシの役割をメインルーターから切り離すことにしました。
最初の構成
補助ゲートウェイを使う方法はいくつかあります。プロキシが必要な端末のゲートウェイとDNSを手動で変更する、VLANを分ける、メインルーターからDHCPで補助ゲートウェイのアドレスを配る、といった方法です。手動設定では、家族の端末も含めて私が個別の設定を管理し、問題が起きれば対応しなければなりません。そこで最初はDHCPで配る方法を選びました。
DHCPでは、番号の付いたオプションでさまざまな設定を通知できます。ゲートウェイはoption 3、DNSはoption 6で指定でき、通常配る設定を上書きする形になります。プロキシが必要な端末にこの2つを指定すれば、その通信は補助ゲートウェイに向かいます。
ただ、しばらく使ってみると、家族が自分で切り替えられないという問題が残りました。端末に配られる設定はルーターが決めるので、利用者が端末側で簡単に変更できません。プロキシを通さない通常の接続に戻すにも、私を呼ぶ必要がありました。
補助ゲートウェイの接続状態を監視して、切断されたら設定の配布をやめる方法も考えました。しかしDHCPにはリース期間があり、端末が設定を取り直すのは次のリース更新時です。切り替えがすぐに反映されず、復旧にも時間がかかるので、これも使い勝手がよくありませんでした。
2つのWi-Fi
当時、自宅にはAPとして使っているXiaomiのルーターが2台あり、それぞれ別のSSIDを設定できました。そこで、プロキシを通るWi-Fiと通らないWi-Fiを用意すればいいのでは、と考えました。家族は使いたいほうにつなぐだけ。通常の接続に戻したければWi-Fiを切り替えればよく、リースの更新を待つ必要もありません。
一番単純なのは、一方のWi-Fiを補助ゲートウェイにつなぎ、もう一方をメインゲートウェイにつなぐ構成です。ただ、自宅の端末はすべて同じサブネットに置いておきたいと考えていました。そのほうが機器の管理やスマートホーム、LAN内のサービスをそのまま使えます。互いに通信できないネットワークや、間にNATが必要なネットワークに分けたくはありませんでした。
幸い、メインルーターはすでにMikroTikのRB5009Uに替えていました。ルーターらしくルーティングテーブルも扱えるので、BGPやASNで試してきたことを応用できそうです。
構成の考え方
Surgeを動かすApple TVを追加して、ネットワークの出口をもう1つ用意します。ただし、各端末のデフォルトゲートウェイはメインルーターのままです。ファイアウォールにルールを追加し、特定のAPの接続ポートから入る通信や、指定したIPアドレス・MACアドレスからの通信だけをSurgeへ送ります。
別のゲートウェイへ送るというのは、別のルーティングを使わせるということです。そこで専用のルーティングテーブルを作り、Surgeへ送るパケットにマークを付けます。マークの付いたパケットは通常のmainテーブルではなく、このテーブルを参照します。専用テーブルにはSurgeをネクストホップにしたデフォルトルートを1本だけ入れます。
BGPでポリシーに応じてネクストホップを選ぶのと、考え方は同じです。ここでは、その判断基準がパケットの送信元になります。
RouterOSはパケットを段階ごとに処理し、ルールが作用する場所も決まっています。まずは、パケットがどの処理をどの順番で通るかを整理します。
RouterOSのパケットフロー
RouterOSのファイアウォールには複数のチェーンがあります。入ってきたIPパケットは、おおむね次の順番で処理されます。
これは公式ドキュメントのPacket Flow in RouterOSをもとに簡略化した図です。公式にはブリッジ、ハードウェアオフロード、IPsecなども含めた詳しい図があります。ルーティングして転送する経路は、次のように示されています。
この構成では、特にいくつかの処理の前後関係が重要になります。
ルーティングの判断はpreroutingとdstnatの後です。経路が決まる前に、mangle preroutingでマークを付け、dstnatで宛先アドレスを書き換える機会があります。ルーティングテーブルの検索に使われるのは、書き換えた後の宛先アドレスです。
srcnatはルーティング判断の後、パケットが出ていく直前にあります。経路はすでに決まっているので、ここで変更できるのは送信元アドレスだけです。
つまり、今回の受信パケットにルーティングマークを付ける場所は、経路が決まる前のmangle preroutingです。forwardではすでに経路が選ばれているため、そこで付けても間に合いません。
ルーター自身が生成する通信はoutputチェーンを通ります。この場合は最初にルーティング判断があり、その後mangle outputを通ります。公式の図には、さらにrouting adjustmentという処理が描かれています。outputチェーンのmangleでもポリシールーティングを行えるようにするための回避策です。ただし、この記事で扱うのは端末から送られてルーターを通過する通信なので、この部分は使いません。
以下のアドレスとルールはIPv4専用で、IPv6の通信は対象になりません。クライアントがIPv6でも外部に接続できる場合は、別途ポリシーを設計してください。この設定だけでIPv4とIPv6の両方をプロキシ経由にできるわけではありません。
ルーティングテーブル
まず、ルーティングテーブルを作成します。
テーブルの作成時にはfibを指定します。公式のポリシールーティングの例でもこの指定があり、テーブル内のルートを転送用のFIBに反映させます。また、RouterOS v7のルーティングマークには、あらかじめ作成したテーブルを対応させる必要があります。テーブルがなければ、mangleで指定したマークは機能しません。
次に、Surgeを指すデフォルトルートを追加します。
メインルーターは10.10.77.1、Surgeは10.10.77.4、サブネットは10.10.77.0/24です。
これでルーティングテーブルが2つになりました。mainのデフォルトルートは従来のWAN接続用インターフェースを使い、rsurgeのデフォルトルートはSurgeを指します。
以降のルールではlan-ipというアドレスリストを使うので、先にLANのサブネットを登録します。他にも直接通信させたい内部サブネットがあれば、このリストに追加してください。
マークを付ける
次に、どの端末の通信にrsurgeを使わせるかをmangleで指定します。
以下で物理ブリッジポートを条件にするin-bridge-portを使うには、ブリッジのuse-ip-firewallを有効にしておく必要があります。デフォルトはnoです。有効にするとブリッジ経由の通信がIPファイアウォールを通り、Bridge FastPathやCPU負荷にも影響するため、機器の処理能力を確認してから設定してください。後述の送信元IPアドレスリストを使う方法では、このブリッジポートの条件は不要です。詳しくはMikroTikのCommon Firewall Matchers and ActionsとBridging and Switchingを参照してください。
このルールには、3つの条件があります。
in-bridge-port=ether8で、どの端末をプロキシ経由にするかを指定します。後からこのポートの先にスイッチを追加し、プロキシが必要なAPや端末をすべてそこにつなぎました。そのため、このポートから入る通信をまとめてマークでき、端末ごとのルールは不要です。mangleではブリッジ内の物理ポートを直接条件にできるので便利です。
dst-address-list=!lan-ipでLAN宛ての通信を除外します。lan-ipは内部サブネットを登録したアドレスリストです。内部宛ての通信はSurgeへ送らず、通常の経路で直接届ける必要があります。これでスマートホームやLAN内のサービスも引き続き使えます。
src-address=!10.10.77.4でSurge自身を除外します。Surgeが処理した通信は、外部へ出るためにメインルーターへ戻ってきます。これを除外しないと、再びマークされてSurgeへ送り返され、ループしてしまいます。
対象の端末が1つのポートにまとまっていない場合は、IPアドレスをアドレスリストに登録し、src-address-listを条件にする方法も使えます。
FastTrack
RouterOSのデフォルト設定にはFastTrackのルールがあります。確立済みの接続で後続の処理の多くを省略し、転送を高速化するものです。公式ドキュメントのFastTrackの説明によると、ルートの検索はルーティングマークの設定前に行われるため、mainテーブルでしか使えません。つまり、mark-routingを使う接続がFastTrackの対象になると、パケットはSurgeではなくmainの経路を通ってしまいます。
このルールがある場合は、Surge経由の接続をFastTrackから除外する必要があります。mangleに同じ条件のルールをもう1つ追加し、接続にコネクションマークを付けます。そのうえでFastTrackのルールにconnection-mark=no-markを追加し、マークのない接続だけを対象にします。
このmark-connectionルールは、先ほどのmark-routingの前でも後でもかまいません。両方ともpassthroughがyesなので、後続のルールも引き続き評価されます。
DNS
ここまでで通信をSurgeへ送れるようになりましたが、DNSの設定が残っています。
SurgeのEnhanced ModeはFake IPを使います。クライアントがドメイン名を問い合わせると、Surgeは198.18.0.0/15の範囲から仮のIPアドレスを返します。クライアントがそのアドレスへ接続すると、Surgeは対応するドメイン名を特定し、ルールに従って処理します。そのため、DNSの問い合わせもSurgeへ届ける必要があります。そうしないと実際のIPアドレスが返され、Fake IPの仕組みを使えません。
dstnatを使い、対象端末のDNS問い合わせをSurgeのFake IP DNSアドレスである198.18.0.2へ転送します。
DNSの2つのルールでもsrc-address=!10.10.77.4でSurge自身を除外します。Surgeが同じブリッジポートにつながっている場合に、外部DNSサーバーへの問い合わせが自分自身に戻されるのを防ぐためです。DNSはTCPも使うので、同じ条件でTCPの53番ポートのルールも追加します。DoHやDoTなどの暗号化DNSはこの2つのルールでは扱えず、端末側やネットワークポリシーで別途管理する必要があります。端末の選択を送信元アドレスリスト方式に変えた場合は、DNSの2つのルールもそれに合わせて変更してください。
ここでも処理順が重要です。DNSの問い合わせはmangle prerouting、dstnat、ルーティング判断の順に進みます。
クライアントが外部のDNSサーバーを指定している場合、宛先はlan-ipに含まれないため、mangleでマークが付きます。dstnatで宛先が198.18.0.2に書き換えられ、その後rsurgeのデフォルトルートを使ってSurgeへ届きます。
一方、DNSサーバーにルーター自身を指定している場合、宛先はLAN内なので、mangleではlan-ipによって除外され、マークが付きません。dstnatで宛先が198.18.0.2に変わっても、ルーティング判断の時点でマークがないためmainを参照します。そのためmainにも、198.18.0.0/16をSurgeへ向けるルートが必要です。
最後に、srcnatルールを追加します。
クライアントがDNSの問い合わせを送った先はルーターの10.10.77.1なので、応答も同じアドレスから来ることを期待します。しかしSurgeへ転送しただけでは、Surgeからクライアントへ直接応答が届き、その送信元は198.18.0.2になります。想定したDNSサーバーからの応答ではないため、クライアントは破棄してしまいます。srcnatで問い合わせの送信元をルーター自身のアドレスに変えると、Surgeの応答はいったんルーターへ戻ります。そこでコネクショントラッキングによりアドレス変換が元に戻り、クライアントへ届きます。
使ってみて
これで自宅には、直接インターネットへ出るWi-FiとSurge経由のWi-Fiができました。どちらも同じサブネットなので、端末同士はそのまま通信できます。接続に問題があっても家族はWi-Fiを切り替えるだけでよく、私を呼ぶ必要がありません。Surgeに不具合があっても、メインルーターやプロキシ不要の端末には影響しなくなりました。
今後、サービスに応じて経路を変えたくなっても、rsurgeへルートを追加するだけです。たとえば、特定のサブネットだけSurgeを通さず、実際のWAN側ネクストホップへ送ることもできます。このとき、ゲートウェイにメインルーター自身のLANアドレスである10.10.77.1を指定してはいけません。有効な上流のネクストホップにはならないためです。WAN側のゲートウェイが固定なら、次のように指定できます。@mainは、このネクストホップをmainテーブルで解決するという意味です。PPPoE接続の場合は、実際の構成に合わせてPPPoEインターフェースを指定します。
ここでの<网段>(宛先サブネット)と<实际WAN网关>(実際のWANゲートウェイ)はプレースホルダーです。自分のネットワークに合った値に置き換えてください。
OpenWrtでもip ruleとip route tableを使えば同じことができます。パケットにマークを付けてから、別のルーティングテーブルを参照させるという考え方は共通です。