diff --git a/configure.ac b/configure.ac index 520cd68..39c65c0 100644 --- a/configure.ac +++ b/configure.ac @@ -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 diff --git a/contrib/jenkins.sh b/contrib/jenkins.sh index 24607db..b43eac6 100755 --- a/contrib/jenkins.sh +++ b/contrib/jenkins.sh @@ -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="" diff --git a/contrib/osmo-hnbgw.spec.in b/contrib/osmo-hnbgw.spec.in index 1d30e56..0c6aac9 100644 --- a/contrib/osmo-hnbgw.spec.in +++ b/contrib/osmo-hnbgw.spec.in @@ -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 diff --git a/debian/control b/debian/control index d9ab85f..7d449c0 100644 --- a/debian/control +++ b/debian/control @@ -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), diff --git a/include/osmocom/hnbgw/Makefile.am b/include/osmocom/hnbgw/Makefile.am index 0ddd42e..2a75df8 100644 --- a/include/osmocom/hnbgw/Makefile.am +++ b/include/osmocom/hnbgw/Makefile.am @@ -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 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 @@ 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; }; 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 @@ 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; diff --git a/include/osmocom/hnbgw/mgw_fsm.h b/include/osmocom/hnbgw/mgw_fsm.h new file mode 100644 index 0000000..8b14eaa --- /dev/null +++ b/include/osmocom/hnbgw/mgw_fsm.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +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); 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 + +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 @@ enum osmo_iuh_vty_node { 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 @@ 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) diff --git a/src/osmo-hnbgw/context_map.c b/src/osmo-hnbgw/context_map.c index 09aa965..18f71ce 100644 --- a/src/osmo-hnbgw/context_map.c +++ b/src/osmo-hnbgw/context_map.c @@ -26,6 +26,7 @@ #include #include +#include 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; diff --git a/src/osmo-hnbgw/hnbgw.c b/src/osmo-hnbgw/hnbgw.c index da15bfc..833486c 100644 --- a/src/osmo-hnbgw/hnbgw.c +++ b/src/osmo-hnbgw/hnbgw.c @@ -52,6 +52,8 @@ #include #include +#include + #include #include @@ -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) { diff --git a/src/osmo-hnbgw/hnbgw_cn.c b/src/osmo-hnbgw/hnbgw_cn.c index 757c430..5ee5fd4 100644 --- a/src/osmo-hnbgw/hnbgw_cn.c +++ b/src/osmo-hnbgw/hnbgw_cn.c @@ -34,6 +34,11 @@ #include #include #include +#include +#include +#include +#include +#include /*********************************************************************** * 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)); } diff --git a/src/osmo-hnbgw/hnbgw_rua.c b/src/osmo-hnbgw/hnbgw_rua.c index 24ca167..1943ac0 100644 --- a/src/osmo-hnbgw/hnbgw_rua.c +++ b/src/osmo-hnbgw/hnbgw_rua.c @@ -38,6 +38,10 @@ #include #include #include +#include +#include +#include +#include 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) diff --git a/src/osmo-hnbgw/hnbgw_vty.c b/src/osmo-hnbgw/hnbgw_vty.c index 4ad1ddb..d064b7d 100644 --- a/src/osmo-hnbgw/hnbgw_vty.c +++ b/src/osmo-hnbgw/hnbgw_vty.c @@ -22,15 +22,19 @@ #include #include +#include #include #include #include +#include #include #include #include +#include + 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); } diff --git a/src/osmo-hnbgw/mgw_fsm.c b/src/osmo-hnbgw/mgw_fsm.c new file mode 100644 index 0000000..d4ef800 --- /dev/null +++ b/src/osmo-hnbgw/mgw_fsm.c @@ -0,0 +1,781 @@ +/* (C) 2021 by sysmocom s.f.m.c. GmbH + * 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 + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include + +/* 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; +} 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 + * 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 + +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" }, + { } +};