Coverage Report

Created: 2025-12-27 06:52

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