FreeBSDのMulti-FIBとIPパケット送出について、なんとなく色々考えていたら、少し問題がありそうなことに気がついたので考えてみた。 なお、実際のcodeは見ていないので、もしかすると大勘違いをしている可能性があります。
そこそこ長いかもしれないので、興味のない人はスルーでお願いします。
想定している構成は以下の通り
AS-A addr(A) AS-C +-------- ISP-A --- Router ---+ node --- ISP-C --- Internet Server addr(C) +-------- ISP-B --- Router ---+ AS-B addr(B)
上記構成において、仮にserverのnic0がaddr(A)を持ち、nic1がaddr(B)を持っているとする。 この仮定は今時の日本においては、それほど荒唐無稽なものではない。
例えば、自宅にFlet'sを引き込み、Multi-Sessionを用いて上流ISPを2社と契約し、固定アドレスを割り当ててもらうような場合がこれに当たる。 まぁ、それほど多いとは思わないけど、こういう構成のサイトは企業も含めてそれなりにある。
このようなサイトにおいて、あるnode(addr(C))からaddr(A)にパケットが来たとする。
addr(A)を持つサーバー(Serv)のFIB次第で、返答のパケットが送り出されるNICが決まる現在のFreeBSDの実装だと、以下のような状況が発生する。
としてパケットを送出する
現在FreeBSDのnetinet/netinet6の実装は、パケットを送り出す際に、送り出すパケットのsource IP Addressは考慮しておらず、FIBを見た上でベストなI/Fから送り出す。 つまり、FreeBSDのip送出ロジックは
となっていると考えられる。
このような実装において、1の状況になってくれれば問題は発生しないが、もし2の状況になった場合、nic1側の中継nodeがaddr(A)がsrcのパケットを中継してくれるとは限らない。 (これを許すと困ったことになるのはISPで運用していれば理解できると思うので、ここでは詳述しない)
この問題を解決するは、本質的には「全てのI/FにFIBを持たなければならない」と考えられる。なぜなら、入ってきたI/Fからパケットを出すためには、(本質的には)それぞれのI/Fにdest毎のnexthopを持たなければならないからである。 (ただし、FIBを共通化することができる可能性はあるし、そのようにNetworkを構成することができる場合も多いとは考えられる。)
現状のFreeBSDの実装でこの問題を無理矢理にでも解決しようとした場合、pfやipfwを用いたpolicy routeで処理するしかない。 例えば、SRC ADDRがaddr(A)ならば、nic0側のnexthopにpacketを投げる。addr(B)ならnic1側のnexthopにpacketを投げるといった設定を投入ればよい。
しかし、ここでPolicy Routeを利用せずに問題を解決しようとすると、解決策がない。突破口になりそうな機能としてはMulti-FIBがあるが、そこには下記のような問題がある。
FreeBSDのMulti-FIBは、Process単位にFIBを選択可能である。
問題は、Processに割り当てられるFIBを、起動時に1つ設定できる。(もちろん、FIBを選択できるようにApplicationを作成すれば問題にならないが、nginxやapacheなどにその修正を投入するのはそれほど簡単ではなく、現実的ではない)。逆に言えば、パケット送出時に必要なFIBを選択することは(あくまでProcessがそのように実装されていなければ)できない。
つまり、Processレベルで見ると、上記パケット送出時の問題がそのまま残っていると言える。
今のFreeBSDの実装においては、本問題を解決するためにはpolicy routeを利用するしか手がない。
まず、ほとんどの場合、現在の実装で問題はないと言える。想定した構成は、あくまで例外的な構成であり、構成変更できる場合はやりようがあるからである。
例えば、Serverのnicを1つにまとめて、routerでNATするという手が考えられる。こうすることによって、serverはNATされたsource addressから通信を受け取るので、返答をrouterに送れば良くなる。ただし、この場合は、Routerの性能などが問題になる可能性がある。特にNATはstateを持つので、耐障害性などを考慮すると、故障箇所が増えたりdebugが面倒になるなどのデメリットはある。
まぁ、文句ばかり言っても仕方がないので、もし実装を変えるならどうするべきかを考察してみる。
問題の根本は、FreeBSDにおけるMulti-FIB実装が「Process単位でFIBを選択する」実装になっていることにある。これをI/F単位にFIB指定する(ProcessからはFIBは選択しない)形式に直すことで、本問題は解決できると思われる。ただし、いくつか制限が出てくる(本質的には不要かもしれない。まだそこまで考えたわけではないので)
以上の制限を課した上で、ip_output.cで、
という処理を行う。
上記実装の問題として、今思いつくのは、
が考えられる。