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...
25 #include <epan/tvbuff.h>
26 #include <epan/conversation.h>
27 #include <epan/in_cksum.h>
28 #include <epan/proto.h>
29 #include <epan/expert.h>
30 #include <epan/packet.h>
31 #include <epan/ipproto.h>
32 #include <epan/ppptypes.h>
34 #include <wsutil/array.h>
35 #include <wsutil/str_util.h>
37 /* Shorthand macros for reading/writing 16/32 bit values from
38 * possibly-unaligned indexes into a uint8_t[]
40 #define GET_16(p,i) (uint16_t)(((p)[(i)] << 8) | ((p)[(i)+1]))
41 #define GET_32(p,i) (uint32_t)(((p)[(i)] << 24) | ((p)[(i)+1] << 16) | ((p)[(i)+2] << 8) | ((p)[(i)+3]))
42 #define PUT_16(p,i,v) G_STMT_START { \
43 (p)[(i)] = ((v) & 0xFF00) >> 8; \
44 (p)[(i)+1] = ((v) & 0x00FF); \
46 #define PUT_32(p,i,v) G_STMT_START { \
47 (p)[(i)] = ((v) & 0xFF000000) >> 24; \
48 (p)[(i)+1] = ((v) & 0x00FF0000) >> 16; \
49 (p)[(i)+2] = ((v) & 0x0000FF00) >> 8; \
50 (p)[(i)+3] = ((v) & 0x000000FF); \
53 /* Store the last connection number we've seen.
54 * Only used on the first pass, in case the connection number itself
55 * gets compressed out.
57 #define CNUM_INVALID UINT16_MAX
58 static uint16_t last_cnum
= CNUM_INVALID
;
60 /* Location in an IPv4 packet of the IP Next Protocol field
61 * (which VJC replaces with the connection ID in uncompressed packets)
63 #define VJC_CONNID_OFFSET 9
65 /* Minimum TCP header length. We get compression data from the TCP header,
66 * and also store it for future use.
68 #define VJC_TCP_HDR_LEN 20
70 /* Structure for tracking the changeable parts of a packet header */
71 typedef struct vjc_hdr_s
{
81 /* The structure used in a wireshark "conversation" */
82 typedef struct vjc_conv_s
{
83 uint32_t last_frame
; // On first pass, where to get the previous info
84 uint32_t last_frame_len
; // On first pass, length of prev. frame (for SAWU/SWU)
85 uint8_t *frame_headers
; // Full copy of the IP header
86 uint8_t header_len
; // Length of the stored IP header
87 wmem_map_t
*vals
; // Hash of frame_number => vjc_hdr_t*
90 static dissector_handle_t vjcu_handle
;
91 static dissector_handle_t vjcc_handle
;
92 static dissector_handle_t ip_handle
;
94 void proto_register_vjc(void);
95 void proto_reg_handoff_vjc(void);
100 static int ett_vjc_change_mask
;
102 static expert_field ei_vjc_sawu
;
103 static expert_field ei_vjc_swu
;
104 static expert_field ei_vjc_no_cnum
;
105 static expert_field ei_vjc_no_conversation
;
106 static expert_field ei_vjc_no_direction
;
107 static expert_field ei_vjc_no_conv_data
;
108 static expert_field ei_vjc_undecoded
;
109 static expert_field ei_vjc_bad_data
;
110 static expert_field ei_vjc_error
;
112 #define VJC_FLAG_R 0x80
113 #define VJC_FLAG_C 0x40
114 #define VJC_FLAG_I 0x20
115 #define VJC_FLAG_P 0x10
116 #define VJC_FLAG_S 0x08
117 #define VJC_FLAG_A 0x04
118 #define VJC_FLAG_W 0x02
119 #define VJC_FLAG_U 0x01
121 #define VJC_FLAGS_SAWU 0x0F
122 #define VJC_FLAGS_SWU 0x0B
124 static int hf_vjc_comp
;
125 static int hf_vjc_cnum
;
126 static int hf_vjc_change_mask
;
127 static int hf_vjc_change_mask_r
;
128 static int hf_vjc_change_mask_c
;
129 static int hf_vjc_change_mask_i
;
130 static int hf_vjc_change_mask_p
;
131 static int hf_vjc_change_mask_s
;
132 static int hf_vjc_change_mask_a
;
133 static int hf_vjc_change_mask_w
;
134 static int hf_vjc_change_mask_u
;
135 static int hf_vjc_chksum
;
136 static int hf_vjc_urg
;
137 static int hf_vjc_d_win
;
138 static int hf_vjc_d_ack
;
139 static int hf_vjc_d_seq
;
140 static int hf_vjc_d_ipid
;
141 static int hf_vjc_tcpdata
;
143 static int * const vjc_change_mask_fields
[] = {
144 &hf_vjc_change_mask_r
,
145 &hf_vjc_change_mask_c
,
146 &hf_vjc_change_mask_i
,
147 &hf_vjc_change_mask_p
,
148 &hf_vjc_change_mask_s
,
149 &hf_vjc_change_mask_a
,
150 &hf_vjc_change_mask_w
,
151 &hf_vjc_change_mask_u
,
155 /* Initialization routine. Called at start of dissection.
156 * Registered in proto_register_vjc() below.
159 vjc_init_protocol(void)
161 last_cnum
= CNUM_INVALID
;
164 /* Cleanup routine. Called at close of file.
165 * Registered in proto_register_vjc() below.
168 vjc_cleanup_protocol(void)
170 last_cnum
= CNUM_INVALID
;
173 /* Find (or optionally create) a VJC conversation. */
174 static conversation_t
*
175 vjc_find_conversation(packet_info
*pinfo
, uint32_t vjc_cnum
, bool create
)
177 /* PPP gives us almost nothing to hook a conversation on; just whether
178 * the packet is considered to be P2P_DIR_RECV or P2P_DIR_SENT.
179 * Ideally we should also be distinguishing conversations based on the
180 * capture interface, VLAN ID, MPLS tags, etc., etc. but that's beyond
181 * the scope of this dissector, and a perennial problem in Wireshark anyway.
182 * See <https://gitlab.com/wireshark/wireshark/-/issues/4561>
184 conversation_t
*conv
= (conversation_t
*)NULL
;
185 switch (pinfo
->p2p_dir
) {
196 conv
= find_conversation_by_id(pinfo
->num
, CONVERSATION_NONE
, vjc_cnum
);
197 if (!conv
&& create
) {
198 conv
= conversation_new_by_id(pinfo
->num
, CONVERSATION_NONE
, vjc_cnum
);
204 /* RFC 1144 section 3.2.2 says that "deltas" are sent for many values in the
205 * header. If the initial byte is 0, that means the following 2 bytes are the
206 * 16-bit value of the delta. Otherwise, the initial byte is the 8-bit value.
209 vjc_delta_uint(proto_tree
*tree
, int hf
, tvbuff_t
*tvb
, unsigned *offset
)
212 if (0 != tvb_get_uint8(tvb
, *offset
)) {
213 proto_tree_add_item_ret_uint(tree
, hf
, tvb
, *offset
, 1,
214 ENC_BIG_ENDIAN
, &ret_val
);
219 proto_tree_add_item_ret_uint(tree
, hf
, tvb
, *offset
, 2,
220 ENC_BIG_ENDIAN
, &ret_val
);
226 /* Same thing but signed, since the TCP window delta can be negative */
228 vjc_delta_int(proto_tree
*tree
, int hf
, tvbuff_t
*tvb
, unsigned *offset
)
231 if (0 != tvb_get_int8(tvb
, *offset
)) {
232 proto_tree_add_item_ret_int(tree
, hf
, tvb
, *offset
, 1,
233 ENC_BIG_ENDIAN
, &ret_val
);
238 proto_tree_add_item_ret_int(tree
, hf
, tvb
, *offset
, 2,
239 ENC_BIG_ENDIAN
, &ret_val
);
245 /* Main dissection routine for uncompressed VJC packets.
246 * Registered in proto_reg_handoff_vjc() below.
249 dissect_vjc_uncomp(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void* data
)
251 /* A Van Jacobson uncompressed packet contains a standard TCP/IP header, with
252 * the IP next protocol ID replaced with the connection number.
253 * It's meant to signify a new TCP connection, or refresh an existing one,
254 * which will have subsequent compressed packets.
256 proto_tree
*subtree
= NULL
;
257 proto_item
*ti
= NULL
;
260 unsigned tcp_len
= 0;
261 uint32_t vjc_cnum
= 0;
262 tvbuff_t
*tcpip_tvb
= NULL
;
263 tvbuff_t
*sub_tvb
= NULL
;
264 conversation_t
*conv
= NULL
;
265 vjc_hdr_t
*this_hdr
= NULL
;
266 vjc_conv_t
*pkt_data
= NULL
;
267 uint8_t *pdata
= NULL
;
268 static uint8_t real_proto
= IP_PROTO_TCP
;
270 ti
= proto_tree_add_item(tree
, proto_vjc
, tvb
, 0, -1, ENC_NA
);
271 subtree
= proto_item_add_subtree(ti
, ett_vjc
);
272 proto_item_set_text(subtree
, "PPP Van Jacobson uncompressed TCP/IP");
274 /* Start with some sanity checks */
275 if (VJC_CONNID_OFFSET
+1 > tvb_captured_length(tvb
)) {
276 proto_tree_add_expert_format(subtree
, pinfo
, &ei_vjc_bad_data
, tvb
, 0, -1,
277 "Packet truncated before Connection ID field");
278 return tvb_captured_length(tvb
);
280 ip_ver
= (tvb_get_uint8(tvb
, 0) & 0xF0) >> 4;
281 ip_len
= (tvb_get_uint8(tvb
, 0) & 0x0F) << 2;
282 tcp_len
= ip_len
+ VJC_TCP_HDR_LEN
;
284 proto_tree_add_expert_format(subtree
, pinfo
, &ei_vjc_bad_data
, tvb
, 0, 1,
285 "IPv%d unsupported for VJC compression", ip_ver
);
286 return tvb_captured_length(tvb
);
289 /* So far so good, continue the dissection */
290 ti
= proto_tree_add_boolean(subtree
, hf_vjc_comp
, tvb
, 0, 0, false);
291 proto_item_set_generated(ti
);
293 proto_tree_add_item_ret_uint(subtree
, hf_vjc_cnum
, tvb
, VJC_CONNID_OFFSET
, 1,
294 ENC_BIG_ENDIAN
, &vjc_cnum
);
296 /* Build a composite TVB containing the original TCP/IP data.
297 * This is easy for uncompressed VJC packets because only one byte
298 * is different from the on-the-wire data.
300 sub_tvb
= tvb_new_child_real_data(tvb
, &real_proto
, 1, 1);
301 tvb_set_free_cb(sub_tvb
, NULL
);
303 tcpip_tvb
= tvb_new_composite();
304 tvb_composite_append(tcpip_tvb
, tvb_new_subset_length(tvb
, 0, VJC_CONNID_OFFSET
));
305 tvb_composite_append(tcpip_tvb
, sub_tvb
);
306 if (0 < tvb_captured_length_remaining(tvb
, VJC_CONNID_OFFSET
+1)) {
307 tvb_composite_append(tcpip_tvb
, tvb_new_subset_length(tvb
, VJC_CONNID_OFFSET
+1, -1));
309 tvb_composite_finalize(tcpip_tvb
);
311 add_new_data_source(pinfo
, tcpip_tvb
, "Original TCP/IP data");
313 if (!(pinfo
->p2p_dir
== P2P_DIR_RECV
|| pinfo
->p2p_dir
== P2P_DIR_SENT
)) {
314 /* We can't make a proper conversation if we don't know the endpoints */
315 proto_tree_add_expert(subtree
, pinfo
, &ei_vjc_no_direction
, tvb
, 0, 0);
317 else if (tcp_len
> tvb_captured_length(tvb
)) {
318 /* Not enough data. We can still pass this packet onward (though probably
319 * to no benefit), but can't base future decompression off of it.
321 proto_tree_add_expert_format(subtree
, pinfo
, &ei_vjc_bad_data
, tvb
, 0, -1,
322 "Packet truncated before end of TCP/IP headers");
324 else if (!pinfo
->fd
->visited
) {
325 /* If this is our first time visiting this packet, set things up for
326 * decompressing future packets.
328 last_cnum
= vjc_cnum
;
329 conv
= vjc_find_conversation(pinfo
, vjc_cnum
, true);
330 pkt_data
= (vjc_conv_t
*)conversation_get_proto_data(conv
, proto_vjc
);
331 if (NULL
== pkt_data
) {
332 pkt_data
= wmem_new0(wmem_file_scope(), vjc_conv_t
);
333 pkt_data
->vals
= wmem_map_new(wmem_file_scope(), g_direct_hash
, g_direct_equal
);
334 conversation_add_proto_data(conv
, proto_vjc
, (void *)pkt_data
);
337 pkt_data
->frame_headers
=
338 (uint8_t *)tvb_memdup(wmem_file_scope(), tcpip_tvb
, 0, tcp_len
);
340 pkt_data
->last_frame
= pinfo
->num
;
341 pkt_data
->header_len
= tcp_len
;
343 // This value is used for re-calculating seq/ack numbers
344 pkt_data
->last_frame_len
= tvb_reported_length(tvb
) - ip_len
;
346 this_hdr
= wmem_new0(wmem_file_scope(), vjc_hdr_t
);
347 this_hdr
->ip_id
= GET_16(pdata
, 4);
348 this_hdr
->seq
= GET_32(pdata
, ip_len
+ 4);
349 this_hdr
->ack
= GET_32(pdata
, ip_len
+ 8);
350 this_hdr
->psh
= (pdata
[ip_len
+ 13] & 0x08) == 0x08;
351 this_hdr
->win
= GET_16(pdata
, ip_len
+ 14);
352 this_hdr
->tcp_chksum
= GET_16(pdata
, ip_len
+ 16);
353 this_hdr
->urg
= GET_16(pdata
, ip_len
+ 18);
354 wmem_map_insert(pkt_data
->vals
, GUINT_TO_POINTER(pinfo
->num
), this_hdr
);
357 /* We've already visited this packet, we should have all the info we need. */
360 return call_dissector_with_data(ip_handle
, tcpip_tvb
, pinfo
, tree
, data
);
363 /* Main dissection routine for compressed VJC packets.
364 * Registered in proto_reg_handoff_vjc() below.
367 dissect_vjc_comp(tvbuff_t
*tvb
, packet_info
*pinfo _U_
, proto_tree
*tree
, void* data _U_
)
369 /* A Van Jacobson compressed packet contains a change mask, which indicates
370 * possible fields that may be present.
372 proto_tree
*subtree
= NULL
;
373 proto_item
*ti
= NULL
;
374 unsigned hdr_len
= 3; // See below
375 bool hdr_error
= false;
377 unsigned pkt_len
= 0;
385 uint32_t ip_chksum
= 0;
386 uint32_t tcp_chksum
= 0;
387 uint32_t vjc_cnum
= 0;
388 conversation_t
*conv
= NULL
;
389 vjc_hdr_t
*this_hdr
= NULL
;
390 vjc_hdr_t
*last_hdr
= NULL
;
391 vjc_conv_t
*pkt_data
= NULL
;
392 uint8_t *pdata
= NULL
;
393 tvbuff_t
*tcpip_tvb
= NULL
;
394 tvbuff_t
*sub_tvb
= NULL
;
396 /* Calculate the length of the VJC header,
397 * accounting for extensions in the delta fields.
398 * We start with a value of 3, because we'll always have
399 * an 8-bit change mask and a 16-bit TCP checksum.
401 #define TEST_HDR_LEN \
402 if (hdr_len > tvb_captured_length(tvb)) { hdr_error = true; goto done_header_len; }
405 flags
= tvb_get_uint8(tvb
, offset
);
406 if (flags
& VJC_FLAG_C
) {
407 // have connection number
411 if ((flags
& VJC_FLAGS_SAWU
) == VJC_FLAGS_SAWU
) {
412 /* Special case for "unidirectional data transfer".
413 * No change to header size; d_ack = 0, and
414 * we're to calculate d_seq ourselves.
417 else if ((flags
& VJC_FLAGS_SAWU
) == VJC_FLAGS_SWU
) {
418 /* Special case for "echoed interactive traffic".
419 * No change to header size; we're to calculate d_seq and d_ack.
423 /* Not a special case, determine the header size by
424 * testing the SAWU flags individually.
426 if (flags
& VJC_FLAG_U
) {
427 // have urgent pointer
431 if (flags
& VJC_FLAG_W
) {
433 if (0 == tvb_get_int8(tvb
, offset
+ hdr_len
))
439 if (flags
& VJC_FLAG_A
) {
441 if (0 == tvb_get_uint8(tvb
, offset
+ hdr_len
))
447 if (flags
& VJC_FLAG_S
) {
449 if (0 == tvb_get_uint8(tvb
, offset
+ hdr_len
))
456 if (flags
& VJC_FLAG_I
) {
458 if (0 == tvb_get_uint8(tvb
, offset
+ hdr_len
))
465 /* Now that we have the header length, use it when assigning the
470 ti
= proto_tree_add_item(tree
, proto_vjc
, tvb
, 0,
471 MIN(hdr_len
, tvb_captured_length(tvb
)), ENC_NA
);
472 subtree
= proto_item_add_subtree(ti
, ett_vjc
);
473 proto_item_set_text(subtree
, "PPP Van Jacobson compressed TCP/IP");
475 proto_tree_add_expert_format(subtree
, pinfo
, &ei_vjc_bad_data
, tvb
, 0, -1,
476 "Packet truncated, compression header incomplete");
477 return tvb_captured_length(tvb
);
480 ti
= proto_tree_add_boolean(subtree
, hf_vjc_comp
, tvb
, 0, 0, true);
481 proto_item_set_generated(ti
);
483 proto_tree_add_bitmask(subtree
, tvb
, 0, hf_vjc_change_mask
,
484 ett_vjc_change_mask
, vjc_change_mask_fields
, ENC_NA
);
485 if ((flags
& VJC_FLAGS_SAWU
) == VJC_FLAGS_SAWU
) {
486 proto_tree_add_expert(subtree
, pinfo
, &ei_vjc_sawu
, tvb
, 0, 1);
488 else if ((flags
& VJC_FLAGS_SAWU
) == VJC_FLAGS_SWU
) {
489 proto_tree_add_expert(subtree
, pinfo
, &ei_vjc_swu
, tvb
, 0, 1);
494 if (flags
& VJC_FLAG_C
) {
495 proto_tree_add_item_ret_uint(subtree
, hf_vjc_cnum
, tvb
, offset
, 1,
496 ENC_BIG_ENDIAN
, &vjc_cnum
);
497 last_cnum
= vjc_cnum
;
501 vjc_cnum
= last_cnum
;
502 if (vjc_cnum
!= CNUM_INVALID
) {
503 ti
= proto_tree_add_uint(subtree
, hf_vjc_cnum
, tvb
, offset
, 0, vjc_cnum
);
504 proto_item_set_generated(ti
);
507 proto_tree_add_expert(subtree
, pinfo
, &ei_vjc_no_cnum
, tvb
, 0, 0);
510 conv
= vjc_find_conversation(pinfo
, vjc_cnum
, false);
512 pkt_data
= (vjc_conv_t
*)conversation_get_proto_data(conv
, proto_vjc
);
513 // Will be testing that pkt_data exists below
516 proto_tree_add_expert(subtree
, pinfo
, &ei_vjc_no_conversation
,
517 tvb
, 1, (flags
& VJC_FLAG_C
) ? 1 : 0);
520 proto_tree_add_item_ret_uint(subtree
, hf_vjc_chksum
, tvb
, offset
, 2,
521 ENC_BIG_ENDIAN
, &tcp_chksum
);
524 if ((flags
& VJC_FLAGS_SAWU
) == VJC_FLAGS_SAWU
) {
525 /* Special case for "unidirectional data transfer".
526 * d_ack is 0, and d_seq changed by the amount of data in the previous packet.
528 flags
&= ~VJC_FLAGS_SAWU
;
530 if (NULL
!= pkt_data
) {
531 d_seq
= pkt_data
->last_frame_len
;
533 ti
= proto_tree_add_uint(subtree
, hf_vjc_d_ack
, tvb
, offset
, 0, d_ack
);
534 proto_item_set_generated(ti
);
535 ti
= proto_tree_add_uint(subtree
, hf_vjc_d_seq
, tvb
, offset
, 0, d_seq
);
536 proto_item_set_generated(ti
);
538 else if ((flags
& VJC_FLAGS_SAWU
) == VJC_FLAGS_SWU
) {
539 /* Special case for "echoed interactive traffic".
540 * d_seq and d_ack changed by the amount of user data in the
543 flags
&= ~VJC_FLAGS_SAWU
;
544 if (NULL
!= pkt_data
) {
545 d_seq
= d_ack
= pkt_data
->last_frame_len
;
547 ti
= proto_tree_add_uint(subtree
, hf_vjc_d_ack
, tvb
, offset
, 0, d_ack
);
548 proto_item_set_generated(ti
);
549 ti
= proto_tree_add_uint(subtree
, hf_vjc_d_seq
, tvb
, offset
, 0, d_seq
);
550 proto_item_set_generated(ti
);
553 /* Not a special case, read the SAWU flags individually */
555 if (flags
& VJC_FLAG_U
) {
556 /* "The packet’s urgent pointer is sent if URG is set ..."
557 * I assume that means the full 16-bit value here.
559 proto_tree_add_item_ret_uint(subtree
, hf_vjc_urg
, tvb
, offset
, 2,
560 ENC_BIG_ENDIAN
, &urg
);
567 if (flags
& VJC_FLAG_W
) {
568 /* "The number sent for the window is also the difference between the current
569 * and previous values. However, either positive or negative changes are
570 * allowed since the window is a 16-bit field."
572 d_win
= vjc_delta_int(subtree
, hf_vjc_d_win
, tvb
, &offset
);
578 /* The rest of the deltas can only be positive. */
579 if (flags
& VJC_FLAG_A
) {
580 d_ack
= vjc_delta_uint(subtree
, hf_vjc_d_ack
, tvb
, &offset
);
586 if (flags
& VJC_FLAG_S
) {
587 d_seq
= vjc_delta_uint(subtree
, hf_vjc_d_seq
, tvb
, &offset
);
594 if (flags
& VJC_FLAG_I
) {
595 d_ipid
= vjc_delta_uint(subtree
, hf_vjc_d_ipid
, tvb
, &offset
);
598 /* "However, unlike the rest of the compressed fields, the assumed
599 * change when I is clear is one, not zero." - section 3.2.2
602 ti
= proto_tree_add_uint(subtree
, hf_vjc_d_ipid
, tvb
, offset
, 0, d_ipid
);
603 proto_item_set_generated(ti
);
606 if (!(pinfo
->p2p_dir
== P2P_DIR_RECV
|| pinfo
->p2p_dir
== P2P_DIR_SENT
)) {
607 /* We can't make a proper conversation if we don't know the endpoints */
608 proto_tree_add_expert(subtree
, pinfo
, &ei_vjc_no_direction
, tvb
, offset
,
609 tvb_captured_length_remaining(tvb
, offset
));
610 return tvb_captured_length(tvb
);
613 proto_tree_add_expert(subtree
, pinfo
, &ei_vjc_undecoded
, tvb
, offset
,
614 tvb_captured_length_remaining(tvb
, offset
));
615 return tvb_captured_length(tvb
);
617 if (NULL
== pkt_data
) {
618 proto_tree_add_expert(subtree
, pinfo
, &ei_vjc_no_conv_data
, tvb
, offset
,
619 tvb_captured_length_remaining(tvb
, offset
));
620 return tvb_captured_length(tvb
);
623 if (!pinfo
->fd
->visited
) {
624 /* We haven't visited this packet before.
625 * Form its vjc_hdr_t from the deltas and the info from the previous frame.
627 last_hdr
= (vjc_hdr_t
*)wmem_map_lookup(pkt_data
->vals
,
628 GUINT_TO_POINTER(pkt_data
->last_frame
));
630 if (NULL
!= last_hdr
) {
631 this_hdr
= wmem_new0(wmem_file_scope(), vjc_hdr_t
);
632 this_hdr
->tcp_chksum
= (uint16_t)tcp_chksum
;
633 this_hdr
->urg
= (uint16_t)urg
;
634 this_hdr
->win
= last_hdr
->win
+ d_win
;
635 this_hdr
->seq
= last_hdr
->seq
+ d_seq
;
636 this_hdr
->ack
= last_hdr
->ack
+ d_ack
;
637 this_hdr
->ip_id
= last_hdr
->ip_id
+ d_ipid
;
638 this_hdr
->psh
= (flags
& VJC_FLAG_P
) == VJC_FLAG_P
;
639 wmem_map_insert(pkt_data
->vals
, GUINT_TO_POINTER(pinfo
->num
), this_hdr
);
641 // This frame is the next frame's last frame
642 pkt_data
->last_frame
= pinfo
->num
;
643 pkt_data
->last_frame_len
= tvb_reported_length_remaining(tvb
, offset
);
646 proto_tree_add_expert_format(subtree
, pinfo
, &ei_vjc_error
, tvb
, 0, 0,
647 "Dissector error: unable to find headers for prior frame %d",
648 pkt_data
->last_frame
);
649 return tvb_captured_length(tvb
);
651 // if last_hdr is null, then this_hdr will stay null and be handled below
654 /* We have visited this packet before.
655 * Get the values we saved the first time.
657 this_hdr
= (vjc_hdr_t
*)wmem_map_lookup(pkt_data
->vals
,
658 GUINT_TO_POINTER(pinfo
->num
));
660 if (NULL
!= this_hdr
) {
661 /* pkt_data->frame_headers is our template packet header data.
662 * Apply changes to it as needed.
663 * The changes are intentionally done in the template before copying.
665 pkt_len
= pkt_data
->header_len
+ tvb_reported_length_remaining(tvb
, offset
);
667 pdata
= pkt_data
->frame_headers
; /* shorthand */
668 ip_len
= (pdata
[0] & 0x0F) << 2;
671 PUT_16(pdata
, 2, pkt_len
);
674 PUT_16(pdata
, 4, this_hdr
->ip_id
);
677 PUT_16(pdata
, 10, 0x0000);
678 ip_chksum
= ip_checksum(pdata
, ip_len
);
679 PUT_16(pdata
, 10, g_htons(ip_chksum
));
682 PUT_32(pdata
, ip_len
+ 4, this_hdr
->seq
);
685 PUT_32(pdata
, ip_len
+ 8, this_hdr
->ack
);
688 PUT_16(pdata
, ip_len
+ 14, this_hdr
->win
);
692 pdata
[ip_len
+ 13] |= 0x08;
695 pdata
[ip_len
+ 13] &= ~0x08;
699 PUT_16(pdata
, ip_len
+ 16, this_hdr
->tcp_chksum
);
703 pdata
[ip_len
+ 13] |= 0x20;
704 PUT_16(pdata
, ip_len
+ 18, this_hdr
->urg
);
707 pdata
[ip_len
+ 13] &= ~0x20;
708 PUT_16(pdata
, ip_len
+ 18, 0x0000);
711 /* Now that we're done manipulating the packet header, stick it into
712 * a TVB for sub-dissectors to use.
714 sub_tvb
= tvb_new_child_real_data(tvb
, pdata
,
715 pkt_data
->header_len
, pkt_data
->header_len
);
716 tvb_set_free_cb(sub_tvb
, NULL
);
719 pkt_len
= tvb_captured_length_remaining(tvb
, offset
);
721 tcpip_tvb
= tvb_new_composite();
722 tvb_composite_append(tcpip_tvb
, sub_tvb
);
723 tvb_composite_append(tcpip_tvb
, tvb_new_subset_remaining(tvb
, offset
));
724 tvb_composite_finalize(tcpip_tvb
);
726 ti
= proto_tree_add_item(subtree
, hf_vjc_tcpdata
, tvb
, offset
, pkt_len
, ENC_NA
);
727 proto_item_set_text(ti
, "TCP data (%d byte%s)", pkt_len
, plurality(pkt_len
, "", "s"));
734 add_new_data_source(pinfo
, tcpip_tvb
, "Decompressed TCP/IP data");
735 return offset
+ call_dissector_with_data(ip_handle
, tcpip_tvb
, pinfo
, tree
, data
);
738 proto_tree_add_expert_format(subtree
, pinfo
, &ei_vjc_error
, tvb
, 0, 0,
739 "Dissector error: unable to find headers for current frame %d",
742 return tvb_captured_length(tvb
);
746 proto_register_vjc(void)
748 static hf_register_info hf
[] = {
750 { "Is compressed", "vjc.compressed", FT_BOOLEAN
, BASE_NONE
,
751 NULL
, 0x0, NULL
, HFILL
}},
753 { "Connection number", "vjc.connection_number", FT_UINT8
, BASE_DEC
,
754 NULL
, 0x0, NULL
, HFILL
}},
755 { &hf_vjc_change_mask
,
756 { "Change mask", "vjc.change_mask", FT_UINT8
, BASE_HEX
,
757 NULL
, 0x0, NULL
, HFILL
}},
758 { &hf_vjc_change_mask_r
,
759 { "Reserved", "vjc.change_mask.reserved", FT_BOOLEAN
, 8,
760 TFS(&tfs_set_notset
), VJC_FLAG_R
, "Undefined bit", HFILL
}},
761 { &hf_vjc_change_mask_c
,
762 { "Connection number flag", "vjc.change_mask.connection_number", FT_BOOLEAN
, 8,
763 TFS(&tfs_set_notset
), VJC_FLAG_C
, "Whether connection number is present", HFILL
}},
764 { &hf_vjc_change_mask_i
,
765 { "IP ID flag", "vjc.change_mask.ip_id", FT_BOOLEAN
, 8,
766 TFS(&tfs_set_notset
), VJC_FLAG_I
, "Whether IP ID is present", HFILL
}},
767 { &hf_vjc_change_mask_p
,
768 { "TCP PSH flag", "vjc.change_mask.psh", FT_BOOLEAN
, 8,
769 TFS(&tfs_set_notset
), VJC_FLAG_P
, "Whether to set TCP PSH", HFILL
}},
770 { &hf_vjc_change_mask_s
,
771 { "TCP Sequence flag", "vjc.change_mask.seq", FT_BOOLEAN
, 8,
772 TFS(&tfs_set_notset
), VJC_FLAG_S
, "Whether TCP SEQ is present", HFILL
}},
773 { &hf_vjc_change_mask_a
,
774 { "TCP Acknowledgement flag", "vjc.change_mask.ack", FT_BOOLEAN
, 8,
775 TFS(&tfs_set_notset
), VJC_FLAG_A
, "Whether TCP ACK is present", HFILL
}},
776 { &hf_vjc_change_mask_w
,
777 { "TCP Window flag", "vjc.change_mask.win", FT_BOOLEAN
, 8,
778 TFS(&tfs_set_notset
), VJC_FLAG_W
, "Whether TCP Window is present", HFILL
}},
779 { &hf_vjc_change_mask_u
,
780 { "TCP Urgent flag", "vjc.change_mask.urg", FT_BOOLEAN
, 8,
781 TFS(&tfs_set_notset
), VJC_FLAG_U
, "Whether TCP URG pointer is present", HFILL
}},
783 { "TCP Checksum", "vjc.checksum", FT_UINT16
, BASE_HEX
,
784 NULL
, 0x0, "TCP checksum of original packet", HFILL
}},
786 { "Urgent pointer", "vjc.urgent_pointer", FT_UINT16
, BASE_DEC
,
787 NULL
, 0x0, "TCP urgent pointer of original packet", HFILL
}},
789 { "Delta window", "vjc.delta_window", FT_INT16
, BASE_DEC
,
790 NULL
, 0x0, "Change in TCP window size from previous packet", HFILL
}},
792 { "Delta ack", "vjc.delta_ack", FT_UINT16
, BASE_DEC
,
793 NULL
, 0x0, "Change in TCP acknowledgement number from previous packet", HFILL
}},
795 { "Delta seq", "vjc.delta_seq", FT_UINT16
, BASE_DEC
,
796 NULL
, 0x0, "Change in TCP sequence number from previous packet", HFILL
}},
798 { "Delta IP ID", "vjc.delta_ipid", FT_UINT16
, BASE_DEC
,
799 NULL
, 0x0, "Change in IP Identification number from previous packet", HFILL
}},
801 { "TCP data", "vjc.tcp_data", FT_BYTES
, BASE_NONE
,
802 NULL
, 0x0, "Original TCP payload", HFILL
}},
805 static int *ett
[] = {
807 &ett_vjc_change_mask
,
810 expert_module_t
* expert_vjc
;
811 static ei_register_info ei
[] = {
813 { "vjc.special.sawu", PI_PROTOCOL
, PI_CHAT
,
814 ".... 1111 = special case for \"unidirectional data transfer\"", EXPFILL
}},
816 { "vjc.special.swu", PI_PROTOCOL
, PI_CHAT
,
817 ".... 1011 = special case for \"echoed interactive traffic\"", EXPFILL
}},
819 { "vjc.no_connection_id", PI_PROTOCOL
, PI_WARN
,
820 "No connection ID and no prior connection (common at capture start)", EXPFILL
}},
821 { &ei_vjc_no_conversation
,
822 { "vjc.no_connection", PI_PROTOCOL
, PI_WARN
,
823 "No saved connection found (common at capture start)", EXPFILL
}},
824 { &ei_vjc_no_direction
,
825 { "vjc.no_direction", PI_UNDECODED
, PI_WARN
,
826 "Connection has no direction info, cannot decompress", EXPFILL
}},
827 { &ei_vjc_no_conv_data
,
828 { "vjc.no_connection_data", PI_UNDECODED
, PI_WARN
,
829 "Could not find saved connection data", EXPFILL
}},
831 { "vjc.no_decompress", PI_UNDECODED
, PI_WARN
,
832 "Undecoded data (impossible due to missing information)", EXPFILL
}},
834 { "vjc.bad_data", PI_PROTOCOL
, PI_ERROR
,
835 "Non-compliant packet data", EXPFILL
}},
837 { "vjc.error", PI_MALFORMED
, PI_ERROR
,
838 "Unrecoverable dissector error", EXPFILL
}},
841 proto_vjc
= proto_register_protocol("Van Jacobson PPP compression", "VJC", "vjc");
842 proto_register_field_array(proto_vjc
, hf
, array_length(hf
));
843 proto_register_subtree_array(ett
, array_length(ett
));
844 expert_vjc
= expert_register_protocol(proto_vjc
);
845 expert_register_field_array(expert_vjc
, ei
, array_length(ei
));
846 vjcc_handle
= register_dissector("vjc_compressed", dissect_vjc_comp
, proto_vjc
);
847 vjcu_handle
= register_dissector("vjc_uncompressed", dissect_vjc_uncomp
, proto_vjc
);
849 register_init_routine(&vjc_init_protocol
);
850 register_cleanup_routine(&vjc_cleanup_protocol
);
854 proto_reg_handoff_vjc(void)
856 ip_handle
= find_dissector("ip");
858 dissector_add_uint("ppp.protocol", PPP_VJC_COMP
, vjcc_handle
);
859 dissector_add_uint("ppp.protocol", PPP_VJC_UNCOMP
, vjcu_handle
);
863 * Editor modelines - https://www.wireshark.org/tools/modelines.html
868 * indent-tabs-mode: nil
871 * vi: set shiftwidth=4 tabstop=8 expandtab:
872 * :indentSize=4:tabSize=8:noTabs=true: