epan/dissectors/pidl/samr/samr.cnf cnf_dissect_lsa_BinaryString => lsarpc_dissect_str...
[wireshark-sm.git] / epan / dissectors / packet-vj-comp.c
blob18b3a98a5d1e40bdd03547d54017ad2cd4f41e2f
1 /* packet-vj-comp.c
2 * Routines for decompression of PPP Van Jacobson compression
3 * RFC 1144
5 * Wireshark - Network traffic analyzer
6 * By Gerald Combs <gerald@wireshark.org>
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
18 /* Currently hard-coded to assume TCP over IPv4.
19 * Nothing in the standard explicitly prevents an IPv6 implementation...
22 #include "config.h"
24 #include <glib.h>
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>
33 #include <epan/tfs.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); \
45 } G_STMT_END
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); \
51 } G_STMT_END
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 {
72 uint16_t tcp_chksum;
73 uint16_t urg;
74 uint16_t win;
75 uint32_t seq;
76 uint32_t ack;
77 uint32_t ip_id;
78 bool psh;
79 } vjc_hdr_t;
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*
88 } vjc_conv_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);
97 static int proto_vjc;
99 static int ett_vjc;
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,
152 NULL
155 /* Initialization routine. Called at start of dissection.
156 * Registered in proto_register_vjc() below.
158 static void
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.
167 static void
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) {
186 case P2P_DIR_RECV:
187 vjc_cnum |= 0x0100;
188 break;
189 case P2P_DIR_SENT:
190 vjc_cnum |= 0x0200;
191 break;
192 default:
193 return conv;
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);
201 return conv;
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.
208 static uint32_t
209 vjc_delta_uint(proto_tree *tree, int hf, tvbuff_t *tvb, unsigned *offset)
211 uint32_t ret_val;
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);
215 (*offset)++;
217 else {
218 (*offset)++;
219 proto_tree_add_item_ret_uint(tree, hf, tvb, *offset, 2,
220 ENC_BIG_ENDIAN, &ret_val);
221 *offset += 2;
223 return ret_val;
226 /* Same thing but signed, since the TCP window delta can be negative */
227 static int32_t
228 vjc_delta_int(proto_tree *tree, int hf, tvbuff_t *tvb, unsigned *offset)
230 int32_t ret_val;
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);
234 (*offset)++;
236 else {
237 (*offset)++;
238 proto_tree_add_item_ret_int(tree, hf, tvb, *offset, 2,
239 ENC_BIG_ENDIAN, &ret_val);
240 *offset += 2;
242 return ret_val;
245 /* Main dissection routine for uncompressed VJC packets.
246 * Registered in proto_reg_handoff_vjc() below.
248 static int
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;
258 uint8_t ip_ver = 0;
259 uint8_t ip_len = 0;
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;
283 if (4 != ip_ver) {
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);
336 pdata = // shorthand
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);
356 else {
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.
366 static int
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;
376 unsigned ip_len = 0;
377 unsigned pkt_len = 0;
378 unsigned d_ipid = 0;
379 unsigned d_seq = 0;
380 unsigned d_ack = 0;
381 int d_win = 0;
382 uint8_t flags = 0;
383 unsigned offset = 0;
384 uint32_t urg = 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; }
404 TEST_HDR_LEN;
405 flags = tvb_get_uint8(tvb, offset);
406 if (flags & VJC_FLAG_C) {
407 // have connection number
408 hdr_len++;
409 TEST_HDR_LEN;
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.
422 else {
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
428 hdr_len += 2;
429 TEST_HDR_LEN;
431 if (flags & VJC_FLAG_W) {
432 // have d_win
433 if (0 == tvb_get_int8(tvb, offset + hdr_len))
434 hdr_len += 3;
435 else
436 hdr_len++;
437 TEST_HDR_LEN;
439 if (flags & VJC_FLAG_A) {
440 // have d_ack
441 if (0 == tvb_get_uint8(tvb, offset + hdr_len))
442 hdr_len += 3;
443 else
444 hdr_len++;
445 TEST_HDR_LEN;
447 if (flags & VJC_FLAG_S) {
448 // have d_seq
449 if (0 == tvb_get_uint8(tvb, offset + hdr_len))
450 hdr_len += 3;
451 else
452 hdr_len++;
453 TEST_HDR_LEN;
456 if (flags & VJC_FLAG_I) {
457 // have IP ID
458 if (0 == tvb_get_uint8(tvb, offset + hdr_len))
459 hdr_len += 3;
460 else
461 hdr_len++;
462 TEST_HDR_LEN;
465 /* Now that we have the header length, use it when assigning the
466 * protocol item.
468 #undef TEST_HDR_LEN
469 done_header_len:
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");
474 if (hdr_error) {
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);
492 offset++;
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;
498 offset++;
500 else {
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);
506 else {
507 proto_tree_add_expert(subtree, pinfo, &ei_vjc_no_cnum, tvb, 0, 0);
510 conv = vjc_find_conversation(pinfo, vjc_cnum, false);
511 if (NULL != conv) {
512 pkt_data = (vjc_conv_t *)conversation_get_proto_data(conv, proto_vjc);
513 // Will be testing that pkt_data exists below
515 else {
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);
522 offset += 2;
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;
529 d_ack = 0;
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
541 * previous packet.
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);
552 else {
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);
561 offset += 2;
563 else {
564 urg = 0;
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);
574 else {
575 d_win = 0;
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);
582 else {
583 d_ack = 0;
586 if (flags & VJC_FLAG_S) {
587 d_seq = vjc_delta_uint(subtree, hf_vjc_d_seq, tvb, &offset);
589 else {
590 d_seq = 0;
594 if (flags & VJC_FLAG_I) {
595 d_ipid = vjc_delta_uint(subtree, hf_vjc_d_ipid, tvb, &offset);
597 else {
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
601 d_ipid = 1;
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);
612 if (NULL == conv) {
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);
645 else {
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
653 else {
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;
670 /* IP length */
671 PUT_16(pdata, 2, pkt_len);
673 /* IP ID */
674 PUT_16(pdata, 4, this_hdr->ip_id);
676 /* IP checksum */
677 PUT_16(pdata, 10, 0x0000);
678 ip_chksum = ip_checksum(pdata, ip_len);
679 PUT_16(pdata, 10, g_htons(ip_chksum));
681 /* TCP seq */
682 PUT_32(pdata, ip_len + 4, this_hdr->seq);
684 /* TCP ack */
685 PUT_32(pdata, ip_len + 8, this_hdr->ack);
687 /* TCP window */
688 PUT_16(pdata, ip_len + 14, this_hdr->win);
690 /* TCP push */
691 if (this_hdr->psh) {
692 pdata[ip_len + 13] |= 0x08;
694 else {
695 pdata[ip_len + 13] &= ~0x08;
698 /* TCP checksum */
699 PUT_16(pdata, ip_len + 16, this_hdr->tcp_chksum);
701 /* TCP urg */
702 if (this_hdr->urg) {
703 pdata[ip_len + 13] |= 0x20;
704 PUT_16(pdata, ip_len + 18, this_hdr->urg);
706 else {
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);
718 // Reuse pkt_len
719 pkt_len = tvb_captured_length_remaining(tvb, offset);
720 if (0 < pkt_len) {
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"));
729 else
731 tcpip_tvb = sub_tvb;
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);
737 else {
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",
740 pinfo->num);
742 return tvb_captured_length(tvb);
745 void
746 proto_register_vjc(void)
748 static hf_register_info hf[] = {
749 { &hf_vjc_comp,
750 { "Is compressed", "vjc.compressed", FT_BOOLEAN, BASE_NONE,
751 NULL, 0x0, NULL, HFILL }},
752 { &hf_vjc_cnum,
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 }},
782 { &hf_vjc_chksum,
783 { "TCP Checksum", "vjc.checksum", FT_UINT16, BASE_HEX,
784 NULL, 0x0, "TCP checksum of original packet", HFILL}},
785 { &hf_vjc_urg,
786 { "Urgent pointer", "vjc.urgent_pointer", FT_UINT16, BASE_DEC,
787 NULL, 0x0, "TCP urgent pointer of original packet", HFILL}},
788 { &hf_vjc_d_win,
789 { "Delta window", "vjc.delta_window", FT_INT16, BASE_DEC,
790 NULL, 0x0, "Change in TCP window size from previous packet", HFILL}},
791 { &hf_vjc_d_ack,
792 { "Delta ack", "vjc.delta_ack", FT_UINT16, BASE_DEC,
793 NULL, 0x0, "Change in TCP acknowledgement number from previous packet", HFILL}},
794 { &hf_vjc_d_seq,
795 { "Delta seq", "vjc.delta_seq", FT_UINT16, BASE_DEC,
796 NULL, 0x0, "Change in TCP sequence number from previous packet", HFILL}},
797 { &hf_vjc_d_ipid,
798 { "Delta IP ID", "vjc.delta_ipid", FT_UINT16, BASE_DEC,
799 NULL, 0x0, "Change in IP Identification number from previous packet", HFILL}},
800 { &hf_vjc_tcpdata,
801 { "TCP data", "vjc.tcp_data", FT_BYTES, BASE_NONE,
802 NULL, 0x0, "Original TCP payload", HFILL}},
805 static int *ett[] = {
806 &ett_vjc,
807 &ett_vjc_change_mask,
810 expert_module_t* expert_vjc;
811 static ei_register_info ei[] = {
812 { &ei_vjc_sawu,
813 { "vjc.special.sawu", PI_PROTOCOL, PI_CHAT,
814 ".... 1111 = special case for \"unidirectional data transfer\"", EXPFILL }},
815 { &ei_vjc_swu,
816 { "vjc.special.swu", PI_PROTOCOL, PI_CHAT,
817 ".... 1011 = special case for \"echoed interactive traffic\"", EXPFILL }},
818 { &ei_vjc_no_cnum,
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 }},
830 { &ei_vjc_undecoded,
831 { "vjc.no_decompress", PI_UNDECODED, PI_WARN,
832 "Undecoded data (impossible due to missing information)", EXPFILL }},
833 { &ei_vjc_bad_data,
834 { "vjc.bad_data", PI_PROTOCOL, PI_ERROR,
835 "Non-compliant packet data", EXPFILL }},
836 { &ei_vjc_error,
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);
853 void
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
865 * Local variables:
866 * c-basic-offset: 4
867 * tab-width: 8
868 * indent-tabs-mode: nil
869 * End:
871 * vi: set shiftwidth=4 tabstop=8 expandtab:
872 * :indentSize=4:tabSize=8:noTabs=true: