How to Generate Wireshark Dissectors using TSN.1 Compiler

A practical guide for programmers


Wireshark, formerly known as Ethereal, is the world's most popular network protocol analyzer. One of the key strength of Wireshark is that users can extend Wireshark to analyze their own protocols by writing custom dissectors. However, writing custom dissectors can be a tedious and error-prone process. In this tutorial, we will show you a fast and easy way to develop Wireshark dissectors using the TSN.1 Compiler. We will give you step-by-step instrutions on how to develop a custom dissector on Linux. If this is the first time you are writing a Wireshark dissector, we suggest you to download the files used in this tutorial and follow the instructions exactly before you modify the example code for your protocol.

For Windows platform, only the build instructions are different, the dissector code is the exactly same. Contact us for more details.

1. Download and Build the Wireshark Source Code

A working Wireshark build is required to build any custom dissector. The following command sequence shows an example Linux build.

> gzip -cd wireshark-0.99.7.tar.gz | tar xvf -
> cd wireshark-0.99.7
> ./autogen.sh
> ./configure
> make
     

2. Write the Standard Dissector Code

Every dissector must start with a source file named "packet-xxx.c", where "xxx" is the name of your protocol. Among other things, this file defines functions to register your custom dissector with Wireshark and dissect packets for your protocol. For this tutorial we will use "packet-foo.c" as illustrated below.

#include "foo.h"

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <gmodule.h>
#include <epan/prefs.h>
#include <epan/packet.h>

/* forward reference */
void proto_register_foo();
void proto_reg_handoff_foo();
void dissect_foo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);

/* Define version if we are not building Wireshark statically */
#ifndef ENABLE_STATIC
G_MODULE_EXPORT const gchar version[] = "0.0";
#endif

static int proto_foo = -1;
static dissector_handle_t foo_handle;

#ifndef ENABLE_STATIC
G_MODULE_EXPORT void plugin_register(void)
{
   /* register the new protocol, protocol fields, and subtrees */
   if (proto_foo == -1) { /* execute protocol initialization only once */
      proto_register_foo();
   }
}

G_MODULE_EXPORT void plugin_reg_handoff(void){
   proto_reg_handoff_foo();
}
#endif

static int ett_foo = -1;

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

void proto_register_foo(void)
{
   module_t *module;

   if (proto_foo == -1)
   {
      proto_foo = proto_register_protocol (
         "Foo Protocol", /* name */
         "FOO",          /* short name */
         "foo"	         /* abbrev */
      );

      module = prefs_register_protocol(proto_foo, proto_reg_handoff_foo);
      proto_register_subtree_array(ett, array_length(ett));

      /* call the tsnc generated register function */
      Foo_register(proto_foo);
   }
}

void proto_reg_handoff_foo(void)
{
   static int Initialized=FALSE;

   /* register with wireshark to dissect udp packets on port 3001 */
   if (!Initialized) {
      foo_handle = create_dissector_handle(dissect_foo, proto_foo);
      dissector_add("udp.port", 3001, foo_handle);
   }
}

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

   if (tree) { /* we are being asked for details */
      proto_item *ti;
      Foo        *msg;

      ti = proto_tree_add_item(tree, proto_foo, tvb, 0, -1, FALSE);

      tree = proto_item_add_subtree(ti, ett_foo);

      msg = ep_alloc(sizeof(Foo));

      /* call the tsnc generated dissect function */
      Foo_dissect(msg, proto_foo, tvb, 0, pinfo, tree);
   }
}

All Wireshark dissector code follow this basic template. Next we will see how to generate the custom dissector code that does the actual packet dissection.

3. Generate the Custom Dissector Code using the TSN.1 Compiler

As you can see, the "packet-foo.c" includes a header file named "foo.h", which contains the prototypes for functions "Foo_register" and "Foo_dissect". These two functions are implemented in "foo.c". Both "foo.h" and "foo.c" are generated by the TSN.1 Compiler from "foo.tsn". The function "Foo_register" registers the field tables with Wireshark, and the function "Foo_dissect" dissects packets of the "foo" protocol.

The "foo.tsn" file contains the TSN.1 defintions for the messages in the "foo" protocol. TSN.1 is a formal notation for describing binary protocols. Please refer to the TSN.1 Specification for description and examples of TSN.1.

Below is the source listing for "foo.tsn".

FooMessageType ::= enumerated
{
   FOO_REQUEST,
   FOO_RESPONSE
}

FooReq() ::=
{
   TransactionId  8;

   // Other FooReq fields ...
}

FooRsp() ::=
{
   TransactionId  8;

   // Other FooRsp fields ...
}

Foo() ::=
{
   // Header Fields
   Type        8;
   SrcAddress  32;
   DstAddress  32;

   // Message Specific Fields
   Payload : case Type of
   {
      FOO_REQUEST  => Req : FooReq;
      FOO_RESPONSE => Rsp : FooRsp;
   }
}

The message definition "Foo" defines the format of a "foo" message. Each message starts with some common header fields. The 8-bit "Type" field indicates the type of the payload the message carries, whether it is a "FooReq" message or a "FooRsp" message. The 32-bit "SrcAddress" and "DstAddress" fields specify the IP address of the sender and the receiver respectively. After the header fields comes the message payload, which is defined using the TSN.1 case of construct. If the "Type" is equal to the enumerated constant "FOO_REQUEST" (0), then the payload contains a "FooReq" message. If the "Type" is "FOO_RESPONSE" (1), then the payload contains a "FooRsp" message.

Assuming you have the TSN.1 Compiler installed, type the following command to generate "foo.h" and "foo.c" from "foo.tsn":

> tsnc -wireshark foo.tsn
     

You can download the TSN.1 Compiler here.

4. Build the Dissector Plugin (Linux)

Now we have the dissector code generated. Let's build the dissector plugin using this Makefile. You may need to make changes to this Makefile to match with your Wireshark build environment.

Assuming "foo.tsn", "packet-foo.c", and "Makefile" are all in the same directory. Type "make" at the command prompt. This will build a dynamic linking library named "packet-foo.so" under ~/.wireshark/plugins/. This is the standard directory where Wireshark looks for dissector plugins at startup.

5. Dissect Packets

That's it! We are ready to dissect packets of the "foo" protocol using Wireshark. To illustrate how this works, download this pre-captured data file "foo_data".

Start Wireshark running. Go to the "File" menu and select "Open", locate the "foo_data" file and open it. You will see that there are two packets: one with a "FooReq" message and the other, a "FooRsp" message. Click on a packet to browse the detailed dissect output for the packet.

You might have noticed that the dissected output is not very informative. For example, the value for the "Type" field in the message header is shown as a numeric value. It would be nice if the corresponding enumerated literal is shown instead. Also the "SrcAddress" and the "DstAddress" are simply displayed as an integer, not in the dotted format for IPv4 address.

To refine the output with these enhancements, simply modify the following three lines in "foo.tsn" as below:

   Type        8  enumerated FooMessageType;
   SrcAddress  32; <display>ipv4</display>
   DstAddress  32; <display>ipv4</display>

Rebuild the dissector plugin with this change by typing "make". Now restart Wireshark and open the "foo_data" file again. There are many other ways to customize the dissect output. Please refer to the TSN.1 Compiler User's Manual for details.

Here is a summary of files used in this tutorial.

File Name Description
packet-foo.c Foo protocol dissector source
foo.tsn Foo protocol TSN.1 message defintions
Makefile Makefile that builds the dissector plugin
foo_data Foo protocol data