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