Revert "TODO epan/dissectors/asn1/kerberos/packet-kerberos-template.c new GSS flags"
[wireshark-sm.git] / epan / dissectors / packet-vj-comp.c
blobcb3bd6c1ffaa4f55dd05f3be516909cdbfb88eae
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 #define WS_LOG_DOMAIN "packet-vjc-comp"
23 #include "config.h"
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>
32 #include <epan/tfs.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 {
55 uint16_t tcp_chksum;
56 uint16_t urg;
57 uint16_t win;
58 uint32_t seq;
59 uint32_t ack;
60 uint32_t ip_id;
61 bool psh;
62 } vjc_hdr_t;
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;
72 } vjc_conv_t;
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);
87 static int proto_vjc;
89 static int ett_vjc;
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,
142 NULL
145 /* Initialization routine. Called at start of dissection.
146 * Registered in proto_register_vjc() below.
148 static void
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.
157 static void
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.
166 static void *
167 vjc_get_conv_key(packet_info *pinfo, uint32_t vjc_cnum)
169 if (vjc_cnum == CNUM_INVALID)
170 return NULL;
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) {
181 case P2P_DIR_RECV:
182 ret_val |= 0x0100;
183 break;
184 case P2P_DIR_SENT:
185 ret_val |= 0x0200;
186 break;
187 default:
188 return NULL;
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.
197 static uint32_t
198 vjc_delta_uint(proto_tree *tree, int hf, tvbuff_t *tvb, unsigned *offset)
200 uint32_t ret_val;
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);
204 (*offset)++;
206 else {
207 (*offset)++;
208 proto_tree_add_item_ret_uint(tree, hf, tvb, *offset, 2,
209 ENC_BIG_ENDIAN, &ret_val);
210 *offset += 2;
212 return ret_val;
215 /* Same thing but signed, since the TCP window delta can be negative */
216 static int32_t
217 vjc_delta_int(proto_tree *tree, int hf, tvbuff_t *tvb, unsigned *offset)
219 int32_t ret_val;
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);
223 (*offset)++;
225 else {
226 (*offset)++;
227 proto_tree_add_item_ret_int(tree, hf, tvb, *offset, 2,
228 ENC_BIG_ENDIAN, &ret_val);
229 *offset += 2;
231 return ret_val;
234 /* Main dissection routine for uncompressed VJC packets.
235 * Registered in proto_reg_handoff_vjc() below.
237 static int
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;
247 uint8_t ip_ver = 0;
248 uint8_t ip_len = 0;
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;
271 if (ip_ver != 4) {
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;
322 pdata = // shorthand
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);
339 else {
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.
349 static int
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;
359 unsigned ip_len = 0;
360 unsigned pkt_len = 0;
361 unsigned d_ipid = 0;
362 unsigned d_seq = 0;
363 unsigned d_ack = 0;
364 int d_win = 0;
365 uint8_t flags = 0;
366 unsigned offset = 0;
367 uint32_t urg = 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; }
388 TEST_HDR_LEN;
389 flags = tvb_get_uint8(tvb, offset);
390 if (flags & VJC_FLAG_C) {
391 // have connection number
392 hdr_len++;
393 TEST_HDR_LEN;
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.
406 else {
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
412 hdr_len += 2;
413 TEST_HDR_LEN;
415 if (flags & VJC_FLAG_W) {
416 // have d_win
417 if (0 == tvb_get_int8(tvb, offset + hdr_len))
418 hdr_len += 3;
419 else
420 hdr_len++;
421 TEST_HDR_LEN;
423 if (flags & VJC_FLAG_A) {
424 // have d_ack
425 if (0 == tvb_get_uint8(tvb, offset + hdr_len))
426 hdr_len += 3;
427 else
428 hdr_len++;
429 TEST_HDR_LEN;
431 if (flags & VJC_FLAG_S) {
432 // have d_seq
433 if (0 == tvb_get_uint8(tvb, offset + hdr_len))
434 hdr_len += 3;
435 else
436 hdr_len++;
437 TEST_HDR_LEN;
440 if (flags & VJC_FLAG_I) {
441 // have IP ID
442 if (0 == tvb_get_uint8(tvb, offset + hdr_len))
443 hdr_len += 3;
444 else
445 hdr_len++;
446 TEST_HDR_LEN;
449 /* Now that we have the header length, use it when assigning the
450 * protocol item.
452 #undef TEST_HDR_LEN
453 done_header_len:
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");
458 if (hdr_error) {
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);
476 offset++;
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;
482 offset++;
484 else {
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));
497 else {
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)));
502 else {
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);
510 else {
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);
526 offset += 2;
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;
533 d_ack = 0;
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
545 * previous packet.
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);
556 else {
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);
565 offset += 2;
567 else {
568 urg = 0;
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);
578 else {
579 d_win = 0;
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);
586 else {
587 d_ack = 0;
590 if (flags & VJC_FLAG_S) {
591 d_seq = vjc_delta_uint(subtree, hf_vjc_d_seq, tvb, &offset);
593 else {
594 d_seq = 0;
598 if (flags & VJC_FLAG_I) {
599 d_ipid = vjc_delta_uint(subtree, hf_vjc_d_ipid, tvb, &offset);
601 else {
602 /* "However, unlike the rest of the compressed fields, the assumed
603 * change when I is clear is one, not zero." - section 3.2.2
605 d_ipid = 1;
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);
647 else {
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;
665 /* IP length */
666 phton16(pdata + 2, pkt_len);
668 /* IP ID */
669 phton16(pdata + 4, this_hdr->ip_id);
671 /* IP checksum */
672 phton16(pdata + 10, 0x0000);
673 ip_chksum = ip_checksum(pdata, ip_len);
674 phton16(pdata + 10, g_htons(ip_chksum));
676 /* TCP seq */
677 phton32(pdata + ip_len + 4, this_hdr->seq);
679 /* TCP ack */
680 phton32(pdata + ip_len + 8, this_hdr->ack);
682 /* TCP window */
683 phton16(pdata + ip_len + 14, this_hdr->win);
685 /* TCP push */
686 if (this_hdr->psh) {
687 pdata[ip_len + 13] |= 0x08;
689 else {
690 pdata[ip_len + 13] &= ~0x08;
693 /* TCP checksum */
694 phton16(pdata + ip_len + 16, this_hdr->tcp_chksum);
696 /* TCP urg */
697 if (this_hdr->urg) {
698 pdata[ip_len + 13] |= 0x20;
699 phton16(pdata + ip_len + 18, this_hdr->urg);
701 else {
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);
713 // Reuse pkt_len
714 pkt_len = tvb_captured_length_remaining(tvb, offset);
715 if (pkt_len > 0) {
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"));
724 else
726 tcpip_tvb = sub_tvb;
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);
732 else {
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",
735 pinfo->num);
737 return tvb_captured_length(tvb);
740 void
741 proto_register_vjc(void)
743 static hf_register_info hf[] = {
744 { &hf_vjc_comp,
745 { "Is compressed", "vjc.compressed", FT_BOOLEAN, BASE_NONE,
746 NULL, 0x0, NULL, HFILL }},
747 { &hf_vjc_cnum,
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 }},
777 { &hf_vjc_chksum,
778 { "TCP Checksum", "vjc.checksum", FT_UINT16, BASE_HEX,
779 NULL, 0x0, "TCP checksum of original packet", HFILL}},
780 { &hf_vjc_urg,
781 { "Urgent pointer", "vjc.urgent_pointer", FT_UINT16, BASE_DEC,
782 NULL, 0x0, "TCP urgent pointer of original packet", HFILL}},
783 { &hf_vjc_d_win,
784 { "Delta window", "vjc.delta_window", FT_INT16, BASE_DEC,
785 NULL, 0x0, "Change in TCP window size from previous packet", HFILL}},
786 { &hf_vjc_d_ack,
787 { "Delta ack", "vjc.delta_ack", FT_UINT16, BASE_DEC,
788 NULL, 0x0, "Change in TCP acknowledgement number from previous packet", HFILL}},
789 { &hf_vjc_d_seq,
790 { "Delta seq", "vjc.delta_seq", FT_UINT16, BASE_DEC,
791 NULL, 0x0, "Change in TCP sequence number from previous packet", HFILL}},
792 { &hf_vjc_d_ipid,
793 { "Delta IP ID", "vjc.delta_ipid", FT_UINT16, BASE_DEC,
794 NULL, 0x0, "Change in IP Identification number from previous packet", HFILL}},
795 { &hf_vjc_tcpdata,
796 { "TCP data", "vjc.tcp_data", FT_BYTES, BASE_NONE,
797 NULL, 0x0, "Original TCP payload", HFILL}},
800 static int *ett[] = {
801 &ett_vjc,
802 &ett_vjc_change_mask,
805 expert_module_t* expert_vjc;
806 static ei_register_info ei[] = {
807 { &ei_vjc_sawu,
808 { "vjc.special.sawu", PI_PROTOCOL, PI_CHAT,
809 ".... 1111 = special case for \"unidirectional data transfer\"", EXPFILL }},
810 { &ei_vjc_swu,
811 { "vjc.special.swu", PI_PROTOCOL, PI_CHAT,
812 ".... 1011 = special case for \"echoed interactive traffic\"", EXPFILL }},
813 { &ei_vjc_no_cnum,
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 }},
825 { &ei_vjc_undecoded,
826 { "vjc.no_decompress", PI_UNDECODED, PI_WARN,
827 "Undecoded data (impossible due to missing information)", EXPFILL }},
828 { &ei_vjc_bad_data,
829 { "vjc.bad_data", PI_PROTOCOL, PI_ERROR,
830 "Non-compliant packet data", EXPFILL }},
831 { &ei_vjc_error,
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);
855 void
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
867 * Local variables:
868 * c-basic-offset: 4
869 * tab-width: 8
870 * indent-tabs-mode: nil
871 * End:
873 * vi: set shiftwidth=4 tabstop=8 expandtab:
874 * :indentSize=4:tabSize=8:noTabs=true: