File: wsdg_wiretap.adoc

package info (click to toggle)
wireshark 4.6.1-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 351,244 kB
  • sloc: ansic: 3,101,885; cpp: 129,710; xml: 100,972; python: 56,512; perl: 24,575; sh: 5,874; lex: 4,383; pascal: 4,304; makefile: 165; ruby: 113; objc: 91; tcl: 35
file content (362 lines) | stat: -rw-r--r-- 14,747 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
[#ChapterWiretap]

== Wiretap
Wiretap is a library used for reading and writing capture files in various formats.

=== Background

Wiretap was initially developed as a library to replace
libpcap, the current standard Unix library for packet capturing. Libpcap
is great in that it is very platform independent and has a wonderful
BPF optimizing engine. But it has some shortcomings as well. These
shortcomings came to a head during Wireshark's development.
Wiretap was developed so that:

1. The library can easily be amended with new packet filtering objects.
Libpcap is very TCP/IP-oriented. I want to filter on IPX objects, SNA objects,
etc. I also want any decent programmer to be able to add new filters to the
library.

2. The library can read file formats from many packet-capturing utilities.
Libpcap only reads Libpcap files.

3. The library can capture on more than one network interface at a time, and
save this trace in one file.

4. Network names can be resolved immediately after a trace and saved in the
trace file. That way, I can ship a trace of my firewall-protected network to a
colleague, and he'll see the proper hostnames for the IP addresses in the
packet capture, even though he doesn't have access to the DNS server behind my
LAN's firewall.

5. I want to look into the possibility of compressing packet data when saved
to a file, like Sniffer.

6. The packet-filter can be optimized for the host OS. Not all OSes have BPF;
SunOS has NIT and Solaris has DLPI, which both use the CMU/Stanford
packet-filter pseudomachine. RMON has another type of packet-filter syntax
which we could support.

Wiretap is very good at reading many file formats, as per #2
above. Wiretap has no filter capability at present; it currently doesn't
support packet capture, so it wouldn't be useful there, and filtering
when reading a capture file is done by Wireshark, using a more powerful
filtering mechanism than that provided by BPF.

=== 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

```c
/* 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-int.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`:

```c
#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:
```c
{ "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.
```c
{ "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` :
```c
{ "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`):
```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.

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

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

```c
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.

```c
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);
...
}
```

=== Additional notes on adding support for reading new capture formats

If your "open" routine needs to allocate any memory that persists while the file is open, you will need to save a reference to the allocated memory in the `priv` member of the `wtap` structure.
The data referenced directly from this pointer will be automatically freed by wiretap (using `g_free()`) when the capture file is closed.
However, if this data contains pointers to other memory structures you've allocated separately, you'll need to explicitly free those structures.
To do this, you'll need to create a "close" routine, and set the `wtap` 's `subtype_close` member to point at the routine.

The "read" routine should set the variable `data_offset` to the byte
offset within the capture file from which the "seek and read" routine
will read.  If the capture records consist of:

	capture record header
	pseudo-header (e.g., for ATM)
	frame data

then `data_offset` should point to the pseudo-header.  The first
sequential read pass will process and store the capture record header
data, but it will not store the pseudo-header.  Note that the
seek_and_read routine should work with the "random_fh" file handle
of the passed in wtap struct, instead of the "fh" file handle used
in the normal read routine.


=== Adding support for writing capture formats
To add the ability to write a new capture file format, you have to:

* Add a "can_write_encap" routine that returns an indication of
whether a given packet encapsulation format is supported by the
new capture file format;

* Add a "dump_open" routine that starts writing a file (writing
	headers, allocating data structures, etc.);

* Add a "dump" routine to write a packet to a file, and have the
	"dump_open" routine set the "subtype_write" member of the
	"wtap_dumper" structure passed to it to point to it;

* Add a "dump_close" routine, if necessary (if, for example, the
	"dump_open" routine allocates any memory, or if some of the file
	header can be written only after all the packets have been
	written), and have the "dump_open" routine set the
	"subtype_close" member of the "wtap_dumper" structure to point
	to it;

* Put pointers to the "can_write_encap" and "dump_open" routines
	in the "file_type_subtype_info" struct passed to
	wtap_register_file_type_subtypes().


=== Adding support for a new encapsulation type

// Start by describing encapsulation types
{missing}