Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions docs/cn/wireshark_baidu_std.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
[English version](../en/wireshark_baidu_std.md)

# 介绍

`wireshark_baidu_std.lua` 是针对 [`baidu_std`](baidu_std.md) 协议的 `Wireshark` 解析插件,同时支持 [`streaming_rpc`](streaming_rpc.md) 协议的解析。

请求包的解析示例:

![request](../images/wireshark_baidu_std_request.png)

响应包的解析示例:

![response](../images/wireshark_baidu_std_response.png)


## 使用方式

1. 将 [`wireshark_baidu_std.lua`](../../tools/wireshark_baidu_std.lua) 放到 "Personal Lua Plugins" 目录下;
1. 将 [`options.proto`](../../src/brpc/options.proto)、[`streaming_rpc_meta.proto`](../../src/brpc/streaming_rpc_meta.proto) 以及 [`baidu_rpc_meta.proto`](../../src/brpc/policy/baidu_rpc_meta.proto) 放到 "Personal configuration" 目录下的 `protobuf` 目录中,目录如不存在可以手动创建;
1. 参考 [Wireshark Protobuf](https://wiki.wireshark.org/Protobuf#protobuf-search-paths-settings) 配置 `Protobuf` 系统 proto 文件路经,如:`/opt/homebrew/opt/protobuf/include`
![wireshark-protobuf-search-paths](../images/wireshark_protobuf_search_paths.png)
1. 可选,如需想使用相关字段进行过滤,可打开如下选项:
![wireshark-protobuf-settings](../images/wireshark_protobuf_settings.png)

上面提到的 "Personal Lua Plugins" 以及 "Personal configuration" 目录可查看 `About Wireshark` 的 `Folders` 页面,相关目录是平台相关的,macOS 下如:

![About Wireshark](../images/wireshark_folders.png)
27 changes: 27 additions & 0 deletions docs/en/wireshark_baidu_std.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
[中文版](../cn/wireshark_baidu_std.md)

# Overview

`wireshark_baidu_std.lua` is a Wireshark Lua dissector written for [`baidu_std`](../cn/baidu_std.md) protocol, including the [`streaming_rpc`](streaming_rpc.md) protocol.

Example for the Echo request:

![request](../images/wireshark_baidu_std_request.png)

Example for the Echo response:

![response](../images/wireshark_baidu_std_response.png)


## How to use

1. Put [`wireshark_baidu_std.lua`](../../tools/wireshark_baidu_std.lua) under "Personal Lua Plugins";
1. And put [`options.proto`](../../src/brpc/options.proto), [`streaming_rpc_meta.proto`](../../src/brpc/streaming_rpc_meta.proto) and [`baidu_rpc_meta.proto`](../../src/brpc/policy/baidu_rpc_meta.proto) in `protobuf` directory under "Personal configuration", create if not exist;
1. Set `Protobuf Search Paths` for Protobuf official library include directory(e.g. `/opt/homebrew/opt/protobuf/include`), see [Wireshark Protobuf](https://wiki.wireshark.org/Protobuf#protobuf-search-paths-settings) for more details.
![wireshark-protobuf-search-paths](../images/wireshark_protobuf_search_paths.png)
1. Optional, turn on `Dissect Protobuf fields as Wireshark fields` if you want to use related `baidu_std` fields for filtering:
![wireshark-protobuf-settings](../images/wireshark_protobuf_settings.png)

The "Personal Lua Plugins" and "Personal configuration" folders above can be found under the `Folders` page of `About Wireshark`, for macOS:

![About Wireshark](../images/wireshark_folders.png)
Binary file added docs/images/wireshark_baidu_std_request.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/wireshark_baidu_std_response.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/wireshark_folders.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/wireshark_protobuf_search_paths.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/wireshark_protobuf_settings.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
279 changes: 279 additions & 0 deletions tools/wireshark_baidu_std.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,279 @@
--
-- Licensed to the Apache Software Foundation (ASF) under one
-- or more contributor license agreements. See the NOTICE file
-- distributed with this work for additional information
-- regarding copyright ownership. The ASF licenses this file
-- to you under the Apache License, Version 2.0 (the
-- "License"); you may not use this file except in compliance
-- with the License. You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing,
-- software distributed under the License is distributed on an
-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-- KIND, either express or implied. See the License for the
-- specific language governing permissions and limitations
-- under the License.
--

--------------------------------------------------------------------------------
-- function
-- Proto.new(name, desc)
-- proto.dissector
-- proto.fields
-- proto.prefs
-- proto.prefs_changed
-- proto.init
-- proto.name
-- proto.description
--------------------------------------------------------------------------------

local plugin_info = {
name = "baidu",
version = "0.1.0",
description = "baidu_std"
}

local major, minor, patch = get_version():match("(%d+)%.(%d+)%.(%d+)")
local version_code = tonumber(major) * 1000 * 1000 + tonumber(minor) * 1000 + tonumber(patch)

local proto = Proto.new(plugin_info.name, plugin_info.description)

local LM_DBG = function(fmt, ...)
if (tonumber(major) < 3) then
critical(table.concat({ plugin_info.name:upper() .. ":", fmt }, ' '):format(...))
else
print (table.concat({ plugin_info.name:upper() .. ":", fmt }, ' '):format(...))
end
end

LM_DBG("Wireshark version =", get_version())
LM_DBG("Lua version =", _VERSION)

--------------------------------------------------------------------------------

local MAGIC_CODE_PRPC = "PRPC"
local MAGIC_CODE_STRM = "STRM"

local PROTO_HEADER_LENGTH = 12


local pf_magic = ProtoField.string(plugin_info.name .. ".magic",
"Magic Code", BASE_NONE)
local pf_body_size = ProtoField.uint32(plugin_info.name .. ".body_size",
"Body Size", BASE_DEC)
local pf_meta_size = ProtoField.uint32(plugin_info.name .. ".meta_size",
"Meta Size", BASE_DEC)

-- used to customize tree item
local pf_any = ProtoField.bytes (plugin_info.name .. ".any",
"Any", BASE_NONE)

proto.fields = {
pf_magic,
pf_body_size,
pf_meta_size,
pf_any
}

----------------------------------------
-- ref fields
local proto_f_magic_code = Field.new(plugin_info.name .. ".magic")
local proto_f_body_size = Field.new(plugin_info.name .. ".body_size")
local proto_f_meta_size = Field.new(plugin_info.name .. ".meta_size")

local proto_f_protobuf_field_name = Field.new("protobuf.field.name")
local proto_f_protobuf_field_value = Field.new("protobuf.field.value")

----------------------------------------
-- protobuf dissector
-- Note:
-- options.proto, baidu_rpc_meta.proto and streaming_rpc_meta.proto
-- should be put in Wireshark Protobuf Search Paths.
local protobuf_dissector = Dissector.get("protobuf")

----------------------------------------
-- declare functions

local check_length = function() end
local dissect_proto = function() end

----------------------------------------
-- main dissector
function proto.dissector(tvbuf, pktinfo, root)
local pktlen = tvbuf:len()

local bytes_consumed = 0

while bytes_consumed < pktlen do
local result = dissect_proto(tvbuf, pktinfo, root, bytes_consumed)

if result > 0 then
bytes_consumed = bytes_consumed + result
elseif result == 0 then
-- hit an error
return 0
else
pktinfo.desegment_offset = bytes_consumed
-- require more bytes
pktinfo.desegment_len = -result

return pktlen
end
end

return bytes_consumed
end

--------------------------------------------------------------------------------
-- heuristic
local function heur_dissect_proto(tvbuf, pktinfo, root)
LM_DBG("heur brpc: pkg number: " .. pktinfo.number)
if (tvbuf:len() < PROTO_HEADER_LENGTH) then
LM_DBG("too short: pkg number: " .. pktinfo.number)
return false
end

local magic = tvbuf:range(0, 4):string()
-- for range dissectors
if magic ~= MAGIC_CODE_PRPC and maigc ~= MAGIC_CODE_STRM then
LM_DBG("invalid magic code: pkg number: " .. pktinfo.number)
return false
end

proto.dissector(tvbuf, pktinfo, root)

pktinfo.conversation = proto

return true
end
proto:register_heuristic("tcp", heur_dissect_proto)

--------------------------------------------------------------------------------

local correlation_method_map = {}

local store_method = function(correlation_id, method)
-- TODO: pop items
correlation_method_map[correlation_id] = method
end

local load_method = function(correlation_id)
return correlation_method_map[correlation_id]
end

-- check packet length, return length of packet if valid
check_length = function(tvbuf, offset)
local msglen = tvbuf:len() - offset

if msglen ~= tvbuf:reported_length_remaining(offset) then
-- captured packets are being sliced/cut-off, so don't try to desegment/reassemble
LM_WARN("Captured packet was shorter than original, can't reassemble")
return 0
end

if msglen < PROTO_HEADER_LENGTH then
-- we need more bytes, so tell the main dissector function that we
-- didn't dissect anything, and we need an unknown number of more
-- bytes (which is what "DESEGMENT_ONE_MORE_SEGMENT" is used for)
return -DESEGMENT_ONE_MORE_SEGMENT
end

-- if we got here, then we know we have enough bytes in the Tvb buffer
-- to at least figure out whether this is valid baidu_std packet

local magic = tvbuf:range(offset, 4):string()
if magic ~= MAGIC_CODE_PRPC and magic ~= MAGIC_CODE_STRM then
return 0
end

-- big endian
local packet_size = tvbuf:range(offset+4, 4):uint() + PROTO_HEADER_LENGTH
if msglen < packet_size then
LM_DBG("Need more bytes to desegment full baidu_std packet")
return -(packet_size - msglen)
end

return packet_size
end

----------------------------------------
dissect_proto = function(tvbuf, pktinfo, root, offset)
local len = check_length(tvbuf, offset)
if len <= 0 then
return len
end

local tree = root:add(proto, tvbuf:range(offset, len))

tree:add(pf_magic, tvbuf:range(offset, 4))
tree:add(pf_body_size, tvbuf:range(offset+4, 4))
tree:add(pf_meta_size, tvbuf:range(offset+8, 4))

local meta_size = proto_f_meta_size().value
local body_size = proto_f_body_size().value
local tvb_meta = tvbuf:range(offset + PROTO_HEADER_LENGTH, meta_size):tvb()
if proto_f_magic_code().value == MAGIC_CODE_PRPC then
-- dissect rpc meta fields
pktinfo.private["pb_msg_type"] = "message,brpc.policy.RpcMeta"
protobuf_dissector:call(tvb_meta, pktinfo, tree)

local direction, method

local service_name, method_name, correlation_id

-- https://ask.wireshark.org/question/31800/protobuf-dissector-with-nested-structures/?answer=31924#post-id-31924
local protobuf_field_names = { proto_f_protobuf_field_name() }
local protobuf_field_values = { proto_f_protobuf_field_value() }
for k, v in pairs(protobuf_field_names) do
if v.value == "request" then
direction = "request"
elseif v.value == "response" then
direction = "response"
elseif v.value == "service_name" then
service_name = protobuf_field_values[k].range:string(ENC_UTF8)
elseif v.value == "method_name" then
method_name = protobuf_field_values[k].range:string(ENC_UTF8)
elseif v.value == "correlation_id" then
correlation_id = protobuf_field_values[k].range:uint64()
end
end

if direction == "request" then
method = service_name .. "/" .. method_name
-- NOTE: convert uint64 to string to be used as key of table
store_method(tostring(correlation_id), method)
elseif direction == "response" then
method = load_method(tostring(correlation_id))
end

if method ~= nil then
-- dissect rpc body
local tvb_body = tvbuf:range(offset + PROTO_HEADER_LENGTH + meta_size, body_size - meta_size):tvb()
pktinfo.private["pb_msg_type"] = "application/grpc,/" .. method .. "," .. direction
protobuf_dissector:call(tvb_body, pktinfo, tree)
end
elseif proto_f_magic_code().value == MAGIC_CODE_STRM then
-- dissect streaming meta fields
pktinfo.private["pb_msg_type"] = "message,brpc.StreamFrameMeta"
protobuf_dissector:call(tvb_meta, pktinfo, tree)
end

-- body fields are business related, customized here

return len
end

--------------------------------------------------------------------------------
-- Editor modelines
--
-- Local variables:
-- c-basic-offset: 4
-- tab-width: 4
-- indent-tab-mode: nil
-- End:
--
-- kate: indent-width 4; tab-width 4;
-- vim: tabstop=4:softtabstop=4:shiftwidth=4:expandtab
-- :indentSize=4:tabSize=4:noTabs=true