diff --git a/example/defaults.conf b/example/defaults.conf index 353cf9b5..f32e1f57 100644 --- a/example/defaults.conf +++ b/example/defaults.conf @@ -92,3 +92,11 @@ osmo_bts_oc2g: max_trx: 1 trx_list: - nominal_power: 25 + +srsepc: + mcc: 901 + mnc: 70 + +srsenb: + mcc: 901 + mnc: 70 diff --git a/example/resources.conf.prod b/example/resources.conf.prod index 992a694c..943160be 100644 --- a/example/resources.conf.prod +++ b/example/resources.conf.prod @@ -11,6 +11,12 @@ ip_address: - addr: 10.42.42.9 - addr: 10.42.42.10 +run_node: +- run_type: ssh + run_addr: 10.42.42.118 + ssh_user: jenkins + ssh_addr: 10.42.42.116 + bts: - label: sysmoBTS 1002 type: osmo-bts-sysmo @@ -148,6 +154,12 @@ bts: direct_pcu: true ciphers: [a5_0, a5_1, a5_3] +enb: +- label: srsENB-zmq + type: srsenb + remote_user: jenkins + addr: 10.42.42.117 + arfcn: - arfcn: 512 band: GSM-1800 @@ -227,5 +239,14 @@ modem: ciphers: [a5_0, a5_1] features: ['sms', 'voice', 'ussd', 'gprs', 'sim'] +- label: srsUE-zmq_1 + type: srsue + remote_user: jenkins + addr: 10.42.42.116 + imsi: '001010123456789' + ki: '00112233445566778899aabbccddeeff' + auth_algo: 'xor' + + osmocon_phone: - serial_device: '/dev/serial/by-id/usb-Silicon_Labs_CP2104_USB_to_UART_Bridge_Controller_00897B41-if00-port0' diff --git a/src/osmo_gsm_tester/resource.py b/src/osmo_gsm_tester/resource.py index 0cdcb8aa..0804591c 100644 --- a/src/osmo_gsm_tester/resource.py +++ b/src/osmo_gsm_tester/resource.py @@ -29,6 +29,7 @@ from . import schema from . import bts_sysmo, bts_osmotrx, bts_osmovirtual, bts_octphy, bts_nanobts, bts_oc2g from . import modem from . import ms_osmo_mobile +from . import srs_ue, srs_enb from .util import is_dict, is_list @@ -45,7 +46,8 @@ R_BTS = 'bts' R_ARFCN = 'arfcn' R_MODEM = 'modem' R_OSMOCON = 'osmocon_phone' -R_ALL = (R_IP_ADDRESS, R_RUN_NODE, R_BTS, R_ARFCN, R_MODEM, R_OSMOCON) +R_ENB = 'enb' +R_ALL = (R_IP_ADDRESS, R_RUN_NODE, R_BTS, R_ARFCN, R_MODEM, R_OSMOCON, R_ENB) RESOURCES_SCHEMA = { 'ip_address[].addr': schema.IPV4, @@ -83,6 +85,11 @@ RESOURCES_SCHEMA = { 'bts[].osmo_trx.max_trxd_version': schema.UINT, 'bts[].osmo_trx.channels[].rx_path': schema.STR, 'bts[].osmo_trx.channels[].tx_path': schema.STR, + 'enb[].label': schema.STR, + 'enb[].type': schema.STR, + 'enb[].remote_user': schema.STR, + 'enb[].addr': schema.IPV4, + 'enb[].band': schema.BAND, 'arfcn[].arfcn': schema.INT, 'arfcn[].band': schema.BAND, 'modem[].type': schema.STR, @@ -91,6 +98,8 @@ RESOURCES_SCHEMA = { 'modem[].imsi': schema.IMSI, 'modem[].ki': schema.KI, 'modem[].auth_algo': schema.AUTH_ALGO, + 'modem[].remote_user': schema.STR, + 'modem[].addr': schema.IPV4, 'modem[].ciphers[]': schema.CIPHER, 'modem[].features[]': schema.MODEM_FEATURE, 'osmocon_phone[].serial_device': schema.STR, @@ -115,12 +124,16 @@ KNOWN_BTS_TYPES = { 'nanobts': bts_nanobts.NanoBts, } +KNOWN_ENB_TYPES = { + 'srsenb': srs_enb.srsENB, +} KNOWN_MS_TYPES = { # Map None to ofono for forward compability None: modem.Modem, 'ofono': modem.Modem, 'osmo-mobile': ms_osmo_mobile.MSOsmoMobile, + 'srsue': srs_ue.srsUE, } diff --git a/src/osmo_gsm_tester/srs_enb.py b/src/osmo_gsm_tester/srs_enb.py new file mode 100644 index 00000000..2b65a5b4 --- /dev/null +++ b/src/osmo_gsm_tester/srs_enb.py @@ -0,0 +1,197 @@ +# osmo_gsm_tester: specifics for running an SRS eNodeB process +# +# Copyright (C) 2020 by sysmocom - s.f.m.c. GmbH +# +# Author: Pau Espin Pedrol +# +# This program 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. +# +# This program 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 this program. If not, see . + +import os +import pprint + +from . import log, util, config, template, process, remote + +class srsENB(log.Origin): + + REMOTE_DIR = '/osmo-gsm-tester-srsenb' + BINFILE = 'srsenb' + CFGFILE = 'srsenb.conf' + CFGFILE_SIB = 'srsenb_sib.conf' + CFGFILE_RR = 'srsenb_rr.conf' + CFGFILE_DRB = 'srsenb_drb.conf' + LOGFILE = 'srsenb.log' + + def __init__(self, suite_run, conf): + super().__init__(log.C_RUN, 'srsenb') + self._addr = conf.get('addr', None) + if self._addr is None: + raise log.Error('addr not set') + self.set_name('srsenb_%s' % self._addr) + self.ue = None + self.epc = None + self.run_dir = None + self.config_file = None + self.config_sib_file = None + self.config_rr_file = None + self.config_drb_file = None + self.process = None + self.rem_host = None + self.remote_config_file = None + self.remote_config_sib_file = None + self.remote_config_rr_file = None + self.remote_config_drb_file = None + self.remote_log_file = None + self.suite_run = suite_run + self.nof_prb=100 + if self.nof_prb == 75: + self.base_srate=15.36e6 + else: + self.base_srate=23.04e6 + self.remote_user = conf.get('remote_user', None) + + def cleanup(self): + if self.process is None: + return + if self.setup_runs_locally(): + return + # copy back files (may not exist, for instance if there was an early error of process): + try: + self.rem_host.scpfrom('scp-back-log', self.remote_log_file, self.log_file) + except Exception as e: + self.log(repr(e)) + + def setup_runs_locally(self): + return self.remote_user is None + + def start(self, epc): + self.log('Starting srsENB') + self.epc = epc + self.run_dir = util.Dir(self.suite_run.get_test_run_dir().new_dir(self.name())) + self.configure() + if self.remote_user: + self.start_remotely() + else: + self.start_locally() + + def start_remotely(self): + self.inst = util.Dir(os.path.abspath(self.suite_run.trial.get_inst('srslte'))) + lib = self.inst.child('lib') + if not os.path.isdir(lib): + raise log.Error('No lib/ in', self.inst) + if not self.inst.isfile('bin', srsENB.BINFILE): + raise log.Error('No %s binary in' % srsENB.BINFILE, self.inst) + + self.rem_host = remote.RemoteHost(self.run_dir, self.remote_user, self._addr) + remote_prefix_dir = util.Dir(srsENB.REMOTE_DIR) + self.remote_inst = util.Dir(remote_prefix_dir.child(os.path.basename(str(self.inst)))) + remote_run_dir = util.Dir(remote_prefix_dir.child(srsENB.BINFILE)) + + self.remote_config_file = remote_run_dir.child(srsENB.CFGFILE) + self.remote_config_sib_file = remote_run_dir.child(srsENB.CFGFILE_SIB) + self.remote_config_rr_file = remote_run_dir.child(srsENB.CFGFILE_RR) + self.remote_config_drb_file = remote_run_dir.child(srsENB.CFGFILE_DRB) + self.remote_log_file = remote_run_dir.child(srsENB.LOGFILE) + + self.rem_host.recreate_remote_dir(self.remote_inst) + self.rem_host.scp('scp-inst-to-remote', str(self.inst), remote_prefix_dir) + self.rem_host.create_remote_dir(remote_run_dir) + self.rem_host.scp('scp-cfg-to-remote', self.config_file, self.remote_config_file) + self.rem_host.scp('scp-cfg-sib-to-remote', self.config_sib_file, self.remote_config_sib_file) + self.rem_host.scp('scp-cfg-rr-to-remote', self.config_rr_file, self.remote_config_rr_file) + self.rem_host.scp('scp-cfg-drb-to-remote', self.config_drb_file, self.remote_config_drb_file) + + remote_env = { 'LD_LIBRARY_PATH': self.remote_inst.child('lib') } + remote_binary = self.remote_inst.child('bin', srsENB.BINFILE) + args = (remote_binary, self.remote_config_file, + '--enb_files.sib_config=' + self.remote_config_sib_file, + '--enb_files.rr_config=' + self.remote_config_rr_file, + '--enb_files.drb_config=' + self.remote_config_drb_file, + '--rf.device_name=zmq', + '--rf.device_args="fail_on_disconnect=true,tx_port=tcp://'+ self.addr() +':2000,rx_port=tcp://'+ self.ue.addr() +':2001,id=enb,base_srate='+ str(self.base_srate) + '"', + '--expert.nof_phy_threads=1', + '--expert.rrc_inactivity_timer=1500', + '--enb.n_prb=' + str(self.nof_prb), + '--log.filename=' + self.remote_log_file) + + self.process = self.rem_host.RemoteProcessFixIgnoreSIGHUP(srsENB.BINFILE, util.Dir(srsENB.REMOTE_DIR), args, remote_env=remote_env) + self.suite_run.remember_to_stop(self.process) + self.process.launch() + + def start_locally(self): + inst = util.Dir(os.path.abspath(self.suite_run.trial.get_inst('srslte'))) + + binary = inst.child('bin', BINFILE) + if not os.path.isfile(binary): + raise log.Error('Binary missing:', binary) + lib = inst.child('lib') + if not os.path.isdir(lib): + raise log.Error('No lib/ in', inst) + + env = { 'LD_LIBRARY_PATH': util.prepend_library_path(lib) } + + self.dbg(run_dir=self.run_dir, binary=binary, env=env) + args = (binary, os.path.abspath(self.config_file), + '--enb_files.sib_config=' + os.path.abspath(self.config_sib_file), + '--enb_files.rr_config=' + os.path.abspath(self.config_rr_file), + '--enb_files.drb_config=' + os.path.abspath(self.config_drb_file), + '--rf.device_name=zmq', + '--rf.device_args="fail_on_disconnect=true,tx_port=tcp://'+ self.addr() +':2000,rx_port=tcp://'+ self.ue.addr() +':2001,id=enb,base_srate='+ str(self.base_srate) + '"', + '--expert.nof_phy_threads=1', + '--expert.rrc_inactivity_timer=1500', + '--enb.n_prb=' + str(self.nof_prb), + '--log.filename=' + self.log_file) + + self.process = process.Process(self.name(), self.run_dir, args, env=env) + self.suite_run.remember_to_stop(self.process) + self.process.launch() + + def gen_conf_file(self, path, filename): + self.dbg(config_file=path) + + values = dict(enb=config.get_defaults('srsenb')) + config.overlay(values, self.suite_run.config()) + config.overlay(values, dict(enb={ 'run_addr': self.addr(), + 'mme_addr': self.epc.addr()})) + + self.dbg('srsENB ' + filename + ':\n' + pprint.pformat(values)) + + with open(path, 'w') as f: + r = template.render(filename, values) + self.dbg(r) + f.write(r) + + def configure(self): + self.config_file = self.run_dir.new_file(srsENB.CFGFILE) + self.config_sib_file = self.run_dir.new_file(srsENB.CFGFILE_SIB) + self.config_rr_file = self.run_dir.new_file(srsENB.CFGFILE_RR) + self.config_drb_file = self.run_dir.new_file(srsENB.CFGFILE_DRB) + self.log_file = self.run_dir.new_file(srsENB.LOGFILE) + + self.gen_conf_file(self.config_file, srsENB.CFGFILE) + self.gen_conf_file(self.config_sib_file, srsENB.CFGFILE_SIB) + self.gen_conf_file(self.config_rr_file, srsENB.CFGFILE_RR) + self.gen_conf_file(self.config_drb_file, srsENB.CFGFILE_DRB) + + def ue_add(self, ue): + if self.ue is not None: + raise log.Error("More than one UE per ENB not yet supported (ZeroMQ)") + self.ue = ue + + def running(self): + return not self.process.terminated() + + def addr(self): + return self._addr + +# vim: expandtab tabstop=4 shiftwidth=4 diff --git a/src/osmo_gsm_tester/srs_epc.py b/src/osmo_gsm_tester/srs_epc.py new file mode 100644 index 00000000..0a0036e9 --- /dev/null +++ b/src/osmo_gsm_tester/srs_epc.py @@ -0,0 +1,207 @@ +# osmo_gsm_tester: specifics for running an SRS EPC process +# +# Copyright (C) 2020 by sysmocom - s.f.m.c. GmbH +# +# Author: Pau Espin Pedrol +# +# This program 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. +# +# This program 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 this program. If not, see . + +import os +import pprint + +from . import log, util, config, template, process, remote + +class srsEPC(log.Origin): + + REMOTE_DIR = '/osmo-gsm-tester-srsepc' + BINFILE = 'srsepc' + CFGFILE = 'srsepc.conf' + DBFILE = 'srsepc_user_db.csv' + PCAPFILE = 'srsepc.pcap' + LOGFILE = 'srsepc.log' + + def __init__(self, suite_run, run_node): + super().__init__(log.C_RUN, 'srsepc') + self._addr = run_node.run_addr() + self.set_name('srsepc_%s' % self._addr) + self.run_dir = None + self.config_file = None + self.db_file = None + self.log_file = None + self.pcap_file = None + self.process = None + self.rem_host = None + self.remote_config_file = None + self.remote_db_file = None + self.remote_log_file = None + self.remote_pcap_file = None + self.subscriber_list = [] + self.suite_run = suite_run + self._run_node = run_node + + def cleanup(self): + if self.process is None: + return + if self._run_node.is_local(): + return + # copy back files (may not exist, for instance if there was an early error of process): + try: + self.rem_host.scpfrom('scp-back-log', self.remote_log_file, self.log_file) + except Exception as e: + self.log(repr(e)) + try: + self.rem_host.scpfrom('scp-back-pcap', self.remote_pcap_file, self.pcap_file) + except Exception as e: + self.log(repr(e)) + + def start(self): + self.log('Starting srsepc') + self.run_dir = util.Dir(self.suite_run.get_test_run_dir().new_dir(self.name())) + self.configure() + if self._run_node.is_local(): + self.start_locally() + else: + self.start_remotely() + + def start_remotely(self): + self.inst = util.Dir(os.path.abspath(self.suite_run.trial.get_inst('srslte'))) + lib = self.inst.child('lib') + if not os.path.isdir(lib): + raise log.Error('No lib/ in', self.inst) + if not self.inst.isfile('bin', srsEPC.BINFILE): + raise log.Error('No %s binary in' % srsEPC.BINFILE, self.inst) + + self.rem_host = remote.RemoteHost(self.run_dir, self._run_node.ssh_user(), self._run_node.ssh_addr()) + remote_prefix_dir = util.Dir(srsEPC.REMOTE_DIR) + remote_inst = util.Dir(remote_prefix_dir.child(os.path.basename(str(self.inst)))) + remote_run_dir = util.Dir(remote_prefix_dir.child(srsEPC.BINFILE)) + self.remote_config_file = remote_run_dir.child(srsEPC.CFGFILE) + self.remote_db_file = remote_run_dir.child(srsEPC.DBFILE) + self.remote_log_file = remote_run_dir.child(srsEPC.LOGFILE) + self.remote_pcap_file = remote_run_dir.child(srsEPC.PCAPFILE) + + self.rem_host.recreate_remote_dir(remote_inst) + self.rem_host.scp('scp-inst-to-remote', str(self.inst), remote_prefix_dir) + self.rem_host.create_remote_dir(remote_run_dir) + self.rem_host.scp('scp-cfg-to-remote', self.config_file, self.remote_config_file) + self.rem_host.scp('scp-db-to-remote', self.db_file, self.remote_db_file) + + remote_lib = remote_inst.child('lib') + remote_binary = remote_inst.child('bin', srsEPC.BINFILE) + # setting capabilities will later disable use of LD_LIBRARY_PATH from ELF loader -> modify RPATH instead. + self.log('Setting RPATH for srsepc') + self.rem_host.change_elf_rpath(remote_binary, remote_lib) + # srsepc requires CAP_NET_ADMIN to create tunnel devices: ioctl(TUNSETIFF): + self.log('Applying CAP_NET_ADMIN capability to srsepc') + self.rem_host.setcap_net_admin(remote_binary) + + args = (remote_binary, self.remote_config_file, + '--hss.db_file=' + self.remote_db_file, + '--log.filename=' + self.remote_log_file, + '--pcap.enable=true', + '--pcap.filename=' + self.remote_pcap_file) + + self.process = self.rem_host.RemoteProcess(srsEPC.BINFILE, args) + #self.process = self.rem_host.RemoteProcessFixIgnoreSIGHUP(srsEPC.BINFILE, remote_run_dir, args) + self.suite_run.remember_to_stop(self.process) + self.process.launch() + + def start_locally(self): + inst = util.Dir(os.path.abspath(self.suite_run.trial.get_inst('srslte'))) + + binary = inst.child('bin', BINFILE) + if not os.path.isfile(binary): + raise log.Error('Binary missing:', binary) + lib = inst.child('lib') + if not os.path.isdir(lib): + raise log.Error('No lib/ in', inst) + + env = {} + # setting capabilities will later disable use of LD_LIBRARY_PATH from ELF loader -> modify RPATH instead. + self.log('Setting RPATH for srsepc') + # srsepc binary needs patchelf <= 0.9 (0.10 and current master fail) to avoid failing during patch. OS#4389, patchelf-GH#192. + util.change_elf_rpath(binary, util.prepend_library_path(lib), self.run_dir.new_dir('patchelf')) + # srsepc requires CAP_NET_ADMIN to create tunnel devices: ioctl(TUNSETIFF): + self.log('Applying CAP_NET_ADMIN capability to srsepc') + util.setcap_net_admin(binary, self.run_dir.new_dir('setcap_net_admin')) + + self.dbg(run_dir=self.run_dir, binary=binary, env=env) + args = (binary, os.path.abspath(self.config_file), + '--hss.db_file=' + self.db_file, + '--log.filename=' + self.log_file, + '--pcap.enable=true', + '--pcap.filename=' + self.pcap_file) + + self.process = process.Process(self.name(), self.run_dir, args, env=env) + self.suite_run.remember_to_stop(self.process) + self.process.launch() + + def configure(self): + self.config_file = self.run_dir.new_file(srsEPC.CFGFILE) + self.db_file = self.run_dir.new_file(srsEPC.DBFILE) + self.log_file = self.run_dir.new_file(srsEPC.LOGFILE) + self.pcap_file = self.run_dir.new_file(srsEPC.PCAPFILE) + self.dbg(config_file=self.config_file, db_file=self.db_file) + + values = dict(epc=config.get_defaults('srsepc')) + config.overlay(values, dict(epc=dict(hss=dict(subscribers=self.subscriber_list)))) + config.overlay(values, self.suite_run.config()) + config.overlay(values, dict(epc={'run_addr': self.addr()})) + + self.dbg('SRSEPC CONFIG:\n' + pprint.pformat(values)) + + with open(self.config_file, 'w') as f: + r = template.render(srsEPC.CFGFILE, values) + self.dbg(r) + f.write(r) + with open(self.db_file, 'w') as f: + r = template.render(srsEPC.DBFILE, values) + self.dbg(r) + f.write(r) + + def subscriber_add(self, modem, msisdn=None, algo_str=None): + if msisdn is None: + msisdn = self.suite_run.resources_pool.next_msisdn(modem) + modem.set_msisdn(msisdn) + + if algo_str is None: + algo_str = modem.auth_algo() or util.OSMO_AUTH_ALGO_NONE + + if algo_str != util.OSMO_AUTH_ALGO_NONE and not modem.ki(): + raise log.Error("Auth algo %r selected but no KI specified" % algo_str) + + subscriber_id = len(self.subscriber_list) # list index + self.subscriber_list.append({'id': subscriber_id, 'imsi': modem.imsi(), 'msisdn': msisdn, 'auth_algo': algo_str, 'ki': modem.ki(), 'opc': None}) + + self.log('Add subscriber', msisdn=msisdn, imsi=modem.imsi(), subscriber_id=subscriber_id, + algo_str=algo_str) + return subscriber_id + + def enb_is_connected(self, enb): + # FIXME: srspec's stdout: "S1 Setup Request - eNB Id 0x66c0", but srsenb.conf has "enb_id = 0x19B" + return 'S1 Setup Request - eNB Id' in (self.process.get_stdout() or '') + + def running(self): + return not self.process.terminated() + + def addr(self): + return self._addr + + def tun_addr(self): + return '172.16.0.1' + + def run_node(self): + return self._run_node + +# vim: expandtab tabstop=4 shiftwidth=4 diff --git a/src/osmo_gsm_tester/srs_ue.py b/src/osmo_gsm_tester/srs_ue.py new file mode 100644 index 00000000..c68c7262 --- /dev/null +++ b/src/osmo_gsm_tester/srs_ue.py @@ -0,0 +1,213 @@ +# osmo_gsm_tester: specifics for running an SRS UE process +# +# Copyright (C) 2020 by sysmocom - s.f.m.c. GmbH +# +# Author: Pau Espin Pedrol +# +# This program 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. +# +# This program 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 this program. If not, see . + +import os +import pprint + +from . import log, util, config, template, process, remote +from .run_node import RunNode +from .ms import MS + +class srsUE(MS): + + REMOTE_DIR = '/osmo-gsm-tester-srsue' + BINFILE = 'srsue' + CFGFILE = 'srsue.conf' + PCAPFILE = 'srsue.pcap' + LOGFILE = 'srsue.log' + + def __init__(self, suite_run, conf): + self._addr = conf.get('addr', None) + if self._addr is None: + raise log.Error('addr not set') + super().__init__('srsue_%s' % self._addr, conf) + self.enb = None + self.run_dir = None + self.config_file = None + self.log_file = None + self.pcap_file = None + self.process = None + self.rem_host = None + self.remote_config_file = None + self.remote_log_file = None + self.remote_pcap_file = None + self.suite_run = suite_run + self.nof_prb=50 + if self.nof_prb == 75: + self.base_srate=15.36e6 + else: + self.base_srate=23.04e6 + self.remote_user = conf.get('remote_user', None) + + def cleanup(self): + if self.process is None: + return + if self.setup_runs_locally(): + return + # copy back files (may not exist, for instance if there was an early error of process): + try: + self.rem_host.scpfrom('scp-back-log', self.remote_log_file, self.log_file) + except Exception as e: + self.log(repr(e)) + try: + self.rem_host.scpfrom('scp-back-pcap', self.remote_pcap_file, self.pcap_file) + except Exception as e: + self.log(repr(e)) + + def setup_runs_locally(self): + return self.remote_user is None + + def netns(self): + return "srsue1" + + def connect(self, enb): + self.log('Starting srsue') + self.enb = enb + self.run_dir = util.Dir(self.suite_run.get_test_run_dir().new_dir(self.name())) + self.configure() + if self.setup_runs_locally(): + self.start_locally() + else: + self.start_remotely() + + def start_remotely(self): + self.inst = util.Dir(os.path.abspath(self.suite_run.trial.get_inst('srslte'))) + lib = self.inst.child('lib') + if not os.path.isdir(lib): + raise log.Error('No lib/ in', self.inst) + if not self.inst.isfile('bin', srsUE.BINFILE): + raise log.Error('No %s binary in' % srsUE.BINFILE, self.inst) + + self.rem_host = remote.RemoteHost(self.run_dir, self.remote_user, self._addr) + remote_prefix_dir = util.Dir(srsUE.REMOTE_DIR) + remote_inst = util.Dir(remote_prefix_dir.child(os.path.basename(str(self.inst)))) + remote_run_dir = util.Dir(remote_prefix_dir.child(srsUE.BINFILE)) + self.remote_config_file = remote_run_dir.child(srsUE.CFGFILE) + self.remote_log_file = remote_run_dir.child(srsUE.LOGFILE) + self.remote_pcap_file = remote_run_dir.child(srsUE.PCAPFILE) + + self.rem_host.recreate_remote_dir(remote_inst) + self.rem_host.scp('scp-inst-to-remote', str(self.inst), remote_prefix_dir) + self.rem_host.create_remote_dir(remote_run_dir) + self.rem_host.scp('scp-cfg-to-remote', self.config_file, self.remote_config_file) + + remote_lib = remote_inst.child('lib') + remote_binary = remote_inst.child('bin', srsUE.BINFILE) + # setting capabilities will later disable use of LD_LIBRARY_PATH from ELF loader -> modify RPATH instead. + self.log('Setting RPATH for srsue') + # srsue binary needs patchelf >= 0.9+52 to avoid failing during patch. OS#4389, patchelf-GH#192. + self.rem_host.set_remote_env({'PATCHELF_BIN': '/opt/bin/patchelf-v0.10' }) + self.rem_host.change_elf_rpath(remote_binary, remote_lib) + + # srsue requires CAP_SYS_ADMIN to cjump to net network namespace: netns(CLONE_NEWNET): + # srsue requires CAP_NET_ADMIN to create tunnel devices: ioctl(TUNSETIFF): + self.log('Applying CAP_SYS_ADMIN+CAP_NET_ADMIN capability to srsue') + self.rem_host.setcap_netsys_admin(remote_binary) + + #'strace', '-ff', + args = (remote_binary, self.remote_config_file, + '--rf.device_name=zmq', + '--rf.device_args="tx_port=tcp://'+ self.addr() +':2001,rx_port=tcp://'+ self.enb.addr() +':2000,id=ue,base_srate='+ str(self.base_srate) + '"', + '--phy.nof_phy_threads=1', + '--gw.netns=' + self.netns(), + '--log.filename=' + 'stdout', #self.remote_log_file, + '--pcap.enable=true', + '--pcap.filename=' + self.remote_pcap_file) + + self.process = self.rem_host.RemoteProcessFixIgnoreSIGHUP(srsUE.BINFILE, util.Dir(srsUE.REMOTE_DIR), args) + #self.process = self.rem_host.RemoteProcessFixIgnoreSIGHUP(srsUE.BINFILE, remote_run_dir, args, remote_lib) + self.suite_run.remember_to_stop(self.process) + self.process.launch() + + def start_locally(self): + inst = util.Dir(os.path.abspath(self.suite_run.trial.get_inst('srslte'))) + + binary = inst.child('bin', BINFILE) + if not os.path.isfile(binary): + raise log.Error('Binary missing:', binary) + lib = inst.child('lib') + if not os.path.isdir(lib): + raise log.Error('No lib/ in', inst) + + env = {} + + # setting capabilities will later disable use of LD_LIBRARY_PATH from ELF loader -> modify RPATH instead. + self.log('Setting RPATH for srsue') + util.change_elf_rpath(binary, util.prepend_library_path(lib), self.run_dir.new_dir('patchelf')) + + # srsue requires CAP_SYS_ADMIN to cjump to net network namespace: netns(CLONE_NEWNET): + # srsue requires CAP_NET_ADMIN to create tunnel devices: ioctl(TUNSETIFF): + self.log('Applying CAP_SYS_ADMIN+CAP_NET_ADMIN capability to srsue') + util.setcap_netsys_admin(binary, self.run_dir.new_dir('setcap_netsys_admin')) + + args = (binary, os.path.abspath(self.config_file), + '--rf.device_name=zmq', + '--rf.device_args="tx_port=tcp://'+ self.addr() +':2001,rx_port=tcp://'+ self.enb.addr() +':2000,id=ue,base_srate='+ str(self.base_srate) + '"', + '--phy.nof_phy_threads=1', + '--gw.netns=' + self.netns(), + '--log.filename=' + self.log_file, + '--pcap.enable=true', + '--pcap.filename=' + self.pcap_file) + + self.dbg(run_dir=self.run_dir, binary=binary, env=env) + self.process = process.Process(self.name(), self.run_dir, args, env=env) + self.suite_run.remember_to_stop(self.process) + self.process.launch() + + def configure(self): + self.config_file = self.run_dir.new_file(srsUE.CFGFILE) + self.log_file = self.run_dir.child(srsUE.LOGFILE) + self.pcap_file = self.run_dir.new_file(srsUE.PCAPFILE) + self.dbg(config_file=self.config_file) + + values = dict(ue=config.get_defaults('srsue')) + config.overlay(values, self.suite_run.config()) + config.overlay(values, dict(ue=self._conf)) + + self.dbg('SRSUE CONFIG:\n' + pprint.pformat(values)) + + with open(self.config_file, 'w') as f: + r = template.render(srsUE.CFGFILE, values) + self.dbg(r) + f.write(r) + + def is_connected(self, mcc_mnc=None): + return 'Network attach successful.' in (self.process.get_stdout() or '') + + def is_attached(self): + return self.is_connected() + + def running(self): + return not self.process.terminated() + + def addr(self): + return self._addr + + def run_node(self): + # TODO: move to an object + return RunNode(RunNode.T_REM_SSH, self._addr, self.remote_user, self._addr) + + def run_netns_wait(self, name, popen_args): + if self.setup_runs_locally(): + proc = process.NetNSProcess(name, self.run_dir.new_dir(name), self.netns(), popen_args, env={}) + else: + proc = self.rem_host.RemoteNetNSProcess(name, self.netns(), popen_args, env={}) + proc.launch_sync() + +# vim: expandtab tabstop=4 shiftwidth=4 diff --git a/src/osmo_gsm_tester/suite.py b/src/osmo_gsm_tester/suite.py index 457bfb34..c2faa362 100644 --- a/src/osmo_gsm_tester/suite.py +++ b/src/osmo_gsm_tester/suite.py @@ -25,6 +25,7 @@ from . import config, log, util, resource, test from .event_loop import MainLoop from . import osmo_nitb, osmo_hlr, osmo_mgcpgw, osmo_mgw, osmo_msc, osmo_bsc, osmo_stp, osmo_ggsn, osmo_sgsn, esme, osmocon, ms_driver, iperf3, process from . import run_node +from . import srs_epc class Timeout(Exception): pass @@ -362,6 +363,18 @@ class SuiteRun(log.Origin): def run_node(self, specifics=None): return run_node.RunNode.from_conf(self.reserved_resources.get(resource.R_RUN_NODE, specifics=specifics)) + def enb(self, specifics=None): + enb = enb_obj(self, self.reserved_resources.get(resource.R_ENB, specifics=specifics)) + self.register_for_cleanup(enb) + return enb + + def epc(self, run_node=None): + if run_node is None: + run_node = self.run_node() + epc_obj = srs_epc.srsEPC(self, run_node) + self.register_for_cleanup(epc_obj) + return epc_obj + def osmocon(self, specifics=None): conf = self.reserved_resources.get(resource.R_OSMOCON, specifics=specifics) osmocon_obj = osmocon.Osmocon(self, conf=conf) @@ -481,4 +494,12 @@ def bts_obj(suite_run, conf): raise RuntimeError('No such BTS type is defined: %r' % bts_type) return bts_class(suite_run, conf) +def enb_obj(suite_run, conf): + enb_type = conf.get('type') + log.dbg('create ENB object', type=enb_type) + enb_class = resource.KNOWN_ENB_TYPES.get(enb_type) + if enb_class is None: + raise RuntimeError('No such ENB type is defined: %r' % enb_type) + return enb_class(suite_run, conf) + # vim: expandtab tabstop=4 shiftwidth=4 diff --git a/src/osmo_gsm_tester/templates/srsenb.conf.tmpl b/src/osmo_gsm_tester/templates/srsenb.conf.tmpl new file mode 100644 index 00000000..0eba35b9 --- /dev/null +++ b/src/osmo_gsm_tester/templates/srsenb.conf.tmpl @@ -0,0 +1,279 @@ +##################################################################### +# srsENB configuration file +##################################################################### + +##################################################################### +# eNB configuration +# +# enb_id: 20-bit eNB identifier. +# cell_id: 8-bit cell identifier. +# tac: 16-bit Tracking Area Code. +# mcc: Mobile Country Code +# mnc: Mobile Network Code +# mme_addr: IP address of MME for S1 connnection +# gtp_bind_addr: Local IP address to bind for GTP connection +# s1c_bind_addr: Local IP address to bind for S1AP connection +# n_prb: Number of Physical Resource Blocks (6,15,25,50,75,100) +# tm: Transmission mode 1-4 (TM1 default) +# nof_ports: Number of Tx ports (1 port default, set to 2 for TM2/3/4) +# +##################################################################### +[enb] +enb_id = 0x19B +cell_id = 0x01 +phy_cell_id = 1 +tac = 0x0007 +mcc = ${enb.mcc} +mnc = ${enb.mnc} +mme_addr = ${enb.mme_addr} +gtp_bind_addr = ${enb.run_addr} +s1c_bind_addr = ${enb.run_addr} +n_prb = 50 +#tm = 4 +#nof_ports = 2 + +##################################################################### +# eNB configuration files +# +# sib_config: SIB1, SIB2 and SIB3 configuration file +# note: when enabling mbms, use the sib.conf.mbsfn configuration file which includes SIB13 +# rr_config: Radio Resources configuration file +# drb_config: DRB configuration file +##################################################################### +[enb_files] +sib_config = sib.conf +rr_config = rr.conf +drb_config = drb.conf + +##################################################################### +# RF configuration +# +# dl_earfcn: EARFCN code for DL +# tx_gain: Transmit gain (dB). +# rx_gain: Optional receive gain (dB). If disabled, AGC if enabled +# +# Optional parameters: +# dl_freq: Override DL frequency corresponding to dl_earfcn +# ul_freq: Override UL frequency corresponding to dl_earfcn (must be set if dl_freq is set) +# device_name: Device driver family. Supported options: "auto" (uses first found), "UHD" or "bladeRF" +# device_args: Arguments for the device driver. Options are "auto" or any string. +# Default for UHD: "recv_frame_size=9232,send_frame_size=9232" +# Default for bladeRF: "" +# #time_adv_nsamples: Transmission time advance (in number of samples) to compensate for RF delay +# from antenna to timestamp insertion. +# Default "auto". B210 USRP: 100 samples, bladeRF: 27. +# burst_preamble_us: Preamble length to transmit before start of burst. +# Default "auto". B210 USRP: 400 us, bladeRF: 0 us. +##################################################################### +[rf] +dl_earfcn = 3400 +tx_gain = 80 +rx_gain = 40 + +#device_name = auto + +# For best performance in 2x2 MIMO and >= 15 MHz use the following device_args settings: +# USRP B210: num_recv_frames=64,num_send_frames=64 + +# For best performance when BW<5 MHz (25 PRB), use the following device_args settings: +# USRP B210: send_frame_size=512,recv_frame_size=512 + +#device_args = auto +#time_adv_nsamples = auto +#burst_preamble_us = auto + + +##################################################################### +# MAC-layer packet capture configuration +# +# Packets are captured to file in the compact format decoded by +# the Wireshark mac-lte-framed dissector and with DLT 147. +# To use the dissector, edit the preferences for DLT_USER to +# add an entry with DLT=147, Payload Protocol=mac-lte-framed. +# For more information see: https://wiki.wireshark.org/MAC-LTE +# +# Please note that this setting will by default only capture MAC +# frames on dedicated channels, and not SIB. You have to build with +# WRITE_SIB_PCAP enabled in enb/src/stack/mac/mac.cc if you want +# SIB to be part of the MAC pcap file. +# +# enable: Enable MAC layer packet captures (true/false) +# filename: File path to use for packet captures +##################################################################### +[pcap] +enable = false +filename = /tmp/enb.pcap + +##################################################################### +# Log configuration +# +# Log levels can be set for individual layers. "all_level" sets log +# level for all layers unless otherwise configured. +# Format: e.g. phy_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. phy_hex_limit = 32 +# +# Logging layers: rf, phy, phy_lib, mac, rlc, pdcp, rrc, gtpu, s1ap, 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 +# file_max_size: Maximum file size (in kilobytes). When passed, multiple files are created. +# If set to negative, a single log file will be created. +##################################################################### +[log] +all_level = warning +all_hex_limit = 32 +filename = /tmp/enb.log +file_max_size = -1 + +[gui] +enable = false + +##################################################################### +# Scheduler configuration options +# +# max_aggr_level: Optional maximum aggregation level index (l=log2(L) can be 0, 1, 2 or 3) +# pdsch_mcs: Optional fixed PDSCH MCS (ignores reported CQIs if specified) +# pdsch_max_mcs: Optional PDSCH MCS limit +# pusch_mcs: Optional fixed PUSCH MCS (ignores reported CQIs if specified) +# pusch_max_mcs: Optional PUSCH MCS limit +# #nof_ctrl_symbols: Number of control symbols +# +##################################################################### +[scheduler] +#max_aggr_level = -1 +#pdsch_mcs = -1 +#pdsch_max_mcs = -1 +#pusch_mcs = -1 +pusch_max_mcs = 16 +nof_ctrl_symbols = 3 + +##################################################################### +# eMBMS configuration options +# +# enable: Enable MBMS transmission in the eNB +# m1u_multiaddr: Multicast addres the M1-U socket will register to +# m1u_if_addr: Address of the inteferface the M1-U interface will listen for multicast packets. +# +##################################################################### +[embms] +#enable = false +#m1u_multiaddr = 239.255.0.1 +#m1u_if_addr = 127.0.1.201 + + + +##################################################################### +# Channel emulator options: +# enable: Enable/Disable internal Downlink/Uplink channel emulator +# +# -- Fading emulator +# fading.enable: Enable/disable fading simulator +# fading.model: Fading model + maximum doppler (E.g. none, epa5, eva70, etu300, etc) +# +# -- Delay Emulator delay(t) = delay_min + (delay_max - delay_min) * (1 + sin(2pi*t/period)) / 2 +# Maximum speed [m/s]: (delay_max - delay_min) * pi * 300 / period +# delay.enable: Enable/disable delay simulator +# delay.period_s: Delay period in seconds. +# delay.init_time_s: Delay initial time in seconds. +# delay.maximum_us: Maximum delay in microseconds +# delay.minumum_us: Minimum delay in microseconds +# +# -- Radio-Link Failure (RLF) Emulator +# rlf.enable: Enable/disable RLF simulator +# rlf.t_on_ms: Time for On state of the channel (ms) +# rlf.t_off_ms: Time for Off state of the channel (ms) +# +# -- High Speed Train Doppler model simulator +# hst.enable: Enable/Disable HST simulator +# hst.period_s: HST simulation period in seconds +# hst.fd_hz: Doppler frequency in Hz +# hst.init_time_s: Initial time in seconds +##################################################################### +[channel.dl] +#enable = false + +[channel.dl.fading] +#enable = false +#model = none + +[channel.dl.delay] +#enable = false +#period_s = 3600 +#init_time_s = 0 +#maximum_us = 100 +#minimum_us = 10 + +[channel.dl.rlf] +#enable = false +#t_on_ms = 10000 +#t_off_ms = 2000 + +[channel.dl.hst] +#enable = false +#period_s = 7.2 +#fd_hz = 750.0 +#init_time_s = 0.0 + +[channel.ul] +#enable = false + +[channel.ul.fading] +#enable = false +#model = none + +[channel.ul.delay] +#enable = false +#period_s = 3600 +#init_time_s = 0 +#maximum_us = 100 +#minimum_us = 10 + +[channel.ul.rlf] +#enable = false +#t_on_ms = 10000 +#t_off_ms = 2000 + +[channel.ul.hst] +#enable = false +#period_s = 7.2 +#fd_hz = -750.0 +#init_time_s = 0.0 + + +##################################################################### +# Expert configuration options +# +# pusch_max_its: Maximum number of turbo decoder iterations (Default 4) +# pusch_8bit_decoder: Use 8-bit for LLR representation and turbo decoder trellis computation (Experimental) +# nof_phy_threads: Selects the number of PHY threads (maximum 4, minimum 1, default 2) +# metrics_period_secs: Sets the period at which metrics are requested from the eNB. +# metrics_csv_enable: Write eNB metrics to CSV file. +# metrics_csv_filename: File path to use for CSV metrics. +# pregenerate_signals: Pregenerate uplink signals after attach. Improves CPU performance. +# tx_amplitude: Transmit amplitude factor (set 0-1 to reduce PAPR) +# link_failure_nof_err: Number of PUSCH failures after which a radio-link failure is triggered. +# a link failure is when SNR<0 and CRC=KO +# max_prach_offset_us: Maximum allowed RACH offset (in us) +# eea_pref_list: Ordered preference list for the selection of encryption algorithm (EEA) (default: EEA0, EEA2, EEA1). +# eia_pref_list: Ordered preference list for the selection of integrity algorithm (EIA) (default: EIA2, EIA1, EIA0). +# +##################################################################### +[expert] +#pusch_max_its = 8 # These are half iterations +#pusch_8bit_decoder = false +#nof_phy_threads = 3 +#metrics_period_secs = 1 +#metrics_csv_enable = false +#metrics_csv_filename = /tmp/enb_metrics.csv +#pregenerate_signals = false +#tx_amplitude = 0.6 +#link_failure_nof_err = 50 +#rrc_inactivity_timer = 60000 +#max_prach_offset_us = 30 +#eea_pref_list = EEA0, EEA2, EEA1 +#eia_pref_list = EIA2, EIA1, EIA0 diff --git a/src/osmo_gsm_tester/templates/srsenb_drb.conf.tmpl b/src/osmo_gsm_tester/templates/srsenb_drb.conf.tmpl new file mode 100644 index 00000000..32b6e937 --- /dev/null +++ b/src/osmo_gsm_tester/templates/srsenb_drb.conf.tmpl @@ -0,0 +1,54 @@ + +// All times are in ms. Use -1 for infinity, where available + +qci_config = ( + +{ + qci=7; + pdcp_config = { + discard_timer = 100; + pdcp_sn_size = 12; + } + rlc_config = { + ul_um = { + sn_field_length = 10; + }; + dl_um = { + sn_field_length = 10; + t_reordering = 45; + }; + }; + logical_channel_config = { + priority = 13; + prioritized_bit_rate = -1; + bucket_size_duration = 100; + log_chan_group = 2; + }; +}, +{ + qci=9; + pdcp_config = { + discard_timer = -1; + status_report_required = true; + } + rlc_config = { + ul_am = { + t_poll_retx = 120; + poll_pdu = 64; + poll_byte = 750; + max_retx_thresh = 16; + }; + dl_am = { + t_reordering = 50; + t_status_prohibit = 50; + }; + }; + logical_channel_config = { + priority = 11; + prioritized_bit_rate = -1; + bucket_size_duration = 100; + log_chan_group = 3; + }; +} + +); diff --git a/src/osmo_gsm_tester/templates/srsenb_rr.conf.tmpl b/src/osmo_gsm_tester/templates/srsenb_rr.conf.tmpl new file mode 100644 index 00000000..1fff1896 --- /dev/null +++ b/src/osmo_gsm_tester/templates/srsenb_rr.conf.tmpl @@ -0,0 +1,90 @@ +mac_cnfg = +{ + phr_cnfg = + { + dl_pathloss_change = "dB3"; // Valid: 1, 3, 6 or INFINITY + periodic_phr_timer = 50; + prohibit_phr_timer = 0; + }; + ulsch_cnfg = + { + max_harq_tx = 4; + periodic_bsr_timer = 20; // in ms + retx_bsr_timer = 320; // in ms + }; + + time_alignment_timer = -1; // -1 is infinity +}; + +phy_cnfg = +{ + phich_cnfg = + { + duration = "Normal"; + resources = "1/6"; + }; + + pusch_cnfg_ded = + { + beta_offset_ack_idx = 6; + beta_offset_ri_idx = 6; + beta_offset_cqi_idx = 6; + }; + + // PUCCH-SR resources are scheduled on time-frequeny domain first, then multiplexed in the same resource. + sched_request_cnfg = + { + dsr_trans_max = 64; + period = 20; // in ms + subframe = [1]; // vector of subframe indices allowed for SR transmissions + nof_prb = 2; // number of PRBs on each extreme used for SR (total prb is twice this number) + }; + cqi_report_cnfg = + { + mode = "periodic"; + simultaneousAckCQI = true; + period = 40; // in ms + subframe = [0]; + nof_prb = 2; + m_ri = 8; // RI period in CQI period + }; +}; + +cell_list = +( + { + // rf_port = 0; + // cell_id = 0x01; + // tac = 0x0001; + // pci = 1; + // root_seq_idx = 204; + // dl_earfcn = 3400; + // ul_earfcn = 474; + ho_active = false; + + // CA cells + scell_list = ( + {cell_id = 0x02; cross_carrier_scheduling = false; scheduling_cell_id = 0x02; ul_allowed = true} + ) + + // Cells available for handover + meas_cell_list = + ( + { + eci = 0x19C02; + dl_earfcn = 2850; + pci = 2; + } + ); + + // ReportCfg (only A3 supported) + meas_report_desc = { + a3_report_type = "RSRP"; + a3_offset = 6; + a3_hysteresis = 0; + a3_time_to_trigger = 480; + rsrq_config = 4; + }; + } + // Add here more cells +); diff --git a/src/osmo_gsm_tester/templates/srsenb_sib.conf.tmpl b/src/osmo_gsm_tester/templates/srsenb_sib.conf.tmpl new file mode 100644 index 00000000..201d022c --- /dev/null +++ b/src/osmo_gsm_tester/templates/srsenb_sib.conf.tmpl @@ -0,0 +1,118 @@ +sib1 = +{ + intra_freq_reselection = "Allowed"; + q_rx_lev_min = -65; + //p_max = 3; + cell_barred = "NotBarred" + si_window_length = 20; + sched_info = + ( + { + si_periodicity = 16; + si_mapping_info = []; // comma-separated array of SIB-indexes (from 3 to 13). + // Leave empty or commented to just scheduler sib2 + } + ); + system_info_value_tag = 0; +}; + +sib2 = +{ + rr_config_common_sib = + { + rach_cnfg = + { + num_ra_preambles = 52; + preamble_init_rx_target_pwr = -104; + pwr_ramping_step = 6; // in dB + preamble_trans_max = 10; + ra_resp_win_size = 10; // in ms + mac_con_res_timer = 64; // in ms + max_harq_msg3_tx = 4; + }; + bcch_cnfg = + { + modification_period_coeff = 16; // in ms + }; + pcch_cnfg = + { + default_paging_cycle = 32; // in rf + nB = "1"; + }; + prach_cnfg = + { + root_sequence_index = 128; + prach_cnfg_info = + { + high_speed_flag = false; + prach_config_index = 3; + prach_freq_offset = 2; + zero_correlation_zone_config = 5; + }; + }; + pdsch_cnfg = + { + /* Warning: Currently disabled and forced to p_b=1 for TM2/3/4 and p_b=0 for TM1 + */ + p_b = 1; + rs_power = 0; + }; + pusch_cnfg = + { + n_sb = 1; + hopping_mode = "inter-subframe"; + pusch_hopping_offset = 2; + enable_64_qam = false; // 64QAM PUSCH is not currently enabled + ul_rs = + { + cyclic_shift = 0; + group_assignment_pusch = 0; + group_hopping_enabled = false; + sequence_hopping_enabled = false; + }; + }; + pucch_cnfg = + { + delta_pucch_shift = 2; + n_rb_cqi = 2; + n_cs_an = 0; + n1_pucch_an = 12; + }; + ul_pwr_ctrl = + { + p0_nominal_pusch = -85; + alpha = 0.7; + p0_nominal_pucch = -107; + delta_flist_pucch = + { + format_1 = 0; + format_1b = 3; + format_2 = 1; + format_2a = 2; + format_2b = 2; + }; + delta_preamble_msg3 = 6; + }; + ul_cp_length = "len1"; + }; + + ue_timers_and_constants = + { + t300 = 2000; // in ms + t301 = 100; // in ms + t310 = 1000; // in ms + n310 = 1; + t311 = 1000; // in ms + n311 = 1; + }; + + freqInfo = + { + ul_carrier_freq_present = true; + ul_bw_present = true; + additional_spectrum_emission = 1; + }; + + time_alignment_timer = "INFINITY"; // use "sf500", "sf750", etc. +}; + diff --git a/src/osmo_gsm_tester/templates/srsepc.conf.tmpl b/src/osmo_gsm_tester/templates/srsepc.conf.tmpl new file mode 100644 index 00000000..8acd22f7 --- /dev/null +++ b/src/osmo_gsm_tester/templates/srsepc.conf.tmpl @@ -0,0 +1,106 @@ +##################################################################### +# srsEPC configuration file +##################################################################### + +##################################################################### +# MME configuration +# +# mme_code: 8-bit MME code identifies the MME within a group. +# mme_group: 16-bit MME group identifier. +# tac: 16-bit Tracking Area Code. +# mcc: Mobile Country Code +# mnc: Mobile Network Code +# apn: Set Access Point Name (APN) +# mme_bind_addr: IP bind addr to listen for eNB S1-MME connnections +# dns_addr: DNS server address for the UEs +# encryption_algo: Preferred encryption algorithm for NAS layer +# (default: EEA0, support: EEA1, EEA2) +# integrity_algo: Preferred integrity protection algorithm for NAS +# (default: EIA1, support: EIA1, EIA2 (EIA0 not support) +# paging_timer: Value of paging timer in seconds (T3413) +# +##################################################################### +[mme] +mme_code = 0x1a +mme_group = 0x0001 +tac = 0x0007 +mcc = ${epc.mcc} +mnc = ${epc.mnc} +mme_bind_addr = ${epc.run_addr} +apn = srsapn +dns_addr = 8.8.8.8 +encryption_algo = EEA0 +integrity_algo = EIA1 +paging_timer = 2 + +##################################################################### +# HSS configuration +# +# db_file: Location of .csv file that stores UEs information. +# +##################################################################### +[hss] +db_file = user_db.csv + +##################################################################### +# SP-GW configuration +# +# gtpu_bind_addr: GTP-U bind address. +# sgi_if_addr: SGi TUN interface IP address. +# sgi_if_name: SGi TUN interface name. +# max_paging_queue: Maximum packets in paging queue (per UE). +# +##################################################################### + +[spgw] +gtpu_bind_addr = ${epc.run_addr} +sgi_if_addr = 172.16.0.1 +sgi_if_name = srs_spgw_sgi +max_paging_queue = 100 + +#################################################################### +# PCAP configuration +# +# Packets are captured to file in the compact format decoded by +# the Wireshark s1ap dissector and with DLT 150. +# To use the dissector, edit the preferences for DLT_USER to +# add an entry with DLT=150, Payload Protocol=s1ap. +# +# enable: Enable or disable the PCAP. +# filename: File name where to save the PCAP. +# +#################################################################### +[pcap] +enable = false +filename = /tmp/epc.pcap + +#################################################################### +# 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: nas, s1ap, mme_gtpc, spgw_gtpc, gtpu, spgw, hss, 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 = info +all_hex_limit = 32 +filename = /tmp/epc.log + +#nas_level = debug +#s1ap_level = debug +#mme_gtpc_level = debug +#spgw_gtpc_level = debug +#gtpu_level = debug +#spgw_level = debug +#hss_level = debug diff --git a/src/osmo_gsm_tester/templates/srsepc_user_db.csv.tmpl b/src/osmo_gsm_tester/templates/srsepc_user_db.csv.tmpl new file mode 100644 index 00000000..32df58d0 --- /dev/null +++ b/src/osmo_gsm_tester/templates/srsepc_user_db.csv.tmpl @@ -0,0 +1,24 @@ +# +# .csv to store UE's information in HSS +# Kept in the following format: "Name,Auth,IMSI,Key,OP_Type,OP,AMF,SQN,QCI,IP_alloc" +# +# Name: Human readable name to help distinguish UE's. Ignored by the HSS +# IMSI: UE's IMSI value +# Auth: Authentication algorithm used by the UE. Valid algorithms are XOR +# (xor) and MILENAGE (mil) +# Key: UE's key, where other keys are derived from. Stored in hexadecimal +# OP_Type: Operator's code type, either OP or OPc +# OP/OPc: Operator Code/Cyphered Operator Code, stored in hexadecimal +# AMF: Authentication management field, stored in hexadecimal +# SQN: UE's Sequence number for freshness of the authentication +# QCI: QoS Class Identifier for the UE's default bearer. +# IP_alloc: IP allocation stratagy for the SPGW. +# With 'dynamic' the SPGW will automatically allocate IPs +# With a valid IPv4 (e.g. '172.16.0.2') the UE will have a statically assigned IP. +# +# Note: Lines starting by '#' are ignored and will be overwritten +#ue2,mil,001010123456780,00112233445566778899aabbccddeeff,opc,63bfa50ee6523365ff14c1f45f88737d,8000,000000001234,7,dynamic +#ue1,xor,001010123456789,00112233445566778899aabbccddeeff,opc,63bfa50ee6523365ff14c1f45f88737d,9001,000000001255,7,dynamic +%for sub in epc.hss.subscribers: +ogt${sub.id},${sub.auth_algo},${sub.imsi},${sub.ki},opc,63bfa50ee6523365ff14c1f45f88737d,8000,000000001234,7,dynamic +%endfor diff --git a/src/osmo_gsm_tester/templates/srsue.conf.tmpl b/src/osmo_gsm_tester/templates/srsue.conf.tmpl new file mode 100644 index 00000000..5e5676da --- /dev/null +++ b/src/osmo_gsm_tester/templates/srsue.conf.tmpl @@ -0,0 +1,347 @@ +##################################################################### +# srsUE configuration file +##################################################################### + +##################################################################### +# RF configuration +# +# dl_earfcn: Downlink EARFCN code. +# freq_offset: Uplink and Downlink optional frequency offset (in Hz) +# tx_gain: Transmit gain (dB). +# rx_gain: Optional receive gain (dB). If disabled, AGC if enabled +# +# Optional parameters: +# dl_freq: Override DL frequency corresponding to dl_earfcn +# ul_freq: Override UL frequency corresponding to dl_earfcn +# nof_radios: Number of available RF devices +# nof_rf_channels: Number of RF channels per radio +# nof_rx_ant: Number of RX antennas per channel +# device_name: Device driver family. Supported options: "auto" (uses first found), "UHD" or "bladeRF" +# device_args: Arguments for the device driver. Options are "auto" or any string. +# Default for UHD: "recv_frame_size=9232,send_frame_size=9232" +# Default for bladeRF: "" +# device_args_2: Arguments for the RF device driver 2. +# device_args_3: Arguments for the RF device driver 3. +# time_adv_nsamples: Transmission time advance (in number of samples) to compensate for RF delay +# from antenna to timestamp insertion. +# Default "auto". B210 USRP: 100 samples, bladeRF: 27. +# burst_preamble_us: Preamble length to transmit before start of burst. +# Default "auto". B210 USRP: 400 us, bladeRF: 0 us. +# continuous_tx: Transmit samples continuously to the radio or on bursts (auto/yes/no). +# Default is auto (yes for UHD, no for rest) +##################################################################### +[rf] +dl_earfcn = 3400 +freq_offset = 0 +tx_gain = 80 +#rx_gain = 40 + +#nof_radios = 1 +#nof_rx_ant = 1 + +# For best performance in 2x2 MIMO and >= 15 MHz use the following device_args settings: +# USRP B210: num_recv_frames=64,num_send_frames=64 + +# For best performance when BW<5 MHz (25 PRB), use the following device_args settings: +# USRP B210: send_frame_size=512,recv_frame_size=512 + +#device_args = auto +#time_adv_nsamples = auto +#burst_preamble_us = auto +#continuous_tx = auto + + +##################################################################### +# Packet capture configuration +# +# Packet capture is supported at both MAC and NAS layers. +# MAC-layer packets are captured to file in the compact format +# decoded by the Wireshark mac-lte-framed dissector. +# To use this dissector, edit the preferences for DLT_USER to +# add an entry with DLT=147, Payload Protocol=mac-lte-framed. +# For more information see: https://wiki.wireshark.org/MAC-LTE +# NAS-layer packets are dissected with DLT=148, and +# Payload Protocol = nas-eps. +# +# enable: Enable MAC layer packet captures (true/false) +# filename: File path to use for MAC packet captures +# nas_enable: Enable NAS layer packet captures (true/false) +# nas_filename: File path to use for NAS packet captures +##################################################################### +[pcap] +enable = false +filename = /tmp/ue.pcap +nas_enable = false +nas_filename = /tmp/nas.pcap + +##################################################################### +# Log configuration +# +# Log levels can be set for individual layers. "all_level" sets log +# level for all layers unless otherwise configured. +# Format: e.g. phy_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. phy_hex_limit = 32 +# +# Logging layers: rf, phy, mac, rlc, pdcp, rrc, nas, gw, usim, 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 +# file_max_size: Maximum file size (in kilobytes). When passed, multiple files are created. +# If set to negative, a single log file will be created. +##################################################################### +[log] +all_level = warning +phy_lib_level = none +all_hex_limit = 32 +filename = /tmp/ue.log +file_max_size = -1 + +##################################################################### +# USIM configuration +# +# mode: USIM mode (soft/pcsc) +# algo: Authentication algorithm (xor/milenage) +# op/opc: 128-bit Operator Variant Algorithm Configuration Field (hex) +# - Specify either op or opc (only used in milenage) +# k: 128-bit subscriber key (hex) +# imsi: 15 digit International Mobile Subscriber Identity +# imei: 15 digit International Mobile Station Equipment Identity +# pin: PIN in case real SIM card is used +# reader: Specify card reader by it's name as listed by 'pcsc_scan'. If empty, try all available readers. +##################################################################### +[usim] +mode = soft +algo = ${ue.auth_algo} +#opc = 63BFA50EE6523365FF14C1F45F88737D +k = ${ue.ki} +imsi = ${ue.imsi} +imei = 353490069873319 +#reader = +#pin = 1234 + +##################################################################### +# RRC configuration +# +# ue_category: Sets UE category (range 1-5). Default: 4 +# release: UE Release (8 to 10) +# feature_group: Hex value of the featureGroupIndicators field in the +# UECapabilityInformation message. Default 0xe6041000 +# mbms_service_id: MBMS service id for autostarting MBMS reception +# (default -1 means disabled) +# mbms_service_port: Port of the MBMS service +##################################################################### +[rrc] +#ue_category = 4 +#release = 8 +#feature_group = 0xe6041000 +#mbms_service_id = -1 +#mbms_service_port = 4321 + +##################################################################### +# NAS configuration +# +# apn: Set Access Point Name (APN) +# apn_protocol: Set APN protocol (IPv4, IPv6 or IPv4v6.) +# user: Username for CHAP authentication +# pass: Password for CHAP authentication +# force_imsi_attach: Whether to always perform an IMSI attach +# eia: List of integrity algorithms included in UE capabilities +# Supported: 1 - Snow3G, 2 - AES +# eea: List of ciphering algorithms included in UE capabilities +# Supported: 0 - NULL, 1 - Snow3G, 2 - AES +##################################################################### +[nas] +#apn = internetinternet +#apn_protocol = ipv4 +#user = srsuser +#pass = srspass +#force_imsi_attach = false +#eia = 1,2 +#eea = 0,1,2 + +##################################################################### +# GW configuration +# +# netns: Network namespace to create TUN device. Default: empty +# ip_devname: Name of the tun_srsue device. Default: tun_srsue +# ip_netmask: Netmask of the tun_srsue device. Default: 255.255.255.0 +##################################################################### +[gw] +#netns = +#ip_devname = tun_srsue +#ip_netmask = 255.255.255.0 + +##################################################################### +# GUI configuration +# +# Simple GUI displaying PDSCH constellation and channel freq response. +# (Requires building with srsGUI) +# enable: Enable the graphical interface (true/false) +##################################################################### +[gui] +enable = false + +##################################################################### +# Channel emulator options: +# enable: Enable/Disable internal Downlink/Uplink channel emulator +# +# -- Fading emulator +# fading.enable: Enable/disable fading simulator +# fading.model: Fading model + maximum doppler (E.g. none, epa5, eva70, etu300, etc) +# +# -- Delay Emulator delay(t) = delay_min + (delay_max - delay_min) * (1 + sin(2pi*t/period)) / 2 +# Maximum speed [m/s]: (delay_max - delay_min) * pi * 300 / period +# delay.enable: Enable/disable delay simulator +# delay.period_s: Delay period in seconds. +# delay.init_time_s: Delay initial time in seconds. +# delay.maximum_us: Maximum delay in microseconds +# delay.minumum_us: Minimum delay in microseconds +# +# -- Radio-Link Failure (RLF) Emulator +# rlf.enable: Enable/disable RLF simulator +# rlf.t_on_ms: Time for On state of the channel (ms) +# rlf.t_off_ms: Time for Off state of the channel (ms) +# +# -- High Speed Train Doppler model simulator +# hst.enable: Enable/Disable HST simulator +# hst.period_s: HST simulation period in seconds +# hst.fd_hz: Doppler frequency in Hz +# hst.init_time_s: Initial time in seconds +##################################################################### +[channel.dl] +#enable = false + +[channel.dl.fading] +#enable = false +#model = none + +[channel.dl.delay] +#enable = false +#period_s = 3600 +#init_time_s = 0 +#maximum_us = 100 +#minimum_us = 10 + +[channel.dl.rlf] +#enable = false +#t_on_ms = 10000 +#t_off_ms = 2000 + +[channel.dl.hst] +#enable = false +#period_s = 7.2 +#fd_hz = 750.0 +#init_time_s = 0.0 + +[channel.ul] +#enable = false + +[channel.ul.fading] +#enable = false +#model = none + +[channel.ul.delay] +#enable = false +#period_s = 3600 +#init_time_s = 0 +#maximum_us = 100 +#minimum_us = 10 + +[channel.ul.rlf] +#enable = false +#t_on_ms = 10000 +#t_off_ms = 2000 + +[channel.ul.hst] +#enable = false +#period_s = 7.2 +#fd_hz = -750.0 +#init_time_s = 0.0 + +##################################################################### +# PHY configuration options +# +# rx_gain_offset: RX Gain offset to add to rx_gain to calibrate RSRP readings +# prach_gain: PRACH gain (dB). If defined, forces a gain for the tranmsission of PRACH only., +# Default is to use tx_gain in [rf] section. +# cqi_max: Upper bound on the maximum CQI to be reported. Default 15. +# cqi_fixed: Fixes the reported CQI to a constant value. Default disabled. +# snr_ema_coeff: Sets the SNR exponential moving average coefficient (Default 0.1) +# snr_estim_alg: Sets the noise estimation algorithm. (Default refs) +# Options: pss: use difference between received and known pss signal, +# refs: use difference between noise references and noiseless (after filtering) +# empty: use empty subcarriers in the boarder of pss/sss signal +# pdsch_max_its: Maximum number of turbo decoder iterations (Default 4) +# nof_phy_threads: Selects the number of PHY threads (maximum 4, minimum 1, default 2) +# equalizer_mode: Selects equalizer mode. Valid modes are: "mmse", "zf" or any +# non-negative real number to indicate a regularized zf coefficient. +# Default is MMSE. +# sfo_ema: EMA coefficient to average sample offsets used to compute SFO +# sfo_correct_period: Period in ms to correct sample time to adjust for SFO +# sss_algorithm: Selects the SSS estimation algorithm. Can choose between +# {full, partial, diff}. +# estimator_fil_auto: The channel estimator smooths the channel estimate with an adaptative filter. +# estimator_fil_stddev: Sets the channel estimator smooth gaussian filter standard deviation. +# estimator_fil_order: Sets the channel estimator smooth gaussian filter order (even values perform better). +# The taps are [w, 1-2w, w] +# +# snr_to_cqi_offset: Sets an offset in the SNR to CQI table. This is used to adjust the reported CQI. +# +# pregenerate_signals: Pregenerate uplink signals after attach. Improves CPU performance. +# +# interpolate_subframe_enabled: Interpolates in the time domain the channel estimates within 1 subframe. Default is to average. +# +# sic_pss_enabled: Applies Successive Interference Cancellation to PSS signals when searching for neighbour cells. +# Must be disabled if cells have identical channel and timing, for instance if generated from +# the same source. +# +# pdsch_csi_enabled: Stores the Channel State Information and uses it for weightening the softbits. It is only +# used in TM1. It is True by default. +# +# pdsch_8bit_decoder: Use 8-bit for LLR representation and turbo decoder trellis computation (Experimental) +# force_ul_amplitude: Forces the peak amplitude in the PUCCH, PUSCH and SRS (set 0.0 to 1.0, set to 0 or negative for disabling) +# +##################################################################### +[phy] +#rx_gain_offset = 62 +#prach_gain = 30 +#cqi_max = 15 +#cqi_fixed = 10 +#snr_ema_coeff = 0.1 +#snr_estim_alg = refs +#pdsch_max_its = 8 # These are half iterations +#nof_phy_threads = 3 +#equalizer_mode = mmse +#sfo_ema = 0.1 +#sfo_correct_period = 10 +#sss_algorithm = full +#estimator_fil_auto = false +#estimator_fil_stddev = 1.0 +#estimator_fil_order = 4 +#snr_to_cqi_offset = 0.0 +#interpolate_subframe_enabled = false +#sic_pss_enabled = true +#pregenerate_signals = false +#pdsch_csi_enabled = true +#pdsch_8bit_decoder = false +#force_ul_amplitude = 0 + +##################################################################### +# General configuration options +# +# metrics_csv_enable: Write UE metrics to CSV file. +# +# metrics_period_secs: Sets the period at which metrics are requested from the UE. +# +# metrics_csv_filename: File path to use for CSV metrics. +# +##################################################################### +[general] +#metrics_csv_enable = false +#metrics_period_secs = 1 +#metrics_csv_filename = /tmp/ue_metrics.csv diff --git a/suites/4g/iperf3.py b/suites/4g/iperf3.py new file mode 100755 index 00000000..5ccc922b --- /dev/null +++ b/suites/4g/iperf3.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +from osmo_gsm_tester.testenv import * + +def print_results(cli_res, srv_res): + cli_sent = cli_res['end']['sum_sent'] + cli_recv = cli_res['end']['sum_received'] + print("RESULT client:") + print("\tSEND: %d KB, %d kbps, %d seconds (%d retrans)" % (cli_sent['bytes']/1000, cli_sent['bits_per_second']/1000, cli_sent['seconds'], cli_sent['retransmits'])) + print("\tRECV: %d KB, %d kbps, %d seconds" % (cli_recv['bytes']/1000, cli_recv['bits_per_second']/1000, cli_recv['seconds'])) + print("RESULT server:") + print("\tSEND: %d KB, %d kbps, %d seconds" % (cli_sent['bytes']/1000, cli_sent['bits_per_second']/1000, cli_sent['seconds'])) + print("\tRECV: %d KB, %d kbps, %d seconds" % (cli_recv['bytes']/1000, cli_recv['bits_per_second']/1000, cli_recv['seconds'])) + + +epc = suite.epc() +enb = suite.enb() +ue = suite.modem() +iperf3srv = suite.iperf3srv({'addr': epc.tun_addr()}) +iperf3srv.set_run_node(epc.run_node()) +iperf3cli = iperf3srv.create_client() +iperf3cli.set_run_node(ue.run_node()) + +epc.subscriber_add(ue) +epc.start() +enb.ue_add(ue) +enb.start(epc) + +print('waiting for ENB to connect to EPC...') +wait(epc.enb_is_connected, enb) +print('ENB is connected to EPC') + +ue.connect(enb) + +iperf3srv.start() +proc = iperf3cli.prepare_test_proc(ue.netns()) + +print('waiting for UE to attach...') +wait(ue.is_connected, None) +print('UE is attached') + +print("Running iperf3 client to %s through %s" % (str(iperf3cli), ue.netns())) +proc.launch_sync() +iperf3srv.stop() +print_results(iperf3cli.get_results(), iperf3srv.get_results()) diff --git a/suites/4g/mo_mt_sms.py b/suites/4g/mo_mt_sms.py deleted file mode 100755 index 8aa4357b..00000000 --- a/suites/4g/mo_mt_sms.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env python3 -from osmo_gsm_tester.testenv import * - -#epc = suite.epc() -#enb = suite.enb() -ue = suite.modem() - -#enb.start() -#epc.enb_add(enb) -#epc.start() - -#wait(epc.enb_is_connected, enb) - -#hss/epc.subscriber_add(ue) - -#ue.connect(epc.mcc_mnc()) -ue.connect() - - -print('waiting for modem to attach...') -#wait(ue.is_connected, msc.mcc_mnc()) -sleep(10) diff --git a/suites/4g/ping.py b/suites/4g/ping.py new file mode 100755 index 00000000..b44ff84c --- /dev/null +++ b/suites/4g/ping.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python3 +from osmo_gsm_tester.testenv import * + +epc = suite.epc() +enb = suite.enb() +ue = suite.modem() + +epc.subscriber_add(ue) +epc.start() +enb.ue_add(ue) +enb.start(epc) + +print('waiting for ENB to connect to EPC...') +wait(epc.enb_is_connected, enb) +print('ENB is connected to EPC') + +ue.connect(enb) +print('waiting for UE to attach...') +wait(ue.is_connected, None) +print('UE is attached') + +ue.run_netns_wait('ping', ('ping', '-c', '10', epc.tun_addr())) diff --git a/suites/4g/suite.conf b/suites/4g/suite.conf index c55b6105..352293ab 100644 --- a/suites/4g/suite.conf +++ b/suites/4g/suite.conf @@ -1,6 +1,9 @@ resources: - ip_address: + run_node: # for EPC - times: 1 + enb: + - times: 1 + type: srsenb modem: - times: 1 type: srsue