某所ネタ。たとえば、ウチのように細々ながら自宅でサーバ立ててると、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のルールでフィルタしておかねばならない。
コメントする