Coverage Report

Created: 2025-02-15 06:25

/src/wireshark/epan/dissectors/packet-vj-comp.c
Line
Count
Source (jump to first uncovered line)
1
/* packet-vj-comp.c
2
 * Routines for decompression of PPP Van Jacobson compression
3
 * RFC 1144
4
 *
5
 * Wireshark - Network traffic analyzer
6
 * By Gerald Combs <gerald@wireshark.org>
7
 *
8
 * SPDX-License-Identifier: GPL-2.0-or-later
9
 */
10
/* The routines in this file were created by reading the description of
11
 * RFC 1144 available here: ftp://ftp.rfc-editor.org/in-notes/rfc1144.pdf
12
 * ONLY the description of the protocol in section 3.2 was used.
13
 * Notably, the sample implementation in Appendix A was NOT read by this file's
14
 * author, due to the questionable legality of using it in Wireshark.
15
 * For details on this issue, see:
16
 * https://gitlab.com/wireshark/wireshark/-/issues/12138
17
 */
18
/* Currently hard-coded to assume TCP over IPv4.
19
 * Nothing in the standard explicitly prevents an IPv6 implementation...
20
 */
21
22
#define WS_LOG_DOMAIN "packet-vjc-comp"
23
#include "config.h"
24
#include <wireshark.h>
25
26
#include <epan/conversation.h>
27
#include <epan/in_cksum.h>
28
#include <epan/expert.h>
29
#include <epan/packet.h>
30
#include <epan/ipproto.h>
31
#include <epan/ppptypes.h>
32
#include <epan/tfs.h>
33
#include <wsutil/pint.h>
34
#include <wsutil/str_util.h>
35
36
/* Store the last connection number we've seen.
37
 * Only used on the first pass, in case the connection number itself
38
 * gets compressed out.
39
 */
40
2.51k
#define CNUM_INVALID UINT16_MAX
41
static uint16_t last_cnum = CNUM_INVALID;
42
43
/* Location in an IPv4 packet of the IP Next Protocol field
44
 * (which VJC replaces with the connection ID in uncompressed packets)
45
 */
46
7.83k
#define VJC_CONNID_OFFSET 9
47
48
/* Minimum TCP header length. We get compression data from the TCP header,
49
 * and also store it for future use.
50
 */
51
1.74k
#define VJC_TCP_HDR_LEN 20
52
53
/* Structure for tracking the changeable parts of a packet header */
54
typedef struct vjc_hdr_s {
55
    uint16_t tcp_chksum;
56
    uint16_t urg;
57
    uint16_t win;
58
    uint32_t seq;
59
    uint32_t ack;
60
    uint32_t ip_id;
61
    bool psh;
62
} vjc_hdr_t;
63
64
/* The structure used in a wireshark "conversation"
65
 * (though it's now in the multimap below)
66
 */
67
typedef struct vjc_conv_s {
68
    uint32_t    last_frame_len; // Length of previous frame (for SAWU/SWU)
69
    uint8_t    *frame_headers;  // Full copy of the IP header
70
    uint8_t     header_len;     // Length of the stored IP header
71
    vjc_hdr_t   vjc_headers;
72
} vjc_conv_t;
73
74
/* Map of frame number and connection ID to vjc_conv_t */
75
static wmem_multimap_t *vjc_conv_table;
76
77
/* Store connection ID for compressed frames that lack it */
78
static wmem_map_t *vjc_conn_id_lookup;
79
80
static dissector_handle_t vjcu_handle;
81
static dissector_handle_t vjcc_handle;
82
static dissector_handle_t ip_handle;
83
84
void proto_register_vjc(void);
85
void proto_reg_handoff_vjc(void);
86
87
static int proto_vjc;
88
89
static int ett_vjc;
90
static int ett_vjc_change_mask;
91
92
static expert_field ei_vjc_sawu;
93
static expert_field ei_vjc_swu;
94
static expert_field ei_vjc_no_cnum;
95
static expert_field ei_vjc_no_conversation;
96
static expert_field ei_vjc_no_direction;
97
static expert_field ei_vjc_no_conv_data;
98
static expert_field ei_vjc_undecoded;
99
static expert_field ei_vjc_bad_data;
100
static expert_field ei_vjc_error;
101
102
14
#define VJC_FLAG_R 0x80
103
3.69k
#define VJC_FLAG_C 0x40
104
2.47k
#define VJC_FLAG_I 0x20
105
212
#define VJC_FLAG_P 0x10
106
1.93k
#define VJC_FLAG_S 0x08
107
1.94k
#define VJC_FLAG_A 0x04
108
1.94k
#define VJC_FLAG_W 0x02
109
2.00k
#define VJC_FLAG_U 0x01
110
111
10.8k
#define VJC_FLAGS_SAWU 0x0F
112
3.03k
#define VJC_FLAGS_SWU 0x0B
113
114
static int hf_vjc_comp;
115
static int hf_vjc_cnum;
116
static int hf_vjc_change_mask;
117
static int hf_vjc_change_mask_r;
118
static int hf_vjc_change_mask_c;
119
static int hf_vjc_change_mask_i;
120
static int hf_vjc_change_mask_p;
121
static int hf_vjc_change_mask_s;
122
static int hf_vjc_change_mask_a;
123
static int hf_vjc_change_mask_w;
124
static int hf_vjc_change_mask_u;
125
static int hf_vjc_chksum;
126
static int hf_vjc_urg;
127
static int hf_vjc_d_win;
128
static int hf_vjc_d_ack;
129
static int hf_vjc_d_seq;
130
static int hf_vjc_d_ipid;
131
static int hf_vjc_tcpdata;
132
133
static int * const vjc_change_mask_fields[] = {
134
    &hf_vjc_change_mask_r,
135
    &hf_vjc_change_mask_c,
136
    &hf_vjc_change_mask_i,
137
    &hf_vjc_change_mask_p,
138
    &hf_vjc_change_mask_s,
139
    &hf_vjc_change_mask_a,
140
    &hf_vjc_change_mask_w,
141
    &hf_vjc_change_mask_u,
142
    NULL
143
};
144
145
/* Initialization routine. Called at start of dissection.
146
 * Registered in proto_register_vjc() below.
147
 */
148
static void
149
vjc_init_protocol(void)
150
14
{
151
14
    last_cnum = CNUM_INVALID;
152
14
}
153
154
/* Cleanup routine. Called at close of file.
155
 * Registered in proto_register_vjc() below.
156
 */
157
static void
158
vjc_cleanup_protocol(void)
159
0
{
160
0
    last_cnum = CNUM_INVALID;
161
0
}
162
163
/* Generate a "conversation" key for a VJC connection.
164
 * Returns NULL if it can't for some reason.
165
 */
166
static void *
167
vjc_get_conv_key(packet_info *pinfo, uint32_t vjc_cnum)
168
1.37k
{
169
1.37k
    if (vjc_cnum == CNUM_INVALID)
170
4
        return NULL;
171
172
    /* PPP gives us almost nothing to hook a conversation on; just whether
173
     * the packet is considered to be P2P_DIR_RECV or P2P_DIR_SENT.
174
     * Ideally we should also be distinguishing conversations based on the
175
     * capture interface, VLAN ID, MPLS tags, etc., etc. but that's beyond
176
     * the scope of this dissector, and a perennial problem in Wireshark anyway.
177
     * See <https://gitlab.com/wireshark/wireshark/-/issues/4561>
178
     */
179
1.36k
    uint16_t ret_val = (uint16_t)vjc_cnum;
180
1.36k
    switch (pinfo->p2p_dir) {
181
284
        case P2P_DIR_RECV:
182
284
            ret_val |= 0x0100;
183
284
            break;
184
12
        case P2P_DIR_SENT:
185
12
            ret_val |= 0x0200;
186
12
            break;
187
1.07k
        default:
188
1.07k
            return NULL;
189
1.36k
    }
190
296
    return GUINT_TO_POINTER(ret_val);
191
1.36k
}
192
193
/* RFC 1144 section 3.2.2 says that "deltas" are sent for many values in the
194
 * header. If the initial byte is 0, that means the following 2 bytes are the
195
 * 16-bit value of the delta. Otherwise, the initial byte is the 8-bit value.
196
 */
197
static uint32_t
198
vjc_delta_uint(proto_tree *tree, int hf, tvbuff_t *tvb, unsigned *offset)
199
1.57k
{
200
1.57k
    uint32_t ret_val;
201
1.57k
    if (0 != tvb_get_uint8(tvb, *offset)) {
202
1.28k
        proto_tree_add_item_ret_uint(tree, hf, tvb, *offset, 1,
203
1.28k
                ENC_BIG_ENDIAN, &ret_val);
204
1.28k
        (*offset)++;
205
1.28k
    }
206
286
    else {
207
286
        (*offset)++;
208
286
        proto_tree_add_item_ret_uint(tree, hf, tvb, *offset, 2,
209
286
                ENC_BIG_ENDIAN, &ret_val);
210
286
        *offset += 2;
211
286
    }
212
1.57k
    return ret_val;
213
1.57k
}
214
215
/* Same thing but signed, since the TCP window delta can be negative */
216
static int32_t
217
vjc_delta_int(proto_tree *tree, int hf, tvbuff_t *tvb, unsigned *offset)
218
135
{
219
135
    int32_t ret_val;
220
135
    if (0 != tvb_get_int8(tvb, *offset)) {
221
109
        proto_tree_add_item_ret_int(tree, hf, tvb, *offset, 1,
222
109
                ENC_BIG_ENDIAN, &ret_val);
223
109
        (*offset)++;
224
109
    }
225
26
    else {
226
26
        (*offset)++;
227
26
        proto_tree_add_item_ret_int(tree, hf, tvb, *offset, 2,
228
26
                ENC_BIG_ENDIAN, &ret_val);
229
26
        *offset += 2;
230
26
    }
231
135
    return ret_val;
232
135
}
233
234
/* Main dissection routine for uncompressed VJC packets.
235
 * Registered in proto_reg_handoff_vjc() below.
236
 */
237
static int
238
dissect_vjc_uncomp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data)
239
2.01k
{
240
    /* A Van Jacobson uncompressed packet contains a standard TCP/IP header, with
241
     * the IP next protocol ID replaced with the connection number.
242
     * It's meant to signify a new TCP connection, or refresh an existing one,
243
     * which will have subsequent compressed packets.
244
     */
245
2.01k
    proto_tree     *subtree     = NULL;
246
2.01k
    proto_item     *ti          = NULL;
247
2.01k
    uint8_t         ip_ver      = 0;
248
2.01k
    uint8_t         ip_len      = 0;
249
2.01k
    unsigned        tcp_len     = 0;
250
2.01k
    uint32_t        vjc_cnum    = 0;
251
2.01k
    tvbuff_t       *tcpip_tvb   = NULL;
252
2.01k
    tvbuff_t       *sub_tvb     = NULL;
253
2.01k
    void           *conv_id     = NULL;
254
2.01k
    vjc_conv_t     *pkt_data    = NULL;
255
2.01k
    uint8_t        *pdata       = NULL;
256
2.01k
    static uint8_t  real_proto  = IP_PROTO_TCP;
257
258
2.01k
    ti = proto_tree_add_item(tree, proto_vjc, tvb, 0, -1, ENC_NA);
259
2.01k
    subtree = proto_item_add_subtree(ti, ett_vjc);
260
2.01k
    proto_item_set_text(subtree, "PPP Van Jacobson uncompressed TCP/IP");
261
262
    /* Start with some sanity checks */
263
2.01k
    if (tvb_captured_length(tvb) < VJC_CONNID_OFFSET+1) {
264
275
        proto_tree_add_expert_format(subtree, pinfo, &ei_vjc_bad_data, tvb, 0, -1,
265
275
                "Packet truncated before Connection ID field");
266
275
        return tvb_captured_length(tvb);
267
275
    }
268
1.74k
    ip_ver = (tvb_get_uint8(tvb, 0) & 0xF0) >> 4;
269
1.74k
    ip_len = (tvb_get_uint8(tvb, 0) & 0x0F) << 2;
270
1.74k
    tcp_len = ip_len + VJC_TCP_HDR_LEN;
271
1.74k
    if (ip_ver != 4) {
272
284
        proto_tree_add_expert_format(subtree, pinfo, &ei_vjc_bad_data, tvb, 0, 1,
273
284
                "IPv%d unsupported for VJC compression", ip_ver);
274
284
        return tvb_captured_length(tvb);
275
284
    }
276
277
    /* So far so good, continue the dissection */
278
1.45k
    ti = proto_tree_add_boolean(subtree, hf_vjc_comp, tvb, 0, 0, false);
279
1.45k
    proto_item_set_generated(ti);
280
281
1.45k
    proto_tree_add_item_ret_uint(subtree, hf_vjc_cnum, tvb, VJC_CONNID_OFFSET, 1,
282
1.45k
            ENC_BIG_ENDIAN, &vjc_cnum);
283
284
    /* Build a composite TVB containing the original TCP/IP data.
285
     * This is easy for uncompressed VJC packets because only one byte
286
     * is different from the on-the-wire data.
287
     */
288
1.45k
    sub_tvb = tvb_new_child_real_data(tvb, &real_proto, 1, 1);
289
1.45k
    tvb_set_free_cb(sub_tvb, NULL);
290
291
1.45k
    tcpip_tvb = tvb_new_composite();
292
1.45k
    tvb_composite_append(tcpip_tvb, tvb_new_subset_length(tvb, 0, VJC_CONNID_OFFSET));
293
1.45k
    tvb_composite_append(tcpip_tvb, sub_tvb);
294
1.45k
    if (tvb_captured_length_remaining(tvb, VJC_CONNID_OFFSET+1) > 0) {
295
1.44k
        tvb_composite_append(tcpip_tvb, tvb_new_subset_remaining(tvb, VJC_CONNID_OFFSET+1));
296
1.44k
    }
297
1.45k
    tvb_composite_finalize(tcpip_tvb);
298
299
1.45k
    add_new_data_source(pinfo, tcpip_tvb, "Original TCP/IP data");
300
301
1.45k
    if (!(pinfo->p2p_dir == P2P_DIR_RECV || pinfo->p2p_dir == P2P_DIR_SENT)) {
302
        /* We can't make a proper conversation if we don't know the endpoints */
303
1.22k
        proto_tree_add_expert(subtree, pinfo, &ei_vjc_no_direction, tvb, 0, 0);
304
1.22k
    }
305
229
    else if (tvb_captured_length(tvb) < tcp_len) {
306
        /* Not enough data. We can still pass this packet onward (though probably
307
         * to no benefit), but can't base future decompression off of it.
308
         */
309
65
        proto_tree_add_expert_format(subtree, pinfo, &ei_vjc_bad_data, tvb, 0, -1,
310
65
                "Packet truncated before end of TCP/IP headers");
311
65
    }
312
164
    else if (!pinfo->fd->visited) {
313
        /* If this is our first time visiting this packet, set things up for
314
         * decompressing future packets.
315
         */
316
164
        last_cnum = vjc_cnum;
317
164
        conv_id = vjc_get_conv_key(pinfo, vjc_cnum);
318
164
        DISSECTOR_ASSERT(conv_id != NULL);
319
320
164
        pkt_data = wmem_new0(wmem_file_scope(), vjc_conv_t);
321
164
        pkt_data->header_len = tcp_len;
322
164
        pdata = // shorthand
323
164
            pkt_data->frame_headers =
324
164
            (uint8_t *)tvb_memdup(wmem_file_scope(), tcpip_tvb, 0, tcp_len);
325
326
        // This value is used for re-calculating seq/ack numbers
327
164
        pkt_data->last_frame_len = tvb_reported_length(tvb) - ip_len;
328
329
164
        pkt_data->vjc_headers.ip_id = pntoh16(pdata + 4);
330
164
        pkt_data->vjc_headers.seq = pntoh32(pdata + ip_len + 4);
331
164
        pkt_data->vjc_headers.ack = pntoh32(pdata + ip_len + 8);
332
164
        pkt_data->vjc_headers.psh = (pdata[ip_len + 13] & 0x08) == 0x08;
333
164
        pkt_data->vjc_headers.win = pntoh16(pdata + ip_len + 14);
334
164
        pkt_data->vjc_headers.tcp_chksum = pntoh16(pdata + ip_len + 16);
335
164
        pkt_data->vjc_headers.urg = pntoh16(pdata + ip_len + 18);
336
337
164
        wmem_multimap_insert32(vjc_conv_table, conv_id, pinfo->num, (void *)pkt_data);
338
164
    }
339
0
    else {
340
        /* We've already visited this packet, we should have all the info we need. */
341
0
    }
342
343
1.45k
    return call_dissector_with_data(ip_handle, tcpip_tvb, pinfo, tree, data);
344
1.74k
}
345
346
/* Main dissection routine for compressed VJC packets.
347
 * Registered in proto_reg_handoff_vjc() below.
348
 */
349
static int
350
dissect_vjc_comp(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, void* data _U_)
351
3.89k
{
352
    /* A Van Jacobson compressed packet contains a change mask, which indicates
353
     * possible fields that may be present.
354
     */
355
3.89k
    proto_tree     *subtree     = NULL;
356
3.89k
    proto_item     *ti          = NULL;
357
3.89k
    unsigned        hdr_len     = 3;    // See below
358
3.89k
    bool            hdr_error   = false;
359
3.89k
    unsigned        ip_len      = 0;
360
3.89k
    unsigned        pkt_len     = 0;
361
3.89k
    unsigned        d_ipid      = 0;
362
3.89k
    unsigned        d_seq       = 0;
363
3.89k
    unsigned        d_ack       = 0;
364
3.89k
    int             d_win       = 0;
365
3.89k
    uint8_t         flags       = 0;
366
3.89k
    unsigned        offset      = 0;
367
3.89k
    uint32_t        urg         = 0;
368
3.89k
    uint32_t        ip_chksum   = 0;
369
3.89k
    uint32_t        tcp_chksum  = 0;
370
3.89k
    uint32_t        vjc_cnum    = 0;
371
3.89k
    void           *conv_id     = NULL;
372
3.89k
    vjc_hdr_t      *this_hdr    = NULL;
373
3.89k
    vjc_hdr_t      *last_hdr    = NULL;
374
3.89k
    vjc_conv_t     *last_data   = NULL;
375
3.89k
    vjc_conv_t     *this_data   = NULL;
376
3.89k
    uint8_t        *pdata       = NULL;
377
3.89k
    tvbuff_t       *tcpip_tvb   = NULL;
378
3.89k
    tvbuff_t       *sub_tvb     = NULL;
379
380
    /* Calculate the length of the VJC header,
381
     * accounting for extensions in the delta fields.
382
     * We start with a value of 3, because we'll always have
383
     * an 8-bit change mask and a 16-bit TCP checksum.
384
     */
385
3.89k
#define TEST_HDR_LEN \
386
6.34k
    if (tvb_captured_length(tvb)< hdr_len) { hdr_error = true; goto done_header_len; }
387
388
3.89k
    TEST_HDR_LEN;
389
1.36k
    flags = tvb_get_uint8(tvb, offset);
390
1.36k
    if (flags & VJC_FLAG_C) {
391
        // have connection number
392
96
        hdr_len++;
393
96
        TEST_HDR_LEN;
394
92
    }
395
1.36k
    if ((flags & VJC_FLAGS_SAWU) == VJC_FLAGS_SAWU) {
396
        /* Special case for "unidirectional data transfer".
397
         * No change to header size; d_ack = 0, and
398
         * we're to calculate d_seq ourselves.
399
         */
400
250
    }
401
1.11k
    else if ((flags & VJC_FLAGS_SAWU) == VJC_FLAGS_SWU) {
402
        /* Special case for "echoed interactive traffic".
403
         * No change to header size; we're to calculate d_seq and d_ack.
404
         */
405
43
    }
406
1.07k
    else {
407
        /* Not a special case, determine the header size by
408
         * testing the SAWU flags individually.
409
         */
410
1.07k
        if (flags & VJC_FLAG_U) {
411
            // have urgent pointer
412
501
            hdr_len += 2;
413
501
            TEST_HDR_LEN;
414
448
        }
415
1.01k
        if (flags & VJC_FLAG_W) {
416
            // have d_win
417
142
            if (0 == tvb_get_int8(tvb, offset + hdr_len))
418
30
                hdr_len += 3;
419
112
            else
420
112
                hdr_len++;
421
142
            TEST_HDR_LEN;
422
140
        }
423
1.01k
        if (flags & VJC_FLAG_A) {
424
            // have d_ack
425
489
            if (0 == tvb_get_uint8(tvb, offset + hdr_len))
426
158
                hdr_len += 3;
427
331
            else
428
331
                hdr_len++;
429
489
            TEST_HDR_LEN;
430
474
        }
431
1.00k
        if (flags & VJC_FLAG_S) {
432
            // have d_seq
433
270
            if (0 == tvb_get_uint8(tvb, offset + hdr_len))
434
122
                hdr_len += 3;
435
148
            else
436
148
                hdr_len++;
437
270
            TEST_HDR_LEN;
438
230
        }
439
1.00k
    }
440
1.25k
    if (flags & VJC_FLAG_I) {
441
        // have IP ID
442
958
        if (0 == tvb_get_uint8(tvb, offset + hdr_len))
443
135
            hdr_len += 3;
444
823
        else
445
823
            hdr_len++;
446
958
        TEST_HDR_LEN;
447
922
    }
448
449
    /* Now that we have the header length, use it when assigning the
450
     * protocol item.
451
     */
452
1.21k
#undef TEST_HDR_LEN
453
3.88k
done_header_len:
454
3.88k
    ti = proto_tree_add_item(tree, proto_vjc, tvb, 0,
455
3.88k
            MIN(hdr_len, tvb_captured_length(tvb)), ENC_NA);
456
3.88k
    subtree = proto_item_add_subtree(ti, ett_vjc);
457
3.88k
    proto_item_set_text(subtree, "PPP Van Jacobson compressed TCP/IP");
458
3.88k
    if (hdr_error) {
459
2.67k
        proto_tree_add_expert_format(subtree, pinfo, &ei_vjc_bad_data, tvb, 0, -1,
460
2.67k
                "Packet truncated, compression header incomplete");
461
2.67k
        return tvb_captured_length(tvb);
462
2.67k
    }
463
464
1.20k
    ti = proto_tree_add_boolean(subtree, hf_vjc_comp, tvb, 0, 0, true);
465
1.20k
    proto_item_set_generated(ti);
466
467
1.20k
    ti = proto_tree_add_bitmask(subtree, tvb, 0, hf_vjc_change_mask,
468
1.20k
            ett_vjc_change_mask, vjc_change_mask_fields, ENC_NA);
469
1.20k
    if ((flags & VJC_FLAGS_SAWU) == VJC_FLAGS_SAWU) {
470
246
        proto_tree_add_expert(ti, pinfo, &ei_vjc_sawu, tvb, 0, 1);
471
246
    }
472
961
    else if ((flags & VJC_FLAGS_SAWU) == VJC_FLAGS_SWU) {
473
43
        proto_tree_add_expert(ti, pinfo, &ei_vjc_swu, tvb, 0, 1);
474
43
    }
475
476
1.20k
    offset++;
477
478
1.20k
    if (flags & VJC_FLAG_C) {
479
79
        proto_tree_add_item_ret_uint(subtree, hf_vjc_cnum, tvb, offset, 1,
480
79
                ENC_BIG_ENDIAN, &vjc_cnum);
481
79
        last_cnum = vjc_cnum;
482
79
        offset++;
483
79
    }
484
1.12k
    else {
485
        /* "If C is clear, the connection is assumed to be the same as for the
486
         * last compressed or uncompressed packet."
487
         */
488
1.12k
        if (!pinfo->fd->visited) {
489
            /* On the first pass, get it from what we saw last,
490
             * and save it for future passes.
491
             * This can store CNUM_INVALID if that's in last_cnum
492
             * but that's not a problem.
493
             */
494
1.12k
            vjc_cnum = last_cnum;
495
1.12k
            wmem_map_insert(vjc_conn_id_lookup, GUINT_TO_POINTER(pinfo->num), GUINT_TO_POINTER(vjc_cnum));
496
1.12k
        }
497
0
        else {
498
0
            if (wmem_map_contains(vjc_conn_id_lookup, GUINT_TO_POINTER(pinfo->num))) {
499
0
                vjc_cnum = GPOINTER_TO_UINT(wmem_map_lookup(vjc_conn_id_lookup,
500
0
                                                GUINT_TO_POINTER(pinfo->num)));
501
0
            }
502
0
            else {
503
0
                vjc_cnum = CNUM_INVALID;
504
0
            }
505
0
        }
506
1.12k
        if (vjc_cnum != CNUM_INVALID) {
507
1.12k
            ti = proto_tree_add_uint(subtree, hf_vjc_cnum, tvb, offset, 0, vjc_cnum);
508
1.12k
            proto_item_set_generated(ti);
509
1.12k
        }
510
4
        else {
511
4
            proto_tree_add_expert(subtree, pinfo, &ei_vjc_no_cnum, tvb, 0, 0);
512
4
        }
513
1.12k
    }
514
1.20k
    conv_id = vjc_get_conv_key(pinfo, vjc_cnum);
515
1.20k
    if (conv_id != NULL) {
516
        // Get the most recent headers from *before* this packet
517
132
        last_data = (vjc_conv_t *)wmem_multimap_lookup32_le(vjc_conv_table, conv_id, pinfo->num - 1);
518
132
    }
519
1.20k
    if (last_data == NULL) {
520
1.10k
        proto_tree_add_expert(subtree, pinfo, &ei_vjc_no_conversation,
521
1.10k
                tvb, 1, (flags & VJC_FLAG_C) ? 1 : 0);
522
1.10k
    }
523
524
1.20k
    proto_tree_add_item_ret_uint(subtree, hf_vjc_chksum, tvb, offset, 2,
525
1.20k
            ENC_BIG_ENDIAN, &tcp_chksum);
526
1.20k
    offset += 2;
527
528
1.20k
    if ((flags & VJC_FLAGS_SAWU) == VJC_FLAGS_SAWU) {
529
        /* Special case for "unidirectional data transfer".
530
         * d_ack is 0, and d_seq changed by the amount of data in the previous packet.
531
         */
532
246
        flags &= ~VJC_FLAGS_SAWU;
533
246
        d_ack = 0;
534
246
        if (last_data != NULL) {
535
14
            d_seq = last_data->last_frame_len;
536
14
            ti = proto_tree_add_uint(subtree, hf_vjc_d_ack, tvb, offset, 0, d_ack);
537
14
            proto_item_set_generated(ti);
538
14
            ti = proto_tree_add_uint(subtree, hf_vjc_d_seq, tvb, offset, 0, d_seq);
539
14
            proto_item_set_generated(ti);
540
14
        }
541
246
    }
542
961
    else if ((flags & VJC_FLAGS_SAWU) == VJC_FLAGS_SWU) {
543
        /* Special case for "echoed interactive traffic".
544
         * d_seq and d_ack changed by the amount of user data in the
545
         * previous packet.
546
         */
547
43
        flags &= ~VJC_FLAGS_SAWU;
548
43
        if (last_data != NULL) {
549
13
            d_seq = d_ack = last_data->last_frame_len;
550
13
            ti = proto_tree_add_uint(subtree, hf_vjc_d_ack, tvb, offset, 0, d_ack);
551
13
            proto_item_set_generated(ti);
552
13
            ti = proto_tree_add_uint(subtree, hf_vjc_d_seq, tvb, offset, 0, d_seq);
553
13
            proto_item_set_generated(ti);
554
13
        }
555
43
    }
556
918
    else {
557
        /* Not a special case, read the SAWU flags individually */
558
559
918
        if (flags & VJC_FLAG_U) {
560
            /* "The packet’s urgent pointer is sent if URG is set ..."
561
            * I assume that means the full 16-bit value here.
562
            */
563
400
            proto_tree_add_item_ret_uint(subtree, hf_vjc_urg, tvb, offset, 2,
564
400
                    ENC_BIG_ENDIAN, &urg);
565
400
            offset += 2;
566
400
        }
567
518
        else {
568
518
            urg = 0;
569
518
        }
570
571
918
        if (flags & VJC_FLAG_W) {
572
            /* "The number sent for the window is also the difference between the current
573
            * and previous values. However, either positive or negative changes are
574
            * allowed since the window is a 16-bit field."
575
            */
576
135
            d_win = vjc_delta_int(subtree, hf_vjc_d_win, tvb, &offset);
577
135
        }
578
783
        else {
579
783
            d_win = 0;
580
783
        }
581
582
        /* The rest of the deltas can only be positive. */
583
918
        if (flags & VJC_FLAG_A) {
584
431
            d_ack = vjc_delta_uint(subtree, hf_vjc_d_ack, tvb, &offset);
585
431
        }
586
487
        else {
587
487
            d_ack = 0;
588
487
        }
589
590
918
        if (flags & VJC_FLAG_S) {
591
222
            d_seq = vjc_delta_uint(subtree, hf_vjc_d_seq, tvb, &offset);
592
222
        }
593
696
        else {
594
696
            d_seq = 0;
595
696
        }
596
918
    }
597
598
1.20k
    if (flags & VJC_FLAG_I) {
599
920
        d_ipid = vjc_delta_uint(subtree, hf_vjc_d_ipid, tvb, &offset);
600
920
    }
601
287
    else {
602
        /* "However, unlike the rest of the compressed fields, the assumed
603
         * change when I is clear is one, not zero." - section 3.2.2
604
         */
605
287
        d_ipid = 1;
606
287
        ti = proto_tree_add_uint(subtree, hf_vjc_d_ipid, tvb, offset, 0, d_ipid);
607
287
        proto_item_set_generated(ti);
608
287
    }
609
610
1.20k
    if (!(pinfo->p2p_dir == P2P_DIR_RECV || pinfo->p2p_dir == P2P_DIR_SENT)) {
611
        /* We can't make a proper conversation if we don't know the endpoints */
612
1.07k
        proto_tree_add_expert(subtree, pinfo, &ei_vjc_no_direction, tvb, offset,
613
1.07k
                tvb_captured_length_remaining(tvb, offset));
614
1.07k
        return tvb_captured_length(tvb);
615
1.07k
    }
616
132
    if (conv_id == NULL) {
617
0
        proto_tree_add_expert(subtree, pinfo, &ei_vjc_undecoded, tvb, offset,
618
0
                tvb_captured_length_remaining(tvb, offset));
619
0
        return tvb_captured_length(tvb);
620
0
    }
621
132
    if (last_data == NULL) {
622
33
        proto_tree_add_expert(subtree, pinfo, &ei_vjc_no_conv_data, tvb, offset,
623
33
                tvb_captured_length_remaining(tvb, offset));
624
33
        return tvb_captured_length(tvb);
625
33
    }
626
627
99
    if (!pinfo->fd->visited) {
628
        /* We haven't visited this packet before.
629
         * Form its vjc_hdr_t from the deltas and the info from the previous frame.
630
         */
631
99
        last_hdr = &last_data->vjc_headers;
632
633
99
        this_data = wmem_memdup(wmem_file_scope(), last_data, sizeof(vjc_conv_t));
634
99
        this_hdr = &this_data->vjc_headers;
635
99
        this_hdr->tcp_chksum = (uint16_t)tcp_chksum;
636
99
        this_hdr->urg = (uint16_t)urg;
637
99
        this_hdr->win = last_hdr->win + d_win;
638
99
        this_hdr->seq = last_hdr->seq + d_seq;
639
99
        this_hdr->ack = last_hdr->ack + d_ack;
640
99
        this_hdr->ip_id = last_hdr->ip_id + d_ipid;
641
99
        this_hdr->psh = (flags & VJC_FLAG_P) == VJC_FLAG_P;
642
99
        wmem_multimap_insert32(vjc_conv_table, conv_id, pinfo->num, (void *)this_data);
643
644
        // This frame is the next frame's last frame
645
99
        this_data->last_frame_len = tvb_reported_length_remaining(tvb, offset);
646
99
    }
647
0
    else {
648
        /* We have visited this packet before.
649
         * Get the values we saved the first time.
650
         */
651
0
        this_data = (vjc_conv_t *)wmem_multimap_lookup32(vjc_conv_table, conv_id, pinfo->num);
652
0
        DISSECTOR_ASSERT(this_data != NULL);
653
0
        this_hdr = &this_data->vjc_headers;
654
0
    }
655
99
    if (this_hdr != NULL) {
656
        /* this_data->frame_headers is our template packet header data.
657
         * Apply changes to it as needed.
658
         * The changes are intentionally done in the template before copying.
659
         */
660
99
        pkt_len = this_data->header_len + tvb_reported_length_remaining(tvb, offset);
661
662
99
        pdata = this_data->frame_headers; /* shorthand */
663
99
        ip_len = (pdata[0] & 0x0F) << 2;
664
665
        /* IP length */
666
99
        phton16(pdata + 2, pkt_len);
667
668
        /* IP ID */
669
99
        phton16(pdata + 4, this_hdr->ip_id);
670
671
        /* IP checksum */
672
99
        phton16(pdata + 10, 0x0000);
673
99
        ip_chksum = ip_checksum(pdata, ip_len);
674
99
        phton16(pdata + 10, g_htons(ip_chksum));
675
676
        /* TCP seq */
677
99
        phton32(pdata + ip_len + 4, this_hdr->seq);
678
679
        /* TCP ack */
680
99
        phton32(pdata + ip_len + 8, this_hdr->ack);
681
682
        /* TCP window */
683
99
        phton16(pdata + ip_len + 14, this_hdr->win);
684
685
        /* TCP push */
686
99
        if (this_hdr->psh) {
687
46
            pdata[ip_len + 13] |= 0x08;
688
46
        }
689
53
        else {
690
53
            pdata[ip_len + 13] &= ~0x08;
691
53
        }
692
693
        /* TCP checksum */
694
99
        phton16(pdata + ip_len + 16, this_hdr->tcp_chksum);
695
696
        /* TCP urg */
697
99
        if (this_hdr->urg) {
698
65
            pdata[ip_len + 13] |= 0x20;
699
65
            phton16(pdata + ip_len + 18, this_hdr->urg);
700
65
        }
701
34
        else {
702
34
            pdata[ip_len + 13] &= ~0x20;
703
34
            phton16(pdata + ip_len + 18, 0x0000);
704
34
        }
705
706
        /* Now that we're done manipulating the packet header, stick it into
707
         * a TVB for sub-dissectors to use.
708
         */
709
99
        sub_tvb = tvb_new_child_real_data(tvb, pdata,
710
99
                this_data->header_len, this_data->header_len);
711
99
        tvb_set_free_cb(sub_tvb, NULL);
712
713
        // Reuse pkt_len
714
99
        pkt_len = tvb_captured_length_remaining(tvb, offset);
715
99
        if (pkt_len > 0) {
716
99
            tcpip_tvb = tvb_new_composite();
717
99
            tvb_composite_append(tcpip_tvb, sub_tvb);
718
99
            tvb_composite_append(tcpip_tvb, tvb_new_subset_remaining(tvb, offset));
719
99
            tvb_composite_finalize(tcpip_tvb);
720
721
99
            ti = proto_tree_add_item(subtree, hf_vjc_tcpdata, tvb, offset, pkt_len, ENC_NA);
722
99
            proto_item_set_text(ti, "TCP data (%d byte%s)", pkt_len, plurality(pkt_len, "", "s"));
723
99
        }
724
0
        else
725
0
        {
726
0
            tcpip_tvb = sub_tvb;
727
0
        }
728
729
99
        add_new_data_source(pinfo, tcpip_tvb, "Decompressed TCP/IP data");
730
99
        return offset + call_dissector_with_data(ip_handle, tcpip_tvb, pinfo, tree, data);
731
99
    }
732
0
    else {
733
0
        proto_tree_add_expert_format(subtree, pinfo, &ei_vjc_error, tvb, 0, 0,
734
0
                "Dissector error: unable to find headers for current frame %d",
735
0
                pinfo->num);
736
0
    }
737
0
    return tvb_captured_length(tvb);
738
99
}
739
740
void
741
proto_register_vjc(void)
742
14
{
743
14
    static hf_register_info hf[] = {
744
14
        { &hf_vjc_comp,
745
14
            { "Is compressed", "vjc.compressed", FT_BOOLEAN, BASE_NONE,
746
14
                NULL, 0x0, NULL, HFILL }},
747
14
        { &hf_vjc_cnum,
748
14
            { "Connection number", "vjc.connection_number", FT_UINT8, BASE_DEC,
749
14
                NULL, 0x0, NULL, HFILL }},
750
14
        { &hf_vjc_change_mask,
751
14
            { "Change mask", "vjc.change_mask", FT_UINT8, BASE_HEX,
752
14
                NULL, 0x0, NULL, HFILL }},
753
14
        { &hf_vjc_change_mask_r,
754
14
            { "Reserved", "vjc.change_mask.reserved", FT_BOOLEAN, 8,
755
14
                TFS(&tfs_set_notset), VJC_FLAG_R, "Undefined bit", HFILL }},
756
14
        { &hf_vjc_change_mask_c,
757
14
            { "Connection number", "vjc.change_mask.connection_number", FT_BOOLEAN, 8,
758
14
                TFS(&tfs_set_notset), VJC_FLAG_C, "Whether connection number is present", HFILL }},
759
14
        { &hf_vjc_change_mask_i,
760
14
            { "IP ID", "vjc.change_mask.ip_id", FT_BOOLEAN, 8,
761
14
                TFS(&tfs_set_notset), VJC_FLAG_I, "Whether IP ID is present", HFILL }},
762
14
        { &hf_vjc_change_mask_p,
763
14
            { "TCP PSH", "vjc.change_mask.psh", FT_BOOLEAN, 8,
764
14
                TFS(&tfs_set_notset), VJC_FLAG_P, "Whether to set TCP PSH", HFILL }},
765
14
        { &hf_vjc_change_mask_s,
766
14
            { "TCP Sequence", "vjc.change_mask.seq", FT_BOOLEAN, 8,
767
14
                TFS(&tfs_set_notset), VJC_FLAG_S, "Whether TCP SEQ is present", HFILL }},
768
14
        { &hf_vjc_change_mask_a,
769
14
            { "TCP Acknowledgement", "vjc.change_mask.ack", FT_BOOLEAN, 8,
770
14
                TFS(&tfs_set_notset), VJC_FLAG_A, "Whether TCP ACK is present", HFILL }},
771
14
        { &hf_vjc_change_mask_w,
772
14
            { "TCP Window", "vjc.change_mask.win", FT_BOOLEAN, 8,
773
14
                TFS(&tfs_set_notset), VJC_FLAG_W, "Whether TCP Window is present", HFILL }},
774
14
        { &hf_vjc_change_mask_u,
775
14
            { "TCP Urgent", "vjc.change_mask.urg", FT_BOOLEAN, 8,
776
14
                TFS(&tfs_set_notset), VJC_FLAG_U, "Whether TCP URG pointer is present", HFILL }},
777
14
        { &hf_vjc_chksum,
778
14
            { "TCP Checksum", "vjc.checksum", FT_UINT16, BASE_HEX,
779
14
                NULL, 0x0, "TCP checksum of original packet", HFILL}},
780
14
        { &hf_vjc_urg,
781
14
            { "Urgent pointer", "vjc.urgent_pointer", FT_UINT16, BASE_DEC,
782
14
                NULL, 0x0, "TCP urgent pointer of original packet", HFILL}},
783
14
        { &hf_vjc_d_win,
784
14
            { "Delta window", "vjc.delta_window", FT_INT16, BASE_DEC,
785
14
                NULL, 0x0, "Change in TCP window size from previous packet", HFILL}},
786
14
        { &hf_vjc_d_ack,
787
14
            { "Delta ack", "vjc.delta_ack", FT_UINT16, BASE_DEC,
788
14
                NULL, 0x0, "Change in TCP acknowledgement number from previous packet", HFILL}},
789
14
        { &hf_vjc_d_seq,
790
14
            { "Delta seq", "vjc.delta_seq", FT_UINT16, BASE_DEC,
791
14
                NULL, 0x0, "Change in TCP sequence number from previous packet", HFILL}},
792
14
        { &hf_vjc_d_ipid,
793
14
            { "Delta IP ID", "vjc.delta_ipid", FT_UINT16, BASE_DEC,
794
14
                NULL, 0x0, "Change in IP Identification number from previous packet", HFILL}},
795
14
        { &hf_vjc_tcpdata,
796
14
            { "TCP data", "vjc.tcp_data", FT_BYTES, BASE_NONE,
797
14
                NULL, 0x0, "Original TCP payload", HFILL}},
798
14
    };
799
800
14
    static int *ett[] = {
801
14
        &ett_vjc,
802
14
        &ett_vjc_change_mask,
803
14
    };
804
805
14
    expert_module_t* expert_vjc;
806
14
    static ei_register_info ei[] = {
807
14
        { &ei_vjc_sawu,
808
14
            { "vjc.special.sawu", PI_PROTOCOL, PI_CHAT,
809
14
                ".... 1111 = special case for \"unidirectional data transfer\"", EXPFILL }},
810
14
        { &ei_vjc_swu,
811
14
            { "vjc.special.swu", PI_PROTOCOL, PI_CHAT,
812
14
                ".... 1011 = special case for \"echoed interactive traffic\"", EXPFILL }},
813
14
        { &ei_vjc_no_cnum,
814
14
            { "vjc.no_connection_id", PI_PROTOCOL, PI_WARN,
815
14
                "No connection ID and no prior connection (common at capture start)", EXPFILL }},
816
14
        { &ei_vjc_no_conversation,
817
14
            { "vjc.no_connection", PI_PROTOCOL, PI_WARN,
818
14
                "No saved connection found (common at capture start)", EXPFILL }},
819
14
        { &ei_vjc_no_direction,
820
14
            { "vjc.no_direction", PI_UNDECODED, PI_WARN,
821
14
                "Connection has no direction info, cannot decompress", EXPFILL }},
822
14
        { &ei_vjc_no_conv_data,
823
14
            { "vjc.no_connection_data", PI_UNDECODED, PI_WARN,
824
14
                "Could not find saved connection data", EXPFILL }},
825
14
        { &ei_vjc_undecoded,
826
14
            { "vjc.no_decompress", PI_UNDECODED, PI_WARN,
827
14
                "Undecoded data (impossible due to missing information)", EXPFILL }},
828
14
        { &ei_vjc_bad_data,
829
14
            { "vjc.bad_data", PI_PROTOCOL, PI_ERROR,
830
14
                "Non-compliant packet data", EXPFILL }},
831
14
        { &ei_vjc_error,
832
14
            { "vjc.error", PI_MALFORMED, PI_ERROR,
833
14
                "Unrecoverable dissector error", EXPFILL }},
834
14
    };
835
836
14
    proto_vjc = proto_register_protocol("Van Jacobson PPP compression", "VJC", "vjc");
837
14
    proto_register_field_array(proto_vjc, hf, array_length(hf));
838
14
    proto_register_subtree_array(ett, array_length(ett));
839
14
    expert_vjc = expert_register_protocol(proto_vjc);
840
14
    expert_register_field_array(expert_vjc, ei, array_length(ei));
841
14
    vjcc_handle = register_dissector("vjc_compressed", dissect_vjc_comp, proto_vjc);
842
14
    vjcu_handle = register_dissector("vjc_uncompressed", dissect_vjc_uncomp, proto_vjc);
843
844
    // TODO: is it possible to postpone allocating these until we actually see VJC?
845
    // It's probably not a common protocol.
846
14
    vjc_conn_id_lookup = wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(),
847
14
                                                    g_direct_hash, g_direct_equal);
848
14
    vjc_conv_table = wmem_multimap_new_autoreset(wmem_epan_scope(), wmem_file_scope(),
849
14
                                                    g_direct_hash, g_direct_equal);
850
851
14
    register_init_routine(&vjc_init_protocol);
852
14
    register_cleanup_routine(&vjc_cleanup_protocol);
853
14
}
854
855
void
856
proto_reg_handoff_vjc(void)
857
14
{
858
14
    ip_handle = find_dissector("ip");
859
860
14
    dissector_add_uint("ppp.protocol", PPP_VJC_COMP, vjcc_handle);
861
14
    dissector_add_uint("ppp.protocol", PPP_VJC_UNCOMP, vjcu_handle);
862
14
}
863
864
/*
865
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
866
 *
867
 * Local variables:
868
 * c-basic-offset: 4
869
 * tab-width: 8
870
 * indent-tabs-mode: nil
871
 * End:
872
 *
873
 * vi: set shiftwidth=4 tabstop=8 expandtab:
874
 * :indentSize=4:tabSize=8:noTabs=true:
875
 */