From 32357948b55bf9b7099f94afd797288c3e60fe61 Mon Sep 17 00:00:00 2001 From: Thomas Gambier <thomas.gambier@nexedi.com> Date: Sat, 24 Feb 2024 10:35:23 +0100 Subject: [PATCH] software/ors-amarisoft: Add fixed-ips option for core network This option will give fixed IP to each SIM card. --- software/ors-amarisoft/buildout.hash.cfg | 8 +- software/ors-amarisoft/config/mme.jinja2.cfg | 2 +- .../ors-amarisoft/config/ue_db.jinja2.cfg | 7 ++ .../instance-core-network-input-schema.json | 6 + .../instance-core-network.jinja2.cfg | 35 ++++++ software/ors-amarisoft/instance.cfg | 1 + software/ors-amarisoft/test/setup.py | 1 + software/ors-amarisoft/test/test_ors.py | 116 +++++++++++++----- 8 files changed, 141 insertions(+), 35 deletions(-) diff --git a/software/ors-amarisoft/buildout.hash.cfg b/software/ors-amarisoft/buildout.hash.cfg index 0dc600b97..e12bb6143 100644 --- a/software/ors-amarisoft/buildout.hash.cfg +++ b/software/ors-amarisoft/buildout.hash.cfg @@ -16,7 +16,7 @@ [template] filename = instance.cfg -md5sum = 5cf7674e2f6c3afb1b2f6b7646457284 +md5sum = 2e131f4542e059b48349ec0411241c31 [template-ors] filename = instance-ors.cfg @@ -96,7 +96,7 @@ md5sum = 601d6237059fa665d3f3ffb6a78ad9ca [template-core-network] _update_hash_filename_ = instance-core-network.jinja2.cfg -md5sum = 4d05284cd328f5fce054b8f227097e25 +md5sum = 326e194e9c98d58d926f89521bb95df5 [template-ue] _update_hash_filename_ = instance-ue.jinja2.cfg @@ -108,7 +108,7 @@ md5sum = c5f581ba01654b2aec46000abf8d0e35 [ue_db.jinja2.cfg] filename = config/ue_db.jinja2.cfg -md5sum = 87a273c99ce64192506811d7718c2b36 +md5sum = 3b901e8733e6afff8940c6c318da4493 [enb.jinja2.cfg] filename = config/enb.jinja2.cfg @@ -128,7 +128,7 @@ md5sum = 959523597e29b048e45ebf58f7ea4c5b [mme.jinja2.cfg] filename = config/mme.jinja2.cfg -md5sum = bee16b3b94fd57f5a19ea7b1f5955533 +md5sum = 25ae6b1022548183293f0ef0c54532a7 [dnsmasq-core-network.jinja2.cfg] filename = config/dnsmasq-core-network.jinja2.cfg diff --git a/software/ors-amarisoft/config/mme.jinja2.cfg b/software/ors-amarisoft/config/mme.jinja2.cfg index f30bab96d..7db7c8a53 100644 --- a/software/ors-amarisoft/config/mme.jinja2.cfg +++ b/software/ors-amarisoft/config/mme.jinja2.cfg @@ -71,7 +71,7 @@ first_ip_addr: "{{ netaddr.IPAddress(netaddr.IPNetwork(slap_configuration.get('tun-ipv4-network', '')).first) + 2 }}", last_ip_addr: "{{ netaddr.IPAddress(netaddr.IPNetwork(slap_configuration.get('tun-ipv4-network', '')).last) - 1 }}", {% endif %} - ip_addr_shift: 2, + p_cscf_addr: ["{{ slap_configuration.get('tun-ipv4-addr', '') }}"], erabs: [ diff --git a/software/ors-amarisoft/config/ue_db.jinja2.cfg b/software/ors-amarisoft/config/ue_db.jinja2.cfg index be53c5e09..38b41d420 100644 --- a/software/ors-amarisoft/config/ue_db.jinja2.cfg +++ b/software/ors-amarisoft/config/ue_db.jinja2.cfg @@ -14,6 +14,13 @@ ue_db: [ K: "{{ s.get('k', '') }}", impu: "{{ s.get('impu', '') }}", impi: "{{ s.get('impi', '') }}", +{%- if "ip" in s %} + pdn_list:[{ + access_point_name: "internet", + default: true, + ipv4_addr: "{{ s['ip'] }}" + }] +{%- endif %} } {%- endfor -%} ] diff --git a/software/ors-amarisoft/instance-core-network-input-schema.json b/software/ors-amarisoft/instance-core-network-input-schema.json index 322095157..6da947aac 100644 --- a/software/ors-amarisoft/instance-core-network-input-schema.json +++ b/software/ors-amarisoft/instance-core-network-input-schema.json @@ -44,6 +44,12 @@ "title": "Use IPv4", "description": "Set to true to use IPv4 for AMF / MME addresses", "type": "boolean" + }, + "fixed_ips": { + "default": false, + "title": "Fixed IP for the UE", + "description": "Set to true to force a static IPv4 for each UE. If true, the number of UE is limited.", + "type": "boolean" } } } diff --git a/software/ors-amarisoft/instance-core-network.jinja2.cfg b/software/ors-amarisoft/instance-core-network.jinja2.cfg index 7ab7c7c50..877e71729 100644 --- a/software/ors-amarisoft/instance-core-network.jinja2.cfg +++ b/software/ors-amarisoft/instance-core-network.jinja2.cfg @@ -1,5 +1,6 @@ {%- set dns_slave_instance_list = [] %} {%- set sim_slave_instance_list = [] %} +{%- set fixed_ip = slapparameter_dict.get("fixed_ips", False) %} {%- for slave in slave_instance_list %} {%- set slave_parameters = json_module.loads(slave['_']) %} {%- if slave_parameters.get('subdomain', '') != '' %} @@ -19,8 +20,38 @@ recipe = slapos.cookbook:publish.serialised -slave-reference = {{ slave_reference }} info = Your SIM card with IMSI {{ slave_parameters.get('imsi', '') }} has been attached to service ${slap-configuration:instance-title}. +{%- if fixed_ip %} +ipv4 = ${sim-ip-configuration:{{slave_reference}}} +{%- endif %} {%- endfor %} +[sim-ip-configuration] +recipe = slapos.recipe.build +sim-slave-instance-list = {{ dumps(sim_slave_instance_list) }} +ipv4-network = {{ slap_configuration.get('tun-ipv4-network', '') }} +init = + import netaddr + import json + network = netaddr.IPNetwork(options['ipv4-network']) + slave_list = options['sim-slave-instance-list'] + # if we don't have enough IPv4 addresses in the network, don't force it + # should we make a promise fail ? + if len(slave_list) + 2 > network.size: + for s in slave_list: + options[s['slave_reference']] = "Too many SIM for the IPv4 network" + else: + # calculate the IP addresses of each SIM + sim_list = [] + first_addr = netaddr.IPAddress(network.first) + for i, s in enumerate(sorted(slave_list, key=lambda x: json.loads(x['_'])['imsi'])): + ip = str(first_addr + 2 + i) + options[s['slave_reference']] = ip + slave_parameters = json.loads(s['_']) + slave_parameters['ip'] = ip + s['_'] = json.dumps(slave_parameters) + options['sim-with-ip-list'] = slave_list + + {%- for slave in dns_slave_instance_list %} {%- set slave_parameters = json_module.loads(slave['_']) %} {% set slave_reference = slave.get('slave_reference', '') %} @@ -70,7 +101,11 @@ configuration.gtp_addr = 127.0.1.100 configuration.ims_addr = 127.0.0.1 configuration.ims_bind = 127.0.0.2 ue_db_path = ${ue-db-config:output} +{%- if fixed_ip %} +sim_list = ${sim-ip-configuration:sim-with-ip-list} +{%- else %} sim_list = {{ dumps(sim_slave_instance_list) }} +{%- endif %} [monitor-httpd-conf-parameter] httpd-include-file = {{ buildout_directory }}/etc/httpd-include-file.conf diff --git a/software/ors-amarisoft/instance.cfg b/software/ors-amarisoft/instance.cfg index a1d12a01b..df5825a5b 100644 --- a/software/ors-amarisoft/instance.cfg +++ b/software/ors-amarisoft/instance.cfg @@ -195,6 +195,7 @@ extra-context = raw iperf3_location ${iperf3:location} raw dnsmasq_location ${dnsmasq:location} key slave_instance_list slap-configuration:slave-instance-list + section slap_configuration slap-configuration [dynamic-template-ue] < = jinja2-template-base diff --git a/software/ors-amarisoft/test/setup.py b/software/ors-amarisoft/test/setup.py index 3009795fd..ed3e5afe9 100644 --- a/software/ors-amarisoft/test/setup.py +++ b/software/ors-amarisoft/test/setup.py @@ -47,6 +47,7 @@ setup( 'slapos.cookbook', 'pcpp', 'xmltodict', + 'netaddr' ], zip_safe=True, test_suite='test', diff --git a/software/ors-amarisoft/test/test_ors.py b/software/ors-amarisoft/test/test_ors.py index 8233f3f38..80e658758 100644 --- a/software/ors-amarisoft/test/test_ors.py +++ b/software/ors-amarisoft/test/test_ors.py @@ -29,6 +29,7 @@ import os import json import glob import requests +import netaddr from test import yamlpp_load @@ -40,14 +41,6 @@ setUpModule, ORSTestCase = makeModuleSetUpAndTestCaseClass( param_dict = { 'testing': True, - 'sim_algo': 'milenage', - 'imsi': '001010000000331', - 'opc': '000102030405060708090A0B0C0D0E0F', - 'amf': '0x9001', - 'sqn': '000000000000', - 'k': '00112233445566778899AABBCCDDEEFF', - 'impu': 'impu331', - 'impi': 'impi331@amarisoft.com', 'tx_gain': 17, 'rx_gain': 17, 'dl_earfcn': 36100, @@ -232,20 +225,47 @@ def test_mme_conf(self): conf = yamlpp_load(conf_file) self.assertEqual(conf['plmn'], param_dict['core_network_plmn']) -def test_sim_card(self): +def getSimParam(id=0): + return { + 'sim_algo': 'milenage', + 'imsi': '{0:015}'.format(1010000000000 + id), + 'opc': '000102030405060708090A0B0C0D0E0F', + 'amf': '0x9001', + 'sqn': '000000000000', + 'k': '00112233445566778899AABBCCDDEEFF', + 'impu': 'impu%s' % '{0:03}'.format(id), + 'impi': 'impi%s@amarisoft.com' % '{0:03}'.format(id) + } + + +def test_sim_card(self, nb_sim_cards, fixed_ips, tun_network): conf_file = glob.glob(os.path.join( self.slap.instance_directory, '*', 'etc', 'ue_db.cfg'))[0] conf = yamlpp_load(conf_file) - for n in "sim_algo imsi opc sqn impu impi".split(): - self.assertEqual(conf['ue_db'][0][n], param_dict[n]) - self.assertEqual(conf['ue_db'][0]['K'], param_dict['k']) - self.assertEqual(conf['ue_db'][0]['amf'], int(param_dict['amf'], 16)) + first_ip = netaddr.IPAddress(tun_network.first) + for i in range(nb_sim_cards): + params = getSimParam(i) + for n in "sim_algo imsi opc sqn impu impi".split(): + self.assertEqual(conf['ue_db'][i][n], params[n], "%s doesn't match" % n) + self.assertEqual(conf['ue_db'][i]['K'], params['k']) + self.assertEqual(conf['ue_db'][i]['amf'], int(params['amf'], 16)) + + p = self.requestSlaveInstanceWithId(i).getConnectionParameterDict() + p = json.loads(p['_']) + self.assertIn('info', p) + if fixed_ips: + self.assertIn('ipv4', p) + if nb_sim_cards + 2 > tun_network.size: + self.assertEqual(p['ipv4'], "Too many SIM for the IPv4 network") + else: + ip = str(first_ip + 2 + i) + self.assertEqual(p['ipv4'], ip) + self.assertEqual(conf['ue_db'][i]['pdn_list'][0]['access_point_name'], "internet") + self.assertTrue(conf['ue_db'][i]['pdn_list'][0]['default']) + self.assertEqual(conf['ue_db'][i]['pdn_list'][0]['ipv4_addr'], ip) - p = self.requestSlaveInstance().getConnectionParameterDict() - p = p['_'] if '_' in p else p - self.assertIn('info', p) def test_monitor_gadget_url(self): parameters = json.loads(self.computer_partition.getConnectionParameterDict()['_']) @@ -304,16 +324,6 @@ class TestCoreNetworkParameters(ORSTestCase): def test_mme_conf(self): test_mme_conf(self) -def requestSlaveInstance(cls): - software_url = cls.getSoftwareURL() - return cls.slap.request( - software_release=software_url, - partition_reference="SIM-CARD", - partition_parameter_kw={'_': json.dumps(param_dict)}, - shared=True, - software_type='core-network', - ) - class TestENBMonitorGadgetUrl(ORSTestCase): @classmethod def getInstanceParameterDict(cls): @@ -351,20 +361,66 @@ class TestCoreNetworkMonitorGadgetUrl(ORSTestCase): test_monitor_gadget_url(self) class TestSimCard(ORSTestCase): + nb_sim_cards = 1 + fixed_ips = False + tun_network = netaddr.IPNetwork('192.168.10.0/24') @classmethod def requestDefaultInstance(cls, state='started'): + default_instance = super( ORSTestCase, cls).requestDefaultInstance(state=state) + cls._updateSlaposResource( + os.path.join( + cls.slap._instance_root, default_instance.getId()), + tun={"ipv4_network": str(cls.tun_network)} + ) cls.requestSlaveInstance() return default_instance @classmethod def requestSlaveInstance(cls): - return requestSlaveInstance(cls) + for i in range(cls.nb_sim_cards): + cls.requestSlaveInstanceWithId(i) @classmethod def getInstanceParameterDict(cls): - return {'_': json.dumps({'testing': True})} + return {'_': json.dumps({'testing': True, 'fixed_ips': cls.fixed_ips})} @classmethod def getInstanceSoftwareType(cls): return "core-network" - def test_sim_card(self): - test_sim_card(self) + @classmethod + def requestSlaveInstanceWithId(cls, id=0): + software_url = cls.getSoftwareURL() + param_dict = getSimParam(id) + return cls.slap.request( + software_release=software_url, + partition_reference="SIM-CARD-%s" % id, + partition_parameter_kw={'_': json.dumps(param_dict)}, + shared=True, + software_type='core-network', + ) + @classmethod + def _updateSlaposResource(cls, partition_path, **kw): + # we can update the .slapos-resourcefile from top partition because buildout + # will search for a .slapos-resource in upper directories until it finds one + with open(os.path.join(partition_path, '.slapos-resource'), 'r+') as f: + resource = json.load(f) + resource.update(kw) + f.seek(0) + f.truncate() + json.dump(resource, f, indent=2) + def test_sim_card(cls): + test_sim_card(cls, cls.nb_sim_cards, cls.fixed_ips, cls.tun_network) + +class TestSimCardManySim(TestSimCard): + nb_sim_cards = 10 + +class TestSimCardFixedIps(TestSimCard): + fixed_ips = True + +class TestSimCardManySimFixedIps(TestSimCard): + nb_sim_cards = 10 + fixed_ips = True + +class TestSimCardTooManySimFixedIps(TestSimCard): + nb_sim_cards = 10 + fixed_ips = True + tun_network = netaddr.IPNetwork("192.168.10.0/29") -- 2.30.9