Analyzing gRPC messages using Wireshark
Wireshark is an open source network protocol analyzer that can be used for protocol development, network troubleshooting, and education. Wireshark lets you analyze gRPC messages that are transferred over the network, and learn about the binary format of these messages.
In this post, you’ll learn how to configure and use the Wireshark gRPC dissector and the Protocol Buffers (Protobuf) dissector, which are protocol-specific components that allow you to analyze gRPC messages with Wireshark.
Features
The main features of the gRPC and Protobuf dissectors are the following:
-
Support dissecting (decoding) gRPC messages serialized in the protocol buffer wire format or as JSON
-
Support dissecting gRPC messages of unary, server streaming, client streaming, and bidirectional streaming RPC calls
-
Enhanced dissection of serialized protocol buffers data by allowing you to do the following:
- Load relevant
.proto
files - Register your own subdissectors for protocol buffer fields of type
byte
orstring
- Load relevant
Capturing gRPC traffic
This post focuses on the analysis of captured gRPC messages. To learn how to store network traffic in capture files, see Capturing Live Network Data from the Wireshark User’s Guide.
Note
Currently, Wireshark can only parse plain text gRPC messages. While Wireshark supports TLS dissection, it requires per-session secret keys. As of the time of writing, the only Go gRPC supports the exporting such keys. To learn how to export keys using Go gRPC – and other languages as support becomes available – see How to Export TLS Master keys of gRPC.Example
Let’s walk through the setup necessary to analyze previously-captured messages that were generated by a slightly extended version of the address book app used in the Protocol Buffers tutorials.
Address book .proto
files
The app’s main protocol file is addressbook.proto
:
syntax = "proto3";
package tutorial;
import "google/protobuf/timestamp.proto";
message Person {
string name = 1;
int32 id = 2; // Unique ID number for this person.
string email = 3;
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
message PhoneNumber {
string number = 1;
PhoneType type = 2;
}
repeated PhoneNumber phone = 4;
google.protobuf.Timestamp last_updated = 5;
bytes portrait_image = 6;
}
message AddressBook {
repeated Person people = 1;
}
This file is identical to the Protocol Buffers tutorial version,
except for the additional portrait_image
field.
Note the import
statement at the top of the file, it is used to import
Timestamp
, which is one of many Protocol Buffers Well-Known Types.
Our variant of the app also defines a person-search service that can be used
to search for address book entries based on selected Person
attributes. The
service is defined in person_search_service.proto
:
syntax = "proto3";
package tutorial;
import "addressbook.proto";
message PersonSearchRequest {
repeated string name = 1;
repeated int32 id = 2;
repeated string phoneNumber = 3;
}
service PersonSearchService {
rpc Search (PersonSearchRequest) returns (stream Person) {}
}
Because the service uses the Person
type defined in addressbook.proto
,
the address book .proto
is imported at the start of the file.
Setting protobuf search paths
Wireshark gives the most meaningful decodings when it knows about the .proto
files used by the apps whose messages you are analyzing.
You can tell Wireshark where to find .proto
files by setting the Protobuf
Search Paths in the preferences accessible from the Edit menu under
Preferences > Protocols > Protobuf.
If our example app’s .proto
files are in the d:/protos/my_proto_files
directory,
and the official Protobuf library directory is
d:/protos/protobuf-3.4.1/include
, then add these two paths as source
directories like this: