Linux iproute2でconntrackレスなStateless NAT

2018.01.25

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

ども、大瀧です。
Linuxで静的NATを組むときの定番はNetfilter(iptables)ですが、iptablesのnatテーブルの運用にはconntrack(コネクション追跡テーブル)のサイジングがつきまといます。そこで今回は静的NATを組むLinuxカーネルのもうひとつの機能として、iproute2によるStateless NAT構成をご紹介します。

動作確認環境

  • Linuxディストリビューション : Amazon Linux 2.0 RC
  • iproute2のバージョン : 3.10.0

構成例と設定コマンド

今回はありがちな静的NAT構成として、以下で設定してみます。

クライアントPCはNAT Box宛(192.168.1.1)のリクエストを送出、NAT Boxの静的NATでWebサーバー宛(10.10.0.100)にDNAT、戻り用にSNATもペアで設定する感じですね。iptablesで書くと以下になります。

$ sudo iptables -A PREROUTING  -i eth0 -j DNAT --to-destination 10.10.0.100
$ sudo iptables -A POSTROUTING -o eth0 -j SNAT --to-source 192.168.1.1

では、iproute2でやってみます。ググるとこの辺が引っかかるのですが、手元ではip route add natの結果がlocalテーブルに載るなど意図した結果になりません。もう少し調べてみるとカーネル3.16以降はtcに移ったよという記事に出会ったので、tcでやってみることにします。以下のコマンドラインです。IPアドレスは登場回数が多いのでシェル変数にしました。

$ export FROMIP=192.168.1.1/32
$ export TOIP=10.10.0.100
$ sudo tc qdisc add dev eth0 ingress
$ sudo tc filter add dev eth0 parent ffff: \
  protocol ip prio 1 u32 match ip dst $FROMIP \
  action nat ingress $FROMIP $TOIP
$ sudo tc qdisc add dev eth0 root handle 1: htb
$ sudo tc filter add dev eth0 parent 1: \
  protocol ip prio 1 u32 match ip src $TOIP \
  action nat egress $TOIP $FROMIP

コマンドラインが複雑になった感は否めませんが、tc filterでeth0デバイスの自ホスト(FROMIP)宛のingressトラフィックのIPアドレスをTOIPにNAT、送信元がTOIPのegressトラフィックのTOIPをFROMIPにNATしているのが読めると思います。

もちろん、IP転送自体はiptablesと同様、事前に有効にしておきましょう。

$ sudo sysctl net.ipv4.ip_forward=1
net.ipv4.ip_forward = 1
$

これでOKです。conntrackのエントリー数を気にすることなく、静的NATを動かすことが出来ます。余談ですが、当初はIPv6でNAT66を行うためにiproute2での設定を探っていたのですが、iproute2のソースinclude/linux/tc_act/tc_nat.hの以下の記述から

struct tc_nat {
	tc_gen;
	__be32 old_addr;
	__be32 new_addr;
	__be32 mask;
	__u32 flags;
};

IPアドレスが32ビットBigEndian決め打ちでIPv6には対応していないようなのでした、残念。 *1

まとめ

Linux iproute2でStateless NATの構成例をご紹介しました。LinuxのNAT周りの情報として何かの参考になれば幸いです。

参考URL

脚注

  1. ip6tablesはNAT66が出来るので、大人しくそちらで代用することにします