wireshark協議解析器原理與插件編寫


 

參考:http://www.wireshark.org/docs/wsdg_html_chunked/ChapterDissection.html
背景為wireshark 1.8.4版本

 

9 packet解析

9.1 工作原理

每個解析器解碼自己的協議部分, 然后把封裝協議的解碼傳遞給后續協議。

因此它可能總是從一個Frame解析器開始, Frame解析器解析捕獲文件自己的數據包細節(如:時間戳), 將數據交給一個解碼Ethernet頭部的Ethernet frame解析器, 然后將載荷交給下一個解析器(如:IP), 如此等等. 在每一步, 數據包的細節會被解碼並顯示.

可以用兩種可能的方式實現協議解析. 一是寫一個解析器模塊, 編譯到主程序中, 這意味着它將永遠是可用的. 另一種方式是實現一個插件(共享庫/DLL), 它注冊自身用於處理解析。

插件形式和內置形式的解析器之間的差別很小. 在Windows平台, 通過列於libwireshark.def中的函數, 我們可以訪問有限的函數, 但它們幾乎已經夠用了.

比較大的好處是插件解析器的構建周期要遠小於內置. 因此以插件開始會使最初的開發工作變得簡單, 而最終代碼的布署會和內置解析器一樣。

另見 README.developer  文件doc/README.developer包含更多有關實現解析器(而且在某些情況下, 比本文檔要新一些)的信息.

9.2 添加基本解析器

讓我們一步一步來實現一個杜撰的“foo”協議的基本解析器。此協議包含以下基本項:

  • packet type 8 bits, 可能值:1-初始化,2-終止,3-數據
  • flags 8 bit, 0x01-開始packet, 0x02-結束packet, 0x04-優先packet
  • seq number 16 bits
  • 1個ip地址

9.2.1 構建解析器

首先需要決定解析器是要以built-in方式,還是以plugin方式實現。plugin方式實現比較容易上手。

代碼9.1 解析器初始化

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

#define FOO_PORT 9877

static int proto_foo = -1;


void
proto_register_foo(void)
{
    proto_foo = proto_register_protocol (
        "FOO Protocol", /* name       */
        "FOO",      /* short name */
        "foo"       /* abbrev     */
        );
}
 

首先include一些必需的頭文件。proto_foo用來記錄我們的協議,當將此解析器注冊到主程序時,它的值將會更新。把所有非外部使用的變量和函數聲明為static是一個好的編程實踐,可以避免名字空間污染。一般情況下這不是問題,除非我們的解析器非常大,分成了多個文件。

我們#define了協議的UDP端口FOO_PORT。

現在我們已經有了與主程序交互所需的基本東西了。接下來實現2個解析器構建函數(dissector setup functions)。

首先調用proto_register_protocol()函數來注冊協議。可以給它3個名字用來將來在不同的地方顯示。比如full和short name用於“Preferences”和“Enabled protocols”對話框。abbrev name用於顯示過濾器。

接下來我們需要handoff例程。

代碼9.2 解析器handoff

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);
}
 

首先創建一個dissector handle,它和foo協議及執行實際解析工作的函數關聯。接下來將此handle與UDP端口號關聯,以便主程序在看到此端口上的UDP數據時調用我們的解析器。

標准wireshark解析器習慣是把proto_register_foo()和proto_reg_handoff_foo()做為解析器代碼的最后2個函數。

最后我們來編寫一些解析器代碼。目前將它做為基本的占位符。

代碼9.3 協議解析

static void
dissect_foo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
    col_set_str(pinfo->cinfo, COL_PROTOCOL, "FOO");
    /* Clear out stuff in the info column */
    col_clear(pinfo->cinfo,COL_INFO);
}
 

此函數用於解析交給它的packets。packet數據放在名為tvb的特殊緩存中。對此隨着我們對協議細節了解的深入將會變得非常熟悉。packet_info結構包含有關協議的一般數據,我們應該在此更新信息。tree參數是細節解析發生的地方。

現在我們進行最小化的實現。第1行我們設置我們協議的文本,以示用戶可以看到協議被識別了。另外唯一做的事情是清除INFO列中的所有數據,如果它正在被顯示的話。

此時,我們已經准備好基本的解析器,可以進行編譯和安裝了。它什么也不做,除了識別協議並標識它。

