/* packet-pdc.c * Routines for PDC dissection * Copyright 2014, Antony Bridle * * Wireshark - Network traffic analyzer * By Gerald Combs * Copyright 1998 Gerald Combs * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "config.h" #include #include "packet-tcp.h" void proto_register_pdc(void); void proto_reg_handoff_pdc(void); /*PDC Protocol*/ #define PDC_PROCOTOL "PDC" /* PDC version */ #define PDC_VERSION 2 #define SIMPDU 1 #define RSMPDU 2 #define DRMPDU 3 #define DTMPDU 4 #define ADMPDU 5 #define EDMPDU 6 #define AKMPDU 8 /*Variable length Parameter Codes*/ #define PARAM_CODE_VERSION 2 #define PARAM_CODE_REFERENCES 3 #define PARAM_CODE_TRANSPORT 4 /* required for the TCP split packet combining */ #define PDC_MSG_SIZE_FIELD_LENGTH 2 /* minimum amount of data to find out how big the split packet should be when recombined */ #define FRAME_HEADER_LEN 8 /* global handle for our dissector */ static dissector_handle_t pdc_tcp_handle; /* global handle for calling asterix decoder if required */ static dissector_handle_t asterix_handle; static int proto_pdc; /*HF Declarations*/ static int hf_pdc_len; static int hf_pdc_mpdu_code; static int hf_pdc_credit; static int hf_pdc_simpdu_state; static int hf_pdc_yr_admu_nr; static int hf_pdc_akmpdu_mns; static int hf_pdc_akmpdu_cdt; static int hf_pdc_simpdu_var; static int hf_pdc_simpdu_var_len; static int hf_pdc_simpdu_param; static int hf_pdc_simpdu_var_version; static int hf_pdc_simpdu_var_REFSRC; static int hf_pdc_simpdu_var_REFDEST; static int hf_pdc_simpdu_var_TSEL; static int hf_pdc_drmpdu_abort; static int hf_pdc_drmpdu_reason; static int hf_pdc_drmpdu_mode; static int hf_pdc_drmpdu_init; static int hf_pdc_dtmpdu_user_size; static int hf_pdc_admpdu_admpdunr; static int hf_pdc_admpdu_size; /*Tree Declarations*/ static int ett_pdc; static int ett_pdc_simpdu_var; /*Value String Declarations*/ static const value_string valstr_simpdu_state[] = { { 1, "Operational" }, { 2, "Standby" }, { 3, "Master" }, { 4, "Slave" }, { 5, "Single" }, { 0, NULL } }; static const value_string valstr_simpdu_param[] = { { 2, "Version Number" }, { 3, "References" }, { 4, "Transport Selector" }, { 0, NULL } }; static const value_string valstr_mpdus[] = { { 1, "State Information" }, { 2, "Request State" }, { 3, "Disconnect Request" }, { 4, "Data" }, { 5, "Acknowledged Data" }, { 6, "Expedited Data" }, { 8, "Data Acknowledgement" }, { 0, NULL } }; static const value_string valstr_drmpdu_abort[] = { { 0, "Orderly Release" }, { 1, "Abortive Release" }, { 0, NULL } }; static const value_string valstr_drmpdu_mode[] = { { 0, "Node Shutdown" }, { 7, "PDC Release"}, { 0, NULL } }; static const value_string valstr_drmpdu_initatior[] = { { 0, "Server" }, { 15, "Client"}, { 0, NULL } }; static const value_string valstr_drmpdu_reason[] = { { 0, "Reason Not Specified" }, { 1, "Normal Disconnect Initiated by the MS-User" }, { 2, "Protocol Error" }, { 3, "Connection Request Refused" }, { 4, "The Remote Operational MS Entity Does Not Respond" }, { 5, "The Protocol Version Is Not Supported" }, { 6, "Mismatched References" }, { 0, NULL } }; /* start of functions here */ static int dissect_simpdu(tvbuff_t *tvb, proto_tree *tree, uint16_t offset, uint8_t lenIndicator) { int bytesProcessed; uint8_t paramCode; proto_item *simpduItem; proto_tree *simpduVarTree; proto_tree *simpduVarTree1; bytesProcessed = 0; /*Add the Credit allocation*/ proto_tree_add_item(tree, hf_pdc_credit, tvb, offset, 1, ENC_BIG_ENDIAN); bytesProcessed += 1; /*Add the State*/ proto_tree_add_item(tree, hf_pdc_simpdu_state, tvb, offset + bytesProcessed , 1, ENC_BIG_ENDIAN); bytesProcessed += 1; /*Add the YR-ADMU-NR*/ proto_tree_add_item(tree, hf_pdc_yr_admu_nr, tvb, offset + bytesProcessed , 4, ENC_BIG_ENDIAN); bytesProcessed += 4; /*Determine what's in the variable part*/ if (lenIndicator > 7) { /*Add the Variable Length Tree*/ simpduItem = proto_tree_add_item (tree, hf_pdc_simpdu_var, tvb, offset + bytesProcessed, lenIndicator - 7, ENC_NA); simpduVarTree = proto_item_add_subtree (simpduItem, ett_pdc_simpdu_var); while ((offset + bytesProcessed) < ( lenIndicator + 1 )) { /*Get the parameter code*/ paramCode = tvb_get_uint8(tvb, offset + bytesProcessed); simpduItem = proto_tree_add_item (simpduVarTree, hf_pdc_simpdu_param, tvb, offset + bytesProcessed, 1, ENC_BIG_ENDIAN); simpduVarTree1 = proto_item_add_subtree (simpduItem, ett_pdc_simpdu_var); bytesProcessed += 1; switch (paramCode) { case PARAM_CODE_VERSION: proto_tree_add_item(simpduVarTree1, hf_pdc_simpdu_var_len, tvb, offset + bytesProcessed, 1, ENC_BIG_ENDIAN); proto_tree_add_item(simpduVarTree1, hf_pdc_simpdu_var_version, tvb, offset + bytesProcessed + 1, 1, ENC_BIG_ENDIAN); bytesProcessed += 2; break; case PARAM_CODE_REFERENCES: proto_tree_add_item(simpduVarTree1, hf_pdc_simpdu_var_len, tvb, offset + bytesProcessed, 1, ENC_BIG_ENDIAN); proto_tree_add_item(simpduVarTree1, hf_pdc_simpdu_var_REFSRC, tvb, offset + bytesProcessed + 1, 2, ENC_BIG_ENDIAN); proto_tree_add_item(simpduVarTree1, hf_pdc_simpdu_var_REFDEST, tvb, offset + bytesProcessed + 3, 2, ENC_BIG_ENDIAN); bytesProcessed += 5; break; case PARAM_CODE_TRANSPORT: proto_tree_add_item(simpduVarTree1, hf_pdc_simpdu_var_len, tvb, offset + bytesProcessed, 1, ENC_BIG_ENDIAN); proto_tree_add_item(simpduVarTree1, hf_pdc_simpdu_var_TSEL, tvb, offset + bytesProcessed + 1, 2, ENC_BIG_ENDIAN); bytesProcessed += 3; break; } } /* end of whileloop */ } return (bytesProcessed); } static int dissect_rsmpdu(void) { return 0; } static int dissect_drmpdu(tvbuff_t *tvb, proto_tree *tree, uint16_t offset) { /*DR-MPDU*/ proto_tree_add_item(tree, hf_pdc_drmpdu_abort, tvb, offset, 1, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_pdc_drmpdu_mode, tvb, offset, 1, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_pdc_drmpdu_init, tvb, offset, 1, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_pdc_drmpdu_reason, tvb, offset + 1, 1, ENC_BIG_ENDIAN); return 2; } #if (PDC_VERSION == 2) static int dissect_admpdu(tvbuff_t *tvb, proto_tree *parent_tree, proto_tree *tree, uint16_t offset, packet_info *pinfo) { uint16_t userDataLen; uint16_t returnLen; tvbuff_t *asterixTVB; /*Add the ad*/ proto_tree_add_item(tree, hf_pdc_admpdu_admpdunr, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; proto_tree_add_item(tree, hf_pdc_admpdu_size, tvb, offset, 2, ENC_BIG_ENDIAN); /* length of user data field */ userDataLen = tvb_get_ntohs(tvb, offset); offset += 2; returnLen = userDataLen + 6; asterixTVB = tvb_new_subset_length(tvb, offset, userDataLen); if (asterix_handle != NULL) call_dissector(asterix_handle, asterixTVB, pinfo, parent_tree); return (returnLen); } #else static int dissect_admpdu(tvbuff_t *tvb, proto_tree *parent_tree _U_, proto_tree *tree, uint16_t offset, packet_info *pinfo _U_) { /*Add the ad*/ proto_tree_add_item(tree, hf_pdc_admpdu_admpdunr, tvb, offset, 4, ENC_BIG_ENDIAN); return 2; } #endif #if (PDC_VERSION == 2) static int dissect_dtmpdu(tvbuff_t *tvb, proto_tree *parent_tree, proto_tree *tree, uint16_t offset, packet_info *pinfo) { uint16_t userDataLen; uint16_t returnLen; tvbuff_t *asterixTVB; proto_tree_add_item(tree, hf_pdc_dtmpdu_user_size, tvb, offset, 2, ENC_BIG_ENDIAN); /* length of user data field */ userDataLen = tvb_get_ntohs(tvb, 2); returnLen = userDataLen + 2; asterixTVB = tvb_new_subset_length(tvb, offset + 2, userDataLen); if (asterix_handle != NULL) call_dissector(asterix_handle, asterixTVB, pinfo, parent_tree); return (returnLen); } #else static int dissect_dtmpdu(tvbuff_t *tvb _U_, proto_tree *parent_tree _U_, proto_tree *tree _U_, uint16_t offset _U_, packet_info *pinfo _U_) { return 2; } #endif #if (PDC_VERSION == 2) static int dissect_edmpdu(tvbuff_t *tvb, proto_tree *parent_tree, proto_tree *tree, uint16_t offset, packet_info *pinfo) { uint16_t userDataLen; uint16_t returnLen; tvbuff_t *asterixTVB; proto_tree_add_item(tree, hf_pdc_dtmpdu_user_size, tvb, offset, 2, ENC_BIG_ENDIAN); /* length of user data field */ userDataLen = tvb_get_ntohs(tvb, 2); returnLen = userDataLen + 2; asterixTVB = tvb_new_subset_length(tvb, offset + 2, userDataLen); if (asterix_handle != NULL) call_dissector(asterix_handle, asterixTVB, pinfo, parent_tree); return (returnLen); } #else static int dissect_edmpdu(tvbuff_t *tvb _U_, proto_tree *parent_tree _U_, proto_tree *tree _U_, uint16_t offset _U_, packet_info *pinfo _U_) { return 2; } #endif static int dissect_akmpdu(tvbuff_t *tvb, proto_tree *tree, uint16_t offset) { proto_tree_add_item(tree, hf_pdc_akmpdu_mns, tvb, offset, 2, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_pdc_akmpdu_cdt, tvb, offset, 2, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_pdc_yr_admu_nr, tvb, offset + 2, 4, ENC_BIG_ENDIAN); return (6); } static int dissect_pdc_packet(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo) { unsigned i = 0; uint8_t len_indicator; uint8_t mpduCode; uint16_t length; proto_item *pdcPacketItem; proto_tree *pdcPacketTree; length = 0; /*Get the length indictor and the MPDU Code*/ len_indicator = tvb_get_uint8 (tvb, i); mpduCode = tvb_get_uint8 (tvb, i + 1); length += 2; /*Add the PDC Tree*/ pdcPacketItem = proto_tree_add_item (tree, proto_pdc, tvb, i, len_indicator + 1, ENC_NA); pdcPacketTree = proto_item_add_subtree (pdcPacketItem, ett_pdc); /*Add the Length and packet type*/ proto_tree_add_item(pdcPacketTree, hf_pdc_len, tvb, i, 1, ENC_BIG_ENDIAN); proto_tree_add_item(pdcPacketTree, hf_pdc_mpdu_code, tvb, i + 1, 1, ENC_BIG_ENDIAN); /*Call the correct dissecting function*/ switch (mpduCode) { case SIMPDU: length += dissect_simpdu(tvb, pdcPacketTree, length, len_indicator); col_set_str(pinfo->cinfo, COL_INFO, "SIMPDU"); break; case RSMPDU: length += dissect_rsmpdu(); col_set_str(pinfo->cinfo, COL_INFO, "RSMPDU"); break; case DRMPDU: length += dissect_drmpdu(tvb, pdcPacketTree, length); col_set_str(pinfo->cinfo, COL_INFO, "DRMPDU"); break; case DTMPDU: length += dissect_dtmpdu(tvb, tree, pdcPacketTree, length, pinfo); col_set_str(pinfo->cinfo, COL_INFO, "DTMPDU"); break; case ADMPDU: length += dissect_admpdu(tvb, tree, pdcPacketTree, length, pinfo); col_set_str(pinfo->cinfo, COL_INFO, "ADMPDU"); break; case EDMPDU: length += dissect_edmpdu(tvb, tree, pdcPacketTree, length, pinfo); col_set_str(pinfo->cinfo, COL_INFO, "EDMPDU"); break; case AKMPDU: length += dissect_akmpdu(tvb, pdcPacketTree, length); col_set_str(pinfo->cinfo, COL_INFO, "AKMPDU"); break; default: break; }; return (length); /* XXX: returned length ignored by caller: Remove keeping track of data processed ? */ /* col_set_str() could then be done separately with 'if (tree)' around the dissection */ } /* Actual dissector bits and bytes done here */ static int dissect_pdc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) { /*Set the Column*/ col_set_str(pinfo->cinfo, COL_PROTOCOL, PDC_PROCOTOL ); col_clear(pinfo->cinfo, COL_INFO); dissect_pdc_packet(tvb, tree, pinfo); return tvb_reported_length(tvb); } /* function to provide TCP split packet combiner with size of packet */ static unsigned get_pdc_message_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void *data _U_) { unsigned size; unsigned extra; uint8_t mpdu_type; mpdu_type = tvb_get_uint8(tvb, offset+1); switch (mpdu_type) { case SIMPDU: size = tvb_get_uint8(tvb, offset); extra = 1; break; case RSMPDU: size = tvb_get_uint8(tvb, offset); extra = 1; break; case DRMPDU: size = tvb_get_uint8(tvb, offset); extra = 1; break; case DTMPDU: size = tvb_get_ntohs(tvb, offset+2); extra = 4; break; case ADMPDU: size = tvb_get_ntohs(tvb, offset+6); extra = 8; break; case EDMPDU: size = tvb_get_uint8(tvb, offset); extra = 1; break; case AKMPDU: size = tvb_get_uint8(tvb, offset)+1; extra = 0; break; default: size = 0; extra = 0; } return size + extra; } /* top level call to recombine split tcp packets */ static int tcp_dissect_pdc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) { uint8_t mpdu_type; uint8_t minimum_bytes; mpdu_type = tvb_get_uint8(tvb,1); switch (mpdu_type) { case SIMPDU: minimum_bytes = 2; break; case RSMPDU: minimum_bytes = 2; break; case DRMPDU: minimum_bytes = 2; break; case DTMPDU: minimum_bytes = 4; break; case ADMPDU: minimum_bytes = 8; break; case EDMPDU: minimum_bytes = 2; break; case AKMPDU: minimum_bytes = 2; break; default: minimum_bytes = 2; break; } tcp_dissect_pdus(tvb, pinfo, tree, true, minimum_bytes, get_pdc_message_len, dissect_pdc, NULL); return tvb_captured_length(tvb); } void proto_register_pdc(void) { static hf_register_info hf[] = { { &hf_pdc_len, { "Length Indicator", "pdc.li", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_pdc_mpdu_code, { "MPDU code", "pdc.mpducode", FT_UINT8, BASE_DEC, VALS(valstr_mpdus), 0x0, NULL, HFILL }}, { &hf_pdc_credit, { "Credit", "pdc.cdt", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_pdc_yr_admu_nr, { "YR-ADMU-NR", "pdc.yradmunr", FT_UINT32, BASE_DEC, NULL, 0x0, NULL , HFILL }}, { &hf_pdc_simpdu_state, { "State", "pdc.state", FT_UINT8, BASE_DEC, VALS(valstr_simpdu_state), 0x0, NULL, HFILL }}, { &hf_pdc_akmpdu_mns, { "MNS", "pdc.akmpdu.mns", FT_UINT16, BASE_DEC, NULL, 0x8000, NULL, HFILL }}, { &hf_pdc_akmpdu_cdt, { "CDT", "pdc.akmpdu.cdt", FT_UINT16, BASE_DEC, NULL, 0x07FF, NULL, HFILL }}, { &hf_pdc_simpdu_var, { "Variable Part", "pdc.simpdu.variable", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_pdc_simpdu_param, { "Parameter", "pdc.simpdu.param", FT_UINT8, BASE_DEC, VALS(valstr_simpdu_param), 0x0, NULL, HFILL }}, { &hf_pdc_simpdu_var_len, { "Length", "pdc.simpdu.variable.length", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_pdc_simpdu_var_version, { "PDC Version Number", "pdc.simpdu.variable.version", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_pdc_simpdu_var_REFSRC, { "Reference Source", "pdc.simpdu.variable.refsrc", FT_UINT32, BASE_DEC, NULL, 0x0,NULL, HFILL }}, { &hf_pdc_simpdu_var_REFDEST, { "Reference Destination", "pdc.simpdu.variable.refdst", FT_UINT32, BASE_DEC, NULL, 0x0,NULL, HFILL }}, { &hf_pdc_simpdu_var_TSEL, { "Transport Selector", "pdc.simpdu.tsel", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_pdc_drmpdu_abort, { "Abort", "pdc.drmpdu.abort", FT_UINT8, BASE_DEC, VALS(valstr_drmpdu_abort), 0x80, NULL, HFILL }}, { &hf_pdc_drmpdu_reason, { "Reason", "pdc.drmpdu.reason", FT_UINT8, BASE_DEC, VALS(valstr_drmpdu_reason), 0x0, NULL, HFILL }}, { &hf_pdc_drmpdu_mode, { "Mode", "pdc.drmpdu.mode", FT_UINT8, BASE_DEC, VALS(valstr_drmpdu_mode), 0x70, NULL, HFILL }}, { &hf_pdc_drmpdu_init, { "Reason", "pdc.drmpdu.init", FT_UINT8, BASE_DEC, VALS(valstr_drmpdu_initatior), 0x0F, NULL, HFILL }}, { &hf_pdc_dtmpdu_user_size, { "User Data Length", "pdc.dtmpdu.usersize", FT_UINT16, BASE_DEC, NULL, 0x0, NULL , HFILL }}, { &hf_pdc_admpdu_admpdunr, { "AD-MPDU-NR", "pdc.admpdu.admpdunr", FT_UINT32, BASE_DEC, NULL, 0x0, NULL , HFILL }}, { &hf_pdc_admpdu_size, { "User Data Size", "pdc.admpdu.usersize", FT_UINT16, BASE_DEC, NULL, 0x0, NULL , HFILL }}, }; /* Setup protocol subtree array */ static int *ett[] = { &ett_pdc, &ett_pdc_simpdu_var }; proto_pdc = proto_register_protocol ( "PDC Protocol", /* name */ "PDC", /* short name */ "pdc" /* abbrev */ ); /*Required Function Calls to register the header fields and subtrees used*/ proto_register_field_array(proto_pdc, hf, array_length(hf)); proto_register_subtree_array(ett, array_length(ett)); /* Register our dissector handle */ pdc_tcp_handle = register_dissector("pdc", tcp_dissect_pdc, proto_pdc); } /* Function to add pdc dissector to tcp.port dissector table and to get handle for asterix dissector */ void proto_reg_handoff_pdc(void) { asterix_handle = find_dissector_add_dependency("asterix", proto_pdc); dissector_add_for_decode_as_with_preference("tcp.port", pdc_tcp_handle); } /* * Editor modelines - https://www.wireshark.org/tools/modelines.html * * Local variables: * c-basic-offset: 8 * tab-width: 8 * indent-tabs-mode: t * End: * * vi: set shiftwidth=8 tabstop=8 noexpandtab: * :indentSize=8:tabSize=8:noTabs=false: */