How to Generate Wireshark Dissectors using TSN.1 Compiler

A practical guide for developers


Wireshark 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 dissectors can be a daunting task. 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 instructions on how to develop a custom dissector plugin.

1. Download and Build the Wireshark Source Code

You must build Wireshark from source code before you can build any custom dissector. If you already have a working build, you can skip this step and go to step 2. This tutorial uses a Linux build environment. For Windows build instructions, please refer to the official Wireshark documentation. First download the Wireshark source code package from wireshark.org. Then issue the following command at the prompt:

> tar xvf wireshark-6.2.1.tar.xz
> cd wireshark-6.2.1
> ./configure
> make
     

If you don't have all the packages required by Wireshark installed on your system, the "configure" command will fail. Install the missing packages and try "configure" again followed by "make". It takes a few minutes for the build to complete. You can verify the build is successful by typing "./wireshark" at the prompt, and the Wireshark main GUI should pop up.

2. Download the Dissector Code for the Echo Protocol

For this tutorial we will develop a dissector for a simple protocol called "Echo". The code for this dissector can be downloaded here. Save the downloaded file "echo_dissector.tar.gz" under the "wireshark-6.2.1" directory and untar it.

> tar zxvf echo_dissector.tar.gz
     

This will install the echo dissector code under "plugins/echo". Most of the files are boilerplate. They are required for all dissectors. The only two files you need to pay attention to are "packet-echo.c" and "echo.tsn".

The "packet-echo.c" implements functions that are required for every dissector to register itself with Wireshark. For example, the dissector tells Wireshark the type of packet and the port it wants to dissect. In our example, we want to dissect UDP packets on port 3001.

#define ECHO_PORT 3001

void proto_reg_handoff_echo(void)
{
   static dissector_handle_t echo_handle;

   echo_handle = create_dissector_handle(dissect_echo, proto_echo);
   dissector_add_uint("udp.port", ECHO_PORT, echo_handle);
}

To dissect any packet, the dissector must register its data fields with Wireshark so that Wireshark knows how to display them. This is done by the "proto_register_echo" function. The real work is actually done inside the "EchoPacket_register" function. Writing this function by hand is quite tedious and boring. Fortunately as we will show later, this can be auto-generated.

void proto_register_echo(void)
{
   /* Setup protocol subtree array */
   static int *ett[] = { &ett_echo };

   /* We use the name "echo2" here because there is already a built-in
      protocol called "echo". */
   proto_echo = proto_register_protocol
      (
         "Echo Protocol", /* name */
         "ECHO2",         /* short name */
         "echo2"	  /* abbrev */
      );

   /* call the TSN.1 Compiler generated register function */
   EchoPacket_register(proto_echo);
   proto_register_subtree_array(ett, array_length(ett));
}

Finally, the code to dissect packets is implemented in the "dissect_echo" function. Wireshark will call this function for every UDP packet received on port 3001. Again the real work is done in the "EchoPacket_dissect" function. Both "EchoPacket_register" and "EchoPacket_dissect" are defined in "echo.c" with their prototypes declared in "echo.h". You will notice that "echo.h" and "echo.c" are not in the "plugins/echo" directory. In the next step, we will show you how to auto-generate them from "echo.tsn".

static void dissect_echo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
   if (check_col(pinfo->cinfo, COL_PROTOCOL))
      col_set_str(pinfo->cinfo, COL_PROTOCOL, "ECHO");
   /* 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;
      EchoPacket *msg;

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

      tree = proto_item_add_subtree(ti, ett_echo);

      msg = (EchoPacket *) ep_alloc(sizeof(EchoPacket));

      /* call the TSN.1 Compiler generated dissect function */
      EchoPacket_dissect(msg, proto_echo, tvb, 0, pinfo, tree);
   }
}

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

The "echo.tsn" file describes the echo protocol message formats in a formal notation called TSN.1. TSN.1 is a simple yet powerful 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 "echo.tsn".

EchoMsgType ::= enumerated
{
   ECHO_REQUEST,
   ECHO_RESPONSE
}

EchoReq() ::=
{
   SequenceNumber  8;
}

EchoRsp() ::=
{
   SequenceNumber  8;
}

EchoPacket() ::=
{
   // Header Fields
   Type        8  enumerated EchoMsgType;
   SrcAddress  32; <display>ipv4</display>
   DstAddress  32; <display>ipv4</display>

   // Message Specific Fields
   Payload : case Type of
   {
      ECHO_REQUEST  => echoReq : EchoReq;
      ECHO_RESPONSE => echoRsp : EchoRsp;
   }
}

There are two messages in the echo protocol, "EchoRequest" and "EchoResponse". The format of an echo packet is described by the "EchoPacket" message. Each packet starts with some common header fields. The 8-bit "Type" field indicates the type of the payload the message carries, whether it is an "EchoReq" message or an "EchoRsp" message. The 32-bit "SrcAddress" and "DstAddress" fields specify the IPv4 addresses 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 "ECHO_REQUEST" (0), then the payload contains an "EchoReq" message. If the "Type" is "ECHO_RESPONSE" (1), then the payload contains an "EchoRsp" message.

To generate the dissector code from "echo.tsn", first download the TSN.1 Compiler here and follow the installation guide to install it. Assuming you have installed the TSN.1 Compiler under your home directory, type the following command to generate "echo.h" and "echo.c" from "echo.tsn":

> cd plugins/echo
> ~/tsnc-v5.8.3/bin/tsnc -wireshark echo.tsn
     

You should find "echo.h" and "echo.c" in the "plugins/echo" directory.

4. Build the Dissector Plugin

Now we have the dissector code generated. Let's rebuild Wireshark with the new echo dissector plugin. Change the current directory back to "wireshark-6.2.1".

> cd ../..
> ./autogen.sh
> make -C plugins
     

The "autogen.sh" script should be invoked every time a new dissector plugin is added to the Wireshark build tree. The "make -C plugins" command is used to build the echo dissector plugin every time you make change to the dissector code. Please refer to "wireshark-6.2.1/doc/README.plugin" if you want to learn more.

5. Dissect Packets

That's it! Now we are ready to dissect packets of the echo protocol using Wireshark. To illustrate how this works, we will use data from a pre-captured data file: plugins/echo/echo_data.

Launch Wireshark and go to the "File" menu and select "Open", locate the "echo_data" file and open it. You will see that there are two packets: one containing an "EchoReq" message and the other, an "EchoRsp" message. Click on a packet to browse the detailed dissect output for the packet. See the screenshot below:

6. Summary

Here is a summary of the files used in this tutorial and their download links.

File Name Web Link
Wireshark Source Code http://wireshark.org
Echo Dissector Source http://www.protomatics.com/download/echo_dissector.tar.gz
TSN.1 Compiler http://www.protomatics.com/trial.html

To learn more about Wireshark dissector plugins, read the "README.plugin" file in the "wireshark-6.2.1/doc" directory. For example, it will tell you what files to change in order to rename the echo protocol to your protocol name. Basically all you need to do is the following: