pespin has submitted this change. ( 
https://gerrit.osmocom.org/c/osmocom-bb/+/32596 )

Change subject: layer23: Introduce apn_fsm
......................................................................

layer23: Introduce apn_fsm

This allows further control on the state of the APNs, as well as
a step further towards administering them through VTY.

Change-Id: I2cc732dfb020d31ab89025e7e22276b819dcb24a
---
M src/host/layer23/include/osmocom/bb/common/Makefile.am
M src/host/layer23/include/osmocom/bb/common/apn.h
A src/host/layer23/include/osmocom/bb/common/apn_fsm.h
M src/host/layer23/include/osmocom/bb/common/settings.h
M src/host/layer23/src/common/Makefile.am
M src/host/layer23/src/common/apn.c
A src/host/layer23/src/common/apn_fsm.c
M src/host/layer23/src/common/settings.c
M src/host/layer23/src/modem/gmm.c
M src/host/layer23/src/modem/sm.c
M src/host/layer23/src/modem/vty.c
11 files changed, 346 insertions(+), 15 deletions(-)

Approvals:
  laforge: Looks good to me, but someone else must approve
  Jenkins Builder: Verified
  osmith: Looks good to me, but someone else must approve
  pespin: Looks good to me, approved




diff --git a/src/host/layer23/include/osmocom/bb/common/Makefile.am 
b/src/host/layer23/include/osmocom/bb/common/Makefile.am
index 28caf78..7c0fa97 100644
--- a/src/host/layer23/include/osmocom/bb/common/Makefile.am
+++ b/src/host/layer23/include/osmocom/bb/common/Makefile.am
@@ -1,5 +1,6 @@
 noinst_HEADERS = \
        apn.h \
+       apn_fsm.h \
        l1ctl.h \
        l1l2_interface.h \
        l23_app.h \
diff --git a/src/host/layer23/include/osmocom/bb/common/apn.h 
b/src/host/layer23/include/osmocom/bb/common/apn.h
index 94784ef..538b31d 100644
--- a/src/host/layer23/include/osmocom/bb/common/apn.h
+++ b/src/host/layer23/include/osmocom/bb/common/apn.h
@@ -20,6 +20,7 @@
 #include <osmocom/core/linuxlist.h>
 #include <osmocom/core/select.h>
 #include <osmocom/core/tun.h>
+#include <osmocom/bb/common/apn_fsm.h>

 struct osmocom_ms;

@@ -50,6 +51,7 @@
                bool tx_gpdu_seq;
        } cfg;
        struct osmo_tundev *tun;
+       struct apn_fsm_ctx fsm;
 };

 struct osmobb_apn *apn_alloc(struct osmocom_ms *ms, const char *name);
diff --git a/src/host/layer23/include/osmocom/bb/common/apn_fsm.h 
b/src/host/layer23/include/osmocom/bb/common/apn_fsm.h
new file mode 100644
index 0000000..890267c
--- /dev/null
+++ b/src/host/layer23/include/osmocom/bb/common/apn_fsm.h
@@ -0,0 +1,29 @@
+#pragma once
+
+#include <osmocom/core/fsm.h>
+
+struct osmobb_apn;
+
+enum apn_fsm_states {
+       APN_ST_DISABLED,
+       APN_ST_INACTIVE,
+       APN_ST_ACTIVATING,
+       APN_ST_ACTIVE,
+};
+
+enum apn_fsm_events {
+       APN_EV_GPRS_ALLOWED,    /* data: bool *allowed */
+       APN_EV_GMM_ATTACHED,
+       APN_EV_GMM_DETACHED,
+       APN_EV_RX_SM_ACT_PDP_CTX_REJ, /* data: enum gsm48_gsm_cause *cause */
+       APN_EV_RX_SM_ACT_PDP_CTX_ACC,
+       APN_EV_RX_SM_DEACT_PDP_CTX_ACC,
+};
+
+struct apn_fsm_ctx {
+       struct osmo_fsm_inst *fi;
+       struct osmobb_apn *apn;
+};
+
+int apn_fsm_ctx_init(struct apn_fsm_ctx *ctx, struct osmobb_apn *apn);
+void apn_fsm_ctx_release(struct apn_fsm_ctx *ctx);
diff --git a/src/host/layer23/include/osmocom/bb/common/settings.h 
b/src/host/layer23/include/osmocom/bb/common/settings.h
index 88ce280..38faa4b 100644
--- a/src/host/layer23/include/osmocom/bb/common/settings.h
+++ b/src/host/layer23/include/osmocom/bb/common/settings.h
@@ -205,6 +205,7 @@
 int gprs_settings_init(struct osmocom_ms *ms);
 int gprs_settings_fi(struct osmocom_ms *ms);
 struct osmobb_apn *ms_find_apn_by_name(struct osmocom_ms *ms, const char 
*apn_name);
+int ms_dispatch_all_apn(struct osmocom_ms *ms, uint32_t event, void *data);

 extern char *layer2_socket_path;

diff --git a/src/host/layer23/src/common/Makefile.am 
b/src/host/layer23/src/common/Makefile.am
index 60af3b8..8f5aeba 100644
--- a/src/host/layer23/src/common/Makefile.am
+++ b/src/host/layer23/src/common/Makefile.am
@@ -16,6 +16,7 @@
 noinst_LIBRARIES = liblayer23.a
 liblayer23_a_SOURCES = \
        apn.c \
+       apn_fsm.c \
        gps.c \
        l1ctl.c \
        l1l2_interface.c \
diff --git a/src/host/layer23/src/common/apn.c 
b/src/host/layer23/src/common/apn.c
index 636246a..b9b032c 100644
--- a/src/host/layer23/src/common/apn.c
+++ b/src/host/layer23/src/common/apn.c
@@ -35,25 +35,33 @@
        if (!apn)
                return NULL;

+       if (apn_fsm_ctx_init(&apn->fsm, apn) != 0)
+               goto ret_free;
+
        talloc_set_name(apn, "apn_%s", name);
        apn->cfg.name = talloc_strdup(apn, name);
        apn->cfg.shutdown = true;
        apn->cfg.tx_gpdu_seq = true;

        apn->tun = osmo_tundev_alloc(apn, name);
-       if (!apn->tun) {
-               talloc_free(apn);
-               return NULL;
-       }
+       if (!apn->tun)
+               goto ret_free_fsm;
        osmo_tundev_set_priv_data(apn->tun, apn);

        apn->ms = ms;
        llist_add_tail(&apn->list, &ms->gprs.apn_list);
        return apn;
+
+ret_free_fsm:
+       apn_fsm_ctx_release(&apn->fsm);
+ret_free:
+       talloc_free(apn);
+       return NULL;
 }

 void apn_free(struct osmobb_apn *apn)
 {
+       apn_fsm_ctx_release(&apn->fsm);
        llist_del(&apn->list);
        osmo_tundev_free(apn->tun);
        talloc_free(apn);
diff --git a/src/host/layer23/src/common/apn_fsm.c 
b/src/host/layer23/src/common/apn_fsm.c
new file mode 100644
index 0000000..edaac48
--- /dev/null
+++ b/src/host/layer23/src/common/apn_fsm.c
@@ -0,0 +1,245 @@
+/* Lifecycle of an APN */
+/*
+ * (C) 2023 by sysmocom - s.m.f.c. GmbH <i...@sysmocom.de>
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: AGPL-3.0+
+ *
+ * Author: Pau Espin Pedrol <pes...@sysmocom.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <errno.h>
+#include <osmocom/core/tdef.h>
+#include <osmocom/core/utils.h>
+
+#include <osmocom/bb/common/logging.h>
+#include <osmocom/bb/common/apn_fsm.h>
+#include <osmocom/bb/common/apn.h>
+#define X(s) (1 << (s))
+
+static struct osmo_tdef T_defs_apn[] = {
+       { .T=1, .default_val=30, .desc = "Activating timeout (s)" },
+       { 0 } /* empty item at the end */
+};
+
+static const struct osmo_tdef_state_timeout apn_fsm_timeouts[32] = {
+       [APN_ST_DISABLED] = {},
+       [APN_ST_INACTIVE] = {},
+       [APN_ST_ACTIVATING] = { .T=1 },
+       [APN_ST_ACTIVE] = {},
+};
+
+#define apn_fsm_state_chg(fi, NEXT_STATE) \
+       osmo_tdef_fsm_inst_state_chg(fi, NEXT_STATE, apn_fsm_timeouts, 
T_defs_apn, -1)
+
+static void st_apn_disabled_on_enter(struct osmo_fsm_inst *fi, uint32_t 
prev_state)
+{
+       struct apn_fsm_ctx *ctx = (struct apn_fsm_ctx *)fi->priv;
+
+       apn_stop(ctx->apn);
+}
+
+static void st_apn_disabled(struct osmo_fsm_inst *fi, uint32_t event, void 
*data)
+{
+       switch (event) {
+       case APN_EV_GPRS_ALLOWED:
+               if (*((bool *)data) == true)
+                       apn_fsm_state_chg(fi, APN_ST_INACTIVE);
+               break;
+       default:
+               OSMO_ASSERT(0);
+       }
+}
+
+static void st_apn_inactive_on_enter(struct osmo_fsm_inst *fi, uint32_t 
prev_state)
+{
+       struct apn_fsm_ctx *ctx = (struct apn_fsm_ctx *)fi->priv;
+
+       int rc = apn_start(ctx->apn);
+       if (rc < 0)
+               apn_fsm_state_chg(fi, APN_ST_DISABLED);
+
+       /* FIXME: Here once we find a way to store whether the ms object is GMM
+       attached, we can transition directly to ACTIVATING. */
+}
+
+static void st_apn_inactive(struct osmo_fsm_inst *fi, uint32_t event, void 
*data)
+{
+       switch (event) {
+       case APN_EV_GPRS_ALLOWED:
+               if (*((bool *)data) == false)
+                       apn_fsm_state_chg(fi, APN_ST_DISABLED);
+               break;
+       case APN_EV_GMM_ATTACHED:
+               apn_fsm_state_chg(fi, APN_ST_ACTIVATING);
+               break;
+       default:
+               OSMO_ASSERT(0);
+       }
+}
+
+static void st_apn_activating_on_enter(struct osmo_fsm_inst *fi, uint32_t 
prev_state)
+{
+       /* FIXME: We could send SMREG-PDP_ACT.req from here. Right now that's 
done by the app. */
+}
+
+static void st_apn_activating(struct osmo_fsm_inst *fi, uint32_t event, void 
*data)
+{
+       switch (event) {
+       case APN_EV_GPRS_ALLOWED:
+               /* TODO: Tx PDP DEACT ACC */
+               apn_fsm_state_chg(fi, APN_ST_DISABLED);
+               break;
+       case APN_EV_GMM_DETACHED:
+               apn_fsm_state_chg(fi, APN_ST_INACTIVE);
+               break;
+       case APN_EV_RX_SM_ACT_PDP_CTX_REJ:
+               apn_fsm_state_chg(fi, APN_ST_INACTIVE);
+               break;
+       case APN_EV_RX_SM_ACT_PDP_CTX_ACC:
+               apn_fsm_state_chg(fi, APN_ST_ACTIVE);
+               break;
+       default:
+               OSMO_ASSERT(0);
+       }
+}
+
+static void st_apn_active_on_enter(struct osmo_fsm_inst *fi, uint32_t 
prev_state)
+{
+       struct apn_fsm_ctx *ctx = (struct apn_fsm_ctx *)fi->priv;
+       struct osmo_netdev *netdev;
+
+       netdev = osmo_tundev_get_netdev(ctx->apn->tun);
+       osmo_netdev_ifupdown(netdev, true);
+}
+
+static void st_apn_active(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+       switch (event) {
+       case APN_EV_GPRS_ALLOWED:
+               /* TODO: Tx PDP DEACT ACC */
+               apn_fsm_state_chg(fi, APN_ST_DISABLED);
+               break;
+       case APN_EV_GMM_DETACHED:
+               apn_fsm_state_chg(fi, APN_ST_INACTIVE);
+               break;
+       case APN_EV_RX_SM_DEACT_PDP_CTX_ACC:
+               apn_fsm_state_chg(fi, APN_ST_INACTIVE);
+               break;
+       default:
+               OSMO_ASSERT(0);
+       }
+}
+
+static int apn_fsm_timer_cb(struct osmo_fsm_inst *fi)
+{
+       switch (fi->T) {
+       case 1:
+               apn_fsm_state_chg(fi, APN_ST_INACTIVE);
+               break;
+       default:
+               OSMO_ASSERT(0);
+       }
+       return 0;
+}
+
+static struct osmo_fsm_state apn_fsm_states[] = {
+       [APN_ST_DISABLED] = {
+               .in_event_mask =
+                       X(APN_EV_GPRS_ALLOWED),
+               .out_state_mask =
+                       X(APN_ST_INACTIVE),
+               .name = "DISABLED",
+               .onenter = st_apn_disabled_on_enter,
+               .action = st_apn_disabled,
+       },
+       [APN_ST_INACTIVE] = {
+               .in_event_mask =
+                       X(APN_EV_GPRS_ALLOWED) |
+                       X(APN_EV_GMM_ATTACHED),
+               .out_state_mask =
+                       X(APN_ST_ACTIVATING),
+               .name = "INACTIVE",
+               .onenter = st_apn_inactive_on_enter,
+               .action = st_apn_inactive,
+       },
+       [APN_ST_ACTIVATING] = {
+               .in_event_mask =
+                       X(APN_EV_GPRS_ALLOWED) |
+                       X(APN_EV_GMM_DETACHED) |
+                       X(APN_EV_RX_SM_ACT_PDP_CTX_REJ) |
+                       X(APN_EV_RX_SM_ACT_PDP_CTX_ACC),
+               .out_state_mask =
+                       X(APN_ST_DISABLED) |
+                       X(APN_ST_INACTIVE) |
+                       X(APN_ST_ACTIVE),
+               .name = "ACTIVATING",
+               .onenter = st_apn_activating_on_enter,
+               .action = st_apn_activating,
+       },
+       [APN_ST_ACTIVE] = {
+               .in_event_mask =
+                       X(APN_EV_GPRS_ALLOWED) |
+                       X(APN_EV_GMM_DETACHED)|
+                       X(APN_EV_RX_SM_DEACT_PDP_CTX_ACC),
+               .out_state_mask =
+                       X(APN_ST_DISABLED) |
+                       X(APN_ST_INACTIVE),
+               .name = "ACTIVE",
+               .onenter = st_apn_active_on_enter,
+               .action = st_apn_active,
+       },
+};
+
+const struct value_string apn_fsm_event_names[] = {
+       { APN_EV_GPRS_ALLOWED,          "GPRS_ALLOWED" },
+       { APN_EV_GMM_ATTACHED,          "GMM_ATTACHED" },
+       { APN_EV_GMM_DETACHED,          "GMM_DETACHED" },
+       { APN_EV_RX_SM_ACT_PDP_CTX_REJ, "ACT_PDP_CTX_REJ" },
+       { APN_EV_RX_SM_ACT_PDP_CTX_ACC, "ACT_PDP_CTX_ACC" },
+       { APN_EV_RX_SM_DEACT_PDP_CTX_ACC, "DEACT_PDP_CTX_ACC" },
+       { 0, NULL }
+};
+
+struct osmo_fsm apn_fsm = {
+       .name = "APN",
+       .states = apn_fsm_states,
+       .num_states = ARRAY_SIZE(apn_fsm_states),
+       .timer_cb = apn_fsm_timer_cb,
+       .event_names = apn_fsm_event_names,
+       .log_subsys = DTUN,
+       .timer_cb = apn_fsm_timer_cb,
+};
+
+int apn_fsm_ctx_init(struct apn_fsm_ctx *ctx, struct osmobb_apn *apn)
+{
+       ctx->apn = apn;
+       ctx->fi = osmo_fsm_inst_alloc(&apn_fsm, apn, ctx, LOGL_INFO, NULL);
+       if (!ctx->fi)
+               return -ENODATA;
+
+       return 0;
+}
+
+void apn_fsm_ctx_release(struct apn_fsm_ctx *ctx)
+{
+       osmo_fsm_inst_free(ctx->fi);
+}
+
+static __attribute__((constructor)) void apn_fsm_init(void)
+{
+       OSMO_ASSERT(osmo_fsm_register(&apn_fsm) == 0);
+       osmo_tdefs_reset(T_defs_apn);
+}
diff --git a/src/host/layer23/src/common/settings.c 
b/src/host/layer23/src/common/settings.c
index 565da96..986d551 100644
--- a/src/host/layer23/src/common/settings.c
+++ b/src/host/layer23/src/common/settings.c
@@ -252,3 +252,14 @@
        }
        return NULL;
 }
+
+int ms_dispatch_all_apn(struct osmocom_ms *ms, uint32_t event, void *data)
+{
+       struct gprs_settings *set = &ms->gprs;
+       int rc = 0;
+       struct osmobb_apn *apn;
+
+       llist_for_each_entry(apn, &set->apn_list, list)
+               rc |= osmo_fsm_inst_dispatch(apn->fsm.fi, event, data);
+       return rc;
+}
diff --git a/src/host/layer23/src/modem/gmm.c b/src/host/layer23/src/modem/gmm.c
index f3a936a..b95f15a 100644
--- a/src/host/layer23/src/modem/gmm.c
+++ b/src/host/layer23/src/modem/gmm.c
@@ -64,10 +64,12 @@
                                ms->subscr.ptmsi = 
gmm_prim->gmmreg.attach_cnf.acc.allocated_ptmsi;
                                app_data.modem_state = MODEM_ST_ATTACHED;
                                /* Activate APN if not yet already: */
-                               apn = 
llist_first_entry_or_null(&ms->gprs.apn_list, struct osmobb_apn, list);
-                               if (!apn || apn->cfg.shutdown)
-                                       break;
-                               modem_sm_smreg_pdp_act_req(ms, apn);
+                               llist_for_each_entry(apn, &ms->gprs.apn_list, 
list) {
+                                       if (apn->fsm.fi->state != 
APN_ST_INACTIVE)
+                                               continue;
+                                       osmo_fsm_inst_dispatch(apn->fsm.fi, 
APN_EV_GMM_ATTACHED, NULL);
+                                       modem_sm_smreg_pdp_act_req(ms, apn);
+                               }
                        } else {
                                uint8_t cause = 
gmm_prim->gmmreg.attach_cnf.rej.cause;
                                LOGP(DGMM, LOGL_ERROR, "%s(): Rx %s: Attach 
rejected, cause=%u (%s)\n",
@@ -94,6 +96,9 @@
                        break;
                case OSMO_PRIM(OSMO_GPRS_GMM_GMMREG_DETACH, PRIM_OP_CONFIRM):
                case OSMO_PRIM(OSMO_GPRS_GMM_GMMREG_DETACH, PRIM_OP_INDICATION):
+                               LOGP(DGMM, LOGL_NOTICE, "%s(): Rx %s\n", 
__func__, pdu_name);
+                               ms_dispatch_all_apn(ms, APN_EV_GMM_DETACHED, 
NULL);
+                       break;
                default:
                        LOGP(DGMM, LOGL_ERROR, "%s(): Rx %s UNIMPLEMENTED\n", 
__func__, pdu_name);
                        break;
diff --git a/src/host/layer23/src/modem/sm.c b/src/host/layer23/src/modem/sm.c
index 56b223f..aac2b52 100644
--- a/src/host/layer23/src/modem/sm.c
+++ b/src/host/layer23/src/modem/sm.c
@@ -50,24 +50,28 @@
 static int modem_sm_handle_pdp_act_cnf(struct osmocom_ms *ms, struct 
osmo_gprs_sm_prim *sm_prim)
 {
        const char *pdu_name = osmo_gprs_sm_prim_name(sm_prim);
-       struct osmobb_apn *apn = llist_first_entry_or_null(&ms->gprs.apn_list, 
struct osmobb_apn, list);
+       struct osmobb_apn *apn = NULL, *apn_it;
        struct osmo_netdev *netdev;
        char buf_addr[INET6_ADDRSTRLEN];
        char buf_addr2[INET6_ADDRSTRLEN];
        int rc;

+       llist_for_each_entry(apn_it, &ms->gprs.apn_list, list) {
+               if (apn_it->fsm.fi->state == APN_ST_ACTIVATING) {
+                       apn = apn_it;
+                       break;
+               }
+       }
+
        if (!apn) {
                LOGP(DSM, LOGL_ERROR, "Rx %s but have no APN!\n", pdu_name);
                return -ENOENT;
        }
-       if (apn->cfg.shutdown) {
-               LOGPAPN(LOGL_ERROR, apn, "Rx %s but APN is administratively 
shutdown!\n", pdu_name);
-               return -ENOENT;
-       }

        if (!sm_prim->smreg.pdp_act_cnf.accepted) {
                LOGPAPN(LOGL_ERROR, apn, "Rx %s: Activate PDP failed! cause 
'%s'\n", pdu_name,
                        get_value_string(gsm48_gsm_cause_names, 
sm_prim->smreg.pdp_act_cnf.rej.cause));
+               osmo_fsm_inst_dispatch(apn->fsm.fi, 
APN_EV_RX_SM_ACT_PDP_CTX_REJ, NULL);
                /* TODO: maybe retry ? */
                return 0;
        }
@@ -117,6 +121,8 @@

        /* TODO: Handle PCO */
        /* TODO: Handle QoS */
+
+       osmo_fsm_inst_dispatch(apn->fsm.fi, APN_EV_RX_SM_ACT_PDP_CTX_ACC, NULL);
        return rc;
 }

diff --git a/src/host/layer23/src/modem/vty.c b/src/host/layer23/src/modem/vty.c
index 33891d4..77f0885 100644
--- a/src/host/layer23/src/modem/vty.c
+++ b/src/host/layer23/src/modem/vty.c
@@ -257,6 +257,7 @@
 {
        struct osmocom_ms *ms = vty->index;
        struct osmobb_apn *apn;
+       bool on = false;

        apn = ms_find_apn_by_name(ms, argv[0]);
        if (!apn) {
@@ -264,6 +265,9 @@
                return CMD_WARNING;
        }

+       /* Disable APN before getting rid of it. */
+       osmo_fsm_inst_dispatch(apn->fsm.fi, APN_EV_GPRS_ALLOWED, &on);
+
        apn_free(apn);

        return CMD_SUCCESS;
@@ -326,9 +330,12 @@
        "Put the APN in administrative shut-down\n")
 {
        struct osmobb_apn *apn = (struct osmobb_apn *) vty->index;
+       bool on = false;
+       int rc;

        if (!apn->cfg.shutdown) {
-               if (apn_stop(apn)) {
+               rc = osmo_fsm_inst_dispatch(apn->fsm.fi, APN_EV_GPRS_ALLOWED, 
&on);
+               if (rc < 0) {
                        vty_out(vty, "%% Failed to Stop APN%s", VTY_NEWLINE);
                        return CMD_WARNING;
                }
@@ -343,13 +350,16 @@
        NO_STR "Remove the APN from administrative shut-down\n")
 {
        struct osmobb_apn *apn = (struct osmobb_apn *) vty->index;
+       bool on = true;
+       int rc;

        if (apn->cfg.shutdown) {
                if (!apn->cfg.dev_name) {
                        vty_out(vty, "%% Failed to start APN, tun-device is not 
configured%s", VTY_NEWLINE);
                        return CMD_WARNING;
                }
-               if (apn_start(apn) < 0) {
+               rc = osmo_fsm_inst_dispatch(apn->fsm.fi, APN_EV_GPRS_ALLOWED, 
&on);
+               if (rc < 0) {
                        vty_out(vty, "%% Failed to start APN, check log for 
details%s", VTY_NEWLINE);
                        return CMD_WARNING;
                }

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

Gerrit-Project: osmocom-bb
Gerrit-Branch: master
Gerrit-Change-Id: I2cc732dfb020d31ab89025e7e22276b819dcb24a
Gerrit-Change-Number: 32596
Gerrit-PatchSet: 2
Gerrit-Owner: pespin <pes...@sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: laforge <lafo...@osmocom.org>
Gerrit-Reviewer: osmith <osm...@sysmocom.de>
Gerrit-Reviewer: pespin <pes...@sysmocom.de>
Gerrit-MessageType: merged

Reply via email to