Change in osmo-gsm-tester[master]: Initial Open5GS support

This is merely a historical archive of years 2008-2021, before the migration to mailman3.

A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.

pespin gerrit-no-reply at lists.osmocom.org
Wed Mar 31 09:49:25 UTC 2021


pespin has submitted this change. ( https://gerrit.osmocom.org/c/osmo-gsm-tester/+/23476 )

Change subject: Initial Open5GS support
......................................................................

Initial Open5GS support

Change-Id: Iff8b88dc22d8f156572839abb48b0c1377c55e33
---
M contrib/jenkins-build-common.sh
M doc/manuals/chapters/install.adoc
M src/osmo_gsm_tester/obj/epc.py
A src/osmo_gsm_tester/obj/epc_open5gs.py
A src/osmo_gsm_tester/obj/hss_open5gs.py
A src/osmo_gsm_tester/obj/mme_open5gs.py
A src/osmo_gsm_tester/obj/pcrf_open5gs.py
A src/osmo_gsm_tester/obj/sgwc_open5gs.py
A src/osmo_gsm_tester/obj/sgwu_open5gs.py
A src/osmo_gsm_tester/obj/smf_open5gs.py
A src/osmo_gsm_tester/obj/upf_open5gs.py
A src/osmo_gsm_tester/templates/open5gs-freediameter.conf.tmpl
A src/osmo_gsm_tester/templates/open5gs-hssd.yaml.tmpl
A src/osmo_gsm_tester/templates/open5gs-mmed.yaml.tmpl
A src/osmo_gsm_tester/templates/open5gs-pcrfd.yaml.tmpl
A src/osmo_gsm_tester/templates/open5gs-sgwcd.yaml.tmpl
A src/osmo_gsm_tester/templates/open5gs-sgwud.yaml.tmpl
A src/osmo_gsm_tester/templates/open5gs-smfd.yaml.tmpl
A src/osmo_gsm_tester/templates/open5gs-upfd.yaml.tmpl
M sysmocom/defaults.conf
A sysmocom/scenarios/cfg-epc-db at .conf
21 files changed, 2,973 insertions(+), 1 deletion(-)

Approvals:
  Jenkins Builder: Verified
  pespin: Looks good to me, approved



diff --git a/contrib/jenkins-build-common.sh b/contrib/jenkins-build-common.sh
index 75d1e4f..9d59ca7 100644
--- a/contrib/jenkins-build-common.sh
+++ b/contrib/jenkins-build-common.sh
@@ -154,7 +154,7 @@
   elif [ -f meson.build ]; then
     rm -rf build && mkdir build && cd build || exit 1
     set +x; echo; echo; set -x
-    meson ../ --prefix=$prefix $configure_opts
+    meson ../ --prefix=$prefix --libdir="lib" $configure_opts
     ninja -j8
     ninja install
     return
diff --git a/doc/manuals/chapters/install.adoc b/doc/manuals/chapters/install.adoc
index c94c596..fb364aa 100644
--- a/doc/manuals/chapters/install.adoc
+++ b/doc/manuals/chapters/install.adoc
@@ -392,6 +392,13 @@
         pydbus
 ----
 
+If one plans to use Open5GS EPC, pymongo modules to interact against MongoDB
+(_epc_open5gs.py_) shall be installed:
+----
+pip3 install \
+        pymongo
+----
+
 IMPORTANT: ofono may need to be installed from source to contain the most
 recent fixes needed to operate your modems. This depends on the modem hardware
 used and the tests run. Please see <<hardware_modems>>.
diff --git a/src/osmo_gsm_tester/obj/epc.py b/src/osmo_gsm_tester/obj/epc.py
index aaa96b7..4c43f5e 100644
--- a/src/osmo_gsm_tester/obj/epc.py
+++ b/src/osmo_gsm_tester/obj/epc.py
@@ -71,6 +71,9 @@
         elif epc_type == 'srsepc':
             from .epc_srs import srsEPC
             epc_class = srsEPC
+        elif epc_type == 'open5gsepc':
+            from .epc_open5gs import Open5gsEPC
+            epc_class = Open5gsEPC
         else:
             raise log.Error('EPC type not supported:', epc_type)
 
diff --git a/src/osmo_gsm_tester/obj/epc_open5gs.py b/src/osmo_gsm_tester/obj/epc_open5gs.py
new file mode 100644
index 0000000..15f4eab
--- /dev/null
+++ b/src/osmo_gsm_tester/obj/epc_open5gs.py
@@ -0,0 +1,184 @@
+# osmo_gsm_tester: specifics for running an Open5GS EPC
+#
+# Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH
+#
+# Author: Pau Espin Pedrol <pespin at sysmocom.de>
+#
+# 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 <http://www.gnu.org/licenses/>.
+
+import os
+import pprint
+import copy
+
+from ..core import log, util, config, template, process, remote
+from ..core import schema
+from . import epc
+from .pcrf_open5gs import Open5gsPCRF
+from .upf_open5gs import Open5gsUPF
+from .smf_open5gs import Open5gsSMF
+from .hss_open5gs import Open5gsHSS
+from .mme_open5gs import Open5gsMME
+from .sgwc_open5gs import Open5gsSGWC
+from .sgwu_open5gs import Open5gsSGWU
+
+def on_register_schemas():
+    config_schema = {
+        'db_host': schema.STR,
+        }
+    schema.register_config_schema('epc', config_schema)
+
+class Open5gsEPC(epc.EPC):
+
+    REMOTE_DIR = '/osmo-gsm-tester-open5gs'
+
+    def __init__(self, testenv, run_node):
+        super().__init__(testenv, run_node, 'open5gs_epc')
+        self.run_dir = None
+        self.pcrf = None
+        self.upf = None
+        self.smf = None
+        self.mme = None
+        self.hss = None
+        self.sgwc = None
+        self.sgwu = None
+        self.subscriber_list = []
+
+    def cleanup(self):
+        if self.pcrf:
+            self.pcrf.cleanup()
+        if self.upf:
+            self.upf.cleanup()
+        if self.smf:
+            self.smf.cleanup()
+        if self.hss:
+            self.hss.cleanup()
+        if self.mme:
+            self.mme.cleanup()
+        if self.sgwc:
+            self.sgwc.cleanup()
+        if self.sgwu:
+            self.sgwu.cleanup()
+
+    def configure(self):
+        values = super().configure(['open5gsepc'])
+        db_host = values['epc']['db_host']
+        db_uri = 'mongodb://'+db_host+'/open5gs'
+        config.overlay(values, dict(epc=dict(db_uri=db_uri)))
+        self.fill_subscribers_mongodb(values['epc']['db_host'], 27017)
+        self.pcrf = Open5gsPCRF(self.testenv, self)
+        self.upf = Open5gsUPF(self.testenv, self)
+        self.smf = Open5gsSMF(self.testenv, self)
+        self.hss = Open5gsHSS(self.testenv, self)
+        self.mme = Open5gsMME(self.testenv, self)
+        self.sgwc = Open5gsSGWC(self.testenv, self)
+        self.sgwu = Open5gsSGWU(self.testenv, self)
+        self.pcrf.configure(copy.deepcopy(values))
+        self.upf.configure(copy.deepcopy(values))
+        self.smf.configure(copy.deepcopy(values))
+        self.hss.configure(copy.deepcopy(values))
+        self.mme.configure(copy.deepcopy(values))
+        self.sgwc.configure(copy.deepcopy(values))
+        self.sgwu.configure(copy.deepcopy(values))
+
+    def start(self):
+        self.log('Starting srsepc')
+        self.run_dir = util.Dir(self.testenv.test().get_run_dir().new_dir(self.name()))
+        self.configure()
+        self.pcrf.start()
+        self.upf.start()
+        self.smf.start()
+        self.hss.start()
+        self.mme.start()
+        self.sgwc.start()
+        self.sgwu.start()
+
+    def subscriber_add(self, modem, msisdn=None, algo_str=None):
+        if msisdn is None:
+            msisdn = modem.msisdn()
+
+        if algo_str is None:
+            algo_str = modem.auth_algo() or 'milenage'
+
+        if algo_str == 'milenage':
+            if not modem.ki():
+                raise log.Error("Auth algo milenage selected but no KI specified")
+            if not modem.opc():
+                raise log.Error("Auth algo milenage selected but no OPC specified")
+        else:
+            raise log.Error("Open5Gs only supports auth algo: milenage")
+
+        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': modem.opc(), 'apn_ipaddr': modem.apn_ipaddr()})
+        return subscriber_id
+
+    def fill_subscribers_mongodb(self, server, port):
+        import pymongo
+
+        myclient = pymongo.MongoClient("mongodb://" + str(server) + ":" + str(port) + "/")
+        mydb = myclient["open5gs"]
+        mycol = mydb["subscribers"]
+
+        for s in self.subscriber_list:
+            self.log('Insert subscriber to DB', msisdn=s['msisdn'], imsi=s['imsi'], subscriber_id=s['id'],
+                     algo_str=s['auth_algo'])
+            slice_data = [ { \
+                "sst": 1, \
+                "default_indicator": True, \
+                "session": [ \
+                    { \
+                    "name": "internet", \
+                    "type": 3, "pcc_rule": [], "ambr": {"uplink": {"value": 1, "unit": 0}, "downlink": {"value": 1, "unit": 0}}, \
+                    "qos": { "index": 9, "arp": {"priority_level": 8, "pre_emption_capability": 1, "pre_emption_vulnerability": 1} } \
+                    } \
+                ] \
+            } ]
+
+            sub_data = {'imsi':  s['imsi'], \
+                        'subscribed_rau_tau_timer': 12, \
+                        'network_access_mode': 2, \
+                        'subscriber_status': 0, \
+                        "access_restriction_data": 32, \
+                        'slice': slice_data, \
+                        'ambr': {"uplink": {"value": 1, "unit": 0}, "downlink": {"value": 1, "unit": 0}}, \
+                        'security': {'k': s['ki'], 'amf': '8000', 'op': None, 'opc': s['opc']},
+                        'schema_version': 1, \
+                        '__v': 0}
+            x = mycol.insert_one(sub_data)
+            self.dbg("Added subscriber with Inserted ID : " + str(x.inserted_id))
+            s['inserted_id'] = x.inserted_id
+
+    def enb_is_connected(self, enb):
+        # Match against sample mmed line: "eNB-S1 accepted[172.18.50.101]:50867"
+        if not self.mme or not self.mme.running():
+            return False
+        stdout_lines = (self.mme.process.get_stdout() or '').splitlines()
+        for l in stdout_lines:
+            if 'eNB' in l and 'accepted' in l and enb.addr() in l:
+                return True
+        return False
+
+    def running(self):
+        return self.pcrf and self.upf and self.smf and self.hss and \
+               self.mme and self.sgwc and self.sgwu and \
+               self.pcrf.running() and self.upf.running() and self.smf.running() and \
+               self.hss.running() and self.mme.running() and self.sgwc.running() and \
+               self.sgwu.running()
+
+    def tun_addr(self):
+        return '172.16.0.1'
+
+    def get_kpis(self):
+        return {}
+
+# vim: expandtab tabstop=4 shiftwidth=4
diff --git a/src/osmo_gsm_tester/obj/hss_open5gs.py b/src/osmo_gsm_tester/obj/hss_open5gs.py
new file mode 100644
index 0000000..211d09a
--- /dev/null
+++ b/src/osmo_gsm_tester/obj/hss_open5gs.py
@@ -0,0 +1,158 @@
+# osmo_gsm_tester: specifics for running an Open5GS hssd process
+#
+# Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH
+#
+# Author: Pau Espin Pedrol <pespin at sysmocom.de>
+#
+# 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 <http://www.gnu.org/licenses/>.
+
+import os
+import pprint
+
+from ..core import log, util, config, template, process, remote
+from ..core import schema
+from . import epc
+
+def on_register_schemas():
+    pass
+
+class Open5gsHSS(log.Origin):
+
+    REMOTE_DIR = '/osmo-gsm-tester-open5gs'
+    BINFILE = 'open5gs-hssd'
+    CFGFILE = 'open5gs-hssd.yaml'
+    LOGFILE = 'open5gs-hssd.log'
+    DIAMETERFILE = 'open5gs-freediameter.conf'
+
+    def __init__(self, testenv, o5gs_epc):
+        super().__init__(log.C_RUN, 'open5gs-hssd')
+        self.testenv = testenv
+        self.o5gs_epc = o5gs_epc
+        self._run_node = o5gs_epc.run_node()
+        self.run_dir = None
+        self.config_file = None
+        self.log_file = None
+        self.diameter_file = None
+        self.process = None
+        self.rem_host = None
+        self.remote_inst = None
+        self.remote_config_file = None
+        self.remote_log_file = None
+        self.remote_diameter_file = None
+
+    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))
+
+    def start(self):
+        self.log('Starting %s' % Open5gsHSS.BINFILE)
+        if self._run_node.is_local():
+            self.start_locally()
+        else:
+            self.start_remotely()
+
+    def start_remotely(self):
+        remote_env = { 'LD_LIBRARY_PATH': self.remote_inst.child('lib') }
+        remote_lib = self.remote_inst.child('lib')
+        remote_binary = self.remote_inst.child('bin', Open5gsHSS.BINFILE)
+
+        args = (remote_binary, '-c', self.remote_config_file)
+
+        self.process = self.rem_host.RemoteProcess(Open5gsHSS.BINFILE, args, remote_env=remote_env)
+        self.testenv.remember_to_stop(self.process)
+        self.process.launch()
+
+    def start_locally(self):
+        binary = self.inst.child('bin', Open5gsHSS.BINFILE)
+        lib = self.inst.child('lib')
+        env = { 'LD_LIBRARY_PATH': util.prepend_library_path(lib) }
+
+        args = (binary, '-c', os.path.abspath(self.config_file))
+
+        self.process = process.Process(self.name(), self.run_dir, args, env=env)
+        self.testenv.remember_to_stop(self.process)
+        self.process.launch()
+
+    def configure(self, values):
+        self.run_dir = util.Dir(self.o5gs_epc.run_dir.new_dir(self.name()))
+        self.inst = util.Dir(os.path.abspath(self.testenv.suite().trial().get_inst('open5gs', self._run_node.run_label())))
+        if not os.path.isdir(self.inst.child('lib')):
+            raise log.Error('No lib/ in', self.inst)
+        if not self.inst.isfile('bin', Open5gsHSS.BINFILE):
+            raise log.Error('No %s binary in' % Open5gsHSS.BINFILE, self.inst)
+
+        self.config_file = self.run_dir.child(Open5gsHSS.CFGFILE)
+        self.log_file = self.run_dir.child(Open5gsHSS.LOGFILE)
+        self.diameter_file = self.run_dir.child(Open5gsHSS.DIAMETERFILE)
+
+        if not self._run_node.is_local():
+            self.rem_host = remote.RemoteHost(self.run_dir, self._run_node.ssh_user(), self._run_node.ssh_addr())
+            remote_prefix_dir = util.Dir(Open5gsHSS.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(Open5gsHSS.BINFILE))
+
+            self.remote_config_file = remote_run_dir.child(Open5gsHSS.CFGFILE)
+            self.remote_log_file = remote_run_dir.child(Open5gsHSS.LOGFILE)
+            self.remote_diameter_file = remote_run_dir.child(Open5gsHSS.DIAMETERFILE)
+
+        logfile = self.log_file if self._run_node.is_local() else self.remote_log_file
+        diameter_file = self.diameter_file if self._run_node.is_local() else self.remote_diameter_file
+        inst_prefix = str(self.inst) if self._run_node.is_local() else str(self.remote_inst)
+        config.overlay(values, dict(hss=dict(log_filename=logfile,
+                                             diameter_filename=diameter_file,
+                                             inst_prefix=inst_prefix)))
+        config.overlay(values, dict(diameter=dict(identity=self.diameter_name(),
+                                                  inst_prefix=inst_prefix,
+                                                  listen_address=self.o5gs_epc.addr(),
+                                                  listen_port=self.diameter_port(),
+                                                  connect_name=self.o5gs_epc.mme.diameter_name(),
+                                                  connect_address=self.o5gs_epc.addr(),
+                                                  connect_port=self.o5gs_epc.mme.diameter_port())))
+
+        self.dbg('OPEN5GS-HSS CONFIG:\n' + pprint.pformat(values))
+
+        with open(self.config_file, 'w') as f:
+            r = template.render(Open5gsHSS.CFGFILE, values)
+            self.dbg(r)
+            f.write(r)
+
+        with open(self.diameter_file, 'w') as f:
+            r = template.render(Open5gsHSS.DIAMETERFILE, values)
+            self.dbg(r)
+            f.write(r)
+
+        if not self._run_node.is_local():
+            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.recreate_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-diam-to-remote', self.diameter_file, self.remote_diameter_file)
+
+    def running(self):
+        return not self.process.terminated()
+
+    def diameter_name(self):
+        return 'hss'
+
+    def diameter_port(self):
+        return 3868;
+
+# vim: expandtab tabstop=4 shiftwidth=4
diff --git a/src/osmo_gsm_tester/obj/mme_open5gs.py b/src/osmo_gsm_tester/obj/mme_open5gs.py
new file mode 100644
index 0000000..0b3d633
--- /dev/null
+++ b/src/osmo_gsm_tester/obj/mme_open5gs.py
@@ -0,0 +1,172 @@
+# osmo_gsm_tester: specifics for running an Open5GS mmed process
+#
+# Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH
+#
+# Author: Pau Espin Pedrol <pespin at sysmocom.de>
+#
+# 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 <http://www.gnu.org/licenses/>.
+
+import os
+import pprint
+
+from ..core import log, util, config, template, process, remote
+from ..core import schema
+from . import epc
+
+def on_register_schemas():
+    pass
+
+class Open5gsMME(log.Origin):
+
+    REMOTE_DIR = '/osmo-gsm-tester-open5gs'
+    BINFILE = 'open5gs-mmed'
+    CFGFILE = 'open5gs-mmed.yaml'
+    LOGFILE = 'open5gs-mmed.log'
+    DIAMETERFILE = 'open5gs-freediameter.conf'
+
+    def __init__(self, testenv, o5gs_epc):
+        super().__init__(log.C_RUN, 'open5gs-mmed')
+        self.testenv = testenv
+        self.o5gs_epc = o5gs_epc
+        self._run_node = o5gs_epc.run_node()
+        self.run_dir = None
+        self.config_file = None
+        self.log_file = None
+        self.diameter_file = None
+        self.process = None
+        self.rem_host = None
+        self.remote_inst = None
+        self.remote_config_file = None
+        self.remote_log_file = None
+        self.remote_diameter_file = None
+
+    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))
+
+    def start(self):
+        self.log('Starting %s' % Open5gsMME.BINFILE)
+        if self._run_node.is_local():
+            self.start_locally()
+        else:
+            self.start_remotely()
+
+    def start_remotely(self):
+        remote_lib = self.remote_inst.child('lib')
+        remote_binary = self.remote_inst.child('bin', Open5gsMME.BINFILE)
+
+        # setting capabilities will later disable use of LD_LIBRARY_PATH from ELF loader -> modify RPATH instead.
+        self.log('Setting RPATH for open5gs-mmed')
+        self.rem_host.change_elf_rpath(remote_binary, remote_lib)
+        # open5gs-mmed requires CAP_NET_ADMIN to create tunnel devices: ioctl(TUNSETIFF):
+        self.log('Applying CAP_NET_ADMIN capability to open5gs-mmed')
+        self.rem_host.setcap_net_admin(remote_binary)
+
+        args = (remote_binary, '-c', self.remote_config_file)
+
+        self.process = self.rem_host.RemoteProcess(Open5gsMME.BINFILE, args)
+        self.testenv.remember_to_stop(self.process)
+        self.process.launch()
+
+    def start_locally(self):
+        binary = self.inst.child('bin', Open5gsMME.BINFILE)
+        lib = self.inst.child('lib')
+        env = {}
+
+        # setting capabilities will later disable use of LD_LIBRARY_PATH from ELF loader -> modify RPATH instead.
+        self.log('Setting RPATH for open5gs-mmed')
+        # open5gs-mmed 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'))
+        # open5gs-mmed requires CAP_NET_ADMIN to create tunnel devices: ioctl(TUNSETIFF):
+        self.log('Applying CAP_NET_ADMIN capability to open5gs-mmed')
+        util.setcap_net_admin(binary, self.run_dir.new_dir('setcap_net_admin'))
+
+        args = (binary, '-c', os.path.abspath(self.config_file))
+
+        self.process = process.Process(self.name(), self.run_dir, args, env=env)
+        self.testenv.remember_to_stop(self.process)
+        self.process.launch()
+
+    def configure(self, values):
+        self.run_dir = util.Dir(self.o5gs_epc.run_dir.new_dir(self.name()))
+        self.inst = util.Dir(os.path.abspath(self.testenv.suite().trial().get_inst('open5gs', self._run_node.run_label())))
+        if not os.path.isdir(self.inst.child('lib')):
+            raise log.Error('No lib/ in', self.inst)
+        if not self.inst.isfile('bin', Open5gsMME.BINFILE):
+            raise log.Error('No %s binary in' % Open5gsMME.BINFILE, self.inst)
+
+        self.config_file = self.run_dir.child(Open5gsMME.CFGFILE)
+        self.log_file = self.run_dir.child(Open5gsMME.LOGFILE)
+        self.diameter_file = self.run_dir.child(Open5gsMME.DIAMETERFILE)
+
+        if not self._run_node.is_local():
+            self.rem_host = remote.RemoteHost(self.run_dir, self._run_node.ssh_user(), self._run_node.ssh_addr())
+            remote_prefix_dir = util.Dir(Open5gsMME.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(Open5gsMME.BINFILE))
+
+            self.remote_config_file = remote_run_dir.child(Open5gsMME.CFGFILE)
+            self.remote_log_file = remote_run_dir.child(Open5gsMME.LOGFILE)
+            self.remote_diameter_file = remote_run_dir.child(Open5gsMME.DIAMETERFILE)
+
+        logfile = self.log_file if self._run_node.is_local() else self.remote_log_file
+        diameter_file = self.diameter_file if self._run_node.is_local() else self.remote_diameter_file
+        inst_prefix = str(self.inst) if self._run_node.is_local() else str(self.remote_inst)
+        config.overlay(values, dict(mme=dict(log_filename=logfile,
+                                             diameter_filename=diameter_file,
+                                             inst_prefix=inst_prefix)))
+        config.overlay(values, dict(diameter=dict(identity=self.diameter_name(),
+                                                  inst_prefix=inst_prefix,
+                                                  listen_address=self.o5gs_epc.addr(),
+                                                  listen_port=self.diameter_port(),
+                                                  connect_name=self.o5gs_epc.hss.diameter_name(),
+                                                  connect_address=self.o5gs_epc.addr(),
+                                                  connect_port=self.o5gs_epc.hss.diameter_port())))
+
+        self.dbg('OPEN5GS-MME CONFIG:\n' + pprint.pformat(values))
+
+        with open(self.config_file, 'w') as f:
+            r = template.render(Open5gsMME.CFGFILE, values)
+            self.dbg(r)
+            f.write(r)
+
+        with open(self.diameter_file, 'w') as f:
+            r = template.render(Open5gsMME.DIAMETERFILE, values)
+            self.dbg(r)
+            f.write(r)
+
+        if not self._run_node.is_local():
+            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.recreate_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-diam-to-remote', self.diameter_file, self.remote_diameter_file)
+
+    def running(self):
+        return not self.process.terminated()
+
+    def diameter_name(self):
+        return 'mme'
+
+    def diameter_port(self):
+        return 3868 + 1;
+
+# vim: expandtab tabstop=4 shiftwidth=4
diff --git a/src/osmo_gsm_tester/obj/pcrf_open5gs.py b/src/osmo_gsm_tester/obj/pcrf_open5gs.py
new file mode 100644
index 0000000..4f93a47
--- /dev/null
+++ b/src/osmo_gsm_tester/obj/pcrf_open5gs.py
@@ -0,0 +1,158 @@
+# osmo_gsm_tester: specifics for running an Open5GS pcrfd process
+#
+# Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH
+#
+# Author: Pau Espin Pedrol <pespin at sysmocom.de>
+#
+# 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 <http://www.gnu.org/licenses/>.
+
+import os
+import pprint
+
+from ..core import log, util, config, template, process, remote
+from ..core import schema
+from . import epc
+
+def on_register_schemas():
+    pass
+
+class Open5gsPCRF(log.Origin):
+
+    REMOTE_DIR = '/osmo-gsm-tester-open5gs'
+    BINFILE = 'open5gs-pcrfd'
+    CFGFILE = 'open5gs-pcrfd.yaml'
+    LOGFILE = 'open5gs-pcrfd.log'
+    DIAMETERFILE = 'open5gs-freediameter.conf'
+
+    def __init__(self, testenv, o5gs_epc):
+        super().__init__(log.C_RUN, 'open5gs-pcrfd')
+        self.testenv = testenv
+        self.o5gs_epc = o5gs_epc
+        self._run_node = o5gs_epc.run_node()
+        self.run_dir = None
+        self.config_file = None
+        self.log_file = None
+        self.diameter_file = None
+        self.process = None
+        self.rem_host = None
+        self.remote_inst = None
+        self.remote_config_file = None
+        self.remote_log_file = None
+        self.remote_diameter_file = None
+
+    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))
+
+    def start(self):
+        self.log('Starting %s' % Open5gsPCRF.BINFILE)
+        if self._run_node.is_local():
+            self.start_locally()
+        else:
+            self.start_remotely()
+
+    def start_remotely(self):
+        remote_env = { 'LD_LIBRARY_PATH': self.remote_inst.child('lib') }
+        remote_lib = self.remote_inst.child('lib')
+        remote_binary = self.remote_inst.child('bin', Open5gsPCRF.BINFILE)
+
+        args = (remote_binary, '-c', self.remote_config_file)
+
+        self.process = self.rem_host.RemoteProcess(Open5gsPCRF.BINFILE, args, remote_env=remote_env)
+        self.testenv.remember_to_stop(self.process)
+        self.process.launch()
+
+    def start_locally(self):
+        binary = self.inst.child('bin', Open5gsPCRF.BINFILE)
+        lib = self.inst.child('lib')
+        env = { 'LD_LIBRARY_PATH': util.prepend_library_path(lib) }
+
+        args = (binary, '-c', os.path.abspath(self.config_file))
+
+        self.process = process.Process(self.name(), self.run_dir, args, env=env)
+        self.testenv.remember_to_stop(self.process)
+        self.process.launch()
+
+    def configure(self, values):
+        self.run_dir = util.Dir(self.o5gs_epc.run_dir.new_dir(self.name()))
+        self.inst = util.Dir(os.path.abspath(self.testenv.suite().trial().get_inst('open5gs', self._run_node.run_label())))
+        if not os.path.isdir(self.inst.child('lib')):
+            raise log.Error('No lib/ in', self.inst)
+        if not self.inst.isfile('bin', Open5gsPCRF.BINFILE):
+            raise log.Error('No %s binary in' % Open5gsPCRF.BINFILE, self.inst)
+
+        self.config_file = self.run_dir.child(Open5gsPCRF.CFGFILE)
+        self.log_file = self.run_dir.child(Open5gsPCRF.LOGFILE)
+        self.diameter_file = self.run_dir.child(Open5gsPCRF.DIAMETERFILE)
+
+        if not self._run_node.is_local():
+            self.rem_host = remote.RemoteHost(self.run_dir, self._run_node.ssh_user(), self._run_node.ssh_addr())
+            remote_prefix_dir = util.Dir(Open5gsPCRF.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(Open5gsPCRF.BINFILE))
+
+            self.remote_config_file = remote_run_dir.child(Open5gsPCRF.CFGFILE)
+            self.remote_log_file = remote_run_dir.child(Open5gsPCRF.LOGFILE)
+            self.remote_diameter_file = remote_run_dir.child(Open5gsPCRF.DIAMETERFILE)
+
+        logfile = self.log_file if self._run_node.is_local() else self.remote_log_file
+        diameter_file = self.diameter_file if self._run_node.is_local() else self.remote_diameter_file
+        inst_prefix = str(self.inst) if self._run_node.is_local() else str(self.remote_inst)
+        config.overlay(values, dict(pcrf=dict(log_filename=logfile,
+                                             diameter_filename=diameter_file,
+                                             inst_prefix=inst_prefix)))
+        config.overlay(values, dict(diameter=dict(identity=self.diameter_name(),
+                                                  inst_prefix=inst_prefix,
+                                                  listen_address=self.o5gs_epc.addr(),
+                                                  listen_port=self.diameter_port(),
+                                                  connect_name=self.o5gs_epc.smf.diameter_name(),
+                                                  connect_address=self.o5gs_epc.addr(),
+                                                  connect_port=self.o5gs_epc.smf.diameter_port())))
+
+        self.dbg('OPEN5GS-PCRF CONFIG:\n' + pprint.pformat(values))
+
+        with open(self.config_file, 'w') as f:
+            r = template.render(Open5gsPCRF.CFGFILE, values)
+            self.dbg(r)
+            f.write(r)
+
+        with open(self.diameter_file, 'w') as f:
+            r = template.render(Open5gsPCRF.DIAMETERFILE, values)
+            self.dbg(r)
+            f.write(r)
+
+        if not self._run_node.is_local():
+            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.recreate_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-diam-to-remote', self.diameter_file, self.remote_diameter_file)
+
+    def running(self):
+        return not self.process.terminated()
+
+    def diameter_name(self):
+        return 'pcrf'
+
+    def diameter_port(self):
+        return 3868 + 2;
+
+# vim: expandtab tabstop=4 shiftwidth=4
diff --git a/src/osmo_gsm_tester/obj/sgwc_open5gs.py b/src/osmo_gsm_tester/obj/sgwc_open5gs.py
new file mode 100644
index 0000000..efc4568
--- /dev/null
+++ b/src/osmo_gsm_tester/obj/sgwc_open5gs.py
@@ -0,0 +1,132 @@
+# osmo_gsm_tester: specifics for running an Open5GS swgcd process
+#
+# Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH
+#
+# Author: Pau Espin Pedrol <pespin at sysmocom.de>
+#
+# 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 <http://www.gnu.org/licenses/>.
+
+import os
+import pprint
+
+from ..core import log, util, config, template, process, remote
+from ..core import schema
+from . import epc
+
+def on_register_schemas():
+    pass
+
+class Open5gsSGWC(log.Origin):
+
+    REMOTE_DIR = '/osmo-gsm-tester-open5gs'
+    BINFILE = 'open5gs-sgwcd'
+    CFGFILE = 'open5gs-sgwcd.yaml'
+    LOGFILE = 'open5gs-sgwcd.log'
+
+    def __init__(self, testenv, o5gs_epc):
+        super().__init__(log.C_RUN, 'open5gs-sgwcd')
+        self.testenv = testenv
+        self.o5gs_epc = o5gs_epc
+        self._run_node = o5gs_epc.run_node()
+        self.run_dir = None
+        self.config_file = None
+        self.log_file = None
+        self.process = None
+        self.rem_host = None
+        self.remote_inst = None
+        self.remote_config_file = None
+        self.remote_log_file = None
+
+    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))
+
+    def start(self):
+        self.log('Starting %s' % Open5gsSGWC.BINFILE)
+        if self._run_node.is_local():
+            self.start_locally()
+        else:
+            self.start_remotely()
+
+    def start_remotely(self):
+        remote_env = { 'LD_LIBRARY_PATH': self.remote_inst.child('lib') }
+        remote_lib = self.remote_inst.child('lib')
+        remote_binary = self.remote_inst.child('bin', Open5gsSGWC.BINFILE)
+
+        args = (remote_binary, '-c', self.remote_config_file)
+
+        self.process = self.rem_host.RemoteProcess(Open5gsSGWC.BINFILE, args, remote_env=remote_env)
+        self.testenv.remember_to_stop(self.process)
+        self.process.launch()
+
+    def start_locally(self):
+        binary = self.inst.child('bin', Open5gsSGWC.BINFILE)
+        lib = self.inst.child('lib')
+        env = { 'LD_LIBRARY_PATH': util.prepend_library_path(lib) }
+
+        args = (binary, '-c', os.path.abspath(self.config_file))
+
+        self.process = process.Process(self.name(), self.run_dir, args, env=env)
+        self.testenv.remember_to_stop(self.process)
+        self.process.launch()
+
+    def configure(self, values):
+        self.run_dir = util.Dir(self.o5gs_epc.run_dir.new_dir(self.name()))
+        self.inst = util.Dir(os.path.abspath(self.testenv.suite().trial().get_inst('open5gs', self._run_node.run_label())))
+        if not os.path.isdir(self.inst.child('lib')):
+            raise log.Error('No lib/ in', self.inst)
+        if not self.inst.isfile('bin', Open5gsSGWC.BINFILE):
+            raise log.Error('No %s binary in' % Open5gsSGWC.BINFILE, self.inst)
+
+        self.config_file = self.run_dir.child(Open5gsSGWC.CFGFILE)
+        self.log_file = self.run_dir.child(Open5gsSGWC.LOGFILE)
+
+        if not self._run_node.is_local():
+            self.rem_host = remote.RemoteHost(self.run_dir, self._run_node.ssh_user(), self._run_node.ssh_addr())
+            remote_prefix_dir = util.Dir(Open5gsSGWC.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(Open5gsSGWC.BINFILE))
+
+            self.remote_config_file = remote_run_dir.child(Open5gsSGWC.CFGFILE)
+            self.remote_log_file = remote_run_dir.child(Open5gsSGWC.LOGFILE)
+
+        logfile = self.log_file if self._run_node.is_local() else self.remote_log_file
+        inst_prefix = str(self.inst) if self._run_node.is_local() else str(self.remote_inst)
+        config.overlay(values, dict(sgwc=dict(log_filename=logfile,
+                                             inst_prefix=inst_prefix)))
+
+        self.dbg('OPEN5GS-SGWC CONFIG:\n' + pprint.pformat(values))
+
+        with open(self.config_file, 'w') as f:
+            r = template.render(Open5gsSGWC.CFGFILE, values)
+            self.dbg(r)
+            f.write(r)
+
+        if not self._run_node.is_local():
+            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.recreate_remote_dir(remote_run_dir)
+            self.rem_host.scp('scp-cfg-to-remote', self.config_file, self.remote_config_file)
+
+    def running(self):
+        return not self.process.terminated()
+
+# vim: expandtab tabstop=4 shiftwidth=4
diff --git a/src/osmo_gsm_tester/obj/sgwu_open5gs.py b/src/osmo_gsm_tester/obj/sgwu_open5gs.py
new file mode 100644
index 0000000..b97dd92
--- /dev/null
+++ b/src/osmo_gsm_tester/obj/sgwu_open5gs.py
@@ -0,0 +1,140 @@
+# osmo_gsm_tester: specifics for running an Open5GS sgwud process
+#
+# Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH
+#
+# Author: Pau Espin Pedrol <pespin at sysmocom.de>
+#
+# 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 <http://www.gnu.org/licenses/>.
+
+import os
+import pprint
+
+from ..core import log, util, config, template, process, remote
+from ..core import schema
+from . import epc
+
+def on_register_schemas():
+    pass
+
+class Open5gsSGWU(log.Origin):
+
+    REMOTE_DIR = '/osmo-gsm-tester-open5gs'
+    BINFILE = 'open5gs-sgwud'
+    CFGFILE = 'open5gs-sgwud.yaml'
+    LOGFILE = 'open5gs-sgwud.log'
+
+    def __init__(self, testenv, o5gs_epc):
+        super().__init__(log.C_RUN, 'open5gs-sgwud')
+        self.testenv = testenv
+        self.o5gs_epc = o5gs_epc
+        self._run_node = o5gs_epc.run_node()
+        self.run_dir = None
+        self.config_file = None
+        self.log_file = None
+        self.process = None
+        self.rem_host = None
+        self.remote_inst = None
+        self.remote_config_file = None
+        self.remote_log_file = None
+
+    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))
+
+    def start(self):
+        self.log('Starting %s' % Open5gsSGWU.BINFILE)
+        if self._run_node.is_local():
+            self.start_locally()
+        else:
+            self.start_remotely()
+
+    def start_remotely(self):
+        remote_env = { 'LD_LIBRARY_PATH': self.remote_inst.child('lib') }
+        remote_lib = self.remote_inst.child('lib')
+        remote_binary = self.remote_inst.child('bin', Open5gsSGWU.BINFILE)
+
+        args = (remote_binary, '-c', self.remote_config_file)
+
+        self.process = self.rem_host.RemoteProcess(Open5gsSGWU.BINFILE, args, remote_env=remote_env)
+        self.testenv.remember_to_stop(self.process)
+        self.process.launch()
+
+    def start_locally(self):
+        binary = self.inst.child('bin', Open5gsSGWU.BINFILE)
+        lib = self.inst.child('lib')
+        env = { 'LD_LIBRARY_PATH': util.prepend_library_path(lib) }
+
+        # setting capabilities will later disable use of LD_LIBRARY_PATH from ELF loader -> modify RPATH instead.
+        self.log('Setting RPATH for open5gs-sgwud')
+        # open5gs-sgwud 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'))
+        # open5gs-sgwud requires CAP_NET_ADMIN to create tunnel devices: ioctl(TUNSETIFF):
+        self.log('Applying CAP_NET_ADMIN capability to open5gs-sgwud')
+        util.setcap_net_admin(binary, self.run_dir.new_dir('setcap_net_admin'))
+
+        args = (binary, '-c', os.path.abspath(self.config_file))
+
+        self.process = process.Process(self.name(), self.run_dir, args, env=env)
+        self.testenv.remember_to_stop(self.process)
+        self.process.launch()
+
+    def configure(self, values):
+        self.run_dir = util.Dir(self.o5gs_epc.run_dir.new_dir(self.name()))
+        self.inst = util.Dir(os.path.abspath(self.testenv.suite().trial().get_inst('open5gs', self._run_node.run_label())))
+        if not os.path.isdir(self.inst.child('lib')):
+            raise log.Error('No lib/ in', self.inst)
+        if not self.inst.isfile('bin', Open5gsSGWU.BINFILE):
+            raise log.Error('No %s binary in' % Open5gsSGWU.BINFILE, self.inst)
+
+        self.config_file = self.run_dir.child(Open5gsSGWU.CFGFILE)
+        self.log_file = self.run_dir.child(Open5gsSGWU.LOGFILE)
+
+        if not self._run_node.is_local():
+            self.rem_host = remote.RemoteHost(self.run_dir, self._run_node.ssh_user(), self._run_node.ssh_addr())
+            remote_prefix_dir = util.Dir(Open5gsSGWU.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(Open5gsSGWU.BINFILE))
+
+            self.remote_config_file = remote_run_dir.child(Open5gsSGWU.CFGFILE)
+            self.remote_log_file = remote_run_dir.child(Open5gsSGWU.LOGFILE)
+
+        logfile = self.log_file if self._run_node.is_local() else self.remote_log_file
+        inst_prefix = str(self.inst) if self._run_node.is_local() else str(self.remote_inst)
+        config.overlay(values, dict(sgwu=dict(log_filename=logfile,
+                                             inst_prefix=inst_prefix)))
+
+        self.dbg('OPEN5GS-SGWU CONFIG:\n' + pprint.pformat(values))
+
+        with open(self.config_file, 'w') as f:
+            r = template.render(Open5gsSGWU.CFGFILE, values)
+            self.dbg(r)
+            f.write(r)
+
+        if not self._run_node.is_local():
+            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.recreate_remote_dir(remote_run_dir)
+            self.rem_host.scp('scp-cfg-to-remote', self.config_file, self.remote_config_file)
+
+    def running(self):
+        return not self.process.terminated()
+
+# vim: expandtab tabstop=4 shiftwidth=4
diff --git a/src/osmo_gsm_tester/obj/smf_open5gs.py b/src/osmo_gsm_tester/obj/smf_open5gs.py
new file mode 100644
index 0000000..5241dc2
--- /dev/null
+++ b/src/osmo_gsm_tester/obj/smf_open5gs.py
@@ -0,0 +1,158 @@
+# osmo_gsm_tester: specifics for running an Open5GS smfd process
+#
+# Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH
+#
+# Author: Pau Espin Pedrol <pespin at sysmocom.de>
+#
+# 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 <http://www.gnu.org/licenses/>.
+
+import os
+import pprint
+
+from ..core import log, util, config, template, process, remote
+from ..core import schema
+from . import epc
+
+def on_register_schemas():
+    pass
+
+class Open5gsSMF(log.Origin):
+
+    REMOTE_DIR = '/osmo-gsm-tester-open5gs'
+    BINFILE = 'open5gs-smfd'
+    CFGFILE = 'open5gs-smfd.yaml'
+    LOGFILE = 'open5gs-smfd.log'
+    DIAMETERFILE = 'open5gs-freediameter.conf'
+
+    def __init__(self, testenv, o5gs_epc):
+        super().__init__(log.C_RUN, 'open5gs-smfd')
+        self.testenv = testenv
+        self.o5gs_epc = o5gs_epc
+        self._run_node = o5gs_epc.run_node()
+        self.run_dir = None
+        self.config_file = None
+        self.log_file = None
+        self.diameter_file = None
+        self.process = None
+        self.rem_host = None
+        self.remote_inst = None
+        self.remote_config_file = None
+        self.remote_log_file = None
+        self.remote_diameter_file = None
+
+    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))
+
+    def start(self):
+        self.log('Starting %s' % Open5gsSMF.BINFILE)
+        if self._run_node.is_local():
+            self.start_locally()
+        else:
+            self.start_remotely()
+
+    def start_remotely(self):
+        remote_env = { 'LD_LIBRARY_PATH': self.remote_inst.child('lib') }
+        remote_lib = self.remote_inst.child('lib')
+        remote_binary = self.remote_inst.child('bin', Open5gsSMF.BINFILE)
+
+        args = (remote_binary, '-c', self.remote_config_file)
+
+        self.process = self.rem_host.RemoteProcess(Open5gsSMF.BINFILE, args, remote_env=remote_env)
+        self.testenv.remember_to_stop(self.process)
+        self.process.launch()
+
+    def start_locally(self):
+        binary = self.inst.child('bin', Open5gsSMF.BINFILE)
+        lib = self.inst.child('lib')
+        env = { 'LD_LIBRARY_PATH': util.prepend_library_path(lib) }
+
+        args = (binary, '-c', os.path.abspath(self.config_file))
+
+        self.process = process.Process(self.name(), self.run_dir, args, env=env)
+        self.testenv.remember_to_stop(self.process)
+        self.process.launch()
+
+    def configure(self, values):
+        self.run_dir = util.Dir(self.o5gs_epc.run_dir.new_dir(self.name()))
+        self.inst = util.Dir(os.path.abspath(self.testenv.suite().trial().get_inst('open5gs', self._run_node.run_label())))
+        if not os.path.isdir(self.inst.child('lib')):
+            raise log.Error('No lib/ in', self.inst)
+        if not self.inst.isfile('bin', Open5gsSMF.BINFILE):
+            raise log.Error('No %s binary in' % Open5gsSMF.BINFILE, self.inst)
+
+        self.config_file = self.run_dir.child(Open5gsSMF.CFGFILE)
+        self.log_file = self.run_dir.child(Open5gsSMF.LOGFILE)
+        self.diameter_file = self.run_dir.child(Open5gsSMF.DIAMETERFILE)
+
+        if not self._run_node.is_local():
+            self.rem_host = remote.RemoteHost(self.run_dir, self._run_node.ssh_user(), self._run_node.ssh_addr())
+            remote_prefix_dir = util.Dir(Open5gsSMF.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(Open5gsSMF.BINFILE))
+
+            self.remote_config_file = remote_run_dir.child(Open5gsSMF.CFGFILE)
+            self.remote_log_file = remote_run_dir.child(Open5gsSMF.LOGFILE)
+            self.remote_diameter_file = remote_run_dir.child(Open5gsSMF.DIAMETERFILE)
+
+        logfile = self.log_file if self._run_node.is_local() else self.remote_log_file
+        diameter_file = self.diameter_file if self._run_node.is_local() else self.remote_diameter_file
+        inst_prefix = str(self.inst) if self._run_node.is_local() else str(self.remote_inst)
+        config.overlay(values, dict(smf=dict(log_filename=logfile,
+                                             diameter_filename=diameter_file,
+                                             inst_prefix=inst_prefix)))
+        config.overlay(values, dict(diameter=dict(identity=self.diameter_name(),
+                                                  inst_prefix=inst_prefix,
+                                                  listen_address=self.o5gs_epc.addr(),
+                                                  listen_port=self.diameter_port(),
+                                                  connect_name=self.o5gs_epc.pcrf.diameter_name(),
+                                                  connect_address=self.o5gs_epc.addr(),
+                                                  connect_port=self.o5gs_epc.pcrf.diameter_port())))
+
+        self.dbg('OPEN5GS-SMF CONFIG:\n' + pprint.pformat(values))
+
+        with open(self.config_file, 'w') as f:
+            r = template.render(Open5gsSMF.CFGFILE, values)
+            self.dbg(r)
+            f.write(r)
+
+        with open(self.diameter_file, 'w') as f:
+            r = template.render(Open5gsSMF.DIAMETERFILE, values)
+            self.dbg(r)
+            f.write(r)
+
+        if not self._run_node.is_local():
+            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.recreate_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-diam-to-remote', self.diameter_file, self.remote_diameter_file)
+
+    def running(self):
+        return not self.process.terminated()
+
+    def diameter_name(self):
+        return 'smf'
+
+    def diameter_port(self):
+        return 3868 + 3;
+
+# vim: expandtab tabstop=4 shiftwidth=4
diff --git a/src/osmo_gsm_tester/obj/upf_open5gs.py b/src/osmo_gsm_tester/obj/upf_open5gs.py
new file mode 100644
index 0000000..f22a6d7
--- /dev/null
+++ b/src/osmo_gsm_tester/obj/upf_open5gs.py
@@ -0,0 +1,147 @@
+# osmo_gsm_tester: specifics for running an Open%GS upfd process
+#
+# Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH
+#
+# Author: Pau Espin Pedrol <pespin at sysmocom.de>
+#
+# 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 <http://www.gnu.org/licenses/>.
+
+import os
+import pprint
+
+from ..core import log, util, config, template, process, remote
+from ..core import schema
+from . import epc
+
+def on_register_schemas():
+    pass
+
+class Open5gsUPF(log.Origin):
+
+    REMOTE_DIR = '/osmo-gsm-tester-open5gs'
+    BINFILE = 'open5gs-upfd'
+    CFGFILE = 'open5gs-upfd.yaml'
+    LOGFILE = 'open5gs-upfd.log'
+    DIAMETERFILE = 'open5gs-freediameter.conf'
+
+    def __init__(self, testenv, o5gs_epc):
+        super().__init__(log.C_RUN, 'open5gs-upfd')
+        self.testenv = testenv
+        self.o5gs_epc = o5gs_epc
+        self._run_node = o5gs_epc.run_node()
+        self.run_dir = None
+        self.config_file = None
+        self.log_file = None
+        self.process = None
+        self.rem_host = None
+        self.remote_inst = None
+        self.remote_config_file = None
+        self.remote_log_file = None
+
+    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))
+
+    def start(self):
+        self.log('Starting %s' % Open5gsUPF.BINFILE)
+        if self._run_node.is_local():
+            self.start_locally()
+        else:
+            self.start_remotely()
+
+    def start_remotely(self):
+        remote_lib = self.remote_inst.child('lib')
+        remote_binary = self.remote_inst.child('bin', Open5gsUPF.BINFILE)
+
+        # setting capabilities will later disable use of LD_LIBRARY_PATH from ELF loader -> modify RPATH instead.
+        self.log('Setting RPATH for open5gs-upfd')
+        self.rem_host.change_elf_rpath(remote_binary, remote_lib)
+        # open5gs-upfd requires CAP_NET_ADMIN to create tunnel devices: ioctl(TUNSETIFF):
+        self.log('Applying CAP_NET_ADMIN capability to open5gs-upfd')
+        self.rem_host.setcap_net_admin(remote_binary)
+
+        args = (remote_binary, '-c', self.remote_config_file)
+
+        self.process = self.rem_host.RemoteProcess(Open5gsUPF.BINFILE, args)
+        self.testenv.remember_to_stop(self.process)
+        self.process.launch()
+
+    def start_locally(self):
+        binary = self.inst.child('bin', Open5gsUPF.BINFILE)
+        lib = self.inst.child('lib')
+        env = {}
+
+        # setting capabilities will later disable use of LD_LIBRARY_PATH from ELF loader -> modify RPATH instead.
+        self.log('Setting RPATH for open5gs-upfd')
+        # open5gs-upfd 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'))
+        # open5gs-upfd requires CAP_NET_ADMIN to create tunnel devices: ioctl(TUNSETIFF):
+        self.log('Applying CAP_NET_ADMIN capability to open5gs-upfd')
+        util.setcap_net_admin(binary, self.run_dir.new_dir('setcap_net_admin'))
+
+        args = (binary, '-c', os.path.abspath(self.config_file))
+
+        self.process = process.Process(self.name(), self.run_dir, args, env=env)
+        self.testenv.remember_to_stop(self.process)
+        self.process.launch()
+
+    def configure(self, values):
+        self.run_dir = util.Dir(self.o5gs_epc.run_dir.new_dir(self.name()))
+        self.inst = util.Dir(os.path.abspath(self.testenv.suite().trial().get_inst('open5gs', self._run_node.run_label())))
+        if not os.path.isdir(self.inst.child('lib')):
+            raise log.Error('No lib/ in', self.inst)
+        if not self.inst.isfile('bin', Open5gsUPF.BINFILE):
+            raise log.Error('No %s binary in' % Open5gsUPF.BINFILE, self.inst)
+
+        self.config_file = self.run_dir.child(Open5gsUPF.CFGFILE)
+        self.log_file = self.run_dir.child(Open5gsUPF.LOGFILE)
+
+        if not self._run_node.is_local():
+            self.rem_host = remote.RemoteHost(self.run_dir, self._run_node.ssh_user(), self._run_node.ssh_addr())
+            remote_prefix_dir = util.Dir(Open5gsUPF.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(Open5gsUPF.BINFILE))
+
+            self.remote_config_file = remote_run_dir.child(Open5gsUPF.CFGFILE)
+            self.remote_log_file = remote_run_dir.child(Open5gsUPF.LOGFILE)
+
+        logfile = self.log_file if self._run_node.is_local() else self.remote_log_file
+        inst_prefix = str(self.inst) if self._run_node.is_local() else str(self.remote_inst)
+        config.overlay(values, dict(upf=dict(log_filename=logfile,
+                                             inst_prefix=inst_prefix)))
+
+        self.dbg('OPEN5GS-UPF CONFIG:\n' + pprint.pformat(values))
+
+        with open(self.config_file, 'w') as f:
+            r = template.render(Open5gsUPF.CFGFILE, values)
+            self.dbg(r)
+            f.write(r)
+
+        if not self._run_node.is_local():
+            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.recreate_remote_dir(remote_run_dir)
+            self.rem_host.scp('scp-cfg-to-remote', self.config_file, self.remote_config_file)
+
+    def running(self):
+        return not self.process.terminated()
+
+# vim: expandtab tabstop=4 shiftwidth=4
diff --git a/src/osmo_gsm_tester/templates/open5gs-freediameter.conf.tmpl b/src/osmo_gsm_tester/templates/open5gs-freediameter.conf.tmpl
new file mode 100644
index 0000000..e3e0f5c
--- /dev/null
+++ b/src/osmo_gsm_tester/templates/open5gs-freediameter.conf.tmpl
@@ -0,0 +1,266 @@
+# This is a sample configuration file for freeDiameter daemon.
+
+# Most of the options can be omitted, as they default to reasonable values.
+# Only TLS-related options must be configured properly in usual setups.
+
+# It is possible to use "include" keyword to import additional files
+# e.g.: include "/etc/freeDiameter.d/*.conf"
+# This is exactly equivalent as copy & paste the content of the included file(s)
+# where the "include" keyword is found.
+
+
+##############################################################
+##  Peer identity and realm
+
+# The Diameter Identity of this daemon.
+# This must be a valid FQDN that resolves to the local host.
+# Default: hostname's FQDN
+#Identity = "aaa.koganei.freediameter.net";
+Identity = "${diameter.identity}.localdomain";
+
+# The Diameter Realm of this daemon.
+# Default: the domain part of Identity (after the first dot).
+#Realm = "koganei.freediameter.net";
+Realm = "localdomain";
+
+##############################################################
+##  Transport protocol configuration
+
+# The port this peer is listening on for incoming connections (TCP and SCTP).
+# Default: 3868. Use 0 to disable.
+Port = ${diameter.listen_port};
+
+# The port this peer is listening on for incoming TLS-protected connections (TCP and SCTP).
+# See TLS_old_method for more information about TLS flavours.
+# Note: we use TLS/SCTP instead of DTLS/SCTP at the moment. This will change in future version of freeDiameter.
+# Default: 5868. Use 0 to disable.
+SecPort = 0;
+
+# Use RFC3588 method for TLS protection, where TLS is negociated after CER/CEA exchange is completed
+# on the unsecure connection. The alternative is RFC6733 mechanism, where TLS protects also the
+# CER/CEA exchange on a dedicated secure port.
+# This parameter only affects outgoing connections.
+# The setting can be also defined per-peer (see Peers configuration section).
+# Default: use RFC6733 method with separate port for TLS.
+#TLS_old_method;
+
+# Disable use of TCP protocol (only listen and connect over SCTP)
+# Default : TCP enabled
+#No_TCP;
+
+# Disable use of SCTP protocol (only listen and connect over TCP)
+# Default : SCTP enabled
+#No_SCTP;
+# This option is ignored if freeDiameter is compiled with DISABLE_SCTP option.
+
+# Prefer TCP instead of SCTP for establishing new connections.
+# This setting may be overwritten per peer in peer configuration blocs.
+# Default : SCTP is attempted first.
+#Prefer_TCP;
+
+# Default number of streams per SCTP associations.
+# This setting may be overwritten per peer basis.
+# Default : 30 streams
+#SCTP_streams = 30;
+
+##############################################################
+##  Endpoint configuration
+
+# Disable use of IP addresses (only IPv6)
+# Default : IP enabled
+#No_IP;
+
+# Disable use of IPv6 addresses (only IP)
+# Default : IPv6 enabled
+#No_IPv6;
+
+# Specify local addresses the server must bind to
+# Default : listen on all addresses available.
+#ListenOn = "202.249.37.5";
+#ListenOn = "2001:200:903:2::202:1";
+#ListenOn = "fe80::21c:5ff:fe98:7d62%eth0";
+ListenOn = "${diameter.listen_address}";
+
+
+##############################################################
+##  Server configuration
+
+# How many Diameter peers are allowed to be connecting at the same time ?
+# This parameter limits the number of incoming connections from the time
+# the connection is accepted until the first CER is received.
+# Default: 5 unidentified clients in paralel.
+#ThreadsPerServer = 5;
+
+##############################################################
+##  TLS Configuration
+
+# TLS is managed by the GNUTLS library in the freeDiameter daemon.
+# You may find more information about parameters and special behaviors
+# in the relevant documentation.
+# http://www.gnu.org/software/gnutls/manual/
+
+# Credentials of the local peer
+# The X509 certificate and private key file to use for the local peer.
+# The files must contain PKCS-1 encoded RSA key, in PEM format.
+# (These parameters are passed to gnutls_certificate_set_x509_key_file function)
+# Default : NO DEFAULT
+#TLS_Cred = "<x509 certif file.PEM>" , "<x509 private key file.PEM>";
+#TLS_Cred = "/etc/ssl/certs/freeDiameter.pem", "/etc/ssl/private/freeDiameter.key";
+TLS_Cred = "${diameter.inst_prefix}/etc/freeDiameter/${diameter.identity}.cert.pem", "${diameter.inst_prefix}/etc/freeDiameter/${diameter.identity}.key.pem";
+
+# Certificate authority / trust anchors
+# The file containing the list of trusted Certificate Authorities (PEM list)
+# (This parameter is passed to gnutls_certificate_set_x509_trust_file function)
+# The directive can appear several times to specify several files.
+# Default : GNUTLS default behavior
+#TLS_CA = "<file.PEM>";
+TLS_CA = "${diameter.inst_prefix}/etc/freeDiameter/cacert.pem";
+
+# Certificate Revocation List file
+# The information about revoked certificates.
+# The file contains a list of trusted CRLs in PEM format. They should have been verified before.
+# (This parameter is passed to gnutls_certificate_set_x509_crl_file function)
+# Note: openssl CRL format might have interoperability issue with GNUTLS format.
+# Default : GNUTLS default behavior
+#TLS_CRL = "<file.PEM>";
+
+# GNU TLS Priority string
+# This string allows to configure the behavior of GNUTLS key exchanges
+# algorithms. See gnutls_priority_init function documentation for information.
+# You should also refer to the Diameter required TLS support here:
+#   http://tools.ietf.org/html/rfc6733#section-13.1
+# Default : "NORMAL"
+# Example: TLS_Prio = "NONE:+VERS-TLS1.1:+AES-128-CBC:+RSA:+SHA1:+COMP-NULL";
+#TLS_Prio = "NORMAL";
+
+# Diffie-Hellman parameters size
+# Set the number of bits for generated DH parameters
+# Valid value should be 768, 1024, 2048, 3072 or 4096.
+# (This parameter is passed to gnutls_dh_params_generate2 function,
+# it usually should match RSA key size)
+# Default : 1024
+#TLS_DH_Bits = 1024;
+
+# Alternatively, you can specify a file to load the PKCS#3 encoded
+# DH parameters directly from. This accelerates the daemon start
+# but is slightly less secure. If this file is provided, the
+# TLS_DH_Bits parameters has no effect.
+# Default : no default.
+#TLS_DH_File = "<file.PEM>";
+
+
+##############################################################
+##  Timers configuration
+
+# The Tc timer of this peer.
+# It is the delay before a new attempt is made to reconnect a disconnected peer.
+# The value is expressed in seconds. The recommended value is 30 seconds.
+# Default: 30
+#TcTimer = 30;
+
+# The Tw timer of this peer.
+# It is the delay before a watchdog message is sent, as described in RFC 3539.
+# The value is expressed in seconds. The default value is 30 seconds. Value must
+# be greater or equal to 6 seconds. See details in the RFC.
+# Default: 30
+#TwTimer = 30;
+
+##############################################################
+##  Applications configuration
+
+# Disable the relaying of Diameter messages?
+# For messages not handled locally, the default behavior is to forward the
+# message to another peer if any is available, according to the routing
+# algorithms. In addition the "0xffffff" application is advertised in CER/CEA
+# exchanges.
+# Default: Relaying is enabled.
+#NoRelay;
+
+# Number of server threads that can handle incoming messages at the same time.
+# Default: 4
+#AppServThreads = 4;
+
+# Other applications are configured by loaded extensions.
+
+##############################################################
+##  Extensions configuration
+
+#  The freeDiameter framework merely provides support for
+# Diameter Base Protocol. The specific application behaviors,
+# as well as advanced functions, are provided
+# by loadable extensions (plug-ins).
+#  These extensions may in addition receive the name of a
+# configuration file, the format of which is extension-specific.
+#
+# Format:
+#LoadExtension = "/path/to/extension" [ : "/optional/configuration/file" ] ;
+#
+# Examples:
+#LoadExtension = "extensions/sample.fdx";
+#LoadExtension = "extensions/sample.fdx":"conf/sample.conf";
+
+# Extensions are named as follow:
+# dict_* for extensions that add content to the dictionary definitions.
+# dbg_*  for extensions useful only to retrieve more information on the framework execution.
+# acl_*  : Access control list, to control which peers are allowed to connect.
+# rt_*   : routing extensions that impact how messages are forwarded to other peers.
+# app_*  : applications, these extensions usually register callbacks to handle specific messages.
+# test_* : dummy extensions that are useful only in testing environments.
+
+
+# The dbg_msg_dump.fdx extension allows you to tweak the way freeDiameter displays some
+# information about some events. This extension does not actually use a configuration file
+# but receives directly a parameter in the string passed to the extension. Here are some examples:
+## LoadExtension = "dbg_msg_dumps.fdx" : "0x1111"; # Removes all default hooks, very quiet even in case of errors.
+## LoadExtension = "dbg_msg_dumps.fdx" : "0x2222"; # Display all events with few details.
+## LoadExtension = "dbg_msg_dumps.fdx" : "0x0080"; # Dump complete information about sent and received messages.
+# The four digits respectively control: connections, routing decisions, sent/received messages, errors.
+# The values for each digit are:
+#  0 - default - keep the default behavior
+#  1 - quiet   - remove any specific log
+#  2 - compact - display only a summary of the information
+#  4 - full    - display the complete information on a single long line
+#  8 - tree    - display the complete information in an easier to read format spanning several lines.
+
+LoadExtension = "${diameter.inst_prefix}/lib/freeDiameter/dbg_msg_dumps.fdx" : "0x8888";
+LoadExtension = "${diameter.inst_prefix}/lib/freeDiameter/dict_rfc5777.fdx";
+LoadExtension = "${diameter.inst_prefix}/lib/freeDiameter/dict_mip6i.fdx";
+LoadExtension = "${diameter.inst_prefix}/lib/freeDiameter/dict_nasreq.fdx";
+LoadExtension = "${diameter.inst_prefix}/lib/freeDiameter/dict_nas_mipv6.fdx";
+LoadExtension = "${diameter.inst_prefix}/lib/freeDiameter/dict_dcca.fdx";
+LoadExtension = "${diameter.inst_prefix}/lib/freeDiameter/dict_dcca_3gpp.fdx";
+
+
+##############################################################
+##  Peers configuration
+
+#  The local server listens for incoming connections. By default,
+# all unknown connecting peers are rejected. Extensions can override this behavior (e.g., acl_wl).
+#
+#  In addition to incoming connections, the local peer can
+# be configured to establish and maintain connections to some
+# Diameter nodes and allow connections from these nodes.
+#  This is achieved with the ConnectPeer directive described below.
+#
+# Note that the configured Diameter Identity MUST match
+# the information received inside CEA, or the connection will be aborted.
+#
+# Format:
+#ConnectPeer = "diameterid" [ { parameter1; parameter2; ...} ] ;
+# Parameters that can be specified in the peer's parameter list:
+#  No_TCP; No_SCTP; No_IP; No_IPv6; Prefer_TCP; TLS_old_method;
+#  No_TLS;       # assume transparent security instead of TLS. DTLS is not supported yet (will change in future versions).
+#  Port = 5868;  # The port to connect to
+#  TcTimer = 30;
+#  TwTimer = 30;
+#  ConnectTo = "202.249.37.5";
+#  ConnectTo = "2001:200:903:2::202:1";
+#  TLS_Prio = "NORMAL";
+#  Realm = "realm.net"; # Reject the peer if it does not advertise this realm.
+# Examples:
+#ConnectPeer = "aaa.wide.ad.jp";
+#ConnectPeer = "old.diameter.serv" { TcTimer = 60; TLS_old_method; No_SCTP; Port=3868; } ;
+ConnectPeer = "${diameter.connect_name}.localdomain" { ConnectTo = "${diameter.connect_address}"; Port = ${diameter.connect_port}; No_TLS; };
+
+
+##############################################################
diff --git a/src/osmo_gsm_tester/templates/open5gs-hssd.yaml.tmpl b/src/osmo_gsm_tester/templates/open5gs-hssd.yaml.tmpl
new file mode 100644
index 0000000..830e8cd
--- /dev/null
+++ b/src/osmo_gsm_tester/templates/open5gs-hssd.yaml.tmpl
@@ -0,0 +1,85 @@
+db_uri: ${epc.db_uri}
+
+#
+# logger:
+#
+#  o Set OGS_LOG_INFO to all domain level
+#   - If `level` is omitted, the default level is OGS_LOG_INFO)
+#   - If `domain` is omitted, the all domain level is set from 'level'
+#    (Nothing is needed)
+#
+#  o Set OGS_LOG_ERROR to all domain level
+#   - `level` can be set with none, fatal, error, warn, info, debug, trace
+#    level: error
+#
+#  o Set OGS_LOG_DEBUG to mme/emm domain level
+#    level: debug
+#    domain: mme,emm
+#
+#  o Set OGS_LOG_TRACE to all domain level
+#    level: trace
+#    domain: core,fd,hss,event,mem,sock
+#
+logger:
+    file: ${hss.log_filename}
+    level: debug
+
+hss:
+    freeDiameter: ${hss.diameter_filename}
+
+#
+# parameter:
+#
+#  o Number of output streams per SCTP associations.
+#      sctp_streams: 30
+#
+#  o Disable use of IPv4 addresses (only IPv6)
+#      no_ipv4: true
+#
+#  o Disable use of IPv6 addresses (only IPv4)
+#      no_ipv6: true
+#
+#  o Prefer IPv4 instead of IPv6 for estabishing new GTP connections.
+#      prefer_ipv4: true
+#
+#  o Enable Multicast traffic to the UE
+#      multicast: true
+#
+#  o Disable Stateless Address Autoconfiguration for IPv6
+#      no_slaac: true
+#
+parameter:
+
+#
+# max:
+#
+# o Maximum Number of UE per AMF/MME
+#    ue: 1024
+# o Maximum Number of gNB/eNB per AMF/MME
+#    gnb: 32
+#
+max:
+
+#
+# pool:
+#
+# o The default memory pool size was set assuming 1024 UEs.
+#   To connect more UEs, you need to increase the size further.
+#
+#   - Pool-size 128         => 65536 Number
+#   - Pool-size 256         => 16384 Number
+#   - Pool-size 512         => 4096 Number
+#   - Pool-size 1024        => 1024 Number
+#   - Pool-size 2048        => 512 Number
+#   - Pool-size 8192        => 128 Number
+#   - Pool-size 1024*1024   => 8 Number
+#
+#    128:  65536
+#    256:  16384
+#    512:  4096
+#    1024: 1024
+#    2048: 512
+#    8192: 128
+#    big:  8
+#
+pool:
diff --git a/src/osmo_gsm_tester/templates/open5gs-mmed.yaml.tmpl b/src/osmo_gsm_tester/templates/open5gs-mmed.yaml.tmpl
new file mode 100644
index 0000000..9aec578
--- /dev/null
+++ b/src/osmo_gsm_tester/templates/open5gs-mmed.yaml.tmpl
@@ -0,0 +1,245 @@
+#
+# logger:
+#
+#  o Set OGS_LOG_INFO to all domain level
+#   - If `level` is omitted, the default level is OGS_LOG_INFO)
+#   - If `domain` is omitted, the all domain level is set from 'level'
+#    (Nothing is needed)
+#
+#  o Set OGS_LOG_ERROR to all domain level
+#   - `level` can be set with none, fatal, error, warn, info, debug, trace
+#    level: error
+#
+#  o Set OGS_LOG_DEBUG to mme/emm domain level
+#    level: debug
+#    domain: mme,emm
+#
+#  o Set OGS_LOG_TRACE to all domain level
+#    level: trace
+#    domain: core,s1ap,nas,fd,gtp,mme,emm,esm,event,tlv,mem,sock
+#
+logger:
+    file: ${mme.log_filename}
+    level: debug
+
+mme:
+    freeDiameter: ${mme.diameter_filename}
+    s1ap:
+      - addr: ${epc.run_addr}
+    gtpc:
+      - addr: ${epc.run_addr}
+    gummei:
+      plmn_id:
+        mcc: ${epc.mcc}
+        mnc: ${epc.mnc}
+      mme_gid: 2
+      mme_code: 1
+    tai:
+      plmn_id:
+        mcc: ${epc.mcc}
+        mnc: ${epc.mnc}
+      tac: 7
+    security:
+        integrity_order : [ EIA2, EIA1, EIA0 ]
+        ciphering_order : [ EEA0, EEA1, EEA2 ]
+    network_name:
+        full: Open5GS
+    mme_name: open5gs-mme0
+
+#
+# sgwc:
+#
+# <GTP-C Client>
+#
+#  o Specify SGW addresses the GTP-C must connect to
+#
+#  o One SGW is defined.
+#    If prefer_ipv4 is not true, [fe80::2%lo] is selected.
+#    gtpc:
+#      addr:
+#        - 127.0.0.3
+#        - fe80::2%lo
+#
+#  o Two SGW are defined. MME selects SGW with round-robin manner per UE
+#    gtpc:
+#      - addr: 127.0.0.3
+#      - addr: fe80::2%lo
+#
+#  o Three SGW are defined. MME selects SGW with round-robin manner per UE
+#    gtpc:
+#      - addr
+#        - 127.0.0.3
+#        - fe80::2%lo
+#      - addr
+#        - 127.0.0.22
+#        - fe80::12%lo
+#      - name: sgw3.open5gs.org
+#
+# <SGW Selection Mode>
+#
+# o Round-Robin
+#
+#   gtpc:
+#     addr: 127.0.0.3
+#     addr: 127.0.2.2
+#     addr: 127.0.4.2
+#
+# o SGW selection by eNodeB TAC
+#   (either single TAC or multiple TACs, DECIMAL representation)
+#
+#   gtpc:
+#     - addr: 127.0.0.3
+#       tac: 26000
+#     - addr: 127.0.2.2
+#       tac: [25000, 27000, 28000]
+#
+# o SGW selection by e_cell_id(28bit)
+#   (either single or multiple e_cell_id, HEX representation)
+#
+#   gtpc:
+#     - addr: 127.0.0.3
+#       e_cell_id: abcde01
+#     - addr: 127.0.2.2
+#       e_cell_id: [12345, a9413, 98765]
+#
+sgwc:
+    gtpc:
+      - addr: ${epc.run_addr}
+        port: 2125
+
+#
+# smf:
+#
+#  <GTP-C Client>
+#
+#  o By default, the SMF uses the first SMF node.
+#    - To use a different APN for each SMF, specify gtpc.apn as the APN name.
+#    - If the HSS uses WebUI to set the SMF IP for each UE,
+#      you can use a specific SMF node for each UE.
+#
+#  o Two SMF are defined. 127.0.0.4:2123 is used.
+#    [fe80::3%lo]:2123 is ignored.
+#    gtpc:
+#      - addr: 127.0.0.4
+#      - addr: fe80::3%lo
+#
+#  o One SMF is defined. if prefer_ipv4 is not true,
+#    [fe80::3%lo] is selected.
+#    gtpc:
+#      - addr:
+#        - 127.0.0.4
+#        - fe80::3%lo
+#
+#  o Two SMF are defined with a different APN.
+#    - Note that if SMF IP for UE is configured in HSS,
+#      the following configurion for this UE is ignored.
+#    gtpc:
+#      - addr: 127.0.0.4
+#        apn: internet
+#      - addr: 127.0.0.5
+#        apn: volte
+#
+#  o If APN is omitted, the default APN uses the first SMF node.
+#    gtpc:
+#      - addr: 127.0.0.4
+#      - addr: 127.0.0.5
+#        apn: volte
+smf:
+    gtpc:
+      - addr: ${epc.run_addr}
+        port: 2124
+
+#
+# parameter:
+#
+#  o Number of output streams per SCTP associations.
+#      sctp_streams: 30
+#
+#  o Disable use of IPv4 addresses (only IPv6)
+#      no_ipv4: true
+#
+#  o Disable use of IPv6 addresses (only IPv4)
+#      no_ipv6: true
+#
+#  o Prefer IPv4 instead of IPv6 for estabishing new GTP connections.
+#      prefer_ipv4: true
+#
+#  o Enable Multicast traffic to the UE
+#      multicast: true
+#
+#  o Disable Stateless Address Autoconfiguration for IPv6
+#      no_slaac: true
+#
+#  o Use OAI UE
+#    - Remove HashMME in Security-mode command message
+#    - Use the length 1 of EPS network feature support in Attach accept message
+#      use_openair: true
+#
+parameter:
+
+#
+# max:
+#
+# o Maximum Number of UE per AMF/MME
+#    ue: 1024
+# o Maximum Number of gNB/eNB per AMF/MME
+#    gnb: 32
+#
+max:
+
+#
+# pool:
+#
+# o The default memory pool size was set assuming 1024 UEs.
+#   To connect more UEs, you need to increase the size further.
+#
+#   - Pool-size 128         => 65536 Number
+#   - Pool-size 256         => 16384 Number
+#   - Pool-size 512         => 4096 Number
+#   - Pool-size 1024        => 1024 Number
+#   - Pool-size 2048        => 512 Number
+#   - Pool-size 8192        => 128 Number
+#   - Pool-size 1024*1024   => 8 Number
+#
+#    128:  65536
+#    256:  16384
+#    512:  4096
+#    1024: 1024
+#    2048: 512
+#    8192: 128
+#    big:  8
+#
+pool:
+
+#
+# sctp:
+#
+#  o heartbit_interval : 5000 (5secs)
+#  o rto_initial : 3000 (3secs)
+#  o rto_min : 1000 (1sec)
+#  o rto_max : 5000 (5secs)
+#  o max_num_of_ostreams : 30
+#  o max_num_of_istreams : 65535
+#  o max_attempts : 4
+#  o max_initial_timeout : 8000(8secs)
+#  o usrsctp_udp_port : 9899
+#
+sctp:
+
+#
+# time:
+#
+#  o Message Wait Duration (Default : 10,000 ms = 10 seconds)
+#
+#  o Message Wait Duration (3000 ms)
+#    message:
+#        duration: 3000
+#
+#  o Handover Wait Duration (Default : 300 ms)
+#    Time to wait for MME to send UEContextReleaseCommand
+#    to the source eNB after receiving HandoverNotify
+#
+#  o Handover Wait Duration (500ms)
+#    handover:
+#        duration: 500
+time:
diff --git a/src/osmo_gsm_tester/templates/open5gs-pcrfd.yaml.tmpl b/src/osmo_gsm_tester/templates/open5gs-pcrfd.yaml.tmpl
new file mode 100644
index 0000000..d61d8ef
--- /dev/null
+++ b/src/osmo_gsm_tester/templates/open5gs-pcrfd.yaml.tmpl
@@ -0,0 +1,87 @@
+db_uri: ${epc.db_uri}
+
+#
+# logger:
+#
+#  o Set OGS_LOG_INFO to all domain level
+#   - If `level` is omitted, the default level is OGS_LOG_INFO)
+#   - If `domain` is omitted, the all domain level is set from 'level'
+#    (Nothing is needed)
+#
+#  o Set OGS_LOG_ERROR to all domain level
+#   - `level` can be set with none, fatal, error, warn, info, debug, trace
+#    level: error
+#
+#  o Set OGS_LOG_DEBUG to mme/emm domain level
+#    level: debug
+#    domain: mme,emm
+#
+#  o Set OGS_LOG_TRACE to all domain level
+#    level: trace
+#    domain: core,fd,pcrf,event,mem,sock
+logger:
+    file: ${pcrf.log_filename}
+
+pcrf:
+    freeDiameter: ${pcrf.diameter_filename}
+
+#
+# parameter:
+#
+#  o Number of output streams per SCTP associations.
+#      sctp_streams: 30
+#
+#  o Disable use of IPv4 addresses (only IPv6)
+#      no_ipv4: true
+#
+#  o Disable use of IPv6 addresses (only IPv4)
+#      no_ipv6: true
+#
+#  o Prefer IPv4 instead of IPv6 for estabishing new GTP connections.
+#      prefer_ipv4: true
+#
+#  o Enable Multicast traffic to the UE
+#      multicast: true
+#
+#  o Disable Stateless Address Autoconfiguration for IPv6
+#      no_slaac: true
+#
+#  o Legacy support for pre-release LTE 11 devices to do calling
+#    - Replace IPv4/v6 local addr field in AAR Media-Subcomponent AVP by any
+#      no_ipv4v6_local_addr_in_packet_filter: true
+#
+parameter:
+
+#
+# max:
+#
+# o Maximum Number of UE per AMF/MME
+#    ue: 1024
+# o Maximum Number of gNB/eNB per AMF/MME
+#    gnb: 32
+#
+max:
+
+#
+# pool:
+#
+# o The default memory pool size was set assuming 1024 UEs.
+#   To connect more UEs, you need to increase the size further.
+#
+#   - Pool-size 128         => 65536 Number
+#   - Pool-size 256         => 16384 Number
+#   - Pool-size 512         => 4096 Number
+#   - Pool-size 1024        => 1024 Number
+#   - Pool-size 2048        => 512 Number
+#   - Pool-size 8192        => 128 Number
+#   - Pool-size 1024*1024   => 8 Number
+#
+#    128:  65536
+#    256:  16384
+#    512:  4096
+#    1024: 1024
+#    2048: 512
+#    8192: 128
+#    big:  8
+#
+pool:
diff --git a/src/osmo_gsm_tester/templates/open5gs-sgwcd.yaml.tmpl b/src/osmo_gsm_tester/templates/open5gs-sgwcd.yaml.tmpl
new file mode 100644
index 0000000..8d2bd1a
--- /dev/null
+++ b/src/osmo_gsm_tester/templates/open5gs-sgwcd.yaml.tmpl
@@ -0,0 +1,183 @@
+#
+# logger:
+#
+#  o Set OGS_LOG_INFO to all domain level
+#   - If `level` is omitted, the default level is OGS_LOG_INFO)
+#   - If `domain` is omitted, the all domain level is set from 'level'
+#    (Nothing is needed)
+#
+#  o Set OGS_LOG_ERROR to all domain level
+#   - `level` can be set with none, fatal, error, warn, info, debug, trace
+#    level: error
+#
+#  o Set OGS_LOG_DEBUG to mme/emm domain level
+#    level: debug
+#    domain: mme,emm
+#
+#  o Set OGS_LOG_TRACE to all domain level
+#    level: trace
+#    domain: core,pfcp,gtp,sgwc,event,tlv,mem,sock
+#
+logger:
+    file: ${sgwc.log_filename}
+    level: debug
+
+#
+# sgwc:
+#
+#  <GTP-C Server>
+#
+#  o GTP-C Server(127.0.0.3:2123, [fe80::2%lo]:2123)
+#    gtpc:
+#      addr:
+#        - 127.0.0.3
+#        - fe80::2%lo
+#
+#  o On SGW, Same Configuration(127.0.0.3:2123,
+#  [fe80::2%lo]:2123) as below.
+#    gtpc:
+#      - addr: 127.0.0.3
+#      - addr: fe80::2%lo
+#
+#  <PFCP Server>
+#
+#  o PFCP Server(127.0.0.3:8805, ::1:8805)
+#    pfcp:
+#      - addr: 127.0.0.3
+#      - addr: ::1
+#
+#  o PFCP-U Server(127.0.0.1:2152, [::1]:2152)
+#    pfcp:
+#      name: localhost
+#
+sgwc:
+    gtpc:
+      - addr: ${epc.run_addr}
+        port: 2125
+    pfcp:
+      - addr: ${epc.run_addr}
+        port: 8805
+
+#
+# sgwu:
+#
+#  <PFCP Client>>
+#
+#  o PFCP Client(127.0.0.6:8805)
+#
+#    pfcp:
+#      addr: 127.0.0.6
+#
+#  <SGWU_SELECTION_MODE - EPC only>
+#
+# o Round-Robin
+#   (note that round robin can be disabled for a particular node
+#     by setting flag 'rr' to 0)
+#
+#  sgwu:
+#    pfcp:
+#      - addr: 127.0.0.6
+#      - addr: 127.0.0.12
+#        rr: 0
+#      - addr: 127.0.0.18
+#
+# o SGWU selection by eNodeB TAC
+#   (either single TAC or multiple TACs, DECIMAL representation)
+#
+#  sgwu:
+#    pfcp:
+#      - addr: 127.0.0.6
+#        tac: 1
+#      - addr: 127.0.0.12
+#        tac: [3,5,8]
+#
+# o SGWU selection by UE's APN (either single APN or multiple APNs)
+#
+#  sgwu:
+#    pfcp:
+#      - addr: 127.0.0.6
+#        apn: ims
+#      - addr: 127.0.0.12
+#        apn: [internet, web]
+#
+# o SGWU selection by CellID(e_cell_id: 28bit)
+#   (either single e_cell_id or multiple e_cell_id, HEX representation)
+#
+#  sgwu:
+#    pfcp:
+#      - addr: 127.0.0.6
+#        e_cell_id: 463
+#      - addr: 127.0.0.12
+#        e_cell_id: [123456789, 9413]
+#
+sgwu:
+    pfcp:
+      - addr: ${epc.run_addr}
+        port: 8806
+
+#
+# parameter:
+#
+#  o Number of output streams per SCTP associations.
+#      sctp_streams: 30
+#
+#  o Disable use of IPv4 addresses (only IPv6)
+#      no_ipv4: true
+#
+#  o Disable use of IPv6 addresses (only IPv4)
+#      no_ipv6: true
+#
+#  o Prefer IPv4 instead of IPv6 for estabishing new GTP connections.
+#      prefer_ipv4: true
+#
+#  o Enable Multicast traffic to the UE
+#      multicast: true
+#
+#  o Disable Stateless Address Autoconfiguration for IPv6
+#      no_slaac: true
+#
+parameter:
+
+#
+# max:
+#
+# o Maximum Number of UE per AMF/MME
+#    ue: 1024
+# o Maximum Number of gNB/eNB per AMF/MME
+#    gnb: 32
+#
+max:
+
+#
+# pool:
+#
+# o The default memory pool size was set assuming 1024 UEs.
+#   To connect more UEs, you need to increase the size further.
+#
+#   - Pool-size 128         => 65536 Number
+#   - Pool-size 256         => 16384 Number
+#   - Pool-size 512         => 4096 Number
+#   - Pool-size 1024        => 1024 Number
+#   - Pool-size 2048        => 512 Number
+#   - Pool-size 8192        => 128 Number
+#   - Pool-size 1024*1024   => 8 Number
+#
+#    128:  65536
+#    256:  16384
+#    512:  4096
+#    1024: 1024
+#    2048: 512
+#    8192: 128
+#    big:  8
+#
+pool:
+
+#
+# time:
+#
+#  o Message Wait Duration (Default : 10,000 ms = 10 seconds)
+#
+#  o Message Wait Duration (3000 ms)
+#    message:
+#        duration: 3000
+time:
diff --git a/src/osmo_gsm_tester/templates/open5gs-sgwud.yaml.tmpl b/src/osmo_gsm_tester/templates/open5gs-sgwud.yaml.tmpl
new file mode 100644
index 0000000..27dd55c
--- /dev/null
+++ b/src/osmo_gsm_tester/templates/open5gs-sgwud.yaml.tmpl
@@ -0,0 +1,138 @@
+#
+# logger:
+#
+#  o Set OGS_LOG_INFO to all domain level
+#   - If `level` is omitted, the default level is OGS_LOG_INFO)
+#   - If `domain` is omitted, the all domain level is set from 'level'
+#    (Nothing is needed)
+#
+#  o Set OGS_LOG_ERROR to all domain level
+#   - `level` can be set with none, fatal, error, warn, info, debug, trace
+#    level: error
+#
+#  o Set OGS_LOG_DEBUG to mme/emm domain level
+#    level: debug
+#    domain: mme,emm
+#
+#  o Set OGS_LOG_TRACE to all domain level
+#    level: trace
+#    domain: core,pfcp,gtp,sgwu,event,tlv,mem,sock
+#
+logger:
+    file: ${sgwu.log_filename}
+    level: debug
+
+#
+# sgwu:
+#
+#  <PFCP Server>
+#
+#  o PFCP Server(127.0.0.6:8805, ::1:8805)
+#    pfcp:
+#      - addr: 127.0.0.6
+#      - addr: ::1
+#
+#  o PFCP-U Server(127.0.0.1:2152, [::1]:2152)
+#    pfcp:
+#      - name: localhost
+#
+#  <GTP-U Server>
+#
+#  o GTP-U Server(127.0.0.6:2152, [::1]:2152)
+#    gtpu:
+#      - addr: 127.0.0.6
+#      - addr: ::1
+#
+#  o GTP-U Server(127.0.0.1:2152, [::1]:2152)
+#    gtpu:
+#      - name: localhost
+#
+sgwu:
+    pfcp:
+      - addr: ${epc.run_addr}
+        port: 8806
+    gtpu:
+      - addr: ${epc.run_addr}
+        port: 2152
+
+#
+# sgwc:
+#
+#  <PFCP Client>>
+#
+#  o PFCP Client(127.0.0.6:8805)
+#
+#    pfcp:
+#      addr: 127.0.0.6
+#
+sgwc:
+    pfcp:
+      - addr: ${epc.run_addr}
+        port: 8805
+
+#
+# parameter:
+#
+#  o Number of output streams per SCTP associations.
+#      sctp_streams: 30
+#
+#  o Disable use of IPv4 addresses (only IPv6)
+#      no_ipv4: true
+#
+#  o Disable use of IPv6 addresses (only IPv4)
+#      no_ipv6: true
+#
+#  o Prefer IPv4 instead of IPv6 for estabishing new GTP connections.
+#      prefer_ipv4: true
+#
+#  o Enable Multicast traffic to the UE
+#      multicast: true
+#
+#  o Disable Stateless Address Autoconfiguration for IPv6
+#      no_slaac: true
+#
+parameter:
+
+#
+# max:
+#
+# o Maximum Number of UE per AMF/MME
+#    ue: 1024
+# o Maximum Number of gNB/eNB per AMF/MME
+#    gnb: 32
+#
+max:
+
+#
+# pool:
+#
+# o The default memory pool size was set assuming 1024 UEs.
+#   To connect more UEs, you need to increase the size further.
+#
+#   - Pool-size 128         => 65536 Number
+#   - Pool-size 256         => 16384 Number
+#   - Pool-size 512         => 4096 Number
+#   - Pool-size 1024        => 1024 Number
+#   - Pool-size 2048        => 512 Number
+#   - Pool-size 8192        => 128 Number
+#   - Pool-size 1024*1024   => 8 Number
+#
+#    128:  65536
+#    256:  16384
+#    512:  4096
+#    1024: 1024
+#    2048: 512
+#    8192: 128
+#    big:  8
+#
+pool:
+
+#
+# time:
+#
+#  o Message Wait Duration (Default : 10,000 ms = 10 seconds)
+#
+#  o Message Wait Duration (3000 ms)
+#    message:
+#        duration: 3000
+time:
diff --git a/src/osmo_gsm_tester/templates/open5gs-smfd.yaml.tmpl b/src/osmo_gsm_tester/templates/open5gs-smfd.yaml.tmpl
new file mode 100644
index 0000000..0e01337
--- /dev/null
+++ b/src/osmo_gsm_tester/templates/open5gs-smfd.yaml.tmpl
@@ -0,0 +1,506 @@
+#
+# logger:
+#
+#  o Set OGS_LOG_INFO to all domain level
+#   - If `level` is omitted, the default level is OGS_LOG_INFO)
+#   - If `domain` is omitted, the all domain level is set from 'level'
+#    (Nothing is needed)
+#
+#  o Set OGS_LOG_ERROR to all domain level
+#   - `level` can be set with none, fatal, error, warn, info, debug, trace
+#    level: error
+#
+#  o Set OGS_LOG_DEBUG to mme/emm domain level
+#    level: debug
+#    domain: mme,emm
+#
+#  o Set OGS_LOG_TRACE to all domain level
+#    level: trace
+#    domain: core,pfcp,fd,pfcp,gtp,smf,event,tlv,mem,sock
+#
+logger:
+    file: ${smf.log_filename}
+#
+# smf:
+#
+#  <SBI Server>
+#
+#  o SBI Server(http://<all address available>:80)
+#    sbi:
+#
+#  o SBI Server(http://<any address>:80)
+#    sbi:
+#      - addr:
+#          - 0.0.0.0
+#          - ::0
+#        port: 7777
+#
+#  o SBI Server(https://<all address avaiable>:443)
+#    sbi:
+#        tls:
+#          key: smf.key
+#          pem: smf.pem
+#
+#  o SBI Server(https://127.0.0.4:443, http://[::1]:80)
+#    sbi:
+#      - addr: 127.0.0.4
+#        tls:
+#          key: smf.key
+#          pem: smf.pem
+#      - addr: ::1
+#
+#  o SBI Server(http://smf.open5gs.org:80)
+#    sbi:
+#      name: smf.open5gs.org
+#
+#  o SBI Server(http://127.0.0.4:7777)
+#    sbi:
+#      - addr: 127.0.0.4
+#        port: 7777
+#
+#  o SBI Server(http://<eth0 IP address>:80)
+#    sbi:
+#      dev: eth0
+#
+#  <PFCP Server>
+#
+#  o PFCP Server(127.0.0.4:8805, ::1:8805)
+#    pfcp:
+#      - addr: 127.0.0.4
+#      - addr: ::1
+#
+#  o PFCP-U Server(127.0.0.1:2152, [::1]:2152)
+#    pfcp:
+#      name: localhost
+#
+#  <GTP-C Server>
+#
+#  o GTP-C Server(127.0.0.4:2123, [fe80::3%lo]:2123)
+#    gtpc:
+#      addr:
+#        - 127.0.0.4
+#        - fe80::3%lo
+#
+#  o On SMF, Same configuration
+#    (127.0.0.4:2123, [fe80::3%lo]:2123).
+#    gtpc:
+#      - addr: 127.0.0.4
+#      - addr: fe80::3%lo
+#
+#  <GTP-U Server>>
+#
+#  o GTP-U Server(127.0.0.4:2152, [::1]:2152)
+#    gtpu:
+#      - addr: 127.0.0.4
+#      - addr: ::1
+#
+#  o GTP-U Server(127.0.0.1:2152, [::1]:2152)
+#    gtpu:
+#      name: localhost
+#
+#  <Subnet for UE Pool>
+#
+#  o IPv4 Pool
+#    subnet:
+#      addr: 10.45.0.1/16
+#
+#  o IPv4/IPv6 Pool
+#    subnet:
+#      - addr: 10.45.0.1/16
+#      - addr: 2001:230:cafe::1/48
+#
+#
+#  o Specific DNN/APN(e.g 'ims') uses 10.46.0.1/16, 2001:230:babe::1/48
+#
+#    subnet:
+#      - addr: 10.45.0.1/16
+#        dnn: internet
+#      - addr: 2001:230:cafe::1/48
+#        dnn: internet
+#      - addr: 10.46.0.1/16
+#        dnn: ims
+#      - addr: 2001:230:babe::1/48
+#        dnn: ims
+#
+#  o Pool Range Sample
+#    subnet:
+#      - addr: 10.45.0.1/24
+#        range: 10.45.0.100-10.45.0.200
+#
+#    subnet:
+#      - addr: 10.45.0.1/24
+#        range:
+#          - 10.45.0.5-10.45.0.50
+#          - 10.45.0.100-
+#
+#    subnet:
+#      - addr: 10.45.0.1/24
+#        range:
+#          - -10.45.0.200
+#          - 10.45.0.210-10.45.0.220
+#
+#    subnet:
+#      - addr: 10.45.0.1/16
+#        range:
+#          - 10.45.0.100-10.45.0.200
+#          - 10.45.1.100-10.45.1.200
+#      - addr: 2001:230:cafe::1/48
+#        range:
+#          - 2001:230:cafe:a0::0-2001:230:cafe:b0::0
+#          - 2001:230:cafe:c0::0-2001:230:cafe:d0::0
+#
+#  <Domain Name Server>
+#
+#  o Primary/Secondary can be configured. Others are ignored.
+#
+#    dns:
+#      - 8.8.8.8
+#      - 8.8.4.4
+#      - 2001:4860:4860::8888
+#      - 2001:4860:4860::8844
+#
+#  <MTU Size>
+#
+#  o Provisioning a limit on the size of the packets sent by the MS
+#    to avoid packet fragmentation in the backbone network
+#    between the MS and the GGSN/PGW and/or across the (S)Gi reference point)
+#    when some of the backbone links does not support
+#    packets larger then 1500 octets
+#
+#  <P-CSCF>
+#
+#  o Proxy Call Session Control Function
+#
+#    p-cscf:
+#      - 127.0.0.1
+#      - ::1
+#
+#  <SMF Selection - 5G Core only>
+#  1. SMF sends SmfInfo(S-NSSAI, DNN, TAI) to the NRF
+#  2. NRF responds to AMF with SmfInfo during NF-Discovery.
+#  3. AMF selects SMF based on S-NSSAI, DNN and TAI in SmfInfo.
+#
+#  Note that if there is no SmfInfo, any AMF can select this SMF.
+#
+#  o S-NSSAI[SST:1] and DNN[internet] - At least 1 DNN is required in S-NSSAI
+#    info:
+#      - s_nssai:
+#          - sst: 1
+#            dnn:
+#              - internet
+#
+#  o S-NSSAI[SST:1 SD:009000] and DNN[internet or ims]
+#    info:
+#      - s_nssai:
+#          - sst: 1
+#            sd: 009000
+#            dnn:
+#              - internet
+#              - ims
+#
+#  o S-NSSAI[SST:1] and DNN[internet] and TAI[PLMN-ID:90170 TAC:1]
+#    info:
+#      - s_nssai:
+#          - sst: 1
+#            dnn:
+#              - internet
+#        tai:
+#          - plmn_id:
+#              mcc: 901
+#              mnc: 70
+#            tac: 1
+#
+#  o If any of conditions below are met:
+#   - S-NSSAI[SST:1] and DNN[internet] and TAI[PLMN-ID:90170 TAC:1-9]
+#   - S-NSSAI[SST:2 SD:000080] and DNN[internet or ims]
+#   - S-NSSAI[SST:4] and DNN[internet] and TAI[PLMN-ID:90170 TAC:10-20,30-40]
+#
+#    info:
+#      - s_nssai:
+#          - sst: 1
+#            dnn:
+#              - internet
+#        tai:
+#          - plmn_id:
+#              mcc: 901
+#              mnc: 70
+#            range:
+#              - 1-9
+#      - s_nssai:
+#          - sst: 2
+#            sd: 000080
+#            dnn:
+#              - internet
+#              - ims
+#      - s_nssai:
+#          - sst: 4
+#            dnn:
+#              - internet
+#        tai:
+#          - plmn_id:
+#              mcc: 901
+#              mnc: 70
+#            range:
+#              - 10-20
+#              - 30-40
+#
+#  o Complex Example
+#    info:
+#      - s_nssai:
+#          - sst: 1
+#            dnn:
+#              - internet
+#          - sst: 1
+#            sd: 000080
+#            dnn:
+#              - internet
+#              - ims
+#          - sst: 1
+#            sd: 009000
+#            dnn:
+#              [internet, ims]
+#          - sst: 2
+#            dnn:
+#              - internet
+#          - sst: 3
+#            sd: 123456
+#            dnn:
+#              - internet
+#        tai:
+#          - plmn_id:
+#              mcc: 901
+#              mnc: 70
+#            tac: [1, 2, 3]
+#          - plmn_id:
+#              mcc: 901
+#              mnc: 70
+#            tac: 4
+#          - plmn_id:
+#              mcc: 901
+#              mnc: 70
+#            tac:
+#              - 5
+#              - 6
+#          - plmn_id:
+#              mcc: 901
+#              mnc: 70
+#            range:
+#              - 100-200
+#              - 300-400
+#          - plmn_id:
+#              mcc: 901
+#              mnc: 70
+#            range:
+#              - 500-600
+#              - 700-800
+#              - 900-1000
+#      - s_nssai:
+#          - sst: 4
+#            dnn:
+#              - internet
+#        tai:
+#          - plmn_id:
+#              mcc: 901
+#              mnc: 70
+#            tac: 99
+#
+
+smf:
+    sbi:
+      - addr: ${epc.run_addr}
+        port: 7777
+    pfcp:
+      - addr: ${epc.run_addr}
+        port: 8808
+    gtpc:
+      - addr: ${epc.run_addr}
+        port: 2124
+    gtpu:
+      - addr: ${epc.run_addr}
+        port: 2153
+    subnet:
+      - addr: 10.45.0.1/16
+      - addr: 2001:230:cafe::1/48
+    dns:
+      - 8.8.8.8
+      - 8.8.4.4
+      - 2001:4860:4860::8888
+      - 2001:4860:4860::8844
+    mtu: 1400
+    freeDiameter: ${smf.diameter_filename}
+
+#
+# nrf:
+#
+#  <SBI Client>>
+#
+#  o SBI Client(http://127.0.0.1:7777)
+#    sbi:
+#      addr: 127.0.0.10
+#      port: 7777
+#
+#  o SBI Client(https://127.0.0.10:443, http://nrf.open5gs.org:80)
+#    sbi:
+#      - addr: 127.0.0.10
+#        tls:
+#          key: nrf.key
+#          pem: nrf.pem
+#      - name: nrf.open5gs.org
+#
+#  o SBI Client(http://[fe80::1%lo]:80)
+#    If prefer_ipv4 is true, http://127.0.0.10:80 is selected.
+#
+#    sbi:
+#      addr:
+#        - 127.0.0.10
+#        - fe80::1%lo
+#
+nrf:
+    sbi:
+      - addr:
+          - ${epc.run_addr}
+        port: 7777
+
+#
+# upf:
+#
+#  <PFCP Client>>
+#
+#  o PFCP Client(127.0.0.7:8805)
+#
+#    pfcp:
+#      addr: 127.0.0.7
+#
+#  <UPF Selection>
+#
+#  o Round-Robin
+#    (note that round robin can be disabled for a particular node
+#     by setting flag 'rr' to 0)
+#
+#  upf:
+#    pfcp:
+#      - addr: 127.0.0.7
+#      - addr: 127.0.0.12
+#        rr: 0
+#      - addr: 127.0.0.19
+#
+#  o UPF selection by eNodeB TAC
+#    (either single TAC or multiple TACs, DECIMAL representation)
+#
+#  upf:
+#    pfcp:
+#      - addr: 127.0.0.7
+#        tac: 1
+#      - addr: 127.0.0.12
+#        tac: [3,5,8]
+#
+#  o UPF selection by UE's DNN/APN (either single DNN/APN or multiple DNNs/APNs)
+#
+#  upf:
+#    pfcp:
+#      - addr: 127.0.0.7
+#        dnn: ims
+#      - addr: 127.0.0.12
+#        dnn: [internet, web]
+#
+#  o UPF selection by CellID(e_cell_id: 28bit, nr_cell_id: 36bit)
+#    (either single enb_id or multiple enb_ids, HEX representation)
+#
+#  upf:
+#    pfcp:
+#      - addr: 127.0.0.7
+#        e_cell_id: 463
+#      - addr: 127.0.0.12
+#        nr_cell_id: [123456789, 9413]
+#
+upf:
+    pfcp:
+      - addr: ${epc.run_addr}
+        port: 8807
+
+#
+# parameter:
+#
+#  o Number of output streams per SCTP associations.
+#      sctp_streams: 30
+#
+#  o Disable use of IPv4 addresses (only IPv6)
+#      no_ipv4: true
+#
+#  o Disable use of IPv6 addresses (only IPv4)
+#      no_ipv6: true
+#
+#  o Prefer IPv4 instead of IPv6 for estabishing new GTP connections.
+#      prefer_ipv4: true
+#
+#  o Enable Multicast traffic to the UE
+#      multicast: true
+#
+#  o Disable Stateless Address Autoconfiguration for IPv6
+#      no_slaac: true
+#
+parameter:
+
+#
+# max:
+#
+# o Maximum Number of UE per AMF/MME
+#    ue: 1024
+# o Maximum Number of gNB/eNB per AMF/MME
+#    gnb: 32
+#
+max:
+
+#
+# pool:
+#
+# o The default memory pool size was set assuming 1024 UEs.
+#   To connect more UEs, you need to increase the size further.
+#
+#   - Pool-size 128         => 65536 Number
+#   - Pool-size 256         => 16384 Number
+#   - Pool-size 512         => 4096 Number
+#   - Pool-size 1024        => 1024 Number
+#   - Pool-size 2048        => 512 Number
+#   - Pool-size 8192        => 128 Number
+#   - Pool-size 1024*1024   => 8 Number
+#
+#    128:  65536
+#    256:  16384
+#    512:  4096
+#    1024: 1024
+#    2048: 512
+#    8192: 128
+#    big:  8
+#
+pool:
+
+#
+# time:
+#
+#  o NF Instance Heartbeat (Default : 0)
+#    NFs will not send heart-beat timer in NFProfile
+#    NRF will send heart-beat timer in NFProfile
+#
+#  o NF Instance Heartbeat (20 seconds)
+#    NFs will send heart-beat timer (20 seconds) in NFProfile
+#    NRF can change heart-beat timer in NFProfile
+#
+#    nf_instance:
+#      heartbeat: 20
+#
+#  o Message Wait Duration (Default : 10,000 ms = 10 seconds)
+#
+#  o Message Wait Duration (3000 ms)
+#    message:
+#        duration: 3000
+#
+#  o Handover Wait Duration (Default : 300 ms)
+#    Time to wait for SMF to send
+#    PFCP Session Modification Request(Remove Indirect Tunnel) to the UPF
+#    after sending Nsmf_PDUSession_UpdateSMContext Response(hoState:COMPLETED)
+#
+#  o Handover Wait Duration (500ms)
+#    handover:
+#        duration: 500
+time:
diff --git a/src/osmo_gsm_tester/templates/open5gs-upfd.yaml.tmpl b/src/osmo_gsm_tester/templates/open5gs-upfd.yaml.tmpl
new file mode 100644
index 0000000..4f7a25d
--- /dev/null
+++ b/src/osmo_gsm_tester/templates/open5gs-upfd.yaml.tmpl
@@ -0,0 +1,197 @@
+#
+# logger:
+#
+#  o Set OGS_LOG_INFO to all domain level
+#   - If `level` is omitted, the default level is OGS_LOG_INFO)
+#   - If `domain` is omitted, the all domain level is set from 'level'
+#    (Nothing is needed)
+#
+#  o Set OGS_LOG_ERROR to all domain level
+#   - `level` can be set with none, fatal, error, warn, info, debug, trace
+#    level: error
+#
+#  o Set OGS_LOG_DEBUG to mme/emm domain level
+#    level: debug
+#    domain: mme,emm
+#
+#  o Set OGS_LOG_TRACE to all domain level
+#    level: trace
+#    domain: core,pfcp,gtp,upf,event,tlv,mem,sock
+#
+logger:
+    file: ${upf.log_filename}
+
+#
+# upf:
+#
+#  <PFCP Server>
+#
+#  o PFCP Server(127.0.0.7:8805, ::1:8805)
+#    pfcp:
+#      - addr: 127.0.0.7
+#      - addr: ::1
+#
+#  o PFCP-U Server(127.0.0.1:2152, [::1]:2152)
+#    pfcp:
+#      name: localhost
+#
+#  <GTP-U Server>>
+#
+#  o GTP-U Server(127.0.0.7:2152, [::1]:2152)
+#    gtpu:
+#      - addr: 127.0.0.7
+#      - addr: ::1
+#
+#  o GTP-U Server(127.0.0.1:2152, [::1]:2152)
+#    gtpu:
+#      name: localhost
+#
+#  <Subnet for UE network>
+#
+#  Note that you need to setup your UE network using TUN device.
+#  (ogstun, ogstun2, ogstunX, ..)
+#
+#  o IPv4 Pool
+#    $ sudo ip addr add 10.45.0.1/16 dev ogstun
+#
+#    subnet:
+#      addr: 10.45.0.1/16
+#
+#  o IPv4/IPv6 Pool
+#    $ sudo ip addr add 10.45.0.1/16 dev ogstun
+#    $ sudo ip addr add 2001:230:cafe::1/48 dev ogstun
+#
+#    subnet:
+#      - addr: 10.45.0.1/16
+#      - addr: 2001:230:cafe::1/48
+#
+#
+#  o Specific DNN/APN(e.g 'ims') uses 10.46.0.1/16, 2001:230:babe::1/48
+#    All other APNs use 10.45.0.1/16, 2001:230:cafe::1/48
+#    $ sudo ip addr add 10.45.0.1/16 dev ogstun
+#    $ sudo ip addr add 10.46.0.1/16 dev ogstun
+#    $ sudo ip addr add 2001:230:cafe::1/48 dev ogstun
+#    $ sudo ip addr add 2001:230:babe::1/48 dev ogstun
+#
+#    subnet:
+#      - addr: 10.45.0.1/16
+#        dnn: internet
+#      - addr: 2001:230:cafe::1/48
+#        dnn: internet
+#      - addr: 10.46.0.1/16
+#        dnn: ims
+#      - addr: 2001:230:babe::1/48
+#        dnn: ims
+#
+#  o Multiple Devices (default: ogstun)
+#    $ sudo ip addr add 10.45.0.1/16 dev ogstun
+#    $ sudo ip addr add 2001:230:cafe::1/48 dev ogstun2
+#    $ sudo ip addr add 10.46.0.1/16 dev ogstun3
+#    $ sudo ip addr add 2001:230:babe::1/48 dev ogstun3
+#
+#    subnet:
+#      - addr: 10.45.0.1/16
+#        dnn: internet
+#      - addr: 2001:230:cafe::1/48
+#        dnn: internet
+#        dev: ogstun2
+#      - addr: 10.46.0.1/16
+#        dnn: ims
+#        dev: ogstun3
+#      - addr: 2001:230:babe::1/48
+#        dnn: ims
+#        dev: ogstun3
+#
+upf:
+    pfcp:
+      - addr: ${epc.run_addr}
+        port: 8807
+    gtpu:
+      - addr: ${epc.run_addr}
+        port: 2154
+    subnet:
+      - addr: 10.45.0.1/16
+      - addr: 2001:230:cafe::1/48
+
+#
+# smf:
+#
+#  <PFCP Client>>
+#
+#  o PFCP Client(127.0.0.3:8805)
+#
+#    pfcp:
+#      addr: 127.0.0.3
+#
+smf:
+    pfcp:
+      - addr: ${epc.run_addr}
+        port: 8808
+
+#
+# parameter:
+#
+#  o Number of output streams per SCTP associations.
+#      sctp_streams: 30
+#
+#  o Disable use of IPv4 addresses (only IPv6)
+#      no_ipv4: true
+#
+#  o Disable use of IPv6 addresses (only IPv4)
+#      no_ipv6: true
+#
+#  o Prefer IPv4 instead of IPv6 for estabishing new GTP connections.
+#      prefer_ipv4: true
+#
+#  o Enable Multicast traffic to the UE
+#      multicast: true
+#
+#  o Disable Stateless Address Autoconfiguration for IPv6
+#      no_slaac: true
+#
+#
+parameter:
+
+#
+# max:
+#
+# o Maximum Number of UE per AMF/MME
+#    ue: 1024
+# o Maximum Number of gNB/eNB per AMF/MME
+#    gnb: 32
+#
+max:
+
+#
+# pool:
+#
+# o The default memory pool size was set assuming 1024 UEs.
+#   To connect more UEs, you need to increase the size further.
+#
+#   - Pool-size 128         => 65536 Number
+#   - Pool-size 256         => 16384 Number
+#   - Pool-size 512         => 4096 Number
+#   - Pool-size 1024        => 1024 Number
+#   - Pool-size 2048        => 512 Number
+#   - Pool-size 8192        => 128 Number
+#   - Pool-size 1024*1024   => 8 Number
+#
+#    128:  65536
+#    256:  16384
+#    512:  4096
+#    1024: 1024
+#    2048: 512
+#    8192: 128
+#    big:  8
+#
+pool:
+
+#
+# time:
+#
+#  o Message Wait Duration (Default : 10,000 ms = 10 seconds)
+#
+#  o Message Wait Duration (3000 ms)
+#    message:
+#        duration: 3000
+time:
diff --git a/sysmocom/defaults.conf b/sysmocom/defaults.conf
index 69d9ed9..eeb2b83 100644
--- a/sysmocom/defaults.conf
+++ b/sysmocom/defaults.conf
@@ -101,6 +101,9 @@
   enable_pcap: false
   log_all_level: warning
 
+open5gsepc:
+  db_host: "localhost"
+
 amarisoft:
   license_server_addr: 0.0.0.0
 
diff --git a/sysmocom/scenarios/cfg-epc-db at .conf b/sysmocom/scenarios/cfg-epc-db at .conf
new file mode 100644
index 0000000..e6c79c4
--- /dev/null
+++ b/sysmocom/scenarios/cfg-epc-db at .conf
@@ -0,0 +1,3 @@
+config:
+  epc:
+    db_host: ${param1}

-- 
To view, visit https://gerrit.osmocom.org/c/osmo-gsm-tester/+/23476
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings

Gerrit-Project: osmo-gsm-tester
Gerrit-Branch: master
Gerrit-Change-Id: Iff8b88dc22d8f156572839abb48b0c1377c55e33
Gerrit-Change-Number: 23476
Gerrit-PatchSet: 2
Gerrit-Owner: pespin <pespin at sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: pespin <pespin at sysmocom.de>
Gerrit-MessageType: merged
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20210331/2a1011e7/attachment.htm>


More information about the gerrit-log mailing list