/*******************************************************************************
OpenAirInterface
Copyright(c) 1999 - 2014 Eurecom
OpenAirInterface is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OpenAirInterface is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OpenAirInterface.The full GNU General Public License is
included in this distribution in the file called "COPYING". If not,
see .
Contact Information
OpenAirInterface Admin: openair_admin@eurecom.fr
OpenAirInterface Tech : openair_tech@eurecom.fr
OpenAirInterface Dev : openair4g-devel@eurecom.fr
Address : Eurecom, Compus SophiaTech 450, route des chappes, 06451 Biot, France.
*******************************************************************************/
/*! \file pdcp_netlink.c
* \brief pdcp communication with linux IP interface,
* have a look at http://man7.org/linux/man-pages/man7/netlink.7.html for netlink.
* Read operation from netlink should be achieved in an asynchronous way to avoid
* subframe overload, netlink congestion...
* \author Sebastien ROUX
* \date 2013
* \version 0.1
* @ingroup pdcp
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
/* Bugfix for version of GCC = 4.4.3 (Ubuntu 10.04) */
#if GCC_VERSION <= 40403
# include
#endif
#include
#include "assertions.h"
#include "queue.h"
#include "liblfds611.h"
#include "UTIL/LOG/log.h"
#include "UTIL/OCG/OCG.h"
#include "UTIL/OCG/OCG_extern.h"
#include "LAYER2/MAC/extern.h"
#include "pdcp.h"
#include "pdcp_primitives.h"
#define PDCP_QUEUE_NB_ELEMENTS 200
extern char nl_rx_buf[NL_MAX_PAYLOAD];
extern struct nlmsghdr *nas_nlh_rx;
extern struct iovec nas_iov_rx;
extern int nas_sock_fd;
extern struct msghdr nas_msg_rx;
static pthread_t pdcp_netlink_thread;
/* We use lock-free queues between the User-plane driver running in kernel-space
* and the corresponding entity in User-space.
* one queue for eNBs (index 0)/one queue for UEs (index 1)
*/
static struct lfds611_queue_state **pdcp_netlink_queue_enb = NULL;
static struct lfds611_queue_state **pdcp_netlink_queue_ue = NULL;
static uint32_t *pdcp_netlink_nb_element_enb = NULL;
static uint32_t *pdcp_netlink_nb_element_ue = NULL;
time_stats_t ip_pdcp_stats_tmp;
static void *pdcp_netlink_thread_fct(void *arg);
int pdcp_netlink_init(void) {
int i;
int nb_inst_enb;
int nb_inst_ue;
pthread_attr_t attr;
struct sched_param sched_param;
reset_meas(&ip_pdcp_stats_tmp);
#if defined(USER_MODE) && defined(OAI_EMU)
nb_inst_enb = oai_emulation.info.nb_enb_local;
nb_inst_ue = oai_emulation.info.nb_ue_local;
#else
nb_inst_enb = 1;
nb_inst_ue = 1;
#endif
#if defined(LINK_PDCP_TO_GTPV1U)
nb_inst_enb = 0;
LOG_I(PDCP, "[NETLINK] Creating 0 queues for eNB Netlink -> PDCP communication\n");
#else
pdcp_netlink_queue_enb = calloc(nb_inst_enb, sizeof(struct lfds611_queue_state*));
pdcp_netlink_nb_element_enb = malloc(nb_inst_enb * sizeof(uint32_t));
LOG_I(PDCP, "[NETLINK] Creating %d queues for eNB Netlink -> PDCP communication\n", nb_inst_enb);
for (i = 0; i < nb_inst_enb; i++) {
pdcp_netlink_nb_element_enb[i] = 0;
if (lfds611_queue_new(&pdcp_netlink_queue_enb[i], PDCP_QUEUE_NB_ELEMENTS) < 0) {
LOG_E(PDCP, "Failed to create new FIFO for eNB Netlink -> PDCP communcation instance %d\n", i);
exit(EXIT_FAILURE);
}
}
#endif
if (nb_inst_ue > 0) {
pdcp_netlink_queue_ue = calloc(nb_inst_ue, sizeof(struct lfds611_queue_state*));
pdcp_netlink_nb_element_ue = malloc(nb_inst_ue * sizeof(uint32_t));
LOG_I(PDCP, "[NETLINK] Creating %d queues for UE Netlink -> PDCP communication\n", nb_inst_ue);
for (i = 0; i < nb_inst_ue; i++) {
pdcp_netlink_nb_element_ue[i] = 0;
if (lfds611_queue_new(&pdcp_netlink_queue_ue[i], PDCP_QUEUE_NB_ELEMENTS) < 0) {
LOG_E(PDCP, "Failed to create new FIFO for UE Netlink -> PDCP communcation instance %d\n", i);
exit(EXIT_FAILURE);
}
}
}
if ((nb_inst_ue + nb_inst_enb) > 0) {
if (pthread_attr_init(&attr) != 0) {
LOG_E(PDCP, "[NETLINK]Failed to initialize pthread attribute for Netlink -> PDCP communication (%d:%s)\n",
errno, strerror(errno));
exit(EXIT_FAILURE);
}
sched_param.sched_priority = 10;
pthread_attr_setschedpolicy(&attr, SCHED_RR);
pthread_attr_setschedparam(&attr, &sched_param);
/* Create one thread that fetchs packets from the netlink.
* When the netlink fifo is full, packets are silently dropped, this behaviour
* should be avoided if we want a reliable link.
*/
if (pthread_create(&pdcp_netlink_thread, &attr, pdcp_netlink_thread_fct, NULL) != 0) {
LOG_E(PDCP, "[NETLINK]Failed to create new thread for Netlink/PDCP communcation (%d:%s)\n",
errno, strerror(errno));
exit(EXIT_FAILURE);
}
}
return 0;
}
int pdcp_netlink_dequeue_element(
module_id_t enb_mod_idP,
module_id_t ue_mod_idP,
eNB_flag_t eNB_flagP,
struct pdcp_netlink_element_s **data_ppP)
{
int ret = 0;
if (eNB_flagP) {
ret = lfds611_queue_dequeue(pdcp_netlink_queue_enb[enb_mod_idP], (void **)data_ppP);
if (ret != 0) {
LOG_D(PDCP,"[NETLINK]De-queueing packet for eNB instance %d\n", enb_mod_idP);
}
} else {
ret = lfds611_queue_dequeue(pdcp_netlink_queue_ue[ue_mod_idP], (void **)data_ppP);
if (ret != 0) {
LOG_D(PDCP, "[NETLINK]De-queueing packet for UE instance %d\n", ue_mod_idP);
}
}
return ret;
}
static
void *pdcp_netlink_thread_fct(void *arg) {
int len = 0;
struct pdcp_netlink_element_s *new_data_p = NULL;
uint8_t pdcp_thread_read_state ;
eNB_flag_t eNB_flag = 0;
pdcp_thread_read_state = 0;
memset(nl_rx_buf, 0, NL_MAX_PAYLOAD);
LOG_I(PDCP, "[NETLINK_THREAD] binding to fd %d\n",nas_sock_fd);
while (1) {
len = recvmsg(nas_sock_fd, &nas_msg_rx, 0);
if (len == 0) {
/* Other peer (kernel) has performed an orderly shutdown
*/
LOG_E(PDCP, "[NETLINK_THREAD] Kernel module has closed the netlink\n");
exit(EXIT_FAILURE);
} else if (len < 0) {
/* There was an error */
LOG_E(PDCP, "[NETLINK_THREAD] An error occured while reading netlink (%d:%s)\n",
errno, strerror(errno));
exit(EXIT_FAILURE);
} else {
/* Normal read.
* NOTE: netlink messages can be assembled to form a multipart message
*/
for (nas_nlh_rx = (struct nlmsghdr *) nl_rx_buf;
NLMSG_OK(nas_nlh_rx, (unsigned int)len);
nas_nlh_rx = NLMSG_NEXT (nas_nlh_rx, len)) {
start_meas(&ip_pdcp_stats_tmp);
/* There is no need to check for nlmsg_type because
* the header is not set in our drivers.
*/
if (pdcp_thread_read_state == 0) {
new_data_p = malloc(sizeof(struct pdcp_netlink_element_s));
if (nas_nlh_rx->nlmsg_len == sizeof (pdcp_data_req_header_t) + sizeof(struct nlmsghdr)) {
pdcp_thread_read_state = 1;
memcpy((void *)&new_data_p->pdcp_read_header, (void *)NLMSG_DATA(nas_nlh_rx), sizeof(pdcp_data_req_header_t));
LOG_I(PDCP, "[NETLINK_THREAD] RX pdcp_data_req_header_t inst %u, "
"rb_id %u data_size %d\n",
new_data_p->pdcp_read_header.inst,
new_data_p->pdcp_read_header.rb_id,
new_data_p->pdcp_read_header.data_size);
} else {
LOG_E(PDCP, "[NETLINK_THREAD] WRONG size %d should be sizeof "
"%d ((pdcp_data_req_header_t) + sizeof(struct nlmsghdr))\n",
nas_nlh_rx->nlmsg_len,
sizeof (pdcp_data_req_header_t) + sizeof(struct nlmsghdr));
}
} else {
pdcp_thread_read_state = 0;
#ifdef OAI_EMU
if (new_data_p->pdcp_read_header.inst >= oai_emulation.info.nb_enb_local) {
new_data_p->pdcp_read_header.inst = new_data_p->pdcp_read_header.inst
- oai_emulation.info.nb_enb_local +
+ oai_emulation.info.first_ue_local;
eNB_flag = 0;
} else {
new_data_p->pdcp_read_header.inst = new_data_p->pdcp_read_header.inst
+ oai_emulation.info.first_enb_local;
eNB_flag = 1;
}
#else
new_data_p->pdcp_read_header.inst = 0;
#endif
new_data_p->data = malloc(sizeof(uint8_t) * new_data_p->pdcp_read_header.data_size);
/* Copy the data */
memcpy(new_data_p->data, NLMSG_DATA(nas_nlh_rx), new_data_p->pdcp_read_header.data_size);
if (eNB_flag) {
if (pdcp_netlink_nb_element_enb[new_data_p->pdcp_read_header.inst]
> PDCP_QUEUE_NB_ELEMENTS) {
LOG_E(PDCP, "[NETLINK_THREAD][Inst %02x] We reached maximum number of elements in eNB pdcp queue (%d)\n",
new_data_p->pdcp_read_header.inst, pdcp_netlink_nb_element_enb);
}
LOG_D(PDCP, "[NETLINK_THREAD] En-queueing packet for eNB instance %d\n", new_data_p->pdcp_read_header.inst);
/* Enqueue the element in the right queue */
lfds611_queue_guaranteed_enqueue(pdcp_netlink_queue_enb[new_data_p->pdcp_read_header.inst], new_data_p);
stop_meas(&ip_pdcp_stats_tmp);
copy_meas(&eNB_pdcp_stats[new_data_p->pdcp_read_header.inst].pdcp_ip,&ip_pdcp_stats_tmp);
} else {
if (pdcp_netlink_nb_element_ue[new_data_p->pdcp_read_header.inst]
> PDCP_QUEUE_NB_ELEMENTS) {
LOG_E(PDCP, "[NETLINK_THREAD][Inst %02x] We reached maximum number of elements in UE pdcp queue (%d)\n",
new_data_p->pdcp_read_header.inst, pdcp_netlink_nb_element_ue);
}
LOG_D(PDCP, "[NETLINK_THREAD] En-queueing packet for UE instance %d\n", new_data_p->pdcp_read_header.inst);
/* Enqueue the element in the right queue */
lfds611_queue_guaranteed_enqueue(pdcp_netlink_queue_ue[new_data_p->pdcp_read_header.inst], new_data_p);
stop_meas(&ip_pdcp_stats_tmp);
copy_meas(&UE_pdcp_stats[new_data_p->pdcp_read_header.inst].pdcp_ip,&ip_pdcp_stats_tmp);
}
}
}
}
}
return NULL;
}