はじめに
gRPC の Go の QuickStart チュートリアルをやった時の備忘 - untitled をやってみた流れで gRPC の通信を WireShark でキャプチャしてみました。
環境
以下を使います。 セットアップの方法については省略します。
tcpdump でのキャプチャ
TCP ダンプでキャプチャを開始します。
% sudo tcpdump -A -n -i lo -W /tmp/grpc-capture.dump
gRPCサーバーを起動します。
~/path/to/grpc-go/examples/helloworld/greeter_server ((v1.63.0)) % go run main.go 2024/08/29 02:12:36 server listening at [::]:50051
gRPCクライアントを起動します。
~/path/to/grpc-go/examples/helloworld/greeter_client ((v1.63.0)) % go run main.go 2024/08/29 02:15:57 Greeting: Hello world
Wireshark で読み込み
以下のようになっていました。
開始(Frame1-3)は 3ウェイハンドシェイクで始まり、FIN/ACK -> FIN/ACK -> ACK でクローズ(Frame18-20)しています。
Frame4-11 でMagic, SETTINGS, WINDOW_UPDATE, PING というフレームが送信されています。これらはHTTP2のものと思います。 間の Frame12,15 が gRPC のリクエスト、レスポンスのようです。
いくつかわかりやすいヘッダーを見てみます。 Content-Type が「application/grpc」で始まっていない場合、gRPC サーバーは HTTP ステータス 415 (サポートされていないメディア タイプ) で応答する必要があるとのことで、ここでは application/grpc になっていました。
User-Agent は必須ではないようですが、user-agent: grpc-go/1.63.0 となっていました。
Frame 12: 216 bytes on wire (1728 bits), 216 bytes captured (1728 bits) Encapsulation type: Ethernet (1) Arrival Time: Aug 29, 2024 11:15:57.925436000 JST UTC Arrival Time: Aug 29, 2024 02:15:57.925436000 UTC Epoch Arrival Time: 1724897757.925436000 [Time shift for this packet: 0.000000000 seconds] [Time delta from previous captured frame: 0.000108000 seconds] [Time delta from previous displayed frame: 0.000108000 seconds] [Time since reference or first frame: 0.000396000 seconds] Frame Number: 12 Frame Length: 216 bytes (1728 bits) Capture Length: 216 bytes (1728 bits) [Frame is marked: False] [Frame is ignored: False] [Protocols in frame: eth:ethertype:ipv6:tcp:http2:grpc:protobuf] [Coloring Rule Name: HTTP] [Coloring Rule String: http || tcp.port == 80 || http2] Ethernet II, Src: 00:00:00_00:00:00 (00:00:00:00:00:00), Dst: 00:00:00_00:00:00 (00:00:00:00:00:00) Destination: 00:00:00_00:00:00 (00:00:00:00:00:00) .... ..0. .... .... .... .... = LG bit: Globally unique address (factory default) .... ...0 .... .... .... .... = IG bit: Individual address (unicast) Source: 00:00:00_00:00:00 (00:00:00:00:00:00) .... ..0. .... .... .... .... = LG bit: Globally unique address (factory default) .... ...0 .... .... .... .... = IG bit: Individual address (unicast) Type: IPv6 (0x86dd) [Stream index: 0] Internet Protocol Version 6, Src: ::1, Dst: ::1 0110 .... = Version: 6 .... 0000 0000 .... .... .... .... .... = Traffic Class: 0x00 (DSCP: CS0, ECN: Not-ECT) .... 0000 00.. .... .... .... .... .... = Differentiated Services Codepoint: Default (0) .... .... ..00 .... .... .... .... .... = Explicit Congestion Notification: Not ECN-Capable Transport (0) .... 0001 1000 1010 0001 0110 = Flow Label: 0x18a16 Payload Length: 162 Next Header: TCP (6) Hop Limit: 64 Source Address: ::1 [Address Space: Reserved by IETF] [Special-Purpose Allocation: Loopback Address] [Source: False] [Destination: False] [Forwardable: False] [Globally Reachable: False] [Reserved-by-Protocol: True] Destination Address: ::1 [Address Space: Reserved by IETF] [Special-Purpose Allocation: Loopback Address] [Source: False] [Destination: False] [Forwardable: False] [Globally Reachable: False] [Reserved-by-Protocol: True] [Stream index: 0] Transmission Control Protocol, Src Port: 55646, Dst Port: 50051, Seq: 34, Ack: 25, Len: 130 Source Port: 55646 Destination Port: 50051 [Stream index: 0] [Stream Packet Number: 12] [Conversation completeness: Complete, WITH_DATA (31)] ..0. .... = RST: Absent ...1 .... = FIN: Present .... 1... = Data: Present .... .1.. = ACK: Present .... ..1. = SYN-ACK: Present .... ...1 = SYN: Present [Completeness Flags: ·FDASS] [TCP Segment Len: 130] Sequence Number: 34 (relative sequence number) Sequence Number (raw): 3678708405 [Next Sequence Number: 164 (relative sequence number)] Acknowledgment Number: 25 (relative ack number) Acknowledgment number (raw): 3438549865 1000 .... = Header Length: 32 bytes (8) Flags: 0x018 (PSH, ACK) 000. .... .... = Reserved: Not set ...0 .... .... = Accurate ECN: Not set .... 0... .... = Congestion Window Reduced: Not set .... .0.. .... = ECN-Echo: Not set .... ..0. .... = Urgent: Not set .... ...1 .... = Acknowledgment: Set .... .... 1... = Push: Set .... .... .0.. = Reset: Not set .... .... ..0. = Syn: Not set .... .... ...0 = Fin: Not set [TCP Flags: ·······AP···] Window: 512 [Calculated window size: 65536] [Window size scaling factor: 128] Checksum: 0x00aa [unverified] [Checksum Status: Unverified] Urgent Pointer: 0 Options: (12 bytes), No-Operation (NOP), No-Operation (NOP), Timestamps TCP Option - No-Operation (NOP) Kind: No-Operation (1) TCP Option - No-Operation (NOP) Kind: No-Operation (1) TCP Option - Timestamps: TSval 848634857, TSecr 848634857 Kind: Time Stamp Option (8) Length: 10 Timestamp value: 848634857 Timestamp echo reply: 848634857 [Timestamps] [Time since first frame in this TCP stream: 0.000396000 seconds] [Time since previous frame in this TCP stream: 0.000108000 seconds] [SEQ/ACK analysis] [iRTT: 0.000029000 seconds] [Bytes in flight: 130] [Bytes sent since last PSH flag: 130] TCP payload (130 bytes) [PDU Size: 9] [PDU Size: 100] [PDU Size: 21] HyperText Transfer Protocol 2 Stream: SETTINGS, Stream ID: 0, Length 0 Length: 0 Type: SETTINGS (4) Flags: 0x01, ACK 0000 000. = Unused: 0x00 .... ...1 = ACK: True 0... .... .... .... .... .... .... .... = Reserved: 0x0 .000 0000 0000 0000 0000 0000 0000 0000 = Stream Identifier: 0 HyperText Transfer Protocol 2 Stream: HEADERS, Stream ID: 1, Length 91, POST /helloworld.Greeter/SayHello Length: 91 Type: HEADERS (1) Flags: 0x04, End Headers 00.0 ..0. = Unused: 0x00 ..0. .... = Priority: False .... 0... = Padded: False .... .1.. = End Headers: True .... ...0 = End Stream: False 0... .... .... .... .... .... .... .... = Reserved: 0x0 .000 0000 0000 0000 0000 0000 0000 0001 = Stream Identifier: 1 [Pad Length: 0] Header Block Fragment: 838645956272d141fc1eca245f15852a4b631b87eb1968a0ff418ba0e41d139d09b8d800d87f5f8b1d75d0620d263d4c4d65647a8a9acac8b4c7602bb8cae040027465864d833505b11f40899acac8b24d494f6a7f857df7c4d82d [Header Length: 225] [Header Count: 8] Header: :method: POST Name Length: 7 Name: :method Value Length: 4 Value: POST :method: POST [Unescaped: POST] Representation: Indexed Header Field Index: 3 Header: :scheme: http Name Length: 7 Name: :scheme Value Length: 4 Value: http :scheme: http [Unescaped: http] Representation: Indexed Header Field Index: 6 Header: :path: /helloworld.Greeter/SayHello Name Length: 5 Name: :path Value Length: 28 Value: /helloworld.Greeter/SayHello :path: /helloworld.Greeter/SayHello [Unescaped: /helloworld.Greeter/SayHello] Representation: Literal Header Field with Incremental Indexing - Indexed Name Index: 5 Header: :authority: localhost:50051 Name Length: 10 Name: :authority Value Length: 15 Value: localhost:50051 :authority: localhost:50051 [Unescaped: localhost:50051] Representation: Literal Header Field with Incremental Indexing - Indexed Name Index: 1 Header: content-type: application/grpc Name Length: 12 Name: content-type Value Length: 16 Value: application/grpc content-type: application/grpc [Unescaped: application/grpc] Representation: Literal Header Field with Incremental Indexing - Indexed Name Index: 31 Header: user-agent: grpc-go/1.63.0 Name Length: 10 Name: user-agent Value Length: 14 Value: grpc-go/1.63.0 user-agent: grpc-go/1.63.0 [Unescaped: grpc-go/1.63.0] Representation: Literal Header Field with Incremental Indexing - Indexed Name Index: 58 Header: te: trailers Name Length: 2 Name: te Value Length: 8 Value: trailers [Unescaped: trailers] Representation: Literal Header Field with Incremental Indexing - New Name Header: grpc-timeout: 999250u Name Length: 12 Name: grpc-timeout Value Length: 7 Value: 999250u [Unescaped: 999250u] Representation: Literal Header Field with Incremental Indexing - New Name [Full request URI: http://localhost:50051/helloworld.Greeter/SayHello] [Response in frame: 15] HyperText Transfer Protocol 2 Stream: DATA, Stream ID: 1, Length 12 Length: 12 Type: DATA (0) Flags: 0x01, End Stream 0000 .00. = Unused: 0x00 .... 0... = Padded: False .... ...1 = End Stream: True 0... .... .... .... .... .... .... .... = Reserved: 0x0 .000 0000 0000 0000 0000 0000 0000 0001 = Stream Identifier: 1 [Pad Length: 0] DATA payload (12 bytes) [Connection window size (before): 65535] [Connection window size (after): 65523] [Stream window size (before): 65535] [Stream window size (after): 65523] GRPC Message: /helloworld.Greeter/SayHello, Request 0... .... = Frame Type: Data (0) .... ...0 = Compressed Flag: Not Compressed (0) Message Length: 7 Message Data: 7 bytes Protocol Buffers: /helloworld.Greeter/SayHello,request Message: <UNKNOWN> Field(1): [Field Name: <UNKNOWN>] .000 1... = Field Number: 1 .... .010 = Wire Type: Length-delimited (2) Value Length: 5 Value: 776f726c64
Frame 15: 169 bytes on wire (1352 bits), 169 bytes captured (1352 bits) Encapsulation type: Ethernet (1) Arrival Time: Aug 29, 2024 11:15:57.925722000 JST UTC Arrival Time: Aug 29, 2024 02:15:57.925722000 UTC Epoch Arrival Time: 1724897757.925722000 [Time shift for this packet: 0.000000000 seconds] [Time delta from previous captured frame: 0.000122000 seconds] [Time delta from previous displayed frame: 0.000122000 seconds] [Time since reference or first frame: 0.000682000 seconds] Frame Number: 15 Frame Length: 169 bytes (1352 bits) Capture Length: 169 bytes (1352 bits) [Frame is marked: False] [Frame is ignored: False] [Protocols in frame: eth:ethertype:ipv6:tcp:http2:grpc:protobuf] [Coloring Rule Name: HTTP] [Coloring Rule String: http || tcp.port == 80 || http2] Ethernet II, Src: 00:00:00_00:00:00 (00:00:00:00:00:00), Dst: 00:00:00_00:00:00 (00:00:00:00:00:00) Destination: 00:00:00_00:00:00 (00:00:00:00:00:00) .... ..0. .... .... .... .... = LG bit: Globally unique address (factory default) .... ...0 .... .... .... .... = IG bit: Individual address (unicast) Source: 00:00:00_00:00:00 (00:00:00:00:00:00) .... ..0. .... .... .... .... = LG bit: Globally unique address (factory default) .... ...0 .... .... .... .... = IG bit: Individual address (unicast) Type: IPv6 (0x86dd) [Stream index: 0] Internet Protocol Version 6, Src: ::1, Dst: ::1 0110 .... = Version: 6 .... 0000 0000 .... .... .... .... .... = Traffic Class: 0x00 (DSCP: CS0, ECN: Not-ECT) .... 0000 00.. .... .... .... .... .... = Differentiated Services Codepoint: Default (0) .... .... ..00 .... .... .... .... .... = Explicit Congestion Notification: Not ECN-Capable Transport (0) .... 0101 0111 1001 1101 0100 = Flow Label: 0x579d4 Payload Length: 115 Next Header: TCP (6) Hop Limit: 64 Source Address: ::1 [Address Space: Reserved by IETF] [Special-Purpose Allocation: Loopback Address] [Source: False] [Destination: False] [Forwardable: False] [Globally Reachable: False] [Reserved-by-Protocol: True] Destination Address: ::1 [Address Space: Reserved by IETF] [Special-Purpose Allocation: Loopback Address] [Source: False] [Destination: False] [Forwardable: False] [Globally Reachable: False] [Reserved-by-Protocol: True] [Stream index: 0] Transmission Control Protocol, Src Port: 50051, Dst Port: 55646, Seq: 55, Ack: 181, Len: 83 Source Port: 50051 Destination Port: 55646 [Stream index: 0] [Stream Packet Number: 15] [Conversation completeness: Complete, WITH_DATA (31)] ..0. .... = RST: Absent ...1 .... = FIN: Present .... 1... = Data: Present .... .1.. = ACK: Present .... ..1. = SYN-ACK: Present .... ...1 = SYN: Present [Completeness Flags: ·FDASS] [TCP Segment Len: 83] Sequence Number: 55 (relative sequence number) Sequence Number (raw): 3438549895 [Next Sequence Number: 138 (relative sequence number)] Acknowledgment Number: 181 (relative ack number) Acknowledgment number (raw): 3678708552 1000 .... = Header Length: 32 bytes (8) Flags: 0x018 (PSH, ACK) 000. .... .... = Reserved: Not set ...0 .... .... = Accurate ECN: Not set .... 0... .... = Congestion Window Reduced: Not set .... .0.. .... = ECN-Echo: Not set .... ..0. .... = Urgent: Not set .... ...1 .... = Acknowledgment: Set .... .... 1... = Push: Set .... .... .0.. = Reset: Not set .... .... ..0. = Syn: Not set .... .... ...0 = Fin: Not set [TCP Flags: ·······AP···] Window: 512 [Calculated window size: 65536] [Window size scaling factor: 128] Checksum: 0x007b [unverified] [Checksum Status: Unverified] Urgent Pointer: 0 Options: (12 bytes), No-Operation (NOP), No-Operation (NOP), Timestamps TCP Option - No-Operation (NOP) Kind: No-Operation (1) TCP Option - No-Operation (NOP) Kind: No-Operation (1) TCP Option - Timestamps: TSval 848634858, TSecr 848634858 Kind: Time Stamp Option (8) Length: 10 Timestamp value: 848634858 Timestamp echo reply: 848634858 [Timestamps] [Time since first frame in this TCP stream: 0.000682000 seconds] [Time since previous frame in this TCP stream: 0.000122000 seconds] [SEQ/ACK analysis] [This is an ACK to the segment in frame: 14] [The RTT to ACK the segment was: 0.000122000 seconds] [iRTT: 0.000029000 seconds] [Bytes in flight: 83] [Bytes sent since last PSH flag: 83] TCP payload (83 bytes) [PDU Size: 23] [PDU Size: 27] [PDU Size: 33] HyperText Transfer Protocol 2 Stream: HEADERS, Stream ID: 1, Length 14, 200 OK Length: 14 Type: HEADERS (1) Flags: 0x04, End Headers 00.0 ..0. = Unused: 0x00 ..0. .... = Priority: False .... 0... = Padded: False .... .1.. = End Headers: True .... ...0 = End Stream: False 0... .... .... .... .... .... .... .... = Reserved: 0x0 .000 0000 0000 0000 0000 0000 0000 0001 = Stream Identifier: 1 [Pad Length: 0] Header Block Fragment: 885f8b1d75d0620d263d4c4d6564 [Header Length: 54] [Header Count: 2] Header: :status: 200 OK Name Length: 7 Name: :status Value Length: 3 Value: 200 :status: 200 [Unescaped: 200] Representation: Indexed Header Field Index: 8 Header: content-type: application/grpc Name Length: 12 Name: content-type Value Length: 16 Value: application/grpc content-type: application/grpc [Unescaped: application/grpc] Representation: Literal Header Field with Incremental Indexing - Indexed Name Index: 31 [Time since request: 0.000286000 seconds] [Request in frame: 12] HyperText Transfer Protocol 2 Stream: DATA, Stream ID: 1, Length 18 Length: 18 Type: DATA (0) Flags: 0x00 0000 .00. = Unused: 0x00 .... 0... = Padded: False .... ...0 = End Stream: False 0... .... .... .... .... .... .... .... = Reserved: 0x0 .000 0000 0000 0000 0000 0000 0000 0001 = Stream Identifier: 1 [Pad Length: 0] DATA payload (18 bytes) [Connection window size (before): 65535] [Connection window size (after): 65517] [Stream window size (before): 65535] [Stream window size (after): 65517] GRPC Message: /helloworld.Greeter/SayHello, Response 0... .... = Frame Type: Data (0) .... ...0 = Compressed Flag: Not Compressed (0) Message Length: 13 Message Data: 13 bytes Protocol Buffers: /helloworld.Greeter/SayHello,response Message: <UNKNOWN> Field(1): [Field Name: <UNKNOWN>] .000 1... = Field Number: 1 .... .010 = Wire Type: Length-delimited (2) Value Length: 11 Value: 48656c6c6f20776f726c64 HyperText Transfer Protocol 2 Stream: HEADERS, Stream ID: 1, Length 24 Length: 24 Type: HEADERS (1) Flags: 0x05, End Headers, End Stream 00.0 ..0. = Unused: 0x00 ..0. .... = Priority: False .... 0... = Padded: False .... .1.. = End Headers: True .... ...1 = End Stream: True 0... .... .... .... .... .... .... .... = Reserved: 0x0 .000 0000 0000 0000 0000 0000 0000 0001 = Stream Identifier: 1 [Pad Length: 0] Header Block Fragment: 40889acac8b21234da8f013040899acac8b5254207317f00 [Header Length: 40] [Header Count: 2] Header: grpc-status: 0 Name Length: 11 Name: grpc-status Value Length: 1 Value: 0 [Unescaped: 0] Representation: Literal Header Field with Incremental Indexing - New Name Header: grpc-message: Name Length: 12 Name: grpc-message Value Length: 0 Value: [Unescaped: ] Representation: Literal Header Field with Incremental Indexing - New Name [Time since request: 0.000286000 seconds] [Request in frame: 12]
まとめ
gRPC の通信を tcpdump で取得して Wireshark で覗いてみました。 HTTP2 の元で動く gRPC の通信の流れがなんとなくつかめました。