3 * Routines for P_Mul (ACP142) packet disassembly.
4 * A protocol for reliable multicast messaging in bandwidth constrained
5 * and delayed acknowledgement (EMCON) environments.
7 * Copyright 2005, Stig Bjorlykke <stig@bjorlykke.org>, Thales Norway AS
11 * Wireshark - Network traffic analyzer
12 * By Gerald Combs <gerald@wireshark.org>
13 * Copyright 1998 Gerald Combs
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation; either version 2
18 * of the License, or (at your option) any later version.
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
25 * You should have received a copy of the GNU General Public License along
26 * with this program; if not, write to the Free Software Foundation, Inc.,
27 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
29 * Ref: http://jcs.dtic.mil/j6/cceb/acps/acp142/
34 * - Obtain dedicated UDP port numbers
35 * - SEQ/ACK analysis for Announce/Request/Reject/Release PDU
40 #include <epan/packet.h>
41 #include <epan/to_str.h>
42 #include <epan/prefs.h>
43 #include <epan/reassemble.h>
44 #include <epan/expert.h>
45 #include <epan/asn1.h>
46 #include <epan/wmem/wmem.h>
49 #include "packet-cdt.h"
50 #include "packet-ber.h"
52 #define PNAME "P_Mul (ACP142)"
53 #define PSNAME "P_MUL"
54 #define PFNAME "p_mul"
56 /* Recommended UDP Port Numbers */
57 #define DEFAULT_P_MUL_PORT_RANGE ""
62 #define Address_PDU 0x02
63 #define Discard_Message_PDU 0x03
64 #define Announce_PDU 0x04
65 #define Request_PDU 0x05
66 #define Reject_PDU 0x06
67 #define Release_PDU 0x07
68 #define FEC_Address_PDU 0x08
69 #define Extra_Address_PDU 0x12
70 #define Extra_FEC_Address_PDU 0x18
71 #define Ack_Ack_PDU 0xFF /* Fake type to indicate Ack-Ack */
73 /* Type of content to decode from Data_PDU */
78 void proto_reg_handoff_p_mul (void);
80 static int proto_p_mul
= -1;
82 static int hf_length
= -1;
83 static int hf_priority
= -1;
84 static int hf_map_first
= -1;
85 static int hf_map_last
= -1;
86 static int hf_map_unused
= -1;
87 static int hf_pdu_type
= -1;
88 static int hf_pdu_type_value
= -1;
89 static int hf_no_pdus
= -1;
90 static int hf_seq_no
= -1;
91 static int hf_unused8
= -1;
92 static int hf_unused16
= -1;
93 static int hf_checksum
= -1;
94 static int hf_checksum_good
= -1;
95 static int hf_checksum_bad
= -1;
96 static int hf_source_id_ack
= -1;
97 static int hf_source_id
= -1;
98 static int hf_message_id
= -1;
99 static int hf_expiry_time
= -1;
100 static int hf_mc_group
= -1;
101 static int hf_ann_mc_group
= -1;
102 static int hf_fec_len
= -1;
103 static int hf_fec_id
= -1;
104 static int hf_fec_parameters
= -1;
105 static int hf_count_of_dest
= -1;
106 static int hf_length_of_res
= -1;
107 static int hf_ack_count
= -1;
108 static int hf_ack_entry
= -1;
109 static int hf_ack_length
= -1;
110 static int hf_miss_seq_no
= -1;
111 static int hf_miss_seq_range
= -1;
112 static int hf_tot_miss_seq_no
= -1;
113 static int hf_timestamp_option
= -1;
114 static int hf_dest_entry
= -1;
115 static int hf_dest_id
= -1;
116 static int hf_msg_seq_no
= -1;
117 static int hf_sym_key
= -1;
118 static int hf_data_fragment
= -1;
120 static int hf_msg_fragments
= -1;
121 static int hf_msg_fragment
= -1;
122 static int hf_msg_fragment_overlap
= -1;
123 static int hf_msg_fragment_overlap_conflicts
= -1;
124 static int hf_msg_fragment_multiple_tails
= -1;
125 static int hf_msg_fragment_too_long_fragment
= -1;
126 static int hf_msg_fragment_error
= -1;
127 static int hf_msg_fragment_count
= -1;
128 static int hf_msg_reassembled_in
= -1;
129 static int hf_msg_reassembled_length
= -1;
131 static int hf_analysis_ack_time
= -1;
132 static int hf_analysis_trans_time
= -1;
133 static int hf_analysis_retrans_time
= -1;
134 static int hf_analysis_total_retrans_time
= -1;
135 static int hf_analysis_last_pdu_num
= -1;
136 static int hf_analysis_addr_pdu_num
= -1;
137 static int hf_analysis_addr_pdu_time
= -1;
138 static int hf_analysis_prev_pdu_num
= -1;
139 static int hf_analysis_prev_pdu_time
= -1;
140 static int hf_analysis_retrans_no
= -1;
141 static int hf_analysis_ack_num
= -1;
142 static int hf_analysis_ack_missing
= -1;
143 static int hf_analysis_ack_dup_no
= -1;
144 static int hf_analysis_msg_resend_from
= -1;
145 static int hf_analysis_ack_resend_from
= -1;
146 static int hf_analysis_total_time
= -1;
148 static gint ett_p_mul
= -1;
149 static gint ett_pdu_type
= -1;
150 static gint ett_dest_entry
= -1;
151 static gint ett_ack_entry
= -1;
152 static gint ett_range_entry
= -1;
153 static gint ett_checksum
= -1;
154 static gint ett_seq_analysis
= -1;
155 static gint ett_ack_analysis
= -1;
156 static gint ett_seq_ack_analysis
= -1;
157 static gint ett_msg_fragment
= -1;
158 static gint ett_msg_fragments
= -1;
160 static expert_field ei_more_data
= EI_INIT
;
161 static expert_field ei_checksum_bad
= EI_INIT
;
162 static expert_field ei_tot_miss_seq_no
= EI_INIT
;
163 static expert_field ei_miss_seq_no
= EI_INIT
;
164 static expert_field ei_analysis_ack_missing
= EI_INIT
;
165 static expert_field ei_miss_seq_range
= EI_INIT
;
166 static expert_field ei_address_pdu_missing
= EI_INIT
;
167 static expert_field ei_analysis_ack_dup_no
= EI_INIT
;
168 static expert_field ei_length
= EI_INIT
;
169 static expert_field ei_analysis_prev_pdu_missing
= EI_INIT
;
170 static expert_field ei_message_discarded
= EI_INIT
;
171 static expert_field ei_ack_length
= EI_INIT
;
172 static expert_field ei_analysis_retrans_no
= EI_INIT
;
174 static dissector_handle_t p_mul_handle
= NULL
;
176 static dissector_handle_t data_handle
= NULL
;
178 typedef struct _p_mul_id_key
{
184 typedef struct _p_mul_seq_val
{
185 gint msg_type
; /* Message type */
186 guint32 prev_msg_id
; /* Previous message package num */
187 nstime_t prev_msg_time
; /* Previous message receive time */
188 guint32 addr_id
; /* PDU package num for Address_PDU */
189 nstime_t addr_time
; /* PDU received time for Address_PDU */
190 guint32 pdu_id
; /* PDU package num */
191 nstime_t pdu_time
; /* PDU receive time */
192 guint32 prev_pdu_id
; /* Previous PDU package num */
193 nstime_t prev_pdu_time
; /* Previous PDU receive time */
194 guint16 last_found_pdu
; /* Last PDU num */
195 nstime_t first_msg_time
; /* First message receive time */
196 guint32 msg_resend_count
; /* Message resend counter */
197 GHashTable
*ack_data
;
200 typedef struct _p_mul_ack_data
{
201 guint32 ack_id
; /* Ack PDU package num */
202 guint32 ack_resend_count
; /* Ack resend counter */
205 /* Hash table with current data for seq/ack analysis */
206 static GHashTable
*p_mul_id_hash_table
= NULL
;
208 /* List of hash tables stored in each frame */
209 static GList
*p_mul_package_data_list
= NULL
;
211 /* User definable values to use for dissection */
212 static range_t
*global_p_mul_port_range
;
213 static gboolean p_mul_reassemble
= TRUE
;
214 static gint decode_option
= DECODE_NONE
;
215 static gboolean use_relative_msgid
= TRUE
;
216 static gboolean use_seq_ack_analysis
= TRUE
;
218 static reassembly_table p_mul_reassembly_table
;
220 static guint32 message_id_offset
= 0;
222 static const fragment_items p_mul_frag_items
= {
223 /* Fragment subtrees */
226 /* Fragment fields */
229 &hf_msg_fragment_overlap
,
230 &hf_msg_fragment_overlap_conflicts
,
231 &hf_msg_fragment_multiple_tails
,
232 &hf_msg_fragment_too_long_fragment
,
233 &hf_msg_fragment_error
,
234 &hf_msg_fragment_count
,
235 /* Reassembled in field */
236 &hf_msg_reassembled_in
,
237 /* Reassembled length field */
238 &hf_msg_reassembled_length
,
239 /* Reassembled data field */
245 static const value_string pdu_vals
[] = {
246 { Data_PDU
, "Data PDU" },
247 { Ack_PDU
, "Ack PDU" },
248 { Address_PDU
, "Address PDU" },
249 { Discard_Message_PDU
, "Discard Message PDU" },
250 { Announce_PDU
, "Announce PDU" },
251 { Request_PDU
, "Request PDU" },
252 { Reject_PDU
, "Reject PDU" },
253 { Release_PDU
, "Release PDU" },
254 { FEC_Address_PDU
, "FEC Address PDU" },
255 { Extra_Address_PDU
, "Extra Address PDU" },
256 { Extra_FEC_Address_PDU
, "Extra FEC Address PDU" },
257 { Ack_Ack_PDU
, "Ack-Ack PDU" },
261 static const enum_val_t decode_options
[] = {
262 { "none", "No decoding", DECODE_NONE
},
263 { "ber", "BER encoded ASN.1", DECODE_BER
},
264 { "cdt", "Compressed Data Type", DECODE_CDT
},
268 static const true_false_string no_yes
= {
272 static const gchar
*get_type (guint8 value
)
274 return val_to_str_const (value
, pdu_vals
, "Unknown");
278 /*Function checksum, found in ACP142 annex B-3 */
279 static guint16
checksum (guint8
*buffer
, gint len
, gint offset
)
281 guint16 c0
= 0, c1
= 0, ret
, ctmp
;
285 if (len
< offset
+2) {
286 /* Buffer to small */
291 buffer
[offset
+1] = 0;
292 ctmp
= len
- offset
- 1;
298 if ((c0
+= *hpp
++) > 254) { c0
-= 255; }
299 if ((c1
+= c0
) > 254) { c1
-= 255; }
302 if ((cs
= ((ctmp
* c0
) - c1
) % 255) < 0) { cs
+= 255; }
304 if ((cs
= (c1
- ((ctmp
+ 1L) * c0
)) % 255) < 0) { cs
+= 255; }
310 static guint
p_mul_id_hash (gconstpointer k
)
312 const p_mul_id_key
*p_mul
= (const p_mul_id_key
*)k
;
316 static gint
p_mul_id_hash_equal (gconstpointer k1
, gconstpointer k2
)
318 const p_mul_id_key
*p_mul1
= (const p_mul_id_key
*)k1
;
319 const p_mul_id_key
*p_mul2
= (const p_mul_id_key
*)k2
;
321 if (p_mul1
->id
!= p_mul2
->id
)
324 if (p_mul1
->seq
!= p_mul2
->seq
)
327 return (ADDRESSES_EQUAL (&p_mul1
->addr
, &p_mul2
->addr
));
330 static p_mul_seq_val
*lookup_seq_val (guint32 message_id
, guint16 seq_no
,
333 p_mul_seq_val
*pkg_data
;
334 p_mul_id_key
*p_mul_key
= wmem_new(wmem_file_scope(), p_mul_id_key
);
336 p_mul_key
->id
= message_id
;
337 p_mul_key
->seq
= seq_no
;
338 SE_COPY_ADDRESS(&p_mul_key
->addr
, addr
);
340 pkg_data
= (p_mul_seq_val
*) g_hash_table_lookup (p_mul_id_hash_table
, p_mul_key
);
345 static void p_mul_id_value_destroy (p_mul_seq_val
*pkg_data
)
347 if (pkg_data
->ack_data
) {
348 g_hash_table_destroy (pkg_data
->ack_data
);
353 static void p_mul_package_data_destroy (GHashTable
*pkg_list
, gpointer user_data _U_
)
355 g_hash_table_destroy (pkg_list
);
358 static void copy_hashtable_data (gpointer key
, p_mul_ack_data
*ack_data1
, GHashTable
*table
)
360 p_mul_ack_data
*ack_data2
;
362 ack_data2
= wmem_new(wmem_file_scope(), p_mul_ack_data
);
363 ack_data2
->ack_id
= ack_data1
->ack_id
;
364 ack_data2
->ack_resend_count
= ack_data1
->ack_resend_count
;
366 g_hash_table_insert (table
, key
, ack_data2
);
369 static p_mul_seq_val
*register_p_mul_id (packet_info
*pinfo
, address
*addr
, guint32 dstIP
,
370 guint8 pdu_type
, guint32 message_id
,
371 guint16 seq_no
, gint no_missing
)
373 p_mul_seq_val
*p_mul_data
= NULL
, *pkg_data
= NULL
;
374 p_mul_id_key
*p_mul_key
;
375 p_mul_ack_data
*ack_data
= NULL
;
376 nstime_t addr_time
, prev_time
;
377 guint addr_id
= 0, prev_id
= 0;
378 guint16 last_found_pdu
= 0;
379 gboolean missing_pdu
= FALSE
, need_set_address
= FALSE
;
380 GHashTable
*pkg_list
;
382 if (pinfo
->flags
.in_error_pkt
) {
383 /* No analysis of error packets */
387 nstime_set_zero(&addr_time
);
388 nstime_set_zero(&prev_time
);
390 p_mul_key
= wmem_new(wmem_file_scope(), p_mul_id_key
);
392 if (!pinfo
->fd
->flags
.visited
&&
393 (pdu_type
== Address_PDU
|| pdu_type
== Data_PDU
|| pdu_type
== Discard_Message_PDU
))
395 /* Try to match corresponding address PDU */
396 p_mul_key
->id
= message_id
;
398 SE_COPY_ADDRESS(&p_mul_key
->addr
, addr
);
399 need_set_address
= TRUE
;
401 p_mul_data
= (p_mul_seq_val
*) g_hash_table_lookup (p_mul_id_hash_table
, p_mul_key
);
404 /* Found address PDU */
405 last_found_pdu
= p_mul_data
->last_found_pdu
;
406 p_mul_data
->last_found_pdu
= seq_no
;
407 addr_id
= p_mul_data
->pdu_id
;
408 addr_time
= p_mul_data
->pdu_time
;
410 /* Save data for last found PDU */
411 p_mul_data
->prev_pdu_id
= pinfo
->fd
->num
;
412 p_mul_data
->prev_pdu_time
= pinfo
->fd
->abs_ts
;
414 if (pdu_type
== Data_PDU
&& p_mul_data
->msg_resend_count
== 0 && last_found_pdu
!= seq_no
- 1) {
415 /* Data_PDU and missing previous PDU */
419 if (last_found_pdu
) {
420 /* Try to match previous data PDU */
421 p_mul_key
->seq
= last_found_pdu
;
422 p_mul_data
= (p_mul_seq_val
*) g_hash_table_lookup (p_mul_id_hash_table
, p_mul_key
);
426 /* Found a previous PDU (Address or Data) */
427 if (p_mul_data
->prev_msg_id
> 0) {
428 prev_id
= p_mul_data
->prev_msg_id
;
430 prev_id
= p_mul_data
->pdu_id
;
432 prev_time
= p_mul_data
->pdu_time
;
434 } else if (pdu_type
== Address_PDU
) {
435 addr_id
= pinfo
->fd
->num
;
436 addr_time
= pinfo
->fd
->abs_ts
;
440 pkg_list
= (GHashTable
*)p_get_proto_data(pinfo
->fd
, proto_p_mul
, 0);
442 /* Never saved list for this packet, create a new */
443 pkg_list
= g_hash_table_new (NULL
, NULL
);
444 p_mul_package_data_list
= g_list_append (p_mul_package_data_list
, pkg_list
);
445 p_add_proto_data (pinfo
->fd
, proto_p_mul
, 0, pkg_list
);
448 if (!pinfo
->fd
->flags
.visited
) {
449 p_mul_key
->id
= message_id
;
450 p_mul_key
->seq
= seq_no
;
451 if (!need_set_address
) {
452 SE_COPY_ADDRESS(&p_mul_key
->addr
, addr
);
454 p_mul_data
= (p_mul_seq_val
*) g_hash_table_lookup (p_mul_id_hash_table
, p_mul_key
);
457 if (pdu_type
== Ack_PDU
) {
458 /* Only save this data if positive ack */
459 if (no_missing
== 0) {
460 ack_data
= (p_mul_ack_data
*)g_hash_table_lookup (p_mul_data
->ack_data
, GUINT_TO_POINTER(dstIP
));
462 /* Only save reference to first ACK */
463 ack_data
= wmem_new0(wmem_file_scope(), p_mul_ack_data
);
464 ack_data
->ack_id
= pinfo
->fd
->num
;
465 g_hash_table_insert (p_mul_data
->ack_data
, GUINT_TO_POINTER(dstIP
), ack_data
);
467 /* Only count when resending */
468 ack_data
->ack_resend_count
++;
473 p_mul_data
->msg_resend_count
++;
474 p_mul_data
->prev_msg_id
= pinfo
->fd
->num
;
475 p_mul_data
->prev_msg_time
= p_mul_data
->pdu_time
;
476 p_mul_data
->pdu_time
= pinfo
->fd
->abs_ts
;
478 if (pdu_type
== Data_PDU
) {
479 p_mul_data
->prev_pdu_id
= prev_id
;
480 p_mul_data
->prev_pdu_time
= prev_time
;
485 if (pdu_type
== Ack_PDU
) {
486 p_mul_data
= wmem_new0(wmem_file_scope(), p_mul_seq_val
);
488 p_mul_data
= (p_mul_seq_val
*)g_malloc0(sizeof (p_mul_seq_val
));
490 p_mul_data
->msg_type
= pdu_type
;
491 if (pdu_type
== Address_PDU
|| pdu_type
== Ack_PDU
) {
492 p_mul_data
->ack_data
= g_hash_table_new (NULL
, NULL
);
495 if (pdu_type
== Ack_PDU
) {
496 /* No matching message for this ack */
497 ack_data
= wmem_new0(wmem_file_scope(), p_mul_ack_data
);
498 ack_data
->ack_id
= pinfo
->fd
->num
;
499 g_hash_table_insert (p_mul_data
->ack_data
, GUINT_TO_POINTER(dstIP
), ack_data
);
501 p_mul_data
->pdu_id
= pinfo
->fd
->num
;
502 p_mul_data
->pdu_time
= pinfo
->fd
->abs_ts
;
503 p_mul_data
->addr_id
= addr_id
;
504 p_mul_data
->addr_time
= addr_time
;
505 p_mul_data
->first_msg_time
= pinfo
->fd
->abs_ts
;
507 if (pdu_type
== Data_PDU
&& !missing_pdu
) {
508 p_mul_data
->prev_pdu_id
= prev_id
;
509 p_mul_data
->prev_pdu_time
= prev_time
;
512 g_hash_table_insert (p_mul_id_hash_table
, p_mul_key
, p_mul_data
);
516 /* Copy the current package data to the frame */
517 pkg_data
= wmem_new(wmem_file_scope(), p_mul_seq_val
);
518 *pkg_data
= *p_mul_data
;
519 if (p_mul_data
->ack_data
) {
520 /* Copy the hash table for ack data */
521 pkg_data
->ack_data
= g_hash_table_new (NULL
, NULL
);
522 g_hash_table_foreach (p_mul_data
->ack_data
, (GHFunc
) copy_hashtable_data
, pkg_data
->ack_data
);
524 g_hash_table_insert (pkg_list
, GUINT_TO_POINTER(message_id
), pkg_data
);
526 /* Fetch last values from data saved in packet */
527 pkg_data
= (p_mul_seq_val
*)g_hash_table_lookup (pkg_list
, GUINT_TO_POINTER(message_id
));
530 DISSECTOR_ASSERT (pkg_data
);
534 static void add_ack_analysis (tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*p_mul_tree
,
535 gint offset
, guint8 pdu_type
, address
*src
, address
*dst
,
536 guint32 message_id
, gint no_missing
)
538 proto_tree
*analysis_tree
= NULL
;
539 proto_item
*sa
= NULL
;
540 proto_item
*en
= NULL
;
541 p_mul_seq_val
*pkg_data
= NULL
;
542 p_mul_ack_data
*ack_data
= NULL
;
543 gboolean item_added
= FALSE
;
547 if (pinfo
->flags
.in_error_pkt
) {
548 /* No analysis of error packets */
552 if (pdu_type
== Address_PDU
) {
553 sa
= proto_tree_add_text (p_mul_tree
, tvb
, 0, 0, "ACK analysis");
554 PROTO_ITEM_SET_GENERATED (sa
);
555 analysis_tree
= proto_item_add_subtree (sa
, ett_ack_analysis
);
557 /* Fetch package data */
558 if ((pkg_data
= lookup_seq_val (message_id
, 0, src
)) == NULL
) {
559 /* No need for seq/ack analysis yet */
565 if (pkg_data
->addr_id
) {
566 en
= proto_tree_add_uint (analysis_tree
, hf_analysis_addr_pdu_num
, tvb
,
567 0, 0, pkg_data
->addr_id
);
568 PROTO_ITEM_SET_GENERATED (en
);
570 nstime_delta (&ns
, &pinfo
->fd
->abs_ts
, &pkg_data
->addr_time
);
571 en
= proto_tree_add_time (analysis_tree
, hf_analysis_total_time
,
573 PROTO_ITEM_SET_GENERATED (en
);
575 proto_tree_add_expert(analysis_tree
, pinfo
, &ei_address_pdu_missing
, tvb
, offset
, 0);
579 memcpy((guint8
*)&dstIp
, dst
->data
, 4);
580 if (pkg_data
->ack_data
) {
581 ack_data
= (p_mul_ack_data
*)g_hash_table_lookup (pkg_data
->ack_data
, GUINT_TO_POINTER(dstIp
));
584 /* Add reference to Ack_PDU */
585 if (ack_data
&& ack_data
->ack_id
) {
586 en
= proto_tree_add_uint (analysis_tree
, hf_analysis_ack_num
, tvb
,
587 0, 0, ack_data
->ack_id
);
588 PROTO_ITEM_SET_GENERATED (en
);
590 } else if (!pkg_data
->msg_resend_count
) {
591 en
= proto_tree_add_item (analysis_tree
,
592 hf_analysis_ack_missing
,
593 tvb
, offset
, 0, ENC_NA
);
594 if (pinfo
->fd
->flags
.visited
) {
595 /* We do not know this on first visit and we do not want to
596 add a entry in the "Expert Severity Info" for this note */
597 expert_add_info(pinfo
, en
, &ei_analysis_ack_missing
);
598 PROTO_ITEM_SET_GENERATED (en
);
605 PROTO_ITEM_SET_HIDDEN (sa
);
607 } else if (pdu_type
== Ack_PDU
) {
608 sa
= proto_tree_add_text (p_mul_tree
, tvb
, 0, 0, "SEQ/ACK analysis");
609 PROTO_ITEM_SET_GENERATED (sa
);
610 analysis_tree
= proto_item_add_subtree (sa
, ett_seq_ack_analysis
);
612 /* Fetch package data */
613 memcpy((guint8
*)&dstIp
, dst
->data
, 4);
614 if ((pkg_data
= register_p_mul_id (pinfo
, src
, dstIp
, pdu_type
, message_id
, 0, no_missing
)) == NULL
) {
615 /* No need for seq/ack analysis yet */
618 if (pkg_data
->ack_data
) {
619 ack_data
= (p_mul_ack_data
*)g_hash_table_lookup (pkg_data
->ack_data
, GUINT_TO_POINTER(dstIp
));
622 /* Add reference to Address_PDU */
623 if (pkg_data
->msg_type
!= Ack_PDU
) {
624 en
= proto_tree_add_uint (analysis_tree
, hf_analysis_addr_pdu_num
, tvb
,
625 0, 0, pkg_data
->pdu_id
);
626 PROTO_ITEM_SET_GENERATED (en
);
628 if (no_missing
== 0) {
629 nstime_delta (&ns
, &pinfo
->fd
->abs_ts
, &pkg_data
->first_msg_time
);
630 en
= proto_tree_add_time (analysis_tree
, hf_analysis_trans_time
,
632 PROTO_ITEM_SET_GENERATED (en
);
635 proto_tree_add_expert(analysis_tree
, pinfo
, &ei_address_pdu_missing
, tvb
, offset
, 0);
638 if (pkg_data
->msg_type
!= Ack_PDU
&& pkg_data
->prev_pdu_id
) {
639 /* Add reference to previous PDU */
640 en
= proto_tree_add_uint (analysis_tree
, hf_analysis_last_pdu_num
,
641 tvb
, 0, 0, pkg_data
->prev_pdu_id
);
642 PROTO_ITEM_SET_GENERATED (en
);
644 nstime_delta (&ns
, &pinfo
->fd
->abs_ts
, &pkg_data
->prev_pdu_time
);
645 en
= proto_tree_add_time (analysis_tree
, hf_analysis_ack_time
,
647 PROTO_ITEM_SET_GENERATED (en
);
650 if (ack_data
&& ack_data
->ack_resend_count
) {
651 /* Add resend statistics */
652 en
= proto_tree_add_uint (analysis_tree
, hf_analysis_ack_dup_no
,
653 tvb
, 0, 0, ack_data
->ack_resend_count
);
654 PROTO_ITEM_SET_GENERATED (en
);
656 expert_add_info_format(pinfo
, en
, &ei_analysis_ack_dup_no
, "Dup ACK #%d", ack_data
->ack_resend_count
);
658 en
= proto_tree_add_uint (analysis_tree
, hf_analysis_ack_resend_from
,
659 tvb
, 0, 0, ack_data
->ack_id
);
660 PROTO_ITEM_SET_GENERATED (en
);
662 col_append_fstr (pinfo
->cinfo
, COL_INFO
, "[Dup ACK %d#%d] ",
663 ack_data
->ack_id
, ack_data
->ack_resend_count
);
668 static p_mul_seq_val
*add_seq_analysis (tvbuff_t
*tvb
, packet_info
*pinfo
,
669 proto_tree
*p_mul_tree
, address
*src
,
671 guint8 pdu_type
, guint32 message_id
,
672 guint16 seq_no
, gint no_missing
)
674 p_mul_seq_val
*pkg_data
;
675 proto_tree
*analysis_tree
;
676 proto_item
*sa
, *en
= NULL
, *eh
= NULL
;
677 gboolean item_added
= FALSE
;
680 pkg_data
= register_p_mul_id (pinfo
, src
, 0, pdu_type
, message_id
, seq_no
,
684 /* No need for seq/ack analysis */
688 sa
= proto_tree_add_text (p_mul_tree
, tvb
, 0, 0, "SEQ analysis");
689 PROTO_ITEM_SET_GENERATED (sa
);
690 analysis_tree
= proto_item_add_subtree (sa
, ett_seq_analysis
);
692 if (pdu_type
== Data_PDU
|| pdu_type
== Discard_Message_PDU
) {
693 /* Add reference to Address_PDU */
694 if (pkg_data
->addr_id
) {
695 en
= proto_tree_add_uint (analysis_tree
, hf_analysis_addr_pdu_num
, tvb
,
696 0, 0, pkg_data
->addr_id
);
697 PROTO_ITEM_SET_GENERATED (en
);
699 nstime_delta (&ns
, &pinfo
->fd
->abs_ts
, &pkg_data
->addr_time
);
700 en
= proto_tree_add_time (analysis_tree
, hf_analysis_addr_pdu_time
,
702 PROTO_ITEM_SET_GENERATED (en
);
704 if (pkg_data
->prev_pdu_id
== pkg_data
->addr_id
) {
705 /* Previous pdu time is the same as time since address pdu */
706 en
= proto_tree_add_time (analysis_tree
, hf_analysis_prev_pdu_time
,
708 PROTO_ITEM_SET_GENERATED (en
);
711 } else if (!pkg_data
->msg_resend_count
) {
712 proto_tree_add_expert(analysis_tree
, pinfo
, &ei_address_pdu_missing
, tvb
, offset
, 0);
717 if ((pdu_type
== Data_PDU
) && (pkg_data
->prev_pdu_id
!= pkg_data
->addr_id
)) {
718 /* Add reference to previous Data_PDU */
719 if (pkg_data
->prev_pdu_id
) {
720 en
= proto_tree_add_uint (analysis_tree
, hf_analysis_prev_pdu_num
, tvb
,
721 0, 0, pkg_data
->prev_pdu_id
);
722 PROTO_ITEM_SET_GENERATED (en
);
724 nstime_delta (&ns
, &pinfo
->fd
->abs_ts
, &pkg_data
->prev_pdu_time
);
725 en
= proto_tree_add_time (analysis_tree
, hf_analysis_prev_pdu_time
,
727 PROTO_ITEM_SET_GENERATED (en
);
729 } else if (!pkg_data
->msg_resend_count
) {
730 proto_tree_add_expert(analysis_tree
, pinfo
, &ei_analysis_prev_pdu_missing
, tvb
, offset
, 0);
735 if ((pdu_type
== Address_PDU
) || (pdu_type
== Data_PDU
) ||
736 (pdu_type
== Discard_Message_PDU
)) {
737 /* Add resend statistics */
738 if (pkg_data
->msg_resend_count
) {
739 en
= proto_tree_add_uint (analysis_tree
, hf_analysis_retrans_no
,
740 tvb
, 0, 0, pkg_data
->msg_resend_count
);
741 PROTO_ITEM_SET_GENERATED (en
);
743 en
= proto_tree_add_uint (analysis_tree
, hf_analysis_msg_resend_from
,
744 tvb
, 0, 0, pkg_data
->pdu_id
);
745 PROTO_ITEM_SET_GENERATED (en
);
747 expert_add_info_format(pinfo
, en
, &ei_analysis_retrans_no
, "Retransmission #%d", pkg_data
->msg_resend_count
);
749 nstime_delta (&ns
, &pinfo
->fd
->abs_ts
, &pkg_data
->prev_msg_time
);
750 en
= proto_tree_add_time (analysis_tree
, hf_analysis_retrans_time
,
752 PROTO_ITEM_SET_GENERATED (en
);
754 nstime_delta (&ns
, &pinfo
->fd
->abs_ts
, &pkg_data
->first_msg_time
);
755 eh
= proto_tree_add_time (analysis_tree
, hf_analysis_total_retrans_time
,
757 PROTO_ITEM_SET_GENERATED (eh
);
759 if (pkg_data
->first_msg_time
.secs
== pkg_data
->prev_msg_time
.secs
&&
760 pkg_data
->first_msg_time
.nsecs
== pkg_data
->prev_msg_time
.nsecs
) {
761 /* Time values does not differ, hide the total time */
762 PROTO_ITEM_SET_HIDDEN (eh
);
766 col_append_fstr (pinfo
->cinfo
, COL_INFO
, "[Retrans %d#%d] ",
767 pkg_data
->pdu_id
, pkg_data
->msg_resend_count
);
772 PROTO_ITEM_SET_HIDDEN (sa
);
779 static void dissect_reassembled_data (tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
)
781 DISSECTOR_ASSERT(tvb
!= NULL
);
783 switch (decode_option
) {
785 dissect_unknown_ber (pinfo
, tvb
, 0, tree
);
788 dissect_cdt (tvb
, pinfo
, tree
);
791 call_dissector (data_handle
, tvb
, pinfo
, tree
);
796 static void dissect_p_mul (tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
)
798 proto_tree
*p_mul_tree
, *field_tree
, *checksum_tree
;
799 proto_item
*ti
, *en
, *len_en
;
800 gboolean save_fragmented
;
801 guint32 message_id
= 0, ip
;
802 guint16 no_dest
= 0, count
= 0, len
, data_len
= 0;
803 guint16 checksum1
, checksum2
;
804 guint16 pdu_length
, no_pdus
= 0, seq_no
= 0;
805 guint8 pdu_type
, *value
, map
= 0, fec_len
;
806 gint i
, tot_no_missing
= 0, no_missing
= 0, offset
= 0;
808 wmem_strbuf_t
*message_id_list
= NULL
;
811 col_set_str (pinfo
->cinfo
, COL_PROTOCOL
, "P_MUL");
812 col_clear (pinfo
->cinfo
, COL_INFO
);
814 /* First fetch PDU Type */
815 pdu_type
= tvb_get_guint8 (tvb
, offset
+ 3) & 0x3F;
817 ti
= proto_tree_add_item (tree
, proto_p_mul
, tvb
, offset
, -1, ENC_NA
);
818 proto_item_append_text (ti
, ", %s", get_type (pdu_type
));
819 p_mul_tree
= proto_item_add_subtree (ti
, ett_p_mul
);
822 pdu_length
= tvb_get_ntohs (tvb
, offset
);
823 len_en
= proto_tree_add_item (p_mul_tree
, hf_length
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
831 case Discard_Message_PDU
:
832 case Extra_Address_PDU
:
833 case FEC_Address_PDU
:
834 case Extra_FEC_Address_PDU
:
836 proto_tree_add_item (p_mul_tree
, hf_priority
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
841 proto_tree_add_item (p_mul_tree
, hf_unused8
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
846 en
= proto_tree_add_uint_format_value(p_mul_tree
, hf_pdu_type
, tvb
, offset
, 1,
847 pdu_type
, "%s (0x%02x)",
848 get_type (pdu_type
), pdu_type
);
849 field_tree
= proto_item_add_subtree (en
, ett_pdu_type
);
851 if (pdu_type
== Discard_Message_PDU
) {
852 expert_add_info(pinfo
, en
, &ei_message_discarded
);
859 case Extra_Address_PDU
:
860 case FEC_Address_PDU
:
861 case Extra_FEC_Address_PDU
:
862 map
= tvb_get_guint8 (tvb
, offset
);
863 proto_tree_add_item (field_tree
, hf_map_first
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
864 proto_tree_add_item (field_tree
, hf_map_last
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
865 if ((map
& 0x80) || (map
& 0x40)) {
866 proto_item_append_text (en
, ", %s / %s",
867 (map
& 0x80) ? "Not first" : "First",
868 (map
& 0x40) ? "Not last" : "Last");
870 proto_item_append_text (en
, ", Only one PDU");
875 proto_tree_add_item (field_tree
, hf_map_unused
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
878 proto_tree_add_item (field_tree
, hf_pdu_type_value
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
884 case Extra_Address_PDU
:
885 case FEC_Address_PDU
:
886 case Extra_FEC_Address_PDU
:
887 /* Total Number of PDUs */
888 no_pdus
= tvb_get_ntohs (tvb
, offset
);
890 proto_tree_add_item (p_mul_tree
, hf_no_pdus
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
891 proto_item_append_text (ti
, ", No PDUs: %u", no_pdus
);
895 /* Sequence Number of PDUs */
896 seq_no
= tvb_get_ntohs (tvb
, offset
);
897 proto_tree_add_item (p_mul_tree
, hf_seq_no
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
898 proto_item_append_text (ti
, ", Seq no: %u", seq_no
);
902 /* Count of Destination Entries */
903 count
= tvb_get_ntohs (tvb
, offset
);
904 proto_tree_add_item (p_mul_tree
, hf_count_of_dest
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
909 proto_tree_add_item (p_mul_tree
, hf_unused16
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
915 en
= proto_tree_add_item (p_mul_tree
, hf_checksum
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
916 checksum_tree
= proto_item_add_subtree (en
, ett_checksum
);
917 len
= tvb_length (tvb
);
918 value
= tvb_get_string (wmem_packet_scope(), tvb
, 0, len
);
919 checksum1
= checksum (value
, len
, offset
);
920 checksum2
= tvb_get_ntohs (tvb
, offset
);
921 if (checksum1
== checksum2
) {
922 proto_item_append_text (en
, " (correct)");
923 en
= proto_tree_add_boolean (checksum_tree
, hf_checksum_good
, tvb
,
925 PROTO_ITEM_SET_GENERATED (en
);
926 en
= proto_tree_add_boolean (checksum_tree
, hf_checksum_bad
, tvb
,
928 PROTO_ITEM_SET_GENERATED (en
);
930 proto_item_append_text (en
, " (incorrect, should be 0x%04x)", checksum1
);
931 expert_add_info(pinfo
, en
, &ei_checksum_bad
);
932 en
= proto_tree_add_boolean (checksum_tree
, hf_checksum_good
, tvb
,
934 PROTO_ITEM_SET_GENERATED (en
);
935 en
= proto_tree_add_boolean (checksum_tree
, hf_checksum_bad
, tvb
,
937 PROTO_ITEM_SET_GENERATED (en
);
941 if (pdu_type
== Ack_PDU
) {
942 /* Source ID of Ack Sender */
943 ip
= tvb_get_ipv4 (tvb
, offset
);
944 SET_ADDRESS (&dst
, AT_IPv4
, sizeof(ip
), wmem_memdup (wmem_packet_scope(), &ip
, 4));
945 proto_tree_add_item (p_mul_tree
, hf_source_id_ack
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
948 /* Count of Ack Info Entries */
949 count
= tvb_get_ntohs (tvb
, offset
);
950 proto_tree_add_item (p_mul_tree
, hf_ack_count
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
954 ip
= tvb_get_ipv4 (tvb
, offset
);
955 SET_ADDRESS (&src
, AT_IPv4
, sizeof(ip
), wmem_memdup (wmem_packet_scope(), &ip
, 4));
956 proto_tree_add_item (p_mul_tree
, hf_source_id
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
960 message_id
= tvb_get_ntohl (tvb
, offset
);
961 if (use_relative_msgid
) {
962 if (message_id_offset
== 0) {
963 /* First P_Mul package - initialize message_id_offset */
964 message_id_offset
= message_id
;
966 message_id
-= message_id_offset
;
967 proto_tree_add_uint_format_value(p_mul_tree
, hf_message_id
, tvb
, offset
, 4,
968 message_id
, "%u (relative message id)", message_id
);
970 proto_tree_add_item (p_mul_tree
, hf_message_id
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
974 proto_item_append_text (ti
, ", MSID: %u", message_id
);
977 if (pdu_type
== Address_PDU
|| pdu_type
== Announce_PDU
||
978 pdu_type
== Extra_Address_PDU
|| pdu_type
== FEC_Address_PDU
||
979 pdu_type
== Extra_FEC_Address_PDU
) {
981 ts
.secs
= tvb_get_ntohl (tvb
, offset
);
983 proto_tree_add_time (p_mul_tree
, hf_expiry_time
, tvb
, offset
, 4, &ts
);
987 if (pdu_type
== FEC_Address_PDU
|| pdu_type
== Extra_FEC_Address_PDU
) {
988 /* FEC Parameters Length */
989 fec_len
= tvb_get_guint8 (tvb
, offset
);
990 proto_tree_add_item (p_mul_tree
, hf_fec_len
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
994 proto_tree_add_item (p_mul_tree
, hf_fec_id
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
999 proto_tree_add_none_format (p_mul_tree
, hf_fec_parameters
, tvb
, offset
,
1000 fec_len
, "FEC Parameters (%d byte%s)",
1001 fec_len
, plurality (fec_len
, "", "s"));
1009 case Extra_Address_PDU
:
1010 case FEC_Address_PDU
:
1011 case Extra_FEC_Address_PDU
:
1012 /* Count of Destination Entries */
1013 no_dest
= tvb_get_ntohs (tvb
, offset
);
1014 proto_tree_add_item (p_mul_tree
, hf_count_of_dest
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
1017 /* Length of Reserved Field */
1018 len
= tvb_get_ntohs (tvb
, offset
);
1019 proto_tree_add_item (p_mul_tree
, hf_length_of_res
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
1022 for (i
= 0; i
< no_dest
; i
++) {
1023 /* Destination Entry */
1024 en
= proto_tree_add_none_format (p_mul_tree
, hf_dest_entry
, tvb
,
1026 "Destination Entry #%d", i
+ 1);
1027 field_tree
= proto_item_add_subtree (en
, ett_dest_entry
);
1029 /* Destination Id */
1030 ip
= tvb_get_ipv4 (tvb
, offset
);
1031 SET_ADDRESS (&dst
, AT_IPv4
, sizeof(ip
), wmem_memdup(wmem_packet_scope(), &ip
, 4));
1032 proto_tree_add_item (field_tree
, hf_dest_id
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
1035 /* Message Sequence Number */
1036 proto_tree_add_item (field_tree
, hf_msg_seq_no
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
1040 /* Reserved Field (variable length) */
1041 proto_tree_add_none_format (field_tree
, hf_sym_key
, tvb
, offset
,
1042 len
, "Symmetric Key (%d byte%s)",
1043 len
, plurality (len
, "", "s"));
1047 if (use_seq_ack_analysis
) {
1048 add_ack_analysis (tvb
, pinfo
, field_tree
, offset
, pdu_type
, &src
, &dst
,
1052 if (no_dest
== 0 && use_seq_ack_analysis
) {
1053 /* Add Ack-Ack analysis */
1054 add_ack_analysis (tvb
, pinfo
, p_mul_tree
, offset
, pdu_type
, &src
, NULL
,
1058 proto_item_append_text (ti
, ", Count of Dest: %u", no_dest
);
1062 /* Fragment of Data (variable length) */
1063 data_len
= tvb_length_remaining (tvb
, offset
);
1064 proto_tree_add_none_format (p_mul_tree
, hf_data_fragment
, tvb
, offset
,
1065 data_len
, "Fragment %d of Data (%d byte%s)",
1067 plurality (data_len
, "", "s"));
1071 message_id_list
= wmem_strbuf_new_label(wmem_packet_scope());
1073 for (i
= 0; i
< count
; i
++) {
1074 /* Ack Info Entry */
1075 len
= tvb_get_ntohs (tvb
, offset
);
1077 en
= proto_tree_add_none_format (p_mul_tree
, hf_ack_entry
, tvb
,
1079 "Ack Info Entry #%d", i
+ 1);
1080 field_tree
= proto_item_add_subtree (en
, ett_ack_entry
);
1082 /* Length of Ack Info Entry */
1083 en
= proto_tree_add_item (field_tree
, hf_ack_length
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
1087 proto_item_append_text (en
, " (invalid length)");
1088 expert_add_info(pinfo
, en
, &ei_ack_length
);
1092 ip
= tvb_get_ipv4 (tvb
, offset
);
1093 SET_ADDRESS (&src
, AT_IPv4
, sizeof(ip
), wmem_memdup (wmem_packet_scope(), &ip
, 4));
1094 proto_tree_add_item (field_tree
, hf_source_id
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
1098 message_id
= tvb_get_ntohl (tvb
, offset
);
1099 if (use_relative_msgid
) {
1100 if (message_id_offset
== 0) {
1101 /* First P_Mul package - initialize message_id_offset */
1102 message_id_offset
= message_id
;
1104 message_id
-= message_id_offset
;
1105 proto_tree_add_uint_format_value(field_tree
, hf_message_id
, tvb
, offset
, 4,
1106 message_id
, "%u (relative message id)", message_id
);
1108 proto_tree_add_item (field_tree
, hf_message_id
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
1113 wmem_strbuf_append_printf (message_id_list
, "%u", message_id
);
1115 wmem_strbuf_append_printf (message_id_list
, ",%u", message_id
);
1119 gint num_seq_no
= (len
- 10) / 2;
1120 guint16 ack_seq_no
, prev_ack_seq_no
= 0;
1121 for (no_missing
= 0; no_missing
< num_seq_no
; no_missing
++) {
1122 /* Missing Data PDU Seq Number */
1123 ack_seq_no
= tvb_get_ntohs (tvb
, offset
);
1124 if ((ack_seq_no
!= 0) && (no_missing
< num_seq_no
- 2) && tvb_get_ntohs (tvb
, offset
+ 2) == 0) {
1125 /* We are handling a range */
1126 guint16 end_seq_no
= tvb_get_ntohs (tvb
, offset
+ 4);
1128 en
= proto_tree_add_bytes_format_value(field_tree
, hf_miss_seq_range
,
1129 tvb
, offset
, 6, NULL
,
1131 ack_seq_no
, end_seq_no
);
1132 if (ack_seq_no
>= end_seq_no
) {
1133 proto_item_append_text (en
, " (invalid)");
1134 expert_add_info(pinfo
, en
, &ei_miss_seq_range
);
1136 proto_tree
*missing_tree
;
1139 missing_tree
= proto_item_add_subtree (en
, ett_range_entry
);
1141 for (sno
= ack_seq_no
; sno
<= end_seq_no
; sno
++) {
1142 en
= proto_tree_add_uint_format_value(missing_tree
, hf_miss_seq_no
,
1143 tvb
, offset
, 6, sno
,
1145 PROTO_ITEM_SET_GENERATED (en
);
1147 tot_no_missing
+= (end_seq_no
- ack_seq_no
+ 1);
1151 no_missing
+= 2; /* Skip the next two */
1152 prev_ack_seq_no
= end_seq_no
;
1154 /* No range, handle one seq no */
1155 en
= proto_tree_add_item (field_tree
, hf_miss_seq_no
, tvb
,offset
, 2, ENC_BIG_ENDIAN
);
1158 if (ack_seq_no
== 0) {
1159 proto_item_append_text (en
, " (invalid)");
1160 expert_add_info(pinfo
, en
, &ei_miss_seq_no
);
1161 } else if (ack_seq_no
<= prev_ack_seq_no
) {
1162 proto_item_append_text (en
, " (end of list indicator)");
1166 prev_ack_seq_no
= ack_seq_no
;
1171 if (use_seq_ack_analysis
) {
1172 add_ack_analysis (tvb
, pinfo
, field_tree
, offset
, pdu_type
, &src
, &dst
,
1173 message_id
, no_missing
);
1176 proto_item_append_text (ti
, ", Count of Ack: %u", count
);
1178 if (tvb_length_remaining (tvb
, offset
) >= 8) {
1179 /* Timestamp Option (in units of 100ms) */
1182 timestamp
= tvb_get_ntoh64 (tvb
, offset
);
1183 proto_tree_add_uint64_format_value(p_mul_tree
, hf_timestamp_option
, tvb
,
1184 offset
, 8, timestamp
,
1185 "%" G_GINT64_MODIFIER
"d.%d second%s (%" G_GINT64_MODIFIER
"u)",
1186 timestamp
/ 10, (int) timestamp
% 10,
1187 (timestamp
== 10) ? "" : "s", timestamp
);
1191 if (tot_no_missing
) {
1192 proto_item_append_text (ti
, ", Missing seq numbers: %u", tot_no_missing
);
1193 en
= proto_tree_add_uint (p_mul_tree
, hf_tot_miss_seq_no
, tvb
, 0, 0,
1195 PROTO_ITEM_SET_GENERATED (en
);
1196 expert_add_info_format(pinfo
, en
, &ei_tot_miss_seq_no
, "Missing seq numbers: %d", tot_no_missing
);
1200 case Discard_Message_PDU
:
1201 seq_no
= G_MAXUINT16
; /* To make the seq_no uniq */
1205 /* Announced Multicast Group */
1206 proto_tree_add_item (p_mul_tree
, hf_ann_mc_group
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
1209 for (i
= 0; i
< count
; i
++) {
1210 /* Destination Id */
1211 proto_tree_add_item (p_mul_tree
, hf_dest_id
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
1219 /* Multicast Group */
1220 proto_tree_add_item (p_mul_tree
, hf_mc_group
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
1229 /* Add SEQ/ACK analysis entry */
1230 if (use_seq_ack_analysis
&& (pdu_type
<= Discard_Message_PDU
) &&
1231 (pdu_type
!= Ack_PDU
) && (pdu_type
!= Address_PDU
|| no_dest
!= 0))
1233 add_seq_analysis (tvb
, pinfo
, p_mul_tree
, &src
, offset
, pdu_type
,
1234 message_id
, seq_no
, tot_no_missing
);
1237 /* Check if printing Ack-Ack */
1238 if (pdu_type
== Address_PDU
&& no_dest
== 0) {
1239 col_append_str (pinfo
->cinfo
, COL_INFO
, get_type (Ack_Ack_PDU
));
1241 col_append_str (pinfo
->cinfo
, COL_INFO
, get_type (pdu_type
));
1243 if (pdu_type
== Address_PDU
|| pdu_type
== Extra_Address_PDU
||
1244 pdu_type
== FEC_Address_PDU
|| pdu_type
== Extra_FEC_Address_PDU
) {
1245 col_append_fstr (pinfo
->cinfo
, COL_INFO
, ", No PDUs: %u", no_pdus
);
1246 } else if (pdu_type
== Data_PDU
) {
1247 col_append_fstr (pinfo
->cinfo
, COL_INFO
, ", Seq no: %u", seq_no
);
1249 if (pdu_type
== Address_PDU
|| pdu_type
== Extra_Address_PDU
||
1250 pdu_type
== FEC_Address_PDU
|| pdu_type
== Extra_FEC_Address_PDU
) {
1252 col_append_fstr (pinfo
->cinfo
, COL_INFO
, ", Count of Dest: %u", no_dest
);
1254 } else if (pdu_type
== Ack_PDU
) {
1255 if (tot_no_missing
) {
1256 col_append_fstr (pinfo
->cinfo
, COL_INFO
, ", Missing seq numbers: %u",
1259 col_append_fstr (pinfo
->cinfo
, COL_INFO
, ", Count of Ack: %u", count
);
1261 if (pdu_type
!= Ack_PDU
) {
1262 col_append_fstr (pinfo
->cinfo
, COL_INFO
, ", MSID: %u", message_id
);
1264 if (message_id_list
&& wmem_strbuf_get_len(message_id_list
) > 0) {
1265 col_append_fstr (pinfo
->cinfo
, COL_INFO
, ", MSID: %s", wmem_strbuf_get_str(message_id_list
));
1269 if (p_mul_reassemble
) {
1270 save_fragmented
= pinfo
->fragmented
;
1272 if (pdu_type
== Address_PDU
&& no_pdus
> 0) {
1273 /* Start fragment table */
1274 fragment_start_seq_check (&p_mul_reassembly_table
,
1275 pinfo
, message_id
, NULL
, no_pdus
- 1);
1276 } else if (pdu_type
== Data_PDU
) {
1277 fragment_head
*frag_msg
;
1280 pinfo
->fragmented
= TRUE
;
1282 /* Add fragment to fragment table */
1283 frag_msg
= fragment_add_seq_check (&p_mul_reassembly_table
,
1284 tvb
, offset
, pinfo
, message_id
, NULL
,
1285 seq_no
- 1, data_len
, TRUE
);
1286 new_tvb
= process_reassembled_data (tvb
, offset
, pinfo
,
1287 "Reassembled P_MUL", frag_msg
,
1288 &p_mul_frag_items
, NULL
, tree
);
1291 col_append_str (pinfo
->cinfo
, COL_INFO
, " (Message Reassembled)");
1294 dissect_reassembled_data (new_tvb
, pinfo
, tree
);
1298 pinfo
->fragmented
= save_fragmented
;
1301 /* Update length of P_Mul packet and check length values */
1302 proto_item_set_len (ti
, offset
);
1303 if (pdu_length
!= (offset
+ data_len
)) {
1304 proto_item_append_text (len_en
, " (incorrect, should be: %d)",
1306 expert_add_info(pinfo
, len_en
, &ei_length
);
1307 } else if ((len
= tvb_length_remaining (tvb
, pdu_length
)) > 0) {
1308 proto_item_append_text (len_en
, " (more data in packet: %d)", len
);
1309 expert_add_info(pinfo
, len_en
, &ei_more_data
);
1313 static void p_mul_init_routine (void)
1315 reassembly_table_init (&p_mul_reassembly_table
,
1316 &addresses_reassembly_table_functions
);
1317 message_id_offset
= 0;
1319 if (p_mul_id_hash_table
) {
1320 g_hash_table_destroy (p_mul_id_hash_table
);
1323 if (p_mul_package_data_list
) {
1324 g_list_foreach (p_mul_package_data_list
, (GFunc
)p_mul_package_data_destroy
, NULL
);
1325 g_list_free (p_mul_package_data_list
);
1328 p_mul_id_hash_table
= g_hash_table_new_full (p_mul_id_hash
, p_mul_id_hash_equal
, NULL
, (GDestroyNotify
)p_mul_id_value_destroy
);
1329 p_mul_package_data_list
= NULL
;
1332 void proto_register_p_mul (void)
1334 static hf_register_info hf
[] = {
1336 { "Length of PDU", "p_mul.length", FT_UINT16
, BASE_DEC
,
1337 NULL
, 0x0, NULL
, HFILL
} },
1339 { "Priority", "p_mul.priority", FT_UINT8
, BASE_DEC
,
1340 NULL
, 0x0, NULL
, HFILL
} },
1342 { "First", "p_mul.first", FT_BOOLEAN
, 8,
1343 TFS (&no_yes
), 0x80, NULL
, HFILL
} },
1345 { "Last", "p_mul.last", FT_BOOLEAN
, 8,
1346 TFS (&no_yes
), 0x40, NULL
, HFILL
} },
1348 { "MAP unused", "p_mul.unused", FT_UINT8
, BASE_DEC
,
1349 NULL
, 0xC0, NULL
, HFILL
} },
1351 { "PDU Type", "p_mul.pdu_type", FT_UINT8
, BASE_DEC
,
1352 VALS (pdu_vals
), 0x3F, NULL
, HFILL
} },
1353 { &hf_pdu_type_value
,
1354 { "PDU Type", "p_mul.pdu_type_value", FT_UINT8
, BASE_DEC
,
1355 VALS (pdu_vals
), 0x3F, NULL
, HFILL
} },
1357 { "Total Number of PDUs", "p_mul.no_pdus", FT_UINT16
, BASE_DEC
,
1358 NULL
, 0x0, NULL
, HFILL
} },
1360 { "Sequence Number of PDUs", "p_mul.seq_no", FT_UINT16
, BASE_DEC
,
1361 NULL
, 0x0, NULL
, HFILL
} },
1363 { "Unused", "p_mul.unused", FT_UINT8
, BASE_DEC
,
1364 NULL
, 0x0, NULL
, HFILL
} },
1366 { "Unused", "p_mul.unused", FT_UINT16
, BASE_DEC
,
1367 NULL
, 0x0, NULL
, HFILL
} },
1369 { "Checksum", "p_mul.checksum", FT_UINT16
, BASE_HEX
,
1370 NULL
, 0x0, NULL
, HFILL
} },
1371 { &hf_checksum_good
,
1372 { "Good", "p_mul.checksum_good", FT_BOOLEAN
, BASE_NONE
,
1373 NULL
, 0x0, "True: checksum matches packet content; False: doesn't match content or not checked", HFILL
} },
1375 { "Bad", "p_mul.checksum_bad", FT_BOOLEAN
, BASE_NONE
,
1376 NULL
, 0x0, "True: checksum doesn't match packet content; False: matches content or not checked", HFILL
} },
1377 { &hf_source_id_ack
,
1378 { "Source ID of Ack Sender", "p_mul.source_id_ack", FT_IPv4
, BASE_NONE
,
1379 NULL
, 0x0, NULL
, HFILL
} },
1381 { "Source ID", "p_mul.source_id", FT_IPv4
, BASE_NONE
,
1382 NULL
, 0x0, NULL
, HFILL
} },
1384 { "Message ID (MSID)", "p_mul.message_id", FT_UINT32
, BASE_DEC
,
1385 NULL
, 0x0, "Message ID", HFILL
} },
1387 { "Expiry Time", "p_mul.expiry_time", FT_ABSOLUTE_TIME
, ABSOLUTE_TIME_LOCAL
,
1388 NULL
, 0x0, NULL
, HFILL
} },
1390 { "Multicast Group", "p_mul.mc_group", FT_UINT32
, BASE_DEC
,
1391 NULL
, 0x0, NULL
, HFILL
} },
1393 { "Announced Multicast Group", "p_mul.ann_mc_group", FT_UINT32
, BASE_DEC
,
1394 NULL
, 0x0, NULL
, HFILL
} },
1396 { "FEC Parameter Length", "p_mul.fec.length", FT_UINT8
, BASE_DEC
,
1397 NULL
, 0x0, "Forward Error Correction Parameter Length", HFILL
} },
1399 { "FEC ID", "p_mul.fec.id", FT_UINT8
, BASE_HEX
,
1400 NULL
, 0x0, "Forward Error Correction ID", HFILL
} },
1401 { &hf_fec_parameters
,
1402 { "FEC Parameters", "p_mul.fec.parameters", FT_NONE
, BASE_NONE
,
1403 NULL
, 0x0, "Forward Error Correction Parameters", HFILL
} },
1404 { &hf_count_of_dest
,
1405 { "Count of Destination Entries", "p_mul.dest_count", FT_UINT16
,BASE_DEC
,
1406 NULL
, 0x0, NULL
, HFILL
} },
1407 { &hf_length_of_res
,
1408 { "Length of Reserved Field", "p_mul.reserved_length",FT_UINT16
,BASE_DEC
,
1409 NULL
, 0x0, NULL
, HFILL
} },
1411 { "Count of Ack Info Entries", "p_mul.ack_count", FT_UINT16
, BASE_DEC
,
1412 NULL
, 0x0, NULL
, HFILL
} },
1414 { "Ack Info Entry", "p_mul.ack_info_entry", FT_NONE
, BASE_NONE
,
1415 NULL
, 0x0, NULL
, HFILL
} },
1417 { "Length of Ack Info Entry", "p_mul.ack_length", FT_UINT16
, BASE_DEC
,
1418 NULL
, 0x0, NULL
, HFILL
} },
1420 { "Missing Data PDU Seq Number", "p_mul.missing_seq_no", FT_UINT16
,
1421 BASE_DEC
, NULL
, 0x0, NULL
, HFILL
} },
1422 { &hf_miss_seq_range
,
1423 { "Missing Data PDU Seq Range", "p_mul.missing_seq_range", FT_BYTES
,
1424 BASE_NONE
, NULL
, 0x0, NULL
, HFILL
} },
1425 { &hf_tot_miss_seq_no
,
1426 { "Total Number of Missing Data PDU Sequence Numbers",
1427 "p_mul.no_missing_seq_no", FT_UINT16
, BASE_DEC
, NULL
, 0x0,
1429 { &hf_timestamp_option
,
1430 { "Timestamp", "p_mul.timestamp", FT_UINT64
, BASE_DEC
,
1431 NULL
, 0x0, "Timestamp Option (in units of 100ms)", HFILL
} },
1433 { "Destination Entry", "p_mul.dest_entry", FT_NONE
, BASE_NONE
,
1434 NULL
, 0x0, NULL
, HFILL
} },
1436 { "Destination ID", "p_mul.dest_id", FT_IPv4
, BASE_NONE
,
1437 NULL
, 0x0, NULL
, HFILL
} },
1439 { "Message Sequence Number", "p_mul.msg_seq_no", FT_UINT16
, BASE_DEC
,
1440 NULL
, 0x0, NULL
, HFILL
} },
1442 { "Symmetric Key", "p_mul.sym_key", FT_NONE
, BASE_NONE
,
1443 NULL
, 0x0, NULL
, HFILL
} },
1444 { &hf_data_fragment
,
1445 { "Fragment of Data", "p_mul.data_fragment", FT_NONE
, BASE_NONE
,
1446 NULL
, 0x0, NULL
, HFILL
} },
1448 /* Fragment entries */
1449 { &hf_msg_fragments
,
1450 { "Message fragments", "p_mul.fragments", FT_NONE
, BASE_NONE
,
1451 NULL
, 0x00, NULL
, HFILL
} },
1453 { "Message fragment", "p_mul.fragment", FT_FRAMENUM
, BASE_NONE
,
1454 NULL
, 0x00, NULL
, HFILL
} },
1455 { &hf_msg_fragment_overlap
,
1456 { "Message fragment overlap", "p_mul.fragment.overlap", FT_BOOLEAN
,
1457 BASE_NONE
, NULL
, 0x0, NULL
, HFILL
} },
1458 { &hf_msg_fragment_overlap_conflicts
,
1459 { "Message fragment overlapping with conflicting data",
1460 "p_mul.fragment.overlap.conflicts", FT_BOOLEAN
, BASE_NONE
, NULL
,
1461 0x0, NULL
, HFILL
} },
1462 { &hf_msg_fragment_multiple_tails
,
1463 { "Message has multiple tail fragments",
1464 "p_mul.fragment.multiple_tails", FT_BOOLEAN
, BASE_NONE
,
1465 NULL
, 0x0, NULL
, HFILL
} },
1466 { &hf_msg_fragment_too_long_fragment
,
1467 { "Message fragment too long", "p_mul.fragment.too_long_fragment",
1468 FT_BOOLEAN
, BASE_NONE
, NULL
, 0x0, NULL
,
1470 { &hf_msg_fragment_error
,
1471 { "Message defragmentation error", "p_mul.fragment.error", FT_FRAMENUM
,
1472 BASE_NONE
, NULL
, 0x00, NULL
, HFILL
} },
1473 { &hf_msg_fragment_count
,
1474 { "Message fragment count", "p_mul.fragment.count", FT_UINT32
, BASE_DEC
,
1475 NULL
, 0x00, NULL
, HFILL
} },
1476 { &hf_msg_reassembled_in
,
1477 { "Reassembled in", "p_mul.reassembled.in", FT_FRAMENUM
, BASE_NONE
,
1478 NULL
, 0x00, NULL
, HFILL
} },
1479 { &hf_msg_reassembled_length
,
1480 { "Reassembled P_MUL length", "p_mul.reassembled.length", FT_UINT32
, BASE_DEC
,
1481 NULL
, 0x00, NULL
, HFILL
} },
1484 ** Ack matching / Resend
1486 { &hf_analysis_ack_time
,
1487 { "Ack Time", "p_mul.analysis.ack_time", FT_RELATIVE_TIME
, BASE_NONE
,
1488 NULL
, 0x0, "The time between the Last PDU and the Ack", HFILL
} },
1489 { &hf_analysis_trans_time
,
1490 { "Transfer Time", "p_mul.analysis.trans_time", FT_RELATIVE_TIME
, BASE_NONE
,
1491 NULL
, 0x0, "The time between the first Address PDU and the Ack", HFILL
} },
1492 { &hf_analysis_retrans_time
,
1493 { "Retransmission Time", "p_mul.analysis.retrans_time", FT_RELATIVE_TIME
, BASE_NONE
,
1494 NULL
, 0x0, "The time between the last PDU and this PDU", HFILL
} },
1495 { &hf_analysis_total_retrans_time
,
1496 { "Total Retransmission Time", "p_mul.analysis.total_retrans_time", FT_RELATIVE_TIME
, BASE_NONE
,
1497 NULL
, 0x0, "The time between the first PDU and this PDU", HFILL
} },
1498 { &hf_analysis_addr_pdu_time
,
1499 { "Time since Address PDU", "p_mul.analysis.elapsed_time", FT_RELATIVE_TIME
, BASE_NONE
,
1500 NULL
, 0x0, "The time between the Address PDU and this PDU", HFILL
} },
1501 { &hf_analysis_prev_pdu_time
,
1502 { "PDU Delay", "p_mul.analysis.pdu_delay", FT_RELATIVE_TIME
, BASE_NONE
,
1503 NULL
, 0x0, "The time between the last PDU and this PDU", HFILL
} },
1504 { &hf_analysis_last_pdu_num
,
1505 { "Last Data PDU in", "p_mul.analysis.last_pdu_in", FT_FRAMENUM
, BASE_NONE
,
1506 NULL
, 0x0, "The last Data PDU found in this frame", HFILL
} },
1507 { &hf_analysis_addr_pdu_num
,
1508 { "Address PDU in", "p_mul.analysis.addr_pdu_in", FT_FRAMENUM
, BASE_NONE
,
1509 NULL
, 0x0, "The Address PDU is found in this frame", HFILL
} },
1510 { &hf_analysis_prev_pdu_num
,
1511 { "Previous PDU in", "p_mul.analysis.prev_pdu_in", FT_FRAMENUM
, BASE_NONE
,
1512 NULL
, 0x0, "The previous PDU is found in this frame", HFILL
} },
1513 { &hf_analysis_ack_num
,
1514 { "Ack PDU in", "p_mul.analysis.ack_in", FT_FRAMENUM
, BASE_NONE
,
1515 NULL
, 0x0, "This packet has an Ack in this frame", HFILL
} },
1516 { &hf_analysis_ack_missing
,
1517 { "Ack PDU missing", "p_mul.analysis.ack_missing", FT_NONE
, BASE_NONE
,
1518 NULL
, 0x0, "The acknowledgement for this packet is missing", HFILL
} },
1519 { &hf_analysis_retrans_no
,
1520 { "Retransmission #", "p_mul.analysis.retrans_no", FT_UINT32
, BASE_DEC
,
1521 NULL
, 0x0, "Retransmission count", HFILL
} },
1522 { &hf_analysis_ack_dup_no
,
1523 { "Duplicate ACK #", "p_mul.analysis.dup_ack_no", FT_UINT32
, BASE_DEC
,
1524 NULL
, 0x0, "Duplicate Ack count", HFILL
} },
1525 { &hf_analysis_msg_resend_from
,
1526 { "Retransmission of Message in", "p_mul.analysis.msg_first_in",
1527 FT_FRAMENUM
, BASE_NONE
,
1528 NULL
, 0x0, "This Message was first sent in this frame", HFILL
} },
1529 { &hf_analysis_ack_resend_from
,
1530 { "Retransmission of Ack in", "p_mul.analysis.ack_first_in",
1531 FT_FRAMENUM
, BASE_NONE
,
1532 NULL
, 0x0, "This Ack was first sent in this frame", HFILL
} },
1533 { &hf_analysis_total_time
,
1534 { "Total Time", "p_mul.analysis.total_time", FT_RELATIVE_TIME
, BASE_NONE
,
1535 NULL
, 0x0, "The time between the first and the last Address PDU", HFILL
} },
1538 static gint
*ett
[] = {
1547 &ett_seq_ack_analysis
,
1551 static ei_register_info ei
[] = {
1552 { &ei_address_pdu_missing
, { "p_mul.analysis.addr_pdu_missing", PI_SEQUENCE
, PI_NOTE
, "Address PDU missing", EXPFILL
}},
1553 { &ei_analysis_ack_missing
, { "p_mul.analysis.ack_missing.expert", PI_SEQUENCE
, PI_NOTE
, "Ack PDU missing", EXPFILL
}},
1554 { &ei_analysis_ack_dup_no
, { "p_mul.analysis.dup_ack_no.expert", PI_SEQUENCE
, PI_NOTE
, "Dup ACK #", EXPFILL
}},
1555 { &ei_analysis_prev_pdu_missing
, { "p_mul.analysis.prev_pdu_missing", PI_SEQUENCE
, PI_NOTE
, "Previous PDU missing", EXPFILL
}},
1556 { &ei_analysis_retrans_no
, { "p_mul.analysis.retrans_no.expert", PI_SEQUENCE
, PI_NOTE
, "Retransmission #", EXPFILL
}},
1557 { &ei_message_discarded
, { "p_mul.message_discarded", PI_RESPONSE_CODE
, PI_NOTE
, "Message discarded", EXPFILL
}},
1558 { &ei_checksum_bad
, { "p_mul.checksum_bad.expert", PI_CHECKSUM
, PI_WARN
, "Bad checksum", EXPFILL
}},
1559 { &ei_ack_length
, { "p_mul.ack_length.invalid", PI_MALFORMED
, PI_WARN
, "Invalid ack info length", EXPFILL
}},
1560 { &ei_miss_seq_range
, { "p_mul.missing_seq_range.invalid", PI_UNDECODED
, PI_WARN
, "Invalid missing sequence range", EXPFILL
}},
1561 { &ei_miss_seq_no
, { "p_mul.missing_seq_no.invalid", PI_UNDECODED
, PI_WARN
, "Invalid missing seq number", EXPFILL
}},
1562 { &ei_tot_miss_seq_no
, { "p_mul.no_missing_seq_no.expert", PI_RESPONSE_CODE
, PI_NOTE
, "Missing seq numbers", EXPFILL
}},
1563 { &ei_length
, { "p_mul.length.invalid", PI_MALFORMED
, PI_WARN
, "Incorrect length field", EXPFILL
}},
1564 { &ei_more_data
, { "p_mul.more_data", PI_MALFORMED
, PI_WARN
, "More data in packet", EXPFILL
}},
1567 module_t
*p_mul_module
;
1568 expert_module_t
* expert_p_mul
;
1570 proto_p_mul
= proto_register_protocol (PNAME
, PSNAME
, PFNAME
);
1572 p_mul_handle
= register_dissector(PFNAME
, dissect_p_mul
, proto_p_mul
);
1574 proto_register_field_array (proto_p_mul
, hf
, array_length (hf
));
1575 proto_register_subtree_array (ett
, array_length (ett
));
1576 expert_p_mul
= expert_register_protocol(proto_p_mul
);
1577 expert_register_field_array(expert_p_mul
, ei
, array_length(ei
));
1578 register_init_routine (&p_mul_init_routine
);
1580 /* Set default UDP ports */
1581 range_convert_str (&global_p_mul_port_range
, DEFAULT_P_MUL_PORT_RANGE
,
1584 /* Register our configuration options */
1585 p_mul_module
= prefs_register_protocol (proto_p_mul
,
1586 proto_reg_handoff_p_mul
);
1588 prefs_register_obsolete_preference (p_mul_module
, "tport");
1589 prefs_register_obsolete_preference (p_mul_module
, "rport");
1590 prefs_register_obsolete_preference (p_mul_module
, "dport");
1591 prefs_register_obsolete_preference (p_mul_module
, "aport");
1593 prefs_register_range_preference (p_mul_module
, "udp_ports",
1594 "P_Mul port numbers",
1595 "Port numbers used for P_Mul traffic",
1596 &global_p_mul_port_range
, MAX_UDP_PORT
);
1597 prefs_register_bool_preference (p_mul_module
, "reassemble",
1598 "Reassemble fragmented P_Mul packets",
1599 "Reassemble fragmented P_Mul packets",
1601 prefs_register_bool_preference (p_mul_module
, "relative_msgid",
1602 "Use relative Message ID",
1603 "Make the P_Mul dissector use relative"
1604 " message id number instead of absolute"
1605 " ones", &use_relative_msgid
);
1606 prefs_register_bool_preference (p_mul_module
, "seq_ack_analysis",
1608 "Calculate sequence/acknowledgement analysis",
1609 &use_seq_ack_analysis
);
1610 prefs_register_enum_preference (p_mul_module
, "decode",
1611 "Decode Data PDU as",
1612 "Type of content in Data_PDU",
1613 &decode_option
, decode_options
, FALSE
);
1616 void proto_reg_handoff_p_mul (void)
1618 static gboolean p_mul_prefs_initialized
= FALSE
;
1619 static range_t
*p_mul_port_range
;
1621 if (!p_mul_prefs_initialized
) {
1622 p_mul_prefs_initialized
= TRUE
;
1623 data_handle
= find_dissector ("data");
1625 dissector_delete_uint_range ("udp.port", p_mul_port_range
, p_mul_handle
);
1626 g_free (p_mul_port_range
);
1629 /* Save port number for later deletion */
1630 p_mul_port_range
= range_copy (global_p_mul_port_range
);
1632 dissector_add_uint_range ("udp.port", p_mul_port_range
, p_mul_handle
);
1641 * indent-tabs-mode: nil
1644 * ex: set shiftwidth=2 tabstop=8 expandtab:
1645 * :indentSize=2:tabSize=8:noTabs=true: