osmo-mgw: refactor endpoint and trunk handling
The trunk and endpoint handling in osmo-mgw is still very complex and
implemented in various places (mostly mgcp_protocol.c). Also we use
still integers for endpoint identification, which is not flexible enough
to address timeslots/subslots on an E1 trunk. Some refactoring is needed.
- get rid of integers as endpoint identifiers, use strings instead and
find the endpoint based on its string name on the trunk.
- identify the trunk based on the trunk prefix given in the endpoint
name.
- refactor trunk and endpoint allocation. Aggregate functionality in
in mgcp_endp.c and mgcp_trunk.c. Also remove non-reusable code that
relates to the still exisiting, but unfinished E1 trunk support.
- refactor rate counters, put them into a separate module and do no
longer allocate them per trunk. Allocate them globally instead.
Change-Id: Ia8cf4d6caf05a4e13f1f507dc68cbabb7e6239aa
Related: OS#2659
diff --git a/src/libosmo-mgcp/mgcp_endp.c b/src/libosmo-mgcp/mgcp_endp.c
index eec46bf..6c78de2 100644
--- a/src/libosmo-mgcp/mgcp_endp.c
+++ b/src/libosmo-mgcp/mgcp_endp.c
@@ -1,7 +1,7 @@
/* Endpoint types */
/*
- * (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
+ * (C) 2017-2020 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Philipp Maier
@@ -23,6 +23,7 @@
#include <osmocom/mgcp/mgcp_internal.h>
#include <osmocom/mgcp/mgcp_endp.h>
+#include <osmocom/mgcp/mgcp_trunk.h>
/* Endpoint typeset definition */
const struct mgcp_endpoint_typeset ep_typeset = {
@@ -32,6 +33,39 @@
.rtp.cleanup_cb = mgcp_cleanup_rtp_bridge_cb
};
+/*! allocate an endpoint and set default values.
+ * \param[in] trunk configuration.
+ * \param[in] name endpoint name.
+ * \returns endpoint on success, NULL on failure. */
+struct mgcp_endpoint *mgcp_endp_alloc(struct mgcp_trunk *trunk, char *name)
+{
+ struct mgcp_endpoint *endp;
+
+ endp = talloc_zero(trunk->endpoints, struct mgcp_endpoint);
+ if (!endp)
+ return NULL;
+
+ INIT_LLIST_HEAD(&endp->conns);
+ endp->cfg = trunk->cfg;
+ endp->trunk = trunk;
+ endp->name = talloc_strdup(endp, name);
+
+ switch (trunk->trunk_type) {
+ case MGCP_TRUNK_VIRTUAL:
+ endp->type = &ep_typeset.rtp;
+ break;
+ case MGCP_TRUNK_E1:
+ /* FIXME: Implement E1 allocation */
+ LOGP(DLMGCP, LOGL_FATAL, "E1 trunks not implemented!\n");
+ break;
+ default:
+ osmo_panic("Cannot allocate unimplemented trunk type %d! %s:%d\n",
+ trunk->trunk_type, __FILE__, __LINE__);
+ }
+
+ return endp;
+}
+
/*! release endpoint, all open connections are closed.
* \param[in] endp endpoint to release */
void mgcp_endp_release(struct mgcp_endpoint *endp)
@@ -53,3 +87,223 @@
endp->local_options.codec = NULL;
endp->wildcarded_req = false;
}
+
+/* Check if the endpoint name contains the prefix (e.g. "rtpbridge/" or
+ * "ds/e1-") and write the epname without the prefix back to the memory
+ * pointed at by epname. (per trunk the prefix is the same for all endpoints,
+ * so no ambiguity is introduced) */
+static void chop_epname_prefix(char *epname, const struct mgcp_trunk *trunk)
+{
+ size_t prefix_len;
+ switch (trunk->trunk_type) {
+ case MGCP_TRUNK_VIRTUAL:
+ prefix_len = sizeof(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK) - 1;
+ if (strncmp
+ (epname, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK,
+ prefix_len) == 0)
+ memmove(epname, epname + prefix_len,
+ strlen(epname) - prefix_len + 1);
+ return;
+ case MGCP_TRUNK_E1:
+ prefix_len = sizeof(MGCP_ENDPOINT_PREFIX_E1_TRUNK) - 1;
+ if (strncmp
+ (epname, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK,
+ prefix_len) == 0)
+ memmove(epname, epname + prefix_len,
+ strlen(epname) - prefix_len + 1);
+ return;
+ default:
+ OSMO_ASSERT(false);
+ }
+}
+
+/* Check if the endpoint name contains a suffix (e.g. "@mgw") and truncate
+ * epname by writing a '\0' char where the suffix starts. */
+static void chop_epname_suffix(char *epname, const struct mgcp_trunk *trunk)
+{
+ char *suffix_begin;
+
+ /* Endpoints on the virtual trunk may have a domain name that is
+ * followed after an @ character, this can be chopped off. All
+ * other supported trunk types do not have any suffixes that may
+ * be chopped off */
+ if (trunk->trunk_type == MGCP_TRUNK_VIRTUAL) {
+ suffix_begin = strchr(epname, '@');
+ if (!suffix_begin)
+ return;
+ *suffix_begin = '\0';
+ }
+}
+
+/* Convert all characters in epname to lowercase and strip trunk prefix and
+ * endpoint name suffix (domain name) from epname. The result is written to
+ * to the memory pointed at by epname_stripped. The expected size of the
+ * result is either equal or lower then the length of the input string
+ * (epname) */
+static void strip_epname(char *epname_stripped, const char *epname,
+ const struct mgcp_trunk *trunk)
+{
+ osmo_str_tolower_buf(epname_stripped, MGCP_ENDPOINT_MAXLEN, epname);
+ chop_epname_prefix(epname_stripped, trunk);
+ chop_epname_suffix(epname_stripped, trunk);
+}
+
+/* Go through the trunk and find a random free (no active calls) endpoint,
+ * this function is called when a wildcarded request is carried out, which
+ * means that it is up to the MGW to choose a random free endpoint. */
+static struct mgcp_endpoint *find_free_endpoint(const struct mgcp_trunk *trunk)
+{
+ struct mgcp_endpoint *endp;
+ unsigned int i;
+
+ for (i = 0; i < trunk->number_endpoints; i++) {
+ endp = trunk->endpoints[i];
+ if (endp->callid == NULL)
+ return endp;
+ }
+
+ return NULL;
+}
+
+/* Find an endpoint specified by its name. If the endpoint can not be found,
+ * return NULL */
+static struct mgcp_endpoint *find_specific_endpoint(const char *epname,
+ const struct mgcp_trunk *trunk)
+{
+ char epname_stripped[MGCP_ENDPOINT_MAXLEN];
+ char epname_stripped_endp[MGCP_ENDPOINT_MAXLEN];
+ struct mgcp_endpoint *endp;
+ unsigned int i;
+
+ /* Strip irrelevant information from the endpoint name */
+ strip_epname(epname_stripped, epname, trunk);
+
+ for (i = 0; i < trunk->number_endpoints; i++) {
+ endp = trunk->endpoints[i];
+ strip_epname(epname_stripped_endp, endp->name, trunk);
+ if (strcmp(epname_stripped_endp, epname_stripped) == 0)
+ return endp;
+ }
+
+ return NULL;
+}
+
+/*! Find an endpoint by its name on a specified trunk.
+ * \param[out] cause pointer to store cause code, can be NULL.
+ * \param[in] epname endpoint name to lookup.
+ * \param[in] trunk where the endpoint is located.
+ * \returns endpoint or NULL if endpoint was not found. */
+struct mgcp_endpoint *mgcp_endp_by_name_trunk(int *cause, const char *epname,
+ const struct mgcp_trunk *trunk)
+{
+ struct mgcp_endpoint *endp;
+
+ if (cause)
+ *cause = 0;
+
+ /* At the moment we only support a primitive ('*'-only) method of
+ * wildcarded endpoint searches that picks the next free endpoint on
+ * a trunk. */
+ if (strstr(epname, "*")) {
+ endp = find_free_endpoint(trunk);
+ if (endp) {
+ LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
+ "(trunk:%d) found free endpoint: %s\n",
+ trunk->trunk_nr, endp->name);
+ endp->wildcarded_req = true;
+ return endp;
+ }
+
+ LOGP(DLMGCP, LOGL_ERROR,
+ "(trunk:%d) Not able to find a free endpoint\n",
+ trunk->trunk_nr);
+ if (cause)
+ *cause = -403;
+ return NULL;
+ }
+
+ /* Find an endpoint by its name (if wildcarded request is not
+ * applicable) */
+ endp = find_specific_endpoint(epname, trunk);
+ if (endp) {
+ LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
+ "(trunk:%d) found endpoint: %s\n",
+ trunk->trunk_nr, endp->name);
+ endp->wildcarded_req = false;
+ return endp;
+ }
+
+ LOGP(DLMGCP, LOGL_ERROR,
+ "(trunk:%d) Not able to find specified endpoint: %s\n",
+ trunk->trunk_nr, epname);
+ if (cause)
+ *cause = -500;
+
+ return NULL;
+}
+
+/* Check if the domain name, which is supplied with the endpoint name
+ * matches the configuration. */
+static int check_domain_name(const char *epname, struct mgcp_config *cfg)
+{
+ char *domain_to_check;
+
+ domain_to_check = strstr(epname, "@");
+ if (!domain_to_check) {
+ LOGP(DLMGCP, LOGL_ERROR, "missing domain name in endpoint name \"%s\", expecting \"%s\"\n",
+ epname, cfg->domain);
+ return -EINVAL;
+ }
+
+ /* Accept any domain if configured as "*" */
+ if (!strcmp(cfg->domain, "*"))
+ return 0;
+
+ if (strcmp(domain_to_check+1, cfg->domain) != 0) {
+ LOGP(DLMGCP, LOGL_ERROR, "wrong domain name in endpoint name \"%s\", expecting \"%s\"\n",
+ epname, cfg->domain);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*! Find an endpoint by its name, search at all trunks.
+ * \param[out] cause, pointer to store cause code, can be NULL.
+ * \param[in] epname, must contain trunk prefix.
+ * \param[in] cfg, mgcp configuration (trunks).
+ * \returns endpoint or NULL if endpoint was not found. */
+struct mgcp_endpoint *mgcp_endp_by_name(int *cause, const char *epname,
+ struct mgcp_config *cfg)
+{
+ struct mgcp_trunk *trunk;
+ struct mgcp_endpoint *endp;
+ char epname_lc[MGCP_ENDPOINT_MAXLEN];
+
+ osmo_str_tolower_buf(epname_lc, sizeof(epname_lc), epname);
+ epname = epname_lc;
+
+ if (cause)
+ *cause = -500;
+
+ /* Identify the trunk where the endpoint is located */
+ trunk = mgcp_trunk_by_name(cfg, epname);
+ if (!trunk)
+ return NULL;
+
+ /* Virtual endpoints require a domain name (see RFC3435, section E.3) */
+ if (trunk->trunk_type == MGCP_TRUNK_VIRTUAL) {
+ if (check_domain_name(epname, cfg))
+ return NULL;
+ }
+
+ /* Identify the endpoint on the trunk */
+ endp = mgcp_endp_by_name_trunk(cause, epname, trunk);
+ if (!endp) {
+ return NULL;
+ }
+
+ if (cause)
+ *cause = 0;
+ return endp;
+}