summaryrefslogtreecommitdiffstats
path: root/epan/dissectors/packet-null.c
blob: d62e12a72426a4d3359829dfd5a541be8f2c1595 (plain)
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
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
/* packet-null.c
 * Routines for null packet disassembly
 *
 * Wireshark - Network traffic analyzer
 * By Gerald Combs <gerald@wireshark.org>
 *
 * This file created by Mike Hall <mlh@io.com>
 * Copyright 1998
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

#include "config.h"

#include <wsutil/pint.h>

#include <epan/packet.h>
#include <epan/capture_dissectors.h>
#include "packet-ip.h"
#include "packet-ppp.h"
#include <epan/etypes.h>
#include <epan/aftypes.h>

void proto_register_null(void);
void proto_reg_handoff_null(void);

static dissector_table_t null_dissector_table;
static dissector_table_t ethertype_dissector_table;

/* protocols and header fields */
static int proto_null;
static int hf_null_etype;
static int hf_null_family;

static int ett_null;

/* Null/loopback structs and definitions */

/* Family values. */
static const value_string family_vals[] = {
  {BSD_AF_INET,          "IP"             },
  {BSD_AF_ISO,           "OSI"            },
  {BSD_AF_APPLETALK,     "Appletalk"      },
  {BSD_AF_IPX,           "Netware IPX/SPX"},
  {BSD_AF_INET6_BSD,     "IPv6"           },
  {BSD_AF_INET6_FREEBSD, "IPv6"           },
  {BSD_AF_INET6_DARWIN,  "IPv6"           },
  {0,                    NULL             }
};

static dissector_handle_t null_handle, loop_handle;
static capture_dissector_handle_t null_cap_handle;

static dissector_handle_t ppp_hdlc_handle;
static capture_dissector_handle_t ppp_hdlc_cap_handle;

