| /* |
| * Copyright (c) 2003-2018, Great Software Laboratory Pvt. Ltd. |
| * Copyright (c) 2017 Intel Corporation |
| * Copyright (c) 2019, Infosys Ltd. |
| * |
| * 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. |
| */ |
| |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <string.h> |
| #include <pthread.h> |
| #include <arpa/inet.h> |
| #include <errno.h> |
| |
| #include "options.h" |
| #include "ipc_api.h" |
| #include "main.h" |
| #include "s1ap.h" |
| #include "s1ap_config.h" |
| #include "sctp_conn.h" |
| #include "s1ap_structs.h" |
| #include "message_queues.h" |
| #include "thread_pool.h" |
| #include "tpool_queue.h" |
| #include "snow_3g.h" |
| #include "f9.h" |
| #include "err_codes.h" |
| #include <sys/ioctl.h> |
| #include <sys/types.h> |
| |
| /*Global and externs **/ |
| extern s1ap_config g_s1ap_cfg; |
| pthread_t s1ap_iam_t; |
| |
| int g_enb_fd = 0; |
| int g_sctp_fd = 0; |
| struct thread_pool *g_tpool; |
| struct thread_pool *g_tpool_tipc_reader; |
| |
| ipc_handle ipc_S1ap_Hndl; |
| |
| ipc_handle ipc_tipc_reader; |
| |
| ipc_handle ipcHndl_sctpsend_reader; |
| ipc_handle ipcHndl_sctpsend_writer; |
| |
| pthread_t tipcReader_t; |
| pthread_t acceptSctp_t; |
| |
| struct time_stat g_attach_stats[65535]; |
| /**End: global and externs**/ |
| |
| extern char processName[255]; |
| extern int pid; |
| |
| extern void |
| handle_mmeapp_message(void * data); |
| |
| #define MAX_ENB 10 |
| #define BUFFER_LEN 1024 |
| |
| /** |
| * @brief Decode int value from the byte array received in the s1ap incoming |
| * packet. |
| * @param[in] bytes - Array of bytes in packet |
| * @param[in] len - Length of the bytes array from which to extract the int |
| * @return Integer value extracted out of bytes array. 0 if failed. |
| */ |
| char *msg_to_hex_str(const char *msg, int len, char **buffer) { |
| |
| char chars[]= "0123456789abcdef"; |
| char *local; |
| |
| if (!len) |
| return NULL; |
| |
| if (!((*buffer) = (char *)malloc(2 * len + 1))) |
| return NULL; |
| |
| local = *buffer; |
| for (int i = 0; i < len; i++) { |
| local[2 * i] = chars[(msg[i] >> 4) & 0x0F]; |
| local[2 * i + 1] = chars[(msg[i]) & 0x0F]; |
| } |
| local[2 * len] = '\0'; |
| |
| return local; |
| } |
| |
| unsigned short get_length(char **msg) { |
| /* get length and consume msg bytes accordingly */ |
| |
| unsigned short ie_len = 0; |
| |
| unsigned char val = ((*msg)[0] & 0xc0) >> 6; |
| if(val == 2) { |
| //log_msg(LOG_INFO, "length more than 128\n"); |
| unsigned short higher = (unsigned char)(*msg)[0] & 0x3f; |
| (*msg)++; |
| unsigned short lower = (unsigned char)(*msg)[0]; |
| ie_len = (higher << 8) | lower; |
| } else { |
| //log_msg(LOG_INFO, "length less than 128\n"); |
| ie_len = (unsigned short)(*msg)[0]; |
| } |
| (*msg)++; |
| return ie_len; |
| } |
| |
| int |
| decode_int_val(unsigned char *bytes, short len) |
| { |
| switch(len) { |
| case 1: |
| case 2: |
| return (bytes[1] & 0xff); |
| |
| case 3: |
| return (bytes[2] & 0xff) | |
| (0xff00 & ((unsigned short)(bytes[1] << 8))); |
| |
| case 4: |
| return (((((unsigned int)(bytes[1]) << 16) & 0xffff00) | |
| ((unsigned int)(bytes[2]) << 8)) & 0xffff00) | |
| ((unsigned int)(bytes[3]) & 0xff); |
| }; |
| return 0; |
| } |
| |
| /** |
| * @brief Pack short number value in to the buffer |
| * @param[out] buffer to fill the value |
| * @param[in] value to fill |
| * @return number of bytes filled in to the buffer |
| */ |
| int |
| copyU16(unsigned char *buffer, uint32_t val) |
| { |
| if (val < 255) { |
| buffer[0] = (val >> 8) & 0xFF; |
| buffer[1] = val & 0xFF; |
| return 2; |
| } else if (val < 65535) { |
| buffer[0] = 0x40; |
| buffer[1] = (val >> 8) & 0xFF; |
| buffer[2] = val & 0xFF; |
| return 3; |
| } else { |
| buffer[0] = 0x80; |
| buffer[1] = (val >> 16) & 0xFF; |
| buffer[2] = (val >> 8) & 0xFF; |
| buffer[3] = val & 0xFF; |
| return 4; |
| } |
| } |
| |
| void |
| calculate_mac(uint8_t *int_key, uint32_t seq_no, uint8_t direction, |
| uint8_t bearer, uint8_t *data, uint16_t data_len, |
| uint8_t *mac) |
| { |
| uint8_t *out; |
| |
| out = f9(int_key, seq_no, bearer, direction, data, data_len * 8); |
| |
| memcpy(mac, out, MAC_SIZE); |
| |
| return; |
| } |
| |
| int |
| init_s1ap_workers() |
| { |
| pthread_attr_t attr; |
| |
| pthread_attr_init(&attr); |
| /* set the thread detach state */ |
| pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); |
| |
| pthread_attr_destroy(&attr); |
| return 0; |
| } |
| |
| /** |
| * @brief Thread to read and distribute sctp request received. Messages will |
| * be passed to s1ap handler function for further processing. |
| * @param[in] thread data |
| * @return thread return data |
| */ |
| void * |
| accept_sctp(void *data) |
| { |
| log_msg(LOG_INFO, "accept connection on sctp sock\n"); |
| int new_socket = 0; |
| int activity = 0; |
| int i = 0; |
| int valread = 0; |
| int sd = 0; |
| int max_sd = 0; |
| int enb_socket[MAX_ENB] = {0}; |
| unsigned char buffer[BUFFER_LEN] = {0}; |
| |
| while(1) { |
| log_msg(LOG_INFO, "WHILE LOOP\n"); |
| |
| fd_set readfds; |
| memset (buffer, 0, BUFFER_LEN); |
| valread = 0; |
| |
| FD_ZERO(&readfds); |
| FD_SET(g_sctp_fd, &readfds); |
| max_sd = g_sctp_fd; |
| |
| for (i = 0; i < MAX_ENB; i++) { |
| sd = enb_socket[i]; |
| |
| if (sd > 0) { |
| FD_SET(sd, &readfds); |
| } |
| |
| if (sd > max_sd) { |
| max_sd = sd; |
| } |
| } |
| |
| activity = select(max_sd + 1, &readfds, NULL, NULL, NULL); |
| |
| if ((activity < 0) && (errno != EINTR)) { |
| log_msg(LOG_ERROR, "select error.\n"); |
| } |
| |
| if (FD_ISSET(g_sctp_fd, &readfds)) { |
| |
| if ((new_socket = accept_sctp_socket(g_sctp_fd)) == -1) { |
| log_msg(LOG_ERROR, "Error in accept on sctp socket.\n"); |
| } |
| |
| log_msg(LOG_INFO, "New Connection Established\n."); |
| |
| for (i = 0; i < MAX_ENB; i++) { |
| |
| if( enb_socket[i] == 0 ) { |
| |
| enb_socket[i] = new_socket; |
| g_enb_fd = new_socket; |
| log_msg(LOG_INFO, "Adding to list of sockets at %d value %d\n" , i, new_socket); |
| |
| break; |
| } |
| } |
| } |
| |
| for (i = 0; i < MAX_ENB; i++) { |
| |
| sd = enb_socket[i]; |
| |
| if (FD_ISSET(sd, &readfds)) { |
| |
| if ((valread = recv_sctp_msg(sd, buffer, SCTP_BUF_SIZE)) == 0) { |
| |
| log_msg(LOG_INFO, "Host Disconnected\n"); |
| close(sd); |
| enb_socket[i] = 0; |
| |
| } else { |
| |
| unsigned char *tmpBuf = (unsigned char *) |
| malloc(sizeof(char) * valread + (2*sizeof(int)) ); |
| memcpy(tmpBuf, &sd, sizeof(sd)); |
| memcpy(tmpBuf + sizeof(int), &valread, sizeof(int)); |
| memcpy(tmpBuf + (2*sizeof(int)), buffer, valread); |
| //tmpBuf[len] = '\0'; |
| log_msg(LOG_INFO, "SCTP Received msg len : %d on fd %d\n", |
| valread, sd); |
| insert_job(g_tpool, handle_s1ap_message, tmpBuf); |
| |
| } |
| } |
| } |
| |
| }/* while */ |
| |
| return NULL; |
| } |
| |
| void * tipc_msg_handler() |
| { |
| int bytesRead = 0; |
| while (1) |
| { |
| unsigned char buffer[BUFFER_LEN] = {0}; |
| if ((bytesRead = read_tipc_msg(ipc_tipc_reader, buffer,BUFFER_LEN)) > 0) |
| { |
| unsigned char *tmpBuf = (unsigned char *) malloc(sizeof(char) * bytesRead); |
| memcpy(tmpBuf, buffer, bytesRead); |
| log_msg(LOG_INFO, "S1AP message received from mme-app"); |
| insert_job(g_tpool_tipc_reader, handle_mmeapp_message, tmpBuf); |
| } |
| } |
| } |
| |
| /** |
| * @brief Initialize sctp socket connection for eNB |
| * @return Error code SUCCESS or FAIL |
| */ |
| int |
| init_sctp() |
| { |
| log_msg(LOG_INFO, "Create sctp sock, ip:%d, port:%d\n", |
| g_s1ap_cfg.s1ap_local_ip, g_s1ap_cfg.sctp_port); |
| /*Create MME sctp listned socket*/ |
| g_sctp_fd = create_sctp_socket(g_s1ap_cfg.s1ap_local_ip, |
| g_s1ap_cfg.sctp_port); |
| |
| if (g_sctp_fd == -1) { |
| log_msg(LOG_ERROR, "Error in creating sctp socket. \n"); |
| return -E_FAIL; |
| } |
| |
| pthread_attr_t attr; |
| |
| pthread_attr_init(&attr); |
| /* set the thread detach state */ |
| pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); |
| |
| int ret = pthread_create(&acceptSctp_t, &attr,&accept_sctp, NULL); |
| if(ret < 0) { |
| log_msg(LOG_ERROR,"SCTP ACCEPTS THREAD FAILED\n"); |
| return -E_FAIL; |
| } |
| |
| pthread_attr_destroy(&attr); |
| return SUCCESS; |
| } |
| |
| |
| /** |
| * @brief Init all writer IPC channels for s1ap program |
| * @return Success/fail error code |
| */ |
| int |
| init_writer_ipc() |
| { |
| if ((ipc_S1ap_Hndl = create_tipc_socket()) <= 0) |
| return -E_FAIL; |
| |
| log_msg(LOG_INFO, "Writer IPCs initialized\n"); |
| |
| return SUCCESS; |
| } |
| |
| /** |
| * @brief Init handlers to process messages coming from mme-app |
| * @return error code. |
| */ |
| int |
| start_mme_resp_handlers() |
| { |
| if ((ipc_tipc_reader = create_tipc_socket()) <= 0) |
| { |
| log_msg(LOG_ERROR, "Failed to create IPC Reader tipc socket \n"); |
| return -E_FAIL; |
| } |
| if ( bind_tipc_socket(ipc_tipc_reader, s1apAppInstanceNum_c) != 1) |
| { |
| log_msg(LOG_ERROR, "Failed to bind IPC Reader tipc socket \n"); |
| return -E_FAIL; |
| } |
| |
| /* Initialize thread pool for mme-app messages */ |
| g_tpool_tipc_reader = thread_pool_new(5); |
| |
| if (g_tpool_tipc_reader == NULL) { |
| log_msg(LOG_ERROR, "Error in creating thread pool. \n"); |
| return -E_FAIL_INIT; |
| } |
| |
| log_msg(LOG_INFO, "S1AP Listener theadpool initalized.\n"); |
| |
| // thread to read incoming ipc messages from tipc socket |
| pthread_attr_t attr; |
| pthread_attr_init(&attr); |
| pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); |
| pthread_create(&tipcReader_t, &attr, &tipc_msg_handler, NULL); |
| pthread_attr_destroy(&attr); |
| |
| |
| return SUCCESS; |
| } |
| |
| /** |
| *@Thread to handle send messages to sctp thread |
| * NOT user currently. |
| */ |
| void * |
| sctp_write(void *args) |
| { |
| while(1) { |
| |
| sleep(10); |
| |
| } |
| } |
| |
| int |
| start_sctp_threads() |
| { |
| pthread_t sctp_writer; |
| pthread_attr_t attr; |
| int res = SUCCESS; |
| |
| res = pthread_attr_init(&attr); |
| if (res != 0) |
| return -E_FAIL; |
| |
| res = pthread_attr_setdetachstate(&attr, |
| PTHREAD_CREATE_DETACHED); |
| if (res != 0) |
| return -E_FAIL; |
| |
| res = pthread_create(&sctp_writer, &attr, |
| sctp_write, NULL); |
| if (res != 0) { |
| log_msg(LOG_ERROR, "Error in creating sctp writer thread.\n"); |
| pthread_attr_destroy(&attr); |
| return -E_FAIL; |
| } |
| |
| pthread_attr_destroy(&attr); |
| |
| return SUCCESS; |
| } |
| |
| /** |
| * @brief - main entry point for s1ap application. Read json config, |
| * start all the handlers. Connect with configured enb |
| */ |
| int |
| main(int argc, char **argv) |
| { |
| memcpy (processName, argv[0], strlen(argv[0])); |
| pid = getpid(); |
| |
| parse_args(argc, argv); |
| |
| init_parser("conf/s1ap.json"); |
| parse_s1ap_conf(); |
| |
| if (init_writer_ipc() != SUCCESS) { |
| log_msg(LOG_ERROR, "Error in initializing writer ipc.\n"); |
| return -E_FAIL_INIT; |
| } |
| |
| if (start_mme_resp_handlers() != SUCCESS) { |
| log_msg(LOG_ERROR, "Error in starting mme response handlers.\n"); |
| return -E_FAIL_INIT; |
| } |
| |
| /* Initialize thread pool for sctp request parsers */ |
| g_tpool = thread_pool_new(THREADPOOL_SIZE); |
| |
| if (g_tpool == NULL) { |
| log_msg(LOG_ERROR, "Error in creating thread pool. \n"); |
| return -E_FAIL_INIT; |
| } |
| log_msg(LOG_INFO, "S1AP Listener theadpool initalized.\n"); |
| |
| |
| if (init_sctp() != SUCCESS) { |
| log_msg(LOG_ERROR, "Error in initializing sctp server.\n"); |
| return -E_FAIL_INIT; |
| } |
| |
| log_msg(LOG_INFO, "Connection accespted from enb \n"); |
| |
| if (start_sctp_threads() != SUCCESS) { |
| log_msg(LOG_ERROR, "Error in creating sctp reader/writer thread.\n"); |
| return -E_FAIL_INIT; |
| } |
| |
| log_msg(LOG_INFO, "sctp reader/writer thread started.\n"); |
| |
| |
| while (1) { |
| sleep(10); |
| } |
| |
| return SUCCESS; |
| } |