mirror of https://github.com/pvnis/srsRAN_4G.git
Merge branch 'embms_merge_final' into next
commit
cb15dee3d4
@ -0,0 +1,52 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2015 Software Radio Systems Limited
|
||||||
|
*
|
||||||
|
* \section LICENSE
|
||||||
|
*
|
||||||
|
* This file is part of the srsUE library.
|
||||||
|
*
|
||||||
|
* srsUE is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* srsUE 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* A copy of the GNU Affero General Public License can be found in
|
||||||
|
* the LICENSE file in the top-level directory of this distribution
|
||||||
|
* and at http://www.gnu.org/licenses/.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef GEN_MCH_TALBES_H
|
||||||
|
#define GEN_MCH_TALBES_H
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
* Common mch table generation - used in phch_common of UE and ENB for MBMS
|
||||||
|
*****************************************************************************/
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif // __cplusplus
|
||||||
|
|
||||||
|
|
||||||
|
void generate_frame_mch_table(uint8_t *table, uint8_t alloc);
|
||||||
|
void generate_mch_table(uint8_t *table, uint32_t sf_alloc, uint8_t num_frames);
|
||||||
|
void generate_mcch_table(uint8_t *table, uint32_t sf_alloc);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif // __cplusplus
|
||||||
|
|
||||||
|
|
||||||
|
#endif // SECURITY_H
|
@ -0,0 +1,62 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2015 Software Radio Systems Limited
|
||||||
|
*
|
||||||
|
* \section LICENSE
|
||||||
|
*
|
||||||
|
* This file is part of the srsUE library.
|
||||||
|
*
|
||||||
|
* srsUE is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* srsUE 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* A copy of the GNU Affero General Public License can be found in
|
||||||
|
* the LICENSE file in the top-level directory of this distribution
|
||||||
|
* and at http://www.gnu.org/licenses/.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "srslte/common/gen_mch_tables.h"
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
* Key Generation
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
void generate_frame_table(uint8_t *table, uint8_t alloc)
|
||||||
|
{
|
||||||
|
table[1] = (alloc >> 5) & 0x01;
|
||||||
|
table[2] = (alloc >> 4) & 0x01;
|
||||||
|
table[3] = (alloc >> 3) & 0x01;
|
||||||
|
table[6] = (alloc >> 2) & 0x01;
|
||||||
|
table[7] = (alloc >> 1) & 0x01;
|
||||||
|
table[8] = (alloc >> 0) & 0x01;
|
||||||
|
}
|
||||||
|
|
||||||
|
void generate_mch_table(uint8_t *table, uint32_t sf_alloc, uint8_t num_frames)
|
||||||
|
{
|
||||||
|
if(num_frames == 1){
|
||||||
|
uint8_t alloc = (sf_alloc) & 0x3F;
|
||||||
|
generate_frame_table(table, alloc);
|
||||||
|
} else if(num_frames == 4){
|
||||||
|
for(uint32_t j=0; j<4; j++){
|
||||||
|
uint8_t alloc = (sf_alloc >> 6*(3-j)) & 0x3F;
|
||||||
|
generate_frame_table(&table[j*10], alloc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void generate_mcch_table(uint8_t *table, uint32_t sf_alloc)
|
||||||
|
{
|
||||||
|
uint8_t alloc = (sf_alloc) & 0x3F;
|
||||||
|
generate_frame_table(table, alloc);
|
||||||
|
}
|
@ -0,0 +1,160 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2015 Software Radio Systems Limited
|
||||||
|
*
|
||||||
|
* \section LICENSE
|
||||||
|
*
|
||||||
|
* This file is part of the srsUE library.
|
||||||
|
*
|
||||||
|
* srsUE is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* srsUE 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* A copy of the GNU Affero General Public License can be found in
|
||||||
|
* the LICENSE file in the top-level directory of this distribution
|
||||||
|
* and at http://www.gnu.org/licenses/.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include "srslte/asn1/liblte_rrc.h"
|
||||||
|
#include "srslte/common/log_filter.h"
|
||||||
|
#include "srslte/phy/utils/bit.h"
|
||||||
|
|
||||||
|
void pack_test()
|
||||||
|
{
|
||||||
|
srslte::log_filter log1("RRC");
|
||||||
|
log1.set_level(srslte::LOG_LEVEL_DEBUG);
|
||||||
|
log1.set_hex_limit(1024);
|
||||||
|
|
||||||
|
uint32_t known_reference_len = 30;
|
||||||
|
uint8_t known_reference[256] = {0x0d, 0x8f, 0xdf, 0xff, 0xff, 0xff, 0xe2, 0x2f,
|
||||||
|
0xfc, 0x38, 0x5e, 0x61, 0xec, 0xa8, 0x00, 0x00,
|
||||||
|
0x02, 0x02, 0x10, 0x00, 0x20, 0x05, 0xe6, 0x1e,
|
||||||
|
0xca, 0x80, 0x00, 0x00, 0x40, 0x42};
|
||||||
|
|
||||||
|
LIBLTE_BYTE_MSG_STRUCT byte_buf;
|
||||||
|
LIBLTE_BIT_MSG_STRUCT bit_buf;
|
||||||
|
LIBLTE_RRC_MCCH_MSG_STRUCT mcch_msg;
|
||||||
|
|
||||||
|
mcch_msg.commonsf_allocpatternlist_r9_size = 2;
|
||||||
|
mcch_msg.commonsf_allocpatternlist_r9[0].radio_fr_alloc_offset = 4;
|
||||||
|
mcch_msg.commonsf_allocpatternlist_r9[0].radio_fr_alloc_period = LIBLTE_RRC_RADIO_FRAME_ALLOCATION_PERIOD_N32;
|
||||||
|
mcch_msg.commonsf_allocpatternlist_r9[0].subfr_alloc_num_frames = LIBLTE_RRC_SUBFRAME_ALLOCATION_NUM_FRAMES_ONE;
|
||||||
|
mcch_msg.commonsf_allocpatternlist_r9[0].subfr_alloc = 0x3F;
|
||||||
|
mcch_msg.commonsf_allocpatternlist_r9[1].radio_fr_alloc_offset = 7;
|
||||||
|
mcch_msg.commonsf_allocpatternlist_r9[1].radio_fr_alloc_period = LIBLTE_RRC_RADIO_FRAME_ALLOCATION_PERIOD_N8;
|
||||||
|
mcch_msg.commonsf_allocpatternlist_r9[1].subfr_alloc_num_frames = LIBLTE_RRC_SUBFRAME_ALLOCATION_NUM_FRAMES_FOUR;
|
||||||
|
mcch_msg.commonsf_allocpatternlist_r9[1].subfr_alloc = 0xFFFFFF;
|
||||||
|
|
||||||
|
mcch_msg.commonsf_allocperiod_r9 = LIBLTE_RRC_MBSFN_COMMON_SF_ALLOC_PERIOD_R9_RF256;
|
||||||
|
|
||||||
|
mcch_msg.pmch_infolist_r9_size = 2;
|
||||||
|
|
||||||
|
mcch_msg.pmch_infolist_r9[0].mbms_sessioninfolist_r9_size = 1;
|
||||||
|
mcch_msg.pmch_infolist_r9[0].mbms_sessioninfolist_r9[0].logicalchannelid_r9 = 1;
|
||||||
|
mcch_msg.pmch_infolist_r9[0].mbms_sessioninfolist_r9[0].sessionid_r9_present = true;
|
||||||
|
mcch_msg.pmch_infolist_r9[0].mbms_sessioninfolist_r9[0].sessionid_r9 = 1;
|
||||||
|
mcch_msg.pmch_infolist_r9[0].mbms_sessioninfolist_r9[0].tmgi_r9.plmn_id_explicit = true;
|
||||||
|
mcch_msg.pmch_infolist_r9[0].mbms_sessioninfolist_r9[0].tmgi_r9.plmn_id_r9.mcc = 0xF987;
|
||||||
|
mcch_msg.pmch_infolist_r9[0].mbms_sessioninfolist_r9[0].tmgi_r9.plmn_id_r9.mnc = 0xF654;
|
||||||
|
mcch_msg.pmch_infolist_r9[0].mbms_sessioninfolist_r9[0].tmgi_r9.serviceid_r9 = 1;
|
||||||
|
mcch_msg.pmch_infolist_r9[0].pmch_config_r9.datamcs_r9 = 16;
|
||||||
|
mcch_msg.pmch_infolist_r9[0].pmch_config_r9.mch_schedulingperiod_r9 = LIBLTE_RRC_MCH_SCHEDULING_PERIOD_R9_RF1024;
|
||||||
|
mcch_msg.pmch_infolist_r9[0].pmch_config_r9.sf_alloc_end_r9 = 1535;
|
||||||
|
|
||||||
|
mcch_msg.pmch_infolist_r9[1].mbms_sessioninfolist_r9_size = 1;
|
||||||
|
mcch_msg.pmch_infolist_r9[1].mbms_sessioninfolist_r9[0].logicalchannelid_r9 = 2;
|
||||||
|
mcch_msg.pmch_infolist_r9[1].mbms_sessioninfolist_r9[0].sessionid_r9_present = true;
|
||||||
|
mcch_msg.pmch_infolist_r9[1].mbms_sessioninfolist_r9[0].sessionid_r9 = 2;
|
||||||
|
mcch_msg.pmch_infolist_r9[1].mbms_sessioninfolist_r9[0].tmgi_r9.plmn_id_explicit = true;
|
||||||
|
mcch_msg.pmch_infolist_r9[1].mbms_sessioninfolist_r9[0].tmgi_r9.plmn_id_r9.mcc = 0xF987;
|
||||||
|
mcch_msg.pmch_infolist_r9[1].mbms_sessioninfolist_r9[0].tmgi_r9.plmn_id_r9.mnc = 0xF654;
|
||||||
|
mcch_msg.pmch_infolist_r9[1].mbms_sessioninfolist_r9[0].tmgi_r9.serviceid_r9 = 2;
|
||||||
|
mcch_msg.pmch_infolist_r9[1].pmch_config_r9.datamcs_r9 = 8;
|
||||||
|
mcch_msg.pmch_infolist_r9[1].pmch_config_r9.mch_schedulingperiod_r9 = LIBLTE_RRC_MCH_SCHEDULING_PERIOD_R9_RF8;
|
||||||
|
mcch_msg.pmch_infolist_r9[1].pmch_config_r9.sf_alloc_end_r9 = 0;
|
||||||
|
|
||||||
|
liblte_rrc_pack_mcch_msg(&mcch_msg, &bit_buf);
|
||||||
|
liblte_pack(bit_buf.msg, bit_buf.N_bits, byte_buf.msg);
|
||||||
|
byte_buf.N_bytes = (bit_buf.N_bits+7)/8;
|
||||||
|
|
||||||
|
//log1.info_hex(byte_buf.msg, byte_buf.N_bytes, "MCCH packed message:");
|
||||||
|
|
||||||
|
assert(byte_buf.N_bytes == known_reference_len);
|
||||||
|
for(uint32 i=0; i<known_reference_len; i++) {
|
||||||
|
assert(byte_buf.msg[i] == known_reference[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void unpack_test()
|
||||||
|
{
|
||||||
|
uint32_t known_reference_len = 30;
|
||||||
|
uint8_t known_reference[256] = {0x0d, 0x8f, 0xdf, 0xff, 0xff, 0xff, 0xe2, 0x2f,
|
||||||
|
0xfc, 0x38, 0x5e, 0x61, 0xec, 0xa8, 0x00, 0x00,
|
||||||
|
0x02, 0x02, 0x10, 0x00, 0x20, 0x05, 0xe6, 0x1e,
|
||||||
|
0xca, 0x80, 0x00, 0x00, 0x40, 0x42};
|
||||||
|
|
||||||
|
LIBLTE_BYTE_MSG_STRUCT byte_buf;
|
||||||
|
LIBLTE_BIT_MSG_STRUCT bit_buf;
|
||||||
|
LIBLTE_RRC_MCCH_MSG_STRUCT mcch_msg;
|
||||||
|
|
||||||
|
liblte_unpack(known_reference, known_reference_len, bit_buf.msg);
|
||||||
|
bit_buf.N_bits = known_reference_len*8;
|
||||||
|
liblte_rrc_unpack_mcch_msg(&bit_buf, &mcch_msg);
|
||||||
|
|
||||||
|
assert(mcch_msg.commonsf_allocpatternlist_r9_size == 2);
|
||||||
|
assert(mcch_msg.commonsf_allocpatternlist_r9[0].radio_fr_alloc_offset == 4);
|
||||||
|
assert(mcch_msg.commonsf_allocpatternlist_r9[0].radio_fr_alloc_period == LIBLTE_RRC_RADIO_FRAME_ALLOCATION_PERIOD_N32);
|
||||||
|
assert(mcch_msg.commonsf_allocpatternlist_r9[0].subfr_alloc_num_frames == LIBLTE_RRC_SUBFRAME_ALLOCATION_NUM_FRAMES_ONE);
|
||||||
|
assert(mcch_msg.commonsf_allocpatternlist_r9[0].subfr_alloc == 0x3F);
|
||||||
|
assert(mcch_msg.commonsf_allocpatternlist_r9[1].radio_fr_alloc_offset == 7);
|
||||||
|
assert(mcch_msg.commonsf_allocpatternlist_r9[1].radio_fr_alloc_period == LIBLTE_RRC_RADIO_FRAME_ALLOCATION_PERIOD_N8);
|
||||||
|
assert(mcch_msg.commonsf_allocpatternlist_r9[1].subfr_alloc_num_frames == LIBLTE_RRC_SUBFRAME_ALLOCATION_NUM_FRAMES_FOUR);
|
||||||
|
assert(mcch_msg.commonsf_allocpatternlist_r9[1].subfr_alloc == 0xFFFFFF);
|
||||||
|
|
||||||
|
assert(mcch_msg.commonsf_allocperiod_r9 == LIBLTE_RRC_MBSFN_COMMON_SF_ALLOC_PERIOD_R9_RF256);
|
||||||
|
|
||||||
|
assert(mcch_msg.pmch_infolist_r9_size == 2);
|
||||||
|
|
||||||
|
assert(mcch_msg.pmch_infolist_r9[0].mbms_sessioninfolist_r9_size == 1);
|
||||||
|
assert(mcch_msg.pmch_infolist_r9[0].mbms_sessioninfolist_r9[0].logicalchannelid_r9 == 1);
|
||||||
|
assert(mcch_msg.pmch_infolist_r9[0].mbms_sessioninfolist_r9[0].sessionid_r9_present == true);
|
||||||
|
assert(mcch_msg.pmch_infolist_r9[0].mbms_sessioninfolist_r9[0].sessionid_r9 == 1);
|
||||||
|
assert(mcch_msg.pmch_infolist_r9[0].mbms_sessioninfolist_r9[0].tmgi_r9.plmn_id_explicit == true);
|
||||||
|
assert(mcch_msg.pmch_infolist_r9[0].mbms_sessioninfolist_r9[0].tmgi_r9.plmn_id_r9.mcc == 0xF987);
|
||||||
|
assert(mcch_msg.pmch_infolist_r9[0].mbms_sessioninfolist_r9[0].tmgi_r9.plmn_id_r9.mnc == 0xF654);
|
||||||
|
assert(mcch_msg.pmch_infolist_r9[0].mbms_sessioninfolist_r9[0].tmgi_r9.serviceid_r9 == 1);
|
||||||
|
assert(mcch_msg.pmch_infolist_r9[0].pmch_config_r9.datamcs_r9 == 16);
|
||||||
|
assert(mcch_msg.pmch_infolist_r9[0].pmch_config_r9.mch_schedulingperiod_r9 == LIBLTE_RRC_MCH_SCHEDULING_PERIOD_R9_RF1024);
|
||||||
|
assert(mcch_msg.pmch_infolist_r9[0].pmch_config_r9.sf_alloc_end_r9 == 1535);
|
||||||
|
|
||||||
|
assert(mcch_msg.pmch_infolist_r9[1].mbms_sessioninfolist_r9_size == 1);
|
||||||
|
assert(mcch_msg.pmch_infolist_r9[1].mbms_sessioninfolist_r9[0].logicalchannelid_r9 == 2);
|
||||||
|
assert(mcch_msg.pmch_infolist_r9[1].mbms_sessioninfolist_r9[0].sessionid_r9_present == true);
|
||||||
|
assert(mcch_msg.pmch_infolist_r9[1].mbms_sessioninfolist_r9[0].sessionid_r9 == 2);
|
||||||
|
assert(mcch_msg.pmch_infolist_r9[1].mbms_sessioninfolist_r9[0].tmgi_r9.plmn_id_explicit == true);
|
||||||
|
assert(mcch_msg.pmch_infolist_r9[1].mbms_sessioninfolist_r9[0].tmgi_r9.plmn_id_r9.mcc == 0xF987);
|
||||||
|
assert(mcch_msg.pmch_infolist_r9[1].mbms_sessioninfolist_r9[0].tmgi_r9.plmn_id_r9.mnc == 0xF654);
|
||||||
|
assert(mcch_msg.pmch_infolist_r9[1].mbms_sessioninfolist_r9[0].tmgi_r9.serviceid_r9 == 2);
|
||||||
|
assert(mcch_msg.pmch_infolist_r9[1].pmch_config_r9.datamcs_r9 == 8);
|
||||||
|
assert(mcch_msg.pmch_infolist_r9[1].pmch_config_r9.mch_schedulingperiod_r9 == LIBLTE_RRC_MCH_SCHEDULING_PERIOD_R9_RF8);
|
||||||
|
assert(mcch_msg.pmch_infolist_r9[1].pmch_config_r9.sf_alloc_end_r9 == 0);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
pack_test();
|
||||||
|
unpack_test();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,91 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2015 Software Radio Systems Limited
|
||||||
|
*
|
||||||
|
* \section LICENSE
|
||||||
|
*
|
||||||
|
* This file is part of the srsUE library.
|
||||||
|
*
|
||||||
|
* srsUE is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* srsUE 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* A copy of the GNU Affero General Public License can be found in
|
||||||
|
* the LICENSE file in the top-level directory of this distribution
|
||||||
|
* and at http://www.gnu.org/licenses/.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <srslte/srslte.h>
|
||||||
|
#include "srslte/common/log_filter.h"
|
||||||
|
#include "srslte/asn1/liblte_rrc.h"
|
||||||
|
|
||||||
|
|
||||||
|
void basic_test() {
|
||||||
|
srslte::log_filter log1("RRC");
|
||||||
|
log1.set_level(srslte::LOG_LEVEL_DEBUG);
|
||||||
|
log1.set_hex_limit(128);
|
||||||
|
|
||||||
|
LIBLTE_BIT_MSG_STRUCT bit_buf;
|
||||||
|
LIBLTE_BIT_MSG_STRUCT bit_buf2;
|
||||||
|
LIBLTE_BYTE_MSG_STRUCT byte_buf;
|
||||||
|
LIBLTE_RRC_UL_DCCH_MSG_STRUCT ul_dcch_msg;
|
||||||
|
|
||||||
|
uint32_t rrc_message_len = 18;
|
||||||
|
uint8_t rrc_message[] = {0x08, 0x10, 0x49, 0x3C, 0x0D, 0x97, 0x89, 0x83,
|
||||||
|
0xC0, 0x84, 0x20, 0x82, 0x08, 0x21, 0x00, 0x01,
|
||||||
|
0xBC, 0x48};
|
||||||
|
|
||||||
|
srslte_bit_unpack_vector(rrc_message, bit_buf.msg, rrc_message_len*8);
|
||||||
|
bit_buf.N_bits = rrc_message_len*8;
|
||||||
|
liblte_rrc_unpack_ul_dcch_msg((LIBLTE_BIT_MSG_STRUCT*)&bit_buf, &ul_dcch_msg);
|
||||||
|
|
||||||
|
assert(ul_dcch_msg.msg_type == LIBLTE_RRC_UL_DCCH_MSG_TYPE_MEASUREMENT_REPORT);
|
||||||
|
LIBLTE_RRC_MEASUREMENT_REPORT_STRUCT *rep = &ul_dcch_msg.msg.measurement_report;
|
||||||
|
assert(rep->meas_id == 1);
|
||||||
|
assert(rep->pcell_rsrp_result == 73);
|
||||||
|
assert(rep->pcell_rsrq_result == 15);
|
||||||
|
assert(rep->have_meas_result_neigh_cells);
|
||||||
|
assert(rep->meas_result_neigh_cells_choice == LIBLTE_RRC_MEAS_RESULT_LIST_EUTRA);
|
||||||
|
LIBLTE_RRC_MEAS_RESULT_LIST_EUTRA_STRUCT *eutra = &rep->meas_result_neigh_cells.eutra;
|
||||||
|
assert(eutra->n_result == 1);
|
||||||
|
assert(eutra->result_eutra_list[0].phys_cell_id == 357);
|
||||||
|
assert(eutra->result_eutra_list[0].have_cgi_info);
|
||||||
|
assert(eutra->result_eutra_list[0].cgi_info.have_plmn_identity_list);
|
||||||
|
assert(eutra->result_eutra_list[0].cgi_info.cell_global_id.plmn_id.mcc == 0xF898);
|
||||||
|
assert(eutra->result_eutra_list[0].cgi_info.cell_global_id.plmn_id.mnc == 0xFF78);
|
||||||
|
assert(eutra->result_eutra_list[0].cgi_info.cell_global_id.cell_id == 0x1084104);
|
||||||
|
assert(eutra->result_eutra_list[0].cgi_info.tracking_area_code == 0x1042);
|
||||||
|
assert(eutra->result_eutra_list[0].cgi_info.have_plmn_identity_list);
|
||||||
|
assert(eutra->result_eutra_list[0].cgi_info.n_plmn_identity_list == 1);
|
||||||
|
assert(eutra->result_eutra_list[0].cgi_info.plmn_identity_list[0].mcc == 0xFFFF);
|
||||||
|
assert(eutra->result_eutra_list[0].cgi_info.plmn_identity_list[0].mnc == 0xFF00);
|
||||||
|
assert(eutra->result_eutra_list[0].meas_result.have_rsrp);
|
||||||
|
assert(eutra->result_eutra_list[0].meas_result.rsrp_result == 60);
|
||||||
|
assert(eutra->result_eutra_list[0].meas_result.have_rsrp);
|
||||||
|
assert(eutra->result_eutra_list[0].meas_result.rsrq_result == 18);
|
||||||
|
|
||||||
|
liblte_rrc_pack_ul_dcch_msg(&ul_dcch_msg, (LIBLTE_BIT_MSG_STRUCT*)&bit_buf2);
|
||||||
|
srslte_bit_pack_vector(bit_buf2.msg, byte_buf.msg, bit_buf2.N_bits);
|
||||||
|
byte_buf.N_bytes = (bit_buf2.N_bits+7)/8;
|
||||||
|
log1.info_hex(byte_buf.msg, byte_buf.N_bytes, "UL_DCCH Packed message\n");
|
||||||
|
|
||||||
|
for(uint32_t i=0; i<rrc_message_len; i++) {
|
||||||
|
assert(byte_buf.msg[i] == rrc_message[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
basic_test();
|
||||||
|
}
|
@ -0,0 +1,101 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2017 Software Radio Systems Limited
|
||||||
|
*
|
||||||
|
* \section LICENSE
|
||||||
|
*
|
||||||
|
* This file is part of srsLTE.
|
||||||
|
*
|
||||||
|
* srsLTE is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* srsLTE 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* A copy of the GNU Affero General Public License can be found in
|
||||||
|
* the LICENSE file in the top-level directory of this distribution
|
||||||
|
* and at http://www.gnu.org/licenses/.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
* File: mbms-gw.h
|
||||||
|
* Description: Top-level MBMS-GW class. Creates and links all
|
||||||
|
* interfaces and helpers.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef MBMS_GW_H
|
||||||
|
#define MBMS_GW_H
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include "srslte/common/log.h"
|
||||||
|
#include "srslte/common/logger_file.h"
|
||||||
|
#include "srslte/common/log_filter.h"
|
||||||
|
#include "srslte/common/buffer_pool.h"
|
||||||
|
#include "srslte/common/threads.h"
|
||||||
|
#include "srslte/asn1/gtpc.h"
|
||||||
|
|
||||||
|
namespace srsepc{
|
||||||
|
|
||||||
|
|
||||||
|
const uint16_t GTPU_RX_PORT = 2152;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
std::string name;
|
||||||
|
std::string sgi_mb_if_addr;
|
||||||
|
std::string m1u_multi_addr;
|
||||||
|
} mbms_gw_args_t;
|
||||||
|
|
||||||
|
struct pseudo_hdr
|
||||||
|
{
|
||||||
|
uint32_t src_addr;
|
||||||
|
uint32_t dst_addr;
|
||||||
|
uint8_t placeholder;
|
||||||
|
uint8_t protocol;
|
||||||
|
uint16_t udp_len;
|
||||||
|
};
|
||||||
|
|
||||||
|
class mbms_gw:
|
||||||
|
public thread
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static mbms_gw* get_instance(void);
|
||||||
|
static void cleanup(void);
|
||||||
|
int init(mbms_gw_args_t* args, srslte::log_filter *mbms_gw_log);
|
||||||
|
void stop();
|
||||||
|
void run_thread();
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
/* Methods */
|
||||||
|
mbms_gw();
|
||||||
|
virtual ~mbms_gw();
|
||||||
|
static mbms_gw *m_instance;
|
||||||
|
|
||||||
|
srslte::error_t init_sgi_mb_if(mbms_gw_args_t *args);
|
||||||
|
srslte::error_t init_m1_u(mbms_gw_args_t *args);
|
||||||
|
void handle_sgi_md_pdu(srslte::byte_buffer_t *msg);
|
||||||
|
uint16_t in_cksum(uint16_t *iphdr, int count);
|
||||||
|
|
||||||
|
/* Members */
|
||||||
|
bool m_running;
|
||||||
|
srslte::byte_buffer_pool *m_pool;
|
||||||
|
srslte::log_filter *m_mbms_gw_log;
|
||||||
|
|
||||||
|
bool m_sgi_mb_up;
|
||||||
|
int m_sgi_mb_if;
|
||||||
|
|
||||||
|
bool m_m1u_up;
|
||||||
|
int m_m1u;
|
||||||
|
struct sockaddr_in m_m1u_multi_addr;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace srsepc
|
||||||
|
|
||||||
|
#endif // SGW_H
|
@ -0,0 +1,39 @@
|
|||||||
|
#####################################################################
|
||||||
|
# srsEPC configuration file
|
||||||
|
#####################################################################
|
||||||
|
|
||||||
|
#####################################################################
|
||||||
|
# MBMS-GW configuration
|
||||||
|
#
|
||||||
|
# name: MBMS-GW name
|
||||||
|
# sgi_mb_if_addr: SGi-mb interface IP address
|
||||||
|
# m1u_addr: Multicast group for eNBs (FIXME this should be setup with M2/M3)
|
||||||
|
#
|
||||||
|
#####################################################################
|
||||||
|
[mbms_gw]
|
||||||
|
name = srsmbmsgw01
|
||||||
|
sgi_mb_if_addr = 172.16.1.1
|
||||||
|
m1u_multi_addr = 239.255.0.1
|
||||||
|
|
||||||
|
####################################################################
|
||||||
|
# Log configuration
|
||||||
|
#
|
||||||
|
# Log levels can be set for individual layers. "all_level" sets log
|
||||||
|
# level for all layers unless otherwise configured.
|
||||||
|
# Format: e.g. s1ap_level = info
|
||||||
|
#
|
||||||
|
# In the same way, packet hex dumps can be limited for each level.
|
||||||
|
# "all_hex_limit" sets the hex limit for all layers unless otherwise
|
||||||
|
# configured.
|
||||||
|
# Format: e.g. s1ap_hex_limit = 32
|
||||||
|
#
|
||||||
|
# Logging layers: mbms_gw, all
|
||||||
|
# Logging levels: debug, info, warning, error, none
|
||||||
|
#
|
||||||
|
# filename: File path to use for log output. Can be set to stdout
|
||||||
|
# to print logs to standard output
|
||||||
|
#####################################################################
|
||||||
|
[log]
|
||||||
|
all_level = debug
|
||||||
|
all_hex_limit = 32
|
||||||
|
filename = /tmp/mbms.log
|
@ -0,0 +1,23 @@
|
|||||||
|
#
|
||||||
|
# Copyright 2013-2017 Software Radio Systems Limited
|
||||||
|
#
|
||||||
|
# This file is part of srsLTE
|
||||||
|
#
|
||||||
|
# srsLTE is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as
|
||||||
|
# published by the Free Software Foundation, either version 3 of
|
||||||
|
# the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# srsLTE 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 Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# A copy of the GNU Affero General Public License can be found in
|
||||||
|
# the LICENSE file in the top-level directory of this distribution
|
||||||
|
# and at http://www.gnu.org/licenses/.
|
||||||
|
#
|
||||||
|
|
||||||
|
file(GLOB SOURCES "*.cc")
|
||||||
|
add_library(srsepc_mbms_gw STATIC ${SOURCES})
|
||||||
|
install(TARGETS srsepc_mbms_gw DESTINATION ${LIBRARY_DIR})
|
@ -0,0 +1,216 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* \section LICENSE
|
||||||
|
*
|
||||||
|
* This file is part of srsLTE.
|
||||||
|
*
|
||||||
|
* srsLTE is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* srsLTE 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* A copy of the GNU Affero General Public License can be found in
|
||||||
|
* the LICENSE file in the top-level directory of this distribution
|
||||||
|
* and at http://www.gnu.org/licenses/.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <boost/program_options.hpp>
|
||||||
|
#include <boost/algorithm/string.hpp>
|
||||||
|
#include "srsepc/hdr/mbms-gw/mbms-gw.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace srsepc;
|
||||||
|
namespace bpo = boost::program_options;
|
||||||
|
|
||||||
|
bool running = true;
|
||||||
|
|
||||||
|
void
|
||||||
|
sig_int_handler(int signo){
|
||||||
|
running = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
std::string mbms_gw_level;
|
||||||
|
int mbms_gw_hex_limit;
|
||||||
|
std::string all_level;
|
||||||
|
int all_hex_limit;
|
||||||
|
std::string filename;
|
||||||
|
}log_args_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
mbms_gw_args_t mbms_gw_args;
|
||||||
|
log_args_t log_args;
|
||||||
|
}all_args_t;
|
||||||
|
|
||||||
|
srslte::LOG_LEVEL_ENUM
|
||||||
|
level(std::string l)
|
||||||
|
{
|
||||||
|
boost::to_upper(l);
|
||||||
|
if("NONE" == l){
|
||||||
|
return srslte::LOG_LEVEL_NONE;
|
||||||
|
}else if("ERROR" == l){
|
||||||
|
return srslte::LOG_LEVEL_ERROR;
|
||||||
|
}else if("WARNING" == l){
|
||||||
|
return srslte::LOG_LEVEL_WARNING;
|
||||||
|
}else if("INFO" == l){
|
||||||
|
return srslte::LOG_LEVEL_INFO;
|
||||||
|
}else if("DEBUG" == l){
|
||||||
|
return srslte::LOG_LEVEL_DEBUG;
|
||||||
|
}else{
|
||||||
|
return srslte::LOG_LEVEL_NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**********************************************************************
|
||||||
|
* Program arguments processing
|
||||||
|
***********************************************************************/
|
||||||
|
string config_file;
|
||||||
|
|
||||||
|
void
|
||||||
|
parse_args(all_args_t *args, int argc, char* argv[]) {
|
||||||
|
|
||||||
|
string mbms_gw_name;
|
||||||
|
string mbms_gw_sgi_mb_if_addr;
|
||||||
|
string mbms_gw_m1u_multi_addr;
|
||||||
|
|
||||||
|
string log_filename;
|
||||||
|
|
||||||
|
// Command line only options
|
||||||
|
bpo::options_description general("General options");
|
||||||
|
general.add_options()
|
||||||
|
("help,h", "Produce help message")
|
||||||
|
("version,v", "Print version information and exit")
|
||||||
|
;
|
||||||
|
|
||||||
|
// Command line or config file options
|
||||||
|
bpo::options_description common("Configuration options");
|
||||||
|
common.add_options()
|
||||||
|
|
||||||
|
("mbms_gw.name", bpo::value<string>(&mbms_gw_name)->default_value("srsmbmsgw01"), "MBMS-GW Name")
|
||||||
|
("mbms_gw.sgi_mb_if_addr", bpo::value<string>(&mbms_gw_sgi_mb_if_addr)->default_value("172.16.1.1"), "SGi-mb TUN interface Address")
|
||||||
|
("mbms_gw.m1u_multi_addr", bpo::value<string>(&mbms_gw_m1u_multi_addr)->default_value("239.255.0.1"), "M1-u GTPu destination multicast address")
|
||||||
|
|
||||||
|
("log.all_level", bpo::value<string>(&args->log_args.all_level)->default_value("info"), "ALL log level")
|
||||||
|
("log.all_hex_limit", bpo::value<int>(&args->log_args.all_hex_limit)->default_value(32), "ALL log hex dump limit")
|
||||||
|
|
||||||
|
("log.filename", bpo::value<string>(&args->log_args.filename)->default_value("/tmp/mbms.log"),"Log filename")
|
||||||
|
;
|
||||||
|
|
||||||
|
// Positional options - config file location
|
||||||
|
bpo::options_description position("Positional options");
|
||||||
|
position.add_options()
|
||||||
|
("config_file", bpo::value< string >(&config_file), "MBMS-GW configuration file")
|
||||||
|
;
|
||||||
|
bpo::positional_options_description p;
|
||||||
|
p.add("config_file", -1);
|
||||||
|
|
||||||
|
// these options are allowed on the command line
|
||||||
|
bpo::options_description cmdline_options;
|
||||||
|
cmdline_options.add(common).add(position).add(general);
|
||||||
|
|
||||||
|
// parse the command line and store result in vm
|
||||||
|
bpo::variables_map vm;
|
||||||
|
bpo::store(bpo::command_line_parser(argc, argv).options(cmdline_options).positional(p).run(), vm);
|
||||||
|
bpo::notify(vm);
|
||||||
|
|
||||||
|
// help option was given - print usage and exit
|
||||||
|
if (vm.count("help")) {
|
||||||
|
cout << "Usage: " << argv[0] << " [OPTIONS] config_file" << endl << endl;
|
||||||
|
cout << common << endl << general << endl;
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Parsing Config File
|
||||||
|
if (!vm.count("config_file")) {
|
||||||
|
cout << "Error: Configuration file not provided" << endl;
|
||||||
|
cout << "Usage: " << argv[0] << " [OPTIONS] config_file" << endl << endl;
|
||||||
|
exit(0);
|
||||||
|
} else {
|
||||||
|
cout << "Reading configuration file " << config_file << "..." << endl;
|
||||||
|
ifstream conf(config_file.c_str(), ios::in);
|
||||||
|
if(conf.fail()) {
|
||||||
|
cout << "Failed to read configuration file " << config_file << " - exiting" << endl;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
bpo::store(bpo::parse_config_file(conf, common), vm);
|
||||||
|
bpo::notify(vm);
|
||||||
|
}
|
||||||
|
|
||||||
|
args->mbms_gw_args.name = mbms_gw_name;
|
||||||
|
args->mbms_gw_args.sgi_mb_if_addr = mbms_gw_sgi_mb_if_addr;
|
||||||
|
args->mbms_gw_args.m1u_multi_addr = mbms_gw_m1u_multi_addr;
|
||||||
|
// Apply all_level to any unset layers
|
||||||
|
if (vm.count("log.all_level")) {
|
||||||
|
if(!vm.count("log.mbms_gw_level")) {
|
||||||
|
args->log_args.mbms_gw_level = args->log_args.all_level;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply all_hex_limit to any unset layers
|
||||||
|
if (vm.count("log.all_hex_limit")) {
|
||||||
|
if(!vm.count("log.mbms_gw_hex_limit")) {
|
||||||
|
args->log_args.mbms_gw_hex_limit = args->log_args.all_hex_limit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
main (int argc,char * argv[] )
|
||||||
|
{
|
||||||
|
cout << endl <<"--- Software Radio Systems MBMS ---" << endl << endl;
|
||||||
|
signal(SIGINT, sig_int_handler);
|
||||||
|
signal(SIGTERM, sig_int_handler);
|
||||||
|
signal(SIGKILL, sig_int_handler);
|
||||||
|
|
||||||
|
all_args_t args;
|
||||||
|
parse_args(&args, argc, argv);
|
||||||
|
|
||||||
|
srslte::logger_stdout logger_stdout;
|
||||||
|
srslte::logger_file logger_file;
|
||||||
|
srslte::logger *logger;
|
||||||
|
|
||||||
|
/*Init logger*/
|
||||||
|
if (!args.log_args.filename.compare("stdout")) {
|
||||||
|
logger = &logger_stdout;
|
||||||
|
} else {
|
||||||
|
logger_file.init(args.log_args.filename);
|
||||||
|
logger_file.log("\n--- Software Radio Systems MBMS log ---\n\n");
|
||||||
|
logger = &logger_file;
|
||||||
|
}
|
||||||
|
|
||||||
|
srslte::log_filter mbms_gw_log;
|
||||||
|
mbms_gw_log.init("MBMS",logger);
|
||||||
|
mbms_gw_log.set_level(level(args.log_args.mbms_gw_level));
|
||||||
|
mbms_gw_log.set_hex_limit(args.log_args.mbms_gw_hex_limit);
|
||||||
|
|
||||||
|
mbms_gw *mbms_gw = mbms_gw::get_instance();
|
||||||
|
if(mbms_gw->init(&args.mbms_gw_args,&mbms_gw_log)) {
|
||||||
|
cout << "Error initializing MBMS-GW" << endl;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
mbms_gw->start();
|
||||||
|
while(running) {
|
||||||
|
sleep(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
mbms_gw->stop();
|
||||||
|
mbms_gw->cleanup();
|
||||||
|
|
||||||
|
cout << std::endl <<"--- exiting ---" << endl;
|
||||||
|
return 0;
|
||||||
|
}
|
@ -0,0 +1,385 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2017 Software Radio Systems Limited
|
||||||
|
*
|
||||||
|
* \section LICENSE
|
||||||
|
*
|
||||||
|
* This file is part of srsLTE.
|
||||||
|
*
|
||||||
|
* srsLTE is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* srsLTE 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* A copy of the GNU Affero General Public License can be found in
|
||||||
|
* the LICENSE file in the top-level directory of this distribution
|
||||||
|
* and at http://www.gnu.org/licenses/.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <linux/if.h>
|
||||||
|
#include <linux/if_tun.h>
|
||||||
|
#include <netinet/udp.h>
|
||||||
|
#include <linux/ip.h>
|
||||||
|
#include "srsepc/hdr/mbms-gw/mbms-gw.h"
|
||||||
|
#include "srslte/upper/gtpu.h"
|
||||||
|
|
||||||
|
namespace srsepc{
|
||||||
|
|
||||||
|
mbms_gw* mbms_gw::m_instance = NULL;
|
||||||
|
pthread_mutex_t mbms_gw_instance_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
|
const uint16_t MBMS_GW_BUFFER_SIZE = 2500;
|
||||||
|
|
||||||
|
mbms_gw::mbms_gw():
|
||||||
|
m_running(false),
|
||||||
|
m_sgi_mb_up(false)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mbms_gw::~mbms_gw()
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mbms_gw*
|
||||||
|
mbms_gw::get_instance(void)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&mbms_gw_instance_mutex);
|
||||||
|
if(NULL == m_instance) {
|
||||||
|
m_instance = new mbms_gw();
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&mbms_gw_instance_mutex);
|
||||||
|
return(m_instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
mbms_gw::cleanup(void)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&mbms_gw_instance_mutex);
|
||||||
|
if(NULL != m_instance) {
|
||||||
|
delete m_instance;
|
||||||
|
m_instance = NULL;
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&mbms_gw_instance_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
mbms_gw::init(mbms_gw_args_t* args, srslte::log_filter *mbms_gw_log)
|
||||||
|
{
|
||||||
|
srslte::error_t err;
|
||||||
|
m_pool = srslte::byte_buffer_pool::get_instance();
|
||||||
|
|
||||||
|
//Init log
|
||||||
|
m_mbms_gw_log = mbms_gw_log;
|
||||||
|
|
||||||
|
err = init_sgi_mb_if(args);
|
||||||
|
if (err != srslte::ERROR_NONE)
|
||||||
|
{
|
||||||
|
m_mbms_gw_log->console("Error initializing SGi-MB.\n");
|
||||||
|
m_mbms_gw_log->error("Error initializing SGi-MB.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
err = init_m1_u(args);
|
||||||
|
if (err != srslte::ERROR_NONE)
|
||||||
|
{
|
||||||
|
m_mbms_gw_log->console("Error initializing SGi-MB.\n");
|
||||||
|
m_mbms_gw_log->error("Error initializing SGi-MB.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
m_mbms_gw_log->info("MBMS GW Initiated\n");
|
||||||
|
m_mbms_gw_log->console("MBMS GW Initiated\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
mbms_gw::stop()
|
||||||
|
{
|
||||||
|
if(m_running)
|
||||||
|
{
|
||||||
|
if(m_sgi_mb_up)
|
||||||
|
{
|
||||||
|
close(m_sgi_mb_if);
|
||||||
|
m_mbms_gw_log->info("Closed SGi-MB interface\n");
|
||||||
|
}
|
||||||
|
m_running = false;
|
||||||
|
thread_cancel();
|
||||||
|
wait_thread_finish();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
srslte::error_t
|
||||||
|
mbms_gw::init_sgi_mb_if(mbms_gw_args_t *args)
|
||||||
|
{
|
||||||
|
char dev[IFNAMSIZ] = "sgi_mb";
|
||||||
|
struct ifreq ifr;
|
||||||
|
|
||||||
|
if(m_sgi_mb_up)
|
||||||
|
{
|
||||||
|
return(srslte::ERROR_ALREADY_STARTED);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Construct the TUN device
|
||||||
|
m_sgi_mb_if = open("/dev/net/tun", O_RDWR);
|
||||||
|
m_mbms_gw_log->info("TUN file descriptor = %d\n", m_sgi_mb_if);
|
||||||
|
if(m_sgi_mb_if < 0)
|
||||||
|
{
|
||||||
|
m_mbms_gw_log->error("Failed to open TUN device: %s\n", strerror(errno));
|
||||||
|
return(srslte::ERROR_CANT_START);
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&ifr, 0, sizeof(ifr));
|
||||||
|
ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
|
||||||
|
strncpy(ifr.ifr_ifrn.ifrn_name, dev, IFNAMSIZ-1);
|
||||||
|
ifr.ifr_ifrn.ifrn_name[IFNAMSIZ-1]='\0';
|
||||||
|
|
||||||
|
if(ioctl(m_sgi_mb_if, TUNSETIFF, &ifr) < 0)
|
||||||
|
{
|
||||||
|
m_mbms_gw_log->error("Failed to set TUN device name: %s\n", strerror(errno));
|
||||||
|
close(m_sgi_mb_if);
|
||||||
|
return(srslte::ERROR_CANT_START);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bring up the interface
|
||||||
|
int sgi_mb_sock = socket(AF_INET, SOCK_DGRAM, 0);
|
||||||
|
if(sgi_mb_sock<0)
|
||||||
|
{
|
||||||
|
m_mbms_gw_log->error("Failed to bring up socket: %s\n", strerror(errno));
|
||||||
|
close(m_sgi_mb_if);
|
||||||
|
return(srslte::ERROR_CANT_START);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ioctl(sgi_mb_sock, SIOCGIFFLAGS, &ifr) < 0)
|
||||||
|
{
|
||||||
|
m_mbms_gw_log->error("Failed to bring up interface: %s\n", strerror(errno));
|
||||||
|
close(m_sgi_mb_if);
|
||||||
|
close(sgi_mb_sock);
|
||||||
|
return(srslte::ERROR_CANT_START);
|
||||||
|
}
|
||||||
|
|
||||||
|
ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
|
||||||
|
if(ioctl(sgi_mb_sock, SIOCSIFFLAGS, &ifr) < 0)
|
||||||
|
{
|
||||||
|
m_mbms_gw_log->error("Failed to set socket flags: %s\n", strerror(errno));
|
||||||
|
close(sgi_mb_sock);
|
||||||
|
close(m_sgi_mb_if);
|
||||||
|
return(srslte::ERROR_CANT_START);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Set IP of the interface
|
||||||
|
struct sockaddr_in *addr = (struct sockaddr_in*)&ifr.ifr_addr;
|
||||||
|
addr->sin_family = AF_INET;
|
||||||
|
addr->sin_addr.s_addr = inet_addr(args->sgi_mb_if_addr.c_str());
|
||||||
|
addr->sin_port = 0;
|
||||||
|
|
||||||
|
if (ioctl(sgi_mb_sock, SIOCSIFADDR, &ifr) < 0) {
|
||||||
|
m_mbms_gw_log->error("Failed to set TUN interface IP. Address: %s, Error: %s\n", args->sgi_mb_if_addr.c_str(), strerror(errno));
|
||||||
|
close(m_sgi_mb_if);
|
||||||
|
close(sgi_mb_sock);
|
||||||
|
return srslte::ERROR_CANT_START;
|
||||||
|
}
|
||||||
|
|
||||||
|
ifr.ifr_netmask.sa_family = AF_INET;
|
||||||
|
((struct sockaddr_in *)&ifr.ifr_netmask)->sin_addr.s_addr = inet_addr("255.255.255.0");
|
||||||
|
if (ioctl(sgi_mb_sock, SIOCSIFNETMASK, &ifr) < 0) {
|
||||||
|
m_mbms_gw_log->error("Failed to set TUN interface Netmask. Error: %s\n", strerror(errno));
|
||||||
|
close(m_sgi_mb_if);
|
||||||
|
close(sgi_mb_sock);
|
||||||
|
return srslte::ERROR_CANT_START;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_sgi_mb_up = true;
|
||||||
|
close(sgi_mb_sock);
|
||||||
|
return(srslte::ERROR_NONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
srslte::error_t
|
||||||
|
mbms_gw::init_m1_u(mbms_gw_args_t *args)
|
||||||
|
{
|
||||||
|
int addrlen;
|
||||||
|
struct sockaddr_in addr;
|
||||||
|
m_m1u = socket(AF_INET, SOCK_DGRAM, 0);
|
||||||
|
if(m_m1u<0)
|
||||||
|
{
|
||||||
|
m_mbms_gw_log->error("Failed to open socket: %s\n", strerror(errno));
|
||||||
|
return srslte::ERROR_CANT_START;
|
||||||
|
}
|
||||||
|
m_m1u_up = true;
|
||||||
|
|
||||||
|
/* set no loopback */
|
||||||
|
char loopch = 0;
|
||||||
|
if(setsockopt(m_m1u, IPPROTO_IP, IP_MULTICAST_LOOP, (char*)&loopch, sizeof(char))<0){
|
||||||
|
m_mbms_gw_log->error("Failed to disable loopback: %s\n", strerror(errno));
|
||||||
|
return srslte::ERROR_CANT_START;
|
||||||
|
} else {
|
||||||
|
m_mbms_gw_log->debug("Loopback disabled\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set local interface for outbound multicast packets*/
|
||||||
|
/* The IP must be associated with a local multicast capable interface */
|
||||||
|
struct in_addr local_if;
|
||||||
|
local_if.s_addr = inet_addr("127.0.1.200");
|
||||||
|
if(setsockopt(m_m1u, IPPROTO_IP, IP_MULTICAST_IF, (char*)&local_if, sizeof(struct in_addr))<0){
|
||||||
|
perror("Error setting multicast interface.\n");
|
||||||
|
} else {
|
||||||
|
printf("Multicast interface specified.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
bzero(&m_m1u_multi_addr,sizeof(m_m1u_multi_addr));
|
||||||
|
m_m1u_multi_addr.sin_family = AF_INET;
|
||||||
|
m_m1u_multi_addr.sin_port = htons(GTPU_RX_PORT+1);
|
||||||
|
m_m1u_multi_addr.sin_addr.s_addr = inet_addr(args->m1u_multi_addr.c_str());
|
||||||
|
m_mbms_gw_log->info("Initialized M1-U\n");
|
||||||
|
|
||||||
|
return srslte::ERROR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
mbms_gw::run_thread()
|
||||||
|
{
|
||||||
|
//Mark the thread as running
|
||||||
|
m_running=true;
|
||||||
|
srslte::byte_buffer_t *msg;
|
||||||
|
msg = m_pool->allocate();
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t seq = 0;
|
||||||
|
while(m_running)
|
||||||
|
{
|
||||||
|
msg->reset();
|
||||||
|
int n;
|
||||||
|
do{
|
||||||
|
n = read(m_sgi_mb_if, msg->msg, SRSLTE_MAX_BUFFER_SIZE_BYTES);
|
||||||
|
}while(n == -1 && errno == EAGAIN);
|
||||||
|
|
||||||
|
if(n<0){
|
||||||
|
m_mbms_gw_log->error("Error reading from TUN interface. Error: %s\n", strerror(errno));
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
msg->N_bytes = n;
|
||||||
|
handle_sgi_md_pdu(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_pool->deallocate(msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
mbms_gw::handle_sgi_md_pdu(srslte::byte_buffer_t *msg)
|
||||||
|
{
|
||||||
|
uint8_t version;
|
||||||
|
srslte::gtpu_header_t header;
|
||||||
|
in_addr_t baddr = inet_addr("172.16.0.255");
|
||||||
|
in_addr_t saddr = inet_addr("172.16.0.254");
|
||||||
|
|
||||||
|
//Setup GTP-U header
|
||||||
|
header.flags = 0x30;
|
||||||
|
header.message_type = 0xFF;
|
||||||
|
header.length = msg->N_bytes;
|
||||||
|
header.teid = 0xAAAA; //FIXME Harcoded TEID for now
|
||||||
|
|
||||||
|
//Sanity Check IP packet
|
||||||
|
if(msg->N_bytes < 20)
|
||||||
|
{
|
||||||
|
m_mbms_gw_log->error("IPv4 min len: %d, drop msg len %d\n", 20, msg->N_bytes);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//IP+UDP Headers
|
||||||
|
struct iphdr *iph = (struct iphdr *) msg->msg;
|
||||||
|
struct udphdr *udph = (struct udphdr *) (msg->msg + iph->ihl*4);
|
||||||
|
if(iph->version != 4)
|
||||||
|
{
|
||||||
|
m_mbms_gw_log->warning("IPv6 not supported yet.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Replace Destination IP with broadcast address
|
||||||
|
iph->daddr = baddr;
|
||||||
|
|
||||||
|
//Replace Source IP with address in same subnet
|
||||||
|
iph->saddr = saddr;
|
||||||
|
|
||||||
|
//Replace IP cheksum
|
||||||
|
iph->check = 0;
|
||||||
|
iph->check = in_cksum((uint16_t*)msg->msg,4*(msg->msg[0] & 0x0F));
|
||||||
|
|
||||||
|
//Set Pseudo Header
|
||||||
|
struct pseudo_hdr phdr;
|
||||||
|
phdr.src_addr = iph->saddr;
|
||||||
|
phdr.dst_addr = iph->daddr;
|
||||||
|
phdr.protocol = IPPROTO_UDP;
|
||||||
|
phdr.placeholder = 0;
|
||||||
|
phdr.udp_len = udph->len;
|
||||||
|
|
||||||
|
//Set Pseudo Datagram
|
||||||
|
udph->check = 0;
|
||||||
|
int psize = sizeof(struct pseudo_hdr) + ntohs(udph->len);
|
||||||
|
uint8_t * pseudo_dgram = (uint8_t*) malloc(psize);
|
||||||
|
memcpy(pseudo_dgram, &phdr,sizeof(struct pseudo_hdr));
|
||||||
|
memcpy(pseudo_dgram+sizeof(pseudo_hdr),udph,ntohs(udph->len));
|
||||||
|
|
||||||
|
//Recompute UDP checksum
|
||||||
|
udph->check = in_cksum((uint16_t*) pseudo_dgram, psize);
|
||||||
|
free(pseudo_dgram);
|
||||||
|
|
||||||
|
//Write GTP-U header into packet
|
||||||
|
if(!srslte::gtpu_write_header(&header, msg))
|
||||||
|
{
|
||||||
|
m_mbms_gw_log->console("Error writing GTP-U header on PDU\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int n = sendto(m_m1u, msg->msg, msg->N_bytes, 0,
|
||||||
|
(sockaddr *) &m_m1u_multi_addr, sizeof(struct sockaddr));
|
||||||
|
if(n<0){
|
||||||
|
m_mbms_gw_log->console("Error writting to M1-U socket.\n");
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
m_mbms_gw_log->debug("Sent %d Bytes\n", msg->N_bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t
|
||||||
|
mbms_gw::in_cksum(uint16_t *iphdr, int count)
|
||||||
|
{
|
||||||
|
|
||||||
|
//RFC 1071
|
||||||
|
uint32_t sum = 0;
|
||||||
|
uint16_t padd = 0;
|
||||||
|
uint16_t result;
|
||||||
|
while(count > 1)
|
||||||
|
{
|
||||||
|
sum+= *iphdr++;
|
||||||
|
count -= 2;
|
||||||
|
}
|
||||||
|
if( count > 0 )
|
||||||
|
{
|
||||||
|
padd = * (uint8_t *) iphdr;
|
||||||
|
sum += padd;
|
||||||
|
}
|
||||||
|
/*Fold 32-bit sum to 16-bit*/
|
||||||
|
// while(sum>>16)
|
||||||
|
// sum = (sum & 0xffff) + (sum >> 16);
|
||||||
|
sum = (sum>>16)+(sum & 0xFFFF);
|
||||||
|
sum = sum + (sum >> 16);
|
||||||
|
result = (uint16_t) ~sum;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
} //namespace srsepc
|
Loading…
Reference in New Issue