Wireshark中foo dissector代码分析

Wireshark中foo dissector代码分析

最近因为工作需要,在Wireshark中添加了私有协议报文的解析插件,不同于之前的lua插件,这次使用原生C语言编写的插件,可以直接集成在Wireshark安装包中,便于安装包的发布,而且C语言编写的插件相对于lua,可以实现更加丰富的功能。

为了简单起见,分析了一下Wireshark开发指导中的foo dissector,它其实包含了dissector开发的主要步骤和流程,这里记录一下,便于自己后续参考。后续dissector被翻译成解析器,根据上下文也可能直接使用dissector。

这里是Wireshark官网上的介绍:Adding a basic dissector

每个dissector都需要实现两个接口:proto_register_XXX 和 proto_reg_handoff_XXX,对应这里分别是 proto_register_fooproto_reg_handoff_foo,这也是他们被系统调用的前后顺序。proto_register_foo 中负责通过proto_register_protocol 注册协议基本信息,proto_reg_handoff_foo中负责注册协议解析实现接口,以及协议关联的上层协议。其中proto_reg_handoff_foo通过create_dissector_handle创建解析器并返回解析器句柄,dissector_add_uint将解析器句柄注册到udp对应端口号的解析流程中,完整流程图如下:

所以,foo解析器重要的实现就是上图中dissect_foo这个函数。这个函数有几个重要的数据结构:

tvbuff_t *tvb           -- 数据包缓冲区
packet_info *pinfo -- 数据包控制信息
proto_tree *tree -- 解析器上一级的tree

在dissect_foo中,proto_tree_add_item负责创建解析器item成员ti,然后proto_item_add_subtree根据这个item成员和ett_foo来创建自己的tree结构foo_tree,接下来就可以用proto_tree_add_item将解析出来的成员添加到foo_tree上,最后完成的时候调用tvb_captured_length完成解析。完整流程图如下:

至此,一个foo dissector解析器就完成了基本流程,更加复杂的解析就应该去细化dissect_foo的实现了。当然,如果你想要看看复杂的解析器如何实现,比如protobuf,可以看看这里

foo协议解析器的完整的实现代码如下:

#include "config.h"
#include <epan/packet.h>

#define FOO_PORT 1234

static int proto_foo;

#define FOO_START_FLAG      0x01
#define FOO_END_FLAG        0x02
#define FOO_PRIORITY_FLAG   0x04

static int hf_foo_startflag;
static int hf_foo_endflag;
static int hf_foo_priorityflag;

static int hf_foo_pdu_type;
static int hf_foo_flags;
static int hf_foo_sequenceno;
static int hf_foo_initialip;
static int ett_foo;

static const value_string packettypenames[] = {
    { 1, "Initialise" },
    { 2, "Terminate" },
    { 3, "Data" },
    { 0, NULL }
};

static int* const bits[] = {
    &hf_foo_startflag,
    &hf_foo_endflag,
    &hf_foo_priorityflag,
    NULL
};

static int
dissect_foo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree _U_, void *data _U_)
{
    int offset = 0;
    uint8_t packet_type = tvb_get_uint8(tvb, 0);

    col_set_str(pinfo->cinfo, COL_PROTOCOL, "FOO");
    /* Clear out stuff in the info column */
    col_clear(pinfo->cinfo,COL_INFO);
    col_add_fstr(pinfo->cinfo, COL_INFO, "Type %s",
             val_to_str(packet_type, packettypenames, "Unknown (0x%02x)"));

    proto_item *ti = proto_tree_add_item(tree, proto_foo, tvb, 0, -1, ENC_NA);
    proto_item_append_text(ti, ", Type %s",
        val_to_str(packet_type, packettypenames, "Unknown (0x%02x)"));
    proto_tree *foo_tree = proto_item_add_subtree(ti, ett_foo);
    proto_tree_add_item(foo_tree, hf_foo_pdu_type, tvb, 0, 1, ENC_BIG_ENDIAN);
    offset += 1;
    proto_tree_add_item(foo_tree, hf_foo_flags, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset += 1;
    proto_tree_add_item(foo_tree, hf_foo_sequenceno, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;
    proto_tree_add_item(foo_tree, hf_foo_initialip, tvb, offset, 4, ENC_BIG_ENDIAN);
    offset += 4;

    proto_tree_add_bitmask(foo_tree, tvb, offset, hf_foo_flags, ett_foo, bits, ENC_BIG_ENDIAN);
    offset += 1;

    return tvb_captured_length(tvb);
}

void
proto_register_foo(void)
{
    static hf_register_info hf[] = {
        { &hf_foo_pdu_type,
            { "FOO PDU Type", "foo.type",
            FT_UINT8, BASE_DEC,
            VALS(packettypenames), 0x0,
            NULL, HFILL }
        },
        { &hf_foo_flags,
            { "FOO PDU Flags", "foo.flags",
            FT_UINT8, BASE_HEX,
            NULL, 0x0,
            NULL, HFILL }
        },
        { &hf_foo_sequenceno,
            { "FOO PDU Sequence Number", "foo.seqn",
            FT_UINT16, BASE_DEC,
            NULL, 0x0,
            NULL, HFILL }
        },
        { &hf_foo_initialip,
            { "FOO PDU Initial IP", "foo.initialip",
            FT_IPv4, BASE_NONE,
            NULL, 0x0,
            NULL, HFILL }
        },
        { &hf_foo_startflag,
            { "FOO PDU Start Flags", "foo.flags.start",
            FT_BOOLEAN, 8,
            NULL, FOO_START_FLAG,
            NULL, HFILL }
        },
        { &hf_foo_endflag,
            { "FOO PDU End Flags", "foo.flags.end",
            FT_BOOLEAN, 8,
            NULL, FOO_END_FLAG,
            NULL, HFILL }
        },
        { &hf_foo_priorityflag,
            { "FOO PDU Priority Flags", "foo.flags.priority",
            FT_BOOLEAN, 8,
            NULL, FOO_PRIORITY_FLAG,
            NULL, HFILL }
        },
    };

    /* Setup protocol subtree array */
    static int *ett[] = {
        &ett_foo
    };

    proto_foo = proto_register_protocol (
        "FOO Protocol", /* name        */
        "FOO",          /* short_name  */
        "foo"           /* filter_name */
        );

    proto_register_field_array(proto_foo, hf, array_length(hf));
    proto_register_subtree_array(ett, array_length(ett));
}

void
proto_reg_handoff_foo(void)
{
    static dissector_handle_t foo_handle;

    foo_handle = create_dissector_handle(dissect_foo, proto_foo);
    dissector_add_uint("udp.port", FOO_PORT, foo_handle);
}
Comments are closed.