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#5152changes/95/26795/28
parent
e7c66defc2
commit
81f1751896
|
@ -60,7 +60,7 @@ PKG_CHECK_MODULES(LIBOSMOSIGTRAN, libosmo-sigtran >= 1.5.0)
|
|||
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
|
||||
|
|
|
@ -35,6 +35,7 @@ osmo-build-dep.sh libosmo-netif
|
|||
osmo-build-dep.sh libosmo-sccp
|
||||
osmo-build-dep.sh libasn1c
|
||||
osmo-build-dep.sh osmo-iuh
|
||||
osmo-build-dep.sh osmo-mgw
|
||||
|
||||
# Additional configure options and depends
|
||||
CONFIG=""
|
||||
|
|
|
@ -32,6 +32,7 @@ BuildRequires: pkgconfig >= 0.20
|
|||
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
|
||||
|
|
|
@ -17,6 +17,7 @@ Build-Depends: debhelper (>=9),
|
|||
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),
|
||||
|
|
|
@ -2,4 +2,4 @@ noinst_HEADERS = \
|
|||
vty.h \
|
||||
context_map.h hnbgw.h hnbgw_cn.h \
|
||||
hnbgw_hnbap.h hnbgw_rua.h hnbgw_ranap.h \
|
||||
ranap_rab_ass.h
|
||||
ranap_rab_ass.h mgw_fsm.h tdefs.h
|
||||
|
|
|
@ -35,6 +35,9 @@ struct hnbgw_context_map {
|
|||
uint32_t scu_conn_id;
|
||||
|
||||
enum hnbgw_context_map_state state;
|
||||
|
||||
/* FSM instance for the MGW */
|
||||
struct osmo_fsm_inst *mgw_fi;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ enum {
|
|||
DHNBAP,
|
||||
DRUA,
|
||||
DRANAP,
|
||||
DMGW,
|
||||
};
|
||||
|
||||
#define LOGHNB(x, ss, lvl, fmt, args ...) \
|
||||
|
@ -133,6 +134,7 @@ struct hnb_gw {
|
|||
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 hnb_gw {
|
|||
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;
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <osmocom/ranap/ranap_ies_defs.h>
|
||||
|
||||
int handle_rab_ass_req(struct hnbgw_context_map *map, struct osmo_prim_hdr *oph, ranap_message *message);
|
||||
int mgw_fsm_handle_rab_ass_resp(struct hnbgw_context_map *map, struct osmo_prim_hdr *oph, ranap_message *message);
|
||||
int mgw_fsm_release(struct hnbgw_context_map *map);
|
|
@ -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[];
|
|
@ -7,5 +7,6 @@ enum osmo_iuh_vty_node {
|
|||
IUH_NODE,
|
||||
IUCS_NODE,
|
||||
IUPS_NODE,
|
||||
MGCP_NODE,
|
||||
};
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ AM_CFLAGS = \
|
|||
$(LIBOSMORUA_CFLAGS) \
|
||||
$(LIBOSMORANAP_CFLAGS) \
|
||||
$(LIBOSMOHNBAP_CFLAGS) \
|
||||
$(LIBOSMOMGCPCLIENT_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
AM_LDFLAGS = \
|
||||
|
@ -38,6 +39,8 @@ osmo_hnbgw_SOURCES = \
|
|||
context_map.c \
|
||||
hnbgw_cn.c \
|
||||
ranap_rab_ass.c \
|
||||
mgw_fsm.c \
|
||||
tdefs.c \
|
||||
$(NULL)
|
||||
|
||||
osmo_hnbgw_LDADD = \
|
||||
|
@ -52,5 +55,7 @@ osmo_hnbgw_LDADD = \
|
|||
$(LIBOSMORUA_LIBS) \
|
||||
$(LIBOSMORANAP_LIBS) \
|
||||
$(LIBOSMOHNBAP_LIBS) \
|
||||
$(LIBOSMOMGCPCLIENT_LIBS) \
|
||||
$(LIBSCTP_LIBS) \
|
||||
$(LIBOSMOMGCPCLIENT_LIBS) \
|
||||
$(NULL)
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
|
||||
#include <osmocom/hnbgw/hnbgw.h>
|
||||
#include <osmocom/hnbgw/context_map.h>
|
||||
#include <osmocom/hnbgw/mgw_fsm.h>
|
||||
|
||||
const struct value_string hnbgw_context_map_state_names[] = {
|
||||
{MAP_S_NULL , "not-initialized"},
|
||||
|
@ -137,6 +138,13 @@ void context_map_deactivate(struct hnbgw_context_map *map)
|
|||
|
||||
if (map->state != MAP_S_RESERVED2)
|
||||
map->state = MAP_S_RESERVED1;
|
||||
|
||||
/* a possibly still existing MGW FSM must be terminated when the context
|
||||
* map is deactivated. (this is a cornercase) */
|
||||
if (map->mgw_fi) {
|
||||
mgw_fsm_release(map);
|
||||
OSMO_ASSERT(map->mgw_fi == NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static struct osmo_timer_list context_map_tmr;
|
||||
|
|
|
@ -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>
|
||||
|
@ -92,6 +94,9 @@ static struct hnb_gw *hnb_gw_create(void *ctx)
|
|||
|
||||
context_map_init(gw);
|
||||
|
||||
gw->config.mgcp_client = talloc_zero(tall_hnb_ctx, struct mgcp_client_conf);
|
||||
mgcp_client_conf_init(gw->config.mgcp_client);
|
||||
|
||||
return gw;
|
||||
}
|
||||
|
||||
|
@ -372,6 +377,11 @@ static const struct log_info_cat log_cat[] = {
|
|||
.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 +691,19 @@ int main(int argc, char **argv)
|
|||
}
|
||||
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) {
|
||||
|
|
|
@ -34,6 +34,11 @@
|
|||
#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>
|
||||
#include <osmocom/ranap/RANAP_ProcedureCode.h>
|
||||
#include <osmocom/ranap/ranap_common.h>
|
||||
#include <osmocom/ranap/ranap_common_ran.h>
|
||||
#include <osmocom/ranap/RANAP_RANAP-PDU.h>
|
||||
|
||||
/***********************************************************************
|
||||
* Outbound RANAP RESET to CN
|
||||
|
@ -345,10 +350,13 @@ static int handle_cn_data_ind(struct hnbgw_cnlink *cnlink,
|
|||
struct osmo_prim_hdr *oph)
|
||||
{
|
||||
struct hnbgw_context_map *map;
|
||||
ranap_message *message;
|
||||
int rc;
|
||||
|
||||
/* connection-oriented data is always passed transparently
|
||||
* towards the specific HNB, via a RUA connection identified by
|
||||
* conn_id */
|
||||
/* Usually connection-oriented data is always passed transparently towards the specific HNB, via a RUA
|
||||
* connection identified by conn_id. An exception is made for RANAP RAB AssignmentRequest and
|
||||
* RANAP RAB AssignmentResponse, since those messages contain transport layer information (RTP stream IP/Port),
|
||||
* which is rewritten by the FSM that controls the co-located media gateway. */
|
||||
|
||||
map = context_map_by_cn(cnlink, param->conn_id);
|
||||
if (!map) {
|
||||
|
@ -356,6 +364,28 @@ static int handle_cn_data_ind(struct hnbgw_cnlink *cnlink,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Intercept RAB Assignment Request, Setup MGW FSM */
|
||||
if (!map->is_ps) {
|
||||
message = talloc_zero(map, ranap_message);
|
||||
rc = ranap_ran_rx_co_decode(map, message, msgb_l2(oph->msg), msgb_l2len(oph->msg));
|
||||
|
||||
if (rc == 0) {
|
||||
switch (message->procedureCode) {
|
||||
case RANAP_ProcedureCode_id_RAB_Assignment:
|
||||
/* mgw_fsm_alloc_and_handle_rab_ass_req() takes ownership of (ranap) message */
|
||||
return handle_rab_ass_req(map, oph, message);
|
||||
case RANAP_ProcedureCode_id_Iu_Release:
|
||||
/* Any IU Release will terminate the MGW FSM, the message itsself is not passed to the
|
||||
* FSM code. It is just forwarded normally by the rua_tx_dt() call below. */
|
||||
mgw_fsm_release(map);
|
||||
break;
|
||||
}
|
||||
ranap_ran_rx_co_free(message);
|
||||
}
|
||||
|
||||
talloc_free(message);
|
||||
}
|
||||
|
||||
return rua_tx_dt(map->hnb_ctx, map->is_ps, map->rua_ctx_id,
|
||||
msgb_l2(oph->msg), msgb_l2len(oph->msg));
|
||||
}
|
||||
|
|
|
@ -38,6 +38,10 @@
|
|||
#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>
|
||||
#include <osmocom/ranap/ranap_common.h>
|
||||
#include <osmocom/ranap/ranap_common_cn.h>
|
||||
|
||||
static const char *cn_domain_indicator_to_str(RUA_CN_DomainIndicator_t cN_DomainIndicator)
|
||||
{
|
||||
|
@ -186,6 +190,7 @@ static int rua_to_scu(struct hnb_context *hnb,
|
|||
struct osmo_sccp_addr *remote_addr;
|
||||
bool is_ps;
|
||||
bool release_context_map = false;
|
||||
ranap_message *message;
|
||||
int rc;
|
||||
|
||||
switch (cN_DomainIndicator) {
|
||||
|
@ -265,6 +270,23 @@ static int rua_to_scu(struct hnb_context *hnb,
|
|||
memcpy(msg->l2h, data, len);
|
||||
}
|
||||
|
||||
/* Intercept RAB Assignment Response, inform MGW FSM. */
|
||||
if (map && !map->is_ps && !release_context_map) {
|
||||
message = talloc_zero(map, ranap_message);
|
||||
rc = ranap_cn_rx_co_decode(map, message, msgb_l2(prim->oph.msg), msgb_l2len(prim->oph.msg));
|
||||
|
||||
if (rc == 0) {
|
||||
switch (message->procedureCode) {
|
||||
case RANAP_ProcedureCode_id_RAB_Assignment:
|
||||
/* mgw_fsm_handle_rab_ass_resp() takes ownership of prim->oph and (ranap) message */
|
||||
return mgw_fsm_handle_rab_ass_resp(map, &prim->oph, message);
|
||||
}
|
||||
ranap_cn_rx_co_free(message);
|
||||
}
|
||||
|
||||
talloc_free(message);
|
||||
}
|
||||
|
||||
rc = osmo_sccp_user_sap_down(cn->sccp_user, &prim->oph);
|
||||
|
||||
if (map && release_context_map)
|
||||
|
|
|
@ -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 @@ DEFUN(cfg_hnbgw_iups, cfg_hnbgw_iups_cmd,
|
|||
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 @@ int hnbgw_vty_go_parent(struct vty *vty)
|
|||
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 @@ static int config_write_hnbgw_iups(struct vty *vty)
|
|||
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,10 @@ void hnbgw_vty_init(struct hnb_gw *gw, void *tall_ctx)
|
|||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,781 @@
|
|||
/* (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_common_cn.h>
|
||||
#include <osmocom/ranap/ranap_common_ran.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>
|
||||
|
||||
/* NOTE: This implementation can only handle one RAB per hnbgw context. This simplification was made because usually
|
||||
* a voice call will require only one RAB at a time. An exception may be corner cases like video calls, which we
|
||||
* do not support at the moment. */
|
||||
|
||||
/* Send Iu Release Request, this is done in erroneous cases from which we cannot recover */
|
||||
static void tx_release_req(struct hnbgw_context_map *map)
|
||||
{
|
||||
struct hnb_context *hnb = map->hnb_ctx;
|
||||
struct hnbgw_cnlink *cn = hnb->gw->sccp.cnlink;
|
||||
struct msgb *msg;
|
||||
struct osmo_scu_prim *prim;
|
||||
static const struct RANAP_Cause cause = {
|
||||
.present = RANAP_Cause_PR_transmissionNetwork,
|
||||
.choice.transmissionNetwork =
|
||||
RANAP_CauseTransmissionNetwork_iu_transport_connection_failed_to_establish,
|
||||
};
|
||||
|
||||
msg = ranap_new_msg_iu_rel_req(&cause);
|
||||
msg->l2h = msg->data;
|
||||
|
||||
prim = (struct osmo_scu_prim *)msgb_push(msg, sizeof(*prim));
|
||||
prim->u.data.conn_id = map->scu_conn_id;
|
||||
osmo_prim_init(&prim->oph, SCCP_SAP_USER, OSMO_SCU_PRIM_N_DATA, PRIM_OP_REQUEST, msg);
|
||||
osmo_sccp_user_sap_down(cn->sccp_user, &prim->oph);
|
||||
}
|
||||
|
||||
#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,
|
||||
MGW_ST_FAILURE,
|
||||
};
|
||||
|
||||
struct mgw_fsm_priv {
|
||||
/* Backpointer to HNBGW context */
|
||||
struct hnbgw_context_map *map;
|
||||
|
||||
/* RAB-ID from RANAP RAB AssignmentRequest message */
|
||||
uint8_t rab_id;
|
||||
|
||||
/* Pointers to messages and prim header we take ownership of */
|
||||
ranap_message *ranap_rab_ass_req_message;
|
||||
ranap_message *ranap_rab_ass_resp_message;
|
||||
struct osmo_prim_hdr *ranap_rab_ass_resp_oph;
|
||||
|
||||
/* 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;
|
||||
struct osmo_sockaddr addr;
|
||||
struct osmo_sockaddr_str addr_str;
|
||||
RANAP_RAB_AssignmentRequestIEs_t *ies;
|
||||
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_FAILURE, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (strchr(mgw_info->addr, '.'))
|
||||
addr_str.af = AF_INET;
|
||||
else
|
||||
addr_str.af = AF_INET6;
|
||||
addr_str.port = mgw_info->port;
|
||||
osmo_strlcpy(addr_str.ip, mgw_info->addr, sizeof(addr_str.ip));
|
||||
rc = osmo_sockaddr_str_to_sockaddr(&addr_str, &addr.u.sas);
|
||||
if (rc < 0) {
|
||||
LOGPFSML(fi, LOGL_ERROR,
|
||||
"Failed to convert RTP IP-address (%s) and Port (%u) to its binary representation\n",
|
||||
mgw_info->addr, mgw_info->port);
|
||||
osmo_fsm_inst_state_chg(fi, MGW_ST_FAILURE, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
ies = &mgw_fsm_priv->ranap_rab_ass_req_message->msg.raB_AssignmentRequestIEs;
|
||||
rc = ranap_rab_ass_req_ies_replace_inet_addr(ies, &addr, mgw_fsm_priv->rab_id);
|
||||
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_FAILURE, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
mgw_fsm_state_chg(MGW_ST_ASSIGN);
|
||||
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;
|
||||
uint8_t encoded[IUH_MSGB_SIZE];
|
||||
RANAP_RAB_AssignmentRequestIEs_t *ies;
|
||||
int rc;
|
||||
|
||||
ies = &mgw_fsm_priv->ranap_rab_ass_req_message->msg.raB_AssignmentRequestIEs;
|
||||
rc = ranap_rab_ass_req_encode(encoded, sizeof(encoded), ies);
|
||||
if (rc < 0) {
|
||||
LOGPFSML(fi, LOGL_ERROR, "failed to re-encode RAB-AssignmentRequest message\n");
|
||||
osmo_fsm_inst_state_chg(fi, MGW_ST_FAILURE, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
LOGPFSML(fi, LOGL_DEBUG, "forwarding modified RAB-AssignmentRequest to HNB\n");
|
||||
rua_tx_dt(map->hnb_ctx, map->is_ps, map->rua_ctx_id, encoded, rc);
|
||||
}
|
||||
|
||||
static void mgw_fsm_assign(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
switch (event) {
|
||||
case MGW_EV_RAB_ASS_RESP:
|
||||
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 hnb_context *hnb = map->hnb_ctx;
|
||||
struct hnbgw_cnlink *cn = hnb->gw->sccp.cnlink;
|
||||
struct mgcp_conn_peer mgw_info;
|
||||
struct osmo_sockaddr addr;
|
||||
struct osmo_sockaddr_str addr_str;
|
||||
RANAP_RAB_AssignmentResponseIEs_t *ies;
|
||||
int rc;
|
||||
bool rab_failed_at_hnb;
|
||||
|
||||
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;
|
||||
|
||||
ies = &mgw_fsm_priv->ranap_rab_ass_resp_message->msg.raB_AssignmentResponseIEs;
|
||||
rc = ranap_rab_ass_resp_ies_extract_inet_addr(&addr, ies, mgw_fsm_priv->rab_id);
|
||||
if (rc < 0) {
|
||||
rab_failed_at_hnb = ranap_rab_ass_resp_ies_check_failure(ies, mgw_fsm_priv->rab_id);
|
||||
if (rab_failed_at_hnb) {
|
||||
LOGPFSML(fi, LOGL_ERROR,
|
||||
"The RAB-AssignmentResponse contains a RAB-FailedList, RAB-Assignment (%u) failed.\n",
|
||||
mgw_fsm_priv->rab_id);
|
||||
|
||||
/* Forward the RAB-AssignmentResponse transparently. This will ensure that the MSC is informed
|
||||
* about the problem. */
|
||||
LOGPFSML(fi, LOGL_DEBUG, "forwarding unmodified RAB-AssignmentResponse to MSC\n");
|
||||
rc = osmo_sccp_user_sap_down(cn->sccp_user, mgw_fsm_priv->ranap_rab_ass_resp_oph);
|
||||
mgw_fsm_priv->ranap_rab_ass_resp_oph = NULL;
|
||||
if (rc < 0) {
|
||||
LOGPFSML(fi, LOGL_DEBUG, "failed to forward RAB-AssignmentResponse message\n");
|
||||
osmo_fsm_inst_state_chg(fi, MGW_ST_FAILURE, 0, 0);
|
||||
}
|
||||
|
||||
/* Even though this is a failure situation, we still release normally as the error is located
|
||||
* at the HNB. */
|
||||
osmo_fsm_inst_state_chg(fi, MGW_ST_RELEASE, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
/* The RAB-ID we are dealing with is not on an FailedList and we were unable to parse the response
|
||||
* normally. This is a situation we cannot recover from. */
|
||||
LOGPFSML(fi, LOGL_ERROR, "Failed to extract RTP IP-address and Port from RAB-AssignmentResponse\n");
|
||||
osmo_fsm_inst_state_chg(fi, MGW_ST_FAILURE, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
rc = osmo_sockaddr_str_from_sockaddr(&addr_str, &addr.u.sas);
|
||||
if (rc < 0) {
|
||||
LOGPFSML(fi, LOGL_ERROR, "Invalid RTP IP-address or Port in RAB-AssignmentResponse\n");
|
||||
osmo_fsm_inst_state_chg(fi, MGW_ST_FAILURE, 0, 0);
|
||||
return;
|
||||
}
|
||||
osmo_strlcpy(mgw_info.addr, addr_str.ip, sizeof(mgw_info.addr));
|
||||
mgw_info.port = addr_str.port;
|
||||
|
||||
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_FAILURE, 0, 0);
|
||||
return;
|
||||
}
|
||||
mgw_fsm_state_chg(MGW_ST_CRCX_MSC);
|
||||
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;
|
||||
struct osmo_sockaddr addr;
|
||||
struct osmo_sockaddr_str addr_str;
|
||||
int rc;
|
||||
int msg_max_len;
|
||||
RANAP_RAB_AssignmentResponseIEs_t *ies;
|
||||
|
||||
switch (event) {
|
||||
case MGW_EV_MGCP_OK:
|
||||
ies = &mgw_fsm_priv->ranap_rab_ass_resp_message->msg.raB_AssignmentResponseIEs;
|
||||
|
||||
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_FAILURE, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Replace RTP IP-Address/Port in ranap message container */
|
||||
if (strchr(mgw_info->addr, '.'))
|
||||
addr_str.af = AF_INET;
|
||||
else
|
||||
addr_str.af = AF_INET6;
|
||||
addr_str.port = mgw_info->port;
|
||||
osmo_strlcpy(addr_str.ip, mgw_info->addr, sizeof(addr_str.ip));
|
||||
rc = osmo_sockaddr_str_to_sockaddr(&addr_str, &addr.u.sas);
|
||||
if (rc < 0) {
|
||||
LOGPFSML(fi, LOGL_ERROR,
|
||||
"Failed to convert RTP IP-address (%s) and Port (%u) to its binary representation\n",
|
||||
mgw_info->addr, mgw_info->port);
|
||||
osmo_fsm_inst_state_chg(fi, MGW_ST_FAILURE, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
rc = ranap_rab_ass_resp_ies_replace_inet_addr(ies, &addr, mgw_fsm_priv->rab_id);
|
||||
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_FAILURE, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
/* When the modified ranap message container is re-encoded, the resulting message might be larger then
|
||||
* the original message. 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->ranap_rab_ass_resp_oph->msg) +
|
||||
msgb_tailroom(mgw_fsm_priv->ranap_rab_ass_resp_oph->msg);
|
||||
rc = msgb_resize_area(mgw_fsm_priv->ranap_rab_ass_resp_oph->msg,
|
||||
mgw_fsm_priv->ranap_rab_ass_resp_oph->msg->l2h,
|
||||
msgb_l2len(mgw_fsm_priv->ranap_rab_ass_resp_oph->msg), msg_max_len);
|
||||
OSMO_ASSERT(rc == 0);
|
||||
|
||||
rc = ranap_rab_ass_resp_encode(msgb_l2(mgw_fsm_priv->ranap_rab_ass_resp_oph->msg),
|
||||
msgb_l2len(mgw_fsm_priv->ranap_rab_ass_resp_oph->msg), ies);
|
||||
if (rc < 0) {
|
||||
LOGPFSML(fi, LOGL_ERROR, "failed to re-encode RAB-AssignmentResponse message\n");
|
||||
osmo_fsm_inst_state_chg(fi, MGW_ST_FAILURE, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Resize l2h back to the actual message length */
|
||||
rc = msgb_resize_area(mgw_fsm_priv->ranap_rab_ass_resp_oph->msg,
|
||||
mgw_fsm_priv->ranap_rab_ass_resp_oph->msg->l2h,
|
||||
msgb_l2len(mgw_fsm_priv->ranap_rab_ass_resp_oph->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;
|
||||
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->ranap_rab_ass_resp_oph;
|
||||
struct hnb_context *hnb = map->hnb_ctx;
|
||||
struct hnbgw_cnlink *cn = hnb->gw->sccp.cnlink;
|
||||
int rc;
|
||||
|
||||
LOGPFSML(fi, LOGL_DEBUG, "forwarding modified RAB-AssignmentResponse to MSC\n");
|
||||
rc = osmo_sccp_user_sap_down(cn->sccp_user, oph);
|
||||
mgw_fsm_priv->ranap_rab_ass_resp_oph = NULL;
|
||||
if (rc < 0) {
|
||||
LOGPFSML(fi, LOGL_DEBUG, "failed to forward RAB-AssignmentResponse message\n");
|
||||
osmo_fsm_inst_state_chg(fi, MGW_ST_FAILURE, 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_failure_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
{
|
||||
struct mgw_fsm_priv *mgw_fsm_priv = fi->priv;
|
||||
tx_release_req(mgw_fsm_priv->map);
|
||||
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, 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_MGCP_TERM:
|
||||
mgw_fsm_priv->mgcpc_ep = NULL;
|
||||
LOGPFSML(fi, LOGL_ERROR, "Media gateway failed\n");
|
||||
osmo_fsm_inst_state_chg(fi, MGW_ST_FAILURE, 0, 0);
|
||||
return;
|
||||
case MGW_EV_MGCP_FAIL:
|
||||
LOGPFSML(fi, LOGL_ERROR, "Media gateway failed to switch RTP streams\n");
|
||||
osmo_fsm_inst_state_chg(fi, MGW_ST_FAILURE, 0, 0);
|
||||
return;
|
||||
case MGW_EV_RELEASE:
|
||||
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;
|
||||
}
|
||||
|
||||
static void mgw_fsm_priv_cleanup(struct mgw_fsm_priv *mgw_fsm_priv)
|
||||
{
|
||||
struct osmo_scu_prim *scu_prim;
|
||||
struct msgb *scu_msg;
|
||||
|
||||
if (mgw_fsm_priv->ranap_rab_ass_req_message) {
|
||||
ranap_ran_rx_co_free(mgw_fsm_priv->ranap_rab_ass_req_message);
|
||||
talloc_free(mgw_fsm_priv->ranap_rab_ass_req_message);
|
||||
mgw_fsm_priv->ranap_rab_ass_req_message = NULL;
|
||||
}
|
||||
|
||||
if (mgw_fsm_priv->ranap_rab_ass_resp_message) {
|
||||
ranap_cn_rx_co_free(mgw_fsm_priv->ranap_rab_ass_resp_message);
|
||||
talloc_free(mgw_fsm_priv->ranap_rab_ass_resp_message);
|
||||
mgw_fsm_priv->ranap_rab_ass_resp_message = NULL;
|
||||
}
|
||||
|
||||
if (mgw_fsm_priv->ranap_rab_ass_resp_oph) {
|
||||
scu_prim = (struct osmo_scu_prim *)mgw_fsm_priv->ranap_rab_ass_resp_oph;
|
||||
scu_msg = scu_prim->oph.msg;
|
||||
msgb_free(scu_msg);
|
||||
mgw_fsm_priv->ranap_rab_ass_resp_oph = NULL;
|
||||
}
|
||||
|
||||
talloc_free(mgw_fsm_priv);
|
||||
}
|
||||
|
||||
static void mgw_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
|
||||
{
|
||||
struct mgw_fsm_priv *mgw_fsm_priv = fi->priv;
|
||||
mgw_fsm_priv_cleanup(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 coming 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),
|
||||
.out_state_mask =
|
||||
S(MGW_ST_ASSIGN) |
|
||||
S(MGW_ST_FAILURE) |
|
||||
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_FAILURE) |
|
||||
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),
|
||||
.out_state_mask =
|
||||
S(MGW_ST_CRCX_MSC) |
|
||||
S(MGW_ST_FAILURE) |
|
||||
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),
|
||||
.out_state_mask =
|
||||
S(MGW_ST_ESTABLISHED) |
|
||||
S(MGW_ST_FAILURE) |
|
||||
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_FAILURE) |
|
||||
S(MGW_ST_RELEASE),
|
||||
},
|
||||
[MGW_ST_RELEASE] = {
|
||||
.name = "MGW_ST_RELEASE",
|
||||
.onenter = mgw_fsm_release_onenter,
|
||||
.in_event_mask = 0,
|
||||
.out_state_mask = 0,
|
||||
},
|
||||
[MGW_ST_FAILURE] = {
|
||||
.name = "MGW_ST_FAILURE",
|
||||
.onenter = mgw_fsm_failure_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) | S(MGW_EV_MGCP_FAIL),
|
||||
.timer_cb = mgw_fsm_timer_cb,
|
||||
.cleanup = mgw_fsm_cleanup,
|
||||
.pre_term = mgw_fsm_pre_term,
|
||||
};
|
||||
|
||||
/* The MSC may ask to release a specific RAB within a RAB-AssignmentRequest */
|
||||
static int handle_rab_release(struct hnbgw_context_map *map, struct osmo_prim_hdr *oph, ranap_message *message)
|
||||
{
|
||||
bool rab_release_req;
|
||||
struct osmo_fsm_inst *fi = map->mgw_fi;
|
||||
struct mgw_fsm_priv *mgw_fsm_priv = fi->priv;
|
||||
int rc;
|
||||
|
||||
/* Check if the RAB that is handled by this FSM is addressed by the release request */
|
||||
rab_release_req = ranap_rab_ass_req_ies_check_release(&message->msg.raB_AssignmentRequestIEs,
|
||||
mgw_fsm_priv->rab_id);
|
||||
if (!rab_release_req)
|
||||
return -EINVAL;
|
||||
|
||||
LOGPFSML(map->mgw_fi, LOGL_NOTICE, "MSC asked to release RAB-ID %u\n", mgw_fsm_priv->rab_id);
|
||||
|
||||
/* Forward the unmodifed RAB-AssignmentRequest to HNB, so that the HNB is informed about the RAB release as
|
||||
* well */
|
||||
LOGPFSML(fi, LOGL_DEBUG, "forwarding unmodified RAB-AssignmentRequest to HNB\n");
|
||||
rc = rua_tx_dt(map->hnb_ctx, map->is_ps, map->rua_ctx_id, msgb_l2(oph->msg), msgb_l2len(oph->msg));
|
||||
|
||||
/* Release the FSM normally */
|
||||
osmo_fsm_inst_state_chg(fi, MGW_ST_RELEASE, 0, 0);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*! 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 AssignmentResponse (function takes no ownership).
|
||||
* \ptmap[in] message ranap message container (function takes ownership).
|
||||
* \returns 0 on success; negative on error. */
|
||||
int handle_rab_ass_req(struct hnbgw_context_map *map, struct osmo_prim_hdr *oph, ranap_message *message)
|
||||
{
|
||||
static bool initialized = false;
|
||||
struct osmo_fsm_inst *fi;
|
||||
struct mgw_fsm_priv *mgw_fsm_priv;
|
||||
struct osmo_sockaddr addr;
|
||||
struct osmo_sockaddr_str addr_str;
|
||||
RANAP_RAB_AssignmentRequestIEs_t *ies;
|
||||
int rc;
|
||||
char fsm_name[255];
|
||||
|
||||
/* Initialize FSM if not done yet */
|
||||
if (!initialized) {
|
||||
OSMO_ASSERT(osmo_fsm_register(&mgw_fsm) == 0);
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
/* The RTP stream negortiation usually begins with a RAB-AssignmentRequest and ends with an IU-Release, however
|
||||
* it may also be thet the MSC decides to release the RAB with a dedicated RAB-AssignmentRequest that contains
|
||||
* a ReleaseList. In this case an FSM will already be present. */
|
||||
if (map->mgw_fi) {
|
||||
/* A RAB Release might be in progress, handle it */
|
||||
rc = handle_rab_release(map, oph, message);
|
||||
if (rc >= 0)
|
||||
return rc;
|
||||
|
||||
LOGPFSML(map->mgw_fi, LOGL_ERROR,
|
||||
"mgw_fsm_alloc_and_handle_rab_ass_req() unable to handle RAB-AssignmentRequest!\n");
|
||||
osmo_fsm_inst_state_chg(fi, MGW_ST_FAILURE, 0, 0);
|
||||
}
|
||||
|
||||
mgw_fsm_priv = talloc_zero(map, struct mgw_fsm_priv);
|
||||
mgw_fsm_priv->ranap_rab_ass_req_message = message;
|
||||
|
||||
/* 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. */
|
||||
ies = &mgw_fsm_priv->ranap_rab_ass_req_message->msg.raB_AssignmentRequestIEs;
|
||||
rc = ranap_rab_ass_req_ies_extract_inet_addr(&addr, &mgw_fsm_priv->rab_id, ies, 0);
|
||||
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);
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = osmo_sockaddr_str_from_sockaddr(&addr_str, &addr.u.sas);
|
||||
if (rc < 0) {
|
||||
LOGP(DMGW, LOGL_ERROR,
|
||||
"mgw_fsm_alloc_and_handle_rab_ass_req() rua_ctx_id=%d, Invalid RTP IP-address or Port in RAB-AssignmentRequest -- abort\n",
|
||||
map->rua_ctx_id);
|
||||
goto error;
|
||||
}
|
||||
osmo_strlcpy(mgw_fsm_priv->msc_rtp_addr, addr_str.ip, sizeof(mgw_fsm_priv->msc_rtp_addr));
|
||||
mgw_fsm_priv->msc_rtp_port = addr_str.port;
|
||||
|
||||
/* 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;
|
||||
error:
|
||||
/* TODO: If we fail in this early stage, we should generate an appropriate RAB AssignmentResponse to inform
|
||||
* the core network about the failure. */
|
||||
mgw_fsm_priv_cleanup(mgw_fsm_priv);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*! 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).
|
||||
* \ptmap[in] message ranap message container with decoded ranap message (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, ranap_message *message)
|
||||
{
|
||||
struct mgw_fsm_priv *mgw_fsm_priv;
|
||||
struct osmo_scu_prim *prim;
|
||||
struct msgb *msg;
|
||||
|
||||
OSMO_ASSERT(oph);
|
||||
|
||||
if (!map->mgw_fi) {
|
||||
/* NOTE: This situation is a corner-case. We may end up here when the co-located MGW caused a problem
|
||||
* on the way between RANAP RAB Assignment Request and RANAP RAB Assignment Response. */
|
||||
|
||||
LOGP(DMGW, LOGL_ERROR,
|
||||
"mgw_fsm_handle_rab_ass_resp() rua_ctx_id=%d, no MGW fsm -- sending Iu-Release-Request!\n",
|
||||
map->rua_ctx_id);
|
||||
|
||||
/* Cleanup ranap message */
|
||||
ranap_cn_rx_co_free(message);
|
||||
talloc_free(message);
|
||||
|
||||
/* Toss RAB-AssignmentResponse */
|
||||
prim = (struct osmo_scu_prim *)oph;
|
||||
msg = prim->oph.msg;
|
||||
msgb_free(msg);
|
||||
|
||||
/* Send a release request, to make sure that the MSC is aware of the problem. */
|
||||
tx_release_req(map);
|
||||
}
|
||||
|
||||
mgw_fsm_priv = map->mgw_fi->priv;
|
||||
mgw_fsm_priv->ranap_rab_ass_resp_oph = oph;
|
||||
mgw_fsm_priv->ranap_rab_ass_resp_message = message;
|
||||
osmo_fsm_inst_dispatch(map->mgw_fi, MGW_EV_RAB_ASS_RESP, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Release the FSM and clear its associated RTP streams.
|
||||
* \ptmap[in] map hanbgw context map that is responsible for this call.
|
||||
* \returns 0 on success; negative on error. */
|
||||
int mgw_fsm_release(struct hnbgw_context_map *map)
|
||||
{
|
||||
if (!map->mgw_fi) {
|
||||
LOGP(DMGW, LOGL_ERROR, "mgw_fsm_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;
|
||||
}
|
|
@ -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" },
|
||||
{ }
|
||||
};
|
Loading…
Reference in New Issue