aboutsummaryrefslogtreecommitdiffstats
path: root/epan/dissectors/packet-grebonding.c
blob: 46f6f9938edefb2eacdf486f8530b41c9cc7bad7 (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
/* packet-grebonding.c
 * Routines for Huawei's GRE bonding control (RFC8157) dissection
 * Thomas Vogt
 *
 * 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.
 */

#include "config.h"

#include <epan/packet.h>
#include <epan/capture_dissectors.h>
#include <epan/expert.h>
#include <epan/to_str.h>

void proto_reg_handoff_greb(void);
void proto_register_greb(void);

static int proto_greb = -1;

static int hf_greb_message_type = -1;
static int hf_greb_tunnel_type = -1;

static int hf_greb_attr = -1;
static int hf_greb_attr_type = -1;
static int hf_greb_attr_length = -1;
static int hf_greb_attr_val_uint64 = -1;
static int hf_greb_attr_val_none = -1;
static int hf_greb_attr_val_ipv6 = -1;
static int hf_greb_attr_val_ipv4 = -1;
static int hf_greb_attr_val_time = -1;
static int hf_greb_attr_val_string = -1;
static int hf_greb_attr_DSL_prot = -1;
static int hf_greb_attr_dt_bras_name = -1;

static int hf_greb_attr_filter_commit = -1;
static int hf_greb_attr_filter_ack = -1;
static int hf_greb_attr_filter_packetsum = -1;
static int hf_greb_attr_filter_packetid = -1;
static int hf_greb_attr_filter_item_type = -1;
static int hf_greb_attr_filter_item_length = -1;
static int hf_greb_attr_filter_item_enabled = -1;
static int hf_greb_attr_filter_item_desc_length = -1;
static int hf_greb_attr_filter_item_desc_val = -1;
static int hf_greb_attr_filter_item_val = -1;

static int hf_greb_attr_error = -1;

/* Initialize the subtree pointers */
static gint ett_grebonding = -1;
static gint ett_grebonding_attrb = -1;
static gint ett_grebonding_filter_list = -1;
static gint ett_grebonding_filter_item = -1;
static gint ett_grebonding_ipv6_prefix = -1;

static gint *ett[] = {
    &ett_grebonding,
    &ett_grebonding_attrb,
    &ett_grebonding_filter_list,
    &ett_grebonding_filter_item,
    &ett_grebonding_ipv6_prefix
};


static const value_string greb_message_types[] = {
#define GREB_TUNNEL_SETUP_REQ 1
    {GREB_TUNNEL_SETUP_REQ, "Tunnel setup request"},
#define GREB_TUNNEL_SETUP_ACK 2
    {GREB_TUNNEL_SETUP_ACK, "Tunnel setup accept"},
#define GREB_TUNNEL_SETUP_DENY 3
    {GREB_TUNNEL_SETUP_DENY, "Tunnel setup deny"},
#define GREB_HELLO 4
    {GREB_HELLO, "Hello"},
#define GREB_TUNNEL_TEAR_DOWN 5
    {GREB_TUNNEL_TEAR_DOWN, "Tunnel tear down"},
#define GREB_NOTIFY 6
    {GREB_NOTIFY, "Notify"},
#define GREB_LINK_DETECTION 10
    {GREB_LINK_DETECTION, "Link Detection (Telekom specific)"},
    {0, NULL}
};

static const value_string greb_tunnel_types[] = {
#define GREB_TUNNEL_LTE 0b0000
    {GREB_TUNNEL_LTE, "LTE-GRE tunnel (Telekom specific)"},
#define GREB_TUNNEL_FIRST 0b0001
    {GREB_TUNNEL_FIRST, "first tunnel (most likely the DSL GRE tunnel)"},
#define GREB_TUNNEL_SECOND 0b0010
    {GREB_TUNNEL_SECOND, "second tunnel (most likely the LTE GRE tunnel)" },
#define GREB_TUNNEL_DSL 0b1000
    {GREB_TUNNEL_DSL, "DSL Tunnel (Telekom specific)" },
    {0, NULL}
};

static const value_string greb_error_codes[] = {
#define GREB_ERROR_HAAP_UNREACHABLE_LTE 1
    {GREB_ERROR_HAAP_UNREACHABLE_LTE, "HAAP not reachable over LTE"},
#define GREB_ERROR_HAAP_UNREACHABLE_DSL 2
    {GREB_ERROR_HAAP_UNREACHABLE_DSL, "HAAP not reachable via DSL"},
#define GREB_ERROR_LTE_TUNNEL_FAILED 3
    {GREB_ERROR_LTE_TUNNEL_FAILED, "LTE tunnel failed"},
#define GREB_ERROR_DSL_TUNNEL_FAILED 4
    {GREB_ERROR_DSL_TUNNEL_FAILED, "DSL tunnel failed"},
#define GREB_ERROR_DSL_UID_NOT_ALLOWED 5
    {GREB_ERROR_DSL_UID_NOT_ALLOWED, "DSL UID not allowed"},
#define GREB_ERROR_UID_NOT_ALLOWED 6
    {GREB_ERROR_UID_NOT_ALLOWED, "UID not allowed"},
#define GREB_ERROR_UID_NOT_MATCHING 7
    {GREB_ERROR_UID_NOT_MATCHING, "LTE and DSL User IDs do not match"},
#define GREB_ERROR_SECOND_SESSION_WITH_UID 8
    {GREB_ERROR_SECOND_SESSION_WITH_UID, "Session with the same User ID already exists"},
#define GREB_ERROR_CIN_NOT_PERMITTED 9
    {GREB_ERROR_CIN_NOT_PERMITTED, "Denied: CIN not permitted"},
#define GREB_ERROR_MAINTENANCE 10
    {GREB_ERROR_MAINTENANCE, "Terminated for maintenance"},
#define GREB_ERROR_BACKEND_COMMUNICATION_FAILURE_LTE 11
    {GREB_ERROR_BACKEND_COMMUNICATION_FAILURE_LTE, "HAAP Backend failure on LTE tunnel establishment"},
#define GREB_ERROR_BACKEND_COMMUNICATION_FAILURE_DSL 12
    {GREB_ERROR_BACKEND_COMMUNICATION_FAILURE_DSL, "HAAP Backend failure on DSL tunnel establishment"},
#define GREB_ERROR_DT_HAAP_UNREACHABLE_DSL 401
    {GREB_ERROR_DT_HAAP_UNREACHABLE_DSL, "DSL GRE tunnel to the HAAP failed (Telekom specific)"},
#define GREB_ERROR_DT_HAAP_UNREACHABLE_LTE 402
    {GREB_ERROR_DT_HAAP_UNREACHABLE_LTE, "LTE GRE tunnel to the HAAP failed (Telekom specific)"},
#define GREB_ERROR_DT_UID_NOT_MATCHING 403
    {GREB_ERROR_DT_UID_NOT_MATCHING, "Mismatch of LTE and DSL User IDs (Telekom specific)"},
#define GREB_ERROR_DT_SECOND_SESSION_WITH_UID 404
    {GREB_ERROR_DT_SECOND_SESSION_WITH_UID, "Session with the same User ID already exists (Telekom specific)"},
#define GREB_ERROR_DT_CIN_NOT_PERMITTED 405
    {GREB_ERROR_DT_CIN_NOT_PERMITTED, "Client uses a not permitted CIN (Telekom specific)"},
#define GREB_ERROR_DT_DSL_TUNNEL_FAILED 406
    {GREB_ERROR_DT_DSL_TUNNEL_FAILED, "Communication error during the DSL Tunnel setup (Telekom specific)"},
#define GREB_ERROR_DT_LTE_TUNNEL_FAILED 407
    {GREB_ERROR_DT_LTE_TUNNEL_FAILED, "Communication error during the LTE Tunnel setup (Telekom specific)"},
#define GREB_ERROR_DT_MAINTENANCE 501
    {GREB_ERROR_DT_MAINTENANCE, "Terminated for maintenance (Telekom specific)"},
#define GREB_ERROR_DT_LTE_PARAM_UPDATE 502
    {GREB_ERROR_DT_LTE_PARAM_UPDATE, "LTE terminated to update parameters (Telekom specific)"},
#define GREB_ERROR_DT_DSL_PARAM_UPDATE 503
    {GREB_ERROR_DT_DSL_PARAM_UPDATE, "DSL terminated to update parameters (Telekom specific)"},
    {0,NULL}
};

static const value_string greb_attribute_types[] = {
#define GREB_ATTRB_H_IP4_ADDR 1
    {GREB_ATTRB_H_IP4_ADDR, "H IPv4 address"},
#define GREB_ATTRB_H_IP6_ADDR 2
    {GREB_ATTRB_H_IP6_ADDR, "H IPv6 address"},
#define GREB_ATTRB_CIN 3
    {GREB_ATTRB_CIN, "CIN (Client ID)"},
#define GREB_ATTRB_SESSIONID 4
    {GREB_ATTRB_SESSIONID, "Session ID"},
#define GREB_ATTRB_TIME 5
    {GREB_ATTRB_TIME, "Time"},
#define GREB_ATTRB_BYPASS_RATE 6
    {GREB_ATTRB_BYPASS_RATE, "Bypass rate"},
#define GREB_ATTRB_DOWNSTREAM_RATE 7
    {GREB_ATTRB_DOWNSTREAM_RATE, "Downstream rate"},
#define GREB_ATTRB_FILTER_LIST 8
    {GREB_ATTRB_FILTER_LIST, "Filter list"},
#define GREB_ATTRB_RTT_THRESHOLD 9
    {GREB_ATTRB_RTT_THRESHOLD, "RTT threshold"},
#define GREB_ATTRB_BYPASS_INTERVAL 10
    {GREB_ATTRB_BYPASS_INTERVAL, "Bypass interval"},
#define GREB_ATTRB_ONLY_FIRST_TUNNEL 11
    {GREB_ATTRB_ONLY_FIRST_TUNNEL, "only first tunnel (DSL)"},
#define GREB_ATTRB_OVERFLOW_TO_SECOND 12
    {GREB_ATTRB_OVERFLOW_TO_SECOND, "overflow to second tunnel (LTE)"},
#define GREB_ATTRB_IPV6_PREFIX 13
    {GREB_ATTRB_IPV6_PREFIX, "IPv6 prefix assigned by HAAP"},
#define GREB_ATTRB_ACTIVE_HELLO_INTERVAL 14
    {GREB_ATTRB_ACTIVE_HELLO_INTERVAL, "Active hello interval"},
#define GREB_ATTRB_HELLO_RETRYS 15
    {GREB_ATTRB_HELLO_RETRYS, "Hello retries"},
#define GREB_ATTRB_IDLE_TIMEOUT 16
    {GREB_ATTRB_IDLE_TIMEOUT, "Idle timeout"},
#define GREB_ATTRB_ERROR 17
    {GREB_ATTRB_ERROR, "Error"},
#define GREB_ATTRB_DSL_FAIL 18
    {GREB_ATTRB_DSL_FAIL, "DSL fail"},
#define GREB_ATTRB_LTE_FAIL 19
    {GREB_ATTRB_LTE_FAIL, "LTE fail"},
#define GREB_ATTRB_BONDING_KEY 20
    {GREB_ATTRB_BONDING_KEY, "Bonding key"},
#define GREB_ATTRB_IPV6_PREFIX2 21
    {GREB_ATTRB_IPV6_PREFIX2, "IPv6 prefix assigned to host"},
#define GREB_ATTRB_CONFIGURED_UPSTREAM 22
    {GREB_ATTRB_CONFIGURED_UPSTREAM, "Configured upstream"},
#define GREB_ATTRB_CONFIGURED_DOWNSTREAM 23
    {GREB_ATTRB_CONFIGURED_DOWNSTREAM, "Configured downstream"},
#define GREB_ATTRB_RTT_VIOLATION 24
    {GREB_ATTRB_RTT_VIOLATION, "RTT violation"},
#define GREB_ATTRB_RTT_COMPLIANCE 25
    {GREB_ATTRB_RTT_COMPLIANCE, "RTT compliance"},
#define GREB_ATTRB_DIAG_START_BONDING 26
    {GREB_ATTRB_DIAG_START_BONDING, "Diagnostic start bonding"},
#define GREB_ATTRB_DIAG_START_DSL 27
    {GREB_ATTRB_DIAG_START_DSL, "Diagnostic start DSL"},
#define GREB_ATTRB_DIAG_END 29
    {GREB_ATTRB_DIAG_END, "Diagnostic End"},
#define GREB_ATTRB_FILTER_LIST_ACK 30
    {GREB_ATTRB_FILTER_LIST_ACK, "Filter list ACK"},
#define GREB_ATTRB_IDLE_HELLO_INTERVAL 31
    {GREB_ATTRB_IDLE_HELLO_INTERVAL, "Idle hello interval"},
#define GREB_ATTRB_NO_TRAFFIC_INTERVAL 32
    {GREB_ATTRB_NO_TRAFFIC_INTERVAL, "No traffic interval"},
#define GREB_ATTRB_ACTIVE_HELLO_STATE 33
    {GREB_ATTRB_ACTIVE_HELLO_STATE, "Active hello state"},
#define GREB_ATTRB_IDLE_HELLO_STATE 34
    {GREB_ATTRB_IDLE_HELLO_STATE, "Idle hello state"},
#define GREB_ATTRB_TUNNEL_VERIFICATION 35
    {GREB_ATTRB_TUNNEL_VERIFICATION, "Tunnel verification"},
#define GREB_ATTRB_DT_DSL_PROT 53
    {GREB_ATTRB_DT_DSL_PROT, "DSL protocol / link type (Telekom specific)"},
#define GREB_ATTRB_DT_BRAS_NAME 54
    {GREB_ATTRB_DT_BRAS_NAME, "Broadband Remote Access Server name (Telekom specific)"},
#define GREB_ATTRB_DT_DOWN_REORDER_TIME 56
    {GREB_ATTRB_DT_DOWN_REORDER_TIME, "Max. downstream reordering buffer time (Telekom specific)"},
#define GREB_ATTRB_DT_COM_UP_BURST_TIME 57
    {GREB_ATTRB_DT_COM_UP_BURST_TIME, "Committed upstream burst time (Telekom specific)"},
#define GREB_ATTRB_DT_UPSTREAM_RATE 59
    {GREB_ATTRB_DT_UPSTREAM_RATE, "DSL synchronization Rate upstream (Telekom specific)"},
    {255, "FIN"},
    {0, NULL}
};

static const value_string greb_DT_DSL_prots[] = {
#define DT_UNDEF 0
    {DT_UNDEF, "Undefined"},
#define DT_ADSL_B 1
    {DT_ADSL_B, "ADSL/ADSL2/ADSL2+ Annex B"},
#define DT_ADSL_J 2
    {DT_ADSL_J, "ADSL2+ Annex J"},
#define DT_VDSL 3
    {DT_VDSL, "VDSL2"},
#define DT_VDSL_VEC 4
    {DT_VDSL_VEC, "VDSL2 Vectoring"},
    {0, NULL}
};


static const value_string greb_filter_types[] = {
#define GREB_ATTRB_FILTER_FQDN 1
    {GREB_ATTRB_FILTER_FQDN, "FQDN"},
#define GREB_ATTRB_FILTER_DSCP 2
    {GREB_ATTRB_FILTER_DSCP, "DSCP"},
#define GREB_ATTRB_FILTER_DPORT 3
    {GREB_ATTRB_FILTER_DPORT, "Destination Port"},
#define GREB_ATTRB_FILTER_DIP 4
    {GREB_ATTRB_FILTER_DIP, "Destination IP"},
#define GREB_ATTRB_FILTER_DIPPORT 5
    {GREB_ATTRB_FILTER_DIPPORT, "Destination IP&Port"},
#define GREB_ATTRB_FILTER_SPORT 6
    {GREB_ATTRB_FILTER_SPORT, "Source Port"},
#define GREB_ATTRB_FILTER_SIP 7
    {GREB_ATTRB_FILTER_SIP, "Source IP"},
#define GREB_ATTRB_FILTER_SIPPORT 8
    {GREB_ATTRB_FILTER_SIPPORT, "Source IP&Port"},
#define GREB_ATTRB_FILTER_SMAC 9
    {GREB_ATTRB_FILTER_SMAC, "Source Mac"},
#define GREB_ATTRB_FILTER_PROTO 10
    {GREB_ATTRB_FILTER_PROTO, "Protocol"},
#define GREB_ATTRB_FILTER_SIPR 11
    {GREB_ATTRB_FILTER_SIPR, "Source IP Range"},
#define GREB_ATTRB_FILTER_DIPR 12
    {GREB_ATTRB_FILTER_DIPR, "Destination IP Range"},
#define GREB_ATTRB_FILTER_SIPRPORT 13
    {GREB_ATTRB_FILTER_SIPRPORT, "Source IP Range&Port"},
#define GREB_ATTRB_FILTER_DIPRPORT 14
    {GREB_ATTRB_FILTER_DIPRPORT, "Destination IP Range&Port"},
#define GREB_ATTRB_DT_COMBO 15
    {GREB_ATTRB_DT_COMBO, "Combination (Telekom specific)"},
    {0, NULL}
};

static const value_string greb_filter_ack_codes[] = {
#define GREB_ATTRB_FILTER_ACK 0
    {GREB_ATTRB_FILTER_ACK, "Filter list acknowledged"},
#define GREB_ATTRB_FILTER_NACK_NO_OLD 1
    {GREB_ATTRB_FILTER_NACK_NO_OLD, "Filter list not acknowledged. No previous filter list to use."},
#define GREB_ATTRB_FILTER_NACK_OLD_USED 2
    {GREB_ATTRB_FILTER_NACK_OLD_USED, "Filter list not acknowledged. Previous filter list will be used."},
    {0, NULL}
};

static void
dissect_greb_h_gateway_ip_address(tvbuff_t *tvb, proto_tree *attrb_tree, guint offset, guint attrb_length)
{
    if (attrb_length == 16)
        proto_tree_add_item(attrb_tree, hf_greb_attr_val_ipv6, tvb, offset, attrb_length, ENC_NA);
    else if (attrb_length == 4)
        proto_tree_add_item(attrb_tree, hf_greb_attr_val_ipv4, tvb, offset, attrb_length, ENC_BIG_ENDIAN);
    else
        proto_tree_add_item(attrb_tree, hf_greb_attr_val_uint64, tvb, offset, attrb_length, ENC_BIG_ENDIAN);
}

static void
dissect_greb_filter_list_ack(tvbuff_t *tvb, proto_tree *attrb_tree, guint offset, guint attrb_length)
{
    proto_item *it_filter;
    proto_tree *filter_tree;
    guint filter_commit_count = tvb_get_guint32(tvb, offset, ENC_BIG_ENDIAN);

    it_filter = proto_tree_add_none_format(attrb_tree, hf_greb_attr_val_none, tvb, offset, attrb_length,
        "Filter list ACK - Commit %d", filter_commit_count);
    filter_tree = proto_item_add_subtree(it_filter, ett_grebonding_filter_list);
    proto_tree_add_item(filter_tree, hf_greb_attr_filter_commit, tvb, offset, attrb_length - 1, ENC_BIG_ENDIAN);
    proto_tree_add_item(filter_tree, hf_greb_attr_filter_ack, tvb, offset + attrb_length, 1, ENC_BIG_ENDIAN);
}


static void
dissect_greb_filter_list(tvbuff_t *tvb, proto_tree *attrb_tree, guint offset, guint attrb_length)
{
    proto_item *it_filter;
    proto_tree *filter_tree;
    guint filter_commit_count = tvb_get_guint32(tvb, offset, ENC_BIG_ENDIAN);
    guint filter_packet_sum = tvb_get_guint16(tvb, offset + 4, ENC_BIG_ENDIAN);
    guint filter_packet_id = tvb_get_guint16(tvb, offset + 6, ENC_BIG_ENDIAN);
    it_filter = proto_tree_add_none_format(attrb_tree, hf_greb_attr_val_none, tvb, offset, attrb_length,
        "Filter list - Commit %d, Packet %d/%d", filter_commit_count, filter_packet_id, filter_packet_sum);
    filter_tree = proto_item_add_subtree(it_filter, ett_grebonding_filter_list);
    proto_tree_add_item(filter_tree, hf_greb_attr_filter_commit, tvb, offset, 4, ENC_BIG_ENDIAN);
    proto_tree_add_item(filter_tree, hf_greb_attr_filter_packetid, tvb, offset + 6, 2, ENC_BIG_ENDIAN);
    proto_tree_add_item(filter_tree, hf_greb_attr_filter_packetsum, tvb, offset + 4, 2, ENC_BIG_ENDIAN);

    offset += 8;

    while (offset < attrb_length) {
        proto_item *it_filter_item;
        proto_tree *filter_item_tree;
        guint filter_item_length = tvb_get_guint16(tvb, offset + 2, ENC_BIG_ENDIAN);
        guint filter_item_desc_length = tvb_get_guint16(tvb, offset + 6, ENC_BIG_ENDIAN);
        // bound lengths to not exceed packet
        if (filter_item_length > (guint) tvb_reported_length_remaining(tvb, offset + 2))
            filter_item_length = tvb_reported_length_remaining(tvb, offset + 2);
        if (filter_item_desc_length > filter_item_length)
            filter_item_length = filter_item_desc_length;

        it_filter_item = proto_tree_add_none_format(filter_tree, hf_greb_attr_val_none, tvb, offset,
            filter_item_length + 4, "Filter item - %s", tvb_get_string_enc(wmem_packet_scope(), tvb, offset + 8,
            filter_item_desc_length, ENC_UTF_8));
        filter_item_tree = proto_item_add_subtree(it_filter_item, ett_grebonding_filter_item);
        proto_tree_add_item(filter_item_tree, hf_greb_attr_filter_item_type, tvb, offset, 2, ENC_BIG_ENDIAN);
        proto_tree_add_item(filter_item_tree, hf_greb_attr_filter_item_length, tvb, offset + 2, 2, ENC_BIG_ENDIAN);
        proto_tree_add_item(filter_item_tree, hf_greb_attr_filter_item_enabled, tvb, offset + 4, 2, ENC_BIG_ENDIAN);
        proto_tree_add_item(filter_item_tree, hf_greb_attr_filter_item_desc_length, tvb, offset + 6, 2, ENC_BIG_ENDIAN);
        proto_tree_add_item(filter_item_tree, hf_greb_attr_filter_item_desc_val, tvb, offset + 8,
            filter_item_desc_length, ENC_UTF_8 | ENC_NA);
        proto_tree_add_item(filter_item_tree, hf_greb_attr_filter_item_val, tvb, offset + 8 + filter_item_desc_length,
            filter_item_length - 4 - filter_item_desc_length, ENC_UTF_8 | ENC_NA);

        offset += filter_item_length + 4;
    }

}

static void
dissect_greb_ipv6_prefix(packet_info *pinfo, tvbuff_t *tvb, proto_tree *attrb_tree, guint offset, guint attrb_length)
{
    proto_item *item_ipv6_prefix;
    proto_tree *ipv6_prefix_tree;
    guint addr_length = attrb_length - 1;

    ipv6_prefix_tree = proto_tree_add_subtree_format(attrb_tree, tvb, offset, attrb_length,
        ett_grebonding_ipv6_prefix, &item_ipv6_prefix, "IPv6 prefix - %s/%d",
        tvb_ip6_to_str(pinfo->pool, tvb, offset), tvb_get_guint8(tvb, offset + addr_length));
    proto_tree_add_item(ipv6_prefix_tree, hf_greb_attr_val_ipv6, tvb, offset, addr_length, ENC_NA);
    proto_tree_add_item(ipv6_prefix_tree, hf_greb_attr_val_uint64, tvb, offset + addr_length, 1, ENC_BIG_ENDIAN);
}

static int
dissect_greb(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
{
    proto_item *ti, *it_attrb;
    proto_tree *greb_tree, *attrb_tree = NULL;
    guint offset = 0;
    guint message_type = tvb_get_guint8(tvb, offset) >> 4;

    col_set_str(pinfo->cinfo, COL_PROTOCOL, "GREbond");
    ti = proto_tree_add_protocol_format(tree, proto_greb, tvb, offset, -1, "Huawei GRE bonding control message (%s)",
        val_to_str(message_type, greb_message_types, "0x%01X (unknown)"));
    col_add_str(pinfo->cinfo, COL_INFO, val_to_str(message_type, greb_message_types, "0x%02X (unknown)"));

    greb_tree = proto_item_add_subtree(ti, ett_grebonding);
    proto_tree_add_item(greb_tree, hf_greb_message_type, tvb, offset, 1, ENC_BIG_ENDIAN);
    proto_tree_add_item(greb_tree, hf_greb_tunnel_type, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset++;

    // going through the attributes, off by one to assure length field exists
    while (offset + 1 < tvb_captured_length(tvb)) {
        guint attrb_type = tvb_get_guint8(tvb, offset);
        guint attrb_length = tvb_get_guint16(tvb, offset + 1, ENC_BIG_ENDIAN);

        it_attrb = proto_tree_add_none_format(greb_tree, hf_greb_attr, tvb, offset, attrb_length + 3, "Attribute - %s",
            val_to_str(attrb_type, greb_attribute_types, "unknown (%d)"));

        attrb_tree = proto_item_add_subtree(it_attrb, ett_grebonding_attrb);
        proto_tree_add_item(attrb_tree, hf_greb_attr_type, tvb, offset, 1, ENC_BIG_ENDIAN);
        proto_tree_add_item(attrb_tree, hf_greb_attr_length, tvb, offset + 1, 2, ENC_BIG_ENDIAN);
        offset += 3;

        // bound attrb_length to not exced packet
        if (attrb_length > (guint) tvb_reported_length_remaining(tvb, offset))
            attrb_length = tvb_reported_length_remaining(tvb, offset);

        if (attrb_length > 0) {
            switch (attrb_type) {
                case GREB_ATTRB_H_IP4_ADDR:
                case GREB_ATTRB_H_IP6_ADDR:
                    dissect_greb_h_gateway_ip_address(tvb, attrb_tree, offset, attrb_length);
                    break;

                case GREB_ATTRB_IPV6_PREFIX2:
                case GREB_ATTRB_IPV6_PREFIX:
                    dissect_greb_ipv6_prefix(pinfo, tvb, attrb_tree, offset, attrb_length);
                    break;

                case GREB_ATTRB_TIME:
                    proto_tree_add_item(attrb_tree, hf_greb_attr_val_time, tvb, offset, attrb_length,
                        ENC_TIME_TIMEVAL);
                    break;

                case GREB_ATTRB_FILTER_LIST:
                    dissect_greb_filter_list(tvb, attrb_tree, offset, attrb_length);
                    break;

                case GREB_ATTRB_FILTER_LIST_ACK:
                    dissect_greb_filter_list_ack(tvb, attrb_tree, offset, attrb_length);
                    break;

                case GREB_ATTRB_CIN:
                    proto_tree_add_item(attrb_tree, hf_greb_attr_val_string, tvb, offset, attrb_length,
                        ENC_UTF_8 | ENC_NA);
                    break;

                case GREB_ATTRB_DT_BRAS_NAME:
                    proto_tree_add_item(attrb_tree, hf_greb_attr_dt_bras_name, tvb, offset, attrb_length,
                        ENC_UTF_8 | ENC_NA);
                    break;

                case GREB_ATTRB_ERROR:
                    proto_tree_add_item(attrb_tree, hf_greb_attr_error, tvb, offset, attrb_length, ENC_BIG_ENDIAN);
                    break;

                case GREB_ATTRB_DT_DSL_PROT:
                    proto_tree_add_item(attrb_tree, hf_greb_attr_DSL_prot, tvb, offset, attrb_length, ENC_BIG_ENDIAN);
                    break;

                default:
                    proto_tree_add_item(attrb_tree, hf_greb_attr_val_uint64, tvb, offset, attrb_length,
                        ENC_BIG_ENDIAN);
                    break;
            }

            offset += attrb_length;
        }
    }

    return tvb_captured_length(tvb);
}

void
proto_register_greb(void)
{
    static hf_register_info hf[] = {
        { &hf_greb_message_type,
            { "Message type", "grebonding.type", FT_UINT8, BASE_DEC, VALS(greb_message_types), 0xF0, "", HFILL }
        },
        { &hf_greb_tunnel_type,
            { "Tunnel type", "grebonding.tunneltype", FT_UINT8, BASE_DEC, VALS(greb_tunnel_types), 0x0F, "", HFILL }
        },
        { &hf_greb_attr,
            { "Attribute", "grebonding.attr", FT_NONE, BASE_NONE, NULL, 0, "", HFILL }
        },
        { &hf_greb_attr_length,
            { "Attribute length", "grebonding.attr.length", FT_UINT16, BASE_DEC, NULL, 0, "", HFILL }
        },
        { &hf_greb_attr_type,
            { "Attribute type", "grebonding.attr.type", FT_UINT8, BASE_DEC, VALS(greb_attribute_types), 0, "", HFILL }
        },
        { &hf_greb_attr_val_uint64,
            { "Attribute value", "grebonding.attr.val.uint64",
                FT_UINT64, BASE_DEC, NULL, 0, "", HFILL }
        },
        { &hf_greb_attr_val_time,
            { "Attribute value", "grebonding.attr.val.time",
                FT_RELATIVE_TIME, BASE_NONE, NULL, 0, "", HFILL }
        },
        { &hf_greb_attr_val_string,
            { "Attribute value", "grebonding.attr.val.string",
                FT_STRING, BASE_NONE, NULL, 0, "", HFILL }
        },
        { &hf_greb_attr_val_none,
            { "Attribute value", "grebonding.attr.val",
                FT_NONE, BASE_NONE, NULL, 0, "", HFILL }
        },
        { &hf_greb_attr_val_ipv6,
            { "Attribute value", "grebonding.attr.val.ipv6",
                FT_IPv6, BASE_NONE, NULL, 0, NULL, HFILL }
        },
        { &hf_greb_attr_val_ipv4,
            { "Attribute value", "grebonding.attr.val.ipv4",
                FT_IPv4, BASE_NONE, NULL, 0, NULL, HFILL }
        },
        { &hf_greb_attr_filter_commit,
            { "Commit", "grebonding.attr.val.filter.commit", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL }
        },
        { &hf_greb_attr_filter_ack,
            { "Ack", "grebonding.attr.val.filter.ack",
                FT_UINT8, BASE_DEC, VALS(greb_filter_ack_codes), 0, NULL, HFILL }
        },
        { &hf_greb_attr_DSL_prot,
            { "DSL Protocol", "grebonding.attr.val.dslproto",
                FT_UINT8, BASE_DEC, VALS(greb_DT_DSL_prots), 0, NULL, HFILL }
        },
        { &hf_greb_attr_dt_bras_name,
            { "Broadband Remote Access Server (BRAS) name", "grebonding.attr.val.bras_name",
                FT_STRING, BASE_NONE, NULL, 0, "", HFILL }
        },
        { &hf_greb_attr_filter_packetsum,
            { "Packet sum", "grebonding.attr.val.filter.packetsum", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }
        },
        { &hf_greb_attr_filter_packetid,
            { "Packet ID", "grebonding.attr.val.filter.packetid", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }
        },
        { &hf_greb_attr_filter_item_enabled,
            { "Enabled", "grebonding.attr.val.filter.item.enabled", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }
        },
        { &hf_greb_attr_filter_item_length,
            { "Length (excl. type and length)", "grebonding.attr.val.filter.item.length",
                FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }
        },
        { &hf_greb_attr_filter_item_type,
            { "Type", "grebonding.attr.val.filter.item.type",
                FT_UINT16, BASE_DEC, VALS(greb_filter_types), 0, NULL, HFILL }
        },
        { &hf_greb_attr_filter_item_desc_val,
            { "Description", "grebonding.attr.val.filter.item.desc", FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL }
        },
        { &hf_greb_attr_filter_item_desc_length,
            { "Description length", "grebonding.attr.val.filter.item.desc.length",
                FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }
        },
        { &hf_greb_attr_filter_item_val,
            { "Value", "grebonding.attr.val.filter.item.val", FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL }
        },
        { &hf_greb_attr_error,
            { "Error message", "grebonding.attr.val.error",
                FT_UINT32, BASE_DEC, VALS(greb_error_codes), 0, NULL, HFILL }
        }
    };

    proto_greb = proto_register_protocol("Huawei GRE bonding", "GREbond", "grebonding");
    proto_register_field_array(proto_greb, hf, array_length(hf));

    proto_register_subtree_array(ett, array_length(ett));
}

void
proto_reg_handoff_greb(void)
{
    dissector_handle_t greb_handle;

    greb_handle = create_dissector_handle(dissect_greb, proto_greb);
    dissector_add_uint("gre.proto", 0x0101, greb_handle); // used in production at Deutsche Telekom
    dissector_add_uint("gre.proto", 0xB7EA, greb_handle); // according to RFC

    // TODO
    // when capturing on the gre-interfaces itself, "Linux cooked" interfaces
    //dissector_add_uint("sll.ltype", 0x0101, greb_handle);
    //dissector_add_uint("sll.ltype", 0xB7EA, greb_handle);
    //dissector_add_uint("sll.gretype", 0x0101, greb_handle);
    //dissector_add_uint("sll.gretype", 0xB7EA, greb_handle);
}