blob: 8948c3596a6ecbc00d324755c916c886f8f63714 [file] [log] [blame]
/*
* 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;
}