IBM Cloud: protocol state filteringとは何か

# 1. はじめに

`protocol state filtering`という機能がリリースされたので、この機能の紹介と機能検証を実施します。ドキュメントはこちら。

- https://cloud.ibm.com/docs/vpc?topic=vpc-release-notes&locale=en#vpc-may3124
- https://cloud.ibm.com/docs/vpc?topic=vpc-vni-about&locale=en#protocol-state-filtering


# 2. protocol state filtering とは

VNIは、IBM Cloudのサーバーに付与されるネットワークインターフェースです。（より厳密なVNIとは何かについての詳細は、[IBM Cloud: Network Attachments/Virtual Network Interface(VNI)方式とLegacy Network Interface方式の違い](https://qiita.com/testnin2/items/75f893329aa923248325) を参照してください。）。

- サーバーにVNI1/VNI2という2つのインターフェースが存在する場合、下図のようにVNI1に入ってきたパケットがVNI1から戻り、VNI2に入ってきたパケットがVNI2から戻っていく、つまり通信経路が対称的であれば特に問題はありません。![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62127/1e33a7f1-d27c-e83d-ef66-5f7ca8506d9d.png)

- しかし、サーバーの初期構成のrouting tableでは、default gatewayがVNI1側のインターフェースに対して構成されているため、VNI2からサーバーに入ってきたパケットがクライアントに返る際にはVNI1側から返ろうとします（非対称ルーティング）。これを避けて、通信経路を対称的にするためには、[ここ](https://qiita.com/testnin2/items/c54d9e5213e86676c791)で設定したように、policy based routingなどの特別な構成が必要になります。
    - 一般的に、主なLinux環境のデフォルト構成では、このようなパケットが入ってきたインターフェースとパケットが出ていくインターフェースが異なるような非対称ルーティングはブロックされています。この非対称ルーティングをブロックしているのはLinux Kernelにおける`rp_filter`というパラメーターです。以下のKBも参考になります。細かいことは省きますが、要は`rp_filter`を2にすると非対称ルーティングは可能になります。
        - [sysctl パラメーターの「all」、「default」、および特定のデバイスの違いは何ですか?](https://access.redhat.com/ja/solutions/6098561)
    - これに加えて、IBM CloudのVPCでは、VNIにて `TCP Connection の protocol state` をチェックしています。そのため、VNI1からパケットを戻ろうとした際に、このパケットはdropされてしまいます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62127/df58b346-0b4d-7293-f71a-4ba8fc701e71.png)
- 今回の新機能において、TCP Connection の protocol state をチェックしないようにすることができるようになりました。これによって、VNI1からパケットを戻ろうとした際に、パケットがdropされることはありません。 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62127/ff204b8f-e2b2-ed65-609e-82020756ada0.png)


protocol state filtering においては、他に以下の注意点があります。

- defaultの設定値は`auto`です。この`auto`の値は、以下のとおり。
    - Bare Metal Server では`disabled`
    - VSIでは`enabled`
    - File share mount target では`enabled`
- ICMPには関係ない。例えば、pingは`protocol state filtering`の値に関係なく応答する。
- Floating IP経由でのアクセス（インターネットからのアクセス）における非対称ルーティング構成は、`protocol state filtering`を無効にしただけでは本稿執筆時点のVPCではまだ動かない。同一VPCからのアクセスや、異なるVPCからのTGW経由でのアクセスの際であれば、`protocol state filtering`を無効にするだけで機能する。


# 検証環境

接続先サーバーの構成は以下の通り。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62127/e44b8225-5749-ddd9-3120-f043e4bc679a.png)

```
[root@syasuda-vni-test1 ~]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 02:00:05:02:70:58 brd ff:ff:ff:ff:ff:ff
    altname enp0s3
    altname ens3
    inet 10.0.0.11/24 brd 10.0.0.255 scope global dynamic noprefixroute eth0
       valid_lft 304sec preferred_lft 304sec
    inet6 fe80::5ff:fe02:7058/64 scope link noprefixroute
       valid_lft forever preferred_lft forever
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 02:00:25:02:70:58 brd ff:ff:ff:ff:ff:ff
    altname enp0s4
    altname ens4
    inet 10.0.0.12/24 brd 10.0.0.255 scope global dynamic noprefixroute eth1
       valid_lft 235sec preferred_lft 235sec
    inet6 fe80::643b:5cef:7e46:542a/64 scope link noprefixroute
       valid_lft forever preferred_lft forever
       
[root@syasuda-vni-target ~]# ip r
default via 10.0.0.1 dev eth0
default via 10.0.0.1 dev eth0 proto dhcp src 10.0.0.11 metric 100
10.0.0.0/24 dev eth0 proto kernel scope link src 10.0.0.11 metric 100
10.0.0.0/24 dev eth1 proto kernel scope link src 10.0.0.12 metric 101
```

## 検証1: VSIプロビジョニング直後の構成

```txt:クライアントからのpingに失敗する
[root@syasuda-client ~]# ping 10.0.0.12
PING 10.0.0.12 (10.0.0.12) 56(84) bytes of data.
^C
--- 10.0.0.12 ping statistics ---
6 packets transmitted, 0 received, 100% packet loss, time 5114ms
```
```txt:サーバーでのtcpdumpの結果。そもそもサーバーが戻りのパケットを返さない。
[root@syasuda-vni-target ~]# tcpdump -i any icmp or port 80 -nn
tcpdump: data link type LINUX_SLL2
dropped privs to tcpdump
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on any, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes
02:11:12.840133 eth1  In  IP 10.8.0.4 > 10.0.0.12: ICMP echo request, id 4, seq 1, length 64
02:11:13.858275 eth1  In  IP 10.8.0.4 > 10.0.0.12: ICMP echo request, id 4, seq 2, length 64
02:11:14.882252 eth1  In  IP 10.8.0.4 > 10.0.0.12: ICMP echo request, id 4, seq 3, length 64
02:11:15.906330 eth1  In  IP 10.8.0.4 > 10.0.0.12: ICMP echo request, id 4, seq 4, length 64
02:11:16.930260 eth1  In  IP 10.8.0.4 > 10.0.0.12: ICMP echo request, id 4, seq 5, length 64
02:11:17.954309 eth1  In  IP 10.8.0.4 > 10.0.0.12: ICMP echo request, id 4, seq 6, length 64
```
```txt:クライアントからのcurlに失敗する
[root@syasuda-client ~]# curl 10.0.0.12
（応答なし）
```
```txt:サーバーでのtcpdumpの結果。そもそもサーバーが戻りのパケットを返さない。
[root@syasuda-vni-target ~]# tcpdump -i any icmp or port 80 -nn
tcpdump: data link type LINUX_SLL2
dropped privs to tcpdump
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on any, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes
02:11:48.023862 eth1  In  IP 10.8.0.4.37694 > 10.0.0.12.80: Flags [S], seq 3995821265, win 32120, options [mss 1460,sackOK,TS val 1211393662 ecr 0,nop,wscale 7], length 0
02:11:49.058427 eth1  In  IP 10.8.0.4.37694 > 10.0.0.12.80: Flags [S], seq 3995821265, win 32120, options [mss 1460,sackOK,TS val 1211394697 ecr 0,nop,wscale 7], length 0
02:11:51.106363 eth1  In  IP 10.8.0.4.37694 > 10.0.0.12.80: Flags [S], seq 3995821265, win 32120, options [mss 1460,sackOK,TS val 1211396745 ecr 0,nop,wscale 7], length 0
02:11:55.138333 eth1  In  IP 10.8.0.4.37694 > 10.0.0.12.80: Flags [S], seq 3995821265, win 32120, options [mss 1460,sackOK,TS val 1211400777 ecr 0,nop,wscale 7], length 0
```


## 検証2: rp_filterを変更した構成

```txt:rp_filterの変更
[root@syasuda-vni-target ~]# sysctl -a |grep '\.rp_filter'
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

[root@syasuda-vni-target ~]# sysctl -w net.ipv4.conf.all.rp_filter=2
[root@syasuda-vni-target ~]# sysctl -w net.ipv4.conf.default.rp_filter=2

[root@syasuda-vni-target ~]# sysctl -a |grep '\.rp_filter'
net.ipv4.conf.all.rp_filter = 2
net.ipv4.conf.default.rp_filter = 2
net.ipv4.conf.eth0.rp_filter = 1
net.ipv4.conf.eth1.rp_filter = 1
net.ipv4.conf.lo.rp_filter = 1
```


```txt:クライアントからのpingは成功する。
[root@syasuda-client ~]# ping 10.0.0.12
PING 10.0.0.12 (10.0.0.12) 56(84) bytes of data.
64 bytes from 10.0.0.12: icmp_seq=1 ttl=56 time=1.89 ms
64 bytes from 10.0.0.12: icmp_seq=2 ttl=56 time=1.92 ms
64 bytes from 10.0.0.12: icmp_seq=3 ttl=56 time=1.84 ms
64 bytes from 10.0.0.12: icmp_seq=4 ttl=56 time=1.68 ms
64 bytes from 10.0.0.12: icmp_seq=5 ttl=56 time=1.77 ms
^C
--- 10.0.0.12 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4007ms
rtt min/avg/max/mdev = 1.683/1.820/1.923/0.086 ms
```
```txt:targetでのtcpdumpの結果。パケットをeth1で受け取って、eth0から返している。
[root@syasuda-vni-target ~]# tcpdump -i any icmp or port 80 -nn
tcpdump: data link type LINUX_SLL2
dropped privs to tcpdump
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on any, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes
02:12:33.654077 eth1  In  IP 10.8.0.4 > 10.0.0.12: ICMP echo request, id 5, seq 1, length 64
02:12:33.654128 eth0  Out IP 10.0.0.12 > 10.8.0.4: ICMP echo reply, id 5, seq 1, length 64
02:12:34.656216 eth1  In  IP 10.8.0.4 > 10.0.0.12: ICMP echo request, id 5, seq 2, length 64
02:12:34.656263 eth0  Out IP 10.0.0.12 > 10.8.0.4: ICMP echo reply, id 5, seq 2, length 64
02:12:35.657168 eth1  In  IP 10.8.0.4 > 10.0.0.12: ICMP echo request, id 5, seq 3, length 64
02:12:35.657225 eth0  Out IP 10.0.0.12 > 10.8.0.4: ICMP echo reply, id 5, seq 3, length 64
02:12:36.659065 eth1  In  IP 10.8.0.4 > 10.0.0.12: ICMP echo request, id 5, seq 4, length 64
02:12:36.659110 eth0  Out IP 10.0.0.12 > 10.8.0.4: ICMP echo reply, id 5, seq 4, length 64
02:12:37.660991 eth1  In  IP 10.8.0.4 > 10.0.0.12: ICMP echo request, id 5, seq 5, length 64
02:12:37.661017 eth0  Out IP 10.0.0.12 > 10.8.0.4: ICMP echo reply, id 5, seq 5, length 64
```
```txt:クライアントからのcurlに失敗する
[root@new-syasuda-tok1-vpc1 ~]# curl 10.0.0.12
（応答なし）
```
```txt:クライアントでのtcpdumpの結果。クライアントはサーバーからACKを受け取っていない。
[root@syasuda-client ~]# tcpdump -i any icmp or port 80 -nn
tcpdump: data link type LINUX_SLL2
dropped privs to tcpdump
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on any, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes
02:22:29.579725 eth0  Out IP 10.8.0.4.56488 > 10.0.0.12.80: Flags [S], seq 2134132328, win 32120, options [mss 1460,sackOK,TS val 1212035220 ecr 0,nop,wscale 7], length 0
02:22:30.592493 eth0  Out IP 10.8.0.4.56488 > 10.0.0.12.80: Flags [S], seq 2134132328, win 32120, options [mss 1460,sackOK,TS val 1212036233 ecr 0,nop,wscale 7], length 0
02:22:32.640496 eth0  Out IP 10.8.0.4.56488 > 10.0.0.12.80: Flags [S], seq 2134132328, win 32120, options [mss 1460,sackOK,TS val 1212038281 ecr 0,nop,wscale 7], length 0
02:22:36.672515 eth0  Out IP 10.8.0.4.56488 > 10.0.0.12.80: Flags [S], seq 2134132328, win 32120, options [mss 1460,sackOK,TS val 1212042313 ecr 0,nop,wscale 7], length 0
```
```txt:サーバーでのtcpdumpの結果。パケットをeth1で受け取って、eth0から返しているが、クライアントからの応答がないので何度か再送している。
[root@syasuda-vni-target ~]# tcpdump -i any icmp or port 80 -nn
tcpdump: data link type LINUX_SLL2
dropped privs to tcpdump
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on any, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes
02:22:29.581629 eth1  In  IP 10.8.0.4.56488 > 10.0.0.12.80: Flags [S], seq 2134132328, win 32120, options [mss 1460,sackOK,TS val 1212035220 ecr 0,nop,wscale 7], length 0
02:22:29.581687 eth0  Out IP 10.0.0.12.80 > 10.8.0.4.56488: Flags [S.], seq 2110509886, ack 2134132329, win 31856, options [mss 1460,sackOK,TS val 600171014 ecr 1212035220,nop,wscale 7], length 0
02:22:30.583361 eth0  Out IP 10.0.0.12.80 > 10.8.0.4.56488: Flags [S.], seq 2110509886, ack 2134132329, win 31856, options [mss 1460,sackOK,TS val 600172016 ecr 1212035220,nop,wscale 7], length 0
02:22:30.594473 eth1  In  IP 10.8.0.4.56488 > 10.0.0.12.80: Flags [S], seq 2134132328, win 32120, options [mss 1460,sackOK,TS val 1212036233 ecr 0,nop,wscale 7], length 0
02:22:30.594492 eth0  Out IP 10.0.0.12.80 > 10.8.0.4.56488: Flags [S.], seq 2110509886, ack 2134132329, win 31856, options [mss 1460,sackOK,TS val 600172027 ecr 1212035220,nop,wscale 7], length 0
02:22:32.631355 eth0  Out IP 10.0.0.12.80 > 10.8.0.4.56488: Flags [S.], seq 2110509886, ack 2134132329, win 31856, options [mss 1460,sackOK,TS val 600174064 ecr 1212035220,nop,wscale 7], length 0
02:22:32.642741 eth1  In  IP 10.8.0.4.56488 > 10.0.0.12.80: Flags [S], seq 2134132328, win 32120, options [mss 1460,sackOK,TS val 1212038281 ecr 0,nop,wscale 7], length 0
02:22:32.642764 eth0  Out IP 10.0.0.12.80 > 10.8.0.4.56488: Flags [S.], seq 2110509886, ack 2134132329, win 31856, options [mss 1460,sackOK,TS val 600174076 ecr 1212035220,nop,wscale 7], length 0
02:22:36.663361 eth0  Out IP 10.0.0.12.80 > 10.8.0.4.56488: Flags [S.], seq 2110509886, ack 2134132329, win 31856, options [mss 1460,sackOK,TS val 600178096 ecr 1212035220,nop,wscale 7], length 0
02:22:36.674601 eth1  In  IP 10.8.0.4.56488 > 10.0.0.12.80: Flags [S], seq 2134132328, win 32120, options [mss 1460,sackOK,TS val 1212042313 ecr 0,nop,wscale 7], length 0
02:22:36.674625 eth0  Out IP 10.0.0.12.80 > 10.8.0.4.56488: Flags [S.], seq 2110509886, ack 2134132329, win 31856, options [mss 1460,sackOK,TS val 600178107 ecr 1212035220,nop,wscale 7], length 0
02:22:45.111350 eth0  Out IP 10.0.0.12.80 > 10.8.0.4.56488: Flags [S.], seq 2110509886, ack 2134132329, win 31856, options [mss 1460,sackOK,TS val 600186544 ecr 1212035220,nop,wscale 7], length 0
02:23:01.495353 eth0  Out IP 10.0.0.12.80 > 10.8.0.4.56488: Flags [S.], seq 2110509886, ack 2134132329, win 31856, options [mss 1460,sackOK,TS val 600202928 ecr 1212035220,nop,wscale 7], length 0
```

