/* packet-ifcp.c * Routines for iFCP dissection * RFC 3821, RFC 3643 * * Copyright 2005 Aboo Valappil (valappil_aboo@emc.com) * 2006 ronnie sahlberg major refactoring * * * Significantly based on packet-fcip.c by * Copyright 2001, Dinesh G Dutt (ddutt@cisco.com) * * Wireshark - Network traffic analyzer * By Gerald Combs * Copyright 1998 Gerald Combs * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "config.h" #include #include #include "packet-tcp.h" #include "packet-fc.h" void proto_register_ifcp(void); void proto_reg_handoff_ifcp(void); #define iFCP_ENCAP_HEADER_LEN 28 #define iFCP_MIN_HEADER_LEN 16 /* up to frame len field */ typedef enum { iFCP_EOFn = 0x41, iFCP_EOFt = 0x42, iFCP_EOFrt = 0x44, iFCP_EOFdt = 0x46, iFCP_EOFni = 0x49, iFCP_EOFdti = 0x4E, iFCP_EOFrti = 0x4F, iFCP_EOFa = 0x50 } ifcp_eof_t; typedef enum { iFCP_SOFf = 0x28, iFCP_SOFi4 = 0x29, iFCP_SOFi2 = 0x2D, iFCP_SOFi3 = 0x2E, iFCP_SOFn4 = 0x31, iFCP_SOFn2 = 0x35, iFCP_SOFn3 = 0x36, iFCP_SOFc4 = 0x39 } ifcp_sof_t; typedef enum { FCENCAP_PROTO_FCIP = 1, FCENCAP_PROTO_iFCP = 2 } fcencap_proto_t; static const value_string ifcp_eof_vals[] = { {iFCP_EOFn, "EOFn" }, {iFCP_EOFt, "EOFt" }, {iFCP_EOFrt, "EOFrt" }, {iFCP_EOFdt, "EOFdt" }, {iFCP_EOFni, "EOFni" }, {iFCP_EOFdti, "EOFdti" }, {iFCP_EOFrti, "EOFrti" }, {iFCP_EOFa, "EOFa" }, {0, NULL}, }; static const value_string ifcp_sof_vals[] = { {iFCP_SOFf, "SOFf" }, {iFCP_SOFi4, "SOFi4" }, {iFCP_SOFi2, "SOFi2" }, {iFCP_SOFi3, "SOFi3" }, {iFCP_SOFn4, "SOFn4" }, {iFCP_SOFn2, "SOFn2" }, {iFCP_SOFn3, "SOFn3" }, {iFCP_SOFc4, "SOFc4" }, {0, NULL}, }; static const value_string fcencap_proto_vals[] = { {FCENCAP_PROTO_iFCP, "iFCP"}, {FCENCAP_PROTO_iFCP, "iFCP"}, {0, NULL}, }; /* RFC 4172 section 5.3.1 shows a chart of the iFCP encapsulated Header Format. * It says that bytes 4-7 MUST be zeros. In reality most vendors are putting * some information in these 4 bytes, particularly Nishon. */ static const uint8_t ifcp_header_4_bytes[4] = { 0x02, 0x01, 0xFD, 0xFE }; static int proto_ifcp; static int hf_ifcp_protocol; static int hf_ifcp_protocol_c; static int hf_ifcp_version; static int hf_ifcp_version_c; static int hf_ifcp_encap_flags_c; static int hf_ifcp_framelen; static int hf_ifcp_framelen_c; static int hf_ifcp_tsec; static int hf_ifcp_tusec; static int hf_ifcp_encap_crc; static int hf_ifcp_sof; static int hf_ifcp_sof_c; static int hf_ifcp_eof; static int hf_ifcp_eof_c; static int hf_ifcp_ls_command_acc; static int hf_ifcp_flags; static int hf_ifcp_flags_ses; static int hf_ifcp_flags_trp; static int hf_ifcp_flags_spc; static int hf_ifcp_common_flags; static int hf_ifcp_common_flags_crcv; static int ett_ifcp; static int ett_ifcp_sof; static int ett_ifcp_eof; static int ett_ifcp_flags; static int ett_ifcp_common_flags; static int ett_ifcp_protocol; static int ett_ifcp_version; static int ett_ifcp_frame_len; static bool ifcp_desegment = true; static dissector_handle_t ifcp_handle; static dissector_handle_t fc_handle; /* This function checks the first 16 bytes of the "header" that it looks sane * and returns true if this looks like iFCP and false if it doesn't. */ static bool ifcp_header_test(tvbuff_t *tvb, int offset) { uint16_t flen, flen1; /* we can only do this test if we have 16 bytes or more */ if(tvb_captured_length_remaining(tvb, offset) 545)){ return false; } /* check the complement of the frame length */ flen1=tvb_get_ntohs(tvb, offset+14)&0x03FF; if(flen!=((~flen1)&0x03FF)){ return false; } /* this should be good enough for our heuristics */ return true; } #define IFCP_FLAGS_SES 0x04 #define IFCP_FLAGS_TRP 0x02 #define IFCP_FLAGS_SPC 0x01 static const true_false_string ifcp_flags_ses_tfs = { "This is a SESSION CONTROL FRAME", "This is a normal frame" }; static const true_false_string ifcp_flags_trp_tfs = { "Address TRANSPARENT Mode Enabled", "Address TRANSLATION Mode Enabled" }; static const true_false_string ifcp_flags_spc_tfs = { "This frame requires SPECIAL PROCESSING", "This is a normal frame" }; static int dissect_ifcpflags(tvbuff_t *tvb, int offset, proto_tree *parent_tree) { static int * const flags[] = { &hf_ifcp_flags_ses, &hf_ifcp_flags_trp, &hf_ifcp_flags_spc, NULL }; proto_tree_add_bitmask(parent_tree, tvb, offset, hf_ifcp_flags, ett_ifcp_flags, flags, ENC_BIG_ENDIAN); offset++; return offset; } #define IFCP_COMMON_FLAGS_CRCV 0x04 static void dissect_commonflags(tvbuff_t *tvb, int offset, proto_tree *parent_tree) { static int * const flags[] = { &hf_ifcp_common_flags_crcv, NULL }; proto_tree_add_bitmask(parent_tree, tvb, offset, hf_ifcp_common_flags, ett_ifcp_common_flags, flags, ENC_BIG_ENDIAN); } static int dissect_ifcp_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void* data _U_) { int offset = 0, frame_len = 0; uint8_t sof = 0, eof = 0; proto_item *ti; proto_tree *tree = NULL; tvbuff_t *next_tvb; uint8_t protocol; proto_tree *protocol_tree = NULL; proto_tree *version_tree = NULL; proto_tree *frame_len_tree = NULL; proto_tree *sof_tree = NULL; proto_tree *eof_tree = NULL; fc_data_t fc_data; /* verify we have a full header (do we need to do this? */ if(tvb_captured_length(tvb)cinfo, COL_PROTOCOL, "iFCP"); frame_len = (tvb_get_ntohs (tvb, offset+12) & 0x03FF)*4; if (parent_tree) { if (tvb_bytes_exist (tvb, offset, frame_len-4)) { sof = tvb_get_uint8 (tvb, offset+iFCP_ENCAP_HEADER_LEN); eof = tvb_get_uint8 (tvb, offset+frame_len - 4); ti = proto_tree_add_protocol_format (parent_tree, proto_ifcp, tvb, offset, iFCP_ENCAP_HEADER_LEN, "iFCP (%s/%s)", val_to_str (sof, ifcp_sof_vals, "0x%x"), val_to_str (eof, ifcp_eof_vals, "0x%x")); } else { sof = tvb_get_uint8 (tvb, offset+iFCP_ENCAP_HEADER_LEN); ti = proto_tree_add_protocol_format (parent_tree, proto_ifcp, tvb, offset, iFCP_ENCAP_HEADER_LEN, "iFCP (%s/%s)", val_to_str (sof, ifcp_sof_vals, "0x%x"), "NA"); } tree = proto_item_add_subtree (ti, ett_ifcp); } /* The Common FC Encap header */ /* protocol */ protocol = tvb_get_uint8 (tvb, offset); ti=proto_tree_add_item(tree, hf_ifcp_protocol, tvb, offset, 1, ENC_BIG_ENDIAN); protocol_tree=proto_item_add_subtree(ti, ett_ifcp_protocol); offset++; /* version */ ti=proto_tree_add_item(tree, hf_ifcp_version, tvb, offset, 1, ENC_BIG_ENDIAN); version_tree=proto_item_add_subtree(ti, ett_ifcp_version); offset++; /* protocol complement */ proto_tree_add_item(protocol_tree, hf_ifcp_protocol_c, tvb, offset, 1, ENC_BIG_ENDIAN); offset++; /* version complement */ proto_tree_add_item(version_tree, hf_ifcp_version_c, tvb, offset, 1, ENC_BIG_ENDIAN); offset++; /* 4 reserved bytes */ offset+=4; /* iFCP specific fields */ if(protocol==FCENCAP_PROTO_iFCP){ /* LS_COMMAND_ACC */ proto_tree_add_item(tree, hf_ifcp_ls_command_acc, tvb, offset, 1, ENC_BIG_ENDIAN); offset++; /* iFCP Flags */ offset=dissect_ifcpflags(tvb, offset, tree); /* SOF */ ti=proto_tree_add_item(tree, hf_ifcp_sof, tvb, offset, 1, ENC_BIG_ENDIAN); sof_tree=proto_item_add_subtree(ti, ett_ifcp_sof); offset++; /* EOF */ ti=proto_tree_add_item(tree, hf_ifcp_eof, tvb, offset, 1, ENC_BIG_ENDIAN); eof_tree=proto_item_add_subtree(ti, ett_ifcp_eof); offset++; } else { offset+=4; sof_tree=tree; /* better than nothing */ eof_tree=tree; } /* Common Flags */ dissect_commonflags(tvb, offset, tree); /* frame len */ ti=proto_tree_add_item(tree, hf_ifcp_framelen, tvb, offset, 2, ENC_BIG_ENDIAN); frame_len_tree=proto_item_add_subtree(ti, ett_ifcp_frame_len); offset+=2; /* complement of flags and frame len */ proto_tree_add_item(frame_len_tree, hf_ifcp_encap_flags_c, tvb, offset, 1, ENC_BIG_ENDIAN); proto_tree_add_item(frame_len_tree, hf_ifcp_framelen_c, tvb, offset, 2, ENC_BIG_ENDIAN); offset+=2; /* timestamp seconds */ proto_tree_add_item(tree, hf_ifcp_tsec, tvb, offset, 4, ENC_BIG_ENDIAN); offset+=4; /* timestamp fractions */ proto_tree_add_item(tree, hf_ifcp_tusec, tvb, offset, 4, ENC_BIG_ENDIAN); offset+=4; /* crc */ proto_tree_add_item(tree, hf_ifcp_encap_crc, tvb, offset, 4, ENC_BIG_ENDIAN); offset+=4; /* FC SOF/-SOF */ proto_tree_add_item(sof_tree, hf_ifcp_sof, tvb, offset, 1, ENC_BIG_ENDIAN); offset++; proto_tree_add_item(sof_tree, hf_ifcp_sof, tvb, offset, 1, ENC_BIG_ENDIAN); offset++; proto_tree_add_item(sof_tree, hf_ifcp_sof_c, tvb, offset, 1, ENC_BIG_ENDIAN); offset++; proto_tree_add_item(sof_tree, hf_ifcp_sof_c, tvb, offset, 1, ENC_BIG_ENDIAN); offset++; /* FC EOF/-EOF */ if(tvb_bytes_exist(tvb, frame_len-4, 4)) { proto_tree_add_item(eof_tree, hf_ifcp_eof, tvb, frame_len-4, 1, ENC_BIG_ENDIAN); proto_tree_add_item(eof_tree, hf_ifcp_eof, tvb, frame_len-3, 1, ENC_BIG_ENDIAN); proto_tree_add_item(eof_tree, hf_ifcp_eof_c, tvb, frame_len-2, 1, ENC_BIG_ENDIAN); proto_tree_add_item(eof_tree, hf_ifcp_eof_c, tvb, frame_len-1, 1, ENC_BIG_ENDIAN); } /* Call the FC Dissector if this is carrying an FC frame */ /* Set the SOF/EOF flags in the packet_info header */ fc_data.sof_eof = 0; switch(sof){ case iFCP_SOFi3: case iFCP_SOFi2: case iFCP_SOFi4: fc_data.sof_eof = FC_DATA_SOF_FIRST_FRAME; break; case iFCP_SOFf: fc_data.sof_eof = FC_DATA_SOF_SOFF; break; default: if(sof){ if (eof != iFCP_EOFn) { fc_data.sof_eof |= FC_DATA_EOF_LAST_FRAME; } else if (eof != iFCP_EOFt) { fc_data.sof_eof |= FC_DATA_EOF_INVALID; } } } next_tvb=tvb_new_subset_length(tvb, offset, frame_len-offset-4); fc_data.ethertype = ETHERTYPE_UNK; if(fc_handle){ call_dissector_with_data(fc_handle, next_tvb, pinfo, parent_tree, &fc_data); } else { call_data_dissector(next_tvb, pinfo, parent_tree); } return tvb_captured_length(tvb); } static unsigned get_ifcp_pdu_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void *data _U_) { unsigned pdu_len; if(!ifcp_header_test(tvb, offset)){ return 0; } pdu_len=(tvb_get_ntohs(tvb, offset+12)&0x03FF)*4; return pdu_len; } static int dissect_ifcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void* data) { tcp_dissect_pdus(tvb, pinfo, parent_tree, ifcp_desegment, iFCP_MIN_HEADER_LEN, get_ifcp_pdu_len, dissect_ifcp_pdu, data); return tvb_captured_length(tvb); } /* This is called for those sessions where we have explicitly said * this to be iFCP using "Decode As..." * In this case we will not check the port number for sanity and just * do as the user said. */ static int dissect_ifcp_handle(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data) { return dissect_ifcp(tvb, pinfo, tree, data); } static bool dissect_ifcp_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) { if(!ifcp_header_test(tvb, 0)){ return false; } dissect_ifcp(tvb, pinfo, tree, data); /* our heuristics are so strong that if the heuristics above passed * and the dissection of the pdu did not cause any exceptions * then we can set this as our conversation dissector */ if(ifcp_handle){ conversation_t* ifcp_conv; ifcp_conv=find_or_create_conversation(pinfo); /* XXX why does this not work? it doesn't result in dissect_ifcp_handle being called look into later*/ conversation_set_dissector(ifcp_conv, ifcp_handle); } return true; } void proto_register_ifcp (void) { /* Setup list of header fields See Section 1.6.1 for details*/ static hf_register_info hf[] = { { &hf_ifcp_protocol, {"Protocol", "ifcp.encap.proto", FT_UINT8, BASE_DEC, VALS(fcencap_proto_vals), 0, NULL, HFILL }}, { &hf_ifcp_protocol_c, {"Protocol (1's Complement)", "ifcp.encap.protoc", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL}}, { &hf_ifcp_version, {"Version", "ifcp.encap.version", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL}}, { &hf_ifcp_version_c, {"Version (1's Complement)", "ifcp.encap.versionc", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL}}, { &hf_ifcp_encap_flags_c, {"iFCP Encapsulation Flags (1's Complement)", "ifcp.encap_flagsc", FT_UINT8, BASE_HEX, NULL, 0xFC, NULL, HFILL}}, { &hf_ifcp_framelen, {"Frame Length (in Words)", "ifcp.encap.framelen", FT_UINT16, BASE_DEC, NULL, 0x03FF, NULL, HFILL}}, { &hf_ifcp_framelen_c, {"Frame Length (1's Complement)", "ifcp.encap.framelenc", FT_UINT16, BASE_DEC, NULL, 0x03FF, NULL, HFILL}}, { &hf_ifcp_tsec, {"Time (secs)", "ifcp.encap.tsec", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL}}, { &hf_ifcp_tusec, {"Time (fraction)", "ifcp.encap.tusec", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL}}, { &hf_ifcp_encap_crc, {"CRC", "ifcp.encap.crc", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL}}, { &hf_ifcp_sof, {"SOF", "ifcp.sof", FT_UINT8, BASE_HEX, VALS (ifcp_sof_vals), 0, NULL, HFILL}}, { &hf_ifcp_eof, {"EOF", "ifcp.eof", FT_UINT8, BASE_HEX, VALS (ifcp_eof_vals), 0, NULL, HFILL}}, { &hf_ifcp_sof_c, {"SOF Compliment", "ifcp.sof_c", FT_UINT8, BASE_HEX, NULL , 0, NULL, HFILL}}, { &hf_ifcp_eof_c, {"EOF Compliment", "ifcp.eof_c", FT_UINT8, BASE_HEX, NULL , 0, NULL, HFILL}}, { &hf_ifcp_ls_command_acc, {"Ls Command Acc", "ifcp.ls_command_acc", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL}}, { &hf_ifcp_common_flags, {"Flags", "ifcp.common_flags", FT_UINT8, BASE_HEX , NULL, 0xfc, NULL, HFILL }}, { &hf_ifcp_common_flags_crcv, {"CRC", "ifcp.common_flags.crcv", FT_BOOLEAN, 8, TFS(&tfs_valid_not_valid), IFCP_COMMON_FLAGS_CRCV, "Is the CRC field valid?", HFILL }}, { &hf_ifcp_flags, {"iFCP Flags", "ifcp.flags", FT_UINT8, BASE_HEX , NULL, 0, NULL, HFILL }}, { &hf_ifcp_flags_ses, {"SES", "ifcp.flags.ses", FT_BOOLEAN, 8, TFS(&ifcp_flags_ses_tfs), IFCP_FLAGS_SES, "Is this a Session control frame", HFILL }}, { &hf_ifcp_flags_trp, {"TRP", "ifcp.flags.trp", FT_BOOLEAN, 8, TFS(&ifcp_flags_trp_tfs), IFCP_FLAGS_TRP, "Is address transparent mode enabled", HFILL }}, { &hf_ifcp_flags_spc, {"SPC", "ifcp.flags.spc", FT_BOOLEAN, 8, TFS(&ifcp_flags_spc_tfs), IFCP_FLAGS_SPC, "Is frame part of link service", HFILL }}, }; static int *ett[] = { &ett_ifcp, &ett_ifcp_sof, &ett_ifcp_eof, &ett_ifcp_protocol, &ett_ifcp_version, &ett_ifcp_frame_len, &ett_ifcp_flags, &ett_ifcp_common_flags, }; module_t *ifcp_module; /* Register the protocol name and description */ proto_ifcp = proto_register_protocol("iFCP", "iFCP", "ifcp"); proto_register_field_array(proto_ifcp, hf, array_length(hf)); proto_register_subtree_array(ett, array_length(ett)); ifcp_module = prefs_register_protocol(proto_ifcp, NULL); prefs_register_bool_preference(ifcp_module, "desegment", "Reassemble iFCP messages spanning multiple TCP segments", "Whether the iFCP dissector should reassemble messages spanning multiple TCP segments." " To use this option, you must also enable \"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.", &ifcp_desegment); prefs_register_obsolete_preference(ifcp_module, "target_port"); ifcp_handle = register_dissector("ifcp", dissect_ifcp_handle, proto_ifcp); } void proto_reg_handoff_ifcp (void) { heur_dissector_add("tcp", dissect_ifcp_heur, "iFCP over TCP", "ifcp_tcp", proto_ifcp, HEURISTIC_ENABLE); dissector_add_for_decode_as_with_preference("tcp.port", ifcp_handle); fc_handle = find_dissector_add_dependency("fc_ifcp", proto_ifcp); } /* * Editor modelines - https://www.wireshark.org/tools/modelines.html * * Local variables: * c-basic-offset: 4 * tab-width: 8 * indent-tabs-mode: nil * End: * * vi: set shiftwidth=4 tabstop=8 expandtab: * :indentSize=4:tabSize=8:noTabs=true: */