aboutsummaryrefslogtreecommitdiffstats
path: root/epan/dissectors/packet-gfp.c
blob: fcd91480c9e50e065c8759ea9c0f513bbf29ed03 (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
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
/* packet-gfp.c
 * Routines for Generic Framing Procedure dissection
 * Copyright 2015, John Thacker <johnthacker@gmail.com>
 *
 * Wireshark - Network traffic analyzer
 * By Gerald Combs <gerald@wireshark.org>
 * Copyright 1998 Gerald Combs
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

/*
 * Generic Framing Procedure (GFP) is used to map octet-aligned variable
 * length payloads (e.g. Ethernet, MPLS, octet-aligned PPP, IP) into
 * octet-synchronous signals such as SONET/SDH (ITU-T G.707) and OTN
 * (ITU-T G.709). GFP is a telecommunications industry standard defined in
 * ITU-T G.7041/Y.1303.
 *
 * Reference:
 * https://www.itu.int/rec/T-REC-G.7041/
 */

#include <config.h>

#include <epan/packet.h>   /* Should be first Wireshark include (other than config.h) */
#include <epan/expert.h>
#include <epan/prefs.h>
#include <epan/crc16-tvb.h>
#include <epan/crc32-tvb.h>
#include <epan/decode_as.h>
#include <epan/proto_data.h>

#include <wiretap/wtap.h>

/* Prototypes */
/* (Required to prevent [-Wmissing-prototypes] warnings */
void proto_reg_handoff_gfp(void);
void proto_register_gfp(void);

/* Initialize the protocol and registered fields */
static int proto_gfp = -1;
static int hf_gfp_pli = -1;
static int hf_gfp_chec = -1;
static int hf_gfp_chec_status = -1;
static int hf_gfp_type = -1;
static int hf_gfp_pti = -1;
static int hf_gfp_pfi = -1;
static int hf_gfp_exi = -1;
static int hf_gfp_upi_data = -1;
static int hf_gfp_upi_management = -1;
static int hf_gfp_thec = -1;
static int hf_gfp_thec_status = -1;
static int hf_gfp_cid = -1;
static int hf_gfp_ehec = -1;
static int hf_gfp_ehec_status = -1;
static int hf_gfp_fcs = -1;
static int hf_gfp_fcs_good = -1;
static int hf_gfp_fcs_bad = -1;

static expert_field ei_gfp_pli_idle_nonempty = EI_INIT;
static expert_field ei_gfp_pli_unknown = EI_INIT;
static expert_field ei_gfp_pli_invalid = EI_INIT;
static expert_field ei_gfp_chec_bad = EI_INIT;
static expert_field ei_gfp_thec_bad = EI_INIT;
static expert_field ei_gfp_ehec_bad = EI_INIT;
static expert_field ei_gfp_exi_short = EI_INIT;
static expert_field ei_gfp_pfi_short = EI_INIT;
static expert_field ei_gfp_payload_undecoded = EI_INIT;
static expert_field ei_gfp_fcs_bad = EI_INIT;

#define GFP_USER_DATA 0
#define GFP_CLIENT_MANAGEMENT 4
#define GFP_MANAGEMENT_COMMUNICATIONS 5

#define GFP_EXT_NULL 0
#define GFP_EXT_LINEAR 1
#define GFP_EXT_RING 2

/* Initialize the subtree pointers */
static gint ett_gfp = -1;
static gint ett_gfp_type = -1;
static gint ett_gfp_fcs = -1;

static dissector_table_t gfp_dissector_table;

/* ITU-T G.7041 6.1.1, 6.2 */
static const range_string gfp_pli_rvals[] = {
    {0, 0, "Idle Frame"},
    {1, 3, "Control Frame (Reserved)"},
    {4, G_MAXUINT16, "Client Frame"},
    {0, 0, NULL}
};

static const int *gfp_type_data_fields[] = {
    &hf_gfp_pti,
    &hf_gfp_pfi,
    &hf_gfp_exi,
    &hf_gfp_upi_data,
    NULL
};

static const int *gfp_type_management_fields[] = {
    &hf_gfp_pti,
    &hf_gfp_pfi,
    &hf_gfp_exi,
    &hf_gfp_upi_management,
    NULL
};

static const value_string gfp_pti_vals[] = {
    {GFP_USER_DATA, "User Data"},
    {GFP_CLIENT_MANAGEMENT, "Client Management"},
    {GFP_MANAGEMENT_COMMUNICATIONS, "Management Communications"},
    {0, NULL}
};

static const value_string gfp_exi_vals[] = {
    {GFP_EXT_NULL, "Null Extension Header"},
    {GFP_EXT_LINEAR, "Linear Frame"},
    {GFP_EXT_RING, "Ring Frame"},
    {0, NULL}
};

static const range_string gfp_upi_data_rvals[] = {
    {0, 0, "Reserved and not available"},
    {1, 1, "Frame-Mapped Ethernet"},
    {2, 2, "Frame-Mapped PPP"},
    {3, 3, "Transparent Fibre Channel"},
    {4, 4, "Transparent FICON"},
    {5, 5, "Transparent ESCON"},
    {6, 6, "Transparent Gbit Ethernet"},
    {7, 7, "Reserved"},
    {8, 8, "Frame-Mapped Multiple Access Protocol over SDH (MAPOS)"},
    {9, 9, "Transparent DVB ASI"},
    {10, 10, "Frame-Mapped IEEE 802.17 Resilient Packet Ring"},
    {11, 11, "Frame-Mapped Fibre Channel FC-BBW"},
    {12, 12, "Asycnchronous Transparent Fibre Channel"},
    {13, 13, "Frame-Mapped MPLS"},
    {14, 14, "Frame-Mapped MPLS (Multicast) [Deprecrated]"},
    {15, 15, "Frame-Mapped OSI network layer protocols (IS-IS, ES-IS, CLNP)"},
    {16, 16, "Frame-Mapped IPv4"},
    {17, 17, "Frame-Mapped IPv6"},
    {18, 18, "Frame-Mapped DVB-ASI"},
    {19, 19, "Frame-Mapped 64B/66B encoded Ethernet, including frame preamble"},
    {20, 20, "Frame-Mapped 64B/66B encoded Ethernet ordered set information"},
    {21, 21, "Transparent transcoded FC-1200"},
    /*UPI value 22 & 23 from Amendment 3 (01/2015)*/
    {22, 22, "Precision Time Protocol message"},
    {23, 23, "Synchronization status message"},
    {24, 239, "Reserved for future standardization"},
    {240, 252, "Reserved for proprietary use"},
    {253, 253, "Reserved for proprietary use, formerly Frame-Mapped 64B/66B encoded Ethernet, including frame preamble"},
    {254, 254, "Reserved for proprietary use, formerly Frame-Mapped 64B/66B encoded Ethernet ordered set information"},
    {255, 255, "Reserved and not available"},
    {0, 0, NULL }
};

static const range_string gfp_upi_management_rvals[] = {
    {0, 0, "Reserved and not available"},
    {1, 1, "Client Signal Fail (Loss of Client Signal)"},
    {2, 2, "Client Signal Fail (Loss of Character Synchronisation)"},
    {3, 3, "Defect Clear Indication (DCI)"},
    {4, 4, "Forward Defect Indication (FDI)"},
    {5, 5, "Reverse Defect Indication (RDI)"},
    {6, 223, "Reserved for future use"},
    {224, 254, "Reserved for proprietary use"},
    {255, 255, "Reserved and not available"},
    {0, 0, NULL}
};


/* Even GFP idle frames must have 4 bytes for the core header.
 * If data is received with fewer than this it is rejected. */
#define GFP_MIN_LENGTH 4

static void gfp_prompt(packet_info *pinfo, gchar* result)
{
    g_snprintf(result, MAX_DECODE_AS_PROMPT_LEN, "UPI %u as",
        GPOINTER_TO_UINT(p_get_proto_data(pinfo->pool, pinfo, proto_gfp, 0)));
}

static gpointer gfp_value(packet_info *pinfo)
{
    return p_get_proto_data(pinfo->pool, pinfo, proto_gfp, 0);
}

/* GFP has several identical 16 bit CRCs in its header (HECs). Note that
 * this function increases the offset. */
static void
gfp_add_hec_tree(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint *offset, const guint len, const int field, const int field_status, expert_field *ei_bad)
{

    guint hec_calc;

    hec_calc = crc16_r3_ccitt_tvb(tvb, *offset, len);
    *offset += len;

    proto_tree_add_checksum(tree, tvb, *offset, field, field_status, ei_bad, pinfo, hec_calc, ENC_BIG_ENDIAN, PROTO_CHECKSUM_VERIFY);
    *offset += 2;
}

/* G.7041 6.1.2 GFP payload area */
static void
dissect_gfp_payload(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, proto_tree *gfp_tree, guint *offset, guint payload_len)
{
    tvbuff_t *payload_tvb;
    proto_item *type_ti = NULL;
    proto_item *fcs_ti;
    proto_tree *fcs_tree = NULL;
    guint pti, pfi, exi, upi;
    guint fcs, fcs_calc;
    guint fcs_len = 0;

    /* G.7041 6.1.2.3 Payload area scrambling
     * Note that payload when sent on the wire is scrambled as per ATM
     * with a 1 + x^43 multiplicative scrambler. Likely already removed by
     * the time we get a capture file (as with ATM). Could have a pref,
     * but if it's present we have to save state over subsequent frames,
     * always would fail to decode the first 43 payload bytes of a capture. */

    /* G.7041 6.1.2.1 Payload Header - at least 4 bytes */
    tvb_ensure_bytes_exist(tvb, *offset, 4);
    payload_len -= 4;

    /* G.7041 6.1.2.1.1 GFP type field - mandatory 2 bytes */
    pti = tvb_get_bits8(tvb, 8*(*offset), 3);
    pfi = tvb_get_bits8(tvb, 8*(*offset)+3, 1);
    exi = tvb_get_bits8(tvb, 8*(*offset)+4, 4);
    upi = tvb_get_guint8(tvb, *offset+1);
    p_add_proto_data(pinfo->pool, pinfo, proto_gfp, 0, GUINT_TO_POINTER(upi));

    col_add_str(pinfo->cinfo, COL_INFO, val_to_str(pti, gfp_pti_vals, "Reserved PTI (%d)"));
    if (pti == GFP_USER_DATA ||
        pti == GFP_MANAGEMENT_COMMUNICATIONS) {
        /* G.7041 Table 6-3 - GFP_MANAGEMENT_COMMUNICATIONS
         * uses the same UPI table as USER_DATA, though
         * "not all of these UPI types are applicable" in that case. */
        type_ti = proto_tree_add_bitmask_with_flags(gfp_tree, tvb, *offset, hf_gfp_type,
            ett_gfp_type, gfp_type_data_fields, ENC_BIG_ENDIAN, BMT_NO_FLAGS);
        col_append_sep_str(pinfo->cinfo, COL_INFO, ": ", rval_to_str(upi, gfp_upi_data_rvals, "Unknown 0x%02x"));
    } else if (pti == GFP_CLIENT_MANAGEMENT) {
        /* G.7041 Table 6-4 */
        type_ti = proto_tree_add_bitmask_with_flags(gfp_tree, tvb, *offset, hf_gfp_type,
            ett_gfp_type, gfp_type_management_fields, ENC_BIG_ENDIAN, BMT_NO_FLAGS);
        col_append_sep_str(pinfo->cinfo, COL_INFO, ": ", rval_to_str(upi, gfp_upi_management_rvals, "Unknown 0x%02x"));
    }

    /* G.7041 6.1.2.1.2 Type HEC (tHEC) - mandatory 2 bytes */
    gfp_add_hec_tree(tvb, pinfo, gfp_tree, offset, 2, hf_gfp_thec, hf_gfp_thec_status, &ei_gfp_thec_bad);

    switch (exi) {
        case GFP_EXT_NULL:
            /* G.7041 6.1.2.1.3.1 Null extension header */
            break;

        case GFP_EXT_LINEAR:
            /* G.7041 6.1.2.1.3.2 Extension header for a linear frame */
            if (payload_len < 4) {
                expert_add_info(pinfo, type_ti, &ei_gfp_exi_short);
                payload_len = 0;
            }
            else {
                payload_len -= 4;
            }
            proto_tree_add_item(gfp_tree, hf_gfp_cid, tvb, *offset, 1, ENC_BIG_ENDIAN);
            /* Next byte spare field, reserved */

            /* 6.1.2.1.4 Extension HEC field */
            gfp_add_hec_tree(tvb, pinfo, gfp_tree, offset, 2, hf_gfp_ehec, hf_gfp_ehec_status, &ei_gfp_ehec_bad);
            break;
        case GFP_EXT_RING:
            /* 6.1.2.1.3.3 Extension header for a ring frame */
            /* "For further study." Undefined so fall through */
        default:
            /* Reserved */
            /* TODO: Mark as error / unhandled? */
            break;
    }

    proto_item_set_end(gfp_tree, tvb, *offset);

    if (pfi == 1) { /* 6.1.2.2.1 Payload FCS field present */
        if (payload_len < 4) {
            expert_add_info(pinfo, type_ti, &ei_gfp_pfi_short);
            fcs_len = payload_len;
            payload_len = 0;
        } else {
            fcs_len = 4;
            payload_len -= 4;
        }

        proto_tree_set_appendix(gfp_tree, tvb, *offset + payload_len, fcs_len);
        fcs = tvb_get_ntohl(tvb, *offset + payload_len);
        /* Same CRC32 as ATM */
        /* As with ATM, we can either compute the CRC as it would be
         * calculated and compare (last step involves taking the complement),
         * or we can include the passed CRC in the input and check to see
         * if the remainder is a known value. I like the first method
         * only because it lets us display what we should have received. */
        /* Method 1: */
        fcs_calc = crc32_mpeg2_tvb_offset(tvb, *offset, payload_len);
        if (fcs == ~fcs_calc) {
            fcs_ti = proto_tree_add_uint_format_value(gfp_tree, hf_gfp_fcs, tvb, *offset+payload_len, 4, fcs, "0x%08x [correct]", fcs);
            fcs_tree = proto_item_add_subtree(fcs_ti, ett_gfp_fcs);
            fcs_ti = proto_tree_add_boolean(fcs_tree, hf_gfp_fcs_good, tvb, *offset+payload_len, 4, TRUE);
            PROTO_ITEM_SET_GENERATED(fcs_ti);
            fcs_ti = proto_tree_add_boolean(fcs_tree, hf_gfp_fcs_bad, tvb, *offset+payload_len, 4, FALSE);
            PROTO_ITEM_SET_GENERATED(fcs_ti);
        } else {
            fcs_ti = proto_tree_add_uint_format_value(gfp_tree, hf_gfp_fcs, tvb, *offset+payload_len, 4, fcs, "0x%08x [incorrect, should be 0x%08x]", fcs, fcs_calc);
            fcs_tree = proto_item_add_subtree(fcs_ti, ett_gfp_fcs);
            fcs_ti = proto_tree_add_boolean(fcs_tree, hf_gfp_fcs_good, tvb, *offset+payload_len, 4, FALSE);
            PROTO_ITEM_SET_GENERATED(fcs_ti);
            fcs_ti = proto_tree_add_boolean(fcs_tree, hf_gfp_fcs_bad, tvb, *offset+payload_len, 4, TRUE);
            PROTO_ITEM_SET_GENERATED(fcs_ti);
            expert_add_info(pinfo, fcs_ti, &ei_gfp_fcs_bad);
        }
        /* Method 2: */
        /* fcs_calc = crc32_mpeg2_tvb_offset(tvb, *offset, payload_len+4);
        fcs_ti = proto_tree_add_uint(gfp_tree, hf_gfp_fcs, tvb, *offset+payload_len, 4, fcs);
        proto_item_append_text(fcs_ti, (fcs_calc == 0xC704DD7B) ? " [correct]" : " [incorrect]"); */
    }

    /* Some client frames we can do. Others are not implemented yet.
     * Transparent mode types are much trickier than frame-mapped,
     * since they requires reassembling streams across multiple GFP packets. */
    payload_tvb = tvb_new_subset_length(tvb, *offset, payload_len);
    switch (pti) {
        case GFP_USER_DATA:
        case GFP_MANAGEMENT_COMMUNICATIONS:
            if (!dissector_try_uint(gfp_dissector_table, upi, payload_tvb, pinfo, tree)) {
                expert_add_info_format(pinfo, type_ti, &ei_gfp_payload_undecoded, "Payload type 0x%02x (%s) unsupported", upi, rval_to_str_const(upi, gfp_upi_data_rvals, "UNKNOWN"));
                call_data_dissector(payload_tvb, pinfo, tree);
            }
            break;

        case GFP_CLIENT_MANAGEMENT:
            call_data_dissector(payload_tvb, pinfo, tree);
            break;

        default:
            break;
    }
    *offset += payload_len;
    *offset += fcs_len;
}

static int
dissect_gfp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
        void *data _U_)
{
    proto_item *ti, *pli_ti;
    proto_tree *gfp_tree;
    guint       offset = 0;
    int         len    = 0;
    guint       pli;

    /*** HEURISTICS ***/

    /* Check that the packet is long enough for it to belong to us. */
    if (tvb_reported_length(tvb) < GFP_MIN_LENGTH)
        return 0;

    /*** COLUMN DATA ***/

    /* Set the Protocol column to the constant string of GFP */
    col_set_str(pinfo->cinfo, COL_PROTOCOL, "GFP");

    col_clear(pinfo->cinfo, COL_INFO);
    /* Avoid asserts for leaving these blank. */
    col_set_str(pinfo->cinfo, COL_RES_DL_SRC, "N/A");
    col_set_str(pinfo->cinfo, COL_RES_DL_DST, "N/A");

    /*** PROTOCOL TREE ***/

    /* create display subtree for the protocol */
    ti = proto_tree_add_item(tree, proto_gfp, tvb, 0, GFP_MIN_LENGTH, ENC_NA);

    gfp_tree = proto_item_add_subtree(ti, ett_gfp);

    /* ITU-T G.7041 6.1.1 GFP core header */
    /* The core header could be scrambled (see G.7041 6.1.1.3) but isn't on
     * the GFP level capture files I've seen as it's removed before then.
     * If using this as a subdissector to a SDH or OTN dissector, that could
     * be an issue. TODO: Maybe add a pref for scrambling? */
    len = 2;
    pli_ti = proto_tree_add_item_ret_uint(gfp_tree, hf_gfp_pli, tvb,
        offset, len, ENC_BIG_ENDIAN, &pli);
    if (pli < 4) { /* Don't interpret as payload length */
        proto_item_append_text(pli_ti, " (%s)", rval_to_str_const(pli, gfp_pli_rvals, "Unknown"));
    }
    col_set_str(pinfo->cinfo, COL_INFO, rval_to_str_const(pli, gfp_pli_rvals, "Unknown"));

    /* 6.1.1.2 Core HEC field */
    gfp_add_hec_tree(tvb, pinfo, gfp_tree, &offset, len, hf_gfp_chec, hf_gfp_chec_status, &ei_gfp_chec_bad);

    if (pli == 0) { /* 6.2.1 GFP idle frames */
        if (tvb_reported_length_remaining(tvb, offset)) {
            expert_add_info(pinfo, pli_ti, &ei_gfp_pli_idle_nonempty);
        }
    } else if (pli < 4) { /* 6.2.2 Other control frames (reserved) */
        expert_add_info(pinfo, pli_ti, &ei_gfp_pli_unknown);
    } else {
        /* G.7041 6.1.2 GFP payload area */
        if (tvb_reported_length(tvb) < pli + offset) {
        /* avoid signed / unsigned comparison */
            proto_item_append_text(pli_ti, " (invalid, reported length is %u)", tvb_reported_length_remaining(tvb, offset));
            expert_add_info(pinfo, pli_ti, &ei_gfp_pli_invalid);
        }
        dissect_gfp_payload(tvb, pinfo, tree, gfp_tree, &offset, pli);
    }

    /* Return the amount of data this dissector was able to dissect */
    return offset;
}

void
proto_register_gfp(void)
{
    /* Setup list of header fields  See Section 1.5 of README.dissector for
     * details. */
    static hf_register_info hf[] = {
        { &hf_gfp_pli,
          { "Payload Length Indicator", "gfp.pli", FT_UINT16, BASE_DEC,
            NULL, 0x0, NULL, HFILL }
        },
        { &hf_gfp_chec,
          { "Core HEC", "gfp.chec", FT_UINT16, BASE_HEX,
            NULL, 0x0, NULL, HFILL }
        },
        { &hf_gfp_chec_status,
          { "cHEC Status", "gfp.chec.status", FT_UINT8, BASE_NONE, VALS(proto_checksum_vals), 0x0,
            NULL, HFILL }
        },
        { &hf_gfp_type,
          { "Type Field", "gfp.type", FT_UINT16, BASE_HEX, NULL, 0x0,
            NULL, HFILL }
        },
        { &hf_gfp_pti,
          { "PTI", "gfp.pti", FT_UINT16, BASE_HEX, VALS(gfp_pti_vals),
            0xE000, "Payload Type Identifier", HFILL }
        },
        { &hf_gfp_pfi,
          { "PFI", "gfp.pfi", FT_BOOLEAN, 16, TFS(&tfs_present_absent),
            0x1000, "Payload FCS Indicator", HFILL }
        },
        { &hf_gfp_exi,
          { "EXI", "gfp.exi", FT_UINT16, BASE_HEX, VALS(gfp_exi_vals),
            0x0F00, "Extension Header Identifier", HFILL }
        },
        { &hf_gfp_upi_data,
          { "UPI", "gfp.upi", FT_UINT16, BASE_HEX|BASE_RANGE_STRING,
            RVALS(gfp_upi_data_rvals),
            0xFF, "User Payload Identifier for Client Data Frame (or Management Communications Frame)", HFILL }
        },
        { &hf_gfp_upi_management,
          { "UPI", "gfp.upi", FT_UINT16, BASE_HEX|BASE_RANGE_STRING,
            RVALS(gfp_upi_management_rvals),
            0xFF, "User Payload Identifier for Client Management Frame", HFILL }
        },
        { &hf_gfp_thec,
          { "Type HEC", "gfp.thec", FT_UINT16, BASE_HEX, NULL, 0x0,
            NULL, HFILL }
        },
        { &hf_gfp_thec_status,
          { "tHEC Status", "gfp.thec.status", FT_UINT8, BASE_NONE, VALS(proto_checksum_vals), 0x0,
            NULL, HFILL }
        },
        { &hf_gfp_cid,
          { "Channel ID", "gfp.cid", FT_UINT8, BASE_HEX, NULL, 0x0,
            NULL, HFILL }
        },
        { &hf_gfp_ehec,
          { "Extension HEC", "gfp.ehec", FT_UINT16, BASE_HEX, NULL, 0x0,
            NULL, HFILL }
        },
        { &hf_gfp_ehec_status,
          { "eHEC Status", "gfp.ehec.status", FT_UINT8, BASE_NONE, VALS(proto_checksum_vals), 0x0,
            NULL, HFILL }
        },
        { &hf_gfp_fcs,
          { "Payload FCS", "gfp.fcs", FT_UINT32, BASE_HEX, NULL, 0x0,
            NULL, HFILL }
        },
        { &hf_gfp_fcs_good,
          { "Good FCS", "gfp.fcs_good", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
            "True: FCS matches payload; False: doesn't match", HFILL }
        },
        { &hf_gfp_fcs_bad,
          { "Bad eHEC", "gfp.fcs_bad", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
            "True: FCS doesn't match payload; False: matches", HFILL }
        }
    };

    /* Setup protocol subtree array */
    static gint *ett[] = {
        &ett_gfp,
        &ett_gfp_type,
        &ett_gfp_fcs
    };

    /* Setup protocol expert items */
    static ei_register_info ei[] = {
        { &ei_gfp_pli_idle_nonempty,
          { "gfp.pli.idle.nonempty", PI_MALFORMED, PI_ERROR,
            "Payload present on idle frame", EXPFILL }
        },
        { &ei_gfp_pli_unknown,
          { "gfp.pli.unknown", PI_UNDECODED, PI_WARN,
            "Unknown control frame type", EXPFILL }
        },
        { &ei_gfp_pli_invalid,
          { "gfp.pli.invalid", PI_MALFORMED, PI_WARN,
            "Bogus PLI does not match reported length", EXPFILL }
        },
        { &ei_gfp_chec_bad,
          { "gfp.chec.bad", PI_CHECKSUM, PI_WARN,
            "Bad cHEC", EXPFILL }
        },
        { &ei_gfp_thec_bad,
          { "gfp.thec.bad", PI_CHECKSUM, PI_WARN,
            "Bad tHEC", EXPFILL }
        },
        { &ei_gfp_ehec_bad,
          { "gfp.ehec.bad", PI_CHECKSUM, PI_WARN,
            "Bad eHEC", EXPFILL }
        },
        { &ei_gfp_exi_short,
          { "gfp.exi.missing", PI_MALFORMED, PI_ERROR,
            "EXI bit set but PLI too short for extension header", EXPFILL}
        },
        { &ei_gfp_pfi_short,
          { "gfp.pfi.missing", PI_MALFORMED, PI_ERROR,
            "PFI bit set but PLI too short for payload FCS", EXPFILL}
        },
        { &ei_gfp_payload_undecoded,
          { "gfp.payload.undecoded", PI_UNDECODED, PI_WARN,
            "Payload type not supported yet by the dissector", EXPFILL}
        },
        { &ei_gfp_fcs_bad,
          { "gfp.fcs.bad", PI_CHECKSUM, PI_WARN,
            "Bad FCS", EXPFILL }
        }
    };

    /* Decode As handling */
    static build_valid_func gfp_da_build_value[1] = {gfp_value};
    static decode_as_value_t gfp_da_values = {gfp_prompt, 1, gfp_da_build_value};
    static decode_as_t gfp_da = {"gfp", "GFP", "gfp.upi", 1, 0, &gfp_da_values, NULL, NULL,
                                 decode_as_default_populate_list, decode_as_default_reset, decode_as_default_change, NULL};

    /* module_t        *gfp_module; */
    expert_module_t *expert_gfp;

    /* Register the protocol name and description */
    proto_gfp = proto_register_protocol("Generic Framing Procedure",
            "GFP", "gfp");

    /* Required function calls to register the header fields and subtrees */
    proto_register_field_array(proto_gfp, hf, array_length(hf));
    proto_register_subtree_array(ett, array_length(ett));

    /* Required function calls to register expert items */
    expert_gfp = expert_register_protocol(proto_gfp);
    expert_register_field_array(expert_gfp, ei, array_length(ei));

    /* Subdissectors for payload */
    gfp_dissector_table = register_dissector_table("gfp.upi", "GFP UPI (for Client Data frames)",
                                                   proto_gfp, FT_UINT8, BASE_DEC);

    /* Don't register a preferences module yet since there are no prefs in
     * order to avoid a warning. (See section 2.6 of README.dissector
     * for more details on preferences). */
    /*gfp_module = prefs_register_protocol(proto_gfp, NULL);*/

    register_decode_as(&gfp_da);
}

/* If this function is registered as a prefs callback (see
 * prefs_register_protocol above) this function is also called by Wireshark's
 * preferences manager whenever "Apply" or "OK" are pressed. In that case, it
 * should accommodate being called more than once by use of the static
 * 'initialized' variable included below.
 *
 * This form of the reg_handoff function is used if if you perform registration
 * functions which are dependent upon prefs.
 */
void
proto_reg_handoff_gfp(void)
{
    static dissector_handle_t gfp_handle;

    gfp_handle = create_dissector_handle(dissect_gfp,
            proto_gfp);

    dissector_add_uint("wtap_encap", WTAP_ENCAP_GFP_T, gfp_handle);
    dissector_add_uint("wtap_encap", WTAP_ENCAP_GFP_F, gfp_handle);

    /* Add a few of the easiest UPIs to decode. There's more that probably
     * would work, but are untested (frame mapped DVB, frame mapped Fibre
     * Channel). The transparent mode ones are trickier, since without a
     * one-to-one mapping of frames, we would have to reassemble payload
     * packets across multiple GFP packets.
     *
     * Section 7.1.1 "Ethernet MAC encapsulation" of G.7041 says
     * "The Ethernet MAC octets from destination address through
     * "frame check sequence, inclusive, are placed in the GFP payload
     * "information field.", so we want the dissector for Ethernet
     * frames including the FCS. */
    dissector_add_uint("gfp.upi", 1, find_dissector("eth_withfcs"));
    dissector_add_uint("gfp.upi", 2, find_dissector("ppp_hdlc"));
    dissector_add_uint("gfp.upi", 12, find_dissector("mpls"));
    dissector_add_uint("gfp.upi", 13, find_dissector("mpls"));
    dissector_add_uint("gfp.upi", 16, find_dissector("ip"));
    dissector_add_uint("gfp.upi", 17, find_dissector("ipv6"));
}

/*
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
 *
 * Local variables:
 * c-basic-offset: 4
 * tab-width: 8
 * indent-tabs-mode: nil
 * End:
 *
 * vi: set shiftwidth=4 tabstop=8 expandtab:
 * :indentSize=4:tabSize=8:noTabs=true:
 */