上記の結果から、TCPの場合は、protocol state filteringが働いて、サーバーからのパケットの返信がクライアントに届いていないことが分かります。

## 検証3: rp_filterを変更し、なおかつprotocol state filteringを変更した構成

戻りのパケットがeth0に紐づいているVNIを経由する際にフィルタリングされているため、eth0側のVNIにおけるprotocol state filteringを明示的にdisabledにします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62127/c14f102b-1c5d-3176-4428-256b67e382d3.png)

```txt:pingは成功。
[root@syasuda-client ~]# ping 10.0.0.12
PING 10.0.0.12 (10.0.0.12) 56(84) bytes of data.
64 bytes from 10.0.0.12: icmp_seq=1 ttl=56 time=2.02 ms
64 bytes from 10.0.0.12: icmp_seq=2 ttl=56 time=1.83 ms
64 bytes from 10.0.0.12: icmp_seq=3 ttl=56 time=1.78 ms
64 bytes from 10.0.0.12: icmp_seq=4 ttl=56 time=1.88 ms
64 bytes from 10.0.0.12: icmp_seq=5 ttl=56 time=1.98 ms
^C
--- 10.0.0.12 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4007ms
rtt min/avg/max/mdev = 1.779/1.897/2.022/0.091 ms
```

```txt:curlも成功
[root@syasuda-client ~]# curl 10.0.0.12
Hellow World!
```