2 * Routines for decompression of PPP Van Jacobson compression
5 * Wireshark - Network traffic analyzer
6 * By Gerald Combs <gerald@wireshark.org>
8 * SPDX-License-Identifier: GPL-2.0-or-later
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
18 /* Currently hard-coded to assume TCP over IPv4.
19 * Nothing in the standard explicitly prevents an IPv6 implementation...
22 #define WS_LOG_DOMAIN "packet-vjc-comp"
24 #include <wireshark.h>
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>
33 #include <wsutil/pint.h>
34 #include <wsutil/str_util.h>
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.
40 #define CNUM_INVALID UINT16_MAX
41 static uint16_t last_cnum
= CNUM_INVALID
;
43 /* Location in an IPv4 packet of the IP Next Protocol field
44 * (which VJC replaces with the connection ID in uncompressed packets)
46 #define VJC_CONNID_OFFSET 9
48 /* Minimum TCP header length. We get compression data from the TCP header,
49 * and also store it for future use.
51 #define VJC_TCP_HDR_LEN 20
53 /* Structure for tracking the changeable parts of a packet header */
54 typedef struct vjc_hdr_s
{
64 /* The structure used in a wireshark "conversation"
65 * (though it's now in the multimap below)
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
;
74 /* Map of frame number and connection ID to vjc_conv_t */
75 static wmem_multimap_t
*vjc_conv_table
;
77 /* Store connection ID for compressed frames that lack it */
78 static wmem_map_t
*vjc_conn_id_lookup
;
80 static dissector_handle_t vjcu_handle
;
81 static dissector_handle_t vjcc_handle
;
82 static dissector_handle_t ip_handle
;
84 void proto_register_vjc(void);
85 void proto_reg_handoff_vjc(void);
90 static int ett_vjc_change_mask
;
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
;
102 #define VJC_FLAG_R 0x80
103 #define VJC_FLAG_C 0x40
104 #define VJC_FLAG_I 0x20
105 #define VJC_FLAG_P 0x10
106 #define VJC_FLAG_S 0x08
107 #define VJC_FLAG_A 0x04
108 #define VJC_FLAG_W 0x02
109 #define VJC_FLAG_U 0x01
111 #define VJC_FLAGS_SAWU 0x0F
112 #define VJC_FLAGS_SWU 0x0B
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
;
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
,
145 /* Initialization routine. Called at start of dissection.
146 * Registered in proto_register_vjc() below.
149 vjc_init_protocol(void)
151 last_cnum
= CNUM_INVALID
;
154 /* Cleanup routine. Called at close of file.
155 * Registered in proto_register_vjc() below.
158 vjc_cleanup_protocol(void)
160 last_cnum
= CNUM_INVALID
;
163 /* Generate a "conversation" key for a VJC connection.
164 * Returns NULL if it can't for some reason.
167 vjc_get_conv_key(packet_info
*pinfo
, uint32_t vjc_cnum
)
169 if (vjc_cnum
== CNUM_INVALID
)
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>
179 uint16_t ret_val
= (uint16_t)vjc_cnum
;
180 switch (pinfo
->p2p_dir
) {
190 return GUINT_TO_POINTER(ret_val
);
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.
198 vjc_delta_uint(proto_tree
*tree
, int hf
, tvbuff_t
*tvb
, unsigned *offset
)
201 if (0 != tvb_get_uint8(tvb
, *offset
)) {
202 proto_tree_add_item_ret_uint(tree
, hf
, tvb
, *offset
, 1,
203 ENC_BIG_ENDIAN
, &ret_val
);
208 proto_tree_add_item_ret_uint(tree
, hf
, tvb
, *offset
, 2,
209 ENC_BIG_ENDIAN
, &ret_val
);
215 /* Same thing but signed, since the TCP window delta can be negative */
217 vjc_delta_int(proto_tree
*tree
, int hf
, tvbuff_t
*tvb
, unsigned *offset
)
220 if (0 != tvb_get_int8(tvb
, *offset
)) {
221 proto_tree_add_item_ret_int(tree
, hf
, tvb
, *offset
, 1,
222 ENC_BIG_ENDIAN
, &ret_val
);
227 proto_tree_add_item_ret_int(tree
, hf
, tvb
, *offset
, 2,
228 ENC_BIG_ENDIAN
, &ret_val
);
234 /* Main dissection routine for uncompressed VJC packets.
235 * Registered in proto_reg_handoff_vjc() below.
238 dissect_vjc_uncomp(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void* data
)
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.
245 proto_tree
*subtree
= NULL
;
246 proto_item
*ti
= NULL
;
249 unsigned tcp_len
= 0;
250 uint32_t vjc_cnum
= 0;
251 tvbuff_t
*tcpip_tvb
= NULL
;
252 tvbuff_t
*sub_tvb
= NULL
;
253 void *conv_id
= NULL
;
254 vjc_conv_t
*pkt_data
= NULL
;
255 uint8_t *pdata
= NULL
;
256 static uint8_t real_proto
= IP_PROTO_TCP
;
258 ti
= proto_tree_add_item(tree
, proto_vjc
, tvb
, 0, -1, ENC_NA
);
259 subtree
= proto_item_add_subtree(ti
, ett_vjc
);
260 proto_item_set_text(subtree
, "PPP Van Jacobson uncompressed TCP/IP");
262 /* Start with some sanity checks */
263 if (tvb_captured_length(tvb
) < VJC_CONNID_OFFSET
+1) {
264 proto_tree_add_expert_format(subtree
, pinfo
, &ei_vjc_bad_data
, tvb
, 0, -1,
265 "Packet truncated before Connection ID field");
266 return tvb_captured_length(tvb
);
268 ip_ver
= (tvb_get_uint8(tvb
, 0) & 0xF0) >> 4;
269 ip_len
= (tvb_get_uint8(tvb
, 0) & 0x0F) << 2;
270 tcp_len
= ip_len
+ VJC_TCP_HDR_LEN
;
272 proto_tree_add_expert_format(subtree
, pinfo
, &ei_vjc_bad_data
, tvb
, 0, 1,
273 "IPv%d unsupported for VJC compression", ip_ver
);
274 return tvb_captured_length(tvb
);
277 /* So far so good, continue the dissection */
278 ti
= proto_tree_add_boolean(subtree
, hf_vjc_comp
, tvb
, 0, 0, false);
279 proto_item_set_generated(ti
);
281 proto_tree_add_item_ret_uint(subtree
, hf_vjc_cnum
, tvb
, VJC_CONNID_OFFSET
, 1,
282 ENC_BIG_ENDIAN
, &vjc_cnum
);
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.
288 sub_tvb
= tvb_new_child_real_data(tvb
, &real_proto
, 1, 1);
289 tvb_set_free_cb(sub_tvb
, NULL
);
291 tcpip_tvb
= tvb_new_composite();
292 tvb_composite_append(tcpip_tvb
, tvb_new_subset_length(tvb
, 0, VJC_CONNID_OFFSET
));
293 tvb_composite_append(tcpip_tvb
, sub_tvb
);
294 if (tvb_captured_length_remaining(tvb
, VJC_CONNID_OFFSET
+1) > 0) {
295 tvb_composite_append(tcpip_tvb
, tvb_new_subset_remaining(tvb
, VJC_CONNID_OFFSET
+1));
297 tvb_composite_finalize(tcpip_tvb
);
299 add_new_data_source(pinfo
, tcpip_tvb
, "Original TCP/IP data");
301 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 proto_tree_add_expert(subtree
, pinfo
, &ei_vjc_no_direction
, tvb
, 0, 0);
305 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.
309 proto_tree_add_expert_format(subtree
, pinfo
, &ei_vjc_bad_data
, tvb
, 0, -1,
310 "Packet truncated before end of TCP/IP headers");
312 else if (!pinfo
->fd
->visited
) {
313 /* If this is our first time visiting this packet, set things up for
314 * decompressing future packets.
316 last_cnum
= vjc_cnum
;
317 conv_id
= vjc_get_conv_key(pinfo
, vjc_cnum
);
318 DISSECTOR_ASSERT(conv_id
!= NULL
);
320 pkt_data
= wmem_new0(wmem_file_scope(), vjc_conv_t
);
321 pkt_data
->header_len
= tcp_len
;
323 pkt_data
->frame_headers
=
324 (uint8_t *)tvb_memdup(wmem_file_scope(), tcpip_tvb
, 0, tcp_len
);
326 // This value is used for re-calculating seq/ack numbers
327 pkt_data
->last_frame_len
= tvb_reported_length(tvb
) - ip_len
;
329 pkt_data
->vjc_headers
.ip_id
= pntoh16(pdata
+ 4);
330 pkt_data
->vjc_headers
.seq
= pntoh32(pdata
+ ip_len
+ 4);
331 pkt_data
->vjc_headers
.ack
= pntoh32(pdata
+ ip_len
+ 8);
332 pkt_data
->vjc_headers
.psh
= (pdata
[ip_len
+ 13] & 0x08) == 0x08;
333 pkt_data
->vjc_headers
.win
= pntoh16(pdata
+ ip_len
+ 14);
334 pkt_data
->vjc_headers
.tcp_chksum
= pntoh16(pdata
+ ip_len
+ 16);
335 pkt_data
->vjc_headers
.urg
= pntoh16(pdata
+ ip_len
+ 18);
337 wmem_multimap_insert32(vjc_conv_table
, conv_id
, pinfo
->num
, (void *)pkt_data
);
340 /* We've already visited this packet, we should have all the info we need. */
343 return call_dissector_with_data(ip_handle
, tcpip_tvb
, pinfo
, tree
, data
);
346 /* Main dissection routine for compressed VJC packets.
347 * Registered in proto_reg_handoff_vjc() below.
350 dissect_vjc_comp(tvbuff_t
*tvb
, packet_info
*pinfo _U_
, proto_tree
*tree
, void* data _U_
)
352 /* A Van Jacobson compressed packet contains a change mask, which indicates
353 * possible fields that may be present.
355 proto_tree
*subtree
= NULL
;
356 proto_item
*ti
= NULL
;
357 unsigned hdr_len
= 3; // See below
358 bool hdr_error
= false;
360 unsigned pkt_len
= 0;
368 uint32_t ip_chksum
= 0;
369 uint32_t tcp_chksum
= 0;
370 uint32_t vjc_cnum
= 0;
371 void *conv_id
= NULL
;
372 vjc_hdr_t
*this_hdr
= NULL
;
373 vjc_hdr_t
*last_hdr
= NULL
;
374 vjc_conv_t
*last_data
= NULL
;
375 vjc_conv_t
*this_data
= NULL
;
376 uint8_t *pdata
= NULL
;
377 tvbuff_t
*tcpip_tvb
= NULL
;
378 tvbuff_t
*sub_tvb
= NULL
;
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.
385 #define TEST_HDR_LEN \
386 if (tvb_captured_length(tvb)< hdr_len) { hdr_error = true; goto done_header_len; }
389 flags
= tvb_get_uint8(tvb
, offset
);
390 if (flags
& VJC_FLAG_C
) {
391 // have connection number
395 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.
401 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.
407 /* Not a special case, determine the header size by
408 * testing the SAWU flags individually.
410 if (flags
& VJC_FLAG_U
) {
411 // have urgent pointer
415 if (flags
& VJC_FLAG_W
) {
417 if (0 == tvb_get_int8(tvb
, offset
+ hdr_len
))
423 if (flags
& VJC_FLAG_A
) {
425 if (0 == tvb_get_uint8(tvb
, offset
+ hdr_len
))
431 if (flags
& VJC_FLAG_S
) {
433 if (0 == tvb_get_uint8(tvb
, offset
+ hdr_len
))
440 if (flags
& VJC_FLAG_I
) {
442 if (0 == tvb_get_uint8(tvb
, offset
+ hdr_len
))
449 /* Now that we have the header length, use it when assigning the
454 ti
= proto_tree_add_item(tree
, proto_vjc
, tvb
, 0,
455 MIN(hdr_len
, tvb_captured_length(tvb
)), ENC_NA
);
456 subtree
= proto_item_add_subtree(ti
, ett_vjc
);
457 proto_item_set_text(subtree
, "PPP Van Jacobson compressed TCP/IP");
459 proto_tree_add_expert_format(subtree
, pinfo
, &ei_vjc_bad_data
, tvb
, 0, -1,
460 "Packet truncated, compression header incomplete");
461 return tvb_captured_length(tvb
);
464 ti
= proto_tree_add_boolean(subtree
, hf_vjc_comp
, tvb
, 0, 0, true);
465 proto_item_set_generated(ti
);
467 ti
= proto_tree_add_bitmask(subtree
, tvb
, 0, hf_vjc_change_mask
,
468 ett_vjc_change_mask
, vjc_change_mask_fields
, ENC_NA
);
469 if ((flags
& VJC_FLAGS_SAWU
) == VJC_FLAGS_SAWU
) {
470 proto_tree_add_expert(ti
, pinfo
, &ei_vjc_sawu
, tvb
, 0, 1);
472 else if ((flags
& VJC_FLAGS_SAWU
) == VJC_FLAGS_SWU
) {
473 proto_tree_add_expert(ti
, pinfo
, &ei_vjc_swu
, tvb
, 0, 1);
478 if (flags
& VJC_FLAG_C
) {
479 proto_tree_add_item_ret_uint(subtree
, hf_vjc_cnum
, tvb
, offset
, 1,
480 ENC_BIG_ENDIAN
, &vjc_cnum
);
481 last_cnum
= vjc_cnum
;
485 /* "If C is clear, the connection is assumed to be the same as for the
486 * last compressed or uncompressed packet."
488 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.
494 vjc_cnum
= last_cnum
;
495 wmem_map_insert(vjc_conn_id_lookup
, GUINT_TO_POINTER(pinfo
->num
), GUINT_TO_POINTER(vjc_cnum
));
498 if (wmem_map_contains(vjc_conn_id_lookup
, GUINT_TO_POINTER(pinfo
->num
))) {
499 vjc_cnum
= GPOINTER_TO_UINT(wmem_map_lookup(vjc_conn_id_lookup
,
500 GUINT_TO_POINTER(pinfo
->num
)));
503 vjc_cnum
= CNUM_INVALID
;
506 if (vjc_cnum
!= CNUM_INVALID
) {
507 ti
= proto_tree_add_uint(subtree
, hf_vjc_cnum
, tvb
, offset
, 0, vjc_cnum
);
508 proto_item_set_generated(ti
);
511 proto_tree_add_expert(subtree
, pinfo
, &ei_vjc_no_cnum
, tvb
, 0, 0);
514 conv_id
= vjc_get_conv_key(pinfo
, vjc_cnum
);
515 if (conv_id
!= NULL
) {
516 // Get the most recent headers from *before* this packet
517 last_data
= (vjc_conv_t
*)wmem_multimap_lookup32_le(vjc_conv_table
, conv_id
, pinfo
->num
- 1);
519 if (last_data
== NULL
) {
520 proto_tree_add_expert(subtree
, pinfo
, &ei_vjc_no_conversation
,
521 tvb
, 1, (flags
& VJC_FLAG_C
) ? 1 : 0);
524 proto_tree_add_item_ret_uint(subtree
, hf_vjc_chksum
, tvb
, offset
, 2,
525 ENC_BIG_ENDIAN
, &tcp_chksum
);
528 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.
532 flags
&= ~VJC_FLAGS_SAWU
;
534 if (last_data
!= NULL
) {
535 d_seq
= last_data
->last_frame_len
;
536 ti
= proto_tree_add_uint(subtree
, hf_vjc_d_ack
, tvb
, offset
, 0, d_ack
);
537 proto_item_set_generated(ti
);
538 ti
= proto_tree_add_uint(subtree
, hf_vjc_d_seq
, tvb
, offset
, 0, d_seq
);
539 proto_item_set_generated(ti
);
542 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
547 flags
&= ~VJC_FLAGS_SAWU
;
548 if (last_data
!= NULL
) {
549 d_seq
= d_ack
= last_data
->last_frame_len
;
550 ti
= proto_tree_add_uint(subtree
, hf_vjc_d_ack
, tvb
, offset
, 0, d_ack
);
551 proto_item_set_generated(ti
);
552 ti
= proto_tree_add_uint(subtree
, hf_vjc_d_seq
, tvb
, offset
, 0, d_seq
);
553 proto_item_set_generated(ti
);
557 /* Not a special case, read the SAWU flags individually */
559 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.
563 proto_tree_add_item_ret_uint(subtree
, hf_vjc_urg
, tvb
, offset
, 2,
564 ENC_BIG_ENDIAN
, &urg
);
571 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."
576 d_win
= vjc_delta_int(subtree
, hf_vjc_d_win
, tvb
, &offset
);
582 /* The rest of the deltas can only be positive. */
583 if (flags
& VJC_FLAG_A
) {
584 d_ack
= vjc_delta_uint(subtree
, hf_vjc_d_ack
, tvb
, &offset
);
590 if (flags
& VJC_FLAG_S
) {
591 d_seq
= vjc_delta_uint(subtree
, hf_vjc_d_seq
, tvb
, &offset
);
598 if (flags
& VJC_FLAG_I
) {
599 d_ipid
= vjc_delta_uint(subtree
, hf_vjc_d_ipid
, tvb
, &offset
);
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
606 ti
= proto_tree_add_uint(subtree
, hf_vjc_d_ipid
, tvb
, offset
, 0, d_ipid
);
607 proto_item_set_generated(ti
);
610 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 proto_tree_add_expert(subtree
, pinfo
, &ei_vjc_no_direction
, tvb
, offset
,
613 tvb_captured_length_remaining(tvb
, offset
));
614 return tvb_captured_length(tvb
);
616 if (conv_id
== NULL
) {
617 proto_tree_add_expert(subtree
, pinfo
, &ei_vjc_undecoded
, tvb
, offset
,
618 tvb_captured_length_remaining(tvb
, offset
));
619 return tvb_captured_length(tvb
);
621 if (last_data
== NULL
) {
622 proto_tree_add_expert(subtree
, pinfo
, &ei_vjc_no_conv_data
, tvb
, offset
,
623 tvb_captured_length_remaining(tvb
, offset
));
624 return tvb_captured_length(tvb
);
627 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.
631 last_hdr
= &last_data
->vjc_headers
;
633 this_data
= wmem_memdup(wmem_file_scope(), last_data
, sizeof(vjc_conv_t
));
634 this_hdr
= &this_data
->vjc_headers
;
635 this_hdr
->tcp_chksum
= (uint16_t)tcp_chksum
;
636 this_hdr
->urg
= (uint16_t)urg
;
637 this_hdr
->win
= last_hdr
->win
+ d_win
;
638 this_hdr
->seq
= last_hdr
->seq
+ d_seq
;
639 this_hdr
->ack
= last_hdr
->ack
+ d_ack
;
640 this_hdr
->ip_id
= last_hdr
->ip_id
+ d_ipid
;
641 this_hdr
->psh
= (flags
& VJC_FLAG_P
) == VJC_FLAG_P
;
642 wmem_multimap_insert32(vjc_conv_table
, conv_id
, pinfo
->num
, (void *)this_data
);
644 // This frame is the next frame's last frame
645 this_data
->last_frame_len
= tvb_reported_length_remaining(tvb
, offset
);
648 /* We have visited this packet before.
649 * Get the values we saved the first time.
651 this_data
= (vjc_conv_t
*)wmem_multimap_lookup32(vjc_conv_table
, conv_id
, pinfo
->num
);
652 DISSECTOR_ASSERT(this_data
!= NULL
);
653 this_hdr
= &this_data
->vjc_headers
;
655 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.
660 pkt_len
= this_data
->header_len
+ tvb_reported_length_remaining(tvb
, offset
);
662 pdata
= this_data
->frame_headers
; /* shorthand */
663 ip_len
= (pdata
[0] & 0x0F) << 2;
666 phton16(pdata
+ 2, pkt_len
);
669 phton16(pdata
+ 4, this_hdr
->ip_id
);
672 phton16(pdata
+ 10, 0x0000);
673 ip_chksum
= ip_checksum(pdata
, ip_len
);
674 phton16(pdata
+ 10, g_htons(ip_chksum
));
677 phton32(pdata
+ ip_len
+ 4, this_hdr
->seq
);
680 phton32(pdata
+ ip_len
+ 8, this_hdr
->ack
);
683 phton16(pdata
+ ip_len
+ 14, this_hdr
->win
);
687 pdata
[ip_len
+ 13] |= 0x08;
690 pdata
[ip_len
+ 13] &= ~0x08;
694 phton16(pdata
+ ip_len
+ 16, this_hdr
->tcp_chksum
);
698 pdata
[ip_len
+ 13] |= 0x20;
699 phton16(pdata
+ ip_len
+ 18, this_hdr
->urg
);
702 pdata
[ip_len
+ 13] &= ~0x20;
703 phton16(pdata
+ ip_len
+ 18, 0x0000);
706 /* Now that we're done manipulating the packet header, stick it into
707 * a TVB for sub-dissectors to use.
709 sub_tvb
= tvb_new_child_real_data(tvb
, pdata
,
710 this_data
->header_len
, this_data
->header_len
);
711 tvb_set_free_cb(sub_tvb
, NULL
);
714 pkt_len
= tvb_captured_length_remaining(tvb
, offset
);
716 tcpip_tvb
= tvb_new_composite();
717 tvb_composite_append(tcpip_tvb
, sub_tvb
);
718 tvb_composite_append(tcpip_tvb
, tvb_new_subset_remaining(tvb
, offset
));
719 tvb_composite_finalize(tcpip_tvb
);
721 ti
= proto_tree_add_item(subtree
, hf_vjc_tcpdata
, tvb
, offset
, pkt_len
, ENC_NA
);
722 proto_item_set_text(ti
, "TCP data (%d byte%s)", pkt_len
, plurality(pkt_len
, "", "s"));
729 add_new_data_source(pinfo
, tcpip_tvb
, "Decompressed TCP/IP data");
730 return offset
+ call_dissector_with_data(ip_handle
, tcpip_tvb
, pinfo
, tree
, data
);
733 proto_tree_add_expert_format(subtree
, pinfo
, &ei_vjc_error
, tvb
, 0, 0,
734 "Dissector error: unable to find headers for current frame %d",
737 return tvb_captured_length(tvb
);
741 proto_register_vjc(void)
743 static hf_register_info hf
[] = {
745 { "Is compressed", "vjc.compressed", FT_BOOLEAN
, BASE_NONE
,
746 NULL
, 0x0, NULL
, HFILL
}},
748 { "Connection number", "vjc.connection_number", FT_UINT8
, BASE_DEC
,
749 NULL
, 0x0, NULL
, HFILL
}},
750 { &hf_vjc_change_mask
,
751 { "Change mask", "vjc.change_mask", FT_UINT8
, BASE_HEX
,
752 NULL
, 0x0, NULL
, HFILL
}},
753 { &hf_vjc_change_mask_r
,
754 { "Reserved", "vjc.change_mask.reserved", FT_BOOLEAN
, 8,
755 TFS(&tfs_set_notset
), VJC_FLAG_R
, "Undefined bit", HFILL
}},
756 { &hf_vjc_change_mask_c
,
757 { "Connection number", "vjc.change_mask.connection_number", FT_BOOLEAN
, 8,
758 TFS(&tfs_set_notset
), VJC_FLAG_C
, "Whether connection number is present", HFILL
}},
759 { &hf_vjc_change_mask_i
,
760 { "IP ID", "vjc.change_mask.ip_id", FT_BOOLEAN
, 8,
761 TFS(&tfs_set_notset
), VJC_FLAG_I
, "Whether IP ID is present", HFILL
}},
762 { &hf_vjc_change_mask_p
,
763 { "TCP PSH", "vjc.change_mask.psh", FT_BOOLEAN
, 8,
764 TFS(&tfs_set_notset
), VJC_FLAG_P
, "Whether to set TCP PSH", HFILL
}},
765 { &hf_vjc_change_mask_s
,
766 { "TCP Sequence", "vjc.change_mask.seq", FT_BOOLEAN
, 8,
767 TFS(&tfs_set_notset
), VJC_FLAG_S
, "Whether TCP SEQ is present", HFILL
}},
768 { &hf_vjc_change_mask_a
,
769 { "TCP Acknowledgement", "vjc.change_mask.ack", FT_BOOLEAN
, 8,
770 TFS(&tfs_set_notset
), VJC_FLAG_A
, "Whether TCP ACK is present", HFILL
}},
771 { &hf_vjc_change_mask_w
,
772 { "TCP Window", "vjc.change_mask.win", FT_BOOLEAN
, 8,
773 TFS(&tfs_set_notset
), VJC_FLAG_W
, "Whether TCP Window is present", HFILL
}},
774 { &hf_vjc_change_mask_u
,
775 { "TCP Urgent", "vjc.change_mask.urg", FT_BOOLEAN
, 8,
776 TFS(&tfs_set_notset
), VJC_FLAG_U
, "Whether TCP URG pointer is present", HFILL
}},
778 { "TCP Checksum", "vjc.checksum", FT_UINT16
, BASE_HEX
,
779 NULL
, 0x0, "TCP checksum of original packet", HFILL
}},
781 { "Urgent pointer", "vjc.urgent_pointer", FT_UINT16
, BASE_DEC
,
782 NULL
, 0x0, "TCP urgent pointer of original packet", HFILL
}},
784 { "Delta window", "vjc.delta_window", FT_INT16
, BASE_DEC
,
785 NULL
, 0x0, "Change in TCP window size from previous packet", HFILL
}},
787 { "Delta ack", "vjc.delta_ack", FT_UINT16
, BASE_DEC
,
788 NULL
, 0x0, "Change in TCP acknowledgement number from previous packet", HFILL
}},
790 { "Delta seq", "vjc.delta_seq", FT_UINT16
, BASE_DEC
,
791 NULL
, 0x0, "Change in TCP sequence number from previous packet", HFILL
}},
793 { "Delta IP ID", "vjc.delta_ipid", FT_UINT16
, BASE_DEC
,
794 NULL
, 0x0, "Change in IP Identification number from previous packet", HFILL
}},
796 { "TCP data", "vjc.tcp_data", FT_BYTES
, BASE_NONE
,
797 NULL
, 0x0, "Original TCP payload", HFILL
}},
800 static int *ett
[] = {
802 &ett_vjc_change_mask
,
805 expert_module_t
* expert_vjc
;
806 static ei_register_info ei
[] = {
808 { "vjc.special.sawu", PI_PROTOCOL
, PI_CHAT
,
809 ".... 1111 = special case for \"unidirectional data transfer\"", EXPFILL
}},
811 { "vjc.special.swu", PI_PROTOCOL
, PI_CHAT
,
812 ".... 1011 = special case for \"echoed interactive traffic\"", EXPFILL
}},
814 { "vjc.no_connection_id", PI_PROTOCOL
, PI_WARN
,
815 "No connection ID and no prior connection (common at capture start)", EXPFILL
}},
816 { &ei_vjc_no_conversation
,
817 { "vjc.no_connection", PI_PROTOCOL
, PI_WARN
,
818 "No saved connection found (common at capture start)", EXPFILL
}},
819 { &ei_vjc_no_direction
,
820 { "vjc.no_direction", PI_UNDECODED
, PI_WARN
,
821 "Connection has no direction info, cannot decompress", EXPFILL
}},
822 { &ei_vjc_no_conv_data
,
823 { "vjc.no_connection_data", PI_UNDECODED
, PI_WARN
,
824 "Could not find saved connection data", EXPFILL
}},
826 { "vjc.no_decompress", PI_UNDECODED
, PI_WARN
,
827 "Undecoded data (impossible due to missing information)", EXPFILL
}},
829 { "vjc.bad_data", PI_PROTOCOL
, PI_ERROR
,
830 "Non-compliant packet data", EXPFILL
}},
832 { "vjc.error", PI_MALFORMED
, PI_ERROR
,
833 "Unrecoverable dissector error", EXPFILL
}},
836 proto_vjc
= proto_register_protocol("Van Jacobson PPP compression", "VJC", "vjc");
837 proto_register_field_array(proto_vjc
, hf
, array_length(hf
));
838 proto_register_subtree_array(ett
, array_length(ett
));
839 expert_vjc
= expert_register_protocol(proto_vjc
);
840 expert_register_field_array(expert_vjc
, ei
, array_length(ei
));
841 vjcc_handle
= register_dissector("vjc_compressed", dissect_vjc_comp
, proto_vjc
);
842 vjcu_handle
= register_dissector("vjc_uncompressed", dissect_vjc_uncomp
, proto_vjc
);
844 // TODO: is it possible to postpone allocating these until we actually see VJC?
845 // It's probably not a common protocol.
846 vjc_conn_id_lookup
= wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(),
847 g_direct_hash
, g_direct_equal
);
848 vjc_conv_table
= wmem_multimap_new_autoreset(wmem_epan_scope(), wmem_file_scope(),
849 g_direct_hash
, g_direct_equal
);
851 register_init_routine(&vjc_init_protocol
);
852 register_cleanup_routine(&vjc_cleanup_protocol
);
856 proto_reg_handoff_vjc(void)
858 ip_handle
= find_dissector("ip");
860 dissector_add_uint("ppp.protocol", PPP_VJC_COMP
, vjcc_handle
);
861 dissector_add_uint("ppp.protocol", PPP_VJC_UNCOMP
, vjcu_handle
);
865 * Editor modelines - https://www.wireshark.org/tools/modelines.html
870 * indent-tabs-mode: nil
873 * vi: set shiftwidth=4 tabstop=8 expandtab:
874 * :indentSize=4:tabSize=8:noTabs=true: