int s1ap_eNB_handle_overload_start(uint32_t assoc_id, uint32_t stream, struct s1ap_message_s *message_p) { S1ap_OverloadStartIEs_t *overload_start_p; s1ap_eNB_mme_data_t *mme_desc_p; DevAssert(message_p != NULL); overload_start_p = &message_p->msg.s1ap_OverloadStartIEs; DevCheck(overload_start_p->overloadResponse.present == S1ap_OverloadResponse_PR_overloadAction, S1ap_OverloadResponse_PR_overloadAction, 0, 0); /* Non UE-associated signalling -> stream 0 */ DevCheck(stream == 0, stream, 0, 0); if ((mme_desc_p = s1ap_eNB_get_MME(NULL, assoc_id, 0)) == NULL) { /* No MME context associated */ return -1; } /* Mark the MME as overloaded and set the overload state according to * the value received. */ mme_desc_p->state = S1AP_ENB_OVERLOAD; mme_desc_p->overload_state = overload_start_p->overloadResponse.choice.overloadAction; return 0; }
int set_log(int component, int level, int interval) { /* Checking parameters */ DevCheck((component >= MIN_LOG_COMPONENTS) && (component < MAX_LOG_COMPONENTS), component, MIN_LOG_COMPONENTS, MAX_LOG_COMPONENTS); DevCheck((level <= LOG_TRACE) && (level >= LOG_EMERG), level, LOG_TRACE, LOG_EMERG); DevCheck((interval > 0) && (interval <= 0xFF), interval, 0, 0xFF); g_log->log_component[component].level = level; switch (level) { case LOG_TRACE: g_log->log_component[component].flag = LOG_MED ; break; case LOG_DEBUG: g_log->log_component[component].flag = LOG_MED ; break; case LOG_INFO: g_log->log_component[component].flag = LOG_LOW ; break; default: g_log->log_component[component].flag = LOG_NONE ; break; } g_log->log_component[component].interval = interval; return 0; }
static inline int s6a_parse_ip_address(struct avp_hdr *hdr, ip_address_t *ip_address) { uint16_t ip_type; DevCheck(hdr->avp_value->os.len >= 2, hdr->avp_value->os.len, 2, 0); ip_type = (hdr->avp_value->os.data[0] << 8) | (hdr->avp_value->os.data[1]); if (ip_type == IANA_IPV4) { /* This is an IPv4 address */ ip_address->pdn_type = IPv4; DevCheck(hdr->avp_value->os.len == 6, hdr->avp_value->os.len, 6, ip_type); memcpy(ip_address->address.ipv4_address, &hdr->avp_value->os.data[2], 4); } else if (ip_type == IANA_IPV6) { /* This is an IPv6 address */ ip_address->pdn_type = IPv6; DevCheck(hdr->avp_value->os.len == 18, hdr->avp_value->os.len, 18, ip_type); memcpy(ip_address->address.ipv6_address, &hdr->avp_value->os.data[2], 16); } else { /* unhandled case... */ return -1; } return 0; }
void s1ap_eNB_handle_register_eNB(instance_t instance, s1ap_register_enb_req_t *s1ap_register_eNB) { s1ap_eNB_instance_t *new_instance; uint8_t index; DevAssert(s1ap_register_eNB != NULL); /* Look if the provided instance already exists */ new_instance = s1ap_eNB_get_instance(instance); if (new_instance != NULL) { /* Checks if it is a retry on the same eNB */ DevCheck(new_instance->eNB_id == s1ap_register_eNB->eNB_id, new_instance->eNB_id, s1ap_register_eNB->eNB_id, 0); DevCheck(new_instance->cell_type == s1ap_register_eNB->cell_type, new_instance->cell_type, s1ap_register_eNB->cell_type, 0); DevCheck(new_instance->tac == s1ap_register_eNB->tac, new_instance->tac, s1ap_register_eNB->tac, 0); DevCheck(new_instance->mcc == s1ap_register_eNB->mcc, new_instance->mcc, s1ap_register_eNB->mcc, 0); DevCheck(new_instance->mnc == s1ap_register_eNB->mnc, new_instance->mnc, s1ap_register_eNB->mnc, 0); DevCheck(new_instance->mnc_digit_length == s1ap_register_eNB->mnc_digit_length, new_instance->mnc_digit_length, s1ap_register_eNB->mnc_digit_length, 0); DevCheck(new_instance->default_drx == s1ap_register_eNB->default_drx, new_instance->default_drx, s1ap_register_eNB->default_drx, 0); } else { new_instance = calloc(1, sizeof(s1ap_eNB_instance_t)); DevAssert(new_instance != NULL); RB_INIT(&new_instance->s1ap_ue_head); RB_INIT(&new_instance->s1ap_mme_head); /* Copy usefull parameters */ new_instance->instance = instance; new_instance->eNB_name = s1ap_register_eNB->eNB_name; new_instance->eNB_id = s1ap_register_eNB->eNB_id; new_instance->cell_type = s1ap_register_eNB->cell_type; new_instance->tac = s1ap_register_eNB->tac; new_instance->mcc = s1ap_register_eNB->mcc; new_instance->mnc = s1ap_register_eNB->mnc; new_instance->mnc_digit_length = s1ap_register_eNB->mnc_digit_length; new_instance->default_drx = s1ap_register_eNB->default_drx; /* Add the new instance to the list of eNB (meaningfull in virtual mode) */ s1ap_eNB_insert_new_instance(new_instance); S1AP_INFO("Registered new eNB[%d] and %s eNB id %u\n", instance, s1ap_register_eNB->cell_type == CELL_MACRO_ENB ? "macro" : "home", s1ap_register_eNB->eNB_id); } DevCheck(s1ap_register_eNB->nb_mme <= S1AP_MAX_NB_MME_IP_ADDRESS, S1AP_MAX_NB_MME_IP_ADDRESS, s1ap_register_eNB->nb_mme, 0); /* Trying to connect to provided list of MME ip address */ for (index = 0; index < s1ap_register_eNB->nb_mme; index++) { s1ap_eNB_register_mme(new_instance, &s1ap_register_eNB->mme_ip_address[index], &s1ap_register_eNB->enb_ip_address, s1ap_register_eNB->sctp_in_streams, s1ap_register_eNB->sctp_out_streams); } }
/**************************************************************************** ** ** ** Name: emm_proc_common_abort() ** ** ** ** Description: The ongoing EMM procedure has been aborted. The network ** ** performs required actions related to the EMM common pro- ** ** cedure previously initiated between the UE with the spe- ** ** cified identifier and the MME. ** ** ** ** Inputs: ueid: UE lower layer identifier ** ** Others: _emm_common_data ** ** ** ** Outputs: None ** ** Return: RETURNok, RETURNerror ** ** Others: None ** ** ** ***************************************************************************/ int emm_proc_common_abort ( unsigned int ueid) { emm_common_data_t *emm_common_data_ctx = NULL; emm_common_failure_callback_t emm_callback; int rc = RETURNerror; LOG_FUNC_IN; #if NAS_BUILT_IN_EPC DevCheck (ueid > 0, ueid, 0, 0); emm_common_data_ctx = emm_common_data_context_get (&emm_common_data_head, ueid); #else assert (ueid < EMM_DATA_NB_UE_MAX); emm_common_data_ctx = _emm_common_data[ueid]; #endif assert (emm_common_data_ctx != NULL); emm_callback = emm_common_data_ctx->abort; if (emm_callback) { struct emm_data_context_s *ctx = NULL; #if NAS_BUILT_IN_EPC ctx = emm_data_context_get (&_emm_data, ueid); #else ctx = _emm_data.ctx[ueid]; #endif rc = (*emm_callback) (ctx); } _emm_common_cleanup (ueid); LOG_FUNC_RETURN (rc); }
/**************************************************************************** ** ** ** Name: emm_proc_common_cleanup() ** ** ** ** Description: Cleans EMM procedure callback functions upon completion ** ** of an EMM common procedure previously initiated within an ** ** EMM procedure currently in progress between the network ** ** and the UE with the specified identifier. ** ** ** ** Inputs: ueid: UE lower layer identifier ** ** Others: None ** ** ** ** Outputs: None ** ** Return: None ** ** Others: _emm_common_data ** ** ** ***************************************************************************/ static void _emm_common_cleanup ( unsigned int ueid) { emm_common_data_t *emm_common_data_ctx = NULL; #if NAS_BUILT_IN_EPC DevCheck (ueid > 0, ueid, 0, 0); emm_common_data_ctx = emm_common_data_context_get (&emm_common_data_head, ueid); #else assert (ueid < EMM_DATA_NB_UE_MAX); emm_common_data_ctx = _emm_common_data[ueid]; #endif if (emm_common_data_ctx) { emm_common_data_ctx->ref_count -= 1; if (emm_common_data_ctx->ref_count == 0) { /* * Release the callback functions */ #if NAS_BUILT_IN_EPC RB_REMOVE (emm_common_data_map, &emm_common_data_head.emm_common_data_root, emm_common_data_ctx); #endif free (emm_common_data_ctx); emm_common_data_ctx = NULL; } } }
static inline int s6a_parse_apn_configuration_profile(struct avp *avp_apn_conf_prof, apn_config_profile_t *apn_config_profile) { struct avp *avp = NULL; struct avp_hdr *hdr; CHECK_FCT(fd_msg_browse(avp_apn_conf_prof, MSG_BRW_FIRST_CHILD, &avp, NULL)); while(avp) { CHECK_FCT(fd_msg_avp_hdr(avp, &hdr)); switch(hdr->avp_code) { case AVP_CODE_CONTEXT_IDENTIFIER: apn_config_profile->context_identifier = hdr->avp_value->u32; break; case AVP_CODE_ALL_APN_CONFIG_INC_IND: CHECK_FCT(s6a_parse_all_apn_conf_inc_ind(hdr, &apn_config_profile->all_apn_conf_ind)); break; case AVP_CODE_APN_CONFIGURATION: { DevCheck(apn_config_profile->nb_apns < MAX_APN_PER_UE, apn_config_profile->nb_apns, MAX_APN_PER_UE, 0); CHECK_FCT(s6a_parse_apn_configuration( avp, &apn_config_profile->apn_configuration[apn_config_profile->nb_apns])); apn_config_profile->nb_apns++; } break; } /* Go to next AVP in the grouped AVP */ CHECK_FCT(fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL)); } return 0; }
int s1ap_eNB_handle_overload_stop(uint32_t assoc_id, uint32_t stream, struct s1ap_message_s *message_p) { /* We received Overload stop message, meaning that the MME is no more * overloaded. This is an empty message, with only message header and no * Information Element. */ DevAssert(message_p != NULL); s1ap_eNB_mme_data_t *mme_desc_p; /* Non UE-associated signalling -> stream 0 */ DevCheck(stream == 0, stream, 0, 0); if ((mme_desc_p = s1ap_eNB_get_MME(NULL, assoc_id, 0)) == NULL) { /* No MME context associated */ return -1; } mme_desc_p->state = S1AP_ENB_STATE_CONNECTED; mme_desc_p->overload_state = S1AP_NO_OVERLOAD; return 0; }
static inline int s6a_parse_pre_emp_vulnerability(struct avp_hdr *hdr, pre_emp_vulnerability_t *pre_emp_vulnerability) { DevCheck(hdr->avp_value->u32 < PRE_EMPTION_VULNERABILITY_MAX, hdr->avp_value->u32, PRE_EMPTION_VULNERABILITY_MAX, 0); *pre_emp_vulnerability = hdr->avp_value->u32; return 0; }
static inline int s6a_parse_qci(struct avp_hdr *hdr, qci_t *qci) { DevCheck(hdr->avp_value->u32 < QCI_MAX, hdr->avp_value->u32, QCI_MAX, 0); *qci = hdr->avp_value->u32; return 0; }
static inline int s6a_parse_pdn_type(struct avp_hdr *hdr, pdn_type_t *pdn_type) { DevCheck(hdr->avp_value->u32 < IP_MAX, hdr->avp_value->u32, IP_MAX, 0); *pdn_type = hdr->avp_value->u32; return 0; }
static inline int s6a_parse_all_apn_conf_inc_ind(struct avp_hdr *hdr, all_apn_conf_ind_t *ptr) { DevCheck(hdr->avp_value->u32 < ALL_APN_MAX, hdr->avp_value->u32, ALL_APN_MAX, 0); *ptr = hdr->avp_value->u32; return 0; }
static inline int s6a_parse_priority_level(struct avp_hdr *hdr, priority_level_t *priority_level) { DevCheck(hdr->avp_value->u32 <= PRIORITY_LEVEL_MAX && hdr->avp_value->u32 >= PRIORITY_LEVEL_MIN, hdr->avp_value->u32, PRIORITY_LEVEL_MAX, PRIORITY_LEVEL_MIN); *priority_level = (priority_level_t)hdr->avp_value->u32; return 0; }
static inline int s6a_parse_access_restriction_data(struct avp_hdr *hdr_access_restriction, access_restriction_t *access_restriction) { DevCheck(hdr_access_restriction->avp_value->u32 < ARD_MAX, hdr_access_restriction->avp_value->u32, ARD_MAX, 0); *access_restriction = hdr_access_restriction->avp_value->u32; return 0; }
static inline int s6a_parse_service_selection(struct avp_hdr *hdr_service_selection, char *service_selection, int *length) { DevCheck(hdr_service_selection->avp_value->os.len <= APN_MAX_LENGTH, hdr_service_selection->avp_value->os.len, APN_MAX_LENGTH, 0); *length = sprintf(service_selection, "%*s", (int)hdr_service_selection->avp_value->os.len, hdr_service_selection->avp_value->os.data); return 0; }
int set_comp_log(int component, int level, int verbosity, int interval) { /* Checking parameters */ DevCheck((component >= MIN_LOG_COMPONENTS) && (component < MAX_LOG_COMPONENTS), component, MIN_LOG_COMPONENTS, MAX_LOG_COMPONENTS); DevCheck((level <= LOG_TRACE) && (level >= LOG_EMERG), level, LOG_TRACE, LOG_EMERG); DevCheck((interval > 0) && (interval <= 0xFF), interval, 0, 0xFF); if ((verbosity == LOG_NONE) || (verbosity == LOG_LOW) || (verbosity == LOG_MED) || (verbosity == LOG_FULL) || (verbosity == LOG_HIGH)) { g_log->log_component[component].flag = verbosity; } g_log->log_component[component].level = level; g_log->log_component[component].interval = interval; return 0; }
static inline int s6a_parse_subscriber_status(struct avp_hdr *hdr_sub_status, subscriber_status_t *sub_status) { DevCheck(hdr_sub_status->avp_value->u32 < SS_MAX, hdr_sub_status->avp_value->u32, SS_MAX, 0); *sub_status = hdr_sub_status->avp_value->u32; return 0; }
struct emm_data_context_s *emm_data_context_get( emm_data_t *emm_data, unsigned int _ueid) { struct emm_data_context_s reference; DevAssert(emm_data != NULL); DevCheck(_ueid > 0, _ueid, 0, 0); memset(&reference, 0, sizeof(struct emm_data_context_s)); reference.ueid = _ueid; return RB_FIND(emm_data_context_map, &emm_data->ctx_map, &reference); }
static inline int s6a_parse_network_access_mode(struct avp_hdr *hdr_network_am, network_access_mode_t *access_mode) { DevCheck(hdr_network_am->avp_value->u32 < NAM_MAX && hdr_network_am->avp_value->u32 != NAM_RESERVED, hdr_network_am->avp_value->u32, NAM_MAX, NAM_RESERVED); *access_mode = hdr_network_am->avp_value->u32; return 0; }
struct emm_common_data_s * emm_common_data_context_get ( struct emm_common_data_head_s *root, unsigned int _ueid) { struct emm_common_data_s reference; DevAssert (root != NULL); DevCheck (_ueid > 0, _ueid, 0, 0); memset (&reference, 0, sizeof (struct emm_common_data_s)); reference.ueid = _ueid; return RB_FIND (emm_common_data_map, &root->emm_common_data_root, &reference); }
/**************************************************************************** ** ** ** Name: emm_proc_common_get_args() ** ** ** ** Description: Returns pointer to the EMM common procedure argument pa- ** ** rameters allocated for the UE with the given identifier. ** ** ** ** Inputs: ueid: UE lower layer identifier ** ** Others: _emm_common_data ** ** ** ** Outputs: None ** ** Return: pointer to the EMM common procedure argu- ** ** ment parameters ** ** Others: None ** ** ** ***************************************************************************/ void * emm_proc_common_get_args ( unsigned int ueid) { emm_common_data_t *emm_common_data_ctx = NULL; LOG_FUNC_IN; #if NAS_BUILT_IN_EPC DevCheck (ueid > 0, ueid, 0, 0); emm_common_data_ctx = emm_common_data_context_get (&emm_common_data_head, ueid); #else assert (ueid < EMM_DATA_NB_UE_MAX); emm_common_data_ctx = _emm_common_data[ueid]; #endif assert (emm_common_data_ctx != NULL); LOG_FUNC_RETURN (emm_common_data_ctx->args); }
static inline int s6a_parse_msisdn(struct avp_hdr *hdr_msisdn, char *msisdn, uint8_t *length) { int ret; DevCheck(hdr_msisdn->avp_value->os.len <= MSISDN_LENGTH, hdr_msisdn->avp_value->os.len, MSISDN_LENGTH, 0); if (hdr_msisdn->avp_value->os.len == 0) return 0; ret = sprintf(msisdn, "%*s", (int)hdr_msisdn->avp_value->os.len, hdr_msisdn->avp_value->os.data); *length = ret; return 0; }
void s1ap_handle_s1_setup_message(s1ap_eNB_mme_data_t *mme_desc_p, int sctp_shutdown) { if (sctp_shutdown) { /* A previously connected MME has been shutdown */ /* TODO check if it was used by some eNB and send a message to inform these eNB if there is no more associated MME */ if (mme_desc_p->state == S1AP_ENB_STATE_CONNECTED) { mme_desc_p->state = S1AP_ENB_STATE_DISCONNECTED; if (mme_desc_p->s1ap_eNB_instance->s1ap_mme_associated_nb > 0) { /* Decrease associated MME number */ mme_desc_p->s1ap_eNB_instance->s1ap_mme_associated_nb --; } /* If there are no more associated MME, inform eNB app */ if (mme_desc_p->s1ap_eNB_instance->s1ap_mme_associated_nb == 0) { MessageDef *message_p; message_p = itti_alloc_new_message(TASK_S1AP, S1AP_DEREGISTERED_ENB_IND); S1AP_DEREGISTERED_ENB_IND(message_p).nb_mme = 0; itti_send_msg_to_task(TASK_ENB_APP, mme_desc_p->s1ap_eNB_instance->instance, message_p); } } } else { /* Check that at least one setup message is pending */ DevCheck(mme_desc_p->s1ap_eNB_instance->s1ap_mme_pending_nb > 0, mme_desc_p->s1ap_eNB_instance->instance, mme_desc_p->s1ap_eNB_instance->s1ap_mme_pending_nb, 0); if (mme_desc_p->s1ap_eNB_instance->s1ap_mme_pending_nb > 0) { /* Decrease pending messages number */ mme_desc_p->s1ap_eNB_instance->s1ap_mme_pending_nb --; } /* If there are no more pending messages, inform eNB app */ if (mme_desc_p->s1ap_eNB_instance->s1ap_mme_pending_nb == 0) { MessageDef *message_p; message_p = itti_alloc_new_message(TASK_S1AP, S1AP_REGISTER_ENB_CNF); S1AP_REGISTER_ENB_CNF(message_p).nb_mme = mme_desc_p->s1ap_eNB_instance->s1ap_mme_associated_nb; itti_send_msg_to_task(TASK_ENB_APP, mme_desc_p->s1ap_eNB_instance->instance, message_p); } } }
int s6a_parse_experimental_result(struct avp *avp, s6a_experimental_result_t *ptr) { struct avp_hdr *hdr; struct avp *child_avp = NULL; if (!avp) { return EINVAL; } CHECK_FCT(fd_msg_avp_hdr(avp, &hdr)); DevAssert(hdr->avp_code == AVP_CODE_EXPERIMENTAL_RESULT); CHECK_FCT(fd_msg_browse(avp, MSG_BRW_FIRST_CHILD, &child_avp, NULL)); while(child_avp) { CHECK_FCT(fd_msg_avp_hdr(child_avp, &hdr)); switch(hdr->avp_code) { case AVP_CODE_EXPERIMENTAL_RESULT_CODE: S6A_ERROR("Got experimental error %u:%s\n", hdr->avp_value->u32, experimental_retcode_2_string(hdr->avp_value->u32)); if (ptr) { *ptr = (s6a_experimental_result_t)hdr->avp_value->u32; } break; case AVP_CODE_VENDOR_ID: DevCheck(hdr->avp_value->u32 == 10415, hdr->avp_value->u32, AVP_CODE_VENDOR_ID, 10415); break; default: return -1; } /* Go to next AVP in the grouped AVP */ CHECK_FCT(fd_msg_browse(child_avp, MSG_BRW_NEXT, &child_avp, NULL)); } return 0; }
//------------------------------------------------------------------------------ int s1ap_eNB_handle_nas_first_req( instance_t instance, s1ap_nas_first_req_t *s1ap_nas_first_req_p) //------------------------------------------------------------------------------ { s1ap_eNB_instance_t *instance_p = NULL; struct s1ap_eNB_mme_data_s *mme_desc_p = NULL; struct s1ap_eNB_ue_context_s *ue_desc_p = NULL; s1ap_message message; S1ap_InitialUEMessageIEs_t *initial_ue_message_p = NULL; uint8_t *buffer = NULL; uint32_t length = 0; DevAssert(s1ap_nas_first_req_p != NULL); /* Retrieve the S1AP eNB instance associated with Mod_id */ instance_p = s1ap_eNB_get_instance(instance); DevAssert(instance_p != NULL); memset(&message, 0, sizeof(s1ap_message)); message.direction = S1AP_PDU_PR_initiatingMessage; message.procedureCode = S1ap_ProcedureCode_id_initialUEMessage; initial_ue_message_p = &message.msg.s1ap_InitialUEMessageIEs; /* Select the MME corresponding to the provided GUMMEI. */ if (s1ap_nas_first_req_p->ue_identity.presenceMask & UE_IDENTITIES_gummei) { mme_desc_p = s1ap_eNB_nnsf_select_mme_by_gummei( instance_p, s1ap_nas_first_req_p->establishment_cause, s1ap_nas_first_req_p->ue_identity.gummei); } if (mme_desc_p == NULL) { /* Select the MME corresponding to the provided s-TMSI. */ if (s1ap_nas_first_req_p->ue_identity.presenceMask & UE_IDENTITIES_s_tmsi) { mme_desc_p = s1ap_eNB_nnsf_select_mme_by_mme_code( instance_p, s1ap_nas_first_req_p->establishment_cause, s1ap_nas_first_req_p->ue_identity.s_tmsi.mme_code); } } if (mme_desc_p == NULL) { /* * If no MME corresponds to the GUMMEI or the s-TMSI, selects the MME with the * highest capacity. */ mme_desc_p = s1ap_eNB_nnsf_select_mme( instance_p, s1ap_nas_first_req_p->establishment_cause); } if (mme_desc_p == NULL) { /* * In case eNB has no MME associated, the eNB should inform RRC and discard * this request. */ S1AP_WARN("No MME is associated to the eNB\n"); // TODO: Inform RRC return -1; } /* The eNB should allocate a unique eNB UE S1AP ID for this UE. The value * will be used for the duration of the connectivity. */ ue_desc_p = s1ap_eNB_allocate_new_UE_context(); DevAssert(ue_desc_p != NULL); /* Keep a reference to the selected MME */ ue_desc_p->mme_ref = mme_desc_p; ue_desc_p->ue_initial_id = s1ap_nas_first_req_p->ue_initial_id; ue_desc_p->eNB_instance = instance_p; do { struct s1ap_eNB_ue_context_s *collision_p; /* Peek a random value for the eNB_ue_s1ap_id */ ue_desc_p->eNB_ue_s1ap_id = (random() + random()) & 0x00ffffff; if ((collision_p = RB_INSERT(s1ap_ue_map, &instance_p->s1ap_ue_head, ue_desc_p)) == NULL) { S1AP_DEBUG("Found usable eNB_ue_s1ap_id: 0x%06x %d(10)\n", ue_desc_p->eNB_ue_s1ap_id, ue_desc_p->eNB_ue_s1ap_id); /* Break the loop as the id is not already used by another UE */ break; } } while(1); initial_ue_message_p->eNB_UE_S1AP_ID = ue_desc_p->eNB_ue_s1ap_id; /* Prepare the NAS PDU */ initial_ue_message_p->nas_pdu.buf = s1ap_nas_first_req_p->nas_pdu.buffer; initial_ue_message_p->nas_pdu.size = s1ap_nas_first_req_p->nas_pdu.length; /* Set the establishment cause according to those provided by RRC */ DevCheck(s1ap_nas_first_req_p->establishment_cause < RRC_CAUSE_LAST, s1ap_nas_first_req_p->establishment_cause, RRC_CAUSE_LAST, 0); initial_ue_message_p->rrC_Establishment_Cause = s1ap_nas_first_req_p->establishment_cause; if (s1ap_nas_first_req_p->ue_identity.presenceMask & UE_IDENTITIES_s_tmsi) { S1AP_DEBUG("S_TMSI_PRESENT\n"); initial_ue_message_p->presenceMask |= S1AP_INITIALUEMESSAGEIES_S_TMSI_PRESENT; MME_CODE_TO_OCTET_STRING(s1ap_nas_first_req_p->ue_identity.s_tmsi.mme_code, &initial_ue_message_p->s_tmsi.mMEC); M_TMSI_TO_OCTET_STRING(s1ap_nas_first_req_p->ue_identity.s_tmsi.m_tmsi, &initial_ue_message_p->s_tmsi.m_TMSI); } if (s1ap_nas_first_req_p->ue_identity.presenceMask & UE_IDENTITIES_gummei) { S1AP_DEBUG("GUMMEI_ID_PRESENT\n"); initial_ue_message_p->presenceMask |= S1AP_INITIALUEMESSAGEIES_GUMMEI_ID_PRESENT; MCC_MNC_TO_PLMNID( s1ap_nas_first_req_p->ue_identity.gummei.mcc, s1ap_nas_first_req_p->ue_identity.gummei.mnc, s1ap_nas_first_req_p->ue_identity.gummei.mnc_len, &initial_ue_message_p->gummei_id.pLMN_Identity); MME_GID_TO_OCTET_STRING(s1ap_nas_first_req_p->ue_identity.gummei.mme_group_id, &initial_ue_message_p->gummei_id.mME_Group_ID); MME_CODE_TO_OCTET_STRING(s1ap_nas_first_req_p->ue_identity.gummei.mme_code, &initial_ue_message_p->gummei_id.mME_Code); } /* Assuming TAI is the TAI from the cell */ INT16_TO_OCTET_STRING(instance_p->tac, &initial_ue_message_p->tai.tAC); MCC_MNC_TO_PLMNID(instance_p->mcc, instance_p->mnc, instance_p->mnc_digit_length, &initial_ue_message_p->tai.pLMNidentity); /* Set the EUTRAN CGI * The cell identity is defined on 28 bits but as we use macro enb id, * we have to pad. */ #warning "TODO get cell id from RRC" MACRO_ENB_ID_TO_CELL_IDENTITY(instance_p->eNB_id, 0, // Cell ID &initial_ue_message_p->eutran_cgi.cell_ID); MCC_MNC_TO_TBCD(instance_p->mcc, instance_p->mnc, instance_p->mnc_digit_length, &initial_ue_message_p->eutran_cgi.pLMNidentity); if (s1ap_eNB_encode_pdu(&message, &buffer, &length) < 0) { /* Failed to encode message */ DevMessage("Failed to encode initial UE message\n"); } /* Update the current S1AP UE state */ ue_desc_p->ue_state = S1AP_UE_WAITING_CSR; /* Assign a stream for this UE : * From 3GPP 36.412 7)Transport layers: * Within the SCTP association established between one MME and eNB pair: * - a single pair of stream identifiers shall be reserved for the sole use * of S1AP elementary procedures that utilize non UE-associated signalling. * - At least one pair of stream identifiers shall be reserved for the sole use * of S1AP elementary procedures that utilize UE-associated signallings. * However a few pairs (i.e. more than one) should be reserved. * - A single UE-associated signalling shall use one SCTP stream and * the stream should not be changed during the communication of the * UE-associated signalling. */ mme_desc_p->nextstream = (mme_desc_p->nextstream + 1) % mme_desc_p->out_streams; if ((mme_desc_p->nextstream == 0) && (mme_desc_p->out_streams > 1)) { mme_desc_p->nextstream += 1; } ue_desc_p->tx_stream = mme_desc_p->nextstream; MSC_LOG_TX_MESSAGE( MSC_S1AP_ENB, MSC_S1AP_MME, (const char *)NULL, 0, MSC_AS_TIME_FMT" initialUEMessage initiatingMessage eNB_ue_s1ap_id %u", 0,0,//MSC_AS_TIME_ARGS(ctxt_pP), initial_ue_message_p->eNB_UE_S1AP_ID); /* Send encoded message over sctp */ s1ap_eNB_itti_send_sctp_data_req(instance_p->instance, mme_desc_p->assoc_id, buffer, length, ue_desc_p->tx_stream); return 0; }
//----------------------------------------------------------------------------- int pdcp_validate_security( const protocol_ctxt_t* const ctxt_pP, pdcp_t * const pdcp_pP, const srb_flag_t srb_flagP, const rb_id_t rb_id, const uint8_t pdcp_header_len, const uint16_t current_sn, uint8_t *const pdcp_pdu_buffer, const uint16_t sdu_buffer_size ) { uint8_t *buffer_decrypted = NULL; stream_cipher_t decrypt_params; DevAssert(pdcp_pP != NULL); DevAssert(pdcp_pdu_buffer != NULL); DevCheck(rb_id < NB_RB_MAX && rb_id >= 0, rb_id, NB_RB_MAX, 0); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PDCP_VALIDATE_SECURITY, VCD_FUNCTION_IN); buffer_decrypted = (uint8_t*)&pdcp_pdu_buffer[pdcp_header_len]; decrypt_params.direction = (pdcp_pP->is_ue == 1) ? SECU_DIRECTION_DOWNLINK : SECU_DIRECTION_UPLINK ; decrypt_params.bearer = rb_id - 1; decrypt_params.count = pdcp_get_next_count_rx(pdcp_pP, srb_flagP, current_sn); decrypt_params.message = &pdcp_pdu_buffer[pdcp_header_len]; decrypt_params.blength = (sdu_buffer_size - pdcp_header_len) << 3; decrypt_params.key_length = 16; if (srb_flagP) { LOG_D(PDCP, "[OSA][RB %d] %s Validating control-plane security\n", rb_id, (pdcp_pP->is_ue != 0) ? "eNB -> UE" : "UE -> eNB"); decrypt_params.key = pdcp_pP->kRRCenc;// + 128; } else { LOG_D(PDCP, "[OSA][RB %d] %s Validating user-plane security\n", rb_id, (pdcp_pP->is_ue != 0) ? "eNB -> UE" : "UE -> eNB"); decrypt_params.key = pdcp_pP->kUPenc;// + 128; } /* Uncipher the block */ stream_decrypt(pdcp_pP->cipheringAlgorithm, &decrypt_params, &buffer_decrypted); if (srb_flagP) { /* Now check the integrity of the complete PDU */ decrypt_params.message = pdcp_pdu_buffer; decrypt_params.blength = sdu_buffer_size << 3; decrypt_params.key = pdcp_pP->kRRCint + 16;// 128; if (stream_check_integrity(pdcp_pP->integrityProtAlgorithm, &decrypt_params, &pdcp_pdu_buffer[sdu_buffer_size]) != 0) { MSC_LOG_EVENT( (ctxt_pP->enb_flag == ENB_FLAG_YES) ? MSC_PDCP_ENB:MSC_PDCP_UE, " Security: failed MAC-I Algo %X UE %"PRIx16" ", pdcp_pP->integrityProtAlgorithm, ctxt_pP->rnti); LOG_E(PDCP, "[OSA][RB %d] %s failed to validate MAC-I of incoming PDU\n", rb_id, (pdcp_pP->is_ue != 0) ? "UE" : "eNB"); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PDCP_VALIDATE_SECURITY, VCD_FUNCTION_OUT); return -1; } } VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PDCP_VALIDATE_SECURITY, VCD_FUNCTION_OUT); return 0; }
//----------------------------------------------------------------------------- int pdcp_apply_security( const protocol_ctxt_t* const ctxt_pP, pdcp_t *const pdcp_pP, const srb_flag_t srb_flagP, const rb_id_t rb_id, const uint8_t pdcp_header_len, const uint16_t current_sn, uint8_t * const pdcp_pdu_buffer, const uint16_t sdu_buffer_size ) { uint8_t *buffer_encrypted = NULL; stream_cipher_t encrypt_params; DevAssert(pdcp_pP != NULL); DevAssert(pdcp_pdu_buffer != NULL); DevCheck(rb_id < NB_RB_MAX && rb_id >= 0, rb_id, NB_RB_MAX, 0); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PDCP_APPLY_SECURITY, VCD_FUNCTION_IN); encrypt_params.direction = (pdcp_pP->is_ue == 1) ? SECU_DIRECTION_UPLINK : SECU_DIRECTION_DOWNLINK; encrypt_params.bearer = rb_id - 1; encrypt_params.count = pdcp_get_next_count_tx(pdcp_pP, srb_flagP, current_sn); encrypt_params.key_length = 16; if (srb_flagP) { /* SRBs */ uint8_t *mac_i; LOG_D(PDCP, "[OSA][RB %d] %s Applying control-plane security %d \n", rb_id, (pdcp_pP->is_ue != 0) ? "UE -> eNB" : "eNB -> UE", pdcp_pP->integrityProtAlgorithm); encrypt_params.message = pdcp_pdu_buffer; encrypt_params.blength = (pdcp_header_len + sdu_buffer_size) << 3; encrypt_params.key = pdcp_pP->kRRCint + 16; // + 128; mac_i = &pdcp_pdu_buffer[pdcp_header_len + sdu_buffer_size]; /* Both header and data parts are integrity protected for * control-plane PDUs */ stream_compute_integrity(pdcp_pP->integrityProtAlgorithm, &encrypt_params, mac_i); encrypt_params.key = pdcp_pP->kRRCenc; // + 128 // bit key } else { LOG_D(PDCP, "[OSA][RB %d] %s Applying user-plane security\n", rb_id, (pdcp_pP->is_ue != 0) ? "UE -> eNB" : "eNB -> UE"); encrypt_params.key = pdcp_pP->kUPenc;// + 128; } encrypt_params.message = &pdcp_pdu_buffer[pdcp_header_len]; encrypt_params.blength = sdu_buffer_size << 3; buffer_encrypted = &pdcp_pdu_buffer[pdcp_header_len]; /* Apply ciphering if any requested */ stream_encrypt(pdcp_pP->cipheringAlgorithm, &encrypt_params, &buffer_encrypted); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PDCP_APPLY_SECURITY, VCD_FUNCTION_OUT); return 0; }