Coverage Report

Created: 2026-08-14 06:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/epan/dissectors/packet-wtp.c
Line
Count
Source
1
/* packet-wtp.c
2
 *
3
 * Routines to dissect WTP component of WAP traffic.
4
 *
5
 * Wireshark - Network traffic analyzer
6
 * By Gerald Combs <gerald@wireshark.org>
7
 * Copyright 1998 Gerald Combs
8
 *
9
 * WAP dissector based on original work by Ben Fowler
10
 * Updated by Neil Hunter <neil.hunter@energis-squared.com>
11
 * WTLS support by Alexandre P. Ferreira (Splice IP)
12
 *
13
 * SPDX-License-Identifier: GPL-2.0-or-later
14
 */
15
16
#include "config.h"
17
0
#define WS_LOG_DOMAIN "packet-wtp"
18
#include <wireshark.h>
19
20
#include <epan/packet.h>
21
#include <epan/reassemble.h>
22
#include <epan/tfs.h>
23
#include <wsutil/array.h>
24
#include "packet-wap.h"
25
#include "packet-wtp.h"
26
#include "packet-wsp.h"
27
28
void proto_register_wtp(void);
29
void proto_reg_handoff_wtp(void);
30
31
static const true_false_string continue_truth = {
32
    "TPI Present" ,
33
    "No TPI"
34
};
35
36
static const true_false_string RID_truth = {
37
    "Re-Transmission",
38
    "First transmission"
39
};
40
41
static const true_false_string TIDNew_truth = {
42
    "TID is new" ,
43
    "TID is valid"
44
};
45
46
static const true_false_string tid_response_truth = {
47
    "Response" ,
48
    "Original"
49
};
50
51
static const true_false_string UP_truth = {
52
    "User Acknowledgement required" ,
53
    "User Acknowledgement optional"
54
};
55
56
static const value_string vals_wtp_pdu_type[] = {
57
    { 0, "Not Allowed" },
58
    { 1, "Invoke" },
59
    { 2, "Result" },
60
    { 3, "Ack" },
61
    { 4, "Abort" },
62
    { 5, "Segmented Invoke" },
63
    { 6, "Segmented Result" },
64
    { 7, "Negative Ack" },
65
    { 0, NULL }
66
};
67
68
static const value_string vals_transaction_trailer[] = {
69
    { 0, "Not last packet" },
70
    { 1, "Last packet of message" },
71
    { 2, "Last packet of group" },
72
    { 3, "Re-assembly not supported" },
73
    { 0, NULL }
74
};
75
76
static const value_string vals_version[] = {
77
    { 0, "Current" },
78
    { 1, "Undefined" },
79
    { 2, "Undefined" },
80
    { 3, "Undefined" },
81
    { 0, NULL }
82
};
83
84
static const value_string vals_abort_type[] = {
85
    { 0, "Provider" },
86
    { 1, "User (WSP)" },
87
    { 0, NULL }
88
};
89
90
static const value_string vals_abort_reason_provider[] = {
91
    { 0x00, "Unknown" },
92
    { 0x01, "Protocol Error" },
93
    { 0x02, "Invalid TID" },
94
    { 0x03, "Not Implemented Class 2" },
95
    { 0x04, "Not Implemented SAR" },
96
    { 0x05, "Not Implemented User Acknowledgement" },
97
    { 0x06, "WTP Version Zero" },
98
    { 0x07, "Capacity Temporarily Exceeded" },
99
    { 0x08, "No Response" },
100
    { 0x09, "Message Too Large" },
101
    { 0x00, NULL }
102
};
103
104
static const value_string vals_transaction_classes[] = {
105
    { 0x00, "Unreliable Invoke without Result" },
106
    { 0x01, "Reliable Invoke without Result" },
107
    { 0x02, "Reliable Invoke with Reliable Result" },
108
    { 0x00, NULL }
109
};
110
111
static const value_string vals_tpi_type[] = {
112
    { 0x00, "Error" },
113
    { 0x01, "Info" },
114
    { 0x02, "Option" },
115
    { 0x03, "Packet sequence number" },
116
    { 0x04, "SDU boundary" },
117
    { 0x05, "Frame boundary" },
118
    { 0x00, NULL }
119
};
120
121
static const value_string vals_tpi_opt[] = {
122
    { 0x01, "Maximum receive unit" },
123
    { 0x02, "Total message size" },
124
    { 0x03, "Delay transmission timer" },
125
    { 0x04, "Maximum group" },
126
    { 0x05, "Current TID" },
127
    { 0x06, "No cached TID" },
128
    { 0x00, NULL }
129
};
130
131
/* File scoped variables for the protocol and registered fields */
132
static int proto_wtp;
133
134
/* These fields used by fixed part of header */
135
static int hf_wtp_header_sub_pdu_size;
136
static int hf_wtp_header_flag_continue;
137
static int hf_wtp_header_pdu_type;
138
static int hf_wtp_header_flag_Trailer;
139
static int hf_wtp_header_flag_RID;
140
static int hf_wtp_header_flag_TID;
141
static int hf_wtp_header_flag_TID_response;
142
143
/* These fields used by Invoke packets */
144
static int hf_wtp_header_Inv_version;
145
static int hf_wtp_header_Inv_flag_TIDNew;
146
static int hf_wtp_header_Inv_flag_UP;
147
static int hf_wtp_header_Inv_Reserved;
148
static int hf_wtp_header_Inv_TransactionClass;
149
150
/* static int hf_wtp_header_variable_part; */
151
/* static int hf_wtp_data; */
152
153
static int hf_wtp_tpi_type;
154
static int hf_wtp_tpi_psn;
155
static int hf_wtp_tpi_opt;
156
static int hf_wtp_tpi_optval;
157
static int hf_wtp_tpi_info;
158
159
static int hf_wtp_header_Ack_flag_TVETOK;
160
static int hf_wtp_header_Abort_type;
161
static int hf_wtp_header_Abort_reason_provider;
162
static int hf_wtp_header_Abort_reason_user;
163
static int hf_wtp_header_sequence_number;
164
static int hf_wtp_header_missing_packets;
165
static int hf_wtp_payload;
166
167
/* These fields used when reassembling WTP fragments */
168
static int hf_wtp_fragments;
169
static int hf_wtp_fragment;
170
static int hf_wtp_fragment_overlap;
171
static int hf_wtp_fragment_overlap_conflict;
172
static int hf_wtp_fragment_multiple_tails;
173
static int hf_wtp_fragment_too_long_fragment;
174
static int hf_wtp_fragment_error;
175
static int hf_wtp_fragment_count;
176
static int hf_wtp_reassembled_in;
177
static int hf_wtp_reassembled_length;
178
179
/* Initialize the subtree pointers */
180
static int ett_wtp;
181
static int ett_wtp_sub_pdu_tree;
182
static int ett_header;
183
static int ett_tpilist;
184
static int ett_wsp_fragments;
185
static int ett_wtp_fragment;
186
187
static const fragment_items wtp_frag_items = {
188
    &ett_wtp_fragment,
189
    &ett_wsp_fragments,
190
    &hf_wtp_fragments,
191
    &hf_wtp_fragment,
192
    &hf_wtp_fragment_overlap,
193
    &hf_wtp_fragment_overlap_conflict,
194
    &hf_wtp_fragment_multiple_tails,
195
    &hf_wtp_fragment_too_long_fragment,
196
    &hf_wtp_fragment_error,
197
    &hf_wtp_fragment_count,
198
    &hf_wtp_reassembled_in,
199
    &hf_wtp_reassembled_length,
200
    /* Reassembled data field */
201
    NULL,
202
    "fragments"
203
};
204
205
/* Handle for WSP dissector */
206
static dissector_handle_t wsp_handle;
207
static dissector_handle_t wtp_fromudp_handle;
208
209
/*
210
 * reassembly of WSP
211
 */
212
static reassembly_table wtp_reassembly_table;
213
214
/*
215
 * Extract some bitfields
216
 */
217
1.34k
#define pdu_type(octet)             (((octet) >> 3) & 0x0F) /* Note pdu type must not be 0x00 */
218
230
#define transaction_class(octet)    ((octet) & 0x03)    /* ......XX */
219
384
#define transmission_trailer(octet) (((octet) >> 1) & 0x01)    /* ......X. */
220
221
static char retransmission_indicator(unsigned char octet)
222
664
{
223
664
    switch (pdu_type(octet)) {
224
230
        case INVOKE:
225
298
        case RESULT:
226
330
        case ACK:
227
399
        case SEGMENTED_INVOKE:
228
416
        case SEGMENTED_RESULT:
229
428
        case NEGATIVE_ACK:
230
428
            return octet & 0x01;    /* .......X */
231
236
        default:
232
236
            return 0;
233
664
    }
234
664
}
235
236
/*
237
 * dissect a TPI
238
 */
239
static void
240
wtp_handle_tpi(proto_tree *tree, tvbuff_t *tvb)
241
564
{
242
564
    int            offset = 0;
243
564
    unsigned char  tByte;
244
564
    unsigned char  tType;
245
564
    unsigned char  tLen;
246
564
    proto_tree    *subTree = NULL;
247
564
    proto_item    *pi;
248
249
564
    tByte = tvb_get_uint8(tvb, offset++);
250
564
    tType = (tByte & 0x78) >> 3;
251
564
    if (tByte & 0x04)                /* Long TPI    */
252
43
        tLen = tvb_get_uint8(tvb, offset++);
253
521
    else
254
521
        tLen = tByte & 0x03;
255
564
    pi = proto_tree_add_uint(tree, hf_wtp_tpi_type,
256
564
            tvb, 0, tvb_captured_length(tvb), tType);
257
564
    subTree = proto_item_add_subtree(pi, ett_tpilist);
258
564
    switch (tType) {
259
21
        case 0x00:            /* Error*/
260
            /* \todo    */
261
21
            break;
262
124
        case 0x01:            /* Info    */
263
            /* Beware, untested case here    */
264
124
            proto_tree_add_item(subTree, hf_wtp_tpi_info,
265
124
                    tvb, offset, tLen, ENC_NA);
266
124
            break;
267
74
        case 0x02:            /* Option    */
268
74
            proto_tree_add_item(subTree, hf_wtp_tpi_opt,
269
74
                    tvb, offset++, 1, ENC_LITTLE_ENDIAN);
270
74
            proto_tree_add_item(subTree, hf_wtp_tpi_optval,
271
74
                    tvb, offset, tLen - 1, ENC_NA);
272
74
            break;
273
89
        case 0x03:            /* PSN    */
274
89
            proto_tree_add_item(subTree, hf_wtp_tpi_psn,
275
89
                    tvb, offset, 1, ENC_LITTLE_ENDIAN);
276
89
            break;
277
2
        case 0x04:            /* SDU boundary    */
278
            /* \todo    */
279
2
            break;
280
8
        case 0x05:            /* Frame boundary    */
281
            /* \todo    */
282
8
            break;
283
246
        default:
284
246
            break;
285
564
    }
286
564
}
287
288
/* Code to actually dissect the packets */
289
static void
290
// NOLINTNEXTLINE(misc-no-recursion)
291
dissect_wtp_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
292
793
{
293
793
    wmem_strbuf_t *szInfo;
294
793
    int            offCur        = 0;   /* current offset from start of WTP data */
295
296
793
    unsigned char  b0;
297
298
    /* continuation flag */
299
793
    unsigned char  fCon;            /* Continue flag    */
300
793
    unsigned char  fRID;            /* Re-transmission indicator*/
301
793
    unsigned char  fTTR = '\0';        /* Transmission trailer    */
302
793
    unsigned       cbHeader       = 0;    /* Fixed header length    */
303
793
    unsigned       vHeader       = 0;    /* Variable header length*/
304
793
    int            abortType      = 0;
305
306
    /* Set up structures we'll need to add the protocol subtree and manage it */
307
793
    proto_item    *ti = NULL;
308
793
    proto_tree    *wtp_tree = NULL;
309
310
793
    char           pdut;
311
793
    char           clsTransaction = 3;
312
793
    int            numMissing = 0;        /* Number of missing packets in a negative ack */
313
793
    int            i;
314
793
    tvbuff_t      *wsp_tvb = NULL;
315
793
    uint8_t        psn = 0;        /* Packet sequence number*/
316
793
    uint16_t       TID = 0;        /* Transaction-Id    */
317
793
    int            dataOffset;
318
793
    int            dataLen;
319
320
793
    b0 = tvb_get_uint8 (tvb, offCur + 0);
321
    /* Discover Concatenated PDUs */
322
793
    if (b0 == 0) {
323
115
        unsigned c_fieldlen = 0;        /* Length of length-field    */
324
115
        unsigned c_pdulen = 0;        /* Length of conc. PDU    */
325
326
115
        if (tree) {
327
115
            ti = proto_tree_add_item(tree, proto_wtp,
328
115
                    tvb, offCur, 1, ENC_NA);
329
115
            wtp_tree = proto_item_add_subtree(ti, ett_wtp_sub_pdu_tree);
330
115
            proto_item_append_text(ti, ", PDU concatenation");
331
115
        }
332
115
        offCur = 1;
333
115
        i = 1;
334
649
        while (offCur < (int) tvb_reported_length(tvb)) {
335
534
            tvbuff_t *wtp_tvb;
336
            /* The length of an embedded WTP PDU is coded as either:
337
             *    - a 7-bit value contained in one octet with highest bit == 0.
338
             *    - a 15-bit value contained in two octets (little endian)
339
             *      if the 1st octet has its highest bit == 1.
340
             * This means that this is NOT encoded as an uintvar-integer!!!
341
             */
342
534
            b0 = tvb_get_uint8(tvb, offCur + 0);
343
534
            if (b0 & 0x80) {
344
41
                c_fieldlen = 2;
345
41
                c_pdulen = ((b0 & 0x7f) << 8) | tvb_get_uint8(tvb, offCur + 1);
346
493
            } else {
347
493
                c_fieldlen = 1;
348
493
                c_pdulen = b0;
349
493
            }
350
534
            if (tree) {
351
530
                proto_tree_add_uint(wtp_tree, hf_wtp_header_sub_pdu_size,
352
530
                        tvb, offCur, c_fieldlen, c_pdulen);
353
530
            }
354
534
            if (i > 1) {
355
418
                col_append_str(pinfo->cinfo, COL_INFO, ", ");
356
418
            }
357
            /* Skip the length field for the WTP sub-tvb */
358
534
            wtp_tvb = tvb_new_subset_length(tvb, offCur + c_fieldlen, c_pdulen);
359
            // We recurse here, but we'll run out of packet before we run out of stack.
360
534
            dissect_wtp_common(wtp_tvb, pinfo, wtp_tree);
361
534
            offCur += c_fieldlen + c_pdulen;
362
534
            i++;
363
534
        }
364
115
        if (tree) {
365
59
            proto_item_append_text(ti, ", PDU count: %u", i);
366
59
        }
367
115
        return;
368
115
    }
369
    /* No concatenation */
370
678
    fCon = b0 & 0x80;
371
678
    fRID = retransmission_indicator(b0);
372
678
    pdut = pdu_type(b0);
373
374
678
    ws_debug("WTP packet %u: tree = %p, pdu = %s (%u) length: %u\n",
375
678
            pinfo->num, tree,
376
678
            val_to_str(pinfo->pool,pdut, vals_wtp_pdu_type, "Unknown PDU type 0x%x"),
377
678
            pdut, tvb_captured_length(tvb));
378
379
678
    szInfo = wmem_strbuf_new(pinfo->pool, "");
380
381
    /* Develop the string to put in the Info column */
382
678
    wmem_strbuf_append_printf(szInfo, "WTP %s",
383
678
            val_to_str(pinfo->pool,pdut, vals_wtp_pdu_type, "Unknown PDU type 0x%x"));
384
385
678
    switch (pdut) {
386
230
        case INVOKE:
387
230
            fTTR = transmission_trailer(b0);
388
230
            TID = tvb_get_ntohs(tvb, offCur + 1);
389
230
            psn = 0;
390
230
            clsTransaction = transaction_class(tvb_get_uint8(tvb, offCur + 3));
391
230
            wmem_strbuf_append_printf(szInfo, " Class %d", clsTransaction);
392
230
            cbHeader = 4;
393
230
            break;
394
395
69
        case SEGMENTED_INVOKE:
396
86
        case SEGMENTED_RESULT:
397
86
            fTTR = transmission_trailer(b0);
398
86
            TID = tvb_get_ntohs(tvb, offCur + 1);
399
86
            psn = tvb_get_uint8(tvb, offCur + 3);
400
86
            if (psn != 0) {
401
68
                wmem_strbuf_append_printf(szInfo, " (%u)", psn);
402
68
            }
403
86
            cbHeader = 4;
404
86
            break;
405
406
32
        case ABORT:
407
32
            cbHeader = 4;
408
32
            break;
409
410
68
        case RESULT:
411
68
            fTTR = transmission_trailer(b0);
412
68
            TID = tvb_get_ntohs(tvb, offCur + 1);
413
68
            psn = 0;
414
68
            cbHeader = 3;
415
68
            break;
416
417
32
        case ACK:
418
32
            cbHeader = 3;
419
32
            break;
420
421
12
        case NEGATIVE_ACK:
422
            /* Variable number of missing packets */
423
12
            numMissing = tvb_get_uint8(tvb, offCur + 3);
424
12
            cbHeader = numMissing + 4;
425
12
            break;
426
427
204
        default:
428
204
            break;
429
678
    };
430
660
    if (fRID) {
431
184
        wmem_strbuf_append(szInfo, " R" );
432
        /*str_index += MIN(returned_length, SZINFO_SIZE-str_index);*/
433
184
    };
434
    /* In the interest of speed, if "tree" is NULL, don't do any work not
435
       necessary to generate protocol tree items. */
436
660
    if (tree) {
437
660
        ws_debug("cbHeader = %d", cbHeader);
438
        /* NOTE - Length will be set when we process the TPI */
439
660
        ti = proto_tree_add_item(tree, proto_wtp, tvb, offCur, -1, ENC_NA);
440
660
        ws_debug("(7) Returned from proto_tree_add_item");
441
660
        wtp_tree = proto_item_add_subtree(ti, ett_wtp);
442
443
        /* Code to process the packet goes here */
444
660
        ws_debug("cbHeader=%d offCur=%d", cbHeader, offCur);
445
        /* Add common items: only CON and PDU Type */
446
660
        proto_tree_add_item(
447
660
                wtp_tree,             /* tree */
448
660
                hf_wtp_header_flag_continue,     /* id */
449
660
                tvb,
450
660
                offCur,             /* start of highlight */
451
660
                1,                /* length of highlight*/
452
660
                b0                /* value */
453
660
                );
454
660
        proto_tree_add_item(wtp_tree, hf_wtp_header_pdu_type, tvb, offCur, 1, ENC_LITTLE_ENDIAN);
455
456
660
        switch(pdut) {
457
229
            case INVOKE:
458
229
                proto_tree_add_item(wtp_tree, hf_wtp_header_flag_Trailer, tvb, offCur, 1, ENC_LITTLE_ENDIAN);
459
229
                proto_tree_add_item(wtp_tree, hf_wtp_header_flag_RID, tvb, offCur, 1, ENC_LITTLE_ENDIAN);
460
229
                proto_tree_add_item(wtp_tree, hf_wtp_header_flag_TID_response, tvb, offCur + 1, 2, ENC_BIG_ENDIAN);
461
229
                proto_tree_add_item(wtp_tree, hf_wtp_header_flag_TID, tvb, offCur + 1, 2, ENC_BIG_ENDIAN);
462
463
229
                proto_tree_add_item(wtp_tree, hf_wtp_header_Inv_version , tvb, offCur + 3, 1, ENC_LITTLE_ENDIAN);
464
229
                proto_tree_add_item(wtp_tree, hf_wtp_header_Inv_flag_TIDNew, tvb, offCur + 3, 1, ENC_LITTLE_ENDIAN);
465
229
                proto_tree_add_item(wtp_tree, hf_wtp_header_Inv_flag_UP, tvb, offCur + 3, 1, ENC_LITTLE_ENDIAN);
466
229
                proto_tree_add_item(wtp_tree, hf_wtp_header_Inv_Reserved, tvb, offCur + 3, 1, ENC_LITTLE_ENDIAN);
467
229
                proto_tree_add_item(wtp_tree, hf_wtp_header_Inv_TransactionClass, tvb, offCur + 3, 1, ENC_LITTLE_ENDIAN);
468
229
                proto_item_append_text(ti,
469
229
                        ", PDU: Invoke (%u)"
470
229
                        ", Transaction Class: %s (%u)",
471
229
                        INVOKE,
472
229
                        val_to_str_const(clsTransaction, vals_transaction_classes, "Undefined"),
473
229
                        clsTransaction);
474
229
                break;
475
476
68
            case RESULT:
477
68
                proto_tree_add_item(wtp_tree, hf_wtp_header_flag_Trailer, tvb, offCur, 1, ENC_LITTLE_ENDIAN);
478
68
                proto_tree_add_item(wtp_tree, hf_wtp_header_flag_RID, tvb, offCur, 1, ENC_LITTLE_ENDIAN);
479
68
                proto_tree_add_item(wtp_tree, hf_wtp_header_flag_TID_response, tvb, offCur + 1, 2, ENC_BIG_ENDIAN);
480
68
                proto_tree_add_item(wtp_tree, hf_wtp_header_flag_TID, tvb, offCur + 1, 2, ENC_BIG_ENDIAN);
481
68
                proto_item_append_text(ti, ", PDU: Result (%u)", RESULT);
482
68
                break;
483
484
32
            case ACK:
485
32
                proto_tree_add_item(wtp_tree, hf_wtp_header_Ack_flag_TVETOK, tvb, offCur, 1, ENC_BIG_ENDIAN);
486
487
32
                proto_tree_add_item(wtp_tree, hf_wtp_header_flag_RID, tvb, offCur, 1, ENC_LITTLE_ENDIAN);
488
32
                proto_tree_add_item(wtp_tree, hf_wtp_header_flag_TID_response, tvb, offCur + 1, 2, ENC_BIG_ENDIAN);
489
32
                proto_tree_add_item(wtp_tree, hf_wtp_header_flag_TID, tvb, offCur + 1, 2, ENC_BIG_ENDIAN);
490
32
                proto_item_append_text(ti, ", PDU: ACK (%u)", ACK);
491
32
                break;
492
493
32
            case ABORT:
494
32
                abortType = tvb_get_uint8 (tvb, offCur) & 0x07;
495
32
                proto_tree_add_item(wtp_tree, hf_wtp_header_Abort_type , tvb, offCur , 1, ENC_LITTLE_ENDIAN);
496
32
                proto_tree_add_item(wtp_tree, hf_wtp_header_flag_TID_response, tvb, offCur + 1, 2, ENC_BIG_ENDIAN);
497
32
                proto_tree_add_item(wtp_tree, hf_wtp_header_flag_TID, tvb, offCur + 1, 2, ENC_BIG_ENDIAN);
498
499
32
                if (abortType == PROVIDER) {
500
3
                    uint8_t reason = tvb_get_uint8(tvb, offCur + 3);
501
3
                    proto_tree_add_item( wtp_tree, hf_wtp_header_Abort_reason_provider , tvb, offCur + 3 , 1, ENC_LITTLE_ENDIAN);
502
3
                    proto_item_append_text(ti,
503
3
                            ", PDU: Abort (%u)"
504
3
                            ", Type: Provider (%u)"
505
3
                            ", Reason: %s (%u)",
506
3
                            ABORT,
507
3
                            PROVIDER,
508
3
                            val_to_str_const(reason, vals_abort_reason_provider, "Undefined"),
509
3
                            reason);
510
3
                }
511
29
                else if (abortType == USER) {
512
5
                    uint8_t reason = tvb_get_uint8(tvb, offCur + 3);
513
5
                    proto_tree_add_item(wtp_tree, hf_wtp_header_Abort_reason_user , tvb, offCur + 3 , 1, ENC_LITTLE_ENDIAN);
514
5
                    proto_item_append_text(ti,
515
5
                            ", PDU: Abort (%u)"
516
5
                            ", Type: User (%u)"
517
5
                            ", Reason: %s (%u)",
518
5
                            ABORT,
519
5
                            PROVIDER,
520
5
                            val_to_str_ext_const(reason, &vals_wsp_reason_codes_ext, "Undefined"),
521
5
                            reason);
522
5
                }
523
32
                break;
524
525
68
            case SEGMENTED_INVOKE:
526
68
                proto_tree_add_item(wtp_tree, hf_wtp_header_flag_Trailer, tvb, offCur, 1, ENC_LITTLE_ENDIAN);
527
68
                proto_tree_add_item(wtp_tree, hf_wtp_header_flag_RID, tvb, offCur, 1, ENC_LITTLE_ENDIAN);
528
68
                proto_tree_add_item(wtp_tree, hf_wtp_header_flag_TID_response, tvb, offCur + 1, 2, ENC_BIG_ENDIAN);
529
68
                proto_tree_add_item(wtp_tree, hf_wtp_header_flag_TID, tvb, offCur + 1, 2, ENC_BIG_ENDIAN);
530
531
68
                proto_tree_add_item(wtp_tree, hf_wtp_header_sequence_number , tvb, offCur + 3, 1, ENC_LITTLE_ENDIAN);
532
68
                proto_item_append_text(ti,
533
68
                        ", PDU: Segmented Invoke (%u)"
534
68
                        ", Packet Sequence Number: %u",
535
68
                        SEGMENTED_INVOKE, psn);
536
68
                break;
537
538
16
            case SEGMENTED_RESULT:
539
16
                proto_tree_add_item(wtp_tree, hf_wtp_header_flag_Trailer, tvb, offCur, 1, ENC_LITTLE_ENDIAN);
540
16
                proto_tree_add_item(wtp_tree, hf_wtp_header_flag_RID, tvb, offCur, 1, ENC_LITTLE_ENDIAN);
541
16
                proto_tree_add_item(wtp_tree, hf_wtp_header_flag_TID_response, tvb, offCur + 1, 2, ENC_BIG_ENDIAN);
542
16
                proto_tree_add_item(wtp_tree, hf_wtp_header_flag_TID, tvb, offCur + 1, 2, ENC_BIG_ENDIAN);
543
544
16
                proto_tree_add_item(wtp_tree, hf_wtp_header_sequence_number , tvb, offCur + 3, 1, ENC_LITTLE_ENDIAN);
545
16
                proto_item_append_text(ti,
546
16
                        ", PDU: Segmented Result (%u)"
547
16
                        ", Packet Sequence Number: %u",
548
16
                        SEGMENTED_RESULT, psn);
549
16
                break;
550
551
11
            case NEGATIVE_ACK:
552
11
                proto_tree_add_item(wtp_tree, hf_wtp_header_flag_RID, tvb, offCur, 1, ENC_LITTLE_ENDIAN);
553
11
                proto_tree_add_item(wtp_tree, hf_wtp_header_flag_TID_response, tvb, offCur + 1, 2, ENC_BIG_ENDIAN);
554
11
                proto_tree_add_item(wtp_tree, hf_wtp_header_flag_TID, tvb, offCur + 1, 2, ENC_BIG_ENDIAN);
555
556
11
                proto_tree_add_item(wtp_tree, hf_wtp_header_missing_packets , tvb, offCur + 3, 1, ENC_LITTLE_ENDIAN);
557
                /* Iterate through missing packets */
558
295
                for (i = 0; i < numMissing; i++)
559
284
                {
560
284
                    proto_tree_add_item(wtp_tree, hf_wtp_header_sequence_number, tvb, offCur + 4 + i, 1, ENC_LITTLE_ENDIAN);
561
284
                }
562
11
                proto_item_append_text(ti,
563
11
                        ", PDU: Negative Ack (%u)"
564
11
                        ", Missing Packets: %u",
565
11
                        NEGATIVE_ACK, numMissing);
566
11
                break;
567
568
204
            default:
569
204
                break;
570
660
        };
571
654
        if (fRID) {
572
183
            proto_item_append_text(ti, ", Retransmission");
573
183
        }
574
654
    } else { /* tree is NULL */
575
0
        ws_debug("(4) tree was %p", tree);
576
0
    }
577
    /* Process the variable part */
578
654
    if (fCon) {            /* Now, analyze variable part    */
579
68
        uint8_t   tCon;
580
68
        uint8_t   tByte;
581
68
        unsigned  tpiLen;
582
68
        tvbuff_t *tmp_tvb;
583
584
68
        vHeader = 0;        /* Start scan all over    */
585
586
587
        do {
587
587
            tByte = tvb_get_uint8(tvb, offCur + cbHeader + vHeader);
588
587
            tCon = tByte & 0x80;
589
587
            if (tByte & 0x04)    /* Long TPI    */
590
50
                tpiLen = 2 + tvb_get_uint8(tvb, offCur + cbHeader + vHeader + 1);
591
537
            else
592
537
                tpiLen = 1 + (tByte & 0x03);
593
587
            if (tree)
594
564
            {
595
564
                tmp_tvb = tvb_new_subset_length(tvb, offCur + cbHeader + vHeader, tpiLen);
596
564
                wtp_handle_tpi(wtp_tree, tmp_tvb);
597
564
            }
598
587
            vHeader += tpiLen;
599
587
        } while (tCon);
600
586
    } else {
601
        /* There is no variable part */
602
586
    }    /* End of variable part of header */
603
604
    /* Set the length of the WTP protocol part now we know the length of the
605
     * fixed and variable WTP headers */
606
654
    if (tree)
607
628
        proto_item_set_len(ti, cbHeader + vHeader);
608
609
654
    ws_debug("cbHeader = %d", cbHeader);
610
611
    /*
612
     * Any remaining data ought to be WSP data (if not WTP ACK, NACK
613
     * or ABORT pdu), so, if we have any remaining data, and it's
614
     * not an ACK, NACK, or ABORT PDU, hand it off (defragmented) to the
615
     * WSP dissector.
616
     * Note that the last packet of a fragmented WTP message needn't
617
     * contain any data, so we allow payloadless packets to be
618
     * reassembled.  (XXX - does the reassembly code handle this
619
     * for packets other than the last packet?)
620
     *
621
     * Try calling a subdissector only if:
622
     *    - The WTP payload is resembled in this very packet,
623
     *    - The WTP payload is not fragmented across packets.
624
     */
625
654
    dataOffset = offCur + cbHeader + vHeader;
626
654
    dataLen = tvb_reported_length_remaining(tvb, dataOffset);
627
654
    if ((dataLen >= 0) &&
628
628
            ! ((pdut==ACK) || (pdut==NEGATIVE_ACK) || (pdut==ABORT)))
629
564
    {
630
        /* Try to reassemble if needed, and hand over to WSP
631
         * A fragmented WTP packet is either:
632
         *    - An INVOKE with fTTR (transmission trailer) not set,
633
         *    - a SEGMENTED_INVOKE,
634
         *    - A RESULT with fTTR (transmission trailer) not set,
635
         *    - a SEGMENTED_RESULT.
636
         */
637
564
        if ( ( (pdut == SEGMENTED_INVOKE) || (pdut == SEGMENTED_RESULT)
638
484
                    || ( ((pdut == INVOKE) || (pdut == RESULT)) && (!fTTR) )
639
564
             ) && tvb_bytes_exist(tvb, dataOffset, dataLen) )
640
149
        {
641
            /* Try reassembling fragments */
642
149
            fragment_head *fd_wtp = NULL;
643
149
            uint32_t reassembled_in = 0;
644
149
            bool save_fragmented = pinfo->fragmented;
645
646
149
            pinfo->fragmented = true;
647
149
            fd_wtp = fragment_add_seq(&wtp_reassembly_table, tvb, dataOffset,
648
149
                    pinfo, TID, NULL, psn, dataLen, !fTTR, 0);
649
            /* XXX - fragment_add_seq() yields NULL unless Wireshark knows
650
             * that the packet is part of a reassembled whole. This means
651
             * that fd_wtp will be NULL as long as Wireshark did not encounter
652
             * (and process) the packet containing the last fragment.
653
             * This implies that Wireshark needs two passes over the data for
654
             * correct reassembly. At the first pass, a capture containing
655
             * three fragments plus a retransmission of the last fragment
656
             * will progressively show:
657
             *
658
             *        Packet 1: (Unreassembled fragment 1)
659
             *        Packet 2: (Unreassembled fragment 2)
660
             *        Packet 3: (Reassembled WTP)
661
             *        Packet 4: (WTP payload reassembled in packet 3)
662
             *
663
             * However at subsequent evaluation (e.g., by applying a display
664
             * filter) the packet summary will show:
665
             *
666
             *        Packet 1: (WTP payload reassembled in packet 3)
667
             *        Packet 2: (WTP payload reassembled in packet 3)
668
             *        Packet 3: (Reassembled WTP)
669
             *        Packet 4: (WTP payload reassembled in packet 3)
670
             *
671
             * This is important to know, and also affects read filters!
672
             */
673
149
            wsp_tvb = process_reassembled_data(tvb, dataOffset, pinfo,
674
149
                    "Reassembled WTP", fd_wtp, &wtp_frag_items,
675
149
                    NULL, wtp_tree);
676
149
            ws_debug("WTP: Packet %u %s -> %d: wsp_tvb = %p, fd_wtp = %p",
677
149
                    pinfo->num,
678
149
                    fd_wtp ? "Reassembled" : "Not reassembled",
679
149
                    fd_wtp ? fd_wtp->reassembled_in : 0,
680
149
                    wsp_tvb,
681
149
                    fd_wtp
682
149
                  );
683
149
            if (fd_wtp) {
684
                /* Reassembled */
685
19
                reassembled_in = fd_wtp->reassembled_in;
686
19
                if (pinfo->num == reassembled_in) {
687
                    /* Reassembled in this very packet:
688
                     * We can safely hand the tvb to the WSP dissector */
689
6
                    call_dissector(wsp_handle, wsp_tvb, pinfo, tree);
690
13
                } else {
691
                    /* Not reassembled in this packet */
692
13
                    col_append_fstr(pinfo->cinfo, COL_INFO,
693
13
                            "%s (WTP payload reassembled in packet %u)",
694
13
                            wmem_strbuf_get_str(szInfo), fd_wtp->reassembled_in);
695
696
13
                    proto_tree_add_item(wtp_tree, hf_wtp_payload, tvb, dataOffset, -1, ENC_NA);
697
13
                }
698
130
            } else {
699
                /* Not reassembled yet, or not reassembled at all */
700
130
                col_append_fstr(pinfo->cinfo, COL_INFO,
701
130
                        "%s (Unreassembled fragment %u)",
702
130
                        wmem_strbuf_get_str(szInfo), psn);
703
130
                proto_tree_add_item(wtp_tree, hf_wtp_payload, tvb, dataOffset, -1, ENC_NA);
704
130
            }
705
            /* Now reset fragmentation information in pinfo */
706
149
            pinfo->fragmented = save_fragmented;
707
149
        }
708
415
        else if ( ((pdut == INVOKE) || (pdut == RESULT)) && (fTTR) )
709
207
        {
710
            /* Non-fragmented payload */
711
207
            wsp_tvb = tvb_new_subset_remaining(tvb, dataOffset);
712
            /* We can safely hand the tvb to the WSP dissector */
713
207
            call_dissector(wsp_handle, wsp_tvb, pinfo, tree);
714
207
        }
715
208
        else
716
208
        {
717
            /* Nothing to hand to subdissector */
718
208
            col_append_str(pinfo->cinfo, COL_INFO, wmem_strbuf_get_str(szInfo));
719
208
        }
720
564
    }
721
90
    else
722
90
    {
723
        /* Nothing to hand to subdissector */
724
90
        col_append_str(pinfo->cinfo, COL_INFO, wmem_strbuf_get_str(szInfo));
725
90
    }
726
654
}
727
728
/*
729
 * Called directly from UDP.
730
 * Put "WTP+WSP" into the "Protocol" column.
731
 */
