netgraph の機能を使用して kernel 内に NAT node を作成し、 ipfw のルールで NAT すべきパケットを 作成した NAT node へ転送することで、 kernel 内で NAT をすることが可能となる。 設定はそんなに難しくなく、 ng_nat(4) に書いてある通りにすれば問題ない。
ng_ipfw と ng_nat が必要になるので、 /boot/loader.conf に以下の設定を追加する。 もちろん ipfw の設定も必要だけど、ここでは省略。
ng_ipfw_load="YES" ng_nat_load="YES"
/etc/start_if.fxp0 に以下のように書いて、 起動時に ngctl を実行して NAT node を作成するようにする。 ファイル名のインタフェース部分と、 NAT する IP アドレスは環境に応じて変更する。 (本当はこのファイルに書くべき事では無いのかもしれないけど、 他にいい場所が見当たらなかったので)
ngctl mkpeer ipfw: nat 60 out ngctl name ipfw:60 nat ngctl connect ipfw: nat: 61 in ngctl msg nat: setaliasaddr AAA.BBB.CCC.DDD
ipfw のルールに以下の設定を追加して、 NAT したいパケットを ng_nat へ送るようにする。 追加する場所、インタフェース名、設定方法は それぞれの環境に応じて異なるので注意。
add netgraph 61 ipv4 from any to any in via tun0 add netgraph 60 ipv4 from any to any out via tun0設定は /etc/ipfw.rule といったファイルに ipfw のルールを全部書いておいて、 rc.conf で firewall_type="/etc/ipfw.rule" とするのがお奨め。
/etc/sysctl.conf に以下の行を追加する。
net.inet.ip.fw.one_pass=0デフォルトだと one_pass が 1 になっているため、 dummynet(4) や ng_ipfw(4) から戻ってきたパケットが 再び ipfw を通らずに抜けてしまう。 これを 0 にすることで、 戻ってきたパケットを ipfw の次のルールから適用させることができるようになる。 詳しくは ipfw(8) を参照。
すでに ppp(8) や natd(8) で NAT を行っていた場合は、 これらを無効にする。
natd_enable="NO" ppp_nat="NO"
パフォーマンスの違いがどのくらいあるのか簡単に計ってみたけど、 どうもあんまり違いは無いみたい。 FreeBSD で NAT をする方法はいろいろあるけど、 パフォーマンス的にはどれもそんなに違いはなさそう。