summaryrefslogtreecommitdiffstats
path: root/epan/dissectors/asn1/e2ap/packet-e2ap-template.c
diff options
context:
space:
mode:
Diffstat (limited to 'epan/dissectors/asn1/e2ap/packet-e2ap-template.c')
-rw-r--r--epan/dissectors/asn1/e2ap/packet-e2ap-template.c833
1 files changed, 833 insertions, 0 deletions
diff --git a/epan/dissectors/asn1/e2ap/packet-e2ap-template.c b/epan/dissectors/asn1/e2ap/packet-e2ap-template.c
new file mode 100644
index 00000000..709469f6
--- /dev/null
+++ b/epan/dissectors/asn1/e2ap/packet-e2ap-template.c
@@ -0,0 +1,833 @@
+/* packet-e2ap.c
+ * Routines for E2APApplication Protocol (e2ap) packet dissection
+ * Copyright 2021, Martin Mathieson
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * 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 <epan/packet.h>
+#include <epan/strutil.h>
+#include <epan/asn1.h>
+#include <epan/prefs.h>
+#include <epan/sctpppids.h>
+#include <epan/expert.h>
+#include <epan/proto_data.h>
+#include <epan/conversation.h>
+#include <epan/to_str.h>
+#include <epan/oids.h>
+
+#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; n<s_gnb_ran_functions_table.num_gnbs; n++) {
+ if ((s_gnb_ran_functions_table.gnb[n].id_len = id_len) &&
+ (memcmp(s_gnb_ran_functions_table.gnb[n].id_value, id_value, id_len) == 0)) {
+ /* Already have an entry for this gnb. */
+ found = TRUE;
+ break;
+ }
+ }
+
+ if (!found) {
+ /* Add entry (if room for 1 more) */
+ guint32 new_idx = s_gnb_ran_functions_table.num_gnbs;
+ if (new_idx < MAX_GNBS-1) {
+ s_gnb_ran_functions_table.gnb[new_idx].id_len = id_len;
+ memcpy(s_gnb_ran_functions_table.gnb[new_idx].id_value, id_value, id_len);
+ s_gnb_ran_functions_table.gnb[new_idx].ran_function_table = table;
+
+ s_gnb_ran_functions_table.num_gnbs++;
+ }
+ }
+ }
+}
+
+/* Look for Service Model function pointers, based on current RANFunctionID from frame */
+static ran_function_dissector_t* lookup_ranfunction_dissector(packet_info *pinfo, proto_tree *tree, tvbuff_t *tvb)
+{
+ /* 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) {
+ 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; n<s_gnb_ran_functions_table.num_gnbs; n++) {
+ if ((s_gnb_ran_functions_table.gnb[n].id_len = id_len) &&
+ (memcmp(s_gnb_ran_functions_table.gnb[n].id_value, id_value, id_len) == 0)) {
+
+ /* Have an entry for this gnb. Set direct pointer to existing data (used by original conversation). */
+ /* N.B. This means that no further updates for the gNB are expected on different conversations.. */
+ p_conv_data = s_gnb_ran_functions_table.gnb[n].ran_function_table;
+ conversation_add_proto_data(p_conv, proto_e2ap, p_conv_data);
+
+ /* TODO: may want to try to add a generated field to pass back to E2setupRequest where RAN function mappings were first seen? */
+ break;
+ }
+ }
+ }
+}
+
+
+/* Dissector tables */
+static dissector_table_t e2ap_ies_dissector_table;
+
+//static dissector_table_t e2ap_ies_p1_dissector_table;
+//static dissector_table_t e2ap_ies_p2_dissector_table;
+static dissector_table_t e2ap_extension_dissector_table;
+static dissector_table_t e2ap_proc_imsg_dissector_table;
+static dissector_table_t e2ap_proc_sout_dissector_table;
+static dissector_table_t e2ap_proc_uout_dissector_table;
+static dissector_table_t e2ap_n2_ie_type_dissector_table;
+
+static int dissect_ProtocolIEFieldValue(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *);
+
+/* Currently not used
+static int dissect_ProtocolIEFieldPairFirstValue(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
+static int dissect_ProtocolIEFieldPairSecondValue(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
+*/
+
+
+static int dissect_InitiatingMessageValue(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *);
+static int dissect_SuccessfulOutcomeValue(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *);
+static int dissect_UnsuccessfulOutcomeValue(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *);
+
+
+#include "packet-e2ap-fn.c"
+
+static int dissect_ProtocolIEFieldValue(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
+{
+ e2ap_ctx_t e2ap_ctx;
+ struct e2ap_private_data *e2ap_data = e2ap_get_private_data(pinfo);
+
+ e2ap_ctx.message_type = e2ap_data->message_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:
+ */