From 3f2c15f2752c883332011429c3a79f0e9ab98683 Mon Sep 17 00:00:00 2001 From: Philipp Maier Date: Thu, 22 Jul 2021 11:53:07 +0200 Subject: [PATCH] mgcp_client: allow to reset endpoints on startup Depending on the usecase of osmo_mpcg_client it may be helpful to send a DLCX to certain endpoints. Usually this would be a wildcarded endpoint that resets the entire trunk to drop lingering RTP flows which may still present after a restart/crash, but it might be also a group of specific endpoints. The user may specify an arbitrary amount of endpoints where the mgcp client will send a DLCX to. It does not matter if the endpoints are wildcarded or not. Change-Id: I47e7ff858d5067b46d52329be5f362ff61c0dff8 Related: SYS#5535 --- TODO-RELEASE | 1 + include/osmocom/mgcp_client/mgcp_client.h | 6 ++ .../mgcp_client/mgcp_client_internal.h | 6 ++ src/libosmo-mgcp-client/mgcp_client.c | 74 ++++++++++++++----- src/libosmo-mgcp-client/mgcp_client_vty.c | 68 +++++++++++++++++ 5 files changed, 135 insertions(+), 20 deletions(-) diff --git a/TODO-RELEASE b/TODO-RELEASE index add44f1ae..3b31cded1 100644 --- a/TODO-RELEASE +++ b/TODO-RELEASE @@ -25,3 +25,4 @@ # #library what description / commit summary line update dependency to libosmocore > 1.5.1 for our use of osmo_sock_set_dscp() +libosmo-mgcp-client struct mgcp_client_conf ABI breackage \ No newline at end of file diff --git a/include/osmocom/mgcp_client/mgcp_client.h b/include/osmocom/mgcp_client/mgcp_client.h index 02996a71e..e9fe0aecf 100644 --- a/include/osmocom/mgcp_client/mgcp_client.h +++ b/include/osmocom/mgcp_client/mgcp_client.h @@ -26,6 +26,12 @@ struct mgcp_client_conf { /* By default, we are always addressing the MGW with e.g. 'rtpbridge/123@mgw'. * If this is nonempty, the contained name will be used instead of 'mgw'. */ char endpoint_domain_name[MGCP_ENDPOINT_MAXLEN]; + + /* The user may configure certain endpoint names that are reset via DLCX + * on startup. Usually this will be one wildcarded endpoint e.g. + * 'rtpbridge/(wildcard)' or a number of specific E1 like e.g. + * 'ds/e1-0/s-3/su16-4' */ + struct llist_head reset_epnames; }; typedef unsigned int mgcp_trans_id_t; diff --git a/include/osmocom/mgcp_client/mgcp_client_internal.h b/include/osmocom/mgcp_client/mgcp_client_internal.h index b4b3b02fe..44b902283 100644 --- a/include/osmocom/mgcp_client/mgcp_client_internal.h +++ b/include/osmocom/mgcp_client/mgcp_client_internal.h @@ -4,6 +4,12 @@ #define MSGB_CB_MGCP_TRANS_ID 0 +/* Struct that holds one endpoint name */ +struct reset_ep { + struct llist_head list; + char name[MGCP_ENDPOINT_MAXLEN]; +}; + struct mgcp_client { struct mgcp_client_conf actual; struct osmo_wqueue wq; diff --git a/src/libosmo-mgcp-client/mgcp_client.c b/src/libosmo-mgcp-client/mgcp_client.c index 69d687bb7..440cd9647 100644 --- a/src/libosmo-mgcp-client/mgcp_client.c +++ b/src/libosmo-mgcp-client/mgcp_client.c @@ -200,6 +200,8 @@ void mgcp_client_conf_init(struct mgcp_client_conf *conf) .remote_addr = NULL, .remote_port = -1, }; + + INIT_LLIST_HEAD(&conf->reset_epnames); } static void mgcp_client_handle_response(struct mgcp_client *mgcp, @@ -753,6 +755,8 @@ struct mgcp_client *mgcp_client_init(void *ctx, struct mgcp_client_conf *conf) { struct mgcp_client *mgcp; + struct reset_ep *reset_ep; + struct reset_ep *actual_reset_ep; mgcp = talloc_zero(ctx, struct mgcp_client); if (!mgcp) @@ -783,6 +787,12 @@ struct mgcp_client *mgcp_client_init(void *ctx, } LOGP(DLMGCP, LOGL_NOTICE, "MGCP client: using endpoint domain '@%s'\n", mgcp_client_endpoint_domain(mgcp)); + INIT_LLIST_HEAD(&mgcp->actual.reset_epnames); + llist_for_each_entry(reset_ep, &conf->reset_epnames, list) { + actual_reset_ep = talloc_memdup(mgcp, reset_ep, sizeof(*reset_ep)); + llist_add_tail(&actual_reset_ep->list, &mgcp->actual.reset_epnames); + } + return mgcp; } @@ -822,13 +832,49 @@ static int init_socket(struct mgcp_client *mgcp) return -EINVAL; } -/*! Initalize client connection (opens socket only, no request is sent yet) +/* Safely ignore the MGCP response to the DLCX sent via _mgcp_client_send_dlcx() */ +static void _ignore_mgcp_response(struct mgcp_response *response, void *priv) { } + +/* Format DLCX message (fire and forget) and send it off to the MGW */ +static void _mgcp_client_send_dlcx(struct mgcp_client *mgcp, const char *epname) +{ + struct msgb *msgb_dlcx; + struct mgcp_msg mgcp_msg_dlcx = { + .verb = MGCP_VERB_DLCX, + .presence = MGCP_MSG_PRESENCE_ENDPOINT, + }; + osmo_strlcpy(mgcp_msg_dlcx.endpoint, epname, sizeof(mgcp_msg_dlcx.endpoint)); + msgb_dlcx = mgcp_msg_gen(mgcp, &mgcp_msg_dlcx); + mgcp_client_tx(mgcp, msgb_dlcx, &_ignore_mgcp_response, NULL); +} + +static const char *_mgcp_client_name_append_domain(const struct mgcp_client *mgcp, const char *name) +{ + static char endpoint[MGCP_ENDPOINT_MAXLEN]; + int rc; + + rc = snprintf(endpoint, sizeof(endpoint), "%s@%s", name, mgcp_client_endpoint_domain(mgcp)); + if (rc > sizeof(endpoint) - 1) { + LOGP(DLMGCP, LOGL_ERROR, "MGCP endpoint exceeds maximum length of %zu: '%s@%s'\n", + sizeof(endpoint) - 1, name, mgcp_client_endpoint_domain(mgcp)); + return NULL; + } + if (rc < 1) { + LOGP(DLMGCP, LOGL_ERROR, "Cannot compose MGCP endpoint name\n"); + return NULL; + } + return endpoint; +} + +/*! Initialize client connection (opens socket) * \param[in,out] mgcp MGCP client descriptor. * \returns 0 on success, -EINVAL on error. */ int mgcp_client_connect(struct mgcp_client *mgcp) { struct osmo_wqueue *wq; int rc; + struct reset_ep *reset_ep; + const char *epname; if (!mgcp) { LOGP(DLMGCP, LOGL_FATAL, "MGCPGW client not initialized properly\n"); @@ -851,9 +897,15 @@ int mgcp_client_connect(struct mgcp_client *mgcp) goto error_close_fd; } - LOGP(DLMGCP, LOGL_INFO, "MGCP GW connection: %s\n", osmo_sock_get_name2(wq->bfd.fd)); + /* If configured, send a DLCX message to the endpoints that are configured to + * be reset on startup. Usually this is a wildcarded endpoint. */ + llist_for_each_entry(reset_ep, &mgcp->actual.reset_epnames, list) { + epname = _mgcp_client_name_append_domain(mgcp, reset_ep->name); + LOGP(DLMGCP, LOGL_INFO, "MGCP GW sending DLCX to: %s\n", epname); + _mgcp_client_send_dlcx(mgcp, epname); + } return 0; error_close_fd: close(wq->bfd.fd); @@ -893,24 +945,6 @@ const char *mgcp_client_endpoint_domain(const struct mgcp_client *mgcp) return mgcp->actual.endpoint_domain_name[0] ? mgcp->actual.endpoint_domain_name : "mgw"; } -static const char *_mgcp_client_name_append_domain(const struct mgcp_client *mgcp, char *name) -{ - static char endpoint[MGCP_ENDPOINT_MAXLEN]; - int rc; - - rc = snprintf(endpoint, sizeof(endpoint), "%s@%s", name, mgcp_client_endpoint_domain(mgcp)); - if (rc > sizeof(endpoint) - 1) { - LOGP(DLMGCP, LOGL_ERROR, "MGCP endpoint exceeds maximum length of %zu: '%s@%s'\n", - sizeof(endpoint) - 1, name, mgcp_client_endpoint_domain(mgcp)); - return NULL; - } - if (rc < 1) { - LOGP(DLMGCP, LOGL_ERROR, "Cannot compose MGCP endpoint name\n"); - return NULL; - } - return endpoint; -} - /*! Compose endpoint name for a wildcarded request to the virtual trunk * \param[in] mgcp MGCP client descriptor. * \returns string containing the endpoint name (e.g. rtpbridge\*@mgw) */ diff --git a/src/libosmo-mgcp-client/mgcp_client_vty.c b/src/libosmo-mgcp-client/mgcp_client_vty.c index cc8db5d85..1126c5cd8 100644 --- a/src/libosmo-mgcp-client/mgcp_client_vty.c +++ b/src/libosmo-mgcp-client/mgcp_client_vty.c @@ -29,6 +29,7 @@ #include #include +#include #define MGW_STR MGCP_CLIENT_MGW_STR @@ -155,10 +156,72 @@ DEFUN(cfg_mgw_endpoint_domain_name, return CMD_SUCCESS; } +DEFUN(cfg_mgw_reset_ep_name, + cfg_mgw_reset_ep_name_cmd, + "mgw reset-endpoint NAME", + MGW_STR "Add an endpoint name that should be reset (DLCX) on connect to the reset-endpoint list," + "e.g. 'rtpbridge/*'\n" + "Endpoint name, e.g. 'rtpbridge/*' or 'ds/e1-0/s-3/su16-4'.\n") +{ + int rc; + struct reset_ep *reset_ep; + + /* stop when the address is already in the list */ + llist_for_each_entry(reset_ep, &global_mgcp_client_conf->reset_epnames, list) { + if (strcmp(argv[0], reset_ep->name) == 0) { + vty_out(vty, "%% duplicate endpoint name configured ('%s')%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + } + + /* the domain name is not part of the actual endpoint name */ + if (strchr(argv[0], '@')) { + vty_out(vty, "%% the endpoint name must be given without domain name ('%s')%s", + argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + reset_ep = talloc_zero(global_mgcp_client_ctx, struct reset_ep); + OSMO_ASSERT(reset_ep); + + rc = osmo_strlcpy(reset_ep->name, argv[0], sizeof(reset_ep->name)); + if (rc >= sizeof(reset_ep->name)) { + vty_out(vty, "%% Error: 'mgw reset-endpoint' name too long, max length is %zu: '%s'%s", + sizeof(reset_ep->name) - 1, argv[0], VTY_NEWLINE); + talloc_free(reset_ep); + return CMD_WARNING; + } + + llist_add_tail(&reset_ep->list, &global_mgcp_client_conf->reset_epnames); + + return CMD_SUCCESS; +} + +DEFUN(cfg_mgw_no_reset_ep_name, + cfg_mgw_no_reset_ep_name_cmd, + "no mgw reset-endpoint NAME", + MGW_STR "remove an endpoint name from the reset-endpoint list, e.g. 'rtpbridge/*'\n" + "Endpoint name, e.g. 'rtpbridge/*' or 'ds/e1-0/s-3/su16-4'.\n") +{ + struct reset_ep *reset_ep; + + llist_for_each_entry(reset_ep, &global_mgcp_client_conf->reset_epnames, list) { + if (strcmp(argv[0], reset_ep->name) == 0) { + llist_del(&reset_ep->list); + talloc_free(reset_ep); + return CMD_SUCCESS; + } + } + + vty_out(vty, "%% no such endpoint name configured ('%s')%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; +} + int mgcp_client_config_write(struct vty *vty, const char *indent) { const char *addr; int port; + struct reset_ep *reset_ep; addr = global_mgcp_client_conf->local_addr; if (addr) @@ -182,6 +245,9 @@ int mgcp_client_config_write(struct vty *vty, const char *indent) vty_out(vty, "%smgw endpoint-domain %s%s", indent, global_mgcp_client_conf->endpoint_domain_name, VTY_NEWLINE); + llist_for_each_entry(reset_ep, &global_mgcp_client_conf->reset_epnames, list) + vty_out(vty, "%smgw reset-endpoint %s%s", indent, reset_ep->name, VTY_NEWLINE); + return CMD_SUCCESS; } @@ -197,6 +263,8 @@ void mgcp_client_vty_init(void *talloc_ctx, int node, struct mgcp_client_conf *c install_lib_element(node, &cfg_mgw_endpoint_range_cmd); install_lib_element(node, &cfg_mgw_rtp_bts_base_port_cmd); install_lib_element(node, &cfg_mgw_endpoint_domain_name_cmd); + install_lib_element(node, &cfg_mgw_reset_ep_name_cmd); + install_lib_element(node, &cfg_mgw_no_reset_ep_name_cmd); /* deprecated 'mgcpgw' commands */ install_lib_element(node, &cfg_mgcpgw_local_ip_cmd);