From ccbd41251265b550acc084240e5b16dd9f1536b7 Mon Sep 17 00:00:00 2001 From: Pau Espin Pedrol Date: Mon, 17 Oct 2022 18:09:15 +0200 Subject: [PATCH] Introduce support for libosmo-mgcp-client MGW pooling Large RAN installations may benefit from distributing the RTP voice stream load over multiple media gateways. libosmo-mgcp-client supports MGW pooling since version 1.8.0 (more than one year ago). OsmoBSC has already been making use of it since then (see osmo-bsc.git 8d22e6870637ed6d392a8a77aeaebc51b23a8a50); lets use this feature in osmo-msc too. This commit is also part of a series of patches cleaning up libosmo-mgcp-client and slowly getting rid of the old non-mgw-pooled VTY configuration, in order to keep only 1 way to configure libosmo-mgcp-client through VTY. Related: SYS#5091 Related: SYS#5987 Change-Id: I7670ba56fe989706579224a364595fdd4b4708ff --- doc/examples/osmo-msc/osmo-msc.cfg | 7 +-- .../osmo-msc/osmo-msc_custom-sccp.cfg | 7 +-- doc/examples/osmo-msc/osmo-msc_multi-cs7.cfg | 7 +-- doc/manuals/chapters/running.adoc | 20 +++++++-- include/osmocom/msc/gsm_data.h | 5 ++- include/osmocom/msc/vty.h | 1 + src/libmsc/call_leg.c | 19 ++++++-- src/libmsc/msc_vty.c | 4 ++ src/osmo-msc/msc_main.c | 43 ++++++++++++++++--- tests/msc_vlr/msc_vlr_tests.c | 6 ++- tests/test_nodes.vty | 9 ++-- 11 files changed, 100 insertions(+), 28 deletions(-) diff --git a/doc/examples/osmo-msc/osmo-msc.cfg b/doc/examples/osmo-msc/osmo-msc.cfg index 83b2c6fec..2b0d98d80 100644 --- a/doc/examples/osmo-msc/osmo-msc.cfg +++ b/doc/examples/osmo-msc/osmo-msc.cfg @@ -12,10 +12,11 @@ network encryption a5 0 rrlp mode none mm info 1 + mgw 0 + mgw remote-ip 127.0.0.1 + mgw remote-port 2427 + mgw local-port 2728 msc - mgw remote-ip 127.0.0.1 - mgw remote-port 2427 - mgw local-port 2728 assign-tmsi auth-tuple-max-reuse-count 3 auth-tuple-reuse-on-error 1 diff --git a/doc/examples/osmo-msc/osmo-msc_custom-sccp.cfg b/doc/examples/osmo-msc/osmo-msc_custom-sccp.cfg index d949ef9a0..d014eddcb 100644 --- a/doc/examples/osmo-msc/osmo-msc_custom-sccp.cfg +++ b/doc/examples/osmo-msc/osmo-msc_custom-sccp.cfg @@ -12,6 +12,10 @@ network encryption a5 0 rrlp mode none mm info 1 + mgw 0 + mgw remote-ip 127.0.0.1 + mgw remote-port 2427 + mgw local-port 2728 cs7 instance 0 point-code 0.23.1 asp asp-clnt-OsmoMSC-A-Iu 2905 0 m3ua @@ -21,7 +25,4 @@ cs7 instance 0 msc cs7-instance-a 0 cs7-instance-iu 0 - mgw remote-ip 127.0.0.1 - mgw remote-port 2427 - mgw local-port 2728 assign-tmsi diff --git a/doc/examples/osmo-msc/osmo-msc_multi-cs7.cfg b/doc/examples/osmo-msc/osmo-msc_multi-cs7.cfg index cb1157db3..62c927095 100644 --- a/doc/examples/osmo-msc/osmo-msc_multi-cs7.cfg +++ b/doc/examples/osmo-msc/osmo-msc_multi-cs7.cfg @@ -12,6 +12,10 @@ network encryption a5 0 rrlp mode none mm info 1 + mgw 0 + mgw remote-ip 127.0.0.1 + mgw remote-port 2427 + mgw local-port 2728 cs7 instance 0 point-code 0.23.1 asp asp-clnt-OsmoMSC-A 2905 0 m3ua @@ -23,7 +27,4 @@ cs7 instance 1 msc cs7-instance-a 0 cs7-instance-iu 1 - mgw remote-ip 127.0.0.1 - mgw remote-port 2427 - mgw local-port 2728 assign-tmsi diff --git a/doc/manuals/chapters/running.adoc b/doc/manuals/chapters/running.adoc index 86653954e..4556e0143 100644 --- a/doc/manuals/chapters/running.adoc +++ b/doc/manuals/chapters/running.adoc @@ -149,11 +149,23 @@ default port for MGCP (2427) on local host (127.0.0.1). Here is an example configuration for a remote MGW: ---- -msc - mgw remote-ip 10.9.8.7 - mgw remote-port 2427 - mgw reset-endpoint rtpbridge/* <1> +network + mgw 0 + mgw remote-ip 10.9.8.7 + mgw remote-port 2427 + mgw reset-endpoint rtpbridge/* <1> ---- <1> The 'reset-endpoint' setting instructs the OsmoMGW to send a wildcarded DLCX to the media gateway. This helps to clear lingering calls from the media gateway when the OsmoMSC is restarted. + +[NOTE] +==== +Previous versions of OsmoMSC (1.9.0 and below) didn't have the 'mgw' VTY node and +hence didn't support the MGW pooling feature. Therefore, historically the MGW +related commands where placed under the `msc` VTY node. The MGW related commands +under the `msc` VTY are still parsed and used but its use is deprecated and +hence discouraged in favour of the new `mgw` node. Writing the config to a file +from within OsmoMSC will automatically convert the config to use the new `mgw` +node. +==== diff --git a/include/osmocom/msc/gsm_data.h b/include/osmocom/msc/gsm_data.h index 43944e723..1cf68207e 100644 --- a/include/osmocom/msc/gsm_data.h +++ b/include/osmocom/msc/gsm_data.h @@ -16,6 +16,7 @@ #include #include +#include #include #include @@ -216,7 +217,9 @@ struct gsm_network { struct { struct osmo_tdef *tdefs; struct mgcp_client_conf conf; - struct mgcp_client *client; + /* MGW pool, also includes the single MGCP client as fallback if no + * pool is configured. */ + struct mgcp_client_pool *mgw_pool; } mgw; struct { diff --git a/include/osmocom/msc/vty.h b/include/osmocom/msc/vty.h index 1e13846ed..11d9ed113 100644 --- a/include/osmocom/msc/vty.h +++ b/include/osmocom/msc/vty.h @@ -17,6 +17,7 @@ extern struct cmd_element cfg_no_description_cmd; enum bsc_vty_node { GSMNET_NODE = _LAST_OSMOVTY_NODE + 1, + MGW_NODE, SUBSCR_NODE, MSC_NODE, MNCC_INT_NODE, diff --git a/src/libmsc/call_leg.c b/src/libmsc/call_leg.c index e890f753a..f8a75c451 100644 --- a/src/libmsc/call_leg.c +++ b/src/libmsc/call_leg.c @@ -122,7 +122,13 @@ void call_leg_release(struct call_leg *cl) static void call_leg_mgw_endpoint_gone(struct call_leg *cl) { + struct mgcp_client *mgcp_client; int i; + + /* Put MGCP client back into MGW pool */ + mgcp_client = osmo_mgcpc_ep_client(cl->mgw_endpoint); + mgcp_client_pool_put(mgcp_client); + cl->mgw_endpoint = NULL; for (i = 0; i < ARRAY_SIZE(cl->rtp); i++) { if (!cl->rtp[i]) @@ -275,10 +281,17 @@ int call_leg_ensure_rtp_alloc(struct call_leg *cl, enum rtp_direction dir, uint3 if (cl->rtp[dir]) return 0; - if (!cl->mgw_endpoint) + if (!cl->mgw_endpoint) { + struct mgcp_client *mgcp_client = mgcp_client_pool_get(gsmnet->mgw.mgw_pool); + if (!mgcp_client) { + LOG_CALL_LEG(cl, LOGL_ERROR, + "cannot ensure MGW endpoint -- no MGW configured, check configuration!\n"); + return -ENODEV; + } cl->mgw_endpoint = osmo_mgcpc_ep_alloc(cl->fi, CALL_LEG_EV_MGW_ENDPOINT_GONE, - gsmnet->mgw.client, gsmnet->mgw.tdefs, cl->fi->id, - "%s", mgcp_client_rtpbridge_wildcard(gsmnet->mgw.client)); + mgcp_client, gsmnet->mgw.tdefs, cl->fi->id, + "%s", mgcp_client_rtpbridge_wildcard(mgcp_client)); + } if (!cl->mgw_endpoint) { LOG_CALL_LEG(cl, LOGL_ERROR, "failed to setup MGW endpoint\n"); return -EIO; diff --git a/src/libmsc/msc_vty.c b/src/libmsc/msc_vty.c index be05a95aa..357b9754b 100644 --- a/src/libmsc/msc_vty.c +++ b/src/libmsc/msc_vty.c @@ -2033,6 +2033,8 @@ void msc_vty_init(struct gsm_network *msc_network) install_element(GSMNET_NODE, &cfg_net_no_per_loc_upd_cmd); install_element(GSMNET_NODE, &cfg_net_call_wait_cmd); install_element(GSMNET_NODE, &cfg_net_no_call_wait_cmd); + mgcp_client_pool_vty_init(GSMNET_NODE, MGW_NODE, " ", msc_network->mgw.mgw_pool); + install_element(CONFIG_NODE, &cfg_msc_cmd); install_node(&msc_node, config_write_msc); @@ -2066,7 +2068,9 @@ void msc_vty_init(struct gsm_network *msc_network) /* Timer configuration commands (generic osmo_tdef API) */ osmo_tdef_vty_groups_init(MSC_NODE, msc_tdef_group); + /* Deprecated: Old MGCP config without pooling support in MSC node: */ mgcp_client_vty_init(msc_network, MSC_NODE, &msc_network->mgw.conf); + #ifdef BUILD_IU ranap_iu_vty_init(MSC_NODE, (enum ranap_nsap_addr_enc*)&msc_network->iu.rab_assign_addr_enc); #endif diff --git a/src/osmo-msc/msc_main.c b/src/osmo-msc/msc_main.c index 9cc412086..c6514c24d 100644 --- a/src/osmo-msc/msc_main.c +++ b/src/osmo-msc/msc_main.c @@ -256,6 +256,7 @@ struct gsm_network *msc_network_alloc(void *ctx, MSC_HLR_REMOTE_IP_DEFAULT); net->gsup_server_port = MSC_HLR_REMOTE_PORT_DEFAULT; + net->mgw.mgw_pool = mgcp_client_pool_alloc(net); mgcp_client_conf_init(&net->mgw.conf); net->call_waiting = true; net->lcls_permitted = false; @@ -546,6 +547,41 @@ extern void *tall_gsms_ctx; extern void *tall_call_ctx; extern void *tall_trans_ctx; +static int msc_mgw_setup(void) +{ + struct mgcp_client *mgcp_client_single; + unsigned int pool_members_initalized; + + /* Initialize MGW pool. This initalizes and connects all MGCP clients that are currently configured in + * the pool. Adding additional MGCP clients to the pool is possible but the user has to configure and + * (re)connect them manually from the VTY. */ + pool_members_initalized = mgcp_client_pool_connect(msc_network->mgw.mgw_pool); + if (pool_members_initalized) { + LOGP(DMSC, LOGL_NOTICE, + "MGW pool with %u pool members configured, (ignoring MGW configuration in VTY node 'msc').\n", + pool_members_initalized); + return 0; + } + + /* Initialize and connect a single MGCP client. This MGCP client will appear as the one and only pool + * member if there is no MGW pool configured. */ + LOGP(DMSC, LOGL_NOTICE, "No MGW pool configured, using MGW configuration in VTY node 'msc'\n"); + mgcp_client_single = mgcp_client_init(msc_network, &msc_network->mgw.conf); + if (!mgcp_client_single) { + LOGP(DMSC, LOGL_ERROR, "MGW (single) client initalization failed\n"); + return -EINVAL; + } + if (mgcp_client_connect(mgcp_client_single)) { + LOGP(DMSC, LOGL_ERROR, "MGW (single) connect failed at (%s:%u)\n", + msc_network->mgw.conf.remote_addr, + msc_network->mgw.conf.remote_port); + return -EINVAL; + } + mgcp_client_pool_register_single(msc_network->mgw.mgw_pool, mgcp_client_single); + + return 0; +} + int main(int argc, char **argv) { int rc; @@ -705,13 +741,8 @@ TODO: we probably want some of the _net_ ctrl commands from bsc_base_ctrl_cmds_i if (sms_queue_start(msc_network) != 0) return -1; - msc_network->mgw.client = mgcp_client_init( - msc_network, &msc_network->mgw.conf); - - if (mgcp_client_connect(msc_network->mgw.client)) { - fprintf(stderr, "MGCPGW connect failed\n"); + if (msc_mgw_setup() != 0) return 7; - } if (ss7_setup(tall_msc_ctx, &sccp_a, &sccp_iu)) { fprintf(stderr, "Setting up SCCP client failed.\n"); diff --git a/tests/msc_vlr/msc_vlr_tests.c b/tests/msc_vlr/msc_vlr_tests.c index 378f84ac8..3ef94f2f7 100644 --- a/tests/msc_vlr/msc_vlr_tests.c +++ b/tests/msc_vlr/msc_vlr_tests.c @@ -1136,6 +1136,7 @@ static void run_tests(int nr) struct gsm_network *test_net(void *ctx) { struct gsm_network *net = gsm_network_init(ctx, mncc_recv); + struct mgcp_client *client; net->gsup_server_addr_str = talloc_strdup(net, "no_gsup_server"); net->gsup_server_port = 0; @@ -1172,8 +1173,9 @@ struct gsm_network *test_net(void *ctx) net->mgw.tdefs = g_mgw_tdefs; mgcp_client_conf_init(&net->mgw.conf); net->mgw.tdefs = g_mgw_tdefs; - net->mgw.client = mgcp_client_init(net, &net->mgw.conf); - + net->mgw.mgw_pool = mgcp_client_pool_alloc(net); + client = mgcp_client_init(net, &net->mgw.conf); + mgcp_client_pool_register_single(net->mgw.mgw_pool, client); return net; } diff --git a/tests/test_nodes.vty b/tests/test_nodes.vty index 95b772dd5..675603bdd 100644 --- a/tests/test_nodes.vty +++ b/tests/test_nodes.vty @@ -27,6 +27,8 @@ OsmoMSC(config-net)# list no timezone call-waiting no call-waiting + mgw <0-255> + no mgw <0-255> OsmoMSC(config-net)# encryption? encryption Encryption options @@ -152,6 +154,10 @@ network authentication optional rrlp mode none mm info 1 + mgw 0 + mgw local-port 2728 + mgw remote-ip 127.0.0.1 + mgw remote-port 2427 msc mncc guard-timeout 180 ncss guard-timeout 30 @@ -160,9 +166,6 @@ msc ... auth-tuple-max-reuse-count 3 auth-tuple-reuse-on-error 1 - mgw local-port 2728 - mgw remote-ip 127.0.0.1 - mgw remote-port 2427 mncc-int default-codec tch-f fr default-codec tch-h hr