static bool
capture_null( const unsigned char *pd, int offset _U_, int len, capture_packet_info_t *cpinfo, const union wtap_pseudo_header *pseudo_header _U_ )
{
  uint32_t null_header;

  /*
   * BSD drivers that use DLT_NULL - including the FreeBSD 3.2 ISDN-for-BSD
   * drivers, as well as the 4.4-Lite and FreeBSD loopback drivers -
   * stuff the AF_ value for the protocol, in *host* byte order, in the
   * first four bytes.
   *
   * However, the IRIX and UNICOS/mp snoop socket mechanism supplies,
   * on loopback devices, a 4-byte header that has a 2 byte (big-endian)
   * AF_ value and 2 bytes of 0, so it's
   *
   *    0000AAAA
   *
   * when read on a little-endian machine and
   *
   *    AAAA0000
   *
   * when read on a big-endian machine.  The current CVS version of libpcap
   * compensates for this by converting it to standard 4-byte format before
   * processing the packet, but snoop captures from IRIX or UNICOS/mp
   * have the 2-byte+2-byte header, as might tcpdump or libpcap captures
   * with older versions of libpcap.
   *
   * AF_ values are small integers, and probably fit in 8 bits (current
   * values on the BSDs do), and have their upper 24 bits zero.
   * This means that, in practice, if you look at the header as a 32-bit
   * integer in host byte order:
   *
   *    on a little-endian machine:
   *
   *            a little-endian DLT_NULL header looks like
   *
   *                    000000AA
   *
   *            a big-endian DLT_NULL header, or a DLT_LOOP header, looks
   *            like
   *
   *                    AA000000
   *
   *            an IRIX or UNICOS/mp DLT_NULL header looks like
   *
   *                    0000AA00
   *
   *    on a big-endian machine:
   *
   *            a big-endian DLT_NULL header, or a DLT_LOOP header, looks
   *            like
   *
   *                    000000AA
   *
   *            a little-endian DLT_NULL header looks like
   *
   *                    AA000000
   *
   *            an IRIX or UNICOS/mp DLT_NULL header looks like
   *
   *                    00AA0000
   *
   * However, according to Gerald Combs, a FreeBSD ISDN PPP dump that
   * Andreas Klemm sent to wireshark-dev has a packet type of DLT_NULL,
   * and the family bits look like PPP's protocol field.  (Was this an
   * older, or different, ISDN driver?)  Looking at what appears to be
   * that capture file, it appears that it's using PPP in HDLC framing,
   * RFC 1549, wherein the first two octets of the frame are 0xFF
   * (address) and 0x03 (control), so the header bytes are, in order:
   *
   *    0xFF
   *    0x03
   *    high-order byte of a PPP protocol field
   *    low-order byte of a PPP protocol field
   *
   * If we treat that as a 32-bit host-byte-order value, it looks like
   *
   *    PPPP03FF
   *
   * where PPPP is a byte-swapped PPP protocol type if we read it on
   * a little-endian machine and
   *
   *    FF03PPPP
   *
   * where PPPP is a PPP protocol type if we read it on a big-endian
   * machine.  0x0000 does not appear to be a valid PPP protocol type
   * value, so at least one of those hex digits is guaranteed not to
   * be 0.
   *
   * Old versions of libpcap for Linux used DLT_NULL for loopback devices,
   * but not any other devices.  (Current versions use DLT_EN10MB for it.)
   * The Linux loopback driver puts an *Ethernet* header at the beginning
   * of loopback packets, with fake source and destination addresses and
   * the appropriate Ethernet type value; however, those older versions of
   * libpcap for Linux compensated for this by skipping the source and
   * destination MAC addresses, replacing them with 2 bytes of 0.
   * This means that if we're reading the capture on a little-endian
   * machine, the header, treated as a 32-bit integer, looks like
   *
   *    EEEE0000
   *
   * where EEEE is a byte-swapped Ethernet type, and if we're reading it
   * on a big-endian machine, it looks like
   *
   *    0000EEEE
   *
   * where EEEE is an Ethernet type.
   *
   * If the first 2 bytes of the header are FF 03:
   *
   *    it can't be a big-endian BSD DLT_NULL header, or a DLT_LOOP
   *    header, as AF_ values are small so the first 2 bytes of the
   *    header would be 0;
   *
   *    it can't be a little-endian BSD DLT_NULL header, as the
   *    resulting AF_ value would be >= 0x03FF, which is too big
   *    for an AF_ value;
   *
   *    it can't be an IRIX or UNICOS/mp DLT_NULL header, as the
   *    resulting AF_ value with be 0x03FF.
   *
   * So the first thing we do is check the first two bytes of the
   * header; if it's FF 03, we treat the packet as a PPP frame.
   *
   * Otherwise, if the upper 16 bits are non-zero, either:
   *
   *    it's a BSD DLT_NULL header whose AF_ value is not in our
   *    byte order;
   *
   *    it's an IRIX or UNICOS/mp DLT_NULL header being read on
   *    a big-endian machine;
   *
   *    it's a Linux DLT_NULL header being read on a little-endian
   *    machine.
   *
   * In all those cases except for the IRIX or UNICOS/mp DLT_NULL header,
   * we should byte-swap it (if it's a Linux DLT_NULL header, that'll
   * put the Ethernet type in the right byte order).  In the case
   * of the IRIX or UNICOS/mp DLT_NULL header, we should just get
   * the upper 16 bits as an AF_ value.
   *
   * If it's a BSD DLT_NULL header whose AF_ value is not in our byte
   * order, then the upper 2 hex digits would be non-zero and the next
   * 2 hex digits down would be zero, as AF_ values fit in 8 bits, and
   * the upper 2 hex digits are the *lower* 8 bits of the value.
   *
   * If it's an IRIX or UNICOS/mp DLT_NULL header, the upper 2 hex digits
   * would be zero and the next 2 hex digits down would be non-zero, as
   * the upper 16 bits are a big-endian AF_ value.  Furthermore, the
   * next 2 hex digits down are likely to be < 0x60, as 0x60 is 96,
   * and, so far, we're far from requiring AF_ values that high.
   *
   * If it's a Linux DLT_NULL header, the third hex digit from the top
   * will be >= 6, as Ethernet types are >= 1536, or 0x0600, and
   * it's byte-swapped, so the second 2 hex digits from the top are
   * >= 0x60.
   *
   * So, if the upper 16 bits are non-zero:
   *
   *    if the upper 2 hex digits are 0 and the next 2 hex digits are
   *    in the range 0x00-0x5F, we treat it as a big-endian IRIX or
   *    UNICOS/mp DLT_NULL header;
   *
   *    otherwise, we byte-swap it and do the next stage.
   *
   * If the upper 16 bits are zero, either:
   *
   *    it's a BSD DLT_NULL header whose AF_ value is in our byte
   *    order;
   *
   *    it's an IRIX or UNICOS/mp DLT_NULL header being read on
   *    a little-endian machine;
   *
   *    it's a Linux DLT_NULL header being read on a big-endian
   *    machine.
   *
   * In all of those cases except for the IRIX or UNICOS/mp DLT_NULL header,
   * we should *not* byte-swap it.  In the case of the IRIX or UNICOS/mp
   * DLT_NULL header, we should extract the AF_ value and byte-swap it.
   *
   * If it's a BSD DLT_NULL header whose AF_ value is in our byte order,
   * the upper 6 hex digits would all be zero.
   *
   * If it's an IRIX or UNICOS/mp DLT_NULL header, the upper 4 hex
   * digits would be zero and the next 2 hex digits would not be zero.
   * Furthermore, the third hex digit from the bottom would be <
   */
  if (!BYTES_ARE_IN_FRAME(0, len, 2))
    return false;

  if (pd[0] == 0xFF && pd[1] == 0x03) {
    /*
     * Hand it to PPP.
     */
    return call_capture_dissector(ppp_hdlc_cap_handle, pd, 0, len, cpinfo, pseudo_header);
  } else {
    /*
     * Treat it as a normal DLT_NULL header.
     */
    if (!BYTES_ARE_IN_FRAME(0, len, (int)sizeof(null_header)))
      return false;

    memcpy((char *)&null_header, (const char *)&pd[0], sizeof(null_header));

    if ((null_header & 0xFFFF0000) != 0) {
      /*
       * It is possible that the AF_ type was only a 16 bit value.
       * IRIX and UNICOS/mp loopback snoop use a 4 byte header with
       * AF_ type in the first 2 bytes!
       * BSD AF_ types will always have the upper 8 bits as 0.
       */
      if ((null_header & 0xFF000000) == 0 &&
          (null_header & 0x00FF0000) < 0x00060000) {
        /*
         * Looks like a IRIX or UNICOS/mp loopback header, in the
         * correct byte order.  Set the null header value to the
         * AF_ type, which is in the upper 16 bits of "null_header".
         */
        null_header >>= 16;
      } else {
        /* Byte-swap it. */
        null_header = GUINT32_SWAP_LE_BE(null_header);
      }
    } else {
      /*
       * Check for an IRIX or UNICOS/mp snoop header.
       */
      if ((null_header & 0x000000FF) == 0 &&
          (null_header & 0x0000FF00) < 0x00000600) {
        /*
         * Looks like a IRIX or UNICOS/mp loopback header, in the
         * wrong byte order.  Set the null header value to the AF_
         * type; that's in the lower 16 bits of "null_header", but
         * is byte-swapped.
         */
        null_header = GUINT16_SWAP_LE_BE(null_header & 0xFFFF);
      }
    }

    /*
     * The null header value must be greater than the IEEE 802.3 maximum
     * frame length to be a valid Ethernet type; if it is, hand it
     * to "capture_ethertype()", otherwise treat it as a BSD AF_type (we
     * wire in the values of the BSD AF_ types, because the values
     * in the file will be BSD values, and the OS on which
     * we're building this might not have the same values or
     * might not have them defined at all; XXX - what if different
     * BSD derivatives have different values?).
     */
    if (null_header > IEEE_802_3_MAX_LEN)
      return try_capture_dissector("ethertype", null_header, pd, 4, len, cpinfo, pseudo_header);
    else
      return try_capture_dissector("null.bsd", null_header, pd, 4, len, cpinfo, pseudo_header);
  }

  return false;
}

static int
dissect_null(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
{
  uint32_t      null_header;
  proto_tree    *fh_tree;
  proto_item    *ti;
  tvbuff_t      *next_tvb;

  /*
   * See comment in "capture_null()" for an explanation of what we're
   * doing.
   */
  if (tvb_get_ntohs(tvb, 0) == 0xFF03) {
    /*
     * Hand it to PPP.
     */
    call_dissector(ppp_hdlc_handle, tvb, pinfo, tree);
  } else {

    /* load the top pane info. This should be overwritten by
       the next protocol in the stack */
    col_set_str(pinfo->cinfo, COL_RES_DL_SRC, "N/A");
    col_set_str(pinfo->cinfo, COL_RES_DL_DST, "N/A");
    col_set_str(pinfo->cinfo, COL_PROTOCOL, "N/A");
    col_set_str(pinfo->cinfo, COL_INFO, "Null/Loopback");

    /*
     * Treat it as a normal DLT_NULL header.  Fetch it in host
     * byte order.
     */
    null_header = tvb_get_h_uint32(tvb, 0);

    if ((null_header & 0xFFFF0000) != 0) {
      /*
       * It is possible that the AF_ type was only a 16 bit value.
       * IRIX and UNICOS/mp loopback snoop use a 4 byte header with
       * AF_ type in the first 2 bytes!
       * BSD AF_ types will always have the upper 8 bits as 0.
       */
      if ((null_header & 0xFF000000) == 0 &&
          (null_header & 0x00FF0000) < 0x00060000) {
        /*
         * Looks like a IRIX or UNICOS/mp loopback header, in the
         * correct byte order.  Set the null header value to the
         * AF_ type, which is in the upper 16 bits of "null_header".
         */
        null_header >>= 16;
      } else {
        /* Byte-swap it. */
        null_header = GUINT32_SWAP_LE_BE(null_header);
      }
    } else {
      /*
       * Check for an IRIX or UNICOS/mp snoop header.
       */
      if ((null_header & 0x000000FF) == 0 &&
          (null_header & 0x0000FF00) < 0x00000600) {
        /*
         * Looks like a IRIX or UNICOS/mp loopback header, in the
         * wrong byte order.  Set the null header value to the AF_
         * type; that's in the lower 16 bits of "null_header", but
         * is byte-swapped.
         */
        null_header = GUINT16_SWAP_LE_BE(null_header & 0xFFFF);
      }
    }

    /*
     * The null header value must be greater than the IEEE 802.3 maximum
     * frame length to be a valid Ethernet type; if it is, dissect it
     * as one, otherwise treat it as a BSD AF_type (we wire in the values
     * of the BSD AF_ types, because the values in the file will be BSD
     * values, and the OS on which we're building this might not have the
     * same values or might not have them defined at all; XXX - what if
     * different BSD derivatives have different values?).
     */
    if (null_header > IEEE_802_3_MAX_LEN) {
      if (tree) {
        ti = proto_tree_add_item(tree, proto_null, tvb, 0, 4, ENC_NA);
        fh_tree = proto_item_add_subtree(ti, ett_null);
        proto_tree_add_uint(fh_tree, hf_null_etype, tvb, 0, 4,
          (uint16_t) null_header);
      }

      next_tvb = tvb_new_subset_remaining(tvb, 4);
      if (!dissector_try_uint(ethertype_dissector_table,
            (uint16_t) null_header, next_tvb, pinfo, tree))
        call_data_dissector(next_tvb, pinfo, tree);
    } else {
      /* populate a tree in the second pane with the status of the link
         layer (ie none) */
      if (tree) {
        ti = proto_tree_add_item(tree, proto_null, tvb, 0, 4, ENC_NA);
        fh_tree = proto_item_add_subtree(ti, ett_null);
        proto_tree_add_uint(fh_tree, hf_null_family, tvb, 0, 4, null_header);
      }

      next_tvb = tvb_new_subset_remaining(tvb, 4);
      if (!dissector_try_uint(null_dissector_table, null_header,
            next_tvb, pinfo, tree)) {
        /* No sub-dissector found.  Label rest of packet as "Data" */
        call_data_dissector(next_tvb, pinfo, tree);
      }
    }
  }
  return tvb_captured_length(tvb);
}

/*
 * OpenBSD DLT_LOOP; like DLT_NULL, but with the first 4 byte *always*
 * being a *big-endian* type.
 */
static int
dissect_loop(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
{
  uint32_t      loop_family;
  proto_tree    *fh_tree;
  proto_item    *ti;
  tvbuff_t      *next_tvb;

  /* load the top pane info. This should be overwritten by
     the next protocol in the stack */
  col_set_str(pinfo->cinfo, COL_RES_DL_SRC, "N/A");
  col_set_str(pinfo->cinfo, COL_RES_DL_DST, "N/A");
  col_set_str(pinfo->cinfo, COL_PROTOCOL, "N/A");
  col_set_str(pinfo->cinfo, COL_INFO, "Null/Loopback");

  /* populate a tree in the second pane with the status of the link
     layer (ie none) */
  loop_family = tvb_get_ntohl(tvb, 0);
  if (tree) {
    ti = proto_tree_add_item(tree, proto_null, tvb, 0, 4, ENC_NA);
    fh_tree = proto_item_add_subtree(ti, ett_null);
    proto_tree_add_uint(fh_tree, hf_null_family, tvb, 0, 4, loop_family);
  }

  next_tvb = tvb_new_subset_remaining(tvb, 4);
  if (!dissector_try_uint(null_dissector_table, loop_family,
        next_tvb, pinfo, tree)) {
    /* No sub-dissector found.  Label rest of packet as "Data" */
    call_data_dissector(next_tvb, pinfo, tree);
  }
  return tvb_captured_length(tvb);
}

void
proto_register_null(void)
{
  static hf_register_info hf[] = {

    /* registered here but handled in ethertype.c */
    { &hf_null_etype,
      { "Type",         "null.type", FT_UINT16, BASE_HEX, VALS(etype_vals), 0x0,
        NULL, HFILL }},

    { &hf_null_family,
      { "Family",       "null.family", FT_UINT32, BASE_DEC, VALS(family_vals), 0x0,
        NULL, HFILL }}
  };
  static int *ett[] = {
    &ett_null,
  };

  proto_null = proto_register_protocol("Null/Loopback", "Null", "null");
  proto_register_field_array(proto_null, hf, array_length(hf));
  proto_register_subtree_array(ett, array_length(ett));

  /* subdissector code */
  null_dissector_table = register_dissector_table("null.type",
                                                  "Null type", proto_null, FT_UINT32, BASE_DEC);

  register_capture_dissector_table("null.bsd", "Null/Loopback BSD AF");

  null_handle = register_dissector("null", dissect_null, proto_null);
  loop_handle = register_dissector("null.loop", dissect_loop, proto_null);
  null_cap_handle = register_capture_dissector("null", capture_null, proto_null);
}

void
proto_reg_handoff_null(void)
{
  /*
   * Get a handle for the PPP-in-HDLC-like-framing dissector and
   * the "I don't know what this is" dissector.
   */
  ppp_hdlc_handle = find_dissector_add_dependency("ppp_hdlc", proto_null);

  ethertype_dissector_table = find_dissector_table("ethertype");

  dissector_add_uint("wtap_encap", WTAP_ENCAP_NULL, null_handle);

  dissector_add_uint("wtap_encap", WTAP_ENCAP_LOOP, loop_handle);

  capture_dissector_add_uint("wtap_encap", WTAP_ENCAP_NULL, null_cap_handle);
  capture_dissector_add_uint("wtap_encap", WTAP_ENCAP_LOOP, null_cap_handle);

  ppp_hdlc_cap_handle = find_capture_dissector("ppp_hdlc");
}

/*
 * Editor modelines
 *
 * Local Variables:
 * c-basic-offset: 2
 * tab-width: 8
 * indent-tabs-mode: nil
 * End:
 *
 * ex: set shiftwidth=2 tabstop=8 expandtab:
 * :indentSize=2:tabSize=8:noTabs=true:
 */