/*================================================================================== Copyright (c) 2018-2019 AT&T Intellectual Property. Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 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. ================================================================================== */ /* Author : Ashwin Sridharan Date : Feb 2019 */ /* A mock e2term for testing -- accepts a subscription request -- accepts a delete subscription request -- when subscription is active, sends out indication messages with X2AP SgNBAdditionRequest at specified rate and monitors control request -- when delete is requested, stops sending ... */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include unsigned long int num_indications = 0; unsigned long int num_controls = 0; unsigned long int num_accepts = 0; unsigned long int num_rejects = 0; unsigned long int num_errors = 0; bool verbose_flag = false; bool RunProgram = true; bool subscription_active = false; int action_type = E2N_RICindicationType::E2N_RICindicationType_report; // PRE-ENCODED X2AP SGNB ADDITION REQUESTS static const std::vector x2ap_files = {"test-data/X2AP-PDU-SgNBAdditionRequest_SubId_10.per", "test-data/X2AP-PDU-SgNBAdditionRequest_SubId_23.per", "test-data/X2AP-PDU-SgNBAdditionRequest_SubId_29.per", "test-data/X2AP-PDU-SgNBAdditionRequest_SubId_180.per", "test-data/X2AP-PDU-SgNBAdditionRequest_SubId_210.per"}; void usage(char *command){ std::cout <<"Usage : " << command << " "; std::cout <<" --name[-n] xapp_instance_name "; std::cout <<" --port[-p] port to listen on e.g tcp:4591 "; std::cout << "--verbose "; std::cout <<" --rate[-r] rate to send indication messages"; std::cout << std::endl; } void EndProgram(int signum){ std::cout <<"Signal received. Stopping program ....." << std::endl; RunProgram = false; } bool Message_Handler(rmr_mbuf_t *message){ bool res; int i; unsigned char meid[32]; unsigned char src[32]; subscription_helper he; subscription_response_helper he_resp; subscription_request sub_req; subscription_response sub_resp; subscription_delete sub_del_req; subscription_delete_response sub_del_resp; ric_control_request control_req; ric_control_response control_resp; ric_control_helper control_data; e2sm_control e2sm_control_proc; asn_dec_rval_t retval; size_t mlen; E2N_E2AP_PDU_t *e2ap_pdu_recv = 0; X2N_X2AP_PDU_t *x2ap_pdu_recv = 0; E2N_E2SM_gNB_X2_eventTriggerDefinition_t *event = 0; std::vector * actions; bool send_msg = true; rmr_get_meid(message, meid); rmr_get_src(message, src); switch(message->mtype){ case RIC_SUB_REQ: std::cout <<"*** Received message from src = " << src << " for gNodeB = " << meid << " of size = " << message->len << " and type = " << message->mtype << std::endl; e2ap_pdu_recv = 0; retval = asn_decode(0, ATS_ALIGNED_BASIC_PER, &asn_DEF_E2N_E2AP_PDU, (void**)&(e2ap_pdu_recv), message->payload, message->len); if(retval.code == RC_OK){ he.clear(); sub_req.get_fields(e2ap_pdu_recv->choice.initiatingMessage, he); } else{ std::cerr <<"Error decoding E2AP Subscription response PDU. Reason = " << strerror(errno) << std::endl; send_msg = false; goto finished_sub_req; } std::cout <<"==============================\nReceived Subscription Request with ID = " << he.get_request_id() << std::endl; //xer_fprint(stdout, &asn_DEF_E2AP_PDU, e2ap_pdu_recv); // get action type (we support only one action in subscription request currently .... actions = he.get_list(); action_type = (*actions)[0].get_type(); std::cout << "Action type in subscription request ID = "<< he.get_request_id() << " set to " << action_type << std::endl; // decode the event trigger ... { e2sm_event_trigger_helper event_trigger_data; event = 0; // used for decoding retval = asn_decode(0, ATS_ALIGNED_BASIC_PER, &asn_DEF_E2N_E2SM_gNB_X2_eventTriggerDefinition, (void**)&(event), (unsigned char *)he.get_event_def(), he.get_event_def_size()); if (retval.code != RC_OK){ std::cerr <<"Error decoding event trigger in subscription request. Reason = " << strerror(errno) << std::endl; send_msg = false; goto finished_sub_req; } //std::cout <<"++++++++++++++ EVENT TRIGGER ++++++++++++++++++++++++++++++++++" << std::endl; //xer_fprint(stdout, &asn_DEF_E2N_E2SM_gNB_X2_eventTriggerDefinition, event); //std::cout <<"++++++++++++++ EVENT TRIGGER ++++++++++++++++++++++++++++++++++" << std::endl; } // set up response object he_resp.set_request(he.get_request_id(), he.get_req_seq()); he_resp.set_function_id(he.get_function_id()); i = 0; // ideally should move all actions to not admitted if failed // but we ignore admitted list anyway when we set up the PDU // for now, just copy to both lists :) ... for(auto &e : *(he.get_list())){ he_resp.add_action(e.get_id()); he_resp.add_action(e.get_id(), 1, 2); i++; } mlen = RMR_BUFFER_SIZE; res = sub_resp.encode_e2ap_subscription_response(&message->payload[0], &mlen, he_resp, true); if (!res){ std::cerr << "Error encoding subscription response successful. Reason = " << sub_resp.get_error() << std::endl; send_msg = false; goto finished_sub_req; } message->mtype = RIC_SUB_RESP; subscription_active = true; finished_sub_req: ASN_STRUCT_FREE(asn_DEF_E2N_E2AP_PDU, e2ap_pdu_recv); ASN_STRUCT_FREE(asn_DEF_E2N_E2SM_gNB_X2_eventTriggerDefinition, event); if(send_msg){ message->len = mlen; // also set subscription id ? std::cout <<"Sending Subscription Response with RMR type " << message->mtype << " and size = " << message->len << std::endl; std::cout <<"======================================" << std::endl; return true; } else{ break; } case RIC_SUB_DEL_REQ: e2ap_pdu_recv = 0; retval = asn_decode(0, ATS_ALIGNED_BASIC_PER, &asn_DEF_E2N_E2AP_PDU, (void**)&(e2ap_pdu_recv), message->payload, message->len); if(retval.code == RC_OK){ he.clear(); sub_del_req.get_fields(e2ap_pdu_recv->choice.initiatingMessage, he); } else{ std::cerr <<"Error decoding E2AP Subscription Delete Request PDU. Reason = " << strerror(errno) << std::endl; send_msg = false; goto finished_sub_del; } std::cout <<"==============================\nReceived Subscription Delete Request with ID = " << he.get_request_id() << std::endl; xer_fprint(stdout, &asn_DEF_E2N_E2AP_PDU, e2ap_pdu_recv); std::cout <<"==============================\nReceived Subscription Delete Request with ID = " << he.get_request_id() << std::endl; // FILE *pfile; // pfile = fopen("subscription_delete.per", "wb"); // fwrite(message->payload, 1, message->len, pfile); // fclose(pfile); // set up response object std::cout <<"Generating response ...." << std::endl; he_resp.clear(); he_resp.set_request(he.get_request_id(), he.get_req_seq()); he_resp.set_function_id(he.get_function_id()); mlen = RMR_BUFFER_SIZE; res = sub_del_resp.encode_e2ap_subscription_delete_response(&message->payload[0], &mlen, he_resp, true); if (!res){ std::cerr << "Error encoding subscription delete response failure . Reason = " << sub_resp.get_error() << std::endl; send_msg = false; goto finished_sub_del; } finished_sub_del: ASN_STRUCT_FREE(asn_DEF_E2N_E2AP_PDU, e2ap_pdu_recv); if(send_msg){ message->mtype = RIC_SUB_DEL_RESP; message->len = mlen; subscription_active = false; return true; } else{ break; } case RIC_CONTROL_REQ: num_controls ++; e2ap_pdu_recv = 0; retval = asn_decode(0,ATS_ALIGNED_BASIC_PER, &asn_DEF_E2N_E2AP_PDU, (void**)&(e2ap_pdu_recv), message->payload, message->len); if(retval.code != RC_OK){ std::cerr <<"Error decoding RIC Control Request" << std::endl; goto finished_ctrl; } if (verbose_flag){ std::cout <<"++++++++ RECEIVED CONTROL REQUEST +++++++++++++++++++++++++++++" << std::endl; xer_fprint(stdout, &asn_DEF_E2N_E2AP_PDU, e2ap_pdu_recv); } res = control_req.get_fields(e2ap_pdu_recv->choice.initiatingMessage, control_data); if(!res){ std::cout <<"Error getting data from E2AP control request" << std::endl; break; } // Decode the X2AP PDU : directly embedded in Control Msg IE x2ap_pdu_recv = 0; retval = asn_decode(0, ATS_ALIGNED_BASIC_PER, &asn_DEF_X2N_X2AP_PDU, (void **)&x2ap_pdu_recv, control_data.control_msg, control_data.control_msg_size ); if (retval.code != RC_OK){ std::cerr <<"Error decoding X2AP PDU in control request message of size " << control_data.control_msg_size << std::endl; num_errors ++; goto finished_ctrl; } if(verbose_flag){ xer_fprint(stdout, &asn_DEF_X2N_X2AP_PDU, x2ap_pdu_recv); std::cout <<"++++++++ RECEIVED CONTROL REQUEST +++++++++++++++++++++++++++++" << std::endl; } if(x2ap_pdu_recv->present == X2N_X2AP_PDU_PR_successfulOutcome && x2ap_pdu_recv->choice.successfulOutcome->procedureCode == X2N_ProcedureCode_id_sgNBAdditionPreparation && x2ap_pdu_recv->choice.successfulOutcome->value.present == X2N_SuccessfulOutcome__value_PR_SgNBAdditionRequestAcknowledge ){ num_accepts ++; } else if ( x2ap_pdu_recv->present == X2N_X2AP_PDU_PR_unsuccessfulOutcome && x2ap_pdu_recv->choice.unsuccessfulOutcome->procedureCode == X2N_ProcedureCode_id_sgNBAdditionPreparation && x2ap_pdu_recv->choice.unsuccessfulOutcome->value.present == X2N_UnsuccessfulOutcome__value_PR_SgNBAdditionRequestReject ){ num_rejects ++; } else{ std::cerr <<"Unknown X2AP PDU : message type = " << x2ap_pdu_recv->present << std::endl; } // generate control ack ? // control_data.control_status = 0; // mlen = RMR_BUFFER_SIZE; // res = control_resp.encode_e2ap_control_response(message->payload, &mlen, control_data, true); // if (!res){ // std::cerr <<"Error encoding control response ack. Reason = " << control_resp.get_error() << std::endl; // } finished_ctrl: ASN_STRUCT_FREE(asn_DEF_E2N_E2AP_PDU, e2ap_pdu_recv); ASN_STRUCT_FREE(asn_DEF_X2N_X2AP_PDU, x2ap_pdu_recv); break; default: std::cerr <<"Error ! Unknown RMR message of type " << message->mtype << " received" << std::endl; break; } return false; }; int main(int argc, char *argv[]){ char name[128] = "mock_subscription_mgr"; char port[16] = "tcp:4591"; double rate = 1; // default rate to send at int verbose_flag = 0; // Parse command line options static struct option long_options[] = { /* Thse options require arguments */ {"name", required_argument, 0, 'n'}, {"port", required_argument, 0, 'p'}, {"rate", required_argument, 0, 'r'}, {"verbose", no_argument, &verbose_flag, 1}, }; while(1) { int option_index = 0; char c = getopt_long(argc, argv, "n:p:a:r:", long_options, &option_index); if(c == -1){ break; } switch(c) { case 0: /* An option flag was set. Do nothing for now */ break; case 'n': strcpy(name, optarg); break; case 'p': strcpy(port, optarg); break; case 'r': rate = atof(optarg); if (rate < 0){ std::cerr <<"Error : Transmit rate must be >= 0/sec" << std::endl; exit(-1); } if (rate > 1000){ std::cerr <<"Error : maximum allowed rate = 1000/sec" << std::endl; exit(-1); } break; case 'h': usage(argv[0]); exit(0); default: std::cout <<"Error ! " << std::endl; usage(argv[0]); exit(1); } }; int log_level = MDCLOG_WARN; if (verbose_flag){ log_level = MDCLOG_INFO; } init_logger(name, static_cast(log_level)); XaPP my_xapp = XaPP(name, port, 16384); my_xapp.StartThread(Message_Handler); e2sm_indication e2sm_indication_obj; bool res; FILE *pfile; //==== e2sm indication header : just some e2sm_header_helper indication_header; // random fill for now indication_header.egNB_id="hello"; indication_header.plmn_id="there"; indication_header.interface_direction = 1; indication_header.egNB_id_type = 2; unsigned char buf_header[32]; size_t buf_header_size = 32; res = e2sm_indication_obj.encode_indication_header(&buf_header[0], &buf_header_size, indication_header); if(!res){ std::cout <<"Error encoding indication header. Reason = " << e2sm_indication_obj.get_error() << std::endl; exit(-1); } //====== load the various x2ap requests unsigned char ** x2ap_bufs = (unsigned char **)calloc(x2ap_files.size(), sizeof(unsigned char *)); size_t * x2ap_buf_sizes = (size_t *)calloc(x2ap_files.size(), sizeof(size_t)); assert(x2ap_bufs != 0); assert(x2ap_buf_sizes != 0); for(int i = 0; i < x2ap_files.size(); i++){ std::ifstream in(x2ap_files[i], std::ifstream::ate | std::ifstream::binary); x2ap_buf_sizes[i] = in.tellg(); std::cout <<"Assigned " << x2ap_buf_sizes[i] << " bytes of memory for file " << x2ap_files[i] << std::endl; x2ap_bufs[i] = (unsigned char *)calloc(x2ap_buf_sizes[i], sizeof(unsigned char)); assert(x2ap_bufs[i] != 0); pfile = fopen(x2ap_files[i].c_str(), "r"); if(pfile == NULL){ std::cerr <<"Error ! Could not find test per file " << x2ap_files[i] << std::endl; exit(-1); } fread((char *)x2ap_bufs[i], sizeof(char), x2ap_buf_sizes[i], pfile); fclose(pfile); } //==== e2ap indication for generated x2ap pdus ric_indication_helper dinput ; dinput.action_id = 100; dinput.func_id = 10; dinput.indication_sn = 100; dinput.req_id = 6; dinput.req_seq_no = 11; /* encoding pdu put here */ size_t data_size = 16384; unsigned char data[data_size]; ric_indication indication_pdu; dinput.indication_header = buf_header; dinput.indication_header_size = buf_header_size; //Register signal handler to stop signal(SIGINT, EndProgram); signal(SIGTERM, EndProgram); unsigned long int interval = 0; if (rate > 0){ interval = 1000.0/rate; } else{ interval = 10; } auto start_time = std::chrono::steady_clock::now(); int count = 0; int packet_index = 0; while(RunProgram){ if ( subscription_active && rate > 0 ){ my_xapp.Send(RIC_INDICATION, data_size, data); num_indications ++; // choose packet to encode dinput.indication_msg = x2ap_bufs[packet_index]; dinput.indication_msg_size = x2ap_buf_sizes[packet_index]; dinput.indication_type = 1; // for now always ask for control back res = indication_pdu.encode_e2ap_indication(&data[0], &data_size, dinput); if (!res){ std::cout <<"Error encoding E2AP Indication PDU. Reason = " << indication_pdu.get_error().c_str() << std::endl; exit(-1); } packet_index ++; packet_index = packet_index % x2ap_files.size(); } std::this_thread::sleep_for(std::chrono::milliseconds(interval)); count ++; } auto end_time = std::chrono::steady_clock::now(); double duration = std::chrono::duration_cast(end_time - start_time).count(); std::cout <<"Number of SgNB Addition Request (in E2AP Indication) sent = " << num_indications << std::endl; std::cout <<"Number of control responses received = " << num_controls << std::endl; std::cout <<"Number of SgNB Addition Acknowledges = " << num_accepts << std::endl; std::cout <<"NUmber of SgNB Addition Rejects = " << num_rejects << std::endl; std::cout <<"Number of E2AP control requests we could not decode = " << num_errors << std::endl; std::cout <<"Duration = " << duration << " E2AP Indication Tx Rate = " << num_indications/duration << std::endl; my_xapp.Stop(); std::cout <<"Stopped RMR thread .." << std::endl; return 0; };