dexter has uploaded this change for review.

View Change

mgw_fsm: add MGW support to osmo-hnbgw

osmo-hnbgw lacks support for an co-located media gateway. This makes it
virtually impossible to isolate the HNB from the core network properly.

Lets add MGCP support to osmo-hnbgw so that it can control a co-located
media gateway to relay the RTP streams between HNB and core network.

Change-Id: Ib9b62e0145184b91c56ce5d8870760bfa49cc5a4
Related: OS#515
---
M configure.ac
M contrib/osmo-hnbgw.spec.in
M debian/control
M include/osmocom/hnbgw/context_map.h
M include/osmocom/hnbgw/hnbgw.h
A include/osmocom/hnbgw/mgw_fsm.h
A include/osmocom/hnbgw/tdefs.h
M include/osmocom/hnbgw/vty.h
M src/osmo-hnbgw/Makefile.am
M src/osmo-hnbgw/hnbgw.c
M src/osmo-hnbgw/hnbgw_cn.c
M src/osmo-hnbgw/hnbgw_rua.c
M src/osmo-hnbgw/hnbgw_vty.c
A src/osmo-hnbgw/mgw_fsm.c
A src/osmo-hnbgw/tdefs.c
15 files changed, 705 insertions(+), 1 deletion(-)

git pull ssh://gerrit.osmocom.org:29418/osmo-hnbgw refs/changes/95/26795/1
diff --git a/configure.ac b/configure.ac
index 4dc8087..f9bca78 100644
--- a/configure.ac
+++ b/configure.ac
@@ -65,7 +65,7 @@
PKG_CHECK_MODULES(LIBOSMORUA, libosmo-rua >= 1.1.0)
PKG_CHECK_MODULES(LIBOSMORANAP, libosmo-ranap >= 1.1.0)
PKG_CHECK_MODULES(LIBOSMOHNBAP, libosmo-hnbap >= 1.1.0)
-
+PKG_CHECK_MODULES(LIBOSMOMGCPCLIENT, libosmo-mgcp-client >= 1.9.0)

dnl checks for header files
AC_HEADER_STDC
diff --git a/contrib/osmo-hnbgw.spec.in b/contrib/osmo-hnbgw.spec.in
index 5ab0f5a..04fad1f 100644
--- a/contrib/osmo-hnbgw.spec.in
+++ b/contrib/osmo-hnbgw.spec.in
@@ -33,6 +33,7 @@
BuildRequires: systemd-rpm-macros
%endif
BuildRequires: pkgconfig(libcrypto) >= 0.9.5
+BuildRequires: pkgconfig(libosmo-mgcp-client) >= 1.9.0
BuildRequires: pkgconfig(libosmo-netif) >= 1.1.0
BuildRequires: pkgconfig(libosmo-sigtran) >= 1.5.0
BuildRequires: pkgconfig(libosmoabis) >= 1.2.0
diff --git a/debian/control b/debian/control
index 1ea3ad2..f7b1047 100644
--- a/debian/control
+++ b/debian/control
@@ -18,6 +18,7 @@
libosmo-sigtran-dev (>= 1.5.0),
libosmo-abis-dev (>= 1.2.0),
libosmo-netif-dev (>= 1.1.0),
+ libosmo-mgcp-client-dev (>= 1.9.0),
libosmo-hnbap-dev (>= 1.1.0),
libosmo-ranap-dev (>= 1.1.0),
libosmo-rua-dev (>= 1.1.0),
diff --git a/include/osmocom/hnbgw/context_map.h b/include/osmocom/hnbgw/context_map.h
index 6279b91..6910fe8 100644
--- a/include/osmocom/hnbgw/context_map.h
+++ b/include/osmocom/hnbgw/context_map.h
@@ -35,6 +35,9 @@
uint32_t scu_conn_id;

enum hnbgw_context_map_state state;
+
+ /* FSM instance for the MGW */
+ struct osmo_fsm_inst *mgw_fi;
};


diff --git a/include/osmocom/hnbgw/hnbgw.h b/include/osmocom/hnbgw/hnbgw.h
index fc8298d..9a46301 100644
--- a/include/osmocom/hnbgw/hnbgw.h
+++ b/include/osmocom/hnbgw/hnbgw.h
@@ -16,6 +16,7 @@
DHNBAP,
DRUA,
DRANAP,
+ DMGW,
};

#define LOGHNB(x, ss, lvl, fmt, args ...) \
@@ -133,6 +134,7 @@
bool hnbap_allow_tmsi;
/*! print hnb-id (true) or MCC-MNC-LAC-RAC-SAC (false) in logs */
bool log_prefix_hnb_id;
+ struct mgcp_client_conf *mgcp_client;
} config;
/*! SCTP listen socket for incoming connections */
struct osmo_stream_srv_link *iuh;
@@ -151,6 +153,7 @@
struct osmo_sccp_addr iucs_remote_addr;
struct osmo_sccp_addr iups_remote_addr;
} sccp;
+ struct mgcp_client *mgcp_client;
};

extern void *talloc_asn1_ctx;
diff --git a/include/osmocom/hnbgw/mgw_fsm.h b/include/osmocom/hnbgw/mgw_fsm.h
new file mode 100644
index 0000000..219026d
--- /dev/null
+++ b/include/osmocom/hnbgw/mgw_fsm.h
@@ -0,0 +1,5 @@
+#pragma once
+
+int mgw_fsm_alloc_and_handle_rab_ass_req(struct hnbgw_context_map *map, struct osmo_prim_hdr *oph_rab_ass_req);
+int mgw_fsm_handle_iu_release(struct hnbgw_context_map *map);
+int mgw_fsm_handle_rab_ass_resp(struct hnbgw_context_map *map, struct osmo_prim_hdr *oph);
diff --git a/include/osmocom/hnbgw/tdefs.h b/include/osmocom/hnbgw/tdefs.h
new file mode 100644
index 0000000..4f98a36
--- /dev/null
+++ b/include/osmocom/hnbgw/tdefs.h
@@ -0,0 +1,6 @@
+#pragma once
+
+#include <osmocom/core/tdef.h>
+
+extern struct osmo_tdef mgw_fsm_T_defs[];
+extern struct osmo_tdef_group hnbgw_tdef_group[];
diff --git a/include/osmocom/hnbgw/vty.h b/include/osmocom/hnbgw/vty.h
index 3d05da5..93b3c45 100644
--- a/include/osmocom/hnbgw/vty.h
+++ b/include/osmocom/hnbgw/vty.h
@@ -7,5 +7,6 @@
IUH_NODE,
IUCS_NODE,
IUPS_NODE,
+ MGCP_NODE,
};

diff --git a/src/osmo-hnbgw/Makefile.am b/src/osmo-hnbgw/Makefile.am
index 0948170..64d5ccd 100644
--- a/src/osmo-hnbgw/Makefile.am
+++ b/src/osmo-hnbgw/Makefile.am
@@ -19,6 +19,7 @@
$(LIBOSMORUA_CFLAGS) \
$(LIBOSMORANAP_CFLAGS) \
$(LIBOSMOHNBAP_CFLAGS) \
+ $(LIBOSMOMGCPCLIENT_CFLAGS) \
$(NULL)

AM_LDFLAGS = \
@@ -38,6 +39,8 @@
context_map.c \
hnbgw_cn.c \
ranap_rab_ass.c \
+ mgw_fsm.c \
+ tdefs.c \
$(NULL)

osmo_hnbgw_LDADD = \
@@ -52,5 +55,7 @@
$(LIBOSMORUA_LIBS) \
$(LIBOSMORANAP_LIBS) \
$(LIBOSMOHNBAP_LIBS) \
+ $(LIBOSMOMGCPCLIENT_LIBS) \
$(LIBSCTP_LIBS) \
+ $(LIBOSMOMGCPCLIENT_LIBS) \
$(NULL)
diff --git a/src/osmo-hnbgw/hnbgw.c b/src/osmo-hnbgw/hnbgw.c
index da15bfc..cc6197c 100644
--- a/src/osmo-hnbgw/hnbgw.c
+++ b/src/osmo-hnbgw/hnbgw.c
@@ -52,6 +52,8 @@
#include <osmocom/vty/command.h>
#include <osmocom/vty/ports.h>

+#include <osmocom/mgcp_client/mgcp_client.h>
+
#include <osmocom/netif/stream.h>

#include <osmocom/ranap/ranap_common.h>
@@ -372,6 +374,11 @@
.color = "",
.description = "RAN Application Part",
},
+ [DMGW] = {
+ .name = "DMGW", .loglevel = LOGL_NOTICE, .enabled = 1,
+ .color = "\033[1;33m",
+ .description = "Media Gateway",
+ },
};

static const struct log_info hnbgw_log_info = {
@@ -681,6 +688,19 @@
}
g_hnb_gw->iuh = srv;

+ /* Initialize and connect MGCP client. */
+ g_hnb_gw->mgcp_client = mgcp_client_init(tall_hnb_ctx, g_hnb_gw->config.mgcp_client);
+ if (!g_hnb_gw->mgcp_client) {
+ LOGP(DMGW, LOGL_ERROR, "MGW client initalization failed\n");
+ return -EINVAL;
+ }
+ if (mgcp_client_connect(g_hnb_gw->mgcp_client)) {
+ LOGP(DMGW, LOGL_ERROR, "MGW connect failed at (%s:%u)\n",
+ g_hnb_gw->config.mgcp_client->remote_addr,
+ g_hnb_gw->config.mgcp_client->remote_port);
+ return -EINVAL;
+ }
+
if (hnbgw_cmdline_config.daemonize) {
rc = osmo_daemonize();
if (rc < 0) {
diff --git a/src/osmo-hnbgw/hnbgw_cn.c b/src/osmo-hnbgw/hnbgw_cn.c
index 7fbb691..c3deba5 100644
--- a/src/osmo-hnbgw/hnbgw_cn.c
+++ b/src/osmo-hnbgw/hnbgw_cn.c
@@ -34,6 +34,7 @@
#include <osmocom/ranap/ranap_ies_defs.h>
#include <osmocom/ranap/ranap_msg_factory.h>
#include <osmocom/hnbgw/context_map.h>
+#include <osmocom/hnbgw/mgw_fsm.h>

/***********************************************************************
* Outbound RANAP RESET to CN
@@ -356,6 +357,21 @@
return 0;
}

+ /* Intercept RAB Assignment Request, Setup MGW FSM */
+ if (!map->is_ps && msgb_l2len(oph->msg) > 2) {
+ switch (((uint8_t*)msgb_l2(oph->msg))[1]) {
+ case RANAP_ProcedureCode_id_RAB_Assignment:
+ /* Just like rua_tx_dt(), mgw_fsm_inst_alloc() will not take ownership of oph. Instead it will
+ * make its own copy of the message contents, so it is safe to free oph->msg() in sccp_sap_up()
+ * below. */
+ return mgw_fsm_alloc_and_handle_rab_ass_req(map, oph);
+ case RANAP_ProcedureCode_id_Iu_Release:
+ /* Any IU Release will terminate the MGW FSM */
+ mgw_fsm_handle_iu_release(map);
+ break;
+ }
+ }
+
return rua_tx_dt(map->hnb_ctx, map->is_ps, map->rua_ctx_id,
msgb_l2(oph->msg), msgb_l2len(oph->msg));
}
diff --git a/src/osmo-hnbgw/hnbgw_rua.c b/src/osmo-hnbgw/hnbgw_rua.c
index 24ca167..202007f 100644
--- a/src/osmo-hnbgw/hnbgw_rua.c
+++ b/src/osmo-hnbgw/hnbgw_rua.c
@@ -38,6 +38,8 @@
#include <osmocom/rua/rua_ies_defs.h>
#include <osmocom/hnbgw/context_map.h>
#include <osmocom/hnbap/HNBAP_CN-DomainIndicator.h>
+#include <osmocom/hnbgw/mgw_fsm.h>
+#include <osmocom/ranap/RANAP_ProcedureCode.h>

static const char *cn_domain_indicator_to_str(RUA_CN_DomainIndicator_t cN_DomainIndicator)
{
@@ -265,6 +267,17 @@
memcpy(msg->l2h, data, len);
}

+ /* Intercept RAB Assignment Response, inform MGW FSM. */
+ if (map && !map->is_ps && !release_context_map && msgb_l2len(prim->oph.msg) > 2)
+ {
+ switch (((uint8_t*)msgb_l2(prim->oph.msg))[1]) {
+ case RANAP_ProcedureCode_id_RAB_Assignment:
+ /* Note: Just like osmo_sccp_user_sap_down(), mgw_fsm_handle_rab_as_resp() will take ownership
+ * of oph */
+ return mgw_fsm_handle_rab_ass_resp(map, &prim->oph);
+ }
+ }
+
rc = osmo_sccp_user_sap_down(cn->sccp_user, &prim->oph);

if (map && release_context_map)
diff --git a/src/osmo-hnbgw/hnbgw_vty.c b/src/osmo-hnbgw/hnbgw_vty.c
index 4ad1ddb..0ec16fe 100644
--- a/src/osmo-hnbgw/hnbgw_vty.c
+++ b/src/osmo-hnbgw/hnbgw_vty.c
@@ -22,15 +22,19 @@

#include <osmocom/core/socket.h>
#include <osmocom/vty/command.h>
+#include <osmocom/vty/tdef_vty.h>

#include <osmocom/hnbgw/vty.h>

#include <osmocom/hnbgw/hnbgw.h>
#include <osmocom/hnbgw/context_map.h>
+#include <osmocom/hnbgw/tdefs.h>
#include <osmocom/sigtran/protocol/sua.h>
#include <osmocom/sigtran/sccp_helpers.h>
#include <osmocom/netif/stream.h>

+#include <osmocom/mgcp_client/mgcp_client.h>
+
static void *tall_hnb_ctx = NULL;
static struct hnb_gw *g_hnb_gw = NULL;

@@ -86,6 +90,19 @@
return CMD_SUCCESS;
}

+static struct cmd_node mgcp_node = {
+ MGCP_NODE,
+ "%s(config-hnbgw-mgcp)# ",
+ 1,
+};
+
+DEFUN(cfg_hnbgw_mgcp, cfg_hnbgw_mgcp_cmd,
+ "mgcp", "Configure MGCP client")
+{
+ vty->node = MGCP_NODE;
+ return CMD_SUCCESS;
+}
+
int hnbgw_vty_go_parent(struct vty *vty)
{
switch (vty->node) {
@@ -95,6 +112,10 @@
vty->node = HNBGW_NODE;
vty->index = NULL;
break;
+ case MGCP_NODE:
+ vty->node = HNBGW_NODE;
+ vty->index = NULL;
+ break;
case HNBGW_NODE:
vty->node = CONFIG_NODE;
vty->index = NULL;
@@ -382,6 +403,14 @@
return CMD_SUCCESS;
}

+static int config_write_hnbgw_mgcp(struct vty *vty)
+{
+ vty_out(vty, " mgcp%s", VTY_NEWLINE);
+ mgcp_client_config_write(vty, " ");
+
+ return CMD_SUCCESS;
+}
+
void hnbgw_vty_init(struct hnb_gw *gw, void *tall_ctx)
{
g_hnb_gw = gw;
@@ -415,4 +444,13 @@
install_element_ve(&show_one_hnb_cmd);
install_element_ve(&show_ue_cmd);
install_element_ve(&show_talloc_cmd);
+
+ install_element(HNBGW_NODE, &cfg_hnbgw_mgcp_cmd);
+ install_node(&mgcp_node, config_write_hnbgw_mgcp);
+
+ g_hnb_gw->config.mgcp_client = talloc_zero(tall_hnb_ctx, struct mgcp_client_conf);
+ mgcp_client_conf_init(g_hnb_gw->config.mgcp_client);
+ mgcp_client_vty_init(tall_hnb_ctx, MGCP_NODE, g_hnb_gw->config.mgcp_client);
+
+ osmo_tdef_vty_groups_init(HNBGW_NODE, hnbgw_tdef_group);
}
diff --git a/src/osmo-hnbgw/mgw_fsm.c b/src/osmo-hnbgw/mgw_fsm.c
new file mode 100644
index 0000000..df97c1a
--- /dev/null
+++ b/src/osmo-hnbgw/mgw_fsm.c
@@ -0,0 +1,562 @@
+/* (C) 2021 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ *
+ * Author: Philipp Maier
+ *
+ * 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 2 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.
+ */
+
+#include <errno.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/prim.h>
+
+#include <osmocom/core/fsm.h>
+#include <osmocom/core/byteswap.h>
+#include <arpa/inet.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/sockaddr_str.h>
+
+#include <osmocom/ranap/ranap_common.h>
+#include <osmocom/ranap/ranap_ies_defs.h>
+#include <osmocom/ranap/ranap_msg_factory.h>
+
+#include <osmocom/ranap/ranap_ies_defs.h>
+#include <osmocom/ranap/iu_helpers.h>
+#include <asn1c/asn1helpers.h>
+
+#include <osmocom/hnbgw/hnbgw.h>
+#include <osmocom/hnbgw/context_map.h>
+#include <osmocom/hnbgw/ranap_rab_ass.h>
+
+#include <osmocom/hnbgw/hnbgw_rua.h>
+
+#include <osmocom/core/tdef.h>
+#include <osmocom/hnbgw/tdefs.h>
+#include <osmocom/mgcp_client/mgcp_client_endpoint_fsm.h>
+
+#define S(x) (1 << (x))
+
+extern int asn1_xer_print;
+
+enum mgw_fsm_event {
+ MGW_EV_MGCP_OK,
+ MGW_EV_MGCP_FAIL,
+ MGW_EV_MGCP_TERM,
+ MGW_EV_RAB_ASS_RESP,
+ MGW_EV_RELEASE,
+};
+
+static const struct value_string mgw_fsm_event_names[] = {
+ OSMO_VALUE_STRING(MGW_EV_MGCP_OK),
+ OSMO_VALUE_STRING(MGW_EV_MGCP_FAIL),
+ OSMO_VALUE_STRING(MGW_EV_MGCP_TERM),
+ OSMO_VALUE_STRING(MGW_EV_RAB_ASS_RESP),
+ OSMO_VALUE_STRING(MGW_EV_RELEASE),
+ { }
+};
+
+enum mgw_fsm_state {
+ MGW_ST_CRCX_HNB,
+ MGW_ST_ASSIGN,
+ MGW_ST_MDCX_HNB,
+ MGW_ST_CRCX_MSC,
+ MGW_ST_ESTABLISHED,
+ MGW_ST_RELEASE,
+};
+
+struct mgw_fsm_priv {
+ /* Backpointer to HNBGW context */
+ struct hnbgw_context_map *map;
+
+ /* RAB-ID from RANAP RAB AssignmentRequest message */
+ uint8_t rab_id;
+
+ /* We have to store the RANAP RAB AssignmentRequest message, since we can not take ownership of the osmo prim
+ * header (oph). */
+ uint8_t rab_ass_req[IUH_MSGB_SIZE];
+ unsigned int rab_ass_req_len;
+
+ /* The RANAP RAB AssignmentResponse is handled differently. In this case we must take the ownership of the
+ * osmo prim header */
+ struct osmo_prim_hdr *oph_rab_ass_resp;
+
+ /* MGW context */
+ struct osmo_mgcpc_ep *mgcpc_ep;
+ struct osmo_mgcpc_ep_ci *mgcpc_ep_ci_hnb;
+ struct osmo_mgcpc_ep_ci *mgcpc_ep_ci_msc;
+ char msc_rtp_addr[INET6_ADDRSTRLEN];
+ uint16_t msc_rtp_port;
+};
+
+struct osmo_tdef mgw_tdefs[] = {
+ {.T = -2427,.default_val = 5,.desc = "timeout for MGCP response from MGW" },
+ { }
+};
+
+struct osmo_tdef_state_timeout mgw_fsm_timeouts[32] = {
+ [MGW_ST_CRCX_HNB] = {.T = -1001 },
+ [MGW_ST_ASSIGN] = {.T = -1002 },
+ [MGW_ST_MDCX_HNB] = {.T = -1003 },
+ [MGW_ST_CRCX_MSC] = {.T = -1004 },
+};
+
+#define mgw_fsm_state_chg(state) \
+ osmo_tdef_fsm_inst_state_chg(fi, state, \
+ mgw_fsm_timeouts, \
+ mgw_fsm_T_defs, \
+ -1)
+
+static void mgw_fsm_crcx_hnb_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct mgw_fsm_priv *mgw_fsm_priv = fi->priv;
+ struct hnbgw_context_map *map = mgw_fsm_priv->map;
+ const char *epname;
+ struct mgcp_conn_peer mgw_info;
+
+ LOGPFSML(fi, LOGL_DEBUG, "RAB-AssignmentRequest received, creating HNB side call-leg on MGW...\n");
+
+ mgw_info = (struct mgcp_conn_peer) {
+ .call_id = (map->rua_ctx_id << 8) | mgw_fsm_priv->rab_id,
+ .ptime = 20,
+ .conn_mode = MGCP_CONN_LOOPBACK,
+ };
+ mgw_info.codecs[0] = CODEC_IUFP;
+ mgw_info.codecs_len = 1;
+
+ epname = mgcp_client_rtpbridge_wildcard(map->hnb_ctx->gw->mgcp_client);
+ mgw_fsm_priv->mgcpc_ep =
+ osmo_mgcpc_ep_alloc(fi, MGW_EV_MGCP_TERM, map->hnb_ctx->gw->mgcp_client, mgw_tdefs, fi->id, "%s", epname);
+ mgw_fsm_priv->mgcpc_ep_ci_hnb = osmo_mgcpc_ep_ci_add(mgw_fsm_priv->mgcpc_ep, "to-HNB");
+
+ osmo_mgcpc_ep_ci_request(mgw_fsm_priv->mgcpc_ep_ci_hnb, MGCP_VERB_CRCX, &mgw_info, fi, MGW_EV_MGCP_OK,
+ MGW_EV_MGCP_FAIL, NULL);
+}
+
+static void mgw_fsm_crcx_hnb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct mgw_fsm_priv *mgw_fsm_priv = fi->priv;
+ const struct mgcp_conn_peer *mgw_info;
+ int rc;
+
+ switch (event) {
+ case MGW_EV_MGCP_OK:
+ mgw_info = osmo_mgcpc_ep_ci_get_rtp_info(mgw_fsm_priv->mgcpc_ep_ci_hnb);
+ if (!mgw_info) {
+ LOGPFSML(fi, LOGL_ERROR, "Got no response from MGW\n");
+ osmo_fsm_inst_state_chg(fi, MGW_ST_RELEASE, 0, 0);
+ return;
+ }
+
+ rc = ranap_rab_ass_req_replace_inet_addr(mgw_fsm_priv->rab_ass_req, sizeof(mgw_fsm_priv->rab_ass_req),
+ mgw_info->addr, mgw_info->port);
+ if (rc < 0) {
+ LOGPFSML(fi, LOGL_ERROR,
+ "Failed to replace RTP IP-address (%s) and Port (%u) in RAB-AssignmentRequest\n",
+ mgw_info->addr, mgw_info->port);
+ osmo_fsm_inst_state_chg(fi, MGW_ST_RELEASE, 0, 0);
+ mgw_fsm_priv->rab_ass_req_len = 0;
+ return;
+ }
+ mgw_fsm_priv->rab_ass_req_len = rc;
+
+ mgw_fsm_state_chg(MGW_ST_ASSIGN);
+ return;
+ case MGW_EV_MGCP_FAIL:
+ osmo_fsm_inst_state_chg(fi, MGW_ST_RELEASE, 0, 0);
+ return;
+ default:
+ OSMO_ASSERT(false);
+ }
+}
+
+static void mgw_fsm_assign_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct mgw_fsm_priv *mgw_fsm_priv = fi->priv;
+ struct hnbgw_context_map *map = mgw_fsm_priv->map;
+
+ LOGPFSML(fi, LOGL_DEBUG, "forwarding modified RAB-AssignmentRequest to HNB\n");
+ rua_tx_dt(map->hnb_ctx, map->is_ps, map->rua_ctx_id, mgw_fsm_priv->rab_ass_req, mgw_fsm_priv->rab_ass_req_len);
+}
+
+static void mgw_fsm_assign(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct mgw_fsm_priv *mgw_fsm_priv = fi->priv;
+ struct osmo_prim_hdr *oph = data;
+
+ switch (event) {
+ case MGW_EV_RAB_ASS_RESP:
+ mgw_fsm_priv->oph_rab_ass_resp = oph;
+ mgw_fsm_state_chg(MGW_ST_MDCX_HNB);
+ return;
+ default:
+ OSMO_ASSERT(false);
+ }
+}
+
+static void mgw_fsm_mdcx_hnb_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct mgw_fsm_priv *mgw_fsm_priv = fi->priv;
+ struct hnbgw_context_map *map = mgw_fsm_priv->map;
+ struct mgcp_conn_peer mgw_info;
+ int rc;
+
+ LOGPFSML(fi, LOGL_DEBUG, "RAB-AssignmentResponse received, completing HNB side call-leg on MGW...\n");
+
+ mgw_info = (struct mgcp_conn_peer) {
+ .call_id = map->rua_ctx_id,
+ .ptime = 20,
+ .conn_mode = MGCP_CONN_RECV_SEND,
+ };
+ mgw_info.codecs[0] = CODEC_IUFP;
+ mgw_info.codecs_len = 1;
+
+ rc = ranap_rab_ass_resp_extract_inet_addr(mgw_info.addr, &mgw_info.port,
+ msgb_l2(mgw_fsm_priv->oph_rab_ass_resp->msg),
+ msgb_l2len(mgw_fsm_priv->oph_rab_ass_resp->msg));
+ if (rc < 0) {
+ LOGPFSML(fi, LOGL_ERROR, "Failed to extract RTP IP-address and Port from RAB-AssignmentResponse\n");
+ osmo_fsm_inst_state_chg(fi, MGW_ST_RELEASE, 0, 0);
+ return;
+ }
+
+ osmo_mgcpc_ep_ci_request(mgw_fsm_priv->mgcpc_ep_ci_hnb, MGCP_VERB_MDCX, &mgw_info, fi, MGW_EV_MGCP_OK,
+ MGW_EV_MGCP_FAIL, NULL);
+}
+
+static void mgw_fsm_mdcx_hnb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct mgw_fsm_priv *mgw_fsm_priv = fi->priv;
+ const struct mgcp_conn_peer *mgw_info;
+
+ switch (event) {
+ case MGW_EV_MGCP_OK:
+ mgw_info = osmo_mgcpc_ep_ci_get_rtp_info(mgw_fsm_priv->mgcpc_ep_ci_hnb);
+ if (!mgw_info) {
+ LOGPFSML(fi, LOGL_ERROR, "Got no response from MGW\n");
+ osmo_fsm_inst_state_chg(fi, MGW_ST_RELEASE, 0, 0);
+ return;
+ }
+ mgw_fsm_state_chg(MGW_ST_CRCX_MSC);
+ return;
+ case MGW_EV_MGCP_FAIL:
+ osmo_fsm_inst_state_chg(fi, MGW_ST_RELEASE, 0, 0);
+ return;
+ default:
+ OSMO_ASSERT(false);
+ }
+}
+
+static void mgw_fsm_crcx_msc_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct mgw_fsm_priv *mgw_fsm_priv = fi->priv;
+ struct hnbgw_context_map *map = mgw_fsm_priv->map;
+ struct mgcp_conn_peer mgw_info;
+
+ LOGPFSML(fi, LOGL_DEBUG, "creating MSC side call-leg on MGW...\n");
+
+ mgw_info = (struct mgcp_conn_peer) {
+ .call_id = (map->rua_ctx_id << 8) | mgw_fsm_priv->rab_id,
+ .ptime = 20,
+ .port = mgw_fsm_priv->msc_rtp_port,
+ };
+
+ osmo_strlcpy(mgw_info.addr, mgw_fsm_priv->msc_rtp_addr, sizeof(mgw_info.addr));
+ mgw_info.codecs[0] = CODEC_IUFP;
+ mgw_info.codecs_len = 1;
+
+ mgw_fsm_priv->mgcpc_ep_ci_msc = osmo_mgcpc_ep_ci_add(mgw_fsm_priv->mgcpc_ep, "to-MSC");
+ osmo_mgcpc_ep_ci_request(mgw_fsm_priv->mgcpc_ep_ci_msc, MGCP_VERB_CRCX, &mgw_info, fi, MGW_EV_MGCP_OK,
+ MGW_EV_MGCP_FAIL, NULL);
+}
+
+static void mgw_fsm_crcx_msc(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct mgw_fsm_priv *mgw_fsm_priv = fi->priv;
+ const struct mgcp_conn_peer *mgw_info;
+ int rc;
+ int msg_max_len;
+
+ switch (event) {
+ case MGW_EV_MGCP_OK:
+ mgw_info = osmo_mgcpc_ep_ci_get_rtp_info(mgw_fsm_priv->mgcpc_ep_ci_msc);
+ if (!mgw_info) {
+ LOGPFSML(fi, LOGL_ERROR, "Got no response from MGW\n");
+ osmo_fsm_inst_state_chg(fi, MGW_ST_RELEASE, 0, 0);
+ return;
+ }
+
+ /* When the RTP IP-Address/Port is replaced the message might grow. Ensure that there is enough room in
+ * l2h to grow. (The current implementation should yield a message with the same size, but there is no
+ * guarantee for that) */
+ msg_max_len =
+ msgb_l2len(mgw_fsm_priv->oph_rab_ass_resp->msg) +
+ msgb_tailroom(mgw_fsm_priv->oph_rab_ass_resp->msg);
+ rc = msgb_resize_area(mgw_fsm_priv->oph_rab_ass_resp->msg, mgw_fsm_priv->oph_rab_ass_resp->msg->l2h,
+ msgb_l2len(mgw_fsm_priv->oph_rab_ass_resp->msg), msg_max_len);
+ OSMO_ASSERT(rc == 0);
+
+ /* Replace RTP IP-Address/Port. */
+ rc = ranap_rab_ass_resp_replace_inet_addr(msgb_l2(mgw_fsm_priv->oph_rab_ass_resp->msg),
+ msgb_l2len(mgw_fsm_priv->oph_rab_ass_resp->msg),
+ mgw_info->addr, mgw_info->port);
+ if (rc < 0) {
+ LOGPFSML(fi, LOGL_ERROR,
+ "Failed to replace RTP IP-address (%s) and Port (%u) in RAB-AssignmentResponse\n",
+ mgw_info->addr, mgw_info->port);
+ osmo_fsm_inst_state_chg(fi, MGW_ST_RELEASE, 0, 0);
+ return;
+ }
+
+ /* Resize l2h back to the actual message length */
+ rc = msgb_resize_area(mgw_fsm_priv->oph_rab_ass_resp->msg, mgw_fsm_priv->oph_rab_ass_resp->msg->l2h,
+ msgb_l2len(mgw_fsm_priv->oph_rab_ass_resp->msg), rc);
+ OSMO_ASSERT(rc == 0);
+
+ /* When the established state is entered, the modified RAB AssignmentResponse is forwarded to the MSC.
+ * The call is then established any way may stay for an indefinate amount of time in this state until
+ * there is an IU Release happening. */
+ osmo_fsm_inst_state_chg(fi, MGW_ST_ESTABLISHED, 0, 0);
+ return;
+ case MGW_EV_MGCP_FAIL:
+ osmo_fsm_inst_state_chg(fi, MGW_ST_RELEASE, 0, 0);
+ return;
+ default:
+ OSMO_ASSERT(false);
+ }
+}
+
+static void mgw_fsm_established_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct mgw_fsm_priv *mgw_fsm_priv = fi->priv;
+ struct hnbgw_context_map *map = mgw_fsm_priv->map;
+ struct osmo_prim_hdr *oph = mgw_fsm_priv->oph_rab_ass_resp;
+ struct hnb_context *hnb = map->hnb_ctx;
+ struct hnbgw_cnlink *cn = hnb->gw->sccp.cnlink;
+ int rc;
+
+ rc = osmo_sccp_user_sap_down(cn->sccp_user, oph);
+ mgw_fsm_priv->oph_rab_ass_resp = NULL;
+ if (rc < 0)
+ osmo_fsm_inst_state_chg(fi, MGW_ST_RELEASE, 0, 0);
+
+ LOGPFSML(fi, LOGL_DEBUG, "HNB and MSC side call-legs completed!\n");
+}
+
+static void mgw_fsm_release_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
+}
+
+static void mgw_fsm_allstate_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct mgw_fsm_priv *mgw_fsm_priv = fi->priv;
+
+ switch (event) {
+ case MGW_EV_RELEASE:
+ osmo_fsm_inst_state_chg(fi, MGW_ST_RELEASE, 0, 0);
+ return;
+ case MGW_EV_MGCP_TERM:
+ mgw_fsm_priv->mgcpc_ep = NULL;
+ osmo_fsm_inst_state_chg(fi, MGW_ST_RELEASE, 0, 0);
+ return;
+ default:
+ OSMO_ASSERT(false);
+ }
+}
+
+static int mgw_fsm_timer_cb(struct osmo_fsm_inst *fi)
+{
+ osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
+ return 0;
+}
+
+void mgw_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
+{
+ struct mgw_fsm_priv *mgw_fsm_priv = fi->priv;
+ struct osmo_scu_prim *scu_prim;
+ struct msgb *scu_msg;
+
+ /* In case we were unable to deliver the RANAP RAB AssignmentResponse we are responsible to free it since se
+ * have taken ownership of the prim header */
+ if (mgw_fsm_priv->oph_rab_ass_resp) {
+ scu_prim = (struct osmo_scu_prim *)mgw_fsm_priv->oph_rab_ass_resp;
+ scu_msg = scu_prim->oph.msg;
+ msgb_free(scu_msg);
+ mgw_fsm_priv->oph_rab_ass_resp = NULL;
+ }
+
+ talloc_free(mgw_fsm_priv);
+}
+
+static void mgw_fsm_pre_term(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
+{
+ struct mgw_fsm_priv *mgw_fsm_priv = fi->priv;
+ struct hnbgw_context_map *map = mgw_fsm_priv->map;
+
+ if (mgw_fsm_priv->mgcpc_ep) {
+ osmo_mgcpc_ep_clear(mgw_fsm_priv->mgcpc_ep);
+ mgw_fsm_priv->mgcpc_ep = NULL;
+ }
+
+ /* Remove FSM from the context map. This will make this FSM unreachable for events comming from outside */
+ map->mgw_fi = NULL;
+}
+
+static const struct osmo_fsm_state mgw_fsm_states[] = {
+ [MGW_ST_CRCX_HNB] = {
+ .name = "MGW_ST_CRCX_HNB",
+ .onenter = mgw_fsm_crcx_hnb_onenter,
+ .action = mgw_fsm_crcx_hnb,
+ .in_event_mask = S(MGW_EV_MGCP_OK) | S(MGW_EV_MGCP_FAIL),
+ .out_state_mask = S(MGW_ST_ASSIGN) | S(MGW_ST_RELEASE) | S(MGW_ST_CRCX_HNB),
+ },
+ [MGW_ST_ASSIGN] = {
+ .name = "MGW_ST_ASSIGN",
+ .onenter = mgw_fsm_assign_onenter,
+ .action = mgw_fsm_assign,
+ .in_event_mask = S(MGW_EV_RAB_ASS_RESP),
+ .out_state_mask = S(MGW_ST_MDCX_HNB) | S(MGW_ST_RELEASE),
+ },
+ [MGW_ST_MDCX_HNB] = {
+ .name = "MGW_ST_MDCX_HNB",
+ .onenter = mgw_fsm_mdcx_hnb_onenter,
+ .action = mgw_fsm_mdcx_hnb,
+ .in_event_mask = S(MGW_EV_MGCP_OK) | S(MGW_EV_MGCP_FAIL),
+ .out_state_mask = S(MGW_ST_CRCX_MSC) | S(MGW_ST_RELEASE),
+ },
+ [MGW_ST_CRCX_MSC] = {
+ .name = "MGW_ST_CRCX_MSC",
+ .onenter = mgw_fsm_crcx_msc_onenter,
+ .action = mgw_fsm_crcx_msc,
+ .in_event_mask = S(MGW_EV_MGCP_OK) | S(MGW_EV_MGCP_FAIL),
+ .out_state_mask = S(MGW_ST_ESTABLISHED) | S(MGW_ST_RELEASE),
+ },
+ [MGW_ST_ESTABLISHED] = {
+ .name = "MGW_ST_ESTABLISHED",
+ .onenter = mgw_fsm_established_onenter,
+ .in_event_mask = 0,
+ .out_state_mask = S(MGW_ST_RELEASE),
+ },
+ [MGW_ST_RELEASE] = {
+ .name = "MGW_ST_RELEASE",
+ .onenter = mgw_fsm_release_onenter,
+ .in_event_mask = 0,
+ .out_state_mask = 0,
+ },
+};
+
+static struct osmo_fsm mgw_fsm = {
+ .name = "mgw",
+ .states = mgw_fsm_states,
+ .num_states = ARRAY_SIZE(mgw_fsm_states),
+ .log_subsys = DMGW,
+ .event_names = mgw_fsm_event_names,
+ .allstate_action = mgw_fsm_allstate_action,
+ .allstate_event_mask = S(MGW_EV_MGCP_TERM) | S(MGW_EV_RELEASE),
+ .timer_cb = mgw_fsm_timer_cb,
+ .cleanup = mgw_fsm_cleanup,
+ .pre_term = mgw_fsm_pre_term,
+};
+
+/*! Allocate MGW FSM and handle RANAP RAB AssignmentRequest).
+ * \ptmap[in] map hanbgw context map that is responsible for this call.
+ * \ptmap[in] oph osmo prim header with RANAP RAB AssignmentRequest (function does not take ownership).
+ * \returns 0 on success; negative on error. */
+int mgw_fsm_alloc_and_handle_rab_ass_req(struct hnbgw_context_map *map, struct osmo_prim_hdr *oph_rab_ass_req)
+{
+ static bool initialized = false;
+ struct osmo_fsm_inst *fi;
+ struct mgw_fsm_priv *mgw_fsm_priv;
+ int rc;
+ char fsm_name[255];
+
+ /* Initialize FSM if not done yet */
+ if (!initialized) {
+ OSMO_ASSERT(osmo_fsm_register(&mgw_fsm) == 0);
+ initialized = true;
+ }
+
+ /* When there is already an FSM, make sure that it is terminated. Under normal circumstances this situation
+ * should not occur. */
+ if (map->mgw_fi) {
+ LOGPFSML(map->mgw_fi, LOGL_ERROR, "another FSM instance is about to replace this FSM!\n");
+ osmo_fsm_inst_dispatch(map->mgw_fi, MGW_EV_RELEASE, NULL);
+ map->mgw_fi = NULL;
+ }
+
+ /* Create context and keep a local copy of the RAB AssignmentRequest, we will need this copy later to
+ * modify and forward it. We cannot take ownership of the prim header oph_rab_ass_req. */
+ mgw_fsm_priv = talloc_zero(map, struct mgw_fsm_priv);
+ OSMO_ASSERT(msgb_l2len(oph_rab_ass_req->msg) <= sizeof(mgw_fsm_priv->rab_ass_req));
+ memcpy(mgw_fsm_priv->rab_ass_req, msgb_l2(oph_rab_ass_req->msg), msgb_l2len(oph_rab_ass_req->msg));
+ mgw_fsm_priv->rab_ass_req_len = msgb_l2len(oph_rab_ass_req->msg);
+
+ /* Parse the RAB Assignment Request now, if it is bad for some reason we will exit early and not bother with
+ * creating an FSM etc. */
+ rc = ranap_rab_ass_req_extract_inet_addr(mgw_fsm_priv->msc_rtp_addr, &mgw_fsm_priv->msc_rtp_port,
+ &mgw_fsm_priv->rab_id, mgw_fsm_priv->rab_ass_req,
+ mgw_fsm_priv->rab_ass_req_len);
+ if (rc < 0) {
+ LOGP(DMGW, LOGL_ERROR,
+ "mgw_fsm_alloc_and_handle_rab_ass_req() rua_ctx_id=%d, invalid RAB-AssignmentRequest -- abort!\n",
+ map->rua_ctx_id);
+ talloc_free(mgw_fsm_priv);
+ return -EINVAL;
+ }
+
+ /* Allocate the FSM and start it. */
+ mgw_fsm_priv->map = map;
+ snprintf(fsm_name, sizeof(fsm_name), "mgw-fsm-%u-%u", map->rua_ctx_id, mgw_fsm_priv->rab_id);
+ fi = osmo_fsm_inst_alloc(&mgw_fsm, map, mgw_fsm_priv, LOGL_DEBUG, fsm_name);
+ map->mgw_fi = fi;
+ mgw_fsm_state_chg(MGW_ST_CRCX_HNB);
+
+ return 0;
+}
+
+/*! Handlie IU release (terminate RTP streams).
+ * \ptmap[in] map hanbgw context map that is responsible for this call.
+ * \returns 0 on success; negative on error. */
+int mgw_fsm_handle_iu_release(struct hnbgw_context_map *map)
+{
+ if (!map->mgw_fi) {
+ LOGP(DMGW, LOGL_ERROR, "mgw_fsm_handle_iu_release() rua_ctx_id=%d, no MGW fsm -- ignored!\n",
+ map->rua_ctx_id);
+ return -EINVAL;
+ }
+
+ osmo_fsm_inst_dispatch(map->mgw_fi, MGW_EV_RELEASE, NULL);
+ return 0;
+}
+
+/*! Handlie RANAP RAB AssignmentResponse (deliver message, complete RTP stream switching).
+ * \ptmap[in] map hanbgw context map that is responsible for this call.
+ * \ptmap[in] oph osmo prim header with RANAP RAB AssignmentResponse (function takes ownership).
+ * \returns 0 on success; negative on error. */
+int mgw_fsm_handle_rab_ass_resp(struct hnbgw_context_map *map, struct osmo_prim_hdr *oph)
+{
+ struct osmo_scu_prim *scu_prim;
+ struct msgb *scu_msg;
+
+ if (!map->mgw_fi) {
+ LOGP(DMGW, LOGL_ERROR, "mgw_fsm_handle_rab_ass_resp() rua_ctx_id=%d, no MGW fsm -- abort!\n",
+ map->rua_ctx_id);
+ scu_prim = (struct osmo_scu_prim *)oph;
+ scu_msg = scu_prim->oph.msg;
+ msgb_free(scu_msg);
+ return -EINVAL;
+ }
+
+ osmo_fsm_inst_dispatch(map->mgw_fi, MGW_EV_RAB_ASS_RESP, oph);
+ return 0;
+}
diff --git a/src/osmo-hnbgw/tdefs.c b/src/osmo-hnbgw/tdefs.c
new file mode 100644
index 0000000..d8198f2
--- /dev/null
+++ b/src/osmo-hnbgw/tdefs.c
@@ -0,0 +1,30 @@
+/* (C) 2021 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ *
+ * Author: Philipp Maier
+ *
+ * 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 2 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.
+ */
+
+#include <osmocom/hnbgw/tdefs.h>
+
+struct osmo_tdef mgw_fsm_T_defs[] = {
+ {.T = -1001, .default_val = 5, .desc = "Timeout for HNB side call-leg (to-HNB) creation" },
+ {.T = -1002, .default_val = 10, .desc = "Timeout for the HNB to respond to RAB Assignment Request" },
+ {.T = -1003, .default_val = 5, .desc = "Timeout for HNB side call-leg (to-HNB) completion" },
+ {.T = -1004, .default_val = 5, .desc = "Timeout for MSC side call-leg (to-MSC) completion" },
+ { }
+};
+
+struct osmo_tdef_group hnbgw_tdef_group[] = {
+ {.name = "mgw", .tdefs = mgw_fsm_T_defs, .desc = "MGW (Media Gateway) interface" },
+ { }
+};

To view, visit change 26795. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: osmo-hnbgw
Gerrit-Branch: master
Gerrit-Change-Id: Ib9b62e0145184b91c56ce5d8870760bfa49cc5a4
Gerrit-Change-Number: 26795
Gerrit-PatchSet: 1
Gerrit-Owner: dexter <pmaier@sysmocom.de>
Gerrit-MessageType: newchange