為了編譯此解析器並創建插件,除了packet-foo.c中的源代碼,還有一堆必需的支持文件,它們是:

  • Makefile.am - This is the UNIX/Linux makefile template
  • Makefile.common - This contains the file names of this plugin
  • Makefile.nmake - This contains the Wireshark plugin makefile for Windows
  • moduleinfo.h - This contains plugin version info
  • moduleinfo.nmake - This contains DLL version info for Windows
  • packet-foo.c - This is your dissector source
  • plugin.rc.in - This contains the DLL resource template for Windows

你可以在plugin目錄內找到這些文件的好例子。Makefile.common和Makefile.am必須被修改,以反映相關的文件和解析器名字。moduleinfo.h和moduleinfo.nmake必須被填充版本信息。將解析器編譯為DLL或共享庫,然后將它拷貝到wireshark的plugin目錄。

以下內容參考wireshark-1.8.4\doc\README.plugins


 以foo協議解析器插件為例,構建此插件至少需要在wireshark-1.8.4/plugins/foo目錄下建立以下文件:

  • AUTHORS
  • COPYING
  • ChangeLog
  • CMakeLists.txt
  • Makefile.am
  • Makefile.common
  • Makefile.nmake
  • moduleinfo.h
  • moduleinfo.nmake
  • plugin.rc.in
  • 當然,還有自己編寫的解析器源代碼

這些文件的例子可以在plugins/gryphon中找到。

AUTHORS,COPYING,ChangeLog:GPL工程的標准文件。

CMakeLists.txt:把gryphon/CMakeLists.txt文件中出現的所有"gryphon"替換為"foo",並把自己的源文件添加到DISSECTOR_SRC變量。

Makefile.am:把gryphon/Makefile.am文件中出現的所有"gryphon"替換為"foo"。

Makefile.common:此文件只應列出export register_*()和handoff_*()的main源文件。所有其他支持源文件應該列在DISSECTOR_SUPPORT_SRC變量中。如果解析器有頭文件,必須列在DISSECTOR_INCLUDES變量中。DISSECOTR_INCLUDES變量不應包括moduleinfo.h。

Makefile.nmake:不需要改動。

moduleinfo.h:用於設置此插件的版本信息。

moduleinfo.nmake:用於設置編譯此插件的版本信息。它的內容應與moduleinfo.h匹配。

plugin.rc.in:這是Windows資源模板文件,用於把插件的特定信息做為資源添加到DLL。不需要修改。


把以上文件准備好、修改好之后,cmd進入plugins/foo目錄,運行nmake -f Makefile.nmake xxx來進行編譯,就像編譯wireshark源碼一樣。編譯好之后生成foo.dll,將它拷貝到編譯好的wireshark的plugins目錄(可能會有中間目錄,視情況)。

可以編寫簡單的udp發送端和接收端來測試剛才編譯的foo協議解析插件。

發送端

#include <WinSock2.h>
#include <stdio.h>

#define  UDP_PORT_FOO  9877

int main(int argc, char** argv)
{
    SOCKET sockfd;
    SOCKADDR_IN addr;

    WORD dwVersion = MAKEWORD(2, 2);
    WSAData wsaData;
    WSAStartup(dwVersion, &wsaData);

    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    addr.sin_family = AF_INET;
    addr.sin_port = htons(UDP_PORT_FOO);
    addr.sin_addr.s_addr = inet_addr("192.168.1.2");

    char buff[] = "hello world";
    puts(buff);

    for(;;)
    {
        sendto(sockfd, buff, (int)strlen(buff), 0, (SOCKADDR*)&addr, sizeof(addr));
        Sleep(1000);
    }

    closesocket(sockfd);

    WSACleanup();

    return 0;
}
 

接收端

#include <WinSock2.h>
#include <stdio.h>

#define  UDP_PORT_FOO  9877

int main(int argc, char** argv)
{
    SOCKET sockfd;
    SOCKADDR_IN addr;

    WORD dwVersion = MAKEWORD(2, 2);
    WSAData wsaData;
    WSAStartup(dwVersion, &wsaData);

    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    addr.sin_family = AF_INET;
    addr.sin_port = htons(UDP_PORT_FOO);
    addr.sin_addr.s_addr = htonl(INADDR_ANY);

    bind(sockfd, (SOCKADDR*)&addr, sizeof(addr));

    char buff[16];
    int n = 0;
    for(;;)
    {
        n = recvfrom(sockfd, buff, 16, 0, NULL, NULL);
        buff[n] = '\0';
        puts(buff);
    }

    closesocket(sockfd);

    WSACleanup();

    return 0;
}
 

然后可以運行這2個程序,開啟添加了foo協議解析插件(所謂添加也就是把foo.dll復制到plugins目錄)的wireshark進行監測。

由於這2個程序在一台機器上運行,使用了環回接口,而wireshark不會捕獲環回接口,因此需要配置一下路由:

# windows7之類的系統需要使用管理員權限來執行# 192.168.1.2是本機IP,192.168.1.1是路由器IP
C:\Windows\system32>route add 192.168.1.2 mask 255.255.255.255192.168.1.1 metric 1

運行后結果:

9.2.2 解析協議的細節

接下來可以做一些復雜一點的解析工作。最簡單的事情是對載荷進行標記。

首先創建一個subtree用來放解析結果。這有助於在detailed display中更佳顯示。對解析器的調用有2種情況。一種情況用於獲取packet的摘要,另一種情況用於解析packet的細節。這兩種情況由tree指針的不同來區別。如果tree指針為NULL,用於獲取簡略信息。如果是非NULL,則需要解析協議的各個細部。記住這些后,讓我們來增強我們的解析器。

代碼9.4 plugin packet解析

static void
dissect_foo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{

    col_set_str(pinfo->cinfo, COL_PROTOCOL, "FOO");
    /* Clear out stuff in the info column */
    col_clear(pinfo->cinfo,COL_INFO);

    if (tree) { /* we are being asked for details */
        proto_item *ti = NULL;
        ti = proto_tree_add_item(tree, proto_foo, tvb, 0, -1, ENC_NA);
    }
}
 

這里所做的是把一個subtree加入到解析中。此subtree會保存此協議的所有細節,且不會在不需要時弄亂顯示。

我們還可以標記被此協議所消費的數據區域。在目前的情況下,這統治是傳遞過來的所有數據,因為我們假定此協議不再封裝其他協議。因此,我們用proto_tree_add_item()往tree里添加新的節點,標識它的協議名,用tvb緩沖區做為數據,並消費此數據的0到最后1個字節(-1表示結束)。ENC_NA(not applicable)是編碼參數。

在這些改變之后,在detailed display中就會有此協議的標識,且選中它將會高亮此packet的剩余內容。如下圖所示:

現在,讓我們進行下一步,添加一些協議解析。這一步我們需要創建2個表來幫助解析。這需要在proto_register_foo()函數中添加一些代碼。

在proto_register_foo()的前面添加了2個static數組。這些數組在proto_register_protocol()調用之后被注冊。

代碼9.5 注冊數據結構

void
proto_register_foo(void)
{
    static hf_register_info hf[] = {
        { &hf_foo_pdu_type,
            { "FOO PDU Type", "foo.type",
            FT_UINT8, BASE_DEC,
            NULL, 0x0,
            NULL, HFILL }
        }
    };

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

    proto_foo = proto_register_protocol (
        "FOO Protocol", /* name       */
        "FOO",      /* short name */
        "foo"       /* abbrev     */
        );

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

變量hf_foo_pdu_type和ett_foo也需要在此文件的前面聲明。

代碼9.6 解析器數據結構全局變量

static int hf_foo_pdu_type = -1;

static gint ett_foo = -1;
 

現在我們可以用一些細節來增加協議的顯示。

代碼9.7 解析器開始解析packets

if (tree) { /* we are being asked for details */
  proto_item *ti = NULL;
  proto_tree *foo_tree = NULL;

  ti = proto_tree_add_item(tree, proto_foo, tvb, 0, -1, ENC_NA);
  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);
}
 

現在解析開始看起來更加有趣了。我們開始破解此協議的第1個比特。packet起始處的一個字節數據定義了foo協議的packet type。

proto_item_add_subtree()調用往協議樹中增加了一個子節點。此節點的展開是由ett_foo變量控制的。它會記住節點是否應該展開,在你在packet中移動的時候。所有后續的解析會添加到此樹中,就像在接下來的調用中看到的那樣。proto_tree_add_item向foo_tree添加了新項,並用hf_foo_pdu_type來控制此項的格式。pdu type是1個字節的數據,從0開始。我們假定它是網絡字節序(也叫big endian),因此用ENC_BGIG_ENDIAN。對於1個字節的數來說,沒用字節序之說,但這是好的編程實踐。

我們來看static數組中的定義細節:

  • hf_foo_pdu_type - 此節點的索引
  • FOO PDU Type - 此項的標識
  • foo.type - 過濾用的字符串。它使我們可以在過濾器框中輸入foo.type=1的語句
  • FT_UINT8 - 指出此項是一個8bit的無符號整數。
  • BASE_DEC - 對於整型來說,它令其打印為一個10進制數。還可以是16進制(BASE_HEX)或8進制(BASE_OCT)。

我們目前忽略結構中的其余成員。

如果此時編譯並安裝此插件,我們會看到它開始顯示一些看起來有用的東西。

現在我們來完成這個簡單協議的解析。我們需要添加更多的變量在hf數組中,以及更多的函數調用。

代碼9.8 完成packet解析

...
static int hf_foo_flags = -1;
static int hf_foo_sequenceno = -1;
static int hf_foo_initialip = -1;
...

static void
dissect_foo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
    gint offset = 0;

    ...

    if (tree) { /* we are being asked for details */
        proto_item *ti = NULL;
        proto_tree *foo_tree = NULL;

        ti = proto_tree_add_item(tree, proto_foo, tvb, 0, -1, ENC_NA);
        foo_tree = proto_item_add_subtree(ti, ett_foo);
        proto_tree_add_item(foo_tree, hf_foo_pdu_type, tvb, offset, 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;
    }
    ...
}

void
proto_register_foo(void) {
    ...
        ...
        { &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 }
        },
        ...
    ...
}
...
 

這會解析這個假想協議的所有比特。We've introduced a new variable offset into the mix to help keep track of where we are in the packet dissection. With these extra bits in place, the whole protocol is now dissected.

此時我們測試用的UDP發送端也要做些修改:

//...
struct proto_foo
{
    UINT8  type;
    UINT8  flags;
    UINT16 seqno;
    UINT32 ipaddr;
};

//...
int main(int argc, char** argv)
{
//...
    proto_foo data;
    data.ipaddr = inet_addr("192.168.1.2");
    INT16 seq = 1;
    for(;;)
    {
        srand((unsigned int)time(NULL));
        data.type = rand() % 3 + 1;
        data.flags = rand() % 4 + 1;
        if(data.flags == 3)
            data.flags = 4;
        data.seqno = htons(seq++);

        sendto(sockfd, (const char*)&data, sizeof(proto_foo), 0, 
                        (SOCKADDR*)&addr, sizeof(addr));
        Sleep(1000);
    }

//...
}
 

此時的解析效果如下圖所示:

9.2.3 改進解析信息

我們可以通過一些額外數據來改進協議的顯示。第一步是添加一些文本標識。讓我們從標識packet types開始。首先添加一個簡單的type-name表。

代碼9.9 命名packet types

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

接下來用VALS宏來把上表與數據的相應部分關聯起來

代碼9.10 把名字添加到協議

{ &hf_foo_pdu_type,
    { "FOO PDU Type", "foo.type",
    FT_UINT8, BASE_DEC,
    VALS(packettypenames), 0x0,
    NULL, HFILL }
}

這有助於破解packets,我們可以對flags結構也這么做。

代碼9.11 把flags添加到協議

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

static int hf_foo_startflag = -1;
static int hf_foo_endflag = -1;
static int hf_foo_priorityflag = -1;

static void
dissect_foo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
    ...
        ...
        proto_tree_add_item(foo_tree, hf_foo_flags, tvb, offset, 1, ENC_BIG_ENDIAN);
        proto_tree_add_item(foo_tree, hf_foo_startflag, tvb, offset, 1, ENC_BIG_ENDIAN);
        proto_tree_add_item(foo_tree, hf_foo_endflag, tvb, offset, 1, ENC_BIG_ENDIAN);
        proto_tree_add_item(foo_tree, hf_foo_priorityflag, tvb, offset, 1, ENC_BIG_ENDIAN);
        offset += 1;
        ...
    ...
}

void
proto_register_foo(void) {
    ...
        ...
        { &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 }
        },
        ...
    ...
}
...
 

這里有些東西要注意。對flags來說,因為每一個bit都是不同的flag,我們使用FT_BOOLEAN類型,因為flag不是開就是關。第二,we include the flag mask in the 7th field of the data, which allows the system to mask the relevant bit. We've also changed the 5th field to 8, to indicate that we are looking at an 8 bit quantity when the flags are extracted. Then finally we add the extra constructs to the dissection routine. Note we keep the same offset for each of the flags.

此時的解析效果:

This is starting to look fairly full featured now, but there are a couple of other things we can do to make things look even more pretty. At the moment our dissection shows the packets as "Foo Protocol" which whilst correct is a little uninformative. We can enhance this by adding a little more detail. First, let's get hold of the actual value of the protocol type. We can use the handy function tvb_get_guint8() to do this. With this value in hand, there are a couple of things we can do. First we can set the INFO column of the non-detailed view to show what sort of PDU it is - which is extremely helpful when looking at protocol traces. Second, we can also display this information in the dissection window.

代碼9.12 增強顯示

static void
dissect_foo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
    guint8 packet_type = tvb_get_guint8(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)"));

    if (tree) { /* we are being asked for details */
        proto_item *ti = NULL;
        proto_tree *foo_tree = NULL;
        gint offset = 0;

        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)"));
        foo_tree = proto_item_add_subtree(ti, ett_foo);
        proto_tree_add_item(foo_tree, hf_foo_pdu_type, tvb, offset, 1, ENC_BIG_ENDIAN);
        offset += 1;
    }
}
 

此時顯示效果:

So here, after grabbing the value of the first 8 bits, we use it with one of the built-in utility routines val_to_str(), to lookup the value. If the value isn't found we provide a fallback which just prints the value in hex. We use this twice, once in the INFO field of the columns - if it's displayed, and similarly we append this data to the base of our dissecting tree.

最終源代碼:

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

#define FOO_PORT 9877

static int proto_foo = -1;
static int hf_foo_pdu_type = -1;
static int hf_foo_flags = -1;
static int hf_foo_seqno = -1;
static int hf_foo_ip = -1;
static gint ett_foo = -1;

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

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


static int hf_foo_start_flag    = -1;
static int hf_foo_end_flag      = -1;
static int hf_foo_prior_flag    = -1;

static void
dissect_foo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{   
    guint8 packet_type = tvb_get_guint8(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, pkt_type_names, "Unknown (0x%02x)"));
    
    /* proto details display */
    if(tree)
    {
        proto_item* ti = NULL;
        proto_tree* foo_tree = NULL;
        gint offset = 0;
        
        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, pkt_type_names, "Unknown (0x%02x)"));
        foo_tree = proto_item_add_subtree(ti, ett_foo);
        proto_tree_add_item(foo_tree, hf_foo_pdu_type, tvb, offset, 1, ENC_BIG_ENDIAN);
        offset += 1;
        proto_tree_add_item(foo_tree, hf_foo_flags, tvb, offset, 1, ENC_BIG_ENDIAN);
        proto_tree_add_item(foo_tree, hf_foo_start_flag, tvb, offset, 1, ENC_BIG_ENDIAN);
        proto_tree_add_item(foo_tree, hf_foo_end_flag, tvb, offset, 1, ENC_BIG_ENDIAN);
        proto_tree_add_item(foo_tree, hf_foo_prior_flag, tvb, offset, 1, ENC_BIG_ENDIAN);
        offset += 1;
        proto_tree_add_item(foo_tree, hf_foo_seqno, tvb, offset, 2, ENC_BIG_ENDIAN);
        offset += 2;
        proto_tree_add_item(foo_tree, hf_foo_ip, tvb, offset, 4, ENC_BIG_ENDIAN);
        offset += 4;
    }
}


void
proto_register_foo(void)
{
    static hf_register_info hf[] = 
    {
        {
            &hf_foo_pdu_type,
            {
                "Type", "foo.type",
                FT_UINT8, BASE_DEC,
                VALS(pkt_type_names), 0x0, 
                NULL, HFILL
            }
        },
        {
            &hf_foo_flags,
            {
                "Flags", "foo.flags",
                FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL
            }
        },
        {
            &hf_foo_start_flag,
            {
                "Start Flag", "foo.flags.start",
                FT_BOOLEAN, 8,
                NULL, FOO_START_FLAG, NULL, HFILL
            }
        },
        {
            &hf_foo_end_flag,
            {
                "End Flag", "foo.flags.end",
                FT_BOOLEAN, 8,
                NULL, FOO_END_FLAG, NULL, HFILL
            }
        },
        {
            &hf_foo_prior_flag,
            {
                "Priority Flag", "foo.flags.prior",
                FT_BOOLEAN, 8,
                NULL, FOO_PRIOR_FLAG, NULL, HFILL
            }
        },
        {
            &hf_foo_seqno,
            {
                "Sequence Number", "foo.seq",
                FT_UINT16, BASE_DEC,
                NULL, 0x0, NULL, HFILL
            }
        },
        {
            &hf_foo_ip,
            {
                "IP Address", "foo.ip",
                FT_IPv4, BASE_NONE,
                NULL, 0x0, NULL, HFILL
            }
        }
    };
    
    static gint *ett[] = { &ett_foo };

    proto_foo = proto_register_protocol (
        "FOO Protocol", /* name       */
        "FOO",      /* short name */
        "foo"       /* abbrev     */
        );
        
    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);
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM