blob: f166b7ca9d956f262f7e61b5c0d3ae2cca84ee31 [file] [log] [blame]
--
-- 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