diff --git a/openair2/E2AP/RAN_FUNCTION/CMakeLists.txt b/openair2/E2AP/RAN_FUNCTION/CMakeLists.txt
index 93dfe6f8a13ec82300cf56c7da32ec2557fe0987..47f7ae41545aae31438e74c4a814a2437451003e 100644
--- a/openair2/E2AP/RAN_FUNCTION/CMakeLists.txt
+++ b/openair2/E2AP/RAN_FUNCTION/CMakeLists.txt
@@ -9,6 +9,7 @@ add_library(e2_ran_func_cuup STATIC
             O-RAN/ran_func_kpm.c    # this file should only contain PDCP-U/GTP; to be done in the future
             ../flexric/test/rnd/fill_rnd_data_kpm.c  # this dependancy will be taken out once RAN Function Definition is implemented
             O-RAN/ran_func_rc.c     # this file should only contain PDCP-U/GTP; to be done in the future
+            O-RAN/ran_func_rc_subs.c
             ../flexric/test/rnd/fill_rnd_data_rc.c   # this dependancy will be taken out once RAN Function Definition is implemented
             )
 
@@ -27,6 +28,7 @@ add_library(e2_ran_func_du_cucp_cuup STATIC
             ../flexric/test/rnd/fill_rnd_data_kpm.c  # this dependancy will be taken out once RAN Function Definition is implemented
             O-RAN/ran_func_rc.c     # this file should only contain RRC/PDCP-C; to be done in the future
                                     # when nr-softmodem is divided in separate executables
+            O-RAN/ran_func_rc_subs.c
             ../flexric/test/rnd/fill_rnd_data_rc.c   # this dependancy will be taken out once RAN Function Definition is implemented
             ../flexric/src/sm/rc_sm/ie/rc_data_ie.c
             CUSTOMIZED/ran_func_mac.c
diff --git a/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc.c b/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc.c
index 90f8d1ae0025587b75ec9f5d2a793841afab5a0e..7cf3e94f64c994c1d1eae2212cff3f7e9337d37c 100644
--- a/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc.c
+++ b/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc.c
@@ -20,22 +20,41 @@
  */
 
 #include "ran_func_rc.h"
+#include "ran_func_rc_subs.h"
 #include "ran_func_rc_extern.h"
 #include "../../flexric/test/rnd/fill_rnd_data_rc.h"  // this dependancy will be taken out once RAN Function Definition is implemented
 #include "../../flexric/src/sm/rc_sm/ie/ir/lst_ran_param.h"
 #include "../../flexric/src/sm/rc_sm/ie/ir/ran_param_list.h"
 #include "../../flexric/src/agent/e2_agent_api.h"
 
-#include <assert.h>
 #include <stdio.h>
-#include <pthread.h>
 #include <unistd.h>
-#include "common/utils/assertions.h"
 #include "common/ran_context.h"
 
-static const int rrc_state_changed_to = 202;
-static bool subs_rrc_state_change = false;
-static uint32_t ric_req_id = 0;
+static pthread_once_t once_rc_mutex = PTHREAD_ONCE_INIT;
+static rc_subs_data_t rc_subs_data = {0};
+static pthread_mutex_t rc_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static void init_once_rc(void)
+{
+  init_rc_subs_data(&rc_subs_data);
+}
+
+void read_rc_setup_sm(void* data)
+{
+  assert(data != NULL);
+//  assert(data->type == RAN_CTRL_V1_3_AGENT_IF_E2_SETUP_ANS_V0);
+  rc_e2_setup_t* rc = (rc_e2_setup_t*)data;
+  rc->ran_func_def = fill_rc_ran_func_def();
+
+  // E2 Setup Request is sent periodically until the connection is established
+  // RC subscritpion data should be initialized only once
+  const int ret = pthread_once(&once_rc_mutex, init_once_rc);
+  DevAssert(ret == 0);
+}
+
+
+RB_PROTOTYPE(ric_id_2_param_id_trees, ric_req_id_s, entries, cmp_ric_req_id);
 
 static ue_id_e2sm_t fill_ue_id_data(const gNB_RRC_UE_t *rrc_ue_context)
 {
@@ -65,11 +84,11 @@ static ue_id_e2sm_t fill_ue_id_data(const gNB_RRC_UE_t *rrc_ue_context)
       ue_id.gnb.gnb_cu_cp_ue_e1ap_lst[i] = rrc_ue_context->rrc_ue_id;
     }
   } else if (RC.nrrrc[0]->node_type == ngran_gNB_DU) {
-    assert(false && "Cannot access RRC layer in DU\n");
+    assert(false && "RRC state change cannot be triggered from DU\n");
   }
 
   #elif defined (NGRAN_GNB_CUUP)
-  assert(false && "Cannot access RRC layer in CU-UP\n");
+  assert(false && "RRC state change cannot be triggered from CU-UP\n");
 
   #endif
 
@@ -88,7 +107,7 @@ static seq_ran_param_t fill_rrc_state_change_seq_ran(const rc_sm_rrc_state_e rrc
 {
   seq_ran_param_t seq_ran_param = {0};
 
-  seq_ran_param.ran_param_id = rrc_state_changed_to;
+  seq_ran_param.ran_param_id = RRC_STATE_CHANGED_TO_E2SM_RC_RAN_PARAM_ID;
   seq_ran_param.ran_param_val.type = ELEMENT_KEY_FLAG_FALSE_RAN_PARAMETER_VAL_TYPE;
   seq_ran_param.ran_param_val.flag_false = calloc(1, sizeof(ran_parameter_value_t));
   assert(seq_ran_param.ran_param_val.flag_false != NULL && "Memory exhausted");
@@ -132,34 +151,70 @@ static rc_ind_data_t* fill_ue_rrc_state_change(const gNB_RRC_UE_t *rrc_ue_contex
   return rc_ind;
 }
 
-
 void signal_rrc_state_changed_to(const gNB_RRC_UE_t *rrc_ue_context, const rc_sm_rrc_state_e rrc_state)
-{
-  AssertFatal(subs_rrc_state_change == true, "xApp should be subscribed to E2 node before sending the RIC INDICATION Message\n");
+{ 
+  pthread_mutex_lock(&rc_mutex);
+  if (rc_subs_data.rb[RRC_STATE_CHANGED_TO_E2SM_RC_RAN_PARAM_ID].rbh_root == NULL) {
+    pthread_mutex_unlock(&rc_mutex);
+    return;
+  }
   
-  rc_ind_data_t* rc_ind_data = fill_ue_rrc_state_change(rrc_ue_context, rrc_state);
+  struct ric_req_id_s *node;
+  RB_FOREACH(node, ric_id_2_param_id_trees, &rc_subs_data.rb[RRC_STATE_CHANGED_TO_E2SM_RC_RAN_PARAM_ID]) {
+    rc_ind_data_t* rc_ind_data = fill_ue_rrc_state_change(rrc_ue_context, rrc_state);
 
-  async_event_agent_api(ric_req_id, rc_ind_data);
-  printf("Event for RIC Req ID %u generated\n", ric_req_id);
+    // Needs review: memory ownership of the type rc_ind_data_t is transferred to the E2 Agent. Bad
+    async_event_agent_api(node->ric_req_id, rc_ind_data);
+    printf( "Event for RIC Req ID %u generated\n", node->ric_req_id);
+  }
+  
+  pthread_mutex_unlock(&rc_mutex);
 }
 
-bool read_rc_sm(void* data)
+static void free_aperiodic_subscription(uint32_t ric_req_id)
 {
-  assert(data != NULL);
-//  assert(data->type == RAN_CTRL_STATS_V1_03);
-  assert(0!=0 && "Not implemented");
-
-  return true;
+  remove_rc_subs_data(&rc_subs_data, ric_req_id);
 }
 
-void read_rc_setup_sm(void* data)
+sm_ag_if_ans_t write_subs_rc_sm(void const* src)
 {
-  assert(data != NULL);
-//  assert(data->type == RAN_CTRL_V1_3_AGENT_IF_E2_SETUP_ANS_V0);
-  rc_e2_setup_t* rc = (rc_e2_setup_t*)data;
-  rc->ran_func_def = fill_rc_ran_func_def();
+  assert(src != NULL); // && src->type == RAN_CTRL_SUBS_V1_03);
+
+  wr_rc_sub_data_t* wr_rc = (wr_rc_sub_data_t*)src;
+
+  assert(wr_rc->rc.ad != NULL && "Cannot be NULL");
+
+  // 9.2.1.2  RIC ACTION DEFINITION IE
+  switch (wr_rc->rc.ad->format) {
+    case FORMAT_1_E2SM_RC_ACT_DEF: {
+      // Parameters to be Reported List
+      // [1-65535]
+      const uint32_t ric_req_id = wr_rc->ric_req_id;
+      arr_ran_param_id_t* arr_ran_param_id = calloc(1, sizeof(arr_ran_param_id_t));
+      assert(arr_ran_param_id != NULL && "Memory exhausted");
+      arr_ran_param_id->len = wr_rc->rc.ad->frmt_1.sz_param_report_def;
+      arr_ran_param_id->ran_param_id = calloc(arr_ran_param_id->len, sizeof(ran_param_id_e));
+
+      const size_t sz = arr_ran_param_id->len;
+      for(size_t i = 0; i < sz; i++) {    
+        arr_ran_param_id->ran_param_id[i] = wr_rc->rc.ad->frmt_1.param_report_def[i].ran_param_id;
+      }
+      insert_rc_subs_data(&rc_subs_data, ric_req_id, arr_ran_param_id);
+      break;
+    }
+  
+    default:
+      AssertFatal(wr_rc->rc.ad->format == FORMAT_1_E2SM_RC_ACT_DEF, "Action Definition Format %d not yet implemented", wr_rc->rc.ad->format);
+  }
+
+  sm_ag_if_ans_t ans = {.type = SUBS_OUTCOME_SM_AG_IF_ANS_V0};
+  ans.subs_out.type = APERIODIC_SUBSCRIPTION_FLRC;
+  ans.subs_out.aper.free_aper_subs = free_aperiodic_subscription;
+
+  return ans;
 }
 
+
 sm_ag_if_ans_t write_ctrl_rc_sm(void const* data)
 {
   assert(data != NULL);
@@ -205,36 +260,12 @@ sm_ag_if_ans_t write_ctrl_rc_sm(void const* data)
   return ans;
 }
 
-sm_ag_if_ans_t write_subs_rc_sm(void const* src)
-{
-  assert(src != NULL); // && src->type == RAN_CTRL_SUBS_V1_03);
-
-  wr_rc_sub_data_t* wr_rc = (wr_rc_sub_data_t*)src;
-
-  ric_req_id = wr_rc->ric_req_id;  // store the ric_req_id to generate asynchronous event
-
-  assert(wr_rc->rc.ad != NULL && "Cannot be NULL");
-
-  // 9.2.1.2  RIC ACTION DEFINITION IE
-  switch (wr_rc->rc.ad->format) {
-    case FORMAT_1_E2SM_RC_ACT_DEF: {
-      // Parameters to be Reported List
-      // [1-65535]
-      for(size_t i = 0; i < wr_rc->rc.ad->frmt_1.sz_param_report_def; i++) {
-        if (wr_rc->rc.ad->frmt_1.param_report_def[i].ran_param_id == rrc_state_changed_to) {
-          subs_rrc_state_change = true;
-        } else {
-          AssertFatal(wr_rc->rc.ad->frmt_1.param_report_def[i].ran_param_id == rrc_state_changed_to, "RAN Parameter ID %d not yet implemented\n", wr_rc->rc.ad->frmt_1.param_report_def[i].ran_param_id);
-        }
-      }
-      break;
-    }
-  
-    default:
-      AssertFatal(wr_rc->rc.ad->format == FORMAT_1_E2SM_RC_ACT_DEF, "Action Definition Format %d not yet implemented", wr_rc->rc.ad->format);
-  }
 
-  sm_ag_if_ans_t ans = {0}; 
+bool read_rc_sm(void* data)
+{
+  assert(data != NULL);
+//  assert(data->type == RAN_CTRL_STATS_V1_03);
+  assert(0!=0 && "Not implemented");
 
-  return ans;
+  return true;
 }
diff --git a/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc.h b/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc.h
index ed877fea021431ac27a37263ba3fb5b58bc99d1e..efabe7caff497e57c0860ff5f8703d627257ad90 100644
--- a/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc.h
+++ b/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc.h
@@ -24,13 +24,12 @@
 
 #include "openair2/E2AP/flexric/src/agent/../sm/sm_io.h"
 
-bool read_rc_sm(void *);
-
 void read_rc_setup_sm(void* data);
 
+sm_ag_if_ans_t write_subs_rc_sm(void const* src);
+
 sm_ag_if_ans_t write_ctrl_rc_sm(void const* data);
 
-sm_ag_if_ans_t write_subs_rc_sm(void const* src);
+bool read_rc_sm(void *);
 
 #endif
-
diff --git a/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc_subs.c b/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc_subs.c
new file mode 100644
index 0000000000000000000000000000000000000000..6c7176eafb02f5a15eebd276c36831e96c463c1f
--- /dev/null
+++ b/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc_subs.c
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The OpenAirInterface Software Alliance licenses this file to You under
+ * the OAI Public License, Version 1.1  (the "License"); you may not use this file
+ * except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.openairinterface.org/?page_id=698
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *-------------------------------------------------------------------------------
+ * For more information about the OpenAirInterface (OAI) Software Alliance:
+ *      contact@openairinterface.org
+ */
+
+#include "ran_func_rc_subs.h"
+#include "common/utils/assertions.h"
+
+#include <assert.h>
+#include <pthread.h>
+
+#define MAX_NUM_RIC_REQ_ID 64
+
+static pthread_mutex_t rc_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+int cmp_ric_req_id(struct ric_req_id_s *c1, struct ric_req_id_s *c2)
+{
+  if (c1->ric_req_id < c2->ric_req_id)
+    return -1;
+
+  if (c1->ric_req_id > c2->ric_req_id)
+    return 1;
+
+  return 0;
+}
+
+RB_GENERATE(ric_id_2_param_id_trees, ric_req_id_s, entries, cmp_ric_req_id);
+
+void init_rc_subs_data(rc_subs_data_t* rc_subs_data)
+{
+  pthread_mutex_lock(&rc_mutex);
+ 
+  // Initialize hash table
+  DevAssert(rc_subs_data->htable == NULL);
+ 
+  // Initialize RB trees
+  // 1 RB tree = 1 ran_param_id => many ric_req_id(s)
+  for (size_t i = 0; i < END_E2SM_RC_RAN_PARAM_ID; i++) {
+    RB_INIT(&rc_subs_data->rb[i]);
+  }
+
+   rc_subs_data->htable = hashtable_create(MAX_NUM_RIC_REQ_ID, NULL, free);
+  assert(rc_subs_data->htable != NULL && "Memory exhausted");
+  pthread_mutex_unlock(&rc_mutex);
+}
+
+void insert_rc_subs_data(rc_subs_data_t* rc_subs_data, uint32_t ric_req_id, arr_ran_param_id_t* arr_ran_param_id)
+{
+  pthread_mutex_lock(&rc_mutex);
+
+  // Insert in hash table
+  DevAssert(rc_subs_data->htable != NULL);
+  uint64_t key = ric_req_id;
+  // Check if the subscription already exists
+  AssertFatal(hashtable_is_key_exists(rc_subs_data->htable, key) == HASH_TABLE_KEY_NOT_EXISTS, "RIC req ID %d already subscribed", ric_req_id);
+  arr_ran_param_id_t* data = malloc(sizeof(*data));
+  assert(data != NULL);
+  *data = *arr_ran_param_id;
+  hashtable_rc_t ret = hashtable_insert(rc_subs_data->htable, key, data);
+  assert(ret == HASH_TABLE_OK  && "Hash table not ok");
+
+  // Insert in RB trees
+  // 1 RB tree = 1 ran_param_id => many ric_req_id(s)
+  const size_t sz = arr_ran_param_id->len;
+  rb_ric_req_id_t *node = calloc(1, sizeof(*node));
+  assert(node != NULL);
+  node->ric_req_id = ric_req_id;
+  for (size_t i = 0; i < sz; i++) {
+    RB_INSERT(ric_id_2_param_id_trees, &rc_subs_data->rb[arr_ran_param_id->ran_param_id[i]], node);
+  }
+
+  pthread_mutex_unlock(&rc_mutex);
+}
+
+void remove_rc_subs_data(rc_subs_data_t* rc_subs_data, uint32_t ric_req_id)
+{
+  pthread_mutex_lock(&rc_mutex);
+  DevAssert(rc_subs_data->htable != NULL);
+
+  uint64_t key = ric_req_id;
+  // Get the array of ran_param_id(s)
+  void *data = NULL;
+  hashtable_rc_t ret = hashtable_get(rc_subs_data->htable, key, &data);
+  AssertFatal(ret == HASH_TABLE_OK && data != NULL, "element for ue_id %d not found\n", ric_req_id);
+  arr_ran_param_id_t arr_ran_param_id = *(arr_ran_param_id_t *)data;
+  // Remove ric_req_id with its ran_param_id(s) from hash table
+  ret = hashtable_remove(rc_subs_data->htable, key);
+  
+  // Remove ric_req_id from each ran_param_id tree where subscribed
+  rb_ric_req_id_t *node = calloc(1, sizeof(*node));
+  assert(node != NULL);
+  node->ric_req_id = ric_req_id;
+  for (size_t i = 0; i < arr_ran_param_id.len; i++) {
+    RB_REMOVE(ric_id_2_param_id_trees, &rc_subs_data->rb[arr_ran_param_id.ran_param_id[i]], node);
+  }
+
+  pthread_mutex_unlock(&rc_mutex);
+}
diff --git a/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc_subs.h b/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc_subs.h
new file mode 100644
index 0000000000000000000000000000000000000000..1eb013c37c185c2a8d4de2810ba5edcc5bfa1cf1
--- /dev/null
+++ b/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc_subs.h
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The OpenAirInterface Software Alliance licenses this file to You under
+ * the OAI Public License, Version 1.1  (the "License"); you may not use this file
+ * except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.openairinterface.org/?page_id=698
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *-------------------------------------------------------------------------------
+ * For more information about the OpenAirInterface (OAI) Software Alliance:
+ *      contact@openairinterface.org
+ */
+
+#ifndef RAN_FUNC_SM_RAN_CTRL_SUBSCRIPTION_AGENT_H
+#define RAN_FUNC_SM_RAN_CTRL_SUBSCRIPTION_AGENT_H
+
+#include "common/utils/hashtable/hashtable.h"
+#include "common/utils/collection/tree.h"
+
+typedef enum {
+  RRC_STATE_CHANGED_TO_E2SM_RC_RAN_PARAM_ID = 202,   // 8.2.4  RAN Parameters for Report Service Style 4
+
+  END_E2SM_RC_RAN_PARAM_ID
+} ran_param_id_e;
+
+typedef struct{
+  size_t len;
+  ran_param_id_e* ran_param_id;
+} arr_ran_param_id_t;
+
+typedef struct ric_req_id_s {
+  RB_ENTRY(ric_req_id_s) entries;
+  uint32_t ric_req_id;
+} rb_ric_req_id_t;
+
+typedef struct {
+  RB_HEAD(ric_id_2_param_id_trees, ric_req_id_s) rb[END_E2SM_RC_RAN_PARAM_ID];  //  1 RB tree = (1 RAN Parameter ID) : (n RIC Request ID) => m RB tree = (m RAN Parameter ID) : (n RIC Request ID)
+  hash_table_t* htable;    // 1 Hash table = (n RIC Request ID) : (m RAN Parameter ID)
+} rc_subs_data_t;
+
+
+int cmp_ric_req_id(struct ric_req_id_s *c1, struct ric_req_id_s *c2);
+
+void init_rc_subs_data(rc_subs_data_t* rc_subs_data);
+void insert_rc_subs_data(rc_subs_data_t* rc_subs_data, uint32_t ric_req_id, arr_ran_param_id_t* arr_ran_param_id);
+void remove_rc_subs_data(rc_subs_data_t* rc_subs_data, uint32_t ric_req_id);
+
+#endif