IBM Cloud: VPC上のVSIをルーターに見立てて通信させてみた


#1. はじめに

IBM CloudのVPCにて、以下のようなネットワーク構成時に、

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62127/1642ba6d-2d7f-95b9-c84d-065328abd5f9.png)


```
Server-A(172.16.5.6) -> Server-B(172.16.5.4, 172.16.7.4) -> Server-C(172.16.7.6)
```

という経路で接続できるか？（Server-Bがあたかもルーターであるかのように構成できるか？という確認をしてみました。（このあたりを手動で構成できるようになることは、今後VPC上でNFV機能を使うときの理解の一助にもなると思われます）

#2. 結論を先にいうと

- Server-A: Server-Cへの経路がServer-Bを通るように設定。
- Server-C: Server-Aへの経路がServer-Bを通るように設定。
- Server-B: パケットが転送できるようにip_forward=1を設定。
- Server-B: Server-Bのインターフェースに設定していないIPをsource IPとして送信するため、eth1/eth3にてIP-Spoofingを有効化。

をすれば通信は可能になります。また、当然ですがSecurity GroupやNetwork ACLといったVPCのFW機能だったり、iptablesなどのOS Firewallでもこれらの通信を許可されているという前提で話を進めています。
また、今回は動作確認をしたかっただけなので、以下では非永続的な設定をしていることをご了承ください。

#3. 経路設定

Server-AのDefault Gatewayは172.16.5.1であり、static routeは構成されていません。そのため、Server-Cに通信しようとしてもそのままでは172.16.5.1にパケットを送信してしまいます。Server-C宛のパケットはServer-Bに送信するように経路設定を変更する必要があります。同様のことはServer-Cにもあてはまります。

##3-1. Server-Aの経路設定

```txt:Server-Aにて、Server-C宛のパケットはServer-Bに送るように構成
[root@server-a ~]# ip r
default via 172.16.5.1 dev eth0
169.254.0.0/16 dev eth0 scope link metric 1002
172.16.5.0/24 dev eth0 proto kernel scope link src 172.16.5.6

[root@server-a ~]# ip route add 172.16.7.0/24 via 172.16.5.4

[root@server-a ~]# ip r
default via 172.16.5.1 dev eth0
169.254.0.0/16 dev eth0 scope link metric 1002
172.16.5.0/24 dev eth0 proto kernel scope link src 172.16.5.6
172.16.7.0/24 via 172.16.5.4 dev eth0
```

##3-2. Server-Cの経路設定

```txt:Server-Cにて、Server-A宛のパケットはServer-Bに送るように構成
[root@server-c ~]# ip r
default via 172.16.7.1 dev eth0
169.254.0.0/16 dev eth0 scope link metric 1002
172.16.7.0/24 dev eth0 proto kernel scope link src 172.16.7.6

[root@server-c ~]# ip route add 172.16.5.0/24 via 172.16.7.4

[root@server-c ~]# ip r
default via 172.16.7.1 dev eth0
169.254.0.0/16 dev eth0 scope link metric 1002
172.16.5.0/24 via 172.16.7.4 dev eth0
172.16.7.0/24 dev eth0 proto kernel scope link src 172.16.7.6
```

##3-3. 経路設定後のテスト

```txt:Server-AからServer-Cにpingを実行
[root@server-a ~]# ping 172.16.7.6
（出力なし）
```

```txt:Server-B(eth1)には届いている
[root@server-b ~]# tcpdump -i eth1 icmp -nn
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth1, link-type EN10MB (Ethernet), capture size 262144 bytes
18:22:02.698138 IP 172.16.5.6 > 172.16.7.6: ICMP echo request, id 31833, seq 1, length 64
18:22:03.697297 IP 172.16.5.6 > 172.16.7.6: ICMP echo request, id 31833, seq 2, length 64
18:22:04.697304 IP 172.16.5.6 > 172.16.7.6: ICMP echo request, id 31833, seq 3, length 64
18:22:05.697325 IP 172.16.5.6 > 172.16.7.6: ICMP echo request, id 31833, seq 4, length 64
```

```txt:Server-B(eth3)でのtcpdumpは何も表示されない
[root@syasuda-router ~]# tcpdump -i eth3 icmp -nn
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth3, link-type EN10MB (Ethernet), capture size 262144 bytes

（何も出力されない）
```

よって、Server-Bでの設定が足りていないことがわかります。

#4. Server-Bでのパケット転送

Linuxサーバーではパケット転送はデフォルトで許可されていません。パケット転送を許可しルーターのように振る舞うためには、カーネルパラメーターの変更が必要です。

##4-1. Server-Bにてip_forward=1を設定

https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt

```txt:ip_forward
ip_forward - BOOLEAN
	0 - disabled (default)
	not 0 - enabled

	Forward Packets between interfaces.

	This variable is special, its change resets all configuration
	parameters to their default state (RFC1122 for hosts, RFC1812
	for routers)
```

```txt:Server-B
[root@server-b ~]# cat /proc/sys/net/ipv4/ip_forward
0

[root@server-b ~]# echo 1 > /proc/sys/net/ipv4/ip_forward

[root@server-b ~]# cat /proc/sys/net/ipv4/ip_forward
1
```

また、念の為iptablesでFORWARDチェーンがブロックされていないことも確認しておきます。（VPC上のVSIではiptablesはデフォルトでは有効化していないのでブロックされていないはず）

```txt:FORWARDチェーンはACCEPTになっている。
[root@syasuda-router ~]# iptables -L -v -n
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination

Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination

Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination
```


##4-2. ip_forward=1後のテスト

```txt:Server-AからServer-Cにpingを実行
[root@syasuda17165 ~]# ping 172.16.7.6
PING 172.16.7.6 (172.16.7.6) 56(84) bytes of data.

(PINGの応答は返ってこない）
```

```txt:Server-B(eth1)には届いている
[root@server-b ~]# tcpdump -i eth1 icmp -nn
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth1, link-type EN10MB (Ethernet), capture size 262144 bytes
18:18:32.697942 IP 172.16.5.6 > 172.16.7.6: ICMP echo request, id 31749, seq 1, length 64
18:18:33.697256 IP 172.16.5.6 > 172.16.7.6: ICMP echo request, id 31749, seq 2, length 64
18:18:34.697301 IP 172.16.5.6 > 172.16.7.6: ICMP echo request, id 31749, seq 3, length 64
18:18:35.697375 IP 172.16.5.6 > 172.16.7.6: ICMP echo request, id 31749, seq 4, length 64
```

```txt:Server-B(eth3)からも出て行っている。
[root@server-b ~]# tcpdump -i eth1 icmp -nn
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth1, link-type EN10MB (Ethernet), capture size 262144 bytes
18:18:55.736464 IP 172.16.5.6 > 172.16.7.6: ICMP echo request, id 31750, seq 1, length 64
18:18:56.736199 IP 172.16.5.6 > 172.16.7.6: ICMP echo request, id 31750, seq 2, length 64
18:18:57.736289 IP 172.16.5.6 > 172.16.7.6: ICMP echo request, id 31750, seq 3, length 64
18:18:58.736299 IP 172.16.5.6 > 172.16.7.6: ICMP echo request, id 31750, seq 4, length 64
```

```txt:Server-Bでtcpdumpでinterfaceをanyで指定すると2つずつパケットが表示されるが、これはeth1から入ってくる分とeth3から出て行く分が表示されている。
[root@server-b ~]# tcpdump -i any icmp -nn
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes
18:16:57.750521 IP 172.16.5.6 > 172.16.7.6: ICMP echo request, id 31707, seq 1, length 64
18:16:57.750540 IP 172.16.5.6 > 172.16.7.6: ICMP echo request, id 31707, seq 1, length 64
18:16:58.750167 IP 172.16.5.6 > 172.16.7.6: ICMP echo request, id 31707, seq 2, length 64
18:16:58.750189 IP 172.16.5.6 > 172.16.7.6: ICMP echo request, id 31707, seq 2, length 64
18:16:59.750230 IP 172.16.5.6 > 172.16.7.6: ICMP echo request, id 31707, seq 3, length 64
18:16:59.750253 IP 172.16.5.6 > 172.16.7.6: ICMP echo request, id 31707, seq 3, length 64
18:17:00.750331 IP 172.16.5.6 > 172.16.7.6: ICMP echo request, id 31707, seq 4, length 64
18:17:00.750353 IP 172.16.5.6 > 172.16.7.6: ICMP echo request, id 31707, seq 4, length 64
```

```txt:Server-Cには届いていない
[root@server-c ~]# tcpdump -i any icmp -nn
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes

（何も出力されない）
```

よって、Server-Bからはパケットは送出されているため、OSの問題ではありません。この場合、Server-Cに届かない理由として考えられるのはVPC側の構成です。

#5. IP Spoofingの有効化

##5-1. Server-Bにて、eth1/eth3でのIP Spoofingの有効化
Security GroupやNetwork ACLでブロックしていないのであれば、別の問題が考えられます。
実は、これはServer-Bのeth3には`172.16.7.4`が割り当てられているので、eth3から送信するパケットのSource IP通信は`172.16.7.4`であるはずなのに、所有していないIPアドレス`172.16.5.6`をsource addressとしてServer-Bが送信しているため、IPアドレスが偽装されていると見なされてVPC側でブロックされているのです。詳細は[こちら](https://qiita.com/testnin2/items/eef96d180583b6a87fda)もご参照ください。
これを防ぐためには、`IP Spoofing`を有効化する必要があります。[ここ](https://qiita.com/testnin2/items/eef96d180583b6a87fda#4-ip-spoofing%E3%81%AE%E6%9C%89%E5%8A%B9%E5%8C%96)にも記載しているとおり、IAMでVPCにおける`IP Spoofing Operatorロール`を有効化しておけば、この変更が可能になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62127/03ace927-d079-d2ea-f4dd-2163f4d71576.png)

##5-2. IP Spoofing後のテスト

ちゃんと届くようになりました！

```txt:Server-AからServer-Cにpingを実行
[root@server-a ~]# ping 172.16.7.6
PING 172.16.7.6 (172.16.7.6) 56(84) bytes of data.
64 bytes from 172.16.7.6: icmp_seq=1 ttl=63 time=0.628 ms
64 bytes from 172.16.7.6: icmp_seq=2 ttl=63 time=0.711 ms
64 bytes from 172.16.7.6: icmp_seq=3 ttl=63 time=0.763 ms
64 bytes from 172.16.7.6: icmp_seq=4 ttl=63 time=0.808 ms
```

```パケットが届くようになった
[root@server-c ~]# tcpdump -i any icmp -nn
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes
06:20:29.528914 IP 172.16.5.6 > 172.16.7.6: ICMP echo request, id 6520, seq 1, length 64
06:20:29.528946 IP 172.16.7.6 > 172.16.5.6: ICMP echo reply, id 6520, seq 1, length 64
06:20:30.528934 IP 172.16.5.6 > 172.16.7.6: ICMP echo request, id 6520, seq 2, length 64
06:20:30.528962 IP 172.16.7.6 > 172.16.5.6: ICMP echo reply, id 6520, seq 2, length 64
06:20:31.529227 IP 172.16.5.6 > 172.16.7.6: ICMP echo request, id 6520, seq 3, length 64
06:20:31.529258 IP 172.16.7.6 > 172.16.5.6: ICMP echo reply, id 6520, seq 3, length 64
```