Change in osmo-gsm-tester[master]: Add support to test gprs IPv4 data plane

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/.

Pau Espin Pedrol gerrit-no-reply at lists.osmocom.org
Tue Nov 6 12:07:27 UTC 2018


Pau Espin Pedrol has submitted this change and it was merged. ( https://gerrit.osmocom.org/11462 )

Change subject: Add support to test gprs IPv4 data plane
......................................................................

Add support to test gprs IPv4 data plane

Since the modem iface and the GGSN iface are on the same host/netns,
it's really difficult to conveniently test data plane without getting
routing loops. As a result, either GGSN or modem iface must be moved to
a different namespace. The decision after a few discussions was finally
to move modem interfaces to a different netns.

Expected setup:
* ofono is patched to avoid removing modem if it detects
through udev that its net iface was removed (due to for instance, net
iface being moved to another netns and thus not being reachable anymore
by systemd-udev process running in root netns).
* After ofono is started (and successfully configured all the modems and
detected its net ifaces through syfs/udev), script "modem-netns-setup.py
start" which creates a netns for each modem, naming it after its usb
path ID. net ifaces for that modem are moved into its netns.
* Modem is configured to use 802-3 data format, and as a result the net
iface is configured through DHCP (DHCP req only replied AFTER pdp ctx is
activated!).
* Since osmo-gsm-tester knowns the modem USB path ID (available in
resources.conf), it can run required steps (ifup, DHCP) to configure the
interface. The interface name is provided by ofono to osmo-gsm-tester.
* As a result, any process willing to transmit data through the modem
must be in the modem netns.

Related: OS#2308
Change-Id: Icb06bdfcdd37c797be95ab5addb28da2d9f6681c
---
M example/resources.conf.prod
M example/resources.conf.rnd
M src/osmo_gsm_tester/modem.py
M src/osmo_gsm_tester/process.py
M src/osmo_gsm_tester/suite.py
M suites/gprs/ping.py
A utils/osmo-gsm-tester_netns_exec.sh
7 files changed, 69 insertions(+), 15 deletions(-)

Approvals:
  Jenkins Builder: Verified
  Pau Espin Pedrol: Looks good to me, approved
  Harald Welte: Looks good to me, but someone else must approve



diff --git a/example/resources.conf.prod b/example/resources.conf.prod
index 22134e7..b12a7bc 100644
--- a/example/resources.conf.prod
+++ b/example/resources.conf.prod
@@ -122,14 +122,14 @@
   ki: 'EBAB63D06C3F546A16C977CB40E57C68'
   auth_algo: 'comp128v1'
   ciphers: [a5_0, a5_1]
-  features: ['sms', 'voice', 'ussd', 'gprs', 'sim']
+  features: ['sms', 'voice', 'ussd', 'sim']
 
 - label: sierra_2nd
   path: '/sys/devices/pci0000:00/0000:00:12.2/usb1/1-5/1-5.4/1-5.4.1/1-5.4.1.3'
   ki: 'EBD2B5F6CF3374106D0A66C11F922001'
   auth_algo: 'comp128v1'
   ciphers: [a5_0, a5_1]
-  features: ['sms', 'voice', 'ussd', 'gprs', 'sim']
+  features: ['sms', 'voice', 'ussd', 'sim']
 
 - label: ec20
   path: '/sys/devices/pci0000:00/0000:00:12.2/usb1/1-5/1-5.4/1-5.4.1/1-5.4.1.6'
@@ -143,7 +143,7 @@
   ki: '5752B3F43277C35D2D1D957007DF74E2'
   auth_algo: 'comp128v1'
   ciphers: [a5_0, a5_1]
-  features: ['gprs', 'sim']
+  features: ['sim']
 
 osmocon_phone:
   - serial_device: '/dev/serial/by-id/usb-Silicon_Labs_CP2104_USB_to_UART_Bridge_Controller_00897B41-if00-port0'
diff --git a/example/resources.conf.rnd b/example/resources.conf.rnd
index dbdf3cc..63650e1 100644
--- a/example/resources.conf.rnd
+++ b/example/resources.conf.rnd
@@ -72,14 +72,14 @@
   ki: '80A37E6FDEA931EAC92FFA5F671EFEAD'
   auth_algo: 'comp128v1'
   ciphers: [a5_0, a5_1]
-  features: ['sms', 'voice', 'ussd', 'gprs', 'sim']
+  features: ['sms', 'voice', 'ussd', 'sim']
 
 - label: sierra_2nd
   path: '/sys/devices/pci0000:00/0000:00:12.2/usb1/1-1/1-1.3'
   ki: '00969E283349D354A8239E877F2E0866'
   auth_algo: 'comp128v1'
   ciphers: [a5_0, a5_1]
-  features: ['sms', 'voice', 'ussd', 'gprs', 'sim']
+  features: ['sms', 'voice', 'ussd', 'sim']
 
 - label: ec20
   path: '/sys/devices/pci0000:00/0000:00:12.2/usb1/1-1/1-1.6'
@@ -93,7 +93,7 @@
   ki: '2F70DCA43C45ACB97E947FDD0C7CA30A'
   auth_algo: 'comp128v1'
   ciphers: [a5_0, a5_1]
-  features: ['gprs', 'sim']
+  features: ['sim']
 
 osmocon_phone:
 - serial_device: '/dev/serial/by-id/usb-Silicon_Labs_CP2104_USB_to_UART_Bridge_Controller_0089279D-if00-port0'
diff --git a/src/osmo_gsm_tester/modem.py b/src/osmo_gsm_tester/modem.py
index 21b208c..d35933a 100644
--- a/src/osmo_gsm_tester/modem.py
+++ b/src/osmo_gsm_tester/modem.py
@@ -17,10 +17,11 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-from . import log, util, sms
+from . import log, util, sms, process
 from .event_loop import MainLoop
 
 from pydbus import SystemBus, Variant
+import os
 
 # Required for Gio.Cancellable.
 # See https://lazka.github.io/pgi-docs/Gio-2.0/classes/Cancellable.html#Gio.Cancellable
@@ -323,15 +324,17 @@
     CTX_PROT_IPv6 = 'ipv6'
     CTX_PROT_IPv46 = 'dual'
 
-    def __init__(self, conf):
+    def __init__(self, suite_run, conf):
+        self.suite_run = suite_run
         self.conf = conf
         self.syspath = conf.get('path')
         self.dbuspath = get_dbuspath_from_syspath(self.syspath)
         super().__init__(log.C_TST, self.dbuspath)
-        self.dbg('creating from syspath %s', self.syspath)
+        self.dbg('creating from syspath %s' % self.syspath)
         self.msisdn = None
         self._ki = None
         self._imsi = None
+        self.run_dir = util.Dir(self.suite_run.get_test_run_dir().new_dir(self.name().strip('/')))
         self.sms_received_list = []
         self.dbus = ModemDbusInteraction(self.dbuspath)
         self.register_attempts = 0
@@ -358,6 +361,9 @@
         self.dbus.cleanup()
         self.dbus = None
 
+    def netns(self):
+        return os.path.basename(self.syspath.rstrip('/'))
+
     def properties(self, *args, **kwargs):
         '''Return a dict of properties on this modem. For the actual arguments,
         see ModemDbusInteraction.properties(), which this function calls.  The
@@ -627,6 +633,23 @@
         connmgr.RemoveContext(ctx_id)
         self.log('context deactivated', path=ctx_id)
 
+    def run_netns_wait(self, name, popen_args):
+        proc = process.NetNSProcess(name, self.run_dir.new_dir(name), self.netns(), popen_args,
+                                       env={})
+        process.run_proc_sync(proc)
+
+    def setup_context_data_plane(self, ctx_id):
+        self.dbg('setup_context_data', path=ctx_id)
+        ctx = systembus_get(ctx_id)
+        ctx_settings = ctx.GetProperties().get('Settings', None)
+        if not ctx_settings:
+            raise log.Error('%s no Settings found! No way to get iface!' % ctx_id)
+        iface = ctx_settings.get('Interface', None)
+        if not iface:
+            raise log.Error('%s Settings contains no iface! %r' % (ctx_id, repr(ctx_settings)))
+        self.run_netns_wait('ifup', ('ip', 'link', 'set', 'dev', iface, 'up'))
+        self.run_netns_wait('dhcp', ('udhcpc', '-q', '-i', iface))
+
     def sms_send(self, to_msisdn_or_modem, *tokens):
         if isinstance(to_msisdn_or_modem, Modem):
             to_msisdn = to_msisdn_or_modem.msisdn
diff --git a/src/osmo_gsm_tester/process.py b/src/osmo_gsm_tester/process.py
index fb5c6f6..a845f7f 100644
--- a/src/osmo_gsm_tester/process.py
+++ b/src/osmo_gsm_tester/process.py
@@ -100,6 +100,9 @@
             time.sleep(wait_step)
         return False
 
+    def send_signal(self, sig):
+        os.kill(self.process_obj.pid, sig)
+
     def terminate(self):
         if self.process_obj is None:
             return
@@ -109,21 +112,21 @@
         while True:
             # first try SIGINT to allow stdout+stderr flushing
             self.log('Terminating (SIGINT)')
-            os.kill(self.process_obj.pid, signal.SIGINT)
+            self.send_signal(signal.SIGINT)
             self.killed = signal.SIGINT
             if self._poll_termination():
                 break
 
             # SIGTERM maybe?
             self.log('Terminating (SIGTERM)')
-            self.process_obj.terminate()
+            self.send_signal(signal.SIGTERM)
             self.killed = signal.SIGTERM
             if self._poll_termination():
                 break
 
             # out of patience
             self.log('Terminating (SIGKILL)')
-            self.process_obj.kill()
+            self.send_signal(signal.SIGKILL)
             self.killed = signal.SIGKILL
             break;
 
@@ -236,6 +239,22 @@
                                      ' '.join(self.popen_args))]
         self.dbg(self.popen_args, dir=self.run_dir, conf=self.popen_kwargs)
 
+class NetNSProcess(Process):
+    NETNS_EXEC_BIN = 'osmo-gsm-tester_netns_exec.sh'
+    def __init__(self, name, run_dir, netns, popen_args, **popen_kwargs):
+        super().__init__(name, run_dir, popen_args, **popen_kwargs)
+        self.netns = netns
+
+        self.popen_args = ['sudo', self.NETNS_EXEC_BIN, self.netns] + list(popen_args)
+        self.dbg(self.popen_args, dir=self.run_dir, conf=self.popen_kwargs)
+
+    # HACK: Since we run under sudo, only way to kill root-owned process is to kill as root...
+    # This function is overwritten from Process.
+    def send_signal(self, sig):
+        kill_cmd = ('kill', '-%d' % int(sig), str(self.process_obj.pid))
+        run_local_netns_sync(self.run_dir, self.name()+"-kill", self.netns, kill_cmd)
+
+
 def run_proc_sync(proc):
     try:
         proc.launch()
@@ -252,6 +271,11 @@
     proc = Process(name, run_dir, popen_args)
     run_proc_sync(proc)
 
+def run_local_netns_sync(run_dir, name, netns, popen_args):
+    run_dir =run_dir.new_dir(name)
+    proc = NetNSProcess(name, run_dir, netns, popen_args)
+    run_proc_sync(proc)
+
 def run_remote_sync(run_dir, remote_user, remote_addr, name, popen_args, remote_cwd=None):
     run_dir = run_dir.new_dir(name)
     proc = RemoteProcess(name, run_dir, remote_user, remote_addr, remote_cwd, popen_args)
diff --git a/src/osmo_gsm_tester/suite.py b/src/osmo_gsm_tester/suite.py
index 22a47a2..67ddefd 100644
--- a/src/osmo_gsm_tester/suite.py
+++ b/src/osmo_gsm_tester/suite.py
@@ -328,7 +328,7 @@
     def modem(self, specifics=None):
         conf = self.reserved_resources.get(resource.R_MODEM, specifics=specifics)
         self.dbg('create Modem object', conf=conf)
-        ms = modem.Modem(conf)
+        ms = modem.Modem(self, conf)
         self.register_for_cleanup(ms)
         return ms
 
diff --git a/suites/gprs/ping.py b/suites/gprs/ping.py
index 1647445..9186fe6 100755
--- a/suites/gprs/ping.py
+++ b/suites/gprs/ping.py
@@ -48,8 +48,10 @@
 
 # We need to use inet46 since ofono qmi only uses ipv4v6 eua (OS#2713)
 ctx_id_v4 = ms.activate_context(apn='inet46', protocol=ms.CTX_PROT_IPv4)
-sleep(5)
-# TODO: send ping to server or open TCP conn with a socket in python
+print("Setting up data plan for %r" % repr(ctx_id_v4))
+ms.setup_context_data_plane(ctx_id_v4)
+print("Running 10 ping requests for %r" % repr(ctx_id_v4))
+ms.run_netns_wait('ping', ('ping', '-c', '10', ggsn.addr()))
 ms.deactivate_context(ctx_id_v4)
 
 # We need to use inet46 since ofono qmi only uses ipv4v6 eua (OS#2713)
diff --git a/utils/osmo-gsm-tester_netns_exec.sh b/utils/osmo-gsm-tester_netns_exec.sh
new file mode 100755
index 0000000..336b746
--- /dev/null
+++ b/utils/osmo-gsm-tester_netns_exec.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+netns="$1"
+shift
+#TODO: Later on I may want to call myself with specific ENV and calling sudo in order to run inside the netns but with dropped privileges
+ip netns exec $netns "$@"

-- 
To view, visit https://gerrit.osmocom.org/11462
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings

Gerrit-Project: osmo-gsm-tester
Gerrit-Branch: master
Gerrit-MessageType: merged
Gerrit-Change-Id: Icb06bdfcdd37c797be95ab5addb28da2d9f6681c
Gerrit-Change-Number: 11462
Gerrit-PatchSet: 4
Gerrit-Owner: Pau Espin Pedrol <pespin at sysmocom.de>
Gerrit-Reviewer: Harald Welte <laforge at gnumonks.org>
Gerrit-Reviewer: Jenkins Builder (1000002)
Gerrit-Reviewer: Max <msuraev at sysmocom.de>
Gerrit-Reviewer: Pau Espin Pedrol <pespin at sysmocom.de>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20181106/fb393d84/attachment.htm>


More information about the gerrit-log mailing list