Skip to content

Instantly share code, notes, and snippets.

@albertored
Last active August 16, 2023 15:31
Show Gist options
  • Select an option

  • Save albertored/94bcce05c55b083f62dacd2473b8666d to your computer and use it in GitHub Desktop.

Select an option

Save albertored/94bcce05c55b083f62dacd2473b8666d to your computer and use it in GitHub Desktop.
Wireshark dissector for ProtoBuf over HTTP (application/x-protobuf content-type)

In OpenTelemetry one of the ways for exchanging telemetry data between different services is using Protocol Buffers over HTTP, see OTLP/HTTP.

If it is needed to debug this sharing of information one can think about sniffing the traffic with a tool like Wireshark. For doing so Wireshark should be able to know which ProtoBuf message type to use for the decoding. Since there is no such a feature built in (only for ProtoBuf over UDP) we should use a custom dissector.

The custom dissector is a .lua script that should be saved in Wireshark config directory (personal or global), the contents are attached here. The mapping for the message type is based on the HTTP request path (but the dissector can be customized for having different behaviours), this mapping is exposed as a preference on the UI (see figure).

In order for the decoding to work correctly all the needed .proto files (and their dependencies) should be added to Wireshark paths (see figure).

protobuf_http_msg_table = {}
function parse_msg_table_opt(text)
local t = {}
for line in string.gmatch(text, "([^|]+)") do
for path, msg_type in string.gmatch(line, "(.+),(.+)") do
t[path] = msg_type
end
end
return t
end
do
local protobuf_dissector = Dissector.get("protobuf")
local request_path = Field.new("http.request.uri")
local protobuf_http_proto = Proto("protobuf_http", "ProtoBuf over HTTP")
local desc = "Pairs of '{HTTP path},{ProtoBuf Msg Type}' separated by a '|' character (no spaces allowed)"
protobuf_http_proto.prefs.msg_table = Pref.string("Path to message type", "", desc)
protobuf_http_proto.fields = { }
protobuf_http_proto.init = function()
protobuf_http_msg_table = parse_msg_table_opt(protobuf_http_proto.prefs.msg_table)
end
protobuf_http_proto.prefs_changed = function()
protobuf_http_msg_table = parse_msg_table_opt(protobuf_http_proto.prefs.msg_table)
end
protobuf_http_proto.dissector = function(tvb, pinfo, tree)
local path = request_path().value
local msg_type = protobuf_http_msg_table[path]
local subtree = tree:add(protobuf_http_proto, tvb())
if msg_type ~= nil then
pinfo.private["pb_msg_type"] = "message," .. msg_type
end
pcall(Dissector.call, protobuf_dissector, tvb, pinfo, subtree)
pinfo.columns.protocol:set("protobuf_http")
end
DissectorTable.get("media_type"):add("application/x-protobuf", protobuf_http_proto)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment