NetBSDでマルチホーム

某所ネタ。たとえば、ウチのように細々ながら自宅でサーバ立ててると、ISP乗り換えのときに一時的に新旧両アドレスでサービスしたい (BフレッツはPPPoEを2セッションまで張れるので、両方のISPに接続できる)、ということもある。そういうときにどう設定しましょうか、という話。

      The Internet
      |          |
      |          |
    ISP A      ISP B
      |          |
      |          |
      |          |
    addr A    addr B
+-----+----------+-----+
|   pppoe0    pppoe1   |
|                      |
|      NetBSD box      |
|                      |
+----------------------+

仮に、default routeをISP A側にしておくと、NetBSD箱から送信アドレスaddr Bで外へ投げるパケットもpppoe0経由で外に出てしまう。このとき、ISP Aから見ると、謎の送信アドレスを持つパケットがお客さんとこから来た、ということで、送信アドレスを偽装しているのと区別がつかない。このパケットはpppoe1からISP Bに送るのが正しいやり方だ。

Linuxの場合は、ある宛先 (defaultも含む) に対する経路を複数設定できる。上記の場合、ISP A側とISP B側の両方をdefault routeとして設定しておけば、カーネルがよきにはからってくれる。

4.2BSDから引き継ぐ由緒正しい (?) プロトコルスタックを持つNetBSDの場合はdefault routeは1つしか設定できない。どうすればよいか、というと、4年半も前に創夢のメーリングリストで話題になっている。送信アドレスがaddr Bのパケットがpppoe0に来たところで、ipfilterを使って、うりゃっとpppoe1の送信キューに積んでしまうわけだ。

というだけなら面白くないので、pfでどうやってやるか調べてみた。結果からいうと、ipfilterと同じような設定ができる。

PF User's Guideを隅から隅まで読むと、負荷分散がどうたらとかいうあたりにroute-to、reply-to、dup-toというソレっぽいキーワードが出てくる。上のipfilterの例と同じことをやろうと思えば、

  pass out on pppoe0 route-to pppoe1 from pppoe1 to any

という感じと思われる。試してみたところ、これは他のところで例えば

  pass in on pppoe1 proto tcp from any to pppoe1 port 80 flags S/SA keep state

のように、keep stateを使って特定のサービスを公開している場合はうまくいかない。出ていこうとするパケットは、先にkeep stateで作ったstateにマッチしてしまうために、pass outの行にはマッチしないためだ。このへんの事情はipfilterでも同じと思われるが、同様の設定がipfilterでも可能かどうかは確認していない。で、この場合は以下のようにpass inの行でreply-toを使うのが正解らしい。このreply-toは、keep stateで作るstateにはたらきかける。

  pass in on pppoe1 reply-to pppoe1 proto tcp from any to pppoe1 port 80 flags S/SA keep state

普通は推奨通りdefault block、つまり最初にblock log allなどとしてから通してもいいヤツをpass in/pass outで列挙する、という形にしているだろう、というのと、default routeを切り替えても大丈夫にしてまとめると、こんな感じかな。以下の例ではこのルールを読み込んだ時点でpppoe0、pppoe1ともにアドレスがついてないといけないことに注意。最初に挙げたような状況なら、addr A、addr Bともに固定だと思うので、それを書いちゃった方がよい。

  tcp_services = "{ ssh, smtp, www, domain }"
  udp_services = "{ domain }"
  block return in log all
  block return out log all

  pass in on pppoe0 reply-to pppoe0 proto icmp from any to pppoe0 keep state
  pass in on pppoe1 reply-to pppoe1 proto icmp from any to pppoe1 keep state
  pass in on pppoe0 reply-to pppoe0 proto tcp from any to pppoe0 port $tcp_services flags S/SA keep state
  pass in on pppoe1 reply-to pppoe1 proto tcp from any to pppoe1 port $tcp_services flags S/SA keep state
  pass in on pppoe0 reply-to pppoe0 proto udp from any to pppoe0 port $udp_services keep state
  pass in on pppoe1 reply-to pppoe1 proto udp from any to pppoe1 port $udp_services keep state

  pass out on pppoe0 from pppoe0 to any flags S/SA keep state
  pass out on pppoe0 route-to pppoe1 from pppoe1 to any flags S/SA keep state
  pass out on pppoe1 from pppoe1 to any flags S/SA keep state
  pass out on pppoe1 route-to pppoe0 from pppoe0 to any flags S/SA keep state

ちなみにroute-to、reply-toで他のインターフェイスに振られたパケットは、再度pfを通る、ということはないようだ。なので、pass out on pppoe1 route-to pppoe0としている場合、on pppoe1のルールでフィルタしておかねばならない。

トラックバック(1)

トラックバックURL: http://makoto.minoura.org/d/mt/mt-tb.cgi/225

昔、NetBSDでマルチホームという記事を書いたが、問題を1つ見付けた。 set... 続きを読む

コメントする

このブログ記事について

このページは、みが2007年6月 2日 16:40に書いたブログ記事です。

ひとつ前のブログ記事は「年金問題」です。

次のブログ記事は「チョウとbutterflyとガとmoth」です。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。