2012-09-24

RHEL6: multi NICs in the same subnet

タイトルは「NIC」としているが、どちらかと言えば「インターフェイス」の意味。要は複数 LAN ポートを同一サブネットに繋ぎたい。

例として、次のように設定する。

  • eth0: 192.168.1.11/24 (00-0c-29-11-11-11)
  • eth1: 192.168.1.12/24 (00-0c-29-22-22-22)

このとき双方に ping を打つと、実際には eth0 だけが応答する。

>ping 192.168.1.11
>ping 192.168.1.12
>arp -a
インターフェイス: 192.168.1.101 --- 0xc
  インターネット アドレス      物理アドレス      種類
  192.168.1.11          00-0c-29-11-11-11     動的
  192.168.1.12          00-0c-29-11-11-11     動的

これは Linux ネットワークの仕様。RHEL5 だと、次を設定することで両方から ping が返るようになる。

net.ipv4.conf.all.arp_announce = 2
net.ipv4.conf.all.arp_ignore = 1
>ping 192.168.1.11
>ping 192.168.1.12
>arp -a
インターフェイス: 192.168.1.101 --- 0xc
  インターネット アドレス      物理アドレス      種類
  192.168.1.11          00-0c-29-11-11-11     動的
  192.168.1.12          00-0c-29-22-22-22     動的

しかし、RHEL6 で同じ設定をしても動かない。具体的には、eth1 への ping が返ってこず、ARP テーブルにも登録されない。

>ping 192.168.1.11
>ping 192.168.1.12
>arp -a
インターフェイス: 192.168.1.101 --- 0xc
  インターネット アドレス      物理アドレス      種類
  192.168.1.11          00-0c-29-11-11-11     動的

この理由がずっと分からなかったが、どうやら RHEL6 から rp_filter の挙動が変わったようだ。結論から言うと、次の設定で RHEL6 でも動くようになる。

net.ipv4.conf.all.arp_announce = 2
net.ipv4.conf.all.arp_ignore = 1
net.ipv4.conf.all.rp_filter = 2

理由はこの辺りを参照。

RHEL5 では、conf.all.rp_filter と conf.<interface>.rp_filter との論理積(AND)が rp_filter の実効値だったが、RHEL6 では最大値(MAX)が実効値になった。

RHEL5 / RHEL6 のどちらにも、/etc/sysctl.conf に次の設定がある。

# Controls source route verification
net.ipv4.conf.default.rp_filter = 1

よって RHEL5 の場合、rp_filter の値は次のようになる。

[rhel5]# sysctl -a | grep '\.rp_filter' | sort
net.ipv4.conf.all.rp_filter = 0
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.eth0.rp_filter = 1
net.ipv4.conf.eth1.rp_filter = 1
net.ipv4.conf.lo.rp_filter = 0

rp_filter の実効値は、eth0 であれば all.rp_filter AND eth0.rp_filter (=0)となり、eth0 の rp_filter は無効となる。つまり /etc/sysctl.conf にそれらしい設定がありながら、実は 全く効いていなかった ということ。騙された。

RHEL6 の場合は、

[rhel6]# sysctl -a | grep '\.rp_filter' | sort
net.ipv4.conf.all.rp_filter = 0
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.eth0.rp_filter = 1
net.ipv4.conf.eth1.rp_filter = 1
net.ipv4.conf.lo.rp_filter = 1

今度は MAX(all.rp_filter, eth0.rp_filter) (=1)となり、rp_filter は有効となる。RHEL5 と全く同じ動作にしたいなら、/etc/sysctl.conf で net.ipv4.conf.default.rp_filter = 0 と設定する必要がある。

今回の目的(同一サブネットに複数インターフェイス)では、rp_filter = 2 にすれば良い。これは loose mode というらしく、いずれかのインターフェイスから到達可能な宛先であればパケットを受信する、というモードのようだ。RFC3704 なんて読む気ないので詳しくは知らない。

そもそも今回の件で rp_filter が影響することが分かり辛いが、極力プライマリインターフェイスで通信しようとする Linux の特性を考えると、何となく理解できなくもない。今回の場合、プライマリ側である eth0 が ARP パケットを処理する際に rp_filter が効くのだろう(と思う)。が、やはり Linux のこの特性は分かりにくい(直感的でない)と思う。

以上のように、Linux で複数インターフェイスを同一サブネットに繋ごうとすると、非常に微妙な領域に足を突っ込むことになる。やるなら 1 個のインターフェイスに仮想 IP アドレスを付ける方法(仮想インターフェイス)をお勧めする。じゃあ私は何故そうしないのかと言うと、現場 SE に「仮想 IP アドレス」とか言っても通じないから。少なくとも自力で設定できるとは思えない。しかし「1 つの LAN ポートに 1 つの IP アドレス」であれば、彼等にも理解してもらえる確率がグッと上がる。:-)

0 件のコメント:

コメントを投稿

注: コメントを投稿できるのは、このブログのメンバーだけです。