10.2. Creating a new wiretap module
Prev | Chapter 10. Wiretap | Next

## 10.2. Creating a new wiretap module

As a demonstrative example, let’s create a new wiretap module for the fictional "Data Undergoing Mysterious Breakage" (DUMB) format. The DUMB format starts with the magic text "DUMB" and is a series of single-digit data lengths and then the associated strings.

To add support for the new DUMB capture format, our first step will be to create two new files, `dumb.h` and `dumb.c` in the wiretap directory. These will contain the code for handling the new format.

Now we’re ready to begin working on `dumb.c`. At minimum, it requires a few functions:

- An `open` routine (`dumb_open`) to determine if this is the correct parser for this file format
- A `read` routine (`dumb_read`) that can read a packet from the file.
- A `seek_read` routine (`dumb_seek_read`) to read a packet from a specific location in the file

    /* dumb.c
     *
     * Copyright 2025, Moshe Kaplan
     *
     * SPDX-License-Identifier: GPL-2.0-or-later
     *
     * Sample file for demonstrating new wiretap module
     */

    #include "config.h"
    #include "dumb.h"

    #include <string.h>

    #include "wtap_module.h"
    #include "file_wrappers.h"

    #define MAGIC_SIZE	4

    static const unsigned char dumb_magic[MAGIC_SIZE] = {
    	'D', 'U', 'M', 'B'
    };

    static int dumb_file_type_subtype = -1;

    void register_dumb(void);

    static bool dumb_read(wtap *wth, wtap_rec *rec, int *err, char **err_info, int64_t *data_offset);
    static bool dumb_seek_read(wtap* wth, int64_t seek_off, wtap_rec* rec, int* err, char** err_info);
    static bool dumb_read_packet(wtap *wth, FILE_T fh, wtap_rec *rec, int *err, char **err_info);

    wtap_open_return_val dumb_open(wtap *wth, int *err, char **err_info)
    {
        uint8_t filebuf[MAGIC_SIZE];
        int bytes_read;

        /* Read the magic into memory */
        bytes_read = file_read(filebuf, MAGIC_SIZE, wth->fh);
        if (bytes_read < 0) {
            /* Read error. */
            *err = file_error(wth->fh, err_info);
            return WTAP_OPEN_ERROR;
        }

        if (bytes_read < MAGIC_SIZE) {
            /* Not enough bytes */
            return WTAP_OPEN_NOT_MINE;
        }

        if (memcmp(filebuf, dumb_magic, sizeof(dumb_magic)) != 0)
    		return WTAP_OPEN_NOT_MINE;

        /* Looks like it's ours! Prepare the data structures: */

        wth->file_type_subtype = dumb_file_type_subtype;
        wth->file_encap = WTAP_ENCAP_JSON;
        wth->file_tsprec = WTAP_TSPREC_SEC;
        wth->subtype_read = dumb_read;
        wth->subtype_seek_read = dumb_seek_read;
        wth->snapshot_length = 0;

        /* And return that it's ours */
        return WTAP_OPEN_MINE;
    }

    static const struct supported_block_type dumb_blocks_supported[] = {
        /* We support packet blocks, with no comments or other options. */
        { WTAP_ENCAP_PER_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
    };

    static const struct file_type_subtype_info dumb_info = {
        "Data Undergoing Mysterious Breakage (DUMB) file format", "dumb", "dumb", NULL,
        false, BLOCKS_SUPPORTED(dumb_blocks_supported),
        NULL, NULL, NULL
    };

    void register_dumb(void)
    {
        dumb_file_type_subtype = wtap_register_file_type_subtype(&dumb_info);

        /*
         * Register name for backwards compatibility with the
         * wtap_filetypes table in Lua.
         */
        wtap_register_backwards_compatibility_lua_name("dumb",
                                                       dumb_file_type_subtype);
    }

    /* Read the next packet */
    static bool dumb_read(wtap *wth, wtap_rec *rec, int *err, char **err_info, int64_t *data_offset)
    {
        *data_offset = file_tell(wth->fh);
    	return dumb_read_packet(wth, wth->fh, rec, err, err_info);
    }

    static bool dumb_seek_read(wtap* wth, int64_t seek_off, wtap_rec* rec, int* err, char** err_info)
    {
        if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
            return false;

        return dumb_read_packet(wth, wth->random_fh, rec, err, err_info);
    }

    static bool dumb_read_packet(wtap *wth, FILE_T fh, wtap_rec *rec, int *err, char **err_info)
    {
        uint8_t     payload_raw;    /* Length of the packet's data: Data */
        uint8_t     packet_size;    /* Length of the entire packet: Length + Data */
        uint8_t     *packet_data;   /* Actual data within the packet */

        /* Read the header with the size
           Use wtap_read_bytes_or_eof because we may be at EOF before we start
         */
        if (!wtap_read_bytes_or_eof(fh, &payload_raw, sizeof(payload_raw), err, err_info)) {
            return false;
        }

        /* Validate that the header is between 0 and 9 bytes */
        if (payload_raw < '0' || payload_raw > '9') {
            return false;
        }
        /* Convert digit from an ASCII character to integer */
        packet_size = payload_raw - '0';

        /* Prepare the buffer for our data */
        ws_buffer_assure_space(&rec->data, packet_size);
        ws_buffer_increase_length(&rec->data, packet_size);
        packet_data = ws_buffer_start_ptr(&rec->data);

        /* Read in the packet data
           Use wtap_read_bytes to return an error of WTAP_ERR_SHORT_READ if we hit EOF.
        */
        if (!wtap_read_bytes(fh, packet_data, packet_size, err, err_info)) {
            return false;
        }

        wtap_setup_packet_rec(rec, wth->file_encap);
        rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
        rec->rec_header.packet_header.caplen = packet_size;
        rec->rec_header.packet_header.len = packet_size;
        return true;
    }

`dumb.h` will have the functions exposed to the rest of wiretap. For now, that will only need to be `dumb_open`:

    #ifndef __DUMB_H__
    #define __DUMB_H__

    #include <glib.h>
    #include "wtap.h"

    wtap_open_return_val dumb_open(wtap *wth, int *err, char **err_info);

    #endif

Now that we have `dumb.c` and `dumb.h`, let’s integrate them into Wireshark:

- In `wiretap/CMakeLists.txt`, add `dumb.c` to the list of `WIRETAP_C_MODULE_FILES`

- In `wiretap/file_access.c`:

  - Add an #include for `dumb.h`
  - Modify `open_info_base` table to define the DUMB format as being supported by Wireshark and specify the `dumb_open` routine. For example:

    { "Data Undergoing Mysterious Breakage (DUMB) file", OPEN_INFO_MAGIC, dumb_open, "dumb", NULL, NULL },

- Modify the `wireshark_file_type_extensions_base` table so that this format can be selected with the 'open' dialog.

    { "Data Undergoing Mysterious Breakage (DUMB)", true, "dumb" },

With this, we now have added support for a simple DUMB file format to libwiretap. Let’s now review this code in more detail:

We declared our DUMB format by adding an entry to `file_access.c’s `open_info_base` :

    { "Data Undergoing Mysterious Breakage (DUMB) file", OPEN_INFO_MAGIC, dumb_open, "dumb", NULL, NULL },

`open_info_base` is an array of `open_info` objects. `open_info` objects are specified as the following (from `file_access.c`):

    struct open_info {
        const char *name;                 /* Description */
        wtap_open_type type;              /* Open routine type */
        wtap_open_routine_t open_routine; /* Open routine */
        const char *extensions;           /* List of extensions used for this file type */
        char **extensions_set;            /* Array of those extensions; populated using extensions member during initialization */
        void* wslua_data;                 /* Data for Lua file readers */
    };

The important values here are the `name`, `type`, and `open_routine`. The remaining parameters can be `NULL`.

For convenience, we also added an entry to `file_access.c’s `wireshark_file_type_extensions_base` table so that this format can be selected with the 'open' dialog.

    { "Data Undergoing Mysterious Breakage (DUMB)", true, "dumb" },

Each entry in this table is an `file_extension_info`, as defined in `wtap.h`:

    struct file_extension_info {
        /* the file type description */
        const char *name;

        /* true if this is a capture file type */
        bool is_capture_file;

        /* a semicolon-separated list of file extensions used for this type */
        const char *extensions;
    };

If `is_capture_file` is `true`, then the extensions specified in `extensions` will be included within Wireshark’s list of known capture file types and so included in Wireshark’s `Open Capture File` dialog.

Now let’s get to the meat of it in `dumb.c`, starting with `dumb_open`. `dumb_open` reads `MAGIC_SIZE` (4) bytes from the file and confirms that they are equal to "DUMB". Once the validation is complete, the `wtap` object in `wth` is set with the various required values, which most significantly contains the filetype information, encapsulation type (`WTAP_ENCAP_JSON`), and the functions used for sequential reading (`subtype_read`), and random access (`subtype_seek_read`). `dumb_open` then returns `WTAP_OPEN_MINE` to indicate that this is the correct handler.

In this example, `dumb_read` (`subtype_read`) and `dumb_seek_read` (`subtype_seek_read`) are the functions called by the rest of Wireshark to retrieve data from the DUMB capture file format. `dumb_read` is the general routine used for reading packets from the capture file and will generally be called repeatedly until the entire file is processed. `dumb_seek_read` would be used later to "seek and read" for individual packets. To avoid repeating the code, both functions call a `dumb_read_packet` function, which does the heavy lifting of extracting data from the file.

We also needed to add a registration routine of `register_dumb`. This will be added to `wtap_modules.c` by `make-regs.py` as part of the build process. The `register_dumb` routine takes a `file_type_subtype_info` struct and passes it to `wtap_register_file_type_subtype`. The `file_type_subtype_info` includes a descriptive name, a short name that’s convenient to type on a command line (no blanks or capital letters, please), common file extensions to open and save, any block types supported, and pointers to the "can_write_encap" and if writing that file type is supported (see below), "dump_open" routines otherwise NULL pointers.

    static const struct supported_block_type dumb_blocks_supported[] = {
        /* We support packet blocks, with no comments or other options. */
        { WTAP_ENCAP_PER_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
    };

    static const struct file_type_subtype_info dumb_info = {
        "Data Undergoing Mysterious Breakage (DUMB) file format", "dumb", "dumb", NULL,
        false, BLOCKS_SUPPORTED(dumb_blocks_supported),
        NULL, NULL, NULL
    };

    void register_dumb(void)
    {
        dumb_file_type_subtype = wtap_register_file_type_subtype(&dumb_info);
    ...
    }

Prev | Up | Next
Chapter 10. Wiretap | Home | 10.3. Additional notes on adding support for reading new capture formats