Open vSwitchでパケットロスのトラブルシューティング
Open vSwitch(OVS)を使った仮想マシン上で特定のサイトでWebページの読み込みができない事象が発生しました。 仮想環境の構成は以下の通りです。
VM側では下記のように外部セグメント(br-outside)と内部セグメント(br-inside)に接続されています。
jinglan@vm-linux:~$ 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: enp1s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 link/ether 52:54:00:bf:71:af brd ff:ff:ff:ff:ff:ff inet 172.16.0.8/16 brd 172.31.255.255 scope global enp1s0 valid_lft forever preferred_lft forever inet6 fe80::5054:ff:febf:71af/64 scope link valid_lft forever preferred_lft forever 3: enp7s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 link/ether 52:54:00:ae:d2:98 brd ff:ff:ff:ff:ff:ff inet6 2400::feae/64 scope global dynamic mngtmpaddr valid_lft 2591933sec preferred_lft 604733sec inet6 fe80::d298/64 scope link valid_lft forever preferred_lft forever
まず、通信を確認します。
jinglan@vm-linux:~$ curl -k https://www.example.net/ <!DOCTYPE html> <html><head> <title>TEST</title> </head><body> <img src="img.png"> </body></html>
HTML自体は読み込むことができます。続いて、画像リソースを試します。
jinglan@vm-linux:~$ curl -k https://www.example.net/img.png curl: (56) OpenSSL SSL_read: SSL_ERROR_SYSCALL, errno 104
要求がタイムアウトし読み込むことができません。念のため、pingによりロスが発生するか確認します。
jinglan@vm-linux:~$ ping www.example.net -c 20 PING www.example.net(www.example.net (2401::6)) 56 data bytes (略) --- www.example.net ping statistics --- 20 packets transmitted, 20 received, 0% packet loss, time 52ms rtt min/avg/max/mdev = 25.105/25.518/26.138/0.326 ms jinglan@vm-linux:~$
pingによるロスはないため通信経路上の可能性は低くなります。続いて、tsharkによるパケットキャプチャにより解析します。
jinglan@vm-linux:~$ sudo tshark -i enp7s0 Capturing on 'enp7s0' 3 12.147649629 2400::feae ? 2401::6 TCP 94 37256 ? 443 [SYN] Seq=0 Win=64800 Len=0 MSS=1440 SACK_PERM=1 TSval=1001037672 TSecr=0 WS=64 4 12.178062944 2401::6 ? 2400::feae TCP 96 443 ? 37256 [SYN, ACK] Seq=0 Ack=1 Win=28560 Len=0 MSS=1440 SACK_PERM=1 TSval=2917789360 TSecr=1001037672 WS=64 5 12.178201144 2400::feae ? 2401::6 TCP 86 37256 ? 443 [ACK] Seq=1 Ack=1 Win=64832 Len=0 TSval=1001037702 TSecr=2917789360 6 12.178781920 2400::feae ? 2401::6 ICMPv6 91 Echo (ping) request id=0x2d02, seq=9728, hop limit=64 7 12.181938940 2400::feae ? 2401::6 TLSv1 603 Client Hello 8 12.204432618 2401::6 ? 2400::feae ICMPv6 93 Echo (ping) reply id=0x2d02, seq=9728, hop limit=50 (request in 6) 9 12.208828328 2401::6 ? 2400::feae TCP 88 443 ? 37256 [ACK] Seq=1 Ack=518 Win=29632 Len=0 TSval=2917789391 TSecr=1001037706 10 12.209173463 2401::6 ? 2400::feae TLSv1.2 1426 Server Hello, Certificate, Server Key Exchange, Server Hello Done 11 12.209221425 2400::feae ? 2401::6 TCP 86 37256 ? 443 [ACK] Seq=518 Ack=1339 Win=64128 Len=0 TSval=1001037733 TSecr=2917789392 12 12.213361297 2400::feae ? 2401::6 TLSv1.2 179 Client Key Exchange, Change Cipher Spec, Encrypted Handshake Message 13 12.237201720 2401::6 ? 2400::feae TLSv1.2 378 New Session Ticket, Change Cipher Spec, Encrypted Handshake Message 14 12.260303449 2400::feae ? 2401::6 TLSv1.2 513 Application Data 15 12.285520418 2401::6 ? 2400::feae TLSv1.2 864 [TCP Previous segment not captured] , Ignored Unknown Record 16 12.285595307 2400::feae ? 2401::6 TCP 98 [TCP Dup ACK 14#1] 37256 ? 443 [ACK] Seq=1038 Ack=1629 Win=64128 Len=0 TSval=1001037810 TSecr=2917789422 SLE=10197 SRE=10973 17 17.288850168 2401::6 ? 2400::feae TLSv1.2 119 Encrypted Alert 18 17.288955979 2400::feae ? 2401::6 TCP 98 [TCP Dup ACK 14#2] 37256 ? 443 [ACK] Seq=1038 Ack=1629 Win=64128 Len=0 TSval=1001042813 TSecr=2917789422 SLE=10197 SRE=11005
挙動を見ると、後半に[TCP Previous segment not captured]とEncrypted Alert、TCP Dup ACKが確認できます。Encrypted Alertは、一部キャプチャができなかったため暗号化された通信を解析できなかったことを示します。TCP Dup ACKはパケットロスにおり再送を要求しています。方向を見るとクライアント側(2400::feae)からサーバ(2401::6)へ送っていることがわかります。
ethtoolを使いNICの状態を確認します。
jinglan@vm-linux:~$ sudo ethtool -S enp7s0 NIC statistics: rx_packets: 190518 tx_packets: 592 rx_bytes: 33578858 tx_bytes: 15763799 rx_broadcast: 4 tx_broadcast: 0 rx_multicast: 358 tx_multicast: 8 rx_errors: 0 tx_errors: 0 tx_dropped: 0 multicast: 358 collisions: 0 rx_length_errors: 0 rx_over_errors: 0 rx_crc_errors: 0 rx_frame_errors: 0 rx_no_buffer_count: 0 rx_missed_errors: 0 tx_aborted_errors: 0 tx_carrier_errors: 0 tx_fifo_errors: 0 tx_heartbeat_errors: 0 tx_window_errors: 0 tx_abort_late_coll: 0 tx_deferred_ok: 0 tx_single_coll_ok: 0 tx_multi_coll_ok: 0 tx_timeout_count: 0 tx_restart_queue: 0 rx_long_length_errors: 0 rx_short_length_errors: 0 rx_align_errors: 0 tx_tcp_seg_good: 0 tx_tcp_seg_failed: 0 rx_flow_control_xon: 0 rx_flow_control_xoff: 0 tx_flow_control_xon: 0 tx_flow_control_xoff: 0 rx_csum_offload_good: 229 rx_csum_offload_errors: 0 rx_header_split: 0 alloc_rx_buff_failed: 0 tx_smbus: 0 rx_smbus: 0 dropped_smbus: 0 rx_dma_failed: 0 tx_dma_failed: 0 rx_hwtstamp_cleared: 0 uncorr_ecc_errors: 0 corr_ecc_errors: 0 tx_hwtstamp_timeouts: 0 tx_hwtstamp_skipped: 0
rx_errorsやtx_errorsが共に0のためVM上ではパケットを破棄していません。
今度は、仮想環境のホスト側を調査します。ovs-vsctlでブリッジの状態を確認します。
jinglan@host:~$ sudo ovs-vsctl show adb46660-6664-4346-ac81-faa417b0b793 Bridge br-outside Port "enx18c2bf547ad4" Interface "enx18c2bf547ad4" Port br-outside Interface br-outside type: internal
OVSのブリッジは物理NICのenx18c2bf547ad4に接続されています。ブリッジのMACアドレステーブルを確認します。
jinglan@host:~$ sudo ovs-appctl fdb/show br-outside port VLAN MAC Age LOCAL 0 52:54:00:d6:aa:71 16 LOCAL 0 52:54:00:ae:d2:98 5 1 0 00:12:e2:70:f1:f4 5 1 0 00:12:e2:70:c9:75 1 LOCAL 0 52:54:00:99:77:84 1
ブリッジにVMのMACアドレスが見えており、正常に接続されていることが確認できます。 続いてポートの状態を確認します。
jinglan@host:~$ sudo ovs-ofctl dump-ports br-outside OFPST_PORT reply (xid=0x2): 2 ports port enx18c2bf547ad4: rx pkts=15974409, bytes=20286845338, drop=0, errs=0, frame=0, over=0, crc=0 tx pkts=10378604, bytes=1155053709, drop=0, errs=0, coll=0 port LOCAL: rx pkts=15972192, bytes=20284180173, drop=0, errs=0, frame=0, over=0, crc=0 tx pkts=10377874, bytes=1071920536, drop=0, errs=1284, coll=0
port LOCALにてerrsがカウントされています。VM側から再度問題のページにcurlで接続します。
jinglan@host:~$ sudo ovs-ofctl dump-ports br-outside OFPST_PORT reply (xid=0x2): 2 ports port enx18c2bf547ad4: rx pkts=15979540, bytes=20293414941, drop=0, errs=0, frame=0, over=0, crc=0 tx pkts=10382454, bytes=1155449792, drop=0, errs=0, coll=0 port LOCAL: rx pkts=15977305, bytes=20290722740, drop=0, errs=0, frame=0, over=0, crc=0 tx pkts=10381724, bytes=1072285819, drop=0, errs=1302, coll=0
errsの値がカウントアップしています。ログからエラーの内容を確認してみます。
jinglan@host:~$ sudo tail /var/log/messages Jun 12 14:28:35 host kernel: [414700.237408] br-outside: dropped over-mtu packet: 1502 > 1500 Jun 12 14:28:41 host kernel: [414705.869334] br-outside: dropped over-mtu packet: 1502 > 1500 Jun 12 14:29:18 host kernel: [414743.245290] br-outside: dropped over-mtu packet: 1502 > 1500
MTUの値が1500を超えたために破棄されたことが分かります。下記コマンドでMTUの大きなパケットを許容します。
jinglan@host:~$ sudo ovs-vsctl set int br-outside mtu_request=1502
curlで再度テストすると正常に通信が行われ、解決となりました。 この追加された2バイトはUSBネットワークアダプタにより追加され、通常にNICよりも大きくなるもののようです。
仮想化技術が普及し当たり前になりましたが、それに伴い低レイヤーの知識が不要になる訳ではないと痛感しました。