2 * Routines for FLIP packet dissection
4 * Copyright 2009, Juha Siltanen <juha.siltanen@nsn.com>
8 * Wireshark - Network traffic analyzer
9 * By Gerald Combs <gerald@wireshark.org>
10 * Copyright 1998 Gerald Combs
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
28 * FLIP (Flow Layer Internal Protocol) is a proprietary protocol
29 * developed by Nokia Siemens Networks.
35 * Version 0.0.1, November 23rd, 2009.
37 * Support for the basic and checksum headers.
39 * Version 0.0.2, August 26th, 2010.
41 * Support for payload dissecting.
43 * Version 0.0.3, September 14th, 2010.
45 * Bugfix: sorting by protocol didn't always fill in the protocol column.
50 #include <epan/packet.h>
51 #include <epan/etypes.h>
52 #include <epan/in_cksum.h>
54 #include <epan/prefs.h>
55 #include <epan/emem.h>
56 #include <epan/strutil.h>
58 #include <epan/rtp_pt.h>
60 static int proto_flip
= -1;
63 static int hf_flip_basic_e
= -1;
64 static int hf_flip_basic_reserved
= -1;
65 static int hf_flip_basic_flowid
= -1;
66 static int hf_flip_basic_seqnum
= -1;
67 static int hf_flip_basic_len
= -1;
70 static int hf_flip_chksum_etype
= -1;
71 static int hf_flip_chksum_spare
= -1;
72 static int hf_flip_chksum_e
= -1;
73 static int hf_flip_chksum_chksum
= -1;
75 #define FLIP_BASIC (0)
76 #define FLIP_CHKSUM (1)
78 #define FLIP_BASIC_HDR_LEN (8)
79 #define FLIP_CHKSUM_HDR_LEN (4)
80 #define FLIP_EXTENSION_HDR_MIN_LEN (4)
83 static const value_string flip_short_header_names
[]={
84 { FLIP_BASIC
, "BASIC" },
85 { FLIP_CHKSUM
, "CHKSUM"},
89 static const value_string flip_long_header_names
[] = {
90 { FLIP_BASIC
, "Basic"},
91 { FLIP_CHKSUM
, "Checksum"},
96 static const value_string flip_boolean
[] = {
102 static const value_string flip_etype
[] = {
103 { FLIP_CHKSUM
, "Checksum" },
107 #define FLIP_PAYLOAD_DECODING_MODE_NONE (0)
108 #define FLIP_PAYLOAD_DECODING_MODE_HEURISTIC (1)
109 #define FLIP_PAYLOAD_DECODING_MODE_FORCED (2)
111 static const enum_val_t flip_payload_decoding_modes
[] = {
112 {"none", "no decoding", FLIP_PAYLOAD_DECODING_MODE_NONE
},
113 {"heuristic", "heuristic", FLIP_PAYLOAD_DECODING_MODE_HEURISTIC
},
114 {"forced", "forced", FLIP_PAYLOAD_DECODING_MODE_FORCED
},
118 static gint global_flip_payload_decoding_mode
=
119 FLIP_PAYLOAD_DECODING_MODE_HEURISTIC
;
121 static gboolean is_heur_enabled_rtp
= TRUE
;
122 static gboolean is_heur_enabled_rtcp
= TRUE
;
124 static const char *global_forced_protocol
= "data";
125 static gboolean is_forced_handle_ok
= FALSE
;
127 static gint ett_flip
= -1;
128 static gint ett_flip_basic
= -1;
129 static gint ett_flip_chksum
= -1;
130 static gint ett_flip_payload
= -1;
132 static dissector_handle_t rtp_handle
;
133 static dissector_handle_t rtcp_handle
;
134 static dissector_handle_t data_handle
;
135 static dissector_handle_t forced_handle
;
137 /* Forward declaration. */
139 proto_reg_handoff_flip(void);
141 is_payload_rtp(tvbuff_t
*tvb
);
143 is_payload_rtcp(tvbuff_t
*tvb
);
145 /* Dissect the checksum extension header. */
147 dissect_flip_chksum_hdr(tvbuff_t
*tvb
,
150 guint16 computed_chksum
,
151 gboolean
*ext_hdr_follows_ptr
)
154 proto_tree
*chksum_hdr_tree
;
156 guint8 chksum_hdr_etype
;
157 guint8 chksum_hdr_spare
;
158 guint8 chksum_hdr_ext
;
159 guint16 chksum_hdr_chksum
;
161 gint bytes_dissected
;
165 chksum_hdr_tree
= NULL
;
170 dw
= tvb_get_ntohl(tvb
, offset
);
171 chksum_hdr_etype
= (guint8
) ((dw
& 0xFF000000) >> 24);
172 chksum_hdr_spare
= (guint8
) ((dw
& 0x00FE0000) >> 17);
173 chksum_hdr_ext
= (guint8
) ((dw
& 0x00010000) >> 16);
174 chksum_hdr_chksum
= (guint16
) (dw
& 0x0000FFFF);
176 /* The actually shouldn't be any headers after checksum. */
177 if (chksum_hdr_ext
== 1) {
178 *ext_hdr_follows_ptr
= TRUE
;
181 *ext_hdr_follows_ptr
= FALSE
;
185 item
= proto_tree_add_text(tree
, tvb
,
186 offset
+ 0, 4, "Checksum Header");
187 chksum_hdr_tree
= proto_item_add_subtree(item
, ett_flip_chksum
);
190 proto_tree_add_uint_format_value(chksum_hdr_tree
, hf_flip_chksum_etype
,
191 tvb
, offset
+ 0, 1, dw
,
192 "%s", val_to_str_const(chksum_hdr_etype
,
196 proto_tree_add_uint_format_value(chksum_hdr_tree
, hf_flip_chksum_spare
,
197 tvb
, offset
+ 1, 1, dw
,
199 chksum_hdr_spare
, chksum_hdr_spare
);
202 proto_tree_add_uint_format_value(chksum_hdr_tree
, hf_flip_chksum_e
,
203 tvb
, offset
+ 1, 1, dw
,
204 "%s", val_to_str_const(chksum_hdr_ext
,
207 /* CHKSUM: 16 bits. */
208 proto_tree_add_uint_format_value(
210 hf_flip_chksum_chksum
,
213 "0x%04x [%s] (computed 0x%04x)",
215 ((chksum_hdr_chksum
== computed_chksum
) ? "Correct" : "Incorrect"),
219 /* Show faulty checksums. */
220 if (computed_chksum
!= chksum_hdr_chksum
) {
221 col_add_fstr(pinfo
->cinfo
, COL_INFO
,
222 "Checksum 0x%04x [%s] (computed 0x%04x)",
224 ((chksum_hdr_chksum
== computed_chksum
) ?
225 "Correct" : "Incorrect"),
229 bytes_dissected
+= FLIP_CHKSUM_HDR_LEN
;
231 return bytes_dissected
;
233 } /* dissect_flip_chksum_hdr() */
236 /* Detection logic grabbed from packet-rtp.c and modified. */
238 #define RTP_VERSION(octet) ((octet) >> 6)
239 #define RTP_MARKER(octet) ((octet) & 0x80)
240 #define RTP_PAYLOAD_TYPE(octet) ((octet) & 0x7F)
242 #define RTP_V2_HEADER_MIN_LEN 12
245 is_payload_rtp(tvbuff_t
*tvb
)
247 guint8 octet1
, octet2
;
248 unsigned int version
;
249 unsigned int payload_type
;
255 len_remaining
= tvb_length_remaining(tvb
, offset
);
256 if (len_remaining
< RTP_V2_HEADER_MIN_LEN
) {
260 octet1
= tvb_get_guint8(tvb
, offset
);
261 version
= RTP_VERSION(octet1
);
263 /* Accept only version 2. */
268 octet2
= tvb_get_guint8(tvb
, offset
+ 1);
269 payload_type
= RTP_PAYLOAD_TYPE(octet2
);
271 if ((payload_type
<= PT_H263
)
273 ((payload_type
>= PT_UNDF_96
) && (payload_type
<= PT_UNDF_127
))) {
283 } /* is_payload_rtp() */
286 /* Detection logic grabbed from packet-rtcp.c and modified. */
293 #define RTCP_V2_HEADER_MIN_LEN 4
296 is_payload_rtcp(tvbuff_t
*tvb
)
298 unsigned int first_byte
;
299 unsigned int packet_type
;
305 len_remaining
= tvb_length_remaining(tvb
, offset
);
306 if (len_remaining
< RTCP_V2_HEADER_MIN_LEN
) {
310 /* Look at first byte */
311 first_byte
= tvb_get_guint8(tvb
, offset
);
313 /* Are version bits set to 2? */
314 if (((first_byte
& 0xC0) >> 6) != 2) {
318 /* Look at packet type */
319 packet_type
= tvb_get_guint8(tvb
, offset
+ 1);
321 /* First packet within compound packet is supposed to be a sender
323 - allow BYE because this happens anyway
324 - allow APP because TBCP ("PoC1") packets aren't compound... */
325 if (!((packet_type
== RTCP_SR
) || (packet_type
== RTCP_RR
) ||
326 (packet_type
== RTCP_BYE
) || (packet_type
== RTCP_APP
))) {
330 /* Overall length must be a multiple of 4 bytes */
331 if (tvb_reported_length(tvb
) % 4) {
337 } /* is_payload_rtcp() */
339 /* Protocol dissection */
341 dissect_flip(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
)
345 proto_tree
*flip_tree
;
346 proto_tree
*basic_hdr_tree
;
352 /* Basic header fields. */
353 guint8 basic_hdr_ext
;
354 guint8 basic_hdr_reserved
;
355 guint32 basic_hdr_flow_id
;
356 guint16 basic_hdr_seqnum
;
357 guint16 basic_hdr_len
;
361 gint bytes_dissected
;
367 /* Error handling for basic header. */
368 gboolean is_faulty_frame
;
373 basic_hdr_tree
= NULL
;
381 is_faulty_frame
= FALSE
;
383 /* Show this protocol as FLIP. */
384 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "FLIP");
387 * The frame can be faulty in several ways:
388 * - too short (even for the basic header)
389 * - length inconsistent (header and frame info different)
390 * - checksum doesn't check out
391 * - extension header is indicated, but the frame is too short for it
392 * - unknown extension header type
395 /* Check that there's enough data at least for the basic header. */
396 frame_len
= tvb_length(tvb
);
397 if (frame_len
< FLIP_BASIC_HDR_LEN
) {
398 /* Not enough. This must be a malformed packet. */
399 goto DISSECT_FLIP_EXIT
;
402 bytes_dissected
+= FLIP_BASIC_HDR_LEN
;
404 /* Process the first 32 bits of the basic header. */
405 dw1
= tvb_get_ntohl(tvb
, offset
+ 0);
406 basic_hdr_ext
= ((dw1
& 0x80000000) >> 31);
407 basic_hdr_reserved
= ((dw1
& 0x70000000) >> 24);
408 basic_hdr_flow_id
= (dw1
& 0x0FFFFFFF);
410 /* Process the second 32 bits of the basic header. */
411 dw2
= tvb_get_ntohl(tvb
, offset
+ 4);
412 basic_hdr_seqnum
= (guint16
) ((dw2
& 0xFFFF0000) >> 16);
413 basic_hdr_len
= (guint16
) (dw2
& 0x0000FFFF);
415 /* Does the basic header indicate that an extension is next? */
416 if (basic_hdr_ext
== 1) {
420 flip_len
= basic_hdr_len
;
423 * Check the length value.
425 if ((flip_len
< FLIP_BASIC_HDR_LEN
) || (flip_len
> frame_len
)) {
426 /* Faulty frame. Show the basic header anyway for debugging. */
427 is_faulty_frame
= TRUE
;
430 /* Fill in the info column. */
431 col_add_fstr(pinfo
->cinfo
, COL_INFO
,
432 "FlowID %s", val_to_str(basic_hdr_flow_id
, NULL
, "0x%08x"));
434 flip_tvb
= tvb_new_subset(tvb
, 0, frame_len
, frame_len
);
436 /* We are asked for details. */
438 if (PTREE_DATA(tree
)->visible
) {
439 ti
= proto_tree_add_protocol_format(
440 tree
, proto_flip
, flip_tvb
, 0, flip_len
,
441 "NSN FLIP, FlowID %s",
442 val_to_str(basic_hdr_flow_id
, NULL
, "0x%08x"));
445 ti
= proto_tree_add_item(tree
, proto_flip
, flip_tvb
, 0,
448 flip_tree
= proto_item_add_subtree(ti
, ett_flip
);
451 item
= proto_tree_add_text(flip_tree
, flip_tvb
, 0, 8, "Basic Header");
452 basic_hdr_tree
= proto_item_add_subtree(item
, ett_flip_basic
);
454 /* Extension header follows? 1 bit. */
455 proto_tree_add_uint_format_value(basic_hdr_tree
,
457 flip_tvb
, offset
+ 0, 1, dw1
,
458 "%s", val_to_str_const(basic_hdr_ext
,
461 /* Reserved: 3 bits. */
462 proto_tree_add_uint_format_value(basic_hdr_tree
,
463 hf_flip_basic_reserved
,
464 flip_tvb
, offset
+ 0, 1, dw1
,
465 "%d", basic_hdr_reserved
);
466 /* Flow ID: 28 bits. */
467 proto_tree_add_item(basic_hdr_tree
, hf_flip_basic_flowid
,
468 flip_tvb
, offset
+ 0, 4, ENC_BIG_ENDIAN
);
470 /* Sequence number: 16 bits. */
471 proto_tree_add_uint_format_value(basic_hdr_tree
, hf_flip_basic_seqnum
,
472 flip_tvb
, offset
+ 4, 2, dw2
,
474 basic_hdr_seqnum
, basic_hdr_seqnum
);
475 /* Packet length: 16 bits. */
476 proto_tree_add_uint_format_value(basic_hdr_tree
, hf_flip_basic_len
,
477 flip_tvb
, offset
+ 6, 2, dw2
,
479 basic_hdr_len
, basic_hdr_len
);
482 offset
+= FLIP_BASIC_HDR_LEN
;
485 * Process faults found when parsing the basic header.
487 if (is_faulty_frame
== TRUE
) {
488 if (flip_len
> frame_len
) {
489 col_add_fstr(pinfo
->cinfo
, COL_INFO
,
490 "Length mismatch: frame %d bytes, hdr %d bytes",
491 frame_len
, flip_len
);
493 else if (flip_len
< FLIP_BASIC_HDR_LEN
) {
494 col_add_fstr(pinfo
->cinfo
, COL_INFO
,
495 "Invalid length in basic header: %d bytes", flip_len
);
498 goto DISSECT_FLIP_EXIT
;
502 * Now we know that the basic header is sensible.
504 payload_len
= basic_hdr_len
;
505 payload_len
-= FLIP_BASIC_HDR_LEN
;
508 * Dissect extension headers (if any).
510 if ((ext_hdr
== TRUE
) && (payload_len
< FLIP_EXTENSION_HDR_MIN_LEN
)) {
511 col_add_fstr(pinfo
->cinfo
, COL_INFO
,
512 "Extension header indicated, but not enough data");
513 goto DISSECT_FLIP_EXIT
;
516 while ((ext_hdr
== TRUE
) && (payload_len
>= FLIP_EXTENSION_HDR_MIN_LEN
)) {
517 /* Detect the next header type. */
520 guint16 computed_chksum
;
522 tvbuff_t
*chksum_tvb
;
524 ext_hdr_type
= tvb_get_guint8(flip_tvb
, offset
);
526 switch (ext_hdr_type
) {
528 /* Calculate checksum, let the chksum dissector verify it. */
532 vec
[0].ptr
= tvb_get_ptr(flip_tvb
, 0, bytes_dissected
+ 2);
533 vec
[0].len
= bytes_dissected
+ 2;
534 vec
[1].ptr
= tvb_get_ptr(flip_tvb
, bytes_dissected
+ 4,
535 flip_len
- (bytes_dissected
+ 4));
536 vec
[1].len
= flip_len
- (bytes_dissected
+ 4);
537 computed_chksum
= in_cksum(&vec
[0], 2);
539 /* Checksums handled in network order. */
540 computed_chksum
= g_htons(computed_chksum
);
543 chksum_tvb
= tvb_new_subset(flip_tvb
, offset
,
545 FLIP_CHKSUM_HDR_LEN
);
547 /* Note that flip_tree is NULL if no details are requested. */
548 bytes_handled
= dissect_flip_chksum_hdr(chksum_tvb
,
553 bytes_dissected
+= bytes_handled
;
554 payload_len
-= bytes_handled
;
555 offset
+= bytes_handled
;
559 /* Unknown header type. */
560 col_add_fstr(pinfo
->cinfo
, COL_INFO
,
561 "Invalid extension header type 0x%02x", ext_hdr_type
);
562 goto DISSECT_FLIP_EXIT
;
568 * Show payload (if any) as bytes.
570 if (payload_len
> 0) {
572 dissector_handle_t handle
;
573 tvbuff_t
*payload_tvb
;
575 gboolean has_user_messed_up
;
577 has_user_messed_up
= FALSE
;
579 payload_tvb
= tvb_new_subset(flip_tvb
, offset
,
580 payload_len
, payload_len
);
583 * 1) no decoding -> data
584 * 2) heuristic decoding
587 switch (global_flip_payload_decoding_mode
) {
588 case FLIP_PAYLOAD_DECODING_MODE_NONE
:
589 /* Dissect as data. */
590 handle
= data_handle
;
593 case FLIP_PAYLOAD_DECODING_MODE_HEURISTIC
:
594 if ((is_heur_enabled_rtp
== TRUE
)
596 (is_payload_rtp(payload_tvb
) == TRUE
)) {
597 /* Dissect as RTP. */
600 else if ((is_heur_enabled_rtcp
== TRUE
)
602 (is_payload_rtcp(payload_tvb
))) {
603 /* Dissect as RTCP. */
604 handle
= rtcp_handle
;
607 /* Dissect as data. */
608 handle
= data_handle
;
612 case FLIP_PAYLOAD_DECODING_MODE_FORCED
:
613 if (is_forced_handle_ok
== TRUE
) {
614 handle
= forced_handle
;
617 /* Use data as backup. */
618 handle
= data_handle
;
620 /* Tell the user he messed up. */
621 has_user_messed_up
= TRUE
;
626 /* Fault in dissector's internal logic. */
632 * If tree is NULL, we still cannot quit, we must give
633 * the RTP/RTCP/data dissectors a chance to fill in
634 * the protocol column.
636 data_len
= call_dissector(handle
, payload_tvb
, pinfo
, tree
);
638 if (has_user_messed_up
== TRUE
) {
639 col_add_fstr(pinfo
->cinfo
, COL_INFO
,
640 "Invalid user dissector \"%s\"",
641 global_forced_protocol
);
644 bytes_dissected
+= data_len
;
646 } /* if (payload_len > 0) */
649 return bytes_dissected
;
651 } /* dissect_flip() */
654 /* Protocol initialization */
656 proto_register_flip(void)
658 static hf_register_info hf
[] = {
663 {"Extension Header Follows", "flip.basic.e", FT_UINT32
, BASE_DEC
,
664 VALS(flip_boolean
), 0x80000000, NULL
, HFILL
}
666 {&hf_flip_basic_reserved
,
667 {"Reserved", "flip.basic.reserved", FT_UINT32
, BASE_DEC
,
668 NULL
, 0x70000000, "Basic Header Reserved", HFILL
}
670 {&hf_flip_basic_flowid
,
671 {"FlowID", "flip.basic.flowid", FT_UINT32
, BASE_HEX
,
672 NULL
, 0x0FFFFFFF, "Basic Header Flow ID", HFILL
}
674 {&hf_flip_basic_seqnum
,
675 {"Seqnum", "flip.basic.seqnum", FT_UINT32
, BASE_DEC
,
676 NULL
, 0xFFFF0000, "Basic Header Sequence Number", HFILL
}
679 {"Len", "flip.basic.len", FT_UINT32
, BASE_DEC
,
680 NULL
, 0x0000FFFF, "Basic Header Packet Length", HFILL
}
685 {&hf_flip_chksum_etype
,
686 {"Extension Type", "flip.chksum.etype", FT_UINT32
, BASE_DEC
,
687 VALS(flip_etype
), 0xFF000000, "Checksum Header Extension Type", HFILL
}
689 {&hf_flip_chksum_spare
,
690 {"Spare", "flip.chksum.spare", FT_UINT32
, BASE_DEC
,
691 NULL
, 0x00FE0000, "Checksum Header Spare", HFILL
}
694 {"Extension Header Follows", "flip.chksum.e", FT_UINT32
, BASE_DEC
,
695 VALS(flip_boolean
), 0x00010000, NULL
, HFILL
}
697 {&hf_flip_chksum_chksum
,
698 {"Checksum", "flip.chksum.chksum", FT_UINT32
, BASE_HEX
,
699 NULL
, 0x0000FFFF, NULL
, HFILL
}
703 static gint
*ett
[] = {
710 module_t
*flip_module
;
712 proto_flip
= proto_register_protocol(
713 "NSN FLIP", /* name */
714 "FLIP", /* short name */
718 proto_register_field_array(proto_flip
, hf
, array_length(hf
));
719 proto_register_subtree_array(ett
, array_length(ett
));
722 flip_module
= prefs_register_protocol(proto_flip
,
723 proto_reg_handoff_flip
);
725 /* Register preferences */
726 prefs_register_enum_preference(
729 "FLIP payload decoding mode",
730 "Decode FLIP payload according to mode",
731 &global_flip_payload_decoding_mode
,
732 flip_payload_decoding_modes
,
735 prefs_register_static_text_preference(
737 "heur_enabled_protocols",
738 "Heuristic mode: enabled protocols",
739 "Enabled protocols for heuristic mode");
741 prefs_register_bool_preference(
745 "Decode payload as RTP if detected",
746 &is_heur_enabled_rtp
);
748 prefs_register_bool_preference(
752 "Decode payload as RTCP if detected",
753 &is_heur_enabled_rtcp
);
755 prefs_register_static_text_preference(
758 "Forced mode: decode to user-specified protocol",
759 "Mapping of flow IDs to their decodings");
761 prefs_register_string_preference(
765 "Decoding to user-defined protocol",
766 &global_forced_protocol
);
768 } /* proto_register_flip() */
770 /* Protocol handoff */
772 proto_reg_handoff_flip(void)
774 dissector_handle_t flip_handle
;
776 static gboolean flip_prefs_initialized
= FALSE
;
778 if (flip_prefs_initialized
== FALSE
) {
779 flip_handle
= new_create_dissector_handle(dissect_flip
, proto_flip
);
780 dissector_add_uint("ethertype", ETHERTYPE_FLIP
, flip_handle
);
782 rtp_handle
= find_dissector("rtp");
783 rtcp_handle
= find_dissector("rtcp");
784 data_handle
= find_dissector("data");
786 flip_prefs_initialized
= TRUE
;
789 /* Preferences update: check user-specified dissector. */
790 is_forced_handle_ok
= FALSE
;
791 forced_handle
= find_dissector(global_forced_protocol
);
792 if (forced_handle
!= NULL
) {
793 is_forced_handle_ok
= TRUE
;
796 } /* proto_reg_handoff_flip() */
798 /* end of file packet-flip.c */