Commit 5b06add2 authored by Joanne Hugé's avatar Joanne Hugé

wip: ims

parent eb22e363
......@@ -16,7 +16,7 @@
[template]
filename = instance.cfg
md5sum = acd9dd8dbe613e7101e62930a8380ef0
md5sum = b22b155ff5de04225ea0c32e8108d812
[template-ors]
filename = instance-ors.cfg
......@@ -36,7 +36,7 @@ md5sum = ab666fdfadbfc7d8a16ace38d295c883
[ru_libinstance.jinja2.cfg]
_update_hash_filename_ = ru/libinstance.jinja2.cfg
md5sum = 2dda7713832be83d94522c7abb4901f9
md5sum = ff4c05c4cf72bbce2109306d60d2aae9
[ru_sdr_libinstance.jinja2.cfg]
_update_hash_filename_ = ru/sdr/libinstance.jinja2.cfg
......@@ -96,7 +96,7 @@ md5sum = 601d6237059fa665d3f3ffb6a78ad9ca
[template-core-network]
_update_hash_filename_ = instance-core-network.jinja2.cfg
md5sum = 326e194e9c98d58d926f89521bb95df5
md5sum = d9406c9538994459452a9c7e302cba1a
[template-ue]
_update_hash_filename_ = instance-ue.jinja2.cfg
......@@ -126,9 +126,13 @@ md5sum = 282b11d7b72b01b8325df4632d82b84d
filename = config/sib23.jinja2.asn
md5sum = 959523597e29b048e45ebf58f7ea4c5b
[mt_call_qos.jinja2.sdp]
_update_hash_filename_ = config/mt_call_qos.jinja2.sdp
md5sum = 9dbd93036c15c87c6de74b88b34062b6
[mme.jinja2.cfg]
filename = config/mme.jinja2.cfg
md5sum = 25ae6b1022548183293f0ef0c54532a7
md5sum = 64b2edf5954eb3b2a63e5896e00d96ad
[dnsmasq-core-network.jinja2.cfg]
filename = config/dnsmasq-core-network.jinja2.cfg
......@@ -140,7 +144,7 @@ md5sum = 95f4f8fb85e0480eb3e9059b9db26540
[ims.jinja2.cfg]
filename = config/ims.jinja2.cfg
md5sum = 36281b03597252cf75169417d02fc28c
md5sum = 32fa740571358898793df77489d31355
[ue.jinja2.cfg]
filename = config/ue.jinja2.cfg
......
{%- set tun_ipv6_start = int(netaddr.IPAddress(slap_configuration.get('tun-ipv6-addr', ''))) %}
{%- set tun_ipv6_end = netaddr.IPNetwork(slap_configuration.get('tun-ipv6-network')).last %}
{%- set tun_ipv4_start = int(netaddr.IPAddress(slap_configuration.get('tun-ipv4-addr', ''))) %}
{%- set tun_ipv4_end = netaddr.IPNetwork(slap_configuration.get('tun-ipv4-network')).last %}
{
log_options: "all.level=debug,all.max_size=32",
log_filename: "{{ directory['log'] }}/ims.log",
sip_addr: [
{addr: "{{ slap_configuration['tun-ipv4-addr'] }}", bind_addr: "0.0.0.0", port_min: 10000, port_max: 20000},
{addr: "{{ netaddr.IPAddress((tun_ipv4_start + tun_ipv4_end) // 2 + 1) }}", bind_addr: "{{ netaddr.IPAddress((tun_ipv4_start + tun_ipv4_end) // 2 + 1) }}", port_min: 10000, port_max: 20000},
{#" slap_configuration['tun-ipv6-addr'] ",#}
],
mms_server_bind_addr: "{{ netaddr.IPAddress(netaddr.IPNetwork(slap_configuration['tun-ipv4-network']).first) + 1 }}:1111",
mms_server_bind_addr: "{{ netaddr.IPAddress(tun_ipv4_start) }}:1111",
sctp_addr: "{{ slap_configuration['configuration.ims_addr'] }}",
cx_server_addr: "127.0.1.100",
cx_bind_addr: "{{ slap_configuration['configuration.ims_addr'] }}",
rx_server_addr: "127.0.1.100",
rx_bind_addr: "{{ slap_configuration['configuration.ims_addr'] }}",
domain: "{{ slap_configuration['configuration.domain'] }}",
domain: "{{ slapparameter_dict.get('ims_domain', 'rapid.space') }}",
include "{{ slap_configuration['ue_db_path'] }}",
......@@ -44,7 +46,7 @@
ipsec_aalg_list: ["hmac-md5-96", "hmac-sha-1-96"],
ipsec_ealg_list: ["null", "aes-cbc", "des-cbc", "des-ede3-cbc"],
mt_call_sdp_file: "{{ directory['software'] }}/mme/config/mt_call.sdp",
mt_call_sdp_file: "{{ directory['etc'] }}/mt_call_qos.sdp",
ue_db_filename: "{{ directory['var'] }}/lte_ue_ims.db",
}
{%- set tun_ipv6_start = int(netaddr.IPAddress(slap_configuration.get('tun-ipv6-addr', ''))) %}
{%- set tun_ipv6_end = netaddr.IPNetwork(slap_configuration.get('tun-ipv6-network')).last %}
{%- set tun_ipv4_start = int(netaddr.IPAddress(slap_configuration.get('tun-ipv4-addr', ''))) %}
{%- set tun_ipv4_end = netaddr.IPNetwork(slap_configuration.get('tun-ipv4-network')).last %}
{
log_options: "all.level=error,all.max_size=0,nas.level=debug,nas.max_size=1,s1ap.level=debug,s1ap.max_size=1,ngap.level=debug,ngap.max_size=1,file.rotate=1G,file.path=/dev/null",
log_filename: "{{ directory['log'] }}/mme.log",
......@@ -26,6 +30,7 @@
],
rx: {
bind_addr: "127.0.1.100",
qci: {audio: 1, video: 2},
},
......@@ -50,11 +55,10 @@
pdn_list: [
{
{% if slap_configuration.get('tun-ipv6-network', '') %}
pdn_type: "ipv4v6",
first_ipv6_prefix: "{{ netaddr.IPAddress(slap_configuration.get('tun-ipv6-addr', '')) + 1 }}",
last_ipv6_prefix: "{{ netaddr.IPAddress(netaddr.IPNetwork(slap_configuration.get('tun-ipv6-network', '')).last) - 1 }}",
first_ipv6_prefix: "{{ netaddr.IPAddress(tun_ipv6_start + 1) }}",
last_ipv6_prefix: "{{ netaddr.IPAddress((tun_ipv6_start + tun_ipv6_end) // 2 - 1) }}",
{% if slapparameter_dict.get('local_domain', '') %}
dns_addr: ["{{ slap_configuration.get('tun-ipv4-addr', '') }}"],
{% else %}
......@@ -64,15 +68,43 @@
pdn_type: "ipv4",
dns_addr: "8.8.8.8",
{% endif %}
tun_ifname: "{{ slap_configuration.get('tun-name', '') }}",
access_point_name: ["default", "internet", "ims", "sos"],
tun_ifname: "{{ slap_configuration.get('tun-name', '') }}-1",
access_point_name: ["default", "internet", "sos"],
{% if slap_configuration.get('tun-name', '') %}
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 }}",
first_ip_addr: "{{ netaddr.IPAddress(tun_ipv4_start + 1) }}",
last_ip_addr: "{{ netaddr.IPAddress((tun_ipv4_start + tun_ipv4_end) // 2 - 1) }}",
{% endif %}
p_cscf_addr: ["{{ slap_configuration.get('tun-ipv4-addr', '') }}"],
erabs: [
{
qci: 9,
priority_level: 15,
pre_emption_capability: "shall_not_trigger_pre_emption",
pre_emption_vulnerability: "not_pre_emptable",
},
],
},
{
{% if slap_configuration.get('tun-ipv6-network', '') %}
pdn_type: "ipv4v6",
first_ipv6_prefix: "{{ netaddr.IPAddress((tun_ipv6_start + tun_ipv6_end) // 2) }}"
last_ipv6_prefix: "{{ netaddr.IPAddress(tun_ipv6_end - 1) }}",
{% if slapparameter_dict.get('local_domain', '') %}
dns_addr: ["{{ slap_configuration.get('tun-ipv4-addr', '') }}"],
{% else %}
dns_addr: ["8.8.8.8", "2001:4860:4860::8888"],
{% endif %}
{% else %}
pdn_type: "ipv4",
dns_addr: "8.8.8.8",
{% endif %}
tun_ifname: "{{ slap_configuration.get('tun-name', '') }}-2",
access_point_name: "ims",
{% if slap_configuration.get('tun-name', '') %}
first_ip_addr: "{{ netaddr.IPAddress((tun_ipv4_start + tun_ipv4_end) // 2 + 2) }}"
last_ip_addr: "{{ netaddr.IPAddress(tun_ipv4_end - 1) }}",
{% endif %}
p_cscf_addr: ["{{ netaddr.IPAddress((tun_ipv4_start + tun_ipv4_end) // 2) + 1 }}"],
erabs: [
{
......
v=0
o=Amarisoft-IMS 0 0 IN IP4 0.0.0.0
s=Amarisoft-IMS
c=IN IP4 0.0.0.0
t=0 0
m=audio 10000 RTP/AVP 116
c=IN IP4 0.0.0.0
b=AS:41
b=RR:0
b=RS:0
a=rtpmap:116 AMR-WB/16000/1
a=fmtp:116 mode-change-capability=2; max-red=0
a=curr:qos local none
a=curr:qos remote none
a=des:qos mandatory local sendrecv
a=des:qos mandatory remote sendrecv
a=ptime:20
a=maxptime:220
a=sendrecv
......@@ -70,6 +70,9 @@ parts =
directory
mme-config
mme-service
ims-config
mt-call-config
ims-service
monitor-base
check-interface-up.py
publish-connection-information
......@@ -135,25 +138,34 @@ service = ${:etc}/service
promise = ${:etc}/promise
log = ${:var}/log
{% if slapparameter_dict.get("mme_config_link", None) %}
[mme-config-dl]
recipe = slapos.recipe.build:download
url = {{ slapparameter_dict.get("mme_config_link") }}
version = {{ slapparameter_dict.get("mme_config_version") }}
offline = false
[ims-sh-wrapper]
recipe = slapos.recipe.template
output = ${directory:bin}/${:_buildout_section_name_}
ims-log = ${directory:log}/ims-output.log
inline =
#!/bin/sh
{% if not slapparameter_dict.get("testing", False) %}
(echo && echo && date "+[%Y/%m/%d %T.%N %Z] Starting IMS software..." && echo) >> ${:ims-log};
tail -c 1M ${:ims-log} > ${:ims-log}.tmp;
mv ${:ims-log}.tmp ${:ims-log};
{{ ims }}/lteims ${directory:etc}/ims.cfg >> ${:ims-log} 2>> ${:ims-log};
{% endif %}
### IMS
[ims-service]
recipe = slapos.cookbook:wrapper
command-line = {{ mme }}/lteims ${directory:etc}/ims.cfg
command-line = ${ims-sh-wrapper:output}
wrapper-path = ${directory:service}/ims
mode = 0775
pidfile = ${directory:run}/ims.pid
hash-files =
${ims-config:output}
${mt-call-config:output}
${ue-db-config:output}
environment = AMARISOFT_PATH=/opt/amarisoft/.amarisoft
${ims-sh-wrapper:output}
environment =
LD_LIBRARY_PATH={{ openssl_location }}/lib:{{ nghttp2_location }}/lib
AMARISOFT_PATH=/opt/amarisoft/.amarisoft
[mme-sh-wrapper]
recipe = slapos.recipe.template
......@@ -162,6 +174,7 @@ mme-log = ${directory:log}/mme-output.log
inline =
#!/bin/sh
{% if not slapparameter_dict.get("testing", False) %}
sudo -n /opt/amarisoft/init-mme;
rm -f ${directory:var}/lte_ue.db;
(echo && echo && date "+[%Y/%m/%d %T.%N %Z] Starting MME software..." && echo) >> ${:mme-log};
tail -c 1M ${:mme-log} > ${:mme-log}.tmp;
......@@ -229,6 +242,11 @@ context =
url = {{ ims_template }}
output = ${directory:etc}/ims.cfg
[mt-call-config]
<= config-base
url = {{ mt_call_template }}
output = ${directory:etc}/mt_call_qos.sdp
[ue-db-config]
<= config-base
url = {{ ue_db_template }}
......
......@@ -91,7 +91,7 @@ init =
options['sdr'] = path + "/trx_sdr"
options['enb'] = path + "/enb"
options['mme'] = path + "/mme"
options['ims'] = path + "/ims"
options['ims'] = path + "/mme"
options['ue'] = path + "/ue"
import os
lte_expiration = "Unknown"
......@@ -187,10 +187,12 @@ extra-context =
key lte_version amarisoft:lte-version
key lte_expiration amarisoft:lte-expiration
key mme amarisoft:mme
key ims amarisoft:ims
raw mme_template ${mme.jinja2.cfg:target}
raw dnsmasq_template ${dnsmasq-core-network.jinja2.cfg:target}
raw ims_template ${ims.jinja2.cfg:target}
raw ue_db_template ${ue_db.jinja2.cfg:target}
raw mt_call_template ${mt_call_qos.jinja2.sdp:target}
raw openssl_location ${openssl:location}
raw nghttp2_location ${nghttp2:location}
raw iperf3_location ${iperf3:location}
......
......@@ -147,6 +147,49 @@ init =
options['{{tap}}'] = json.dumps(tap)
{%- endfor %}
{#- split slapos tun interface #}
{%- set vtun_list = [] %}
[vtun]
recipe = plone.recipe.command
command = {{ netcapdo }} {{ pythonwitheggs }} {{ ru_tunsplit }} {{ slaplte.tun }} 2
update-command = ${:command}
stop-on-error = true
{%- if testing %}
# StandaloneSlapOS does not provide slaptun
command = :
{%- endif %}
{%- for i in range(1,3) %}
{%- set tun = '%s-%d' % (slaplte.tun, i) %}
{%- do vtun_list.append(tun) %}
[vtun.{{ tun }}]
recipe = slapos.recipe.build
depends = ${vtun:recipe}
init =
import types
def readfile(path):
with open(path) as f:
return f.read()
import netaddr
# ~ import tunsplit
tunsplit = types.ModuleType('tunsplit')
exec(readfile('{{ ru_tunsplit }}'), tunsplit.__dict__)
# simulate what tunsplit would assign to the tun
# ( tun subinterface will be created for real later at install time - when it
# is too late to update section options )
if {{ testing }}:
slapnet = netaddr.IPNetwork('{{ str(test_slapnet) }}')
else:
slapnet = tunsplit.ifnet6('{{ slaplte.tun }}')
tunnet = tunsplit.netsplit(slapnet, {{ 1+ntun }}) [{{ i }}]
options['network'] = str(tunnet)
options['gateway'] = str(tunnet[1])
options['addr'] = str(tunnet[-1])
{%- endfor %}
{#- provide CPRI-based RUs IP address via DHCP #}
{%- if ntap > 0 %}
......
#!/usr/bin/env python
# Copyright (C) 2023-2024 Nexedi SA and Contributors.
#
# This program is free software: you can Use, Study, Modify and Redistribute
# it under the terms of the GNU General Public License version 3, or (at your
# option) any later version, as published by the Free Software Foundation.
#
# You can also Link and Combine this program with other software covered by
# the terms of any of the Free Software licenses or any of the Open Source
# Initiative approved licenses and Convey the resulting work. Corresponding
# source of such a combination shall include the source code for all other
# software used.
#
# This program is distributed WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# See COPYING file for full licensing terms.
# See https://www.nexedi.com/licensing for rationale and options.
"""Program tunsplit brings tun interface into state with several children
interfaces each covering part of original interface address space.
Usage: tunsplit <interface> <nchildren>
"""
# TODO Relying on tunsplit should be removed once SlapOS is improved to provide
# several TAP interfaces to instances. See discussion at
# https://lab.nexedi.com/nexedi/slapos/merge_requests/1471#note_194356
# for details.
import netifaces
import netaddr
from socket import AF_INET6
from math import log2, ceil
import sys
import subprocess
import json
# LinkDB represents snapshot of state of all network interfaces.
class LinkDB:
def __init__(db):
db.linkv = ip('link', 'show')
# ifget returns information about interface with specified name.
def ifget(db, ifname):
for link in db.linkv:
if link['ifname'] == ifname:
return link
raise KeyError('interface %r not found' % ifname)
def main():
tun = sys.argv[1]
n = int(sys.argv[2])
assert n >= 0, n
# determine tun's network address and owner
ldb = LinkDB()
_ = ldb.ifget(tun)
owner = _['linkinfo']['info_data']['user']
net = ifnet6(tun)
print('%s: split %s by %d' % (tun, net, n))
# do the split
# with leaving first range for the original tun
subtun_set = set()
for i, subnet in enumerate(netsplit(net, 1+n)):
if i == 0:
print('preserve %s' % subnet)
continue # leave this range for original tun
subtun = '%s-%d' % (tun, i)
subtun_set.add(subtun)
print('-> %s %s' % (subtun, subnet))
def note(msg):
print(' # %s: %s' % (subtun, msg))
# create subtun
try:
link = ldb.ifget(subtun)
except KeyError:
run('ip', 'tuntap', 'add', 'dev', subtun, 'mode', 'tun', 'user', owner)
link = ip('link', 'show', 'dev', subtun)[0]
else:
note('already exists')
# set it up
if 'UP' not in link['flags']:
run('ip', 'link', 'set', subtun, 'up')
else:
note('already up')
# add subnet address
addrv = []
for _ in ip('-6', 'addr', 'show', 'dev', subtun):
addrv.extend(_['addr_info'])
for addr in addrv:
_ = netaddr.IPNetwork('%s/%s' % (addr['local'], addr['prefixlen']))
if _ == subnet and addr['noprefixroute']:
note('already has %s addr' % str(subnet))
break
else:
run('ip', 'addr', 'add', str(subnet), 'dev', subtun, 'noprefixroute')
# add /128 route to subnet::1
rtv = ip('-6', 'route', 'show', 'dev', subtun)
for rt in rtv:
if rt['dst'] == str(subnet[1]) and 'gateway' not in rt:
note('already has %s route' % str(subnet[1]))
break
else:
run('ip', 'route', 'add', str(subnet[1]), 'dev', subtun)
# add route to subnet via subnet::1
for rt in rtv:
if rt['dst'] == str(subnet) and rt.get('gateway') == str(subnet[1]):
note('already has %s route' % str(subnet))
break
else:
run('ip', 'route', 'add', str(subnet), 'dev', subtun, 'via', str(subnet[1]))
# remove other existing children
for ifname in netifaces.interfaces():
if ifname.startswith('%s-' % tun) and (ifname not in subtun_set):
print('-> del %s' % ifname)
run('ip', 'link', 'del', ifname)
# netsplit splits network into n subnetworks.
def netsplit(net, n): # -> []subnet
# see how much prefix bits we need to take to be able to divide by n
ptake = ceil(log2(n))
return list( net.subnet(net.prefixlen + ptake) )[:n]
# ifnet6 returns IPv6 network address associated with given interface.
def ifnet6(ifname):
addr = None
net = None
prefixlen = None
for iaddr in netifaces.ifaddresses(ifname)[AF_INET6]:
a = iaddr['addr']
if '%' in a: # link-local
a = a.split('%')[0]
a = netaddr.IPAddress(a)
assert a.is_link_local(), a
continue
if addr is not None:
raise RuntimeError('%s: multiple addresses: %s and %s' % (ifname, addr, a))
addr = netaddr.IPAddress(a)
netmask, plen = iaddr['netmask'].split('/')
prefixlen = int(plen)
net = netaddr.IPNetwork('%s/%d' % (a, prefixlen))
if addr is None:
raise RuntimeError('%s: no non link-local addresses' % ifname)
# normalize network
# ex 2401:5180:0:66:a7ff:ffff:ffff:ffff/71 -> 2401:5180:0:66:a600::/71
net = net.cidr
return net
# run executes `*argv` as action.
def run(*argv):
print(' # %s' % ' '.join(argv))
subprocess.check_call(argv)
# ip returns decoded output of `ip -details *argv`
def ip(*argv):
_ = subprocess.check_output(['ip', '-json', '-details'] + list(argv))
return json.loads(_)
if __name__ == '__main__':
main()
......@@ -35,6 +35,7 @@ parts +=
drb_lte.jinja2.cfg
drb_nr.jinja2.cfg
sib23.jinja2.asn
mt_call_qos.jinja2.sdp
monitor-httpd-extra-conf
# copy all gadget file
gadget
......@@ -114,6 +115,8 @@ filename = enb.jinja2.cfg
[sib23.jinja2.asn]
<= copy-config-to-instance
filename = sib23.jinja2.asn
[mt_call_qos.jinja2.sdp]
<= copy-config-to-instance
[ue_db.jinja2.cfg]
<= copy-config-to-instance
filename = ue_db.jinja2.cfg
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment