2 * Routines for CPFI Cross Point Frame Injector dissection
3 * CPFI - Cross Point Frame Injector is a CNT proprietary
4 * protocol used to carry Fibre Channel data over UDP
6 * Copyright 2003, Dave Sclarsky <dave_sclarsky[AT]cnt.com>
8 * Wireshark - Network traffic analyzer
9 * By Gerald Combs <gerald@wireshark.org>
10 * Copyright 1998 Gerald Combs
12 * Copied from packet-m2tp.c
13 * Thanks to Heinz Prantner for his motivation and assistance
15 * SPDX-License-Identifier: GPL-2.0-or-later
20 #include <epan/packet.h>
21 #include <epan/prefs.h>
22 #include "packet-fc.h"
24 void proto_register_cpfi(void);
25 void proto_reg_handoff_cpfi(void);
27 #define CPFI_DEFAULT_UDP_PORT 5000 /* Not IANA registered */
28 #define CPFI_DEFAULT_TTOT_UDP_PORT 5001 /* Not IANA registered */
30 #define FIRST_TIO_CARD_ADDRESS 0x380
34 #define CPFI_FRAME_TYPE_MASK 0xF0000000
35 #define CPFI_FRAME_TYPE_SHIFT 28
36 #define CPFI_SOURCE_MASK 0x0FFC0000
37 #define CPFI_SOURCE_SHIFT 18
38 #define CPFI_DEST_MASK 0x0003FF00
39 #define CPFI_DEST_SHIFT 8
40 #define CPFI_SOF_TYPE_MASK 0x000000F0
41 #define CPFI_SPEED_MASK 0x0000000C
42 #define CPFI_OPM_ERROR_MASK 0x00000002
43 #define CPFI_FROM_LCM_MASK 0x00000001
46 #define CPFI_EOF_TYPE_MASK 0x78000000
47 #define CPFI_EOF_ERROR_MASK 0x7FE00000
49 /* configurable parameters */
50 static unsigned gbl_cpfi_ttot_udp_port
= CPFI_DEFAULT_TTOT_UDP_PORT
;
51 static bool cpfi_arrow_moves
= true;
53 /* Initialize the protocol and registered fields */
54 static int proto_cpfi
;
55 static int hf_cpfi_word_one
;
56 static int hf_cpfi_word_two
;
58 static int hf_cpfi_frame_type
;
59 static int hf_cpfi_source
;
60 static int hf_cpfi_dest
;
61 static int hf_cpfi_SOF_type
;
62 static int hf_cpfi_speed
;
63 static int hf_cpfi_OPM_error
;
64 static int hf_cpfi_from_LCM
;
66 static int hf_cpfi_CRC_32
;
67 static int hf_cpfi_EOF_type
;
69 static int hf_cpfi_t_instance
;
70 static int hf_cpfi_t_src_instance
;
71 static int hf_cpfi_t_dst_instance
;
72 static int hf_cpfi_t_board
;
73 static int hf_cpfi_t_src_board
;
74 static int hf_cpfi_t_dst_board
;
75 static int hf_cpfi_t_port
;
76 static int hf_cpfi_t_src_port
;
77 static int hf_cpfi_t_dst_port
;
79 static char src_str
[20];
80 static char dst_str
[20];
81 static const char l_to_r_arrow
[] = "-->";
82 static const char r_to_l_arrow
[] = "<--";
83 static const char *left
= src_str
;
84 static const char *right
= dst_str
;
85 static const char *arrow
= l_to_r_arrow
;
86 static const char direction_and_port_string
[] = "[%s %s %s] ";
89 /* Initialize the subtree pointers */
91 static int ett_cpfi_header
;
92 static int ett_cpfi_footer
;
94 static dissector_handle_t cpfi_handle
;
95 static dissector_handle_t fc_handle
;
98 static const value_string sof_type_vals
[] = {
113 static const value_string speed_vals
[] = {
121 static const value_string eof_type_vals
[] = {
135 dissect_cpfi_header(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
)
143 uint8_t src_instance
= 0;
144 uint8_t src_board
= 0;
145 uint8_t src_port
= 0;
147 uint8_t dst_instance
= 0;
148 uint8_t dst_board
= 0;
149 uint8_t dst_port
= 0;
150 proto_tree
*extra_tree
= NULL
;
152 /* add a tree for the header */
155 proto_item
*extra_item
;
156 extra_item
= proto_tree_add_protocol_format(tree
, proto_cpfi
, tvb
, 0, -1, "Header");
157 extra_tree
= proto_item_add_subtree(extra_item
, ett_cpfi_header
);
160 /* Extract the common header, and get the bits we need */
161 word1
= tvb_get_ntohl (tvb
, 0);
163 word2
= tvb_get_ntohl (tvb
, sizeof(word1
));
166 /* Figure out where the frame came from. dstTDA is source of frame! */
167 tda
= (word1
& CPFI_DEST_MASK
) >> CPFI_DEST_SHIFT
;
168 if ( tda
>= FIRST_TIO_CARD_ADDRESS
)
170 (void) g_strlcpy(src_str
, " CPFI", sizeof(src_str
));
171 src
= 0; /* Make it smallest */
175 const uint8_t *srcmac
;
177 /* Make sure this is an Ethernet address. */
178 DISSECTOR_ASSERT(pinfo
->src
.type
== AT_ETHER
);
179 srcmac
= (const uint8_t *)pinfo
->src
.data
;
181 src_instance
= srcmac
[2]-1;
182 src_board
= tda
>> 4;
183 src_port
= tda
& 0x0f;
184 src
= (1 << 24) + (src_instance
<< 16) + (src_board
<< 8) + src_port
;
185 snprintf(src_str
, sizeof(src_str
), "%u.%u.%u", src_instance
, src_board
, src_port
);
188 /* Figure out where the frame is going. srcTDA is destination of frame! */
189 tda
= (word1
& CPFI_SOURCE_MASK
) >> CPFI_SOURCE_SHIFT
;
190 if ( tda
>= FIRST_TIO_CARD_ADDRESS
)
192 (void) g_strlcpy(dst_str
, " CPFI", sizeof(dst_str
));
193 dst
= 0; /* Make it smallest */
197 const uint8_t *dstmac
;
199 /* Make sure this is an Ethernet address. */
200 DISSECTOR_ASSERT(pinfo
->dst
.type
== AT_ETHER
);
201 dstmac
= (const uint8_t *)pinfo
->dst
.data
;
203 dst_instance
= dstmac
[2]-1;
204 dst_board
= tda
>> 4;
205 dst_port
= tda
& 0x0f;
206 dst
= (1 << 24) + (dst_instance
<< 16) + (dst_board
<< 8) + dst_port
;
207 snprintf(dst_str
, sizeof(dst_str
), "%u.%u.%u", dst_instance
, dst_board
, dst_port
);
210 /* Set up the source and destination and arrow per user configuration. */
211 if ( cpfi_arrow_moves
&& (dst
< src
) )
214 arrow
= r_to_l_arrow
;
220 arrow
= l_to_r_arrow
;
225 proto_item
*hidden_item
;
226 /* For "real" TDAs (i.e. not for microTDAs), add hidden addresses to allow filtering */
229 hidden_item
= proto_tree_add_bytes(extra_tree
, hf_cpfi_t_instance
, tvb
, 0, 1, &src_instance
);
230 proto_item_set_hidden(hidden_item
);
231 hidden_item
= proto_tree_add_bytes(extra_tree
, hf_cpfi_t_src_instance
, tvb
, 0, 1, &src_instance
);
232 proto_item_set_hidden(hidden_item
);
233 hidden_item
= proto_tree_add_bytes(extra_tree
, hf_cpfi_t_board
, tvb
, 0, 1, &src_board
);
234 proto_item_set_hidden(hidden_item
);
235 hidden_item
= proto_tree_add_bytes(extra_tree
, hf_cpfi_t_src_board
, tvb
, 0, 1, &src_board
);
236 proto_item_set_hidden(hidden_item
);
237 hidden_item
= proto_tree_add_bytes(extra_tree
, hf_cpfi_t_port
, tvb
, 0, 1, &src_port
);
238 proto_item_set_hidden(hidden_item
);
239 hidden_item
= proto_tree_add_bytes(extra_tree
, hf_cpfi_t_src_port
, tvb
, 0, 1, &src_port
);
240 proto_item_set_hidden(hidden_item
);
244 hidden_item
= proto_tree_add_bytes(extra_tree
, hf_cpfi_t_instance
, tvb
, 0, 1, &dst_instance
);
245 proto_item_set_hidden(hidden_item
);
246 hidden_item
= proto_tree_add_bytes(extra_tree
, hf_cpfi_t_dst_instance
, tvb
, 0, 1, &dst_instance
);
247 proto_item_set_hidden(hidden_item
);
248 hidden_item
= proto_tree_add_bytes(extra_tree
, hf_cpfi_t_board
, tvb
, 0, 1, &dst_board
);
249 proto_item_set_hidden(hidden_item
);
250 hidden_item
= proto_tree_add_bytes(extra_tree
, hf_cpfi_t_dst_board
, tvb
, 0, 1, &dst_board
);
251 proto_item_set_hidden(hidden_item
);
252 hidden_item
= proto_tree_add_bytes(extra_tree
, hf_cpfi_t_port
, tvb
, 0, 1, &dst_port
);
253 proto_item_set_hidden(hidden_item
);
254 hidden_item
= proto_tree_add_bytes(extra_tree
, hf_cpfi_t_dst_port
, tvb
, 0, 1, &dst_port
);
255 proto_item_set_hidden(hidden_item
);
258 /* add word 1 components to the protocol tree */
259 proto_tree_add_item(extra_tree
, hf_cpfi_word_one
, tvb
, 0, 4, ENC_BIG_ENDIAN
);
261 proto_tree_add_item(extra_tree
, hf_cpfi_frame_type
, tvb
, 0, 4, ENC_BIG_ENDIAN
);
262 proto_tree_add_item(extra_tree
, hf_cpfi_source
, tvb
, 0, 4, ENC_BIG_ENDIAN
);
263 proto_tree_add_item(extra_tree
, hf_cpfi_dest
, tvb
, 0, 4, ENC_BIG_ENDIAN
);
264 proto_tree_add_item(extra_tree
, hf_cpfi_SOF_type
, tvb
, 0, 4, ENC_BIG_ENDIAN
);
265 proto_tree_add_item(extra_tree
, hf_cpfi_speed
, tvb
, 0, 4, ENC_BIG_ENDIAN
);
266 proto_tree_add_item(extra_tree
, hf_cpfi_OPM_error
, tvb
, 0, 4, ENC_BIG_ENDIAN
);
267 proto_tree_add_item(extra_tree
, hf_cpfi_from_LCM
, tvb
, 0, 4, ENC_BIG_ENDIAN
);
269 /* add word 2 components to the protocol tree */
270 proto_tree_add_item(extra_tree
, hf_cpfi_word_two
, tvb
, 4, 4, ENC_BIG_ENDIAN
);
276 dissect_cpfi_footer(tvbuff_t
*tvb
, proto_tree
*tree
)
278 proto_tree
*extra_tree
= NULL
;
280 /* add a tree for the footer */
283 proto_item
*extra_item
;
284 extra_item
= proto_tree_add_protocol_format(tree
, proto_cpfi
, tvb
, 0, -1, "Footer");
285 extra_tree
= proto_item_add_subtree(extra_item
, ett_cpfi_footer
);
289 proto_tree_add_item(extra_tree
, hf_cpfi_CRC_32
, tvb
, 0, 4, ENC_BIG_ENDIAN
);
290 proto_tree_add_item(extra_tree
, hf_cpfi_EOF_type
, tvb
, 4, 4, ENC_BIG_ENDIAN
);
296 dissect_cpfi(tvbuff_t
*message_tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
)
298 tvbuff_t
*header_tvb
, *body_tvb
, *footer_tvb
;
299 proto_item
*cpfi_item
= NULL
;
300 proto_tree
*cpfi_tree
= NULL
;
301 int length
, reported_length
, body_length
, reported_body_length
;
305 frame_type
= (tvb_get_ntohl (message_tvb
, 0) & CPFI_FRAME_TYPE_MASK
) >> CPFI_FRAME_TYPE_SHIFT
;
307 /* If this is not a CPFI frame, return 0 to let another dissector try to
310 if ( !((frame_type
== 9) && fc_handle
) )
313 /* If we don't have Ethernet addresses, can't do further dissection... */
314 if (pinfo
->dst
.type
!= AT_ETHER
|| pinfo
->src
.type
!= AT_ETHER
)
317 length
= tvb_captured_length_remaining(message_tvb
, 8);
318 reported_length
= tvb_reported_length_remaining(message_tvb
, 8);
319 if (reported_length
< 8)
321 /* We don't even have enough for the footer. */
325 /* Length of packet, minus the footer. */
326 reported_body_length
= reported_length
- 8;
327 /* How much of that do we have in the tvbuff? */
328 body_length
= length
;
329 if (body_length
> reported_body_length
)
330 body_length
= reported_body_length
;
332 length
= tvb_captured_length_remaining(message_tvb
, 8+body_length
);
335 /* The footer wasn't captured at all.
336 XXX - we'd like to throw a BoundsError if that's the case. */
340 /* In the interest of speed, if "tree" is NULL, don't do any work not
341 necessary to generate protocol tree items. */
343 /* create the protocol tree */
344 cpfi_item
= proto_tree_add_item(tree
, proto_cpfi
, message_tvb
, 0, -1, ENC_NA
);
345 cpfi_tree
= proto_item_add_subtree(cpfi_item
, ett_cpfi
);
348 /* Set up the frame controls - can we do better than this? */
350 fc_data
.sof_eof
|= FC_DATA_SOF_FIRST_FRAME
;
351 fc_data
.sof_eof
|= FC_DATA_EOF_LAST_FRAME
;
352 fc_data
.sof_eof
|= FC_DATA_EOF_INVALID
;
354 /* dissect the message */
356 /* extract and process the header */
357 header_tvb
= tvb_new_subset_length(message_tvb
, 0, 8);
358 dissect_cpfi_header(header_tvb
, pinfo
, cpfi_tree
);
360 body_tvb
= tvb_new_subset_length_caplen(message_tvb
, 8, body_length
, reported_body_length
);
361 fc_data
.ethertype
= ETHERTYPE_UNK
;
362 call_dissector_with_data(fc_handle
, body_tvb
, pinfo
, tree
, &fc_data
);
364 /* add more info, now that FC added its */
365 proto_item_append_text(cpfi_item
, direction_and_port_string
, left
, arrow
, right
);
366 col_prepend_fstr(pinfo
->cinfo
, COL_INFO
, direction_and_port_string
, left
, arrow
, right
);
369 footer_tvb
= tvb_new_subset_length_caplen(message_tvb
, 8+body_length
, length
, 8);
370 dissect_cpfi_footer(footer_tvb
, cpfi_tree
);
372 return tvb_reported_length(message_tvb
);
375 /* Register the protocol with Wireshark */
377 proto_register_cpfi(void)
379 module_t
*cpfi_module
;
381 /* Setup list of header fields */
382 static hf_register_info hf
[] = {
385 { "Word one", "cpfi.word_one",
386 FT_UINT32
, BASE_HEX
, NULL
, 0x0,
389 { "Word two", "cpfi.word_two",
390 FT_UINT32
, BASE_HEX
, NULL
, 0x0,
393 { &hf_cpfi_frame_type
,
394 { "FrmType", "cpfi.frmtype",
395 FT_UINT32
, BASE_HEX
, NULL
, CPFI_FRAME_TYPE_MASK
,
396 "Frame Type", HFILL
}},
398 { "srcTDA", "cpfi.srcTDA",
399 FT_UINT32
, BASE_HEX
, NULL
, CPFI_SOURCE_MASK
,
400 "Source TDA (10 bits)", HFILL
}},
402 { "dstTDA", "cpfi.dstTDA",
403 FT_UINT32
, BASE_HEX
, NULL
, CPFI_DEST_MASK
,
404 "Source TDA (10 bits)", HFILL
}},
406 { "SOFtype", "cpfi.SOFtype",
407 FT_UINT32
, BASE_HEX
, VALS(sof_type_vals
), CPFI_SOF_TYPE_MASK
,
410 { "speed", "cpfi.speed",
411 FT_UINT32
, BASE_HEX
, VALS(speed_vals
), CPFI_SPEED_MASK
,
413 { &hf_cpfi_OPM_error
,
414 { "OPMerror", "cpfi.OPMerror",
415 FT_BOOLEAN
, 32, NULL
, CPFI_OPM_ERROR_MASK
,
416 "OPM Error?", HFILL
}},
418 { "fromLCM", "cpfi.fromLCM",
419 FT_BOOLEAN
, 32, NULL
, CPFI_FROM_LCM_MASK
,
420 "from LCM?", HFILL
}},
423 { "CRC-32", "cpfi.crc-32",
424 FT_UINT32
, BASE_HEX
, NULL
, 0x0,
428 { "EOFtype", "cpfi.EOFtype",
429 FT_UINT32
, BASE_HEX
, VALS(eof_type_vals
), CPFI_EOF_TYPE_MASK
,
432 { &hf_cpfi_t_instance
,
433 { "Instance", "cpfi.instance",
435 NULL
, 0x0, NULL
, HFILL
}},
437 { &hf_cpfi_t_src_instance
,
438 { "Source Instance", "cpfi.src_instance",
440 NULL
, 0x0, NULL
, HFILL
}},
442 { &hf_cpfi_t_dst_instance
,
443 { "Destination Instance", "cpfi.dst_instance",
445 NULL
, 0x0, NULL
, HFILL
}},
448 { "Board", "cpfi.board",
450 NULL
, 0x0, NULL
, HFILL
}},
452 { &hf_cpfi_t_src_board
,
453 { "Source Board", "cpfi.src_board",
455 NULL
, 0x0, NULL
, HFILL
}},
457 { &hf_cpfi_t_dst_board
,
458 { "Destination Board", "cpfi.dst_board",
460 NULL
, 0x0, NULL
, HFILL
}},
463 { "Port", "cpfi.port",
465 NULL
, 0x0, NULL
, HFILL
}},
467 { &hf_cpfi_t_src_port
,
468 { "Source Port", "cpfi.src_port",
470 NULL
, 0x0, NULL
, HFILL
}},
472 { &hf_cpfi_t_dst_port
,
473 { "Destination Port", "cpfi.dst_port",
475 NULL
, 0x0, NULL
, HFILL
}},
479 /* Setup protocol subtree array */
480 static int *ett
[] = {
487 /* Register the protocol name and description */
488 proto_cpfi
= proto_register_protocol("Cross Point Frame Injector", "CPFI", "cpfi");
490 /* Required function calls to register the header fields and subtrees used */
491 proto_register_field_array(proto_cpfi
, hf
, array_length(hf
));
492 proto_register_subtree_array(ett
, array_length(ett
));
494 /* Register our configuration options for CPFI */
495 cpfi_module
= prefs_register_protocol(proto_cpfi
, proto_reg_handoff_cpfi
);
496 prefs_register_uint_preference(cpfi_module
, "udp.port2", "InstanceToInstance UDP Port",
497 "Set the port for InstanceToInstance messages (if other"
498 " than the default of 5001)",
499 10, &gbl_cpfi_ttot_udp_port
);
500 prefs_register_bool_preference(cpfi_module
, "arrow_ctl",
501 "Enable Active Arrow Control",
502 "Control the way the '-->' is displayed."
503 " When enabled, keeps the 'lowest valued' endpoint of the src-dest pair"
504 " on the left, and the arrow moves to distinguish source from dest."
505 " When disabled, keeps the arrow pointing right so the source of the frame"
506 " is always on the left.",
509 cpfi_handle
= register_dissector("cpfi", dissect_cpfi
, proto_cpfi
);
513 proto_reg_handoff_cpfi(void)
515 static bool cpfi_init_complete
= false;
516 static unsigned cpfi_ttot_udp_port
;
518 if ( !cpfi_init_complete
)
520 fc_handle
= find_dissector_add_dependency("fc", proto_cpfi
);
521 dissector_add_uint_with_preference("udp.port", CPFI_DEFAULT_UDP_PORT
, cpfi_handle
);
522 cpfi_init_complete
= true;
526 dissector_delete_uint("udp.port", cpfi_ttot_udp_port
, cpfi_handle
);
529 cpfi_ttot_udp_port
= gbl_cpfi_ttot_udp_port
;
531 /* Port preference has a specific enough name to not use
532 the "auto" preference */
533 dissector_add_uint("udp.port", cpfi_ttot_udp_port
, cpfi_handle
);
542 * indent-tabs-mode: nil
545 * ex: set shiftwidth=2 tabstop=8 expandtab:
546 * :indentSize=2:tabSize=8:noTabs=true: