もう一度ENIについて考えてみた(Linux編)

Amazon Linux AMI 2012.09より、複数のENIを使用した際の自動設定が非常に賢くなっているので、もう一度追加ENIについて考えてみた。

Amazon Linux AMIを使用した検証

論より証拠。まずは動かしてみましょう。
VPCを作成します。

  • VPC: 10.0.0.0/16
    • Subnet1: 10.0.1.0/24
    • Subnet2: 10.0.2.0/24
    • Subnet3: 10.0.254.0/24
  • Subnet1&2 共通RouteTable
    • 10.0.0.0/16 local
    • 0.0.0.0/0 igw-xxxxxxxx

Amazon Linux AMI (2012.09バージョン)を起動します。

  • AMI: amzn-ami-pv-2012.09.0.x86_64-ebs (ami-4e6cd34f)
  • ENI: 1つだけ (10.0.1.4のIPアドレスを指定) ※起動時には追加のENIを設定しない
  • SecurityGroup:
    • 22/tcp (ssh) 0.0.0.0/0
    • ICMP Echo Request 0.0.0.0/0

起動後にElasticIPアドレスを付けsshできる事を確認してください。
別途ENIを作成します(10.0.2.4)。そのENIをこのインスタンスにアタッチすると...

$ ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue state UNKNOWN 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 02:d0:61:a1:e7:54 brd ff:ff:ff:ff:ff:ff
    inet 10.0.1.4/24 brd 10.0.1.255 scope global eth0
    inet6 fe80::d0:61ff:fea1:e754/64 scope link 
       valid_lft forever preferred_lft forever
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 02:d0:61:84:4f:4a brd ff:ff:ff:ff:ff:ff
    inet 10.0.2.4/24 brd 10.0.2.255 scope global eth1
    inet6 fe80::d0:61ff:fe84:4f4a/64 scope link 
       valid_lft forever preferred_lft forever

自動的にIPアドレスが設定されています。さらにElasticIPアドレスをeth1側にも付けてあげると、このアドレスに対してもpingsshも出来ます。また元々付いていたeth0のElasticIPアドレスに対してもpingsshの疎通があります。あまりに普通に動いてしまうので伝わらないかもしれませんが、大きな進歩なのです。

同じ事をCentOS6でやってみると...

eth0(10.0.1.5)を付けて起動した後、eth1(10.0.2.5)を付けていくら待ってもeth1はupしません。
/etc/sysconfig/network-scripts/ifcfg-eth1をifcfg-eth0を元にDEVICE名だけを変えて、./ifup eth1してみるとeth0経由で繋いでいたコンソールが切れてしまうと思います。eth1にEIPを付けてあげれば、そのIPアドレスに対しては通信できるようになっています。

# /sbin/route  -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
10.0.1.0        0.0.0.0         255.255.255.0   U     0      0        0 eth0
10.0.2.0        0.0.0.0         255.255.255.0   U     0      0        0 eth1
169.254.0.0     0.0.0.0         255.255.0.0     U     1002   0        0 eth0
169.254.0.0     0.0.0.0         255.255.0.0     U     1003   0        0 eth1
0.0.0.0         10.0.2.1        0.0.0.0         UG    0      0        0 eth1

defaulte gatewayがeth1側のsubnetのgatewayに書き変わってしまっているので、当然ですね。rebootをしてもeth0 -> eth1とifupしていくでしょうから、この状況は変わらないと思います。両方のIPにpingを打ちつつ見ていると、先にeth0側からreplyがあり、数秒後に途絶えて、代わりにeth1からはreplyが返ってくる状況になります。これは/etc/sysconfig/network-scripts/ifcfg-eth1に

DEVICE=eth1
BOOTPROTO=dhcp
ONBOOT=on
DEFROUTE=no

とDEFROUTE=noを指定する事で、eth1側のgatewayをdefault gatewayとして使用する事を抑制できます。
再びeth0側で通信できる状態にはなりましたが、eth1側は疎通が出来なくなります。10.0.254.0/24にインスタンスを立ててeth1側にpingを打ってみると

[ec2-user@ip-10-0-254-4 ~]$ ping -c 1 10.0.1.5
PING 10.0.1.5 (10.0.1.5) 56(84) bytes of data.
64 bytes from 10.0.1.5: icmp_seq=1 ttl=64 time=0.322 ms

--- 10.0.1.5 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.322/0.322/0.322/0.000 ms
[ec2-user@ip-10-0-254-4 ~]$ ping -c 1 10.0.2.5
PING 10.0.2.5 (10.0.2.5) 56(84) bytes of data.
^C
--- 10.0.2.5 ping statistics ---
1 packets transmitted, 0 received, 100% packet loss, time 4883ms

eth1側にpingが飛びません。eth1のインターフェースでicmpをtcpdumpしてみると、

# tcpdump -i eth1 icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth1, link-type EN10MB (Ethernet), capture size 65535 bytes
16:32:19.772456 IP 10.0.254.4 > 10.0.2.5: ICMP echo request, id 54276, seq 1, length 64
16:32:20.771503 IP 10.0.254.4 > 10.0.2.5: ICMP echo request, id 54276, seq 2, length 64
16:32:21.771512 IP 10.0.254.4 > 10.0.2.5: ICMP echo request, id 54276, seq 3, length 64
16:32:22.771610 IP 10.0.254.4 > 10.0.2.5: ICMP echo request, id 54276, seq 4, length 64

echo requestパケットは来ているものの、echo replyパケットが返っていない事が分かります。これはOS上のdefault gatewayがeth0ですので、戻りのパケットがeth0から出て行こうとしてしまうからです(そしてrp_filterによってパケットが落とされる)。

どうしたらよいのか

Linux Route Hacksを参考に、Policy Based Routingを設定してみます。
ENI毎に、対象のsubnetに対してrouting tableを作成します。

ip route flush table 1000
ip route add table 1000 to 10.0.1.0/24 dev eth0
ip route add table 1000 to default via 10.0.1.1 dev eth0
ip rule add from 10.0.1.0/24 table 1000 priority 1000
ip route flush table 1001
ip route add table 1001 to 10.0.2.0/24 dev eth1
ip route add table 1001 to default via 10.0.2.1 dev eth1
ip rule add from 10.0.2.0/24 table 1001 priority 1001
ip route flush cache

すると、どちらのインターフェースからも疎通が取れるようになりました。

[ec2-user@ip-10-0-254-4 ~]$ ping -c 1 10.0.1.5
PING 10.0.1.5 (10.0.1.5) 56(84) bytes of data.
64 bytes from 10.0.1.5: icmp_seq=1 ttl=64 time=0.319 ms

--- 10.0.1.5 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.319/0.319/0.319/0.000 ms
[ec2-user@ip-10-0-254-4 ~]$ ping -c 1 10.0.2.5
PING 10.0.2.5 (10.0.2.5) 56(84) bytes of data.
64 bytes from 10.0.2.5: icmp_seq=1 ttl=64 time=0.313 ms

--- 10.0.2.5 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.313/0.313/0.313/0.000 ms

スクリプト

ENIの情報をmeta-dataから取得してrouteの設定を行うスクリプトを作成しました→adding policy based route for each ENI · GitHub(要perl)。
例えば/usr/local/sbin/add_policy_route.shのように置いて、/etc/rc.local などから起動します。

さいごに

@suz_labさんもsuz-lab.com - このウェブサイトは販売用です! -&nbspsuz-lab リソースおよび情報でおっしゃってますが、複数ENIを使用するべきかどうか(≒厳格なENI毎のSecurityGroup設定やNetwork ACLを活用したセキュリティ設定が必要かどうか)をきちんと判断した上で使用するのがよいと思います。ENI分けたけどSubnet間のNACLは普通に許可されている、というのではあまり意味がなくなってしまいます。

WindowsWindowsで別の問題があるので別のエントリにて...(そのうち