sccplite: Support multiple MGW in MGW pool

Before this patch, the MGW was selected at startup, and the MGCP data
was always forwarded to that same MGW.
If several MGW were configured in the MGW pool, then osmo-bsc would
select any of those from the pool, and start configured the BTS-side
connection on an endpoint in that MGW. However, when the MSC submitted
the MGCP encapsulated in IPA to the BSC, the BSC would always forward
the MGCP message to that same MGW selected at startup.
As a result, multiple MGWs configured with osmo-bsc using SCCPlite was
broken.

This commit fixes support for multiple MGWs by looking up the already
selected MGW (to setup the BTS-side conn on the endpoint), based on the
CIC (MGCP Endpoint) which was provided by the MSC upon AssignReq.

Related: OS#6189
Depends: libosmocore.git Change-Id Iee361d740845257fa62c9093e30e8079fa933827
Depends: osmo-mgw.git Change-Id I18d7bdf650c0ec87ae16ed4944aed9f495400137
Change-Id: Ia106a21b7692eb5b2ac3b5ac2b358bedbc3b9da6
This commit is contained in:
Pau Espin 2023-09-22 18:20:12 +02:00
parent eff19b55b0
commit eb5ac9dca4
3 changed files with 121 additions and 20 deletions

View File

@ -7,3 +7,4 @@
# If any interfaces have been added since the last public release: c:r:a + 1.
# If any interfaces have been removed or changed since the last public release: c:r:0.
#library what description / commit summary line
libosmocore > 1.9.0 working (compiling) OSMO_SOCKADDR_STR_FMT_ARGS_NOT_NULL

View File

@ -2,6 +2,7 @@
* SCCPlite MGCP handling
*
* (C) 2018 by Harald Welte <laforge@gnumonks.org>
* (C) 2023 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
@ -19,6 +20,15 @@
*
*/
#include <string.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/sockaddr_str.h>
#include <osmocom/mgcp_client/mgcp_client_endpoint_fsm.h>
#include <osmocom/mgcp_client/mgcp_client.h>
#include <osmocom/bsc/bsc_msc_data.h>
#include <osmocom/bsc/osmo_bsc.h>
#include <osmocom/bsc/gsm_data.h>
@ -46,22 +56,123 @@ static struct bsc_msc_data *msc_from_asp(struct osmo_ss7_asp *asp)
return osmo_msc_data_find(bsc_gsmnet, msc_nr);
}
/* negative on error, zero upon success */
static int parse_local_endpoint_name(char *buf, size_t buf_len, const char *data)
{
char line[1024];
char *epstart, *sep;
const char *start = data;
char *eol = strpbrk(start, "\r\n");
if (!eol)
return -1;
if (eol - start > sizeof(line))
return -1;
memcpy(line, start, eol - start);
line[eol - start] = '\0';
if (!(epstart = strchr(line, ' ')))
return -1;
epstart++;
/* epstart now points to trans */
if (!(epstart = strchr(epstart, ' ')))
return -1;
epstart++;
/* epstart now points to endpoint */
if (!(sep = strchr(epstart, '@')))
return -1;
if (sep - epstart >= buf_len)
return -1;
*sep = '\0';
osmo_strlcpy(buf, epstart, buf_len);
return 0;
}
/* We received an IPA-encapsulated MGCP message from a MSC. Transfers msg ownership. */
int bsc_sccplite_rx_mgcp(struct osmo_ss7_asp *asp, struct msgb *msg)
{
struct bsc_msc_data *msc;
struct gsm_subscriber_connection *conn;
char rcv_ep_local_name[1024];
struct osmo_sockaddr_str osa_str = {};
struct osmo_sockaddr osa = {};
socklen_t dest_len;
struct mgcp_client *mgcp_cli = NULL;
int rc;
LOGP(DMSC, LOGL_NOTICE, "%s: Received IPA-encapsulated MGCP: %s\n",
LOGP(DMSC, LOGL_INFO, "%s: Received IPA-encapsulated MGCP: %s\n",
osmo_ss7_asp_get_name(asp), msg->l2h);
msc = msc_from_asp(asp);
if (msc) {
/* we don't have a write queue here as we simply expect the socket buffers
* to be large enough to deal with whatever small/infrequent MGCP messages */
rc = send(msc->mgcp_ipa.ofd.fd, msgb_l2(msg), msgb_l2len(msg), 0);
} else
rc = 0;
msc = msc_from_asp(asp);
if (!msc) {
rc = 0;
goto free_msg_ret;
}
rc = parse_local_endpoint_name(rcv_ep_local_name, sizeof(rcv_ep_local_name), (const char *)msg->l2h);
if (rc < 0) {
LOGP(DMSC, LOGL_ERROR, "(%s:) Received IPA-encapsulated MGCP: Failed to parse CIC\n",
osmo_ss7_asp_get_name(asp));
goto free_msg_ret;
}
/* Lookup which conn attached to the MSC holds an MGW endpoint with the
* name Endpoint Number as the one provided in the MGCP msg we received
* from MSC. Since CIC are unique per MSC, that's the same MGW in the
* pool where we have to forward the MGCP message. */
llist_for_each_entry(conn, &bsc_gsmnet->subscr_conns, entry) {
const char *ep_local_name;
if (conn->sccp.msc != msc)
continue; /* Only conns belonging to this MSC */
if (!conn->user_plane.mgw_endpoint)
continue;
ep_local_name = osmo_mgcpc_ep_local_name(conn->user_plane.mgw_endpoint);
LOGPFSMSL(conn->fi, DMSC, LOGL_DEBUG, "ep_local_name='%s' vs rcv_ep_local_name='%s'\n",
ep_local_name ? : "(null)", rcv_ep_local_name);
if (!ep_local_name)
continue;
if (strcmp(ep_local_name, rcv_ep_local_name) != 0)
continue;
mgcp_cli = osmo_mgcpc_ep_client(conn->user_plane.mgw_endpoint);
if (!mgcp_cli)
continue;
break;
}
if (!mgcp_cli) {
LOGP(DMSC, LOGL_ERROR, "(%s:) Received IPA-encapsulated MGCP: Failed to find associated MGW\n",
osmo_ss7_asp_get_name(asp));
rc = 0;
goto free_msg_ret;
}
rc = osmo_sockaddr_str_from_str(&osa_str, mgcp_client_remote_addr_str(mgcp_cli),
mgcp_client_remote_port(mgcp_cli));
if (rc < 0) {
LOGP(DMSC, LOGL_ERROR, "(%s:) Received IPA-encapsulated MGCP: Failed to parse MGCP address %s:%u\n",
osmo_ss7_asp_get_name(asp), mgcp_client_remote_addr_str(mgcp_cli), mgcp_client_remote_port(mgcp_cli));
goto free_msg_ret;
}
LOGP(DMSC, LOGL_NOTICE, "%s: Forwarding IPA-encapsulated MGCP to MGW at " OSMO_SOCKADDR_STR_FMT "\n",
osmo_ss7_asp_get_name(asp), OSMO_SOCKADDR_STR_FMT_ARGS_NOT_NULL(&osa_str));
rc = osmo_sockaddr_str_to_sockaddr(&osa_str, &osa.u.sas);
if (rc < 0) {
LOGP(DMSC, LOGL_ERROR, "(%s:) Received IPA-encapsulated MGCP: Failed to parse MGCP address " OSMO_SOCKADDR_STR_FMT "\n",
osmo_ss7_asp_get_name(asp), OSMO_SOCKADDR_STR_FMT_ARGS_NOT_NULL(&osa_str));
goto free_msg_ret;
}
dest_len = osmo_sockaddr_size(&osa);
/* we don't have a write queue here as we simply expect the socket buffers
* to be large enough to deal with whatever small/infrequent MGCP messages */
rc = sendto(msc->mgcp_ipa.ofd.fd, msgb_l2(msg), msgb_l2len(msg), 0, &osa.u.sa, dest_len);
free_msg_ret:
msgb_free(msg);
return rc;
}

View File

@ -376,26 +376,15 @@ static const struct osmo_stat_item_group_desc msc_statg_desc = {
int osmo_bsc_msc_init(struct bsc_msc_data *msc)
{
struct gsm_network *net = msc->network;
struct mgcp_client *mgcp_cli;
int rc;
/* Everything below refers to SCCP-Lite MSC connections only. */
if (msc_is_aoip(msc))
return 0;
/* Note: MGW is preselected here at startup, which means currently
* osmo-bsc configured for SCCPLite doesn't support MGW pools with more
* than 1 MGW.
*/
mgcp_cli = mgcp_client_pool_get(net->mgw.mgw_pool);
OSMO_ASSERT(mgcp_cli);
rc = osmo_sock_init2_ofd(&msc->mgcp_ipa.ofd, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
msc->mgcp_ipa.local_addr, msc->mgcp_ipa.local_port,
mgcp_client_remote_addr_str(mgcp_cli),
mgcp_client_remote_port(mgcp_cli),
OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
mgcp_client_pool_put(mgcp_cli);
NULL, 0, OSMO_SOCK_F_BIND);
if (rc < 0) {
LOGP(DMSC, LOGL_ERROR, "msc %u: Could not create/connect/bind MGCP proxy socket: %d\n",
msc->nr, rc);