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_trunk.c b/src/libosmo-mgcp/mgcp_trunk.c
new file mode 100644
index 0000000..96c5318
--- /dev/null
+++ b/src/libosmo-mgcp/mgcp_trunk.c
@@ -0,0 +1,183 @@
+/* Trunk handling */
+
+/*
+ * (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2009-2012 by On-Waves
+ * (C) 2017-2020 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
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <osmocom/mgcp/mgcp_internal.h>
+#include <osmocom/mgcp/mgcp_endp.h>
+#include <osmocom/mgcp/mgcp_trunk.h>
+
+/*! allocate trunk and add it (if required) to the trunk list
+ *  (called once at startup by VTY)
+ *  \param[in] cfg mgcp configuration
+ *  \param[in] nr trunk number
+ *  \param[in] ttype trunk type
+ *  \returns pointer to allocated trunk, NULL on failure */
+struct mgcp_trunk *mgcp_trunk_alloc(struct mgcp_config *cfg, enum mgcp_trunk_type ttype, int nr)
+{
+	struct mgcp_trunk *trunk;
+
+	trunk = talloc_zero(cfg, struct mgcp_trunk);
+	if (!trunk) {
+		LOGP(DLMGCP, LOGL_ERROR, "Failed to allocate.\n");
+		return NULL;
+	}
+
+	trunk->cfg = cfg;
+	trunk->trunk_type = ttype;
+	trunk->trunk_nr = nr;
+
+	trunk->audio_send_ptime = 1;
+	trunk->audio_send_name = 1;
+	trunk->vty_number_endpoints = 33;
+	trunk->omit_rtcp = 0;
+
+	mgcp_trunk_set_keepalive(trunk, MGCP_KEEPALIVE_ONCE);
+
+	/* Note: Trunk Nr.0 is reserved as "virtual trunk",
+	 * it is not stored using a separate pointer and
+	 * not in the trunk list. */
+	if (nr > 0)
+		llist_add_tail(&trunk->entry, &cfg->trunks);
+
+        mgcp_ratectr_trunk_alloc(cfg, &trunk->ratectr);
+
+	return trunk;
+}
+
+/*! allocate endpoints and set default values.
+ *  (called once at startup by VTY)
+ *  \param[in] trunk trunk configuration
+ *  \returns 0 on success, -1 on failure */
+int mgcp_trunk_alloc_endpts(struct mgcp_trunk *trunk)
+{
+	int i;
+	char ep_name_buf[MGCP_ENDPOINT_MAXLEN];
+	struct mgcp_endpoint *endp;
+
+	/* Make sure the amount of requested endpoints does not execeed
+	 * sane limits. The VTY already limits the possible amount,
+	 * however miss-initalation of the struct or memory corruption
+	 * could still lead to an excessive allocation of endpoints, so
+	 * better stop early if that is the case. */
+	OSMO_ASSERT(trunk->vty_number_endpoints < 65534);
+
+	/* This function is called once on startup by the VTY to allocate the
+	 * endpoints. The number of endpoints must not change througout the
+	 * runtime of the MGW */
+	OSMO_ASSERT(trunk->number_endpoints == 0);
+	OSMO_ASSERT(trunk->endpoints == NULL);
+
+	/* allocate pointer array for the endpoints */
+	trunk->endpoints = _talloc_zero_array(trunk->cfg,
+					     sizeof(struct mgcp_endpoint *), trunk->vty_number_endpoints, "endpoints");
+	if (!trunk->endpoints)
+		return -1;
+
+	/* create endpoints */
+	for (i = 0; i < trunk->vty_number_endpoints; ++i) {
+		switch (trunk->trunk_type) {
+		case MGCP_TRUNK_VIRTUAL:
+			snprintf(ep_name_buf, sizeof(ep_name_buf), "%s%x@%s", MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, i,
+				 trunk->cfg->domain);
+			break;
+		case MGCP_TRUNK_E1:
+			/* FIXME: E1 trunk implementation is work in progress, this endpoint
+			 * name is incomplete (subslots) */
+			snprintf(ep_name_buf, sizeof(ep_name_buf), "%s-1/%x", MGCP_ENDPOINT_PREFIX_E1_TRUNK, i);
+			break;
+		default:
+			osmo_panic("Cannot allocate unimplemented trunk type %d! %s:%d\n",
+				   trunk->trunk_type, __FILE__, __LINE__);
+		}
+
+		endp = mgcp_endp_alloc(trunk, ep_name_buf);
+		if (!endp) {
+			talloc_free(trunk->endpoints);
+			return -1;
+		}
+		trunk->endpoints[i] = endp;
+	}
+
+	/* make the endpoints we just created available to the MGW code */
+	trunk->number_endpoints = trunk->vty_number_endpoints;
+
+	return 0;
+}
+
+/*! get trunk configuration by trunk number (index).
+ *  \param[in] cfg mgcp configuration
+ *  \param[in] index trunk number
+ *  \returns pointer to trunk configuration, NULL on error */
+struct mgcp_trunk *mgcp_trunk_by_num(const struct mgcp_config *cfg, int index)
+{
+	struct mgcp_trunk *trunk;
+
+	llist_for_each_entry(trunk, &cfg->trunks, entry)
+	    if (trunk->trunk_nr == index)
+		return trunk;
+
+	return NULL;
+}
+
+/*! Find a trunk by the trunk prefix in the endpoint name.
+ *  \param[in] epname endpoint name with trunk prefix to look up.
+ *  \param[in] cfg that contains the trunks where the endpoint is located.
+ *  \returns trunk or NULL if trunk was not found. */
+struct mgcp_trunk *mgcp_trunk_by_name(const struct mgcp_config *cfg, const char *epname)
+{
+	size_t prefix_len;
+	char epname_lc[MGCP_ENDPOINT_MAXLEN];
+
+	osmo_str_tolower_buf(epname_lc, sizeof(epname_lc), epname);
+	epname = epname_lc;
+
+	prefix_len = sizeof(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK) - 1;
+	if (strncmp(epname, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, prefix_len) == 0) {
+		return cfg->virt_trunk;
+	}
+
+	/* E1 trunks are not implemented yet, so we deny any request for an
+	 * e1 trunk for now. */
+	prefix_len = sizeof(MGCP_ENDPOINT_PREFIX_E1_TRUNK) - 1;
+	if (strncmp(epname, MGCP_ENDPOINT_PREFIX_E1_TRUNK, prefix_len) == 0) {
+		LOGP(DLMGCP, LOGL_ERROR,
+		     "endpoint name \"%s\" suggests an E1 trunk, but E1 trunks are not implemented in this version of osmo-mgw!\n", epname);
+		return NULL;
+	}
+
+	/* Earlier versions of osmo-mgw were accepting endpoint names
+	 * without trunk prefix. This is normally not allowed, each MGCP
+	 * request should supply an endpoint name with trunk prefix.
+	 * However in order to stay compatible with old versions of
+	 * osmo-bsc and osmo-msc we still accept endpoint names without
+	 * trunk prefix and just assume that the virtual trunk should
+	 * be selected. There is even a TTCN3 test for this, see also:
+	 * MGCP_Test.TC_crcx_noprefix */
+	if ((epname[0] >= '0' && epname[0] <= '9') || (epname[0] >= 'a' && epname[0] <= 'f')) {
+		LOGP(DLMGCP, LOGL_ERROR, "missing trunk prefix in endpoint name \"%s\", assuming trunk \"%s\"!\n", epname,
+		     MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK);
+		return cfg->virt_trunk;
+	}
+
+	LOGP(DLMGCP, LOGL_ERROR, "unable to find trunk for endpoint name \"%s\"!\n", epname);
+	return NULL;
+}