/* packet-e2ap.c * Routines for E2APApplication Protocol (e2ap) packet dissection * Copyright 2021, Martin Mathieson * * Wireshark - Network traffic analyzer * By Gerald Combs * Copyright 1998 Gerald Combs * * SPDX-License-Identifier: GPL-2.0-or-later * * References: ORAN-WG3.E2AP-v03.00, ORAN-WG3.E2SM-KPM-v03.00, ORAN-WG3.E2SM-RC.03.00 */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include "packet-e2ap.h" #include "packet-per.h" #include "packet-ntp.h" #define PNAME "E2 Application Protocol" #define PSNAME "E2AP" #define PFNAME "e2ap" /* Dissector will use SCTP PPID 70, 71 or 72 or SCTP port 37464. */ #define SCTP_PORT_E2AP 37464 void proto_register_e2ap(void); void proto_reg_handoff_e2ap(void); static dissector_handle_t e2ap_handle; #include "packet-e2ap-val.h" /* Initialize the protocol and registered fields */ static int proto_e2ap = -1; #include "packet-e2ap-hf.c" static int hf_e2ap_unmapped_ran_function_id = -1; static int hf_e2ap_ran_function_name_not_recognised = -1; static int hf_e2ap_ran_function_setup_frame = -1; static int hf_e2ap_dissector_version= -1; static int hf_e2ap_frame_version = -1; static int hf_e2ap_timestamp_string = -1; /* Initialize the subtree pointers */ static gint ett_e2ap = -1; static expert_field ei_e2ap_ran_function_names_no_match = EI_INIT; static expert_field ei_e2ap_ran_function_id_not_mapped = EI_INIT; static expert_field ei_e2ap_ran_function_dissector_mismatch = EI_INIT; static expert_field ei_e2ap_ran_function_max_dissectors_registered = EI_INIT; #include "packet-e2ap-ett.c" /* Forward declarations */ static int dissect_e2ap_RANfunction_Name(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_); static int dissect_E2SM_KPM_EventTriggerDefinition_PDU(tvbuff_t *tvb _U_, packet_info *pinfo _U_, proto_tree *tree _U_, void *data _U_); static int dissect_E2SM_KPM_ActionDefinition_PDU(tvbuff_t *tvb _U_, packet_info *pinfo _U_, proto_tree *tree _U_, void *data _U_); static int dissect_E2SM_KPM_IndicationHeader_PDU(tvbuff_t *tvb _U_, packet_info *pinfo _U_, proto_tree *tree _U_, void *data _U_); static int dissect_E2SM_KPM_IndicationMessage_PDU(tvbuff_t *tvb _U_, packet_info *pinfo _U_, proto_tree *tree _U_, void *data _U_); static int dissect_E2SM_KPM_RANfunction_Description_PDU(tvbuff_t *tvb _U_, packet_info *pinfo _U_, proto_tree *tree _U_, void *data _U_); static int dissect_E2SM_RC_EventTrigger_PDU(tvbuff_t *tvb _U_, packet_info *pinfo _U_, proto_tree *tree _U_, void *data _U_); static int dissect_E2SM_RC_ActionDefinition_PDU(tvbuff_t *tvb _U_, packet_info *pinfo _U_, proto_tree *tree _U_, void *data _U_); static int dissect_E2SM_RC_RANFunctionDefinition_PDU(tvbuff_t *tvb _U_, packet_info *pinfo _U_, proto_tree *tree _U_, void *data _U_); static int dissect_E2SM_RC_IndicationMessage_PDU(tvbuff_t *tvb _U_, packet_info *pinfo _U_, proto_tree *tree _U_, void *data _U_); static int dissect_E2SM_RC_IndicationHeader_PDU(tvbuff_t *tvb _U_, packet_info *pinfo _U_, proto_tree *tree _U_, void *data _U_); static int dissect_E2SM_RC_CallProcessID_PDU(tvbuff_t *tvb _U_, packet_info *pinfo _U_, proto_tree *tree _U_, void *data _U_); static int dissect_E2SM_RC_ControlHeader_PDU(tvbuff_t *tvb _U_, packet_info *pinfo _U_, proto_tree *tree _U_, void *data _U_); static int dissect_E2SM_RC_ControlMessage_PDU(tvbuff_t *tvb _U_, packet_info *pinfo _U_, proto_tree *tree _U_, void *data _U_); static int dissect_E2SM_RC_ControlOutcome_PDU(tvbuff_t *tvb _U_, packet_info *pinfo _U_, proto_tree *tree _U_, void *data _U_); //static int dissect_E2SM_RC_QueryOutcome_PDU(tvbuff_t *tvb _U_, packet_info *pinfo _U_, proto_tree *tree _U_, void *data _U_); //static int dissect_E2SM_RC_QueryDefinition_PDU(tvbuff_t *tvb _U_, packet_info *pinfo _U_, proto_tree *tree _U_, void *data _U_); //static int dissect_E2SM_RC_QueryHeader_PDU(tvbuff_t *tvb _U_, packet_info *pinfo _U_, proto_tree *tree _U_, void *data _U_); static int dissect_E2SM_NI_EventTriggerDefinition_PDU(tvbuff_t *tvb _U_, packet_info *pinfo _U_, proto_tree *tree _U_, void *data _U_); static int dissect_E2SM_NI_ActionDefinition_PDU(tvbuff_t *tvb _U_, packet_info *pinfo _U_, proto_tree *tree _U_, void *data _U_); static int dissect_E2SM_NI_RANfunction_Description_PDU(tvbuff_t *tvb _U_, packet_info *pinfo _U_, proto_tree *tree _U_, void *data _U_); static int dissect_E2SM_NI_IndicationMessage_PDU(tvbuff_t *tvb _U_, packet_info *pinfo _U_, proto_tree *tree _U_, void *data _U_); static int dissect_E2SM_NI_IndicationHeader_PDU(tvbuff_t *tvb _U_, packet_info *pinfo _U_, proto_tree *tree _U_, void *data _U_); static int dissect_E2SM_NI_CallProcessID_PDU(tvbuff_t *tvb _U_, packet_info *pinfo _U_, proto_tree *tree _U_, void *data _U_); static int dissect_E2SM_NI_ControlHeader_PDU(tvbuff_t *tvb _U_, packet_info *pinfo _U_, proto_tree *tree _U_, void *data _U_); static int dissect_E2SM_NI_ControlMessage_PDU(tvbuff_t *tvb _U_, packet_info *pinfo _U_, proto_tree *tree _U_, void *data _U_); static int dissect_E2SM_NI_ControlOutcome_PDU(tvbuff_t *tvb _U_, packet_info *pinfo _U_, proto_tree *tree _U_, void *data _U_); enum { INITIATING_MESSAGE, SUCCESSFUL_OUTCOME, UNSUCCESSFUL_OUTCOME }; typedef struct _e2ap_ctx_t { guint32 message_type; guint32 ProcedureCode; guint32 ProtocolIE_ID; guint32 ProtocolExtensionID; } e2ap_ctx_t; /* Temporary private info to remember while dissecting frame */ struct e2ap_private_data { guint32 procedure_code; guint32 protocol_ie_id; guint32 protocol_extension_id; guint32 message_type; guint32 ran_ue_e2ap_id; guint32 ran_function_id; guint32 gnb_id_len; #define MAX_GNB_ID_BYTES 6 guint8 gnb_id_bytes[MAX_GNB_ID_BYTES]; dissector_handle_t component_configuration_dissector; }; /* Lookup temporary private info */ static struct e2ap_private_data* e2ap_get_private_data(packet_info *pinfo) { struct e2ap_private_data *e2ap_data = (struct e2ap_private_data*)p_get_proto_data(pinfo->pool, pinfo, proto_e2ap, 0); if (!e2ap_data) { e2ap_data = wmem_new0(pinfo->pool, struct e2ap_private_data); p_add_proto_data(pinfo->pool, pinfo, proto_e2ap, 0, e2ap_data); } return e2ap_data; } /****************************************************************************************************************/ /* We learn which set of RAN functions pointers corresponds to a given ranFunctionID when we see E2SetupRequest */ /* TODO: unfortunately, it seems that different versions of these protocols are not backward-compatible, so */ /* it would be good to show where (going by OID) the dissector isn't at the same version as the message.. */ /* An alternative would be to have multiple versions of each protocol and have them register in tables... */ /* Static table mapping from string -> ran_function */ static const char* g_ran_function_name_table[MAX_RANFUNCTIONS] = { "ORAN-E2SM-KPM", "ORAN-E2SM-RC", "ORAN-E2SM-NI" }; /* Per-conversation mapping: ranFunctionId -> ran_function+dissector */ typedef struct { guint32 setup_frame; guint32 ran_function_id; ran_function_t ran_function; char oid[MAX_OID_LEN]; // i.e., OID from setupRequest ran_function_dissector_t *dissector; } ran_function_id_mapping_t; typedef struct { #define MAX_RANFUNCTION_ENTRIES 8 guint32 num_entries; ran_function_id_mapping_t entries[MAX_RANFUNCTION_ENTRIES]; } ran_functionid_table_t; static const char *ran_function_to_str(ran_function_t ran_function) { switch (ran_function) { case KPM_RANFUNCTIONS: return "KPM"; case RC_RANFUNCTIONS: return "RC"; case NI_RANFUNCTIONS: return "NI"; default: return "Unknown"; } } /* Table of RAN Function tables, indexed by gnbId (bytes) */ typedef struct { #define MAX_GNBS 6 guint32 num_gnbs; struct { guint8 id_value[MAX_GNB_ID_BYTES]; guint32 id_len; ran_functionid_table_t *ran_function_table; } gnb[MAX_GNBS]; } gnb_ran_functions_t; static gnb_ran_functions_t s_gnb_ran_functions_table; /* Table of available dissectors for each RAN function */ typedef struct { guint32 num_available_dissectors; #define MAX_DISSECTORS_PER_RAN_FUNCTION 3 ran_function_dissector_t* ran_function_dissectors[MAX_DISSECTORS_PER_RAN_FUNCTION]; } ran_function_available_dissectors_t; /* Available dissectors should be set here */ static ran_function_available_dissectors_t g_ran_functions_available_dissectors[MAX_RANFUNCTIONS]; /* Will be called from outside this file by separate dissectors */ void register_e2ap_ran_function_dissector(ran_function_t ran_function, ran_function_dissector_t *dissector) { if ((ran_function >= MIN_RANFUNCTIONS) && (ran_function <= MAX_RANFUNCTIONS)) { ran_function_available_dissectors_t *available_dissectors = &g_ran_functions_available_dissectors[ran_function]; if (available_dissectors->num_available_dissectors < MAX_DISSECTORS_PER_RAN_FUNCTION) { available_dissectors->ran_function_dissectors[available_dissectors->num_available_dissectors++] = dissector; } } } /* Get RANfunctionID table from conversation data - create new if necessary */ static ran_functionid_table_t* get_ran_functionid_table(packet_info *pinfo) { conversation_t *p_conv; ran_functionid_table_t *p_conv_data = NULL; /* Lookup conversation */ p_conv = find_conversation(pinfo->num, &pinfo->net_dst, &pinfo->net_src, conversation_pt_to_endpoint_type(pinfo->ptype), pinfo->destport, pinfo->srcport, 0); if (!p_conv) { /* None, so create new data and set */ p_conv = conversation_new(pinfo->num, &pinfo->net_dst, &pinfo->net_src, conversation_pt_to_endpoint_type(pinfo->ptype), pinfo->destport, pinfo->srcport, 0); p_conv_data = (ran_functionid_table_t*)wmem_new0(wmem_file_scope(), ran_functionid_table_t); conversation_add_proto_data(p_conv, proto_e2ap, p_conv_data); } else { /* Will return existing conversation data */ p_conv_data = (ran_functionid_table_t*)conversation_get_proto_data(p_conv, proto_e2ap); } return p_conv_data; } /* Store new RANfunctionID -> Service Model mapping in table */ void e2ap_store_ran_function_mapping(packet_info *pinfo, proto_tree *tree, tvbuff_t *tvb, const char *name) { struct e2ap_private_data *e2ap_data = e2ap_get_private_data(pinfo); ran_functionid_table_t *table = get_ran_functionid_table(pinfo); if (!name) { return; } /* Stop if already reached table limit */ if (table->num_entries == MAX_RANFUNCTION_ENTRIES) { proto_tree_add_expert_format(tree, pinfo, &ei_e2ap_ran_function_max_dissectors_registered, tvb, 0, 0, "Dissector wants to register for %s, but max (%u) already reached", name, MAX_RANFUNCTION_ENTRIES); return; } guint32 ran_function_id = e2ap_data->ran_function_id; ran_function_t ran_function = MAX_RANFUNCTIONS; /* i.e. invalid */ ran_function_dissector_t *ran_function_dissector = NULL; /* Check known RAN function names */ for (int n=MIN_RANFUNCTIONS; n < MAX_RANFUNCTIONS; n++) { if (strcmp(name, g_ran_function_name_table[n]) == 0) { ran_function = n; /* Don't know OID yet, so for now, just choose first/only one */ /* TODO: is latest one likely to be more compatible? First fields (at least) come from E2SM.. */ if (g_ran_functions_available_dissectors[table->entries[n].ran_function].num_available_dissectors) { ran_function_dissector = g_ran_functions_available_dissectors[table->entries[n].ran_function].ran_function_dissectors[0]; } break; } } /* Nothing to do if no matches */ if (ran_function == MAX_RANFUNCTIONS) { return; } /* If ID already mapped, can stop here */ for (guint n=0; n < table->num_entries; n++) { if (table->entries[n].ran_function_id == ran_function_id) { return; } } /* OK, store this new entry */ guint idx = table->num_entries++; table->entries[idx].setup_frame = pinfo->num; table->entries[idx].ran_function_id = ran_function_id; table->entries[idx].ran_function = ran_function; table->entries[idx].dissector = ran_function_dissector; /* When add first entry, also want to set up table from gnbId -> table */ if (idx == 0) { guint id_len = e2ap_data->gnb_id_len; guint8 *id_value = &e2ap_data->gnb_id_bytes[0]; gboolean found = FALSE; for (guint n=0; nran_function_id; /* Get ranFunction table corresponding to this frame's conversation */ ran_functionid_table_t *table = get_ran_functionid_table(pinfo); if (!table) { /* There is no ran function table associated with this frame's conversation info */ return NULL; } /* Find the entry in this table corresponding to ran_function_id */ for (guint n=0; n < table->num_entries; n++) { if (ran_function_id == table->entries[n].ran_function_id) { if (tree) { /* Point back at the setup frame where this ranfunction was mapped */ proto_item *ti = proto_tree_add_uint(tree, hf_e2ap_ran_function_setup_frame, tvb, 0, 0, table->entries[n].setup_frame); /* Show that mapping */ proto_item_append_text(ti, " (%u -> %s)", table->entries[n].ran_function_id, ran_function_to_str(table->entries[n].ran_function)); proto_item_set_generated(ti); /* Also take the chance to compare signalled and available dissector */ char *frame_version = oid_resolved_from_string(pinfo->pool, table->entries[n].oid); ti = proto_tree_add_string(tree, hf_e2ap_frame_version, tvb, 0, 0, frame_version); proto_item_set_generated(ti); char *dissector_version = oid_resolved_from_string(pinfo->pool, table->entries[n].dissector->oid); ti = proto_tree_add_string(tree, hf_e2ap_dissector_version, tvb, 0, 0, dissector_version); proto_item_set_generated(ti); if (strcmp(frame_version, dissector_version) != 0) { /* Expert info for version mismatch! */ expert_add_info_format(pinfo, ti, &ei_e2ap_ran_function_dissector_mismatch, "Dissector version mismatch - frame is %s but dissector is %s", frame_version, dissector_version); } } /* Return the dissector */ return table->entries[n].dissector; } } if (tree) { /* No match found.. */ proto_item *ti = proto_tree_add_item(tree, hf_e2ap_unmapped_ran_function_id, tvb, 0, 0, ENC_NA); expert_add_info_format(pinfo, ti, &ei_e2ap_ran_function_id_not_mapped, "Service Model not mapped for FunctionID %u", ran_function_id); } return NULL; } /* Return the oid associated with this frame's conversation */ static char* lookup_ranfunction_oid(packet_info *pinfo) { /* Get ranFunctionID from this frame */ struct e2ap_private_data *e2ap_data = e2ap_get_private_data(pinfo); guint ran_function_id = e2ap_data->ran_function_id; /* Get ranFunction table corresponding to this frame's conversation */ ran_functionid_table_t *table = get_ran_functionid_table(pinfo); if (!table) { /* There is no ran function table associated with this frame's conversation info */ return NULL; } /* Find the entry in this table corresponding to ran_function_id */ for (guint n=0; n < table->num_entries; n++) { if (ran_function_id == table->entries[n].ran_function_id) { return (char*)(table->entries[n].oid); } } /* Not found */ return NULL; } /* We now know the OID - can we set a dissector that is an exact match from what has been signalled? */ static void update_dissector_using_oid(packet_info *pinfo, ran_function_t ran_function) { char *frame_oid = lookup_ranfunction_oid(pinfo); if (frame_oid == NULL) { /* TODO: error? */ return; } gboolean found = FALSE; /* Look at available dissectors for this RAN function */ ran_function_available_dissectors_t *available = &g_ran_functions_available_dissectors[ran_function]; if (!available->num_available_dissectors) { /* Oops - none available at all! */ return; } // Get mapping in use struct e2ap_private_data *e2ap_data = e2ap_get_private_data(pinfo); guint ran_function_id = e2ap_data->ran_function_id; ran_function_id_mapping_t *mapping = NULL; ran_functionid_table_t *table = get_ran_functionid_table(pinfo); /* Find the entry in this table corresponding to ran_function_id */ for (guint n=0; n < table->num_entries; n++) { if (ran_function_id == table->entries[n].ran_function_id) { mapping = &(table->entries[n]); } } if (!mapping) { return; } /* Set dissector pointer in ran_function_id_mapping_t */ for (guint32 n=0; n < available->num_available_dissectors; n++) { /* If exact match, set it */ if (strcmp(frame_oid, available->ran_function_dissectors[n]->oid) == 0) { mapping->dissector = available->ran_function_dissectors[n]; found = TRUE; break; } } /* If not exact match, just set to first one available (TODO: closest above better?) */ if (!found) { mapping->dissector = available->ran_function_dissectors[0]; } } /* Update RANfunctionID -> Service Model mapping in table (now that we know OID) */ void e2ap_update_ran_function_mapping(packet_info *pinfo, proto_tree *tree, tvbuff_t *tvb, const char *oid) { /* Copy OID into table entry (so may be used to choose and be compared with chosen available dissector */ struct e2ap_private_data *e2ap_data = e2ap_get_private_data(pinfo); ran_functionid_table_t *table = get_ran_functionid_table(pinfo); ran_function_t ran_function = MAX_RANFUNCTIONS; for (guint n=0; n < table->num_entries; n++) { if (e2ap_data->ran_function_id == table->entries[n].ran_function_id) { ran_function = table->entries[n].ran_function; g_strlcpy(table->entries[n].oid, oid, MAX_OID_LEN); } } /* Look up version from oid and show as generated field */ char *version = oid_resolved_from_string(pinfo->pool, oid); proto_item *ti = proto_tree_add_string(tree, hf_e2ap_frame_version, tvb, 0, 0, version); proto_item_set_generated(ti); /* Can now pick most appropriate dissector for this RAN Function name, based upon this OID and the available dissectors */ if (ran_function < MAX_RANFUNCTIONS) { if (pinfo->fd->visited) { update_dissector_using_oid(pinfo, ran_function); } } } /* This will get used for E2nodeConfigurationUpdate, where we have a gnb-id but haven't seen E2setupRequest */ static void update_conversation_from_gnb_id(asn1_ctx_t *actx) { packet_info *pinfo = actx->pinfo; struct e2ap_private_data *e2ap_data = e2ap_get_private_data(pinfo); /* Look for conversation data */ conversation_t *p_conv; ran_functionid_table_t *p_conv_data = NULL; /* Lookup conversation */ p_conv = find_conversation(pinfo->num, &pinfo->net_dst, &pinfo->net_src, conversation_pt_to_endpoint_type(pinfo->ptype), pinfo->destport, pinfo->srcport, 0); if (!p_conv) { /* None, so create new data and set */ p_conv = conversation_new(pinfo->num, &pinfo->net_dst, &pinfo->net_src, conversation_pt_to_endpoint_type(pinfo->ptype), pinfo->destport, pinfo->srcport, 0); p_conv_data = (ran_functionid_table_t*)wmem_new0(wmem_file_scope(), ran_functionid_table_t); conversation_add_proto_data(p_conv, proto_e2ap, p_conv_data); /* Look to see if we already know about the mappings in effect on this gNB */ guint id_len = e2ap_data->gnb_id_len; guint8 *id_value = &e2ap_data->gnb_id_bytes[0]; for (guint n=0; nmessage_type; e2ap_ctx.ProcedureCode = e2ap_data->procedure_code; e2ap_ctx.ProtocolIE_ID = e2ap_data->protocol_ie_id; e2ap_ctx.ProtocolExtensionID = e2ap_data->protocol_extension_id; return (dissector_try_uint_new(e2ap_ies_dissector_table, e2ap_data->protocol_ie_id, tvb, pinfo, tree, FALSE, &e2ap_ctx)) ? tvb_captured_length(tvb) : 0; } /* Currently not used static int dissect_ProtocolIEFieldPairFirstValue(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) { struct e2ap_private_data *e2ap_data = e2ap_get_private_data(pinfo); return (dissector_try_uint(e2ap_ies_p1_dissector_table, e2ap_data->protocol_ie_id, tvb, pinfo, tree)) ? tvb_captured_length(tvb) : 0; } static int dissect_ProtocolIEFieldPairSecondValue(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) { struct e2ap_private_data *e2ap_data = e2ap_get_private_data(pinfo); return (dissector_try_uint(e2ap_ies_p2_dissector_table, e2ap_data->protocol_ie_id, tvb, pinfo, tree)) ? tvb_captured_length(tvb) : 0; } */ static int dissect_InitiatingMessageValue(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) { struct e2ap_private_data *e2ap_data = e2ap_get_private_data(pinfo); return (dissector_try_uint_new(e2ap_proc_imsg_dissector_table, e2ap_data->procedure_code, tvb, pinfo, tree, TRUE, data)) ? tvb_captured_length(tvb) : 0; } static int dissect_SuccessfulOutcomeValue(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) { struct e2ap_private_data *e2ap_data = e2ap_get_private_data(pinfo); return (dissector_try_uint_new(e2ap_proc_sout_dissector_table, e2ap_data->procedure_code, tvb, pinfo, tree, TRUE, data)) ? tvb_captured_length(tvb) : 0; } static int dissect_UnsuccessfulOutcomeValue(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) { struct e2ap_private_data *e2ap_data = e2ap_get_private_data(pinfo); return (dissector_try_uint_new(e2ap_proc_uout_dissector_table, e2ap_data->procedure_code, tvb, pinfo, tree, TRUE, data)) ? tvb_captured_length(tvb) : 0; } static int dissect_e2ap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) { proto_item *e2ap_item = NULL; proto_tree *e2ap_tree = NULL; /* make entry in the Protocol column on summary display */ col_set_str(pinfo->cinfo, COL_PROTOCOL, "E2AP"); col_clear(pinfo->cinfo, COL_INFO); /* create the e2ap protocol tree */ e2ap_item = proto_tree_add_item(tree, proto_e2ap, tvb, 0, -1, ENC_NA); e2ap_tree = proto_item_add_subtree(e2ap_item, ett_e2ap); return dissect_E2AP_PDU_PDU(tvb, pinfo, e2ap_tree, NULL); } static void e2ap_init_protocol(void) { s_gnb_ran_functions_table.num_gnbs = 0; } /*--- proto_reg_handoff_e2ap ---------------------------------------*/ void proto_reg_handoff_e2ap(void) { dissector_add_uint_with_preference("sctp.port", SCTP_PORT_E2AP, e2ap_handle); dissector_add_uint("sctp.ppi", E2_CP_PROTOCOL_ID, e2ap_handle); dissector_add_uint("sctp.ppi", E2_UP_PROTOCOL_ID, e2ap_handle); dissector_add_uint("sctp.ppi", E2_DU_PROTOCOL_ID, e2ap_handle); #include "packet-e2ap-dis-tab.c" /********************************/ /* Known OIDs for RAN providers */ /* KPM */ oid_add_from_string("KPM v1", "1.3.6.1.4.1.53148.1.1.2.2"); oid_add_from_string("KPM v2", "1.3.6.1.4.1.53148.1.2.2.2"); oid_add_from_string("KPM v3", "1.2.6.1.4.1.53148.1.3.2.2"); /* RC */ // TODO: appears to be the same??? Asking for clarification from ORAN.. oid_add_from_string("RC v1", "1.3.6.1.4.1.53148.1.1.2.3"); //oid_add_from_string("RC v3", "1.3.6.1.4.1.53148.1.1.2.3"); //oid_add_from_string("RC v4", "1.3.6.1.4.1.53148.1.1.2.3"); /* NI */ oid_add_from_string("NI v1", "1.3.6.1.4.1.53148.1.1.2.1"); /********************************/ /* Register 'built-in' dissectors */ static ran_function_dissector_t kpm_v3 = { "ORAN-E2SM-KPM", "1.2.6.1.4.1.53148.1.3.2.2", 3, 0, { dissect_E2SM_KPM_RANfunction_Description_PDU, NULL, NULL, NULL, NULL, NULL, NULL, dissect_E2SM_KPM_ActionDefinition_PDU, dissect_E2SM_KPM_IndicationMessage_PDU, dissect_E2SM_KPM_IndicationHeader_PDU, NULL, /* no dissect_E2SM_KPM_CallProcessID_PDU */ dissect_E2SM_KPM_EventTriggerDefinition_PDU } }; static ran_function_dissector_t rc_v1 = { "ORAN-E2SM-RC", "1.3.6.1.4.1.53148.1.1.2.3", 1, 3, { dissect_E2SM_RC_RANFunctionDefinition_PDU, dissect_E2SM_RC_ControlHeader_PDU, dissect_E2SM_RC_ControlMessage_PDU, dissect_E2SM_RC_ControlOutcome_PDU, /* new for v3 */ NULL, //dissect_E2SM_RC_QueryOutcome_PDU, NULL, //dissect_E2SM_RC_QueryDefinition_PDU, NULL, //dissect_E2SM_RC_QueryHeader_PDU, dissect_E2SM_RC_ActionDefinition_PDU, dissect_E2SM_RC_IndicationMessage_PDU, dissect_E2SM_RC_IndicationHeader_PDU, dissect_E2SM_RC_CallProcessID_PDU, dissect_E2SM_RC_EventTrigger_PDU } }; static ran_function_dissector_t ni_v1 = { "ORAN-E2SM-NI", "1.3.6.1.4.1.53148.1.1.2.1", 1, 0, { dissect_E2SM_NI_RANfunction_Description_PDU, dissect_E2SM_NI_ControlHeader_PDU, dissect_E2SM_NI_ControlMessage_PDU, dissect_E2SM_NI_ControlOutcome_PDU, NULL, NULL, NULL, dissect_E2SM_NI_ActionDefinition_PDU, dissect_E2SM_NI_IndicationMessage_PDU, dissect_E2SM_NI_IndicationHeader_PDU, dissect_E2SM_NI_CallProcessID_PDU, dissect_E2SM_NI_EventTriggerDefinition_PDU } }; /* Register available dissectors. TODO: break these out into separate * ASN.1 protocols that register themselves, or leave one of each here? */ register_e2ap_ran_function_dissector(KPM_RANFUNCTIONS, &kpm_v3); register_e2ap_ran_function_dissector(RC_RANFUNCTIONS, &rc_v1); register_e2ap_ran_function_dissector(NI_RANFUNCTIONS, &ni_v1); } /*--- proto_register_e2ap -------------------------------------------*/ void proto_register_e2ap(void) { /* List of fields */ static hf_register_info hf[] = { #include "packet-e2ap-hfarr.c" { &hf_e2ap_unmapped_ran_function_id, { "Unmapped RANfunctionID", "e2ap.unmapped-ran-function-id", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_e2ap_ran_function_name_not_recognised, { "RANfunction name not recognised", "e2ap.ran-function-name-not-recognised", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_e2ap_ran_function_setup_frame, { "RANfunction setup frame", "e2ap.setup-frame", FT_FRAMENUM, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_e2ap_dissector_version, { "Version (dissector)", "e2ap.version.dissector", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_e2ap_frame_version, { "Version (frame)", "e2ap.version.frame", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_e2ap_timestamp_string, { "Timestamp string", "e2ap.timestamp-string", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }}, }; /* List of subtrees */ static gint *ett[] = { &ett_e2ap, #include "packet-e2ap-ettarr.c" }; static ei_register_info ei[] = { { &ei_e2ap_ran_function_names_no_match, { "e2ap.ran-function-names-no-match", PI_PROTOCOL, PI_WARN, "RAN Function name doesn't match known service models", EXPFILL }}, { &ei_e2ap_ran_function_id_not_mapped, { "e2ap.ran-function-id-not-known", PI_PROTOCOL, PI_WARN, "Service Model not known for RANFunctionID", EXPFILL }}, { &ei_e2ap_ran_function_dissector_mismatch, { "e2ap.ran-function-dissector-version-mismatch", PI_PROTOCOL, PI_WARN, "Available dissector does not match signalled", EXPFILL }}, { &ei_e2ap_ran_function_max_dissectors_registered, { "e2ap.ran-function-max-dissectors-registered", PI_PROTOCOL, PI_WARN, "Available dissector does not match signalled", EXPFILL }}, }; expert_module_t* expert_e2ap; /* Register protocol */ proto_e2ap = proto_register_protocol(PNAME, PSNAME, PFNAME); /* Register fields and subtrees */ proto_register_field_array(proto_e2ap, hf, array_length(hf)); proto_register_subtree_array(ett, array_length(ett)); /* Register dissector */ e2ap_handle = register_dissector("e2ap", dissect_e2ap, proto_e2ap); expert_e2ap = expert_register_protocol(proto_e2ap); expert_register_field_array(expert_e2ap, ei, array_length(ei)); /* Register dissector tables */ e2ap_ies_dissector_table = register_dissector_table("e2ap.ies", "E2AP-PROTOCOL-IES", proto_e2ap, FT_UINT32, BASE_DEC); // e2ap_ies_p1_dissector_table = register_dissector_table("e2ap.ies.pair.first", "E2AP-PROTOCOL-IES-PAIR FirstValue", proto_e2ap, FT_UINT32, BASE_DEC); // e2ap_ies_p2_dissector_table = register_dissector_table("e2ap.ies.pair.second", "E2AP-PROTOCOL-IES-PAIR SecondValue", proto_e2ap, FT_UINT32, BASE_DEC); e2ap_extension_dissector_table = register_dissector_table("e2ap.extension", "E2AP-PROTOCOL-EXTENSION", proto_e2ap, FT_UINT32, BASE_DEC); e2ap_proc_imsg_dissector_table = register_dissector_table("e2ap.proc.imsg", "E2AP-ELEMENTARY-PROCEDURE InitiatingMessage", proto_e2ap, FT_UINT32, BASE_DEC); e2ap_proc_sout_dissector_table = register_dissector_table("e2ap.proc.sout", "E2AP-ELEMENTARY-PROCEDURE SuccessfulOutcome", proto_e2ap, FT_UINT32, BASE_DEC); e2ap_proc_uout_dissector_table = register_dissector_table("e2ap.proc.uout", "E2AP-ELEMENTARY-PROCEDURE UnsuccessfulOutcome", proto_e2ap, FT_UINT32, BASE_DEC); e2ap_n2_ie_type_dissector_table = register_dissector_table("e2ap.n2_ie_type", "E2AP N2 IE Type", proto_e2ap, FT_STRING, STRING_CASE_SENSITIVE); register_init_routine(&e2ap_init_protocol); } /* * Editor modelines * * Local Variables: * c-basic-offset: 2 * tab-width: 8 * indent-tabs-mode: nil * End: * * ex: set shiftwidth=2 tabstop=8 expandtab: * :indentSize=2:tabSize=8:noTabs=true: */