732
static int
733
dissect_wtp_fromudp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
734
263
{
735
263
    col_set_str(pinfo->cinfo, COL_PROTOCOL, "WTP+WSP");
736
263
    col_clear(pinfo->cinfo, COL_INFO);
737
738
263
    dissect_wtp_common(tvb, pinfo, tree);
739
263
    return tvb_captured_length(tvb);
740
263
}
741
742
/*
743
 * Called from a higher-level WAP dissector, presumably WTLS.
744
 * Put "WTLS+WSP+WTP" to the "Protocol" column.
745
 *
746
 * XXX - is this supposed to be called from WTLS?  If so, we're not
747
 * calling it....
748
 *
749
 * XXX - can this be called from any other dissector?
750
 */
751
static int
752
dissect_wtp_fromwtls(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
753
0
{
754
0
    col_set_str(pinfo->cinfo, COL_PROTOCOL, "WTLS+WTP+WSP");
755
0
    col_clear(pinfo->cinfo, COL_INFO);
756
757
0
    dissect_wtp_common(tvb, pinfo, tree);
758
0
    return tvb_captured_length(tvb);
759
0
}
760
761
/* Register the protocol with Wireshark */
762
void
763
proto_register_wtp(void)
764
16
{
765
766
    /* Setup list of header fields */
767
16
    static hf_register_info hf[] = {
768
16
        { &hf_wtp_header_sub_pdu_size,
769
16
            { "Sub PDU size", "wtp.sub_pdu_size",
770
16
                FT_UINT16, BASE_DEC, NULL, 0x0,
771
16
                "Size of Sub-PDU (bytes)", HFILL
772
16
            }
773
16
        },
774
16
        { &hf_wtp_header_flag_continue,
775
16
            { "Continue Flag", "wtp.continue_flag",
776
16
                FT_BOOLEAN, 8, TFS( &continue_truth ), 0x80,
777
16
                NULL, HFILL
778
16
            }
779
16
        },
780
16
        { &hf_wtp_header_pdu_type,
781
16
            { "PDU Type", "wtp.pdu_type",
782
16
                FT_UINT8, BASE_HEX, VALS( vals_wtp_pdu_type ), 0x78,
783
16
                NULL, HFILL
784
16
            }
785
16
        },
786
16
        { &hf_wtp_header_flag_Trailer,
787
16
            { "Trailer Flags", "wtp.trailer_flags",
788
16
                FT_UINT8, BASE_HEX, VALS( vals_transaction_trailer ), 0x06,
789
16
                NULL, HFILL
790
16
            }
791
16
        },
792
16
        { &hf_wtp_header_flag_RID,
793
16
            { "Re-transmission Indicator", "wtp.RID",
794
16
                FT_BOOLEAN, 8, TFS( &RID_truth ), 0x01,
795
16
                NULL, HFILL
796
16
            }
797
16
        },
798
16
        { &hf_wtp_header_flag_TID_response,
799
16
            { "TID Response", "wtp.TID.response",
800
16
                FT_BOOLEAN, 16, TFS( &tid_response_truth ), 0x8000,
801
16
                NULL, HFILL
802
16
            }
803
16
        },
804
16
        { &hf_wtp_header_flag_TID,
805
16
            { "Transaction ID", "wtp.TID",
806
16
                FT_UINT16, BASE_HEX, NULL, 0x7FFF,
807
16
                NULL, HFILL
808
16
            }
809
16
        },
810
16
        { &hf_wtp_header_Inv_version,
811
16
            { "Version", "wtp.header.version",
812
16
                FT_UINT8, BASE_HEX, VALS( vals_version ), 0xC0,
813
16
                NULL, HFILL
814
16
            }
815
16
        },
816
16
        { &hf_wtp_header_Inv_flag_TIDNew,
817
16
            { "TIDNew", "wtp.header.TIDNew",
818
16
                FT_BOOLEAN, 8, TFS( &TIDNew_truth ), 0x20,
819
16
                NULL, HFILL
820
16
        }
821
16
        },
822
16
        { &hf_wtp_header_Inv_flag_UP,
823
16
            { "U/P flag", "wtp.header.UP",
824
16
                FT_BOOLEAN, 8, TFS( &UP_truth ), 0x10,
825
16
                NULL, HFILL
826
16
            }
827
16
        },
828
16
        { &hf_wtp_header_Inv_Reserved,
829
16
            { "Reserved", "wtp.inv.reserved",
830
16
                FT_UINT8, BASE_HEX, NULL, 0x0C,
831
16
                NULL, HFILL
832
16
            }
833
16
        },
834
16
        { &hf_wtp_header_Inv_TransactionClass,
835
16
            { "Transaction Class", "wtp.inv.transaction_class",
836
16
                FT_UINT8, BASE_HEX, VALS( vals_transaction_classes ), 0x03,
837
16
                NULL, HFILL
838
16
            }
839
16
        },
840
16
        { &hf_wtp_header_Ack_flag_TVETOK,
841
16
            { "Tve/Tok flag", "wtp.ack.tvetok",
842
16
                FT_BOOLEAN, 8, NULL, 0x04,
843
16
                NULL, HFILL
844
16
            }
845
16
    },
846
16
        { &hf_wtp_header_Abort_type,
847
16
            { "Abort Type", "wtp.abort.type",
848
16
                FT_UINT8, BASE_HEX, VALS ( vals_abort_type ), 0x07,
849
16
                NULL, HFILL
850
16
            }
851
16
        },
852
16
        { &hf_wtp_header_Abort_reason_provider,
853
16
            { "Abort Reason", "wtp.abort.reason.provider",
854
16
                FT_UINT8, BASE_HEX, VALS ( vals_abort_reason_provider ), 0x00,
855
16
                NULL, HFILL
856
16
            }
857
16
        },
858
        /* Assume WSP is the user and use its reason codes */
859
16
        { &hf_wtp_header_Abort_reason_user,
860
16
            { "Abort Reason", "wtp.abort.reason.user",
861
16
                FT_UINT8, BASE_HEX|BASE_EXT_STRING, &vals_wsp_reason_codes_ext, 0x00,
862
16
                NULL, HFILL
863
16
            }
864
16
        },
865
16
        { &hf_wtp_header_sequence_number,
866
16
            { "Packet Sequence Number", "wtp.header.sequence",
867
16
                FT_UINT8, BASE_DEC, NULL, 0x00,
868
16
                NULL, HFILL
869
16
            }
870
16
        },
871
16
        { &hf_wtp_header_missing_packets,
872
16
            { "Missing Packets", "wtp.header.missing_packets",
873
16
                FT_UINT8, BASE_DEC, NULL, 0x00,
874
16
                NULL, HFILL
875
16
            }
876
16
        },
877
16
        { &hf_wtp_payload,
878
16
            { "Payload", "wtp.payload",
879
16
                FT_BYTES, BASE_NONE, NULL, 0x00,
880
16
                NULL, HFILL
881
16
            }
882
16
        },
883
#if 0
884
        { &hf_wtp_header_variable_part,
885
            { "Header: Variable part", "wtp.header_variable_part",
886
                FT_BYTES, BASE_NONE, NULL, 0x0,
887
                "Variable part of the header", HFILL
888
            }
889
        },
890
        { &hf_wtp_data,
891
            { "Data", "wtp.header_data",
892
                FT_BYTES, BASE_NONE, NULL, 0x0,
893
                NULL, HFILL
894
            }
895
        },
896
#endif
897
16
        { &hf_wtp_tpi_type,
898
16
            { "TPI", "wtp.tpi",
899
16
                FT_UINT8, BASE_HEX, VALS(vals_tpi_type), 0x00,
900
16
                "Identification of the Transport Information Item", HFILL
901
16
            }
902
16
        },
903
16
        { &hf_wtp_tpi_psn,
904
16
            { "Packet sequence number", "wtp.tpi.psn",
905
16
                FT_UINT8, BASE_DEC, NULL, 0x00,
906
16
                "Sequence number of this packet", HFILL
907
16
        }
908
16
        },
909
16
        { &hf_wtp_tpi_opt,
910
16
            { "Option", "wtp.tpi.opt",
911
16
                FT_UINT8, BASE_HEX, VALS(vals_tpi_opt), 0x00,
912
16
                "The given option for this TPI", HFILL
913
16
            }
914
16
        },
915
16
        { &hf_wtp_tpi_optval,
916
16
            { "Option Value", "wtp.tpi.opt.val",
917
16
                FT_NONE, BASE_NONE, NULL, 0x00,
918
16
                "The value that is supplied with this option", HFILL
919
16
            }
920
16
        },
921
16
        { &hf_wtp_tpi_info,
922
16
            { "Information", "wtp.tpi.info",
923
16
                FT_NONE, BASE_NONE, NULL, 0x00,
924
16
                "The information being send by this TPI", HFILL
925
16
            }
926
16
        },
927
928
        /* Fragment fields */
929
16
        { &hf_wtp_fragment_overlap,
930
16
            { "Fragment overlap", "wtp.fragment.overlap",
931
16
                FT_BOOLEAN, BASE_NONE, NULL, 0x0,
932
16
                "Fragment overlaps with other fragments", HFILL
933
16
            }
934
16
        },
935
16
        { &hf_wtp_fragment_overlap_conflict,
936
16
            { "Conflicting data in fragment overlap", "wtp.fragment.overlap.conflict",
937
16
                FT_BOOLEAN, BASE_NONE, NULL, 0x0,
938
16
                "Overlapping fragments contained conflicting data", HFILL
939
16
            }
940
16
        },
941
16
        { &hf_wtp_fragment_multiple_tails,
942
16
            { "Multiple tail fragments found", "wtp.fragment.multipletails",
943
16
                FT_BOOLEAN, BASE_NONE, NULL, 0x0,
944
16
                "Several tails were found when defragmenting the packet", HFILL
945
16
            }
946
16
        },
947
16
        { &hf_wtp_fragment_too_long_fragment,
948
16
            { "Fragment too long", "wtp.fragment.toolongfragment",
949
16
                FT_BOOLEAN, BASE_NONE, NULL, 0x0,
950
16
                "Fragment contained data past end of packet", HFILL
951
16
            }
952
16
        },
953
16
        { &hf_wtp_fragment_error,
954
16
            { "Defragmentation error", "wtp.fragment.error",
955
16
                FT_FRAMENUM, BASE_NONE, NULL, 0x0,
956
16
                "Defragmentation error due to illegal fragments", HFILL
957
16
            }
958
16
        },
959
16
        { &hf_wtp_fragment_count,
960
16
            { "Fragment count", "wtp.fragment.count",
961
16
                FT_UINT32, BASE_DEC, NULL, 0x0,
962
16
                NULL, HFILL
963
16
            }
964
16
        },
965
16
        { &hf_wtp_reassembled_in,
966
16
            { "Reassembled in", "wtp.reassembled.in",
967
16
                FT_FRAMENUM, BASE_NONE, NULL, 0x0,
968
16
                "WTP fragments are reassembled in the given packet", HFILL
969
16
            }
970
16
        },
971
16
        { &hf_wtp_reassembled_length,
972
16
            { "Reassembled WTP length", "wtp.reassembled.length",
973
16
                FT_UINT32, BASE_DEC, NULL, 0x0,
974
16
                "The total length of the reassembled payload", HFILL
975
16
            }
976
16
        },
977
16
        { &hf_wtp_fragment,
978
16
            { "WTP Fragment", "wtp.fragment",
979
16
                FT_FRAMENUM, BASE_NONE, NULL, 0x0,
980
16
                NULL, HFILL
981
16
            }
982
16
        },
983
16
        { &hf_wtp_fragments,
984
16
            { "WTP Fragments", "wtp.fragments",
985
16
                FT_NONE, BASE_NONE, NULL, 0x0,
986
16
                NULL, HFILL
987
16
            }
988
16
        },
989
16
    };
990
991
    /* Setup protocol subtree array */
992
16
    static int *ett[] = {
993
16
        &ett_wtp,
994
16
        &ett_wtp_sub_pdu_tree,
995
16
        &ett_header,
996
16
        &ett_tpilist,
997
16
        &ett_wsp_fragments,
998
16
        &ett_wtp_fragment,
999
16
    };
1000
1001
    /* Register the protocol name and description */
1002
    /* Abbreviated protocol name should Match IANA: https://www.iana.org/assignments/port-numbers/ */
1003
16
    proto_wtp = proto_register_protocol("Wireless Transaction Protocol", "WTP", "wtp");
1004
1005
    /* Required calls to register the header fields and subtrees used */
1006
16
    proto_register_field_array(proto_wtp, hf, array_length(hf));
1007
16
    proto_register_subtree_array(ett, array_length(ett));
1008
1009
16
    register_dissector("wtp-wtls", dissect_wtp_fromwtls, proto_wtp);
1010
16
    wtp_fromudp_handle = register_dissector("wtp-udp", dissect_wtp_fromudp, proto_wtp);
1011
16
    reassembly_table_register(&wtp_reassembly_table,
1012
16
                          &addresses_reassembly_table_functions);
1013
16
}
1014
1015
void
1016
proto_reg_handoff_wtp(void)
1017
16
{
1018
    /*
1019
     * Get a handle for the connection-oriented WSP dissector - if WTP
1020
     * PDUs have data, it is WSP.
1021
     */
1022
16
    wsp_handle = find_dissector_add_dependency("wsp-co", proto_wtp);
1023
1024
16
    dissector_add_uint_with_preference("udp.port", UDP_PORT_WTP_WSP, wtp_fromudp_handle);
1025
16
    dissector_add_uint("gsm_sms_ud.udh.port", UDP_PORT_WTP_WSP, wtp_fromudp_handle);
1026
16
    dissector_add_uint("gsm_sms.udh.port", UDP_PORT_WTP_WSP, wtp_fromudp_handle);
1027
16
}
1028
1029
/*
1030
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
1031
 *
1032
 * Local variables:
1033
 * c-basic-offset: 4
1034
 * tab-width: 8
1035
 * indent-tabs-mode: nil
1036
 * End:
1037
 *
1038
 * vi: set shiftwidth=4 tabstop=8 expandtab:
1039
 * :indentSize=4:tabSize=8:noTabs=true:
1040
 */