Change in osmo-mgw[master]: osmo-mgw: refactor endpoint and trunk handling

This is merely a historical archive of years 2008-2021, before the migration to mailman3.

A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.

dexter gerrit-no-reply at lists.osmocom.org
Mon Jun 15 10:28:55 UTC 2020


dexter has submitted this change. ( https://gerrit.osmocom.org/c/osmo-mgw/+/18644 )

Change subject: osmo-mgw: refactor endpoint and trunk handling
......................................................................

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
---
M include/osmocom/mgcp/Makefile.am
M include/osmocom/mgcp/mgcp.h
M include/osmocom/mgcp/mgcp_common.h
M include/osmocom/mgcp/mgcp_endp.h
M include/osmocom/mgcp/mgcp_internal.h
A include/osmocom/mgcp/mgcp_ratectr.h
A include/osmocom/mgcp/mgcp_trunk.h
M src/libosmo-mgcp/Makefile.am
M src/libosmo-mgcp/mgcp_codec.c
M src/libosmo-mgcp/mgcp_conn.c
M src/libosmo-mgcp/mgcp_endp.c
M src/libosmo-mgcp/mgcp_msg.c
M src/libosmo-mgcp/mgcp_network.c
M src/libosmo-mgcp/mgcp_osmux.c
M src/libosmo-mgcp/mgcp_protocol.c
A src/libosmo-mgcp/mgcp_ratectr.c
M src/libosmo-mgcp/mgcp_sdp.c
A src/libosmo-mgcp/mgcp_trunk.c
M src/libosmo-mgcp/mgcp_vty.c
M src/osmo-mgw/mgw_main.c
M tests/mgcp/mgcp_test.c
21 files changed, 938 insertions(+), 628 deletions(-)

Approvals:
  Jenkins Builder: Verified
  pespin: Looks good to me, but someone else must approve
  laforge: Looks good to me, approved
  neels: Looks good to me, approved



diff --git a/include/osmocom/mgcp/Makefile.am b/include/osmocom/mgcp/Makefile.am
index 036b4ca..fb7654f 100644
--- a/include/osmocom/mgcp/Makefile.am
+++ b/include/osmocom/mgcp/Makefile.am
@@ -7,5 +7,7 @@
 	mgcp_sdp.h \
 	mgcp_codec.h \
 	mgcp_ctrl.h \
+	mgcp_trunk.h \
 	debug.h \
+	mgcp_ratectr.h \
 	$(NULL)
diff --git a/include/osmocom/mgcp/mgcp.h b/include/osmocom/mgcp/mgcp.h
index 538c907..3811991 100644
--- a/include/osmocom/mgcp/mgcp.h
+++ b/include/osmocom/mgcp/mgcp.h
@@ -34,6 +34,8 @@
 #include <sys/socket.h>
 #include <netinet/in.h>
 
+#include "mgcp_ratectr.h"
+
 #define RTP_PORT_DEFAULT_RANGE_START 16002
 #define RTP_PORT_DEFAULT_RANGE_END RTP_PORT_DEFAULT_RANGE_START + 64
 
@@ -59,8 +61,8 @@
 #define MGCP_POLICY_REJECT	5
 #define MGCP_POLICY_DEFER	6
 
-typedef int (*mgcp_change)(struct mgcp_trunk *cfg, int endpoint, int state);
-typedef int (*mgcp_policy)(struct mgcp_trunk *cfg, int endpoint, int state, const char *transactio_id);
+typedef int (*mgcp_change)(struct mgcp_endpoint *endp, int state);
+typedef int (*mgcp_policy)(struct mgcp_endpoint *endp, int state, const char *transaction_id);
 typedef int (*mgcp_reset)(struct mgcp_trunk *cfg);
 typedef int (*mgcp_rqnt)(struct mgcp_endpoint *endp, char tone);
 
@@ -177,57 +179,6 @@
 	MGCP_DLCX_DEFERRED_BY_POLICY,
 };
 
-struct mgcp_trunk {
-	struct llist_head entry;
-
-	struct mgcp_config *cfg;
-
-	int trunk_nr;
-	int trunk_type;
-
-	char *audio_fmtp_extra;
-	char *audio_name;
-	int audio_payload;
-	int audio_send_ptime;
-	int audio_send_name;
-	int audio_loop;
-
-	int no_audio_transcoding;
-
-	int omit_rtcp;
-	int keepalive_interval;
-
-	/* RTP patching */
-	int force_constant_ssrc; /* 0: don't, 1: once */
-	int force_aligned_timing;
-	bool rfc5993_hr_convert;
-
-	/* spec handling */
-	int force_realloc;
-
-	/* timer */
-	struct osmo_timer_list keepalive_timer;
-
-	/* When set, incoming RTP packets are not filtered
-	 * when ports and ip-address do not match (debug) */
-	int rtp_accept_all;
-
-	unsigned int number_endpoints;
-	int vty_number_endpoints;
-	struct mgcp_endpoint *endpoints;
-
-	/* Rate counter group which contains stats for generic MGCP events. */
-	struct rate_ctr_group *mgcp_general_ctr_group;
-	/* Rate counter group which contains stats for processed CRCX commands. */
-	struct rate_ctr_group *mgcp_crcx_ctr_group;
-	/* Rate counter group which contains stats for processed MDCX commands. */
-	struct rate_ctr_group *mgcp_mdcx_ctr_group;
-	/* Rate counter group which contains stats for processed DLCX commands. */
-	struct rate_ctr_group *mgcp_dlcx_ctr_group;
-	/* Rate counter group which aggregates stats of individual RTP connections. */
-	struct rate_ctr_group *all_rtp_conn_stats;
-};
-
 enum mgcp_role {
 	MGCP_BSC = 0,
 	MGCP_BSC_NAT,
@@ -295,6 +246,10 @@
 
 	/* osmocom CTRL interface */
 	struct ctrl_handle *ctrl;
+
+	/* global rate counters to measure the MGWs overall performance and
+	 * health */
+	struct mgcp_ratectr_global ratectr;
 };
 
 /* config management */
@@ -302,7 +257,6 @@
 int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg,
 		      enum mgcp_role role);
 int mgcp_vty_init(void);
-int mgcp_endpoints_allocate(struct mgcp_trunk *cfg);
 void mgcp_trunk_set_keepalive(struct mgcp_trunk *trunk, int interval);
 
 /*
@@ -311,7 +265,7 @@
 struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg);
 
 
-int mgcp_send_reset_ep(struct mgcp_endpoint *endp, int endpoint);
+int mgcp_send_reset_ep(struct mgcp_endpoint *endp);
 int mgcp_send_reset_all(struct mgcp_config *cfg);
 
 
diff --git a/include/osmocom/mgcp/mgcp_common.h b/include/osmocom/mgcp/mgcp_common.h
index a1bbb19..07d8d37 100644
--- a/include/osmocom/mgcp/mgcp_common.h
+++ b/include/osmocom/mgcp/mgcp_common.h
@@ -100,6 +100,10 @@
 /* A prefix to denote the virtual trunk (RTP on both ends) */
 #define MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK "rtpbridge/"
 
+/* A prefix to denote the e1 trunk
+ * (see also RFC3435 section E.2) */
+#define MGCP_ENDPOINT_PREFIX_E1_TRUNK "ds/e1-"
+
 /* Maximal number of payload types / codecs that can be negotiated via SDP at
  * at once. */
 #define MGCP_MAX_CODECS 10
diff --git a/include/osmocom/mgcp/mgcp_endp.h b/include/osmocom/mgcp/mgcp_endp.h
index 8fa8390..5737cd2 100644
--- a/include/osmocom/mgcp/mgcp_endp.h
+++ b/include/osmocom/mgcp/mgcp_endp.h
@@ -28,8 +28,8 @@
 struct mgcp_endpoint;
 
 #define LOGPENDP(endp, cat, level, fmt, args...) \
-LOGP(cat, level, "endpoint:0x%x " fmt, \
-     endp ? ENDPOINT_NUMBER(endp) : -1, \
+LOGP(cat, level, "endpoint:%s " fmt, \
+     endp ? endp->name : "none", \
      ## args)
 
 /* Callback type for RTP dispatcher functions
@@ -68,6 +68,9 @@
 /*! MGCP endpoint model */
 struct mgcp_endpoint {
 
+	/*! Unique endpoint name, used for addressing via MGCP */
+	char *name;
+
 	/*! Call identifier string (as supplied by the call agant) */
 	char *callid;
 
@@ -80,7 +83,7 @@
 	/*! Backpointer to the MGW configuration */
 	struct mgcp_config *cfg;
 
-	/*! Backpointer to the Trunk specific configuration */
+	/*! Backpointer to the trunk this endpoint belongs to */
 	struct mgcp_trunk *trunk;
 
 	/*! Endpoint properties (see above) */
@@ -100,7 +103,9 @@
 	uint32_t x_osmo_ign;
 };
 
-/*! Extract endpoint number for a given endpoint */
-#define ENDPOINT_NUMBER(endp) abs((int)(endp - endp->trunk->endpoints))
-
+struct mgcp_endpoint *mgcp_endp_alloc(struct mgcp_trunk *trunk, char *name);
 void mgcp_endp_release(struct mgcp_endpoint *endp);
+struct mgcp_endpoint *mgcp_endp_by_name_trunk(int *cause, const char *epname,
+					      const struct mgcp_trunk *trunk);
+struct mgcp_endpoint *mgcp_endp_by_name(int *cause, const char *epname,
+					struct mgcp_config *cfg);
diff --git a/include/osmocom/mgcp/mgcp_internal.h b/include/osmocom/mgcp/mgcp_internal.h
index b2e2210..174bfd9 100644
--- a/include/osmocom/mgcp/mgcp_internal.h
+++ b/include/osmocom/mgcp/mgcp_internal.h
@@ -282,9 +282,6 @@
 	return endpoint + 60;
 }
 
-struct mgcp_trunk *mgcp_trunk_alloc(struct mgcp_config *cfg, enum mgcp_trunk_type ttype, int index);
-struct mgcp_trunk *mgcp_trunk_num(struct mgcp_config *cfg, int index);
-
 char *get_lco_identifier(const char *options);
 int check_local_cx_options(void *ctx, const char *options);
 void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
diff --git a/include/osmocom/mgcp/mgcp_ratectr.h b/include/osmocom/mgcp/mgcp_ratectr.h
new file mode 100644
index 0000000..e05b70c
--- /dev/null
+++ b/include/osmocom/mgcp/mgcp_ratectr.h
@@ -0,0 +1,22 @@
+#pragma once
+
+/* NOTE: When adding counters, also the dump_ratectr_* routines in vty.c must be updated. */
+
+struct mgcp_ratectr_global {
+	/* Rate counter group which contains stats for generic MGCP events. */
+	struct rate_ctr_group *mgcp_general_ctr_group;
+};
+
+struct mgcp_ratectr_trunk {
+	/* Rate counter group which contains stats for processed CRCX commands. */
+	struct rate_ctr_group *mgcp_crcx_ctr_group;
+	/* Rate counter group which contains stats for processed MDCX commands. */
+	struct rate_ctr_group *mgcp_mdcx_ctr_group;
+	/* Rate counter group which contains stats for processed DLCX commands. */
+	struct rate_ctr_group *mgcp_dlcx_ctr_group;
+	/* Rate counter group which aggregates stats of individual RTP connections. */
+	struct rate_ctr_group *all_rtp_conn_stats;
+};
+
+int mgcp_ratectr_global_alloc(void *ctx, struct mgcp_ratectr_global *ratectr);
+int mgcp_ratectr_trunk_alloc(void *ctx, struct mgcp_ratectr_trunk *ratectr);
diff --git a/include/osmocom/mgcp/mgcp_trunk.h b/include/osmocom/mgcp/mgcp_trunk.h
new file mode 100644
index 0000000..436e39a
--- /dev/null
+++ b/include/osmocom/mgcp/mgcp_trunk.h
@@ -0,0 +1,49 @@
+#pragma once
+
+struct mgcp_trunk {
+	struct llist_head entry;
+
+	struct mgcp_config *cfg;
+
+	int trunk_nr;
+	int trunk_type;
+
+	char *audio_fmtp_extra;
+	char *audio_name;
+	int audio_payload;
+	int audio_send_ptime;
+	int audio_send_name;
+	int audio_loop;
+
+	int no_audio_transcoding;
+
+	int omit_rtcp;
+	int keepalive_interval;
+
+	/* RTP patching */
+	int force_constant_ssrc; /* 0: don't, 1: once */
+	int force_aligned_timing;
+	bool rfc5993_hr_convert;
+
+	/* spec handling */
+	int force_realloc;
+
+	/* timer */
+	struct osmo_timer_list keepalive_timer;
+
+	/* When set, incoming RTP packets are not filtered
+	 * when ports and ip-address do not match (debug) */
+	int rtp_accept_all;
+
+	unsigned int number_endpoints;
+	unsigned int vty_number_endpoints;
+	struct mgcp_endpoint **endpoints;
+
+	/* global rate counters to measure the trunks overall performance and health */
+	struct mgcp_ratectr_trunk ratectr;
+};
+
+struct mgcp_trunk *mgcp_trunk_alloc(struct mgcp_config *cfg, enum mgcp_trunk_type ttype, int nr);
+int mgcp_trunk_alloc_endpts(struct mgcp_trunk *tcfg);
+struct mgcp_trunk *mgcp_trunk_by_num(const struct mgcp_config *cfg, int index);
+struct mgcp_trunk *mgcp_trunk_by_name(const struct mgcp_config *cfg, const char *epname);
diff --git a/src/libosmo-mgcp/Makefile.am b/src/libosmo-mgcp/Makefile.am
index a0c015b..77d0cdf 100644
--- a/src/libosmo-mgcp/Makefile.am
+++ b/src/libosmo-mgcp/Makefile.am
@@ -40,5 +40,7 @@
 	mgcp_conn.c \
 	mgcp_stat.c \
 	mgcp_endp.c \
+	mgcp_trunk.c \
 	mgcp_ctrl.c \
+	mgcp_ratectr.c \
 	$(NULL)
diff --git a/src/libosmo-mgcp/mgcp_codec.c b/src/libosmo-mgcp/mgcp_codec.c
index 3cea495..c251317 100644
--- a/src/libosmo-mgcp/mgcp_codec.c
+++ b/src/libosmo-mgcp/mgcp_codec.c
@@ -19,6 +19,7 @@
  */
 #include <osmocom/mgcp/mgcp_internal.h>
 #include <osmocom/mgcp/mgcp_endp.h>
+#include <osmocom/mgcp/mgcp_trunk.h>
 #include <errno.h>
 
 /* Helper function to dump codec information of a specified codec to a printable
diff --git a/src/libosmo-mgcp/mgcp_conn.c b/src/libosmo-mgcp/mgcp_conn.c
index 0b499b4..6802b91 100644
--- a/src/libosmo-mgcp/mgcp_conn.c
+++ b/src/libosmo-mgcp/mgcp_conn.c
@@ -25,6 +25,7 @@
 #include <osmocom/mgcp/mgcp_internal.h>
 #include <osmocom/mgcp/mgcp_common.h>
 #include <osmocom/mgcp/mgcp_endp.h>
+#include <osmocom/mgcp/mgcp_trunk.h>
 #include <osmocom/mgcp/mgcp_sdp.h>
 #include <osmocom/mgcp/mgcp_codec.h>
 #include <osmocom/gsm/gsm_utils.h>
@@ -254,10 +255,9 @@
 	return NULL;
 }
 
-static void
-aggregate_rtp_conn_stats(struct mgcp_trunk *trunk, struct mgcp_conn_rtp *conn_rtp)
+static void aggregate_rtp_conn_stats(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn_rtp)
 {
-	struct rate_ctr_group *all_stats = trunk->all_rtp_conn_stats;
+	struct rate_ctr_group *all_stats = endp->trunk->ratectr.all_rtp_conn_stats;
 	struct rate_ctr_group *conn_stats = conn_rtp->rate_ctr_group;
 
 	if (all_stats == NULL || conn_stats == NULL)
@@ -296,7 +296,7 @@
 
 	switch (conn->type) {
 	case MGCP_CONN_TYPE_RTP:
-		aggregate_rtp_conn_stats(endp->trunk, &conn->u.rtp);
+		aggregate_rtp_conn_stats(endp, &conn->u.rtp);
 		mgcp_rtp_conn_cleanup(&conn->u.rtp);
 		break;
 	default:
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 at sysmocom.de>
+ * (C) 2017-2020 by sysmocom s.f.m.c. GmbH <info at 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;
+}
diff --git a/src/libosmo-mgcp/mgcp_msg.c b/src/libosmo-mgcp/mgcp_msg.c
index 7124a39..019466e 100644
--- a/src/libosmo-mgcp/mgcp_msg.c
+++ b/src/libosmo-mgcp/mgcp_msg.c
@@ -129,167 +129,6 @@
 	return ret;
 }
 
-/* We have a null terminated string with the endpoint name here. We only
- * support two kinds. Simple ones as seen on the BSC level and the ones
- * seen on the trunk side. (helper function for find_endpoint()) */
-static struct mgcp_endpoint *find_e1_endpoint(struct mgcp_config *cfg,
-					      const char *mgcp)
-{
-	char *rest = NULL;
-	struct mgcp_trunk *trunk;
-	int trunk_index, endp;
-	struct mgcp_endpoint *endp_ptr;
-
-	trunk_index = strtoul(mgcp + 6, &rest, 10);
-	if (rest == NULL || rest[0] != '/' || trunk_index < 1) {
-		LOGP(DLMGCP, LOGL_ERROR, "Wrong trunk name '%s'\n", mgcp);
-		return NULL;
-	}
-
-	endp = strtoul(rest + 1, &rest, 10);
-	if (rest == NULL || rest[0] != '@') {
-		LOGP(DLMGCP, LOGL_ERROR, "Wrong endpoint name '%s'\n", mgcp);
-		return NULL;
-	}
-
-	/* signalling is on timeslot 1 */
-	if (endp == 1)
-		return NULL;
-
-	trunk = mgcp_trunk_num(cfg, trunk_index);
-	if (!trunk) {
-		LOGP(DLMGCP, LOGL_ERROR, "The trunk %d is not declared.\n",
-		     trunk_index);
-		return NULL;
-	}
-
-	if (!trunk->endpoints) {
-		LOGP(DLMGCP, LOGL_ERROR,
-		     "Endpoints of trunk %d not allocated.\n", trunk_index);
-		return NULL;
-	}
-
-	if (endp < 1 || endp >= trunk->number_endpoints) {
-		LOGP(DLMGCP, LOGL_ERROR, "Failed to find endpoint '%s'\n",
-		     mgcp);
-		return NULL;
-	}
-
-	endp_ptr = &trunk->endpoints[endp];
-	endp_ptr->wildcarded_req = false;
-	return endp_ptr;
-}
-
-/* Find an endpoint that is not in use. Do this by going through the endpoint
- * array, check the callid. A callid nullpointer indicates that the endpoint
- * is free */
-static struct mgcp_endpoint *find_free_endpoint(struct mgcp_endpoint *endpoints,
-						unsigned int number_endpoints)
-{
-	struct mgcp_endpoint *endp;
-	unsigned int i;
-
-	for (i = 0; i < number_endpoints; i++) {
-		if (endpoints[i].callid == NULL) {
-			endp = &endpoints[i];
-			LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
-			     "found free endpoint\n");
-			endp->wildcarded_req = true;
-			return endp;
-		}
-	}
-
-	LOGP(DLMGCP, LOGL_ERROR, "Not able to find a free endpoint\n");
-	return NULL;
-}
-
-/* Check if the domain name, which is supplied with the endpoint name
- * matches the configuration. */
-static int check_domain_name(struct mgcp_config *cfg, const char *mgcp)
-{
-	char *domain_to_check;
-
-	domain_to_check = strstr(mgcp, "@");
-	if (!domain_to_check)
-		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 '%s', expecting '%s'\n", mgcp, cfg->domain);
-		return -EINVAL;
-	}
-
-	return 0;
-}
-
-/* Search the endpoint pool for the endpoint that had been selected via the
- * MGCP message (helper function for mgcp_analyze_header()) */
-static struct mgcp_endpoint *find_endpoint(struct mgcp_config *cfg,
-					   const char *mgcp,
-					   int *cause)
-{
-	char *endptr = NULL;
-	unsigned int gw = INT_MAX;
-	const char *endpoint_number_str;
-	struct mgcp_endpoint *endp;
-	struct mgcp_trunk *virt_trunk = cfg->virt_trunk;
-
-	*cause = 0;
-
-	/* Check if the domainname in the request is correct */
-	if (check_domain_name(cfg, mgcp)) {
-		*cause = -500;
-		return NULL;
-	}
-
-	/* Check if the E1 trunk is requested */
-	if (strncmp(mgcp, "ds/e1", 5) == 0) {
-		endp = find_e1_endpoint(cfg, mgcp);
-		if (!endp)
-			*cause = -500;
-		return endp;
-	}
-
-	/* Check if the virtual trunk is addressed (new, correct way with prefix) */
-	if (strncmp
-	    (mgcp, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK,
-	     strlen(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK)) == 0) {
-		endpoint_number_str =
-		    mgcp + strlen(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK);
-		if (endpoint_number_str[0] == '*') {
-			endp = find_free_endpoint(virt_trunk->endpoints,
-						  virt_trunk->number_endpoints);
-			if (!endp)
-				*cause = -403;
-			return endp;
-		}
-		gw = strtoul(endpoint_number_str, &endptr, 16);
-		if (gw < virt_trunk->number_endpoints && endptr[0] == '@') {
-			endp = &virt_trunk->endpoints[gw];
-			endp->wildcarded_req = false;
-			return endp;
-		}
-	}
-
-	/* Deprecated method without prefix */
-	LOGP(DLMGCP, LOGL_NOTICE,
-	     "Addressing virtual trunk without prefix (deprecated), please use %s: '%s'\n",
-	     MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, mgcp);
-	gw = strtoul(mgcp, &endptr, 16);
-	if (gw < virt_trunk->number_endpoints && endptr[0] == '@') {
-		endp = &virt_trunk->endpoints[gw];
-		endp->wildcarded_req = false;
-		return endp;
-	}
-
-	LOGP(DLMGCP, LOGL_ERROR, "Not able to find the endpoint: '%s'\n", mgcp);
-	*cause = -500;
-	return NULL;
-}
-
 /*! Analyze and parse the the hader of an MGCP messeage string.
  *  \param[out] pdata caller provided memory to store the parsing results
  *  \param[in] data mgcp message string
@@ -317,7 +156,7 @@
 			pdata->trans = elem;
 			break;
 		case 1:
-			pdata->endp = find_endpoint(pdata->cfg, elem, &cause);
+			pdata->endp = mgcp_endp_by_name(&cause, elem, pdata->cfg);
 			if (!pdata->endp) {
 				LOGP(DLMGCP, LOGL_ERROR,
 				     "Unable to find Endpoint `%s'\n", elem);
@@ -391,8 +230,8 @@
 	const size_t line_len = strlen(line);
 	if (line[0] != '\0' && line_len < 2) {
 		LOGP(DLMGCP, LOGL_ERROR,
-		     "Wrong MGCP option format: '%s' on 0x%x\n",
-		     line, ENDPOINT_NUMBER(endp));
+		     "Wrong MGCP option format: '%s' on %s\n",
+		     line, endp->name);
 		return 0;
 	}
 
diff --git a/src/libosmo-mgcp/mgcp_network.c b/src/libosmo-mgcp/mgcp_network.c
index 2d3fdc3..8efc6b7 100644
--- a/src/libosmo-mgcp/mgcp_network.c
+++ b/src/libosmo-mgcp/mgcp_network.c
@@ -42,6 +42,7 @@
 #include <osmocom/mgcp/osmux.h>
 #include <osmocom/mgcp/mgcp_conn.h>
 #include <osmocom/mgcp/mgcp_endp.h>
+#include <osmocom/mgcp/mgcp_trunk.h>
 #include <osmocom/mgcp/mgcp_codec.h>
 #include <osmocom/mgcp/debug.h>
 #include <osmocom/codec/codec.h>
@@ -61,11 +62,11 @@
 				 int id, int inc)
 {
 	struct rate_ctr_group *conn_stats = conn_rtp->rate_ctr_group;
-	struct rate_ctr_group *trunk_stats = endp->trunk->all_rtp_conn_stats;
+	struct rate_ctr_group *mgw_stats = endp->trunk->ratectr.all_rtp_conn_stats;
 
-	/* add to both the per-connection and the per-trunk global stats */
+	/* add to both the per-connection and the global stats */
 	rate_ctr_add(&conn_stats->ctr[id], inc);
-	rate_ctr_add(&trunk_stats->ctr[id], inc);
+	rate_ctr_add(&mgw_stats->ctr[id], inc);
 }
 
 static void rtpconn_rate_ctr_inc(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *endp, int id)
@@ -648,9 +649,8 @@
 		return;
 
 #if 0
-	DEBUGP(DRTP,
-	       "endpoint:0x%x payload hdr payload %u -> endp payload %u\n",
-	       ENDPOINT_NUMBER(endp), rtp_hdr->payload_type, payload);
+	LOGPENDP(endp, DRTP, LOGL_DEBUG, "payload hdr payload %u -> endp payload %u\n",
+		 rtp_hdr->payload_type, payload);
 	rtp_hdr->payload_type = payload;
 #endif
 }
@@ -1436,11 +1436,10 @@
 
 /* Bind RTP and RTCP port (helper function for mgcp_bind_net_rtp_port()) */
 static int bind_rtp(struct mgcp_config *cfg, const char *source_addr,
-		    struct mgcp_rtp_end *rtp_end, int endpno)
+		    struct mgcp_rtp_end *rtp_end, struct mgcp_endpoint *endp)
 {
 	/* NOTE: The port that is used for RTCP is the RTP port incremented by one
 	 * (e.g. RTP-Port = 16000 ==> RTCP-Port = 16001) */
-	 struct mgcp_endpoint *endp = &cfg->virt_trunk->endpoints[endpno];
 
 	if (mgcp_create_bind(source_addr, &rtp_end->rtp,
 			     rtp_end->local_port) != 0) {
@@ -1527,8 +1526,7 @@
 
 	mgcp_get_local_addr(local_ip_addr, conn);
 
-	return bind_rtp(endp->cfg, local_ip_addr, end,
-			ENDPOINT_NUMBER(endp));
+	return bind_rtp(endp->cfg, local_ip_addr, end, endp);
 }
 
 /*! free allocated RTP and RTCP ports.
diff --git a/src/libosmo-mgcp/mgcp_osmux.c b/src/libosmo-mgcp/mgcp_osmux.c
index 8da7361..ca8b5f0 100644
--- a/src/libosmo-mgcp/mgcp_osmux.c
+++ b/src/libosmo-mgcp/mgcp_osmux.c
@@ -27,6 +27,7 @@
 #include <osmocom/mgcp/osmux.h>
 #include <osmocom/mgcp/mgcp_conn.h>
 #include <osmocom/mgcp/mgcp_endp.h>
+#include <osmocom/mgcp/mgcp_trunk.h>
 
 static struct osmo_fd osmux_fd;
 
@@ -204,7 +205,7 @@
 
 	for (i=0; i<cfg->virt_trunk->number_endpoints; i++) {
 
-		endp = &cfg->virt_trunk->endpoints[i];
+		endp = cfg->virt_trunk->endpoints[i];
 
 		llist_for_each_entry(conn, &endp->conns, entry) {
 			if (conn->type != MGCP_CONN_TYPE_RTP)
diff --git a/src/libosmo-mgcp/mgcp_protocol.c b/src/libosmo-mgcp/mgcp_protocol.c
index 4d77a4c..1d25c45 100644
--- a/src/libosmo-mgcp/mgcp_protocol.c
+++ b/src/libosmo-mgcp/mgcp_protocol.c
@@ -32,7 +32,6 @@
 #include <osmocom/core/msgb.h>
 #include <osmocom/core/talloc.h>
 #include <osmocom/core/select.h>
-#include <osmocom/core/stats.h>
 
 #include <osmocom/mgcp/mgcp.h>
 #include <osmocom/mgcp/mgcp_common.h>
@@ -40,6 +39,7 @@
 #include <osmocom/mgcp/mgcp_stat.h>
 #include <osmocom/mgcp/mgcp_msg.h>
 #include <osmocom/mgcp/mgcp_endp.h>
+#include <osmocom/mgcp/mgcp_trunk.h>
 #include <osmocom/mgcp/mgcp_sdp.h>
 #include <osmocom/mgcp/mgcp_codec.h>
 #include <osmocom/mgcp/mgcp_conn.h>
@@ -53,101 +53,6 @@
 #define MGCP_REQUEST(NAME, REQ, DEBUG_NAME) \
 	{ .name = NAME, .handle_request = REQ, .debug_name = DEBUG_NAME },
 
-static const struct rate_ctr_desc mgcp_general_ctr_desc[] = {
-	/* rx_msgs = rx_msgs_retransmitted + rx_msgs_handled + rx_msgs_unhandled + err_rx_msg_parse + err_rx_no_endpoint */
-	[MGCP_GENERAL_RX_MSGS_TOTAL] = {"mgcp:rx_msgs", "total number of MGCP messages received."},
-	[MGCP_GENERAL_RX_MSGS_RETRANSMITTED] = {"mgcp:rx_msgs_retransmitted", "number of received retransmissions."},
-	[MGCP_GENERAL_RX_MSGS_HANDLED] = {"mgcp:rx_msgs_handled", "number of handled MGCP messages."},
-	[MGCP_GENERAL_RX_MSGS_UNHANDLED] = {"mgcp:rx_msgs_unhandled", "number of unhandled MGCP messages."},
-	[MGCP_GENERAL_RX_FAIL_MSG_PARSE] = {"mgcp:err_rx_msg_parse", "error parsing MGCP message."},
-	[MGCP_GENERAL_RX_FAIL_NO_ENDPOINT] = {"mgcp:err_rx_no_endpoint", "can't find MGCP endpoint, probably we've used all allocated endpoints."},
-};
-
-const static struct rate_ctr_group_desc mgcp_general_ctr_group_desc = {
-	.group_name_prefix = "mgcp",
-	.group_description = "mgcp general statistics",
-	.class_id = OSMO_STATS_CLASS_GLOBAL,
-	.num_ctr = ARRAY_SIZE(mgcp_general_ctr_desc),
-	.ctr_desc = mgcp_general_ctr_desc
-};
-
-static const struct rate_ctr_desc mgcp_crcx_ctr_desc[] = {
-	[MGCP_CRCX_SUCCESS] = {"crcx:success", "CRCX command processed successfully."},
-	[MGCP_CRCX_FAIL_BAD_ACTION] = {"crcx:bad_action", "bad action in CRCX command."},
-	[MGCP_CRCX_FAIL_UNHANDLED_PARAM] = {"crcx:unhandled_param", "unhandled parameter in CRCX command."},
-	[MGCP_CRCX_FAIL_MISSING_CALLID] = {"crcx:missing_callid", "missing CallId in CRCX command."},
-	[MGCP_CRCX_FAIL_INVALID_MODE] = {"crcx:invalid_mode", "invalid connection mode in CRCX command."},
-	[MGCP_CRCX_FAIL_LIMIT_EXCEEDED] = {"crcx:limit_exceeded", "limit of concurrent connections was reached."},
-	[MGCP_CRCX_FAIL_UNKNOWN_CALLID] = {"crcx:unkown_callid", "unknown CallId in CRCX command."},
-	[MGCP_CRCX_FAIL_ALLOC_CONN] = {"crcx:alloc_conn_fail", "connection allocation failure."},
-	[MGCP_CRCX_FAIL_NO_REMOTE_CONN_DESC] = {"crcx:no_remote_conn_desc", "no opposite end specified for connection."},
-	[MGCP_CRCX_FAIL_START_RTP] = {"crcx:start_rtp_failure", "failure to start RTP processing."},
-	[MGCP_CRCX_FAIL_REJECTED_BY_POLICY] = {"crcx:conn_rejected", "connection rejected by policy."},
-	[MGCP_CRCX_FAIL_NO_OSMUX] = {"crcx:no_osmux", "no osmux offered by peer."},
-	[MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS] = {"crcx:conn_opt", "connection options invalid."},
-	[MGCP_CRCX_FAIL_CODEC_NEGOTIATION] = {"crcx:codec_nego", "codec negotiation failure."},
-	[MGCP_CRCX_FAIL_BIND_PORT] = {"crcx:bind_port", "port bind failure."},
-};
-
-const static struct rate_ctr_group_desc mgcp_crcx_ctr_group_desc = {
-	.group_name_prefix = "crcx",
-	.group_description = "crxc statistics",
-	.class_id = OSMO_STATS_CLASS_GLOBAL,
-	.num_ctr = ARRAY_SIZE(mgcp_crcx_ctr_desc),
-	.ctr_desc = mgcp_crcx_ctr_desc
-};
-
-static const struct rate_ctr_desc mgcp_mdcx_ctr_desc[] = {
-	[MGCP_MDCX_SUCCESS] = {"mdcx:success", "MDCX command processed successfully."},
-	[MGCP_MDCX_FAIL_WILDCARD] = {"mdcx:wildcard", "wildcard endpoint names in MDCX commands are unsupported."},
-	[MGCP_MDCX_FAIL_NO_CONN] = {"mdcx:no_conn", "endpoint specified in MDCX command has no active connections."},
-	[MGCP_MDCX_FAIL_INVALID_CALLID] = {"mdcx:callid", "invalid CallId specified in MDCX command."},
-	[MGCP_MDCX_FAIL_INVALID_CONNID] = {"mdcx:connid", "invalid connection ID specified in MDCX command."},
-	[MGCP_MDCX_FAIL_UNHANDLED_PARAM] = {"crcx:unhandled_param", "unhandled parameter in MDCX command."},
-	[MGCP_MDCX_FAIL_NO_CONNID] = {"mdcx:no_connid", "no connection ID specified in MDCX command."},
-	[MGCP_MDCX_FAIL_CONN_NOT_FOUND] = {"mdcx:conn_not_found", "connection specified in MDCX command does not exist."},
-	[MGCP_MDCX_FAIL_INVALID_MODE] = {"mdcx:invalid_mode", "invalid connection mode in MDCX command."},
-	[MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS] = {"mdcx:conn_opt", "connection options invalid."},
-	[MGCP_MDCX_FAIL_NO_REMOTE_CONN_DESC] = {"mdcx:no_remote_conn_desc", "no opposite end specified for connection."},
-	[MGCP_MDCX_FAIL_START_RTP] = {"mdcx:start_rtp_failure", "failure to start RTP processing."},
-	[MGCP_MDCX_FAIL_REJECTED_BY_POLICY] = {"mdcx:conn_rejected", "connection rejected by policy."},
-	[MGCP_MDCX_DEFERRED_BY_POLICY] = {"mdcx:conn_deferred", "connection deferred by policy."},
-};
-
-const static struct rate_ctr_group_desc mgcp_mdcx_ctr_group_desc = {
-	.group_name_prefix = "mdcx",
-	.group_description = "mdcx statistics",
-	.class_id = OSMO_STATS_CLASS_GLOBAL,
-	.num_ctr = ARRAY_SIZE(mgcp_mdcx_ctr_desc),
-	.ctr_desc = mgcp_mdcx_ctr_desc
-};
-
-static const struct rate_ctr_desc mgcp_dlcx_ctr_desc[] = {
-	[MGCP_DLCX_SUCCESS] = {"dlcx:success", "DLCX command processed successfully."},
-	[MGCP_DLCX_FAIL_WILDCARD] = {"dlcx:wildcard", "wildcard names in DLCX commands are unsupported."},
-	[MGCP_DLCX_FAIL_NO_CONN] = {"dlcx:no_conn", "endpoint specified in DLCX command has no active connections."},
-	[MGCP_DLCX_FAIL_INVALID_CALLID] = {"dlcx:callid", "CallId specified in DLCX command mismatches endpoint's CallId ."},
-	[MGCP_DLCX_FAIL_INVALID_CONNID] = {"dlcx:connid", "connection ID specified in DLCX command does not exist on endpoint."},
-	[MGCP_DLCX_FAIL_UNHANDLED_PARAM] = {"dlcx:unhandled_param", "unhandled parameter in DLCX command."},
-	[MGCP_DLCX_FAIL_REJECTED_BY_POLICY] = {"dlcx:rejected", "connection deletion rejected by policy."},
-	[MGCP_DLCX_DEFERRED_BY_POLICY] = {"dlcx:deferred", "connection deletion deferred by policy."},
-};
-
-const static struct rate_ctr_group_desc mgcp_dlcx_ctr_group_desc = {
-	.group_name_prefix = "dlcx",
-	.group_description = "dlcx statistics",
-	.class_id = OSMO_STATS_CLASS_GLOBAL,
-	.num_ctr = ARRAY_SIZE(mgcp_dlcx_ctr_desc),
-	.ctr_desc = mgcp_dlcx_ctr_desc
-};
-
-const static struct rate_ctr_group_desc all_rtp_conn_rate_ctr_group_desc = {
-	.group_name_prefix = "all_rtp_conn",
-	.group_description = "aggregated statistics for all rtp connections",
-	.class_id = 1,
-	.num_ctr = ARRAY_SIZE(all_rtp_conn_rate_ctr_desc),
-	.ctr_desc = all_rtp_conn_rate_ctr_desc
-};
 
 static struct msgb *handle_audit_endpoint(struct mgcp_parse_data *data);
 static struct msgb *handle_create_con(struct mgcp_parse_data *data);
@@ -297,9 +202,7 @@
 	/* NOTE: Only in the virtual trunk we allow dynamic endpoint names */
 	if (endp->wildcarded_req
 	    && endp->trunk->trunk_type == MGCP_TRUNK_VIRTUAL) {
-		rc = msgb_printf(msg, "Z: %s%x@%s\r\n",
-				 MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK,
-				 ENDPOINT_NUMBER(endp), endp->cfg->domain);
+		rc = msgb_printf(msg, "Z: %s\r\n", endp->name);
 		if (rc < 0)
 			return -EINVAL;
 	}
@@ -379,8 +282,7 @@
  *   - or a response (three numbers, space, transaction id) */
 struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
 {
-	struct mgcp_trunk *trunk = cfg->virt_trunk;
-	struct rate_ctr_group *rate_ctrs = trunk->mgcp_general_ctr_group;
+	struct rate_ctr_group *rate_ctrs = cfg->ratectr.mgcp_general_ctr_group;
 	struct mgcp_parse_data pdata;
 	int rc, i, code, handled = 0;
 	struct msgb *resp = NULL;
@@ -832,7 +734,7 @@
 {
 	struct mgcp_trunk *trunk = p->endp->trunk;
 	struct mgcp_endpoint *endp = p->endp;
-	struct rate_ctr_group *rate_ctrs = trunk->mgcp_crcx_ctr_group;
+	struct rate_ctr_group *rate_ctrs = trunk->ratectr.mgcp_crcx_ctr_group;
 	int error_code = 400;
 	const char *local_options = NULL;
 	const char *callid = NULL;
@@ -1043,8 +945,7 @@
 	/* policy CB */
 	if (p->cfg->policy_cb) {
 		int rc;
-		rc = p->cfg->policy_cb(trunk, ENDPOINT_NUMBER(endp),
-				       MGCP_ENDP_CRCX, p->trans);
+		rc = p->cfg->policy_cb(endp, MGCP_ENDP_CRCX, p->trans);
 		switch (rc) {
 		case MGCP_POLICY_REJECT:
 			LOGPCONN(_conn, DLMGCP, LOGL_NOTICE,
@@ -1066,7 +967,7 @@
 	LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
 		 "CRCX: Creating connection: port: %u\n", conn->end.local_port);
 	if (p->cfg->change_cb)
-		p->cfg->change_cb(trunk, ENDPOINT_NUMBER(endp), MGCP_ENDP_CRCX);
+		p->cfg->change_cb(endp, MGCP_ENDP_CRCX);
 
 	/* Send dummy packet, see also comments in mgcp_keepalive_timer_cb() */
 	OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
@@ -1088,9 +989,8 @@
 /* MDCX command handler, processes the received command */
 static struct msgb *handle_modify_con(struct mgcp_parse_data *p)
 {
-	struct mgcp_trunk *trunk = p->endp->trunk;
 	struct mgcp_endpoint *endp = p->endp;
-	struct rate_ctr_group *rate_ctrs = trunk->mgcp_mdcx_ctr_group;
+	struct rate_ctr_group *rate_ctrs = endp->trunk->ratectr.mgcp_mdcx_ctr_group;
 	int error_code = 500;
 	int silent = 0;
 	int have_sdp = 0;
@@ -1257,8 +1157,7 @@
 	/* policy CB */
 	if (p->cfg->policy_cb) {
 		int rc;
-		rc = p->cfg->policy_cb(endp->trunk, ENDPOINT_NUMBER(endp),
-				       MGCP_ENDP_MDCX, p->trans);
+		rc = p->cfg->policy_cb(endp, MGCP_ENDP_MDCX, p->trans);
 		switch (rc) {
 		case MGCP_POLICY_REJECT:
 			LOGPCONN(conn->conn, DLMGCP, LOGL_NOTICE,
@@ -1287,8 +1186,7 @@
 	LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
 		 "MDCX: modified conn:%s\n", mgcp_conn_dump(conn->conn));
 	if (p->cfg->change_cb)
-		p->cfg->change_cb(endp->trunk, ENDPOINT_NUMBER(endp),
-				  MGCP_ENDP_MDCX);
+		p->cfg->change_cb(endp, MGCP_ENDP_MDCX);
 
 	/* Send dummy packet, see also comments in mgcp_keepalive_timer_cb() */
 	OSMO_ASSERT(endp->trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
@@ -1314,9 +1212,8 @@
 /* DLCX command handler, processes the received command */
 static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
 {
-	struct mgcp_trunk *trunk = p->endp->trunk;
 	struct mgcp_endpoint *endp = p->endp;
-	struct rate_ctr_group *rate_ctrs = trunk->mgcp_dlcx_ctr_group;
+	struct rate_ctr_group *rate_ctrs = endp->trunk->ratectr.mgcp_dlcx_ctr_group;
 	int error_code = 400;
 	int silent = 0;
 	char *line;
@@ -1377,8 +1274,7 @@
 	/* policy CB */
 	if (p->cfg->policy_cb) {
 		int rc;
-		rc = p->cfg->policy_cb(endp->trunk, ENDPOINT_NUMBER(endp),
-				       MGCP_ENDP_DLCX, p->trans);
+		rc = p->cfg->policy_cb(endp, MGCP_ENDP_DLCX, p->trans);
 		switch (rc) {
 		case MGCP_POLICY_REJECT:
 			LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "DLCX: rejected by policy\n");
@@ -1442,8 +1338,7 @@
 	}
 
 	if (p->cfg->change_cb)
-		p->cfg->change_cb(endp->trunk, ENDPOINT_NUMBER(endp),
-				  MGCP_ENDP_DLCX);
+		p->cfg->change_cb(endp, MGCP_ENDP_DLCX);
 
 	rate_ctr_inc(&rate_ctrs->ctr[MGCP_DLCX_SUCCESS]);
 	if (silent)
@@ -1548,7 +1443,7 @@
 	/* Send walk over all endpoints and send out dummy packets through
 	 * every connection present on each endpoint */
 	for (i = 1; i < trunk->number_endpoints; ++i) {
-		struct mgcp_endpoint *endp = &trunk->endpoints[i];
+		struct mgcp_endpoint *endp = trunk->endpoints[i];
 		llist_for_each_entry(conn, &endp->conns, entry) {
 			if (conn->mode == MGCP_CONN_RECV_ONLY)
 				send_dummy(endp, &conn->u.rtp);
@@ -1574,66 +1469,12 @@
 				    trunk->keepalive_interval, 0);
 }
 
-static int free_rate_counter_group(struct rate_ctr_group *rate_ctr_group)
-{
-	rate_ctr_group_free(rate_ctr_group);
-	return 0;
-}
-
-static int alloc_mgcp_rate_counters(struct mgcp_trunk *trunk, void *ctx)
-{
-	/* FIXME: Each new rate counter group requires a unique index. At the
-	 * moment we generate an index using a counter, but perhaps there is
-	 * a better way of assigning indices? */
-	static unsigned int general_rate_ctr_index = 0;
-	static unsigned int crcx_rate_ctr_index = 0;
-	static unsigned int mdcx_rate_ctr_index = 0;
-	static unsigned int dlcx_rate_ctr_index = 0;
-	static unsigned int all_rtp_conn_rate_ctr_index = 0;
-
-	if (trunk->mgcp_general_ctr_group == NULL) {
-		trunk->mgcp_general_ctr_group = rate_ctr_group_alloc(ctx, &mgcp_general_ctr_group_desc, general_rate_ctr_index);
-		if (!trunk->mgcp_general_ctr_group)
-			return -1;
-		talloc_set_destructor(trunk->mgcp_general_ctr_group, free_rate_counter_group);
-		general_rate_ctr_index++;
-	}
-	if (trunk->mgcp_crcx_ctr_group == NULL) {
-		trunk->mgcp_crcx_ctr_group = rate_ctr_group_alloc(ctx, &mgcp_crcx_ctr_group_desc, crcx_rate_ctr_index);
-		if (!trunk->mgcp_crcx_ctr_group)
-			return -1;
-		talloc_set_destructor(trunk->mgcp_crcx_ctr_group, free_rate_counter_group);
-		crcx_rate_ctr_index++;
-	}
-	if (trunk->mgcp_mdcx_ctr_group == NULL) {
-		trunk->mgcp_mdcx_ctr_group = rate_ctr_group_alloc(ctx, &mgcp_mdcx_ctr_group_desc, mdcx_rate_ctr_index);
-		if (!trunk->mgcp_mdcx_ctr_group)
-			return -1;
-		talloc_set_destructor(trunk->mgcp_mdcx_ctr_group, free_rate_counter_group);
-		mdcx_rate_ctr_index++;
-	}
-	if (trunk->mgcp_dlcx_ctr_group == NULL) {
-		trunk->mgcp_dlcx_ctr_group = rate_ctr_group_alloc(ctx, &mgcp_dlcx_ctr_group_desc, dlcx_rate_ctr_index);
-		if (!trunk->mgcp_dlcx_ctr_group)
-			return -1;
-		talloc_set_destructor(trunk->mgcp_dlcx_ctr_group, free_rate_counter_group);
-		dlcx_rate_ctr_index++;
-	}
-	if (trunk->all_rtp_conn_stats == NULL) {
-		trunk->all_rtp_conn_stats = rate_ctr_group_alloc(ctx, &all_rtp_conn_rate_ctr_group_desc,
-								 all_rtp_conn_rate_ctr_index);
-		if (!trunk->all_rtp_conn_stats)
-			return -1;
-		talloc_set_destructor(trunk->all_rtp_conn_stats, free_rate_counter_group);
-		all_rtp_conn_rate_ctr_index++;
-	}
-	return 0;
-}
-
 /*! allocate configuration with default values.
  *  (called once at startup by main function) */
 struct mgcp_config *mgcp_config_alloc(void)
 {
+	/* FIXME: This is unrelated to the protocol, put this in some
+	 * appropiate place! */
 	struct mgcp_config *cfg;
 
 	cfg = talloc_zero(NULL, struct mgcp_config);
@@ -1657,109 +1498,21 @@
 
 	cfg->get_net_downlink_format_cb = &mgcp_get_net_downlink_format_default;
 
-	INIT_LLIST_HEAD(&cfg->trunks);
-
-	/* default trunk handling */
+	/* Allocate virtual trunk */
 	cfg->virt_trunk = mgcp_trunk_alloc(cfg, MGCP_TRUNK_VIRTUAL, 0);
 	if (!cfg->virt_trunk) {
 		talloc_free(cfg);
 		return NULL;
 	}
-	/* virtual trunk is not part of the list! */
-	llist_del(&cfg->virt_trunk->entry);
+
+	/* Initalize list head for user configurable trunks */
+	INIT_LLIST_HEAD(&cfg->trunks);
+
+        mgcp_ratectr_global_alloc(cfg, &cfg->ratectr);
 
 	return cfg;
 }
 
-/*! allocate configuration with default values. Do not link it into global list yet!
- *  (called once at startup by VTY)
- *  \param[in] cfg mgcp configuration
- *  \param[in] nr trunk number
- *  \returns pointer to allocated trunk configuration */
-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_name = talloc_strdup(trunk, "AMR/8000");
-	trunk->audio_payload = 126;
-	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);
-	if (alloc_mgcp_rate_counters(trunk, trunk) < 0) {
-		talloc_free(trunk);
-		return NULL;
-	}
-	llist_add_tail(&trunk->entry, &cfg->trunks);
-
-	return trunk;
-}
-
-/*! 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_num(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;
-}
-
-/*! 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_endpoints_allocate(struct mgcp_trunk *trunk)
-{
-	int i;
-
-	trunk->endpoints = _talloc_zero_array(trunk->cfg,
-					     sizeof(struct mgcp_endpoint),
-					     trunk->vty_number_endpoints,
-					     "endpoints");
-	if (!trunk->endpoints)
-		return -1;
-
-	for (i = 0; i < trunk->vty_number_endpoints; ++i) {
-		INIT_LLIST_HEAD(&trunk->endpoints[i].conns);
-		trunk->endpoints[i].cfg = trunk->cfg;
-		trunk->endpoints[i].trunk = trunk;
-
-		switch (trunk->trunk_type) {
-		case MGCP_TRUNK_VIRTUAL:
-			trunk->endpoints[i].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__);
-		}
-	}
-
-	trunk->number_endpoints = trunk->vty_number_endpoints;
-	alloc_mgcp_rate_counters(trunk, trunk->cfg);
-
-	return 0;
-}
-
 static int send_agent(struct mgcp_config *cfg, const char *buf, int len)
 {
 	return write(cfg->gw_fd.bfd.fd, buf, len);
@@ -1790,17 +1543,16 @@
 
 /*! Reset a single endpoint by sending RSIP message to self.
  *  (called by VTY)
- *  \param[in] endp trunk endpoint
- *  \param[in] endpoint number
+ *  \param[in] endp to reset
  *  \returns 0 on success, -1 on error */
-int mgcp_send_reset_ep(struct mgcp_endpoint *endp, int endpoint)
+int mgcp_send_reset_ep(struct mgcp_endpoint *endp)
 {
 	char buf[MGCP_ENDPOINT_MAXLEN + 128];
 	int len;
 	int rc;
 
 	len = snprintf(buf, sizeof(buf),
-		       "RSIP 39 %x@%s MGCP 1.0\r\n", endpoint, endp->cfg->domain);
+		       "RSIP 39 %s MGCP 1.0\r\n", endp->name);
 	if (len < 0)
 		return -1;
 
diff --git a/src/libosmo-mgcp/mgcp_ratectr.c b/src/libosmo-mgcp/mgcp_ratectr.c
new file mode 100644
index 0000000..302786f
--- /dev/null
+++ b/src/libosmo-mgcp/mgcp_ratectr.c
@@ -0,0 +1,204 @@
+/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
+/* rate-counter implementation */
+
+/*
+ * (C) 2009-2012 by Holger Hans Peter Freyther <zecke at selfish.org>
+ * (C) 2009-2012 by On-Waves
+ * (C) 2017-2020 by sysmocom s.f.m.c. GmbH <info at 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 <errno.h>
+#include <osmocom/core/stats.h>
+#include <osmocom/mgcp/mgcp_internal.h>
+#include <osmocom/mgcp/mgcp_ratectr.h>
+
+static const struct rate_ctr_desc mgcp_general_ctr_desc[] = {
+	/* rx_msgs = rx_msgs_retransmitted + rx_msgs_handled + rx_msgs_unhandled + err_rx_msg_parse + err_rx_no_endpoint */
+	[MGCP_GENERAL_RX_MSGS_TOTAL] = { "mgcp:rx_msgs", "total number of MGCP messages received." },
+	[MGCP_GENERAL_RX_MSGS_RETRANSMITTED] = { "mgcp:rx_msgs_retransmitted", "number of received retransmissions." },
+	[MGCP_GENERAL_RX_MSGS_HANDLED] = { "mgcp:rx_msgs_handled", "number of handled MGCP messages." },
+	[MGCP_GENERAL_RX_MSGS_UNHANDLED] = { "mgcp:rx_msgs_unhandled", "number of unhandled MGCP messages." },
+	[MGCP_GENERAL_RX_FAIL_MSG_PARSE] = { "mgcp:err_rx_msg_parse", "error parsing MGCP message." },
+	[MGCP_GENERAL_RX_FAIL_NO_ENDPOINT] =
+	    { "mgcp:err_rx_no_endpoint", "can't find MGCP endpoint, probably we've used all allocated endpoints." },
+};
+
+const static struct rate_ctr_group_desc mgcp_general_ctr_group_desc = {
+	.group_name_prefix = "mgcp",
+	.group_description = "mgcp general statistics",
+	.class_id = OSMO_STATS_CLASS_GLOBAL,
+	.num_ctr = ARRAY_SIZE(mgcp_general_ctr_desc),
+	.ctr_desc = mgcp_general_ctr_desc
+};
+
+static const struct rate_ctr_desc mgcp_crcx_ctr_desc[] = {
+	[MGCP_CRCX_SUCCESS] = { "crcx:success", "CRCX command processed successfully." },
+	[MGCP_CRCX_FAIL_BAD_ACTION] = { "crcx:bad_action", "bad action in CRCX command." },
+	[MGCP_CRCX_FAIL_UNHANDLED_PARAM] = { "crcx:unhandled_param", "unhandled parameter in CRCX command." },
+	[MGCP_CRCX_FAIL_MISSING_CALLID] = { "crcx:missing_callid", "missing CallId in CRCX command." },
+	[MGCP_CRCX_FAIL_INVALID_MODE] = { "crcx:invalid_mode", "invalid connection mode in CRCX command." },
+	[MGCP_CRCX_FAIL_LIMIT_EXCEEDED] = { "crcx:limit_exceeded", "limit of concurrent connections was reached." },
+	[MGCP_CRCX_FAIL_UNKNOWN_CALLID] = { "crcx:unkown_callid", "unknown CallId in CRCX command." },
+	[MGCP_CRCX_FAIL_ALLOC_CONN] = { "crcx:alloc_conn_fail", "connection allocation failure." },
+	[MGCP_CRCX_FAIL_NO_REMOTE_CONN_DESC] =
+	    { "crcx:no_remote_conn_desc", "no opposite end specified for connection." },
+	[MGCP_CRCX_FAIL_START_RTP] = { "crcx:start_rtp_failure", "failure to start RTP processing." },
+	[MGCP_CRCX_FAIL_REJECTED_BY_POLICY] = { "crcx:conn_rejected", "connection rejected by policy." },
+	[MGCP_CRCX_FAIL_NO_OSMUX] = { "crcx:no_osmux", "no osmux offered by peer." },
+	[MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS] = { "crcx:conn_opt", "connection options invalid." },
+	[MGCP_CRCX_FAIL_CODEC_NEGOTIATION] = { "crcx:codec_nego", "codec negotiation failure." },
+	[MGCP_CRCX_FAIL_BIND_PORT] = { "crcx:bind_port", "port bind failure." },
+};
+
+const static struct rate_ctr_group_desc mgcp_crcx_ctr_group_desc = {
+	.group_name_prefix = "crcx",
+	.group_description = "crxc statistics",
+	.class_id = OSMO_STATS_CLASS_GLOBAL,
+	.num_ctr = ARRAY_SIZE(mgcp_crcx_ctr_desc),
+	.ctr_desc = mgcp_crcx_ctr_desc
+};
+
+static const struct rate_ctr_desc mgcp_mdcx_ctr_desc[] = {
+	[MGCP_MDCX_SUCCESS] = { "mdcx:success", "MDCX command processed successfully." },
+	[MGCP_MDCX_FAIL_WILDCARD] = { "mdcx:wildcard", "wildcard endpoint names in MDCX commands are unsupported." },
+	[MGCP_MDCX_FAIL_NO_CONN] = { "mdcx:no_conn", "endpoint specified in MDCX command has no active connections." },
+	[MGCP_MDCX_FAIL_INVALID_CALLID] = { "mdcx:callid", "invalid CallId specified in MDCX command." },
+	[MGCP_MDCX_FAIL_INVALID_CONNID] = { "mdcx:connid", "invalid connection ID specified in MDCX command." },
+	[MGCP_MDCX_FAIL_UNHANDLED_PARAM] = { "crcx:unhandled_param", "unhandled parameter in MDCX command." },
+	[MGCP_MDCX_FAIL_NO_CONNID] = { "mdcx:no_connid", "no connection ID specified in MDCX command." },
+	[MGCP_MDCX_FAIL_CONN_NOT_FOUND] =
+	    { "mdcx:conn_not_found", "connection specified in MDCX command does not exist." },
+	[MGCP_MDCX_FAIL_INVALID_MODE] = { "mdcx:invalid_mode", "invalid connection mode in MDCX command." },
+	[MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS] = { "mdcx:conn_opt", "connection options invalid." },
+	[MGCP_MDCX_FAIL_NO_REMOTE_CONN_DESC] =
+	    { "mdcx:no_remote_conn_desc", "no opposite end specified for connection." },
+	[MGCP_MDCX_FAIL_START_RTP] = { "mdcx:start_rtp_failure", "failure to start RTP processing." },
+	[MGCP_MDCX_FAIL_REJECTED_BY_POLICY] = { "mdcx:conn_rejected", "connection rejected by policy." },
+	[MGCP_MDCX_DEFERRED_BY_POLICY] = { "mdcx:conn_deferred", "connection deferred by policy." },
+};
+
+const static struct rate_ctr_group_desc mgcp_mdcx_ctr_group_desc = {
+	.group_name_prefix = "mdcx",
+	.group_description = "mdcx statistics",
+	.class_id = OSMO_STATS_CLASS_GLOBAL,
+	.num_ctr = ARRAY_SIZE(mgcp_mdcx_ctr_desc),
+	.ctr_desc = mgcp_mdcx_ctr_desc
+};
+
+static const struct rate_ctr_desc mgcp_dlcx_ctr_desc[] = {
+	[MGCP_DLCX_SUCCESS] = { "dlcx:success", "DLCX command processed successfully." },
+	[MGCP_DLCX_FAIL_WILDCARD] = { "dlcx:wildcard", "wildcard names in DLCX commands are unsupported." },
+	[MGCP_DLCX_FAIL_NO_CONN] = { "dlcx:no_conn", "endpoint specified in DLCX command has no active connections." },
+	[MGCP_DLCX_FAIL_INVALID_CALLID] =
+	    { "dlcx:callid", "CallId specified in DLCX command mismatches endpoint's CallId ." },
+	[MGCP_DLCX_FAIL_INVALID_CONNID] =
+	    { "dlcx:connid", "connection ID specified in DLCX command does not exist on endpoint." },
+	[MGCP_DLCX_FAIL_UNHANDLED_PARAM] = { "dlcx:unhandled_param", "unhandled parameter in DLCX command." },
+	[MGCP_DLCX_FAIL_REJECTED_BY_POLICY] = { "dlcx:rejected", "connection deletion rejected by policy." },
+	[MGCP_DLCX_DEFERRED_BY_POLICY] = { "dlcx:deferred", "connection deletion deferred by policy." },
+};
+
+const static struct rate_ctr_group_desc mgcp_dlcx_ctr_group_desc = {
+	.group_name_prefix = "dlcx",
+	.group_description = "dlcx statistics",
+	.class_id = OSMO_STATS_CLASS_GLOBAL,
+	.num_ctr = ARRAY_SIZE(mgcp_dlcx_ctr_desc),
+	.ctr_desc = mgcp_dlcx_ctr_desc
+};
+
+const static struct rate_ctr_group_desc all_rtp_conn_rate_ctr_group_desc = {
+	.group_name_prefix = "all_rtp_conn",
+	.group_description = "aggregated statistics for all rtp connections",
+	.class_id = 1,
+	.num_ctr = ARRAY_SIZE(all_rtp_conn_rate_ctr_desc),
+	.ctr_desc = all_rtp_conn_rate_ctr_desc
+};
+
+static int free_rate_counter_group(struct rate_ctr_group *rate_ctr_group)
+{
+	rate_ctr_group_free(rate_ctr_group);
+	return 0;
+}
+
+/*! allocate global rate counters into a given rate counter struct
+ *  (called once at startup)
+ *  \param[in] ctx talloc context.
+ *  \param[out] ratectr struct that holds the counters
+ *  \returns 0 on success, -EINVAL on failure */
+int mgcp_ratectr_global_alloc(void *ctx, struct mgcp_ratectr_global *ratectr)
+{
+	/* FIXME: Each new rate counter group requires a unique index. At the
+	 * moment we generate an index using a counter, but perhaps there is
+	 * a better way of assigning indices? */
+	static unsigned int general_rate_ctr_index = 0;
+
+	if (ratectr->mgcp_general_ctr_group == NULL) {
+		ratectr->mgcp_general_ctr_group =
+		    rate_ctr_group_alloc(ctx, &mgcp_general_ctr_group_desc, general_rate_ctr_index);
+		if (!ratectr->mgcp_general_ctr_group)
+			return -EINVAL;
+		talloc_set_destructor(ratectr->mgcp_general_ctr_group, free_rate_counter_group);
+		general_rate_ctr_index++;
+	}
+	return 0;
+}
+
+/*! allocate trunk specific rate counters into a given rate counter struct
+ *  (called once on trunk initialization)
+ *  \param[in] ctx talloc context.
+ *  \param[out] ratectr struct that holds the counters
+ *  \returns 0 on success, -EINVAL on failure */
+int mgcp_ratectr_trunk_alloc(void *ctx, struct mgcp_ratectr_trunk *ratectr)
+{
+	/* FIXME: see comment in mgcp_ratectr_global_alloc()  */
+	static unsigned int crcx_rate_ctr_index = 0;
+	static unsigned int mdcx_rate_ctr_index = 0;
+	static unsigned int dlcx_rate_ctr_index = 0;
+	static unsigned int all_rtp_conn_rate_ctr_index = 0;
+
+	if (ratectr->mgcp_crcx_ctr_group == NULL) {
+		ratectr->mgcp_crcx_ctr_group = rate_ctr_group_alloc(ctx, &mgcp_crcx_ctr_group_desc, crcx_rate_ctr_index);
+		if (!ratectr->mgcp_crcx_ctr_group)
+			return -EINVAL;
+		talloc_set_destructor(ratectr->mgcp_crcx_ctr_group, free_rate_counter_group);
+		crcx_rate_ctr_index++;
+	}
+	if (ratectr->mgcp_mdcx_ctr_group == NULL) {
+		ratectr->mgcp_mdcx_ctr_group = rate_ctr_group_alloc(ctx, &mgcp_mdcx_ctr_group_desc, mdcx_rate_ctr_index);
+		if (!ratectr->mgcp_mdcx_ctr_group)
+			return -EINVAL;
+		talloc_set_destructor(ratectr->mgcp_mdcx_ctr_group, free_rate_counter_group);
+		mdcx_rate_ctr_index++;
+	}
+	if (ratectr->mgcp_dlcx_ctr_group == NULL) {
+		ratectr->mgcp_dlcx_ctr_group = rate_ctr_group_alloc(ctx, &mgcp_dlcx_ctr_group_desc, dlcx_rate_ctr_index);
+		if (!ratectr->mgcp_dlcx_ctr_group)
+			return -EINVAL;
+		talloc_set_destructor(ratectr->mgcp_dlcx_ctr_group, free_rate_counter_group);
+		dlcx_rate_ctr_index++;
+	}
+	if (ratectr->all_rtp_conn_stats == NULL) {
+		ratectr->all_rtp_conn_stats = rate_ctr_group_alloc(ctx, &all_rtp_conn_rate_ctr_group_desc,
+								 all_rtp_conn_rate_ctr_index);
+		if (!ratectr->all_rtp_conn_stats)
+			return -EINVAL;
+		talloc_set_destructor(ratectr->all_rtp_conn_stats, free_rate_counter_group);
+		all_rtp_conn_rate_ctr_index++;
+	}
+	return 0;
+}
diff --git a/src/libosmo-mgcp/mgcp_sdp.c b/src/libosmo-mgcp/mgcp_sdp.c
index 428bde6..f80ebb8 100644
--- a/src/libosmo-mgcp/mgcp_sdp.c
+++ b/src/libosmo-mgcp/mgcp_sdp.c
@@ -25,6 +25,7 @@
 #include <osmocom/mgcp/mgcp_internal.h>
 #include <osmocom/mgcp/mgcp_msg.h>
 #include <osmocom/mgcp/mgcp_endp.h>
+#include <osmocom/mgcp/mgcp_trunk.h>
 #include <osmocom/mgcp/mgcp_codec.h>
 #include <osmocom/mgcp/mgcp_sdp.h>
 
@@ -358,10 +359,11 @@
 			break;
 		default:
 			if (p->endp)
+				/* TODO: Check spec: We used the bare endpoint number before,
+				 * now we use the endpoint name as a whole? Is this allowed? */
 				LOGP(DLMGCP, LOGL_NOTICE,
-				     "Unhandled SDP option: '%c'/%d on 0x%x\n",
-				     line[0], line[0],
-				     ENDPOINT_NUMBER(p->endp));
+				     "Unhandled SDP option: '%c'/%d on %s\n",
+				     line[0], line[0], endp->name);
 			else
 				LOGP(DLMGCP, LOGL_NOTICE,
 				     "Unhandled SDP option: '%c'/%d\n",
@@ -381,7 +383,7 @@
 		codec_param = param_by_pt(codecs[i].payload_type, fmtp_params, fmtp_used);
 		rc = mgcp_codec_add(conn, codecs[i].payload_type, codecs[i].map_line, codec_param);
 		if (rc < 0)
-			LOGP(DLMGCP, LOGL_NOTICE, "endpoint:0x%x, failed to add codec\n", ENDPOINT_NUMBER(p->endp));
+			LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "failed to add codec\n");
 	}
 
 	talloc_free(tmp_ctx);
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 at selfish.org>
+ * (C) 2009-2012 by On-Waves
+ * (C) 2017-2020 by sysmocom s.f.m.c. GmbH <info at 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;
+}
diff --git a/src/libosmo-mgcp/mgcp_vty.c b/src/libosmo-mgcp/mgcp_vty.c
index 7278c1c..54b139a 100644
--- a/src/libosmo-mgcp/mgcp_vty.c
+++ b/src/libosmo-mgcp/mgcp_vty.c
@@ -29,6 +29,7 @@
 #include <osmocom/mgcp/vty.h>
 #include <osmocom/mgcp/mgcp_conn.h>
 #include <osmocom/mgcp/mgcp_endp.h>
+#include <osmocom/mgcp/mgcp_trunk.h>
 
 #include <string.h>
 #include <inttypes.h>
@@ -49,7 +50,7 @@
 	if (nr == 0)
 		trunk = cfg->virt_trunk;
 	else
-		trunk = mgcp_trunk_num(cfg, nr);
+		trunk = mgcp_trunk_by_num(cfg, nr);
 
 	return trunk;
 }
@@ -208,15 +209,13 @@
 		end->force_output_ptime, VTY_NEWLINE);
 }
 
-static void dump_endpoint(struct vty *vty, struct mgcp_endpoint *endp, int epidx,
+static void dump_endpoint(struct vty *vty, struct mgcp_endpoint *endp,
 			  int trunk_nr, enum mgcp_trunk_type trunk_type, int show_stats)
 {
 	struct mgcp_conn *conn;
 
-	vty_out(vty, "%s trunk %d endpoint %s%.2x:%s",
-		trunk_type == MGCP_TRUNK_VIRTUAL ? "Virtual" : "E1", trunk_nr,
-		trunk_type == MGCP_TRUNK_VIRTUAL ? MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK : "",
-		epidx, VTY_NEWLINE);
+	vty_out(vty, "%s trunk %d endpoint %s:%s",
+		trunk_type == MGCP_TRUNK_VIRTUAL ? "Virtual" : "E1", trunk_nr, endp->name, VTY_NEWLINE);
 
 	if (llist_empty(&endp->conns)) {
 		vty_out(vty, "   No active connections%s", VTY_NEWLINE);
@@ -244,50 +243,84 @@
 	}
 }
 
-static void dump_trunk(struct vty *vty, struct mgcp_trunk *cfg, int show_stats)
+static void dump_ratectr_global(struct vty *vty, struct mgcp_ratectr_global *ratectr)
+{
+	vty_out(vty, "%s", VTY_NEWLINE);
+	vty_out(vty, "Rate counters (global):%s", VTY_NEWLINE);
+
+	if (ratectr->mgcp_general_ctr_group) {
+		vty_out(vty, "   %s:%s",
+			ratectr->mgcp_general_ctr_group->desc->
+			group_description, VTY_NEWLINE);
+		vty_out_rate_ctr_group_fmt(vty,
+					   "   %25n: %10c (%S/s %M/m %H/h %D/d) %d",
+					   ratectr->mgcp_general_ctr_group);
+	}
+}
+
+static void dump_ratectr_trunk(struct vty *vty, struct mgcp_ratectr_trunk *ratectr)
+{
+	vty_out(vty, "%s", VTY_NEWLINE);
+	vty_out(vty, "Rate counters (trunk):%s", VTY_NEWLINE);
+
+	if (ratectr->mgcp_crcx_ctr_group) {
+		vty_out(vty, "   %s:%s",
+			ratectr->mgcp_crcx_ctr_group->desc->group_description,
+			VTY_NEWLINE);
+		vty_out_rate_ctr_group_fmt(vty,
+					   "   %25n: %10c (%S/s %M/m %H/h %D/d) %d",
+					   ratectr->mgcp_crcx_ctr_group);
+	}
+	if (ratectr->mgcp_dlcx_ctr_group) {
+		vty_out(vty, "   %s:%s",
+			ratectr->mgcp_dlcx_ctr_group->desc->group_description,
+			VTY_NEWLINE);
+		vty_out_rate_ctr_group_fmt(vty,
+					   "   %25n: %10c (%S/s %M/m %H/h %D/d) %d",
+					   ratectr->mgcp_dlcx_ctr_group);
+	}
+	if (ratectr->mgcp_mdcx_ctr_group) {
+		vty_out(vty, "   %s:%s",
+			ratectr->mgcp_mdcx_ctr_group->desc->group_description,
+			VTY_NEWLINE);
+		vty_out_rate_ctr_group_fmt(vty,
+					   "   %25n: %10c (%S/s %M/m %H/h %D/d) %d",
+					   ratectr->mgcp_mdcx_ctr_group);
+	}
+	if (ratectr->all_rtp_conn_stats) {
+		vty_out(vty, "   %s:%s",
+			ratectr->all_rtp_conn_stats->desc->group_description,
+			VTY_NEWLINE);
+		vty_out_rate_ctr_group_fmt(vty,
+					   "   %25n: %10c (%S/s %M/m %H/h %D/d) %d",
+					   ratectr->all_rtp_conn_stats);
+	}
+}
+
+
+static void dump_trunk(struct vty *vty, struct mgcp_trunk *trunk, int show_stats)
 {
 	int i;
 
 	vty_out(vty, "%s trunk %d with %d endpoints:%s",
-		cfg->trunk_type == MGCP_TRUNK_VIRTUAL ? "Virtual" : "E1",
-		cfg->trunk_nr, cfg->number_endpoints - 1, VTY_NEWLINE);
+		trunk->trunk_type == MGCP_TRUNK_VIRTUAL ? "Virtual" : "E1",
+		trunk->trunk_nr, trunk->number_endpoints - 1, VTY_NEWLINE);
 
-	if (!cfg->endpoints) {
+	if (!trunk->endpoints) {
 		vty_out(vty, "No endpoints allocated yet.%s", VTY_NEWLINE);
 		return;
 	}
 
-	for (i = 0; i < cfg->number_endpoints; ++i) {
-		struct mgcp_endpoint *endp = &cfg->endpoints[i];
-		dump_endpoint(vty, endp, i, cfg->trunk_nr, cfg->trunk_type, show_stats);
-		if (i < cfg->number_endpoints - 1)
+	for (i = 0; i < trunk->number_endpoints; ++i) {
+		struct mgcp_endpoint *endp = trunk->endpoints[i];
+		dump_endpoint(vty, endp, trunk->trunk_nr, trunk->trunk_type,
+			      show_stats);
+		if (i < trunk->number_endpoints - 1)
 			vty_out(vty, "%s", VTY_NEWLINE);
 	}
 
-	if (show_stats) {
-		vty_out(vty, "%s", VTY_NEWLINE);
-		vty_out(vty, "Rate counters:%s", VTY_NEWLINE);
-	}
-	if (show_stats && cfg->mgcp_general_ctr_group) {
-		vty_out(vty, "   %s:%s", cfg->mgcp_general_ctr_group->desc->group_description, VTY_NEWLINE);
-		vty_out_rate_ctr_group_fmt(vty, "   %25n: %10c (%S/s %M/m %H/h %D/d) %d", cfg->mgcp_general_ctr_group);
-	}
-	if (show_stats && cfg->mgcp_crcx_ctr_group) {
-		vty_out(vty, "   %s:%s", cfg->mgcp_crcx_ctr_group->desc->group_description, VTY_NEWLINE);
-		vty_out_rate_ctr_group_fmt(vty, "   %25n: %10c (%S/s %M/m %H/h %D/d) %d", cfg->mgcp_crcx_ctr_group);
-	}
-	if (show_stats && cfg->mgcp_dlcx_ctr_group) {
-		vty_out(vty, "   %s:%s", cfg->mgcp_dlcx_ctr_group->desc->group_description, VTY_NEWLINE);
-		vty_out_rate_ctr_group_fmt(vty, "   %25n: %10c (%S/s %M/m %H/h %D/d) %d", cfg->mgcp_dlcx_ctr_group);
-	}
-	if (show_stats && cfg->mgcp_mdcx_ctr_group) {
-		vty_out(vty, "   %s:%s", cfg->mgcp_mdcx_ctr_group->desc->group_description, VTY_NEWLINE);
-		vty_out_rate_ctr_group_fmt(vty, "   %25n: %10c (%S/s %M/m %H/h %D/d) %d", cfg->mgcp_mdcx_ctr_group);
-	}
-	if (show_stats && cfg->all_rtp_conn_stats) {
-		vty_out(vty, "   %s:%s", cfg->all_rtp_conn_stats->desc->group_description, VTY_NEWLINE);
-		vty_out_rate_ctr_group_fmt(vty, "   %25n: %10c (%S/s %M/m %H/h %D/d) %d", cfg->all_rtp_conn_stats);
-	}
+	if (show_stats)
+		dump_ratectr_trunk(vty, &trunk->ratectr);
 }
 
 #define SHOW_MGCP_STR "Display information about the MGCP Media Gateway\n"
@@ -304,44 +337,41 @@
 	dump_trunk(vty, g_cfg->virt_trunk, show_stats);
 
 	llist_for_each_entry(trunk, &g_cfg->trunks, entry)
-	    dump_trunk(vty, trunk, show_stats);
+		dump_trunk(vty, trunk, show_stats);
 
 	if (g_cfg->osmux)
 		vty_out(vty, "Osmux used CID: %d%s", osmux_cid_pool_count_used(),
 			VTY_NEWLINE);
 
+	if (show_stats)
+		dump_ratectr_global(vty, &g_cfg->ratectr);
+
 	return CMD_SUCCESS;
 }
 
 static void
 dump_mgcp_endpoint(struct vty *vty, struct mgcp_trunk *trunk, const char *epname)
 {
-	const size_t virt_prefix_len = sizeof(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK) - 1;
-	unsigned long epidx;
-	char *endp;
-	int i;
+	struct mgcp_endpoint *endp;
 
-	if (strncmp(epname, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, virt_prefix_len) == 0)
-		epname += virt_prefix_len;
-	errno = 0;
-	epidx = strtoul(epname, &endp, 16);
-	if (epname[0] == '\0' || *endp != '\0') {
-		vty_out(vty, "endpoint name '%s' is not a hex number%s", epname, VTY_NEWLINE);
-		return;
-	}
-	if ((errno == ERANGE && epidx == ULONG_MAX) /* parsed value out of range */
-	    || epidx >= trunk->number_endpoints) {
-		vty_out(vty, "endpoint %.2lx not configured on trunk %d%s", epidx, trunk->trunk_nr, VTY_NEWLINE);
-		return;
-	}
-
-	for (i = 0; i < trunk->number_endpoints; ++i) {
-		struct mgcp_endpoint *endp = &trunk->endpoints[i];
-		if (i == epidx) {
-			dump_endpoint(vty, endp, i, trunk->trunk_nr, trunk->trunk_type, true);
-			break;
+	if (trunk) {
+		/* If a trunk is given, search on that specific trunk only */
+		endp = mgcp_endp_by_name_trunk(NULL, epname, trunk);
+		if (!endp) {
+			vty_out(vty, "endpoint %s not configured on trunk %d%s", epname, trunk->trunk_nr, VTY_NEWLINE);
+			return;
+		}
+	} else {
+		/* If no trunk is given, search on all possible trunks */
+		endp = mgcp_endp_by_name(NULL, epname, g_cfg);
+		if (!endp) {
+			vty_out(vty, "endpoint %s not configured%s", epname, VTY_NEWLINE);
+			return;
 		}
 	}
+
+	trunk = endp->trunk;
+	dump_endpoint(vty, endp, trunk->trunk_nr, trunk->trunk_type, true);
 }
 
 DEFUN(show_mcgp_endpoint, show_mgcp_endpoint_cmd,
@@ -350,12 +380,7 @@
       SHOW_MGCP_STR
       "Display information about an endpoint\n" "The name of the endpoint\n")
 {
-	struct mgcp_trunk *trunk;
-
-	dump_mgcp_endpoint(vty, g_cfg->virt_trunk, argv[0]);
-	llist_for_each_entry(trunk, &g_cfg->trunks, entry)
-		dump_mgcp_endpoint(vty, trunk, argv[0]);
-
+	dump_mgcp_endpoint(vty, NULL, argv[0]);
 	return CMD_SUCCESS;
 }
 
@@ -810,7 +835,7 @@
 	struct mgcp_trunk *trunk;
 	int index = atoi(argv[0]);
 
-	trunk = mgcp_trunk_num(g_cfg, index);
+	trunk = mgcp_trunk_by_num(g_cfg, index);
 	if (!trunk) {
 		trunk = mgcp_trunk_alloc(g_cfg, MGCP_TRUNK_E1, index);
 		if (!trunk) {
@@ -1138,7 +1163,7 @@
 		return CMD_WARNING;
 	}
 
-	endp = &trunk->endpoints[endp_no];
+	endp = trunk->endpoints[endp_no];
 	int loop = atoi(argv[2]);
 	llist_for_each_entry(conn, &endp->conns, entry) {
 		if (conn->type == MGCP_CONN_TYPE_RTP)
@@ -1197,7 +1222,7 @@
 		return CMD_WARNING;
 	}
 
-	endp = &trunk->endpoints[endp_no];
+	endp = trunk->endpoints[endp_no];
 
 	conn_id = argv[2];
 	conn = mgcp_conn_get_rtp(endp, conn_id);
@@ -1250,7 +1275,7 @@
 		return CMD_WARNING;
 	}
 
-	endp = &trunk->endpoints[endp_no];
+	endp = trunk->endpoints[endp_no];
 	mgcp_endp_release(endp);
 	return CMD_SUCCESS;
 }
@@ -1283,8 +1308,8 @@
 		return CMD_WARNING;
 	}
 
-	endp = &trunk->endpoints[endp_no];
-	rc = mgcp_send_reset_ep(endp, ENDPOINT_NUMBER(endp));
+	endp = trunk->endpoints[endp_no];
+	rc = mgcp_send_reset_ep(endp);
 	if (rc < 0) {
 		vty_out(vty, "Error %d sending reset.%s", rc, VTY_NEWLINE);
 		return CMD_WARNING;
@@ -1521,7 +1546,7 @@
 		return -1;
 	}
 
-	if (mgcp_endpoints_allocate(g_cfg->virt_trunk) != 0) {
+	if (mgcp_trunk_alloc_endpts(g_cfg->virt_trunk) != 0) {
 		LOGP(DLMGCP, LOGL_ERROR,
 		     "Failed to initialize the virtual trunk (%d endpoints)\n",
 		     g_cfg->virt_trunk->number_endpoints);
@@ -1529,7 +1554,7 @@
 	}
 
 	llist_for_each_entry(trunk, &g_cfg->trunks, entry) {
-		if (mgcp_endpoints_allocate(trunk) != 0) {
+		if (mgcp_trunk_alloc_endpts(trunk) != 0) {
 			LOGP(DLMGCP, LOGL_ERROR,
 			     "Failed to initialize trunk %d (%d endpoints)\n",
 			     trunk->trunk_nr, trunk->number_endpoints);
diff --git a/src/osmo-mgw/mgw_main.c b/src/osmo-mgw/mgw_main.c
index 22f2ab8..99e2499 100644
--- a/src/osmo-mgw/mgw_main.c
+++ b/src/osmo-mgw/mgw_main.c
@@ -38,6 +38,7 @@
 #include <osmocom/mgcp/vty.h>
 #include <osmocom/mgcp/debug.h>
 #include <osmocom/mgcp/mgcp_endp.h>
+#include <osmocom/mgcp/mgcp_trunk.h>
 #include <osmocom/mgcp/mgcp_ctrl.h>
 
 #include <osmocom/core/application.h>
@@ -203,7 +204,7 @@
 		/* Walk over all endpoints and trigger a release, this will release all
 		 * endpoints, possible open connections are forcefully dropped */
 		for (i = 1; i < reset_trunk->number_endpoints; ++i)
-			mgcp_endp_release(&reset_trunk->endpoints[i]);
+			mgcp_endp_release(reset_trunk->endpoints[i]);
 	}
 
 	return 0;
diff --git a/tests/mgcp/mgcp_test.c b/tests/mgcp/mgcp_test.c
index fa38296..ed0fda0 100644
--- a/tests/mgcp/mgcp_test.c
+++ b/tests/mgcp/mgcp_test.c
@@ -26,6 +26,7 @@
 #include <osmocom/mgcp/mgcp_stat.h>
 #include <osmocom/mgcp/mgcp_msg.h>
 #include <osmocom/mgcp/mgcp_endp.h>
+#include <osmocom/mgcp/mgcp_trunk.h>
 #include <osmocom/mgcp/mgcp_sdp.h>
 #include <osmocom/mgcp/mgcp_codec.h>
 
@@ -70,7 +71,7 @@
 }
 
 #define AUEP1	"AUEP 158663169 ds/e1-1/2 at mgw MGCP 1.0\r\n"
-#define AUEP1_RET "200 158663169 OK\r\n"
+#define AUEP1_RET "500 158663169 FAIL\r\n"
 #define AUEP2	"AUEP 18983213 ds/e1-2/1 at mgw MGCP 1.0\r\n"
 #define AUEP2_RET "500 18983213 FAIL\r\n"
 #define EMPTY	"\r\n"
@@ -81,7 +82,7 @@
 #define MDCX_WRONG_EP "MDCX 18983213 ds/e1-3/1 at mgw MGCP 1.0\r\n"
 #define MDCX_ERR_RET "500 18983213 FAIL\r\n"
 #define MDCX_UNALLOCATED "MDCX 18983214 ds/e1-1/2 at mgw MGCP 1.0\r\n"
-#define MDCX_RET "400 18983214 FAIL\r\n"
+#define MDCX_RET "500 18983214 FAIL\r\n"
 
 #define MDCX3 \
 	"MDCX 18983215 1 at mgw MGCP 1.0\r\n" \
@@ -593,12 +594,22 @@
 
 static int last_endpoint = -1;
 
-static int mgcp_test_policy_cb(struct mgcp_trunk *cfg, int endpoint,
-			       int state, const char *transactio_id)
+static int mgcp_test_policy_cb(struct mgcp_endpoint *endp,
+			       int state, const char *transaction_id)
 {
-	fprintf(stderr, "Policy CB got state %d on endpoint 0x%x\n",
-		state, endpoint);
-	last_endpoint = endpoint;
+	unsigned int i;
+	struct mgcp_trunk *trunk;
+
+	fprintf(stderr, "Policy CB got state %d on endpoint %s\n",
+		state, endp->name);
+
+	trunk = endp->trunk;
+	last_endpoint = -1;
+	for (i = 0; i < trunk->vty_number_endpoints; i++) {
+		if (strcmp(endp->name, trunk->endpoints[i]->name) == 0)
+			last_endpoint = i;
+	}
+
 	return MGCP_POLICY_CONT;
 }
 
@@ -645,7 +656,7 @@
 {
 	int i;
 	for (i = 1; i < trunk->number_endpoints; i++)
-		mgcp_endp_release(&trunk->endpoints[i]);
+		mgcp_endp_release(trunk->endpoints[i]);
 }
 
 #define CONN_UNMODIFIED (0x1000)
@@ -758,13 +769,13 @@
 	cfg = mgcp_config_alloc();
 
 	cfg->virt_trunk->vty_number_endpoints = 64;
-	mgcp_endpoints_allocate(cfg->virt_trunk);
+        mgcp_trunk_alloc_endpts(cfg->virt_trunk);
 	cfg->policy_cb = mgcp_test_policy_cb;
 
 	memset(last_conn_id, 0, sizeof(last_conn_id));
 
 	trunk2 = mgcp_trunk_alloc(cfg, MGCP_TRUNK_E1, 1);
-	mgcp_endpoints_allocate(trunk2);
+        mgcp_trunk_alloc_endpts(trunk2);
 
 	for (i = 0; i < ARRAY_SIZE(tests); i++) {
 		const struct mgcp_test *t = &tests[i];
@@ -810,7 +821,7 @@
 			printf("Dummy packets: %d\n", dummy_packets);
 
 		if (last_endpoint != -1) {
-			endp = &cfg->virt_trunk->endpoints[last_endpoint];
+			endp = cfg->virt_trunk->endpoints[last_endpoint];
 
 			conn = mgcp_conn_get_rtp(endp, "1");
 			if (conn) {
@@ -866,7 +877,7 @@
 		/* Check detected payload type */
 		if (conn && t->ptype != PTYPE_IGNORE) {
 			OSMO_ASSERT(last_endpoint != -1);
-			endp = &cfg->virt_trunk->endpoints[last_endpoint];
+			endp = cfg->virt_trunk->endpoints[last_endpoint];
 
 			fprintf(stderr, "endpoint 0x%x: "
 				"payload type %d (expected %d)\n",
@@ -898,12 +909,12 @@
 	cfg = mgcp_config_alloc();
 
 	cfg->virt_trunk->vty_number_endpoints = 64;
-	mgcp_endpoints_allocate(cfg->virt_trunk);
+        mgcp_trunk_alloc_endpts(cfg->virt_trunk);
 
 	memset(last_conn_id, 0, sizeof(last_conn_id));
 
 	trunk2 = mgcp_trunk_alloc(cfg, MGCP_TRUNK_E1, 1);
-	mgcp_endpoints_allocate(trunk2);
+        mgcp_trunk_alloc_endpts(trunk2);
 
 	for (i = 0; i < ARRAY_SIZE(retransmit); i++) {
 		const struct mgcp_test *t = &retransmit[i];
@@ -966,10 +977,10 @@
 	cfg->rqnt_cb = rqnt_cb;
 
 	cfg->virt_trunk->vty_number_endpoints = 64;
-	mgcp_endpoints_allocate(cfg->virt_trunk);
+        mgcp_trunk_alloc_endpts(cfg->virt_trunk);
 
 	trunk2 = mgcp_trunk_alloc(cfg, MGCP_TRUNK_E1, 1);
-	mgcp_endpoints_allocate(trunk2);
+        mgcp_trunk_alloc_endpts(trunk2);
 
 	inp = create_msg(CRCX, NULL);
 	msg = mgcp_handle_message(cfg, inp);
@@ -1035,6 +1046,7 @@
 {
 	int i;
 	struct mgcp_endpoint endp;
+	struct mgcp_endpoint *endpoints[1];
 	struct mgcp_config cfg = {0};
 	struct mgcp_trunk trunk;
 
@@ -1046,7 +1058,8 @@
 	endp.cfg = &cfg;
 	endp.type = &ep_typeset.rtp;
 	trunk.vty_number_endpoints = 1;
-	trunk.endpoints = &endp;
+	trunk.endpoints = endpoints;
+	trunk.endpoints[0] = &endp;
 	endp.trunk = &trunk;
 	INIT_LLIST_HEAD(&endp.conns);
 
@@ -1264,6 +1277,7 @@
 
 	struct mgcp_trunk trunk;
 	struct mgcp_endpoint endp;
+	struct mgcp_endpoint *endpoints[1];
 	struct mgcp_config cfg = {0};
 	struct mgcp_rtp_state state;
 	struct mgcp_rtp_end *rtp;
@@ -1296,7 +1310,8 @@
 	endp.type = &ep_typeset.rtp;
 
 	trunk.vty_number_endpoints = 1;
-	trunk.endpoints = &endp;
+	trunk.endpoints = endpoints;
+	trunk.endpoints[0] = &endp;
 	trunk.force_constant_ssrc = patch_ssrc;
 	trunk.force_aligned_timing = patch_ts;
 
@@ -1372,11 +1387,11 @@
 
 	cfg = mgcp_config_alloc();
 	cfg->virt_trunk->vty_number_endpoints = 64;
-	mgcp_endpoints_allocate(cfg->virt_trunk);
+        mgcp_trunk_alloc_endpts(cfg->virt_trunk);
 	cfg->policy_cb = mgcp_test_policy_cb;
 
 	trunk2 = mgcp_trunk_alloc(cfg, MGCP_TRUNK_E1, 1);
-	mgcp_endpoints_allocate(trunk2);
+        mgcp_trunk_alloc_endpts(trunk2);
 
 	/* Allocate endpoint 1 at mgw with two codecs */
 	last_endpoint = -1;
@@ -1388,7 +1403,7 @@
 	msgb_free(resp);
 
 	OSMO_ASSERT(last_endpoint == 1);
-	endp = &cfg->virt_trunk->endpoints[last_endpoint];
+	endp = cfg->virt_trunk->endpoints[last_endpoint];
 	conn = mgcp_conn_get_rtp(endp, conn_id);
 	OSMO_ASSERT(conn);
 	OSMO_ASSERT(conn->end.codec->payload_type == 18);
@@ -1403,7 +1418,7 @@
 	msgb_free(resp);
 
 	OSMO_ASSERT(last_endpoint == 2);
-	endp = &cfg->virt_trunk->endpoints[last_endpoint];
+	endp = cfg->virt_trunk->endpoints[last_endpoint];
 	conn = mgcp_conn_get_rtp(endp, conn_id);
 	OSMO_ASSERT(conn);
 	OSMO_ASSERT(conn->end.codec->payload_type == 18);
@@ -1423,7 +1438,7 @@
 	msgb_free(resp);
 
 	OSMO_ASSERT(last_endpoint == 3);
-	endp = &cfg->virt_trunk->endpoints[last_endpoint];
+	endp = cfg->virt_trunk->endpoints[last_endpoint];
 	conn = mgcp_conn_get_rtp(endp, conn_id);
 	OSMO_ASSERT(conn);
 	OSMO_ASSERT(conn->end.codec->payload_type == 0);
@@ -1438,7 +1453,7 @@
 	msgb_free(resp);
 
 	OSMO_ASSERT(last_endpoint == 4);
-	endp = &cfg->virt_trunk->endpoints[last_endpoint];
+	endp = cfg->virt_trunk->endpoints[last_endpoint];
 	conn = mgcp_conn_get_rtp(endp, conn_id);
 	OSMO_ASSERT(conn);
 	OSMO_ASSERT(conn->end.codec->payload_type == 18);
@@ -1456,7 +1471,7 @@
 	msgb_free(resp);
 
 	OSMO_ASSERT(last_endpoint == 5);
-	endp = &cfg->virt_trunk->endpoints[last_endpoint];
+	endp = cfg->virt_trunk->endpoints[last_endpoint];
 	conn = mgcp_conn_get_rtp(endp, conn_id);
 	OSMO_ASSERT(conn);
 	OSMO_ASSERT(conn->end.codec->payload_type == 3);
@@ -1467,7 +1482,7 @@
 	msgb_free(inp);
 	msgb_free(resp);
 	OSMO_ASSERT(last_endpoint == 5);
-	endp = &cfg->virt_trunk->endpoints[last_endpoint];
+	endp = cfg->virt_trunk->endpoints[last_endpoint];
 	conn = mgcp_conn_get_rtp(endp, conn_id);
 	OSMO_ASSERT(conn);
 	OSMO_ASSERT(conn->end.codec->payload_type == 3);
@@ -1497,7 +1512,7 @@
 	msgb_free(resp);
 
 	OSMO_ASSERT(last_endpoint == 5);
-	endp = &cfg->virt_trunk->endpoints[last_endpoint];
+	endp = cfg->virt_trunk->endpoints[last_endpoint];
 	conn = mgcp_conn_get_rtp(endp, conn_id);
 	OSMO_ASSERT(conn);
 	OSMO_ASSERT(conn->end.codec->payload_type == 0);
@@ -1518,9 +1533,9 @@
 
 	cfg = mgcp_config_alloc();
 	cfg->virt_trunk->vty_number_endpoints = 64;
-	mgcp_endpoints_allocate(cfg->virt_trunk);
+        mgcp_trunk_alloc_endpts(cfg->virt_trunk);
 
-	endp = &cfg->virt_trunk->endpoints[1];
+	endp = cfg->virt_trunk->endpoints[1];
 
 	_conn = mgcp_conn_alloc(NULL, endp, MGCP_CONN_TYPE_RTP,
 				"test-connection");
@@ -1567,12 +1582,12 @@
 
 	cfg->virt_trunk->vty_number_endpoints = 64;
 	cfg->virt_trunk->audio_send_name = 0;
-	mgcp_endpoints_allocate(cfg->virt_trunk);
+        mgcp_trunk_alloc_endpts(cfg->virt_trunk);
 
 	cfg->policy_cb = mgcp_test_policy_cb;
 
 	trunk2 = mgcp_trunk_alloc(cfg, MGCP_TRUNK_E1, 1);
-	mgcp_endpoints_allocate(trunk2);
+        mgcp_trunk_alloc_endpts(trunk2);
 
 	inp = create_msg(CRCX, NULL);
 	msg = mgcp_handle_message(cfg, inp);

-- 
To view, visit https://gerrit.osmocom.org/c/osmo-mgw/+/18644
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings

Gerrit-Project: osmo-mgw
Gerrit-Branch: master
Gerrit-Change-Id: Ia8cf4d6caf05a4e13f1f507dc68cbabb7e6239aa
Gerrit-Change-Number: 18644
Gerrit-PatchSet: 7
Gerrit-Owner: dexter <pmaier at sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: dexter <pmaier at sysmocom.de>
Gerrit-Reviewer: laforge <laforge at osmocom.org>
Gerrit-Reviewer: neels <nhofmeyr at sysmocom.de>
Gerrit-Reviewer: pespin <pespin at sysmocom.de>
Gerrit-MessageType: merged
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20200615/02587b54/attachment.htm>


More information about the gerrit-log mailing list