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
Tue May 19 19:49:47 UTC 2020


dexter has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmo-mgw/+/18372 )


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 placed (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.

- rename struct mgcp_trunk_config to mgcp_trunk and "tcfg" to "trunk"
  in order to better match the reality.

- refactor trunk and endpoint allocation. Aggregate functionality in
  in mgcp_endp.c and mgcp_trunk.c.

Change-Id: Ice8aaf03faa2fd99074f8665eea3a696d30c5eb3
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_trunk.h
A include/osmocom/mgcp_client/mgcp_common.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
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
20 files changed, 1,054 insertions(+), 782 deletions(-)



  git pull ssh://gerrit.osmocom.org:29418/osmo-mgw refs/changes/72/18372/1

diff --git a/include/osmocom/mgcp/Makefile.am b/include/osmocom/mgcp/Makefile.am
index 036b4ca..0d5f5f6 100644
--- a/include/osmocom/mgcp/Makefile.am
+++ b/include/osmocom/mgcp/Makefile.am
@@ -7,5 +7,6 @@
 	mgcp_sdp.h \
 	mgcp_codec.h \
 	mgcp_ctrl.h \
+	mgcp_trunk.h \
 	debug.h \
 	$(NULL)
diff --git a/include/osmocom/mgcp/mgcp.h b/include/osmocom/mgcp/mgcp.h
index 43f480e..99c12a1 100644
--- a/include/osmocom/mgcp/mgcp.h
+++ b/include/osmocom/mgcp/mgcp.h
@@ -42,7 +42,7 @@
  */
 struct mgcp_endpoint;
 struct mgcp_config;
-struct mgcp_trunk_config;
+struct mgcp_trunk;
 struct mgcp_rtp_end;
 
 #define MGCP_ENDP_CRCX 1
@@ -59,10 +59,9 @@
 #define MGCP_POLICY_REJECT	5
 #define MGCP_POLICY_DEFER	6
 
-typedef int (*mgcp_realloc)(struct mgcp_trunk_config *cfg, int endpoint);
-typedef int (*mgcp_change)(struct mgcp_trunk_config *cfg, int endpoint, int state);
-typedef int (*mgcp_policy)(struct mgcp_trunk_config *cfg, int endpoint, int state, const char *transactio_id);
-typedef int (*mgcp_reset)(struct mgcp_trunk_config *cfg);
+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);
 
 /**
@@ -178,57 +177,6 @@
 	MGCP_DLCX_DEFERRED_BY_POLICY,
 };
 
-struct mgcp_trunk_config {
-	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,
@@ -256,14 +204,13 @@
 	mgcp_change change_cb;
 	mgcp_policy policy_cb;
 	mgcp_reset reset_cb;
-	mgcp_realloc realloc_cb;
 	mgcp_rqnt rqnt_cb;
 	void *data;
 
 	uint32_t last_call_id;
 
 	/* trunk handling */
-	struct mgcp_trunk_config trunk;
+	struct mgcp_trunk *trunk;
 	struct llist_head trunks;
 
 	enum mgcp_role role;
@@ -301,8 +248,7 @@
 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_config *cfg);
-void mgcp_trunk_set_keepalive(struct mgcp_trunk_config *tcfg, int interval);
+void mgcp_trunk_set_keepalive(struct mgcp_trunk *trunk, int interval);
 
 /*
  * format helper functions
@@ -310,7 +256,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 75f093d..79399a4 100644
--- a/include/osmocom/mgcp/mgcp_endp.h
+++ b/include/osmocom/mgcp/mgcp_endp.h
@@ -63,6 +63,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;
 
@@ -75,8 +78,8 @@
 	/*! Backpointer to the MGW configuration */
 	struct mgcp_config *cfg;
 
-	/*! Backpointer to the Trunk specific configuration */
-	struct mgcp_trunk_config *tcfg;
+	/*! Backpointer to the related trunk */
+	struct mgcp_trunk *trunk;
 
 	/*! Endpoint properties (see above) */
 	const struct mgcp_endpoint_type *type;
@@ -95,7 +98,9 @@
 	uint32_t x_osmo_ign;
 };
 
-/*! Extract endpoint number for a given endpoint */
-#define ENDPOINT_NUMBER(endp) abs((int)(endp - endp->tcfg->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,
+					      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 e9d5d2d..557559f 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_config *mgcp_trunk_alloc(struct mgcp_config *cfg, int index);
-struct mgcp_trunk_config *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,
@@ -342,8 +339,8 @@
 void mgcp_conn_watchdog_kick(struct mgcp_conn *conn);
 
 #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)
 
 #define LOGPCONN(conn, cat, level, fmt, args...) \
diff --git a/include/osmocom/mgcp/mgcp_trunk.h b/include/osmocom/mgcp/mgcp_trunk.h
new file mode 100644
index 0000000..7c37ead
--- /dev/null
+++ b/include/osmocom/mgcp/mgcp_trunk.h
@@ -0,0 +1,57 @@
+#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;
+	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;
+};
+
+struct mgcp_trunk *mgcp_trunk_alloc(struct mgcp_config *cfg, int nr, int trunk_type);
+int mgcp_trunk_alloc_endpts(struct mgcp_trunk *tcfg);
+struct mgcp_trunk *mgcp_trunk_by_num(struct mgcp_config *cfg, int index);
+struct mgcp_trunk *mgcp_trunk_by_name(const char *epname, struct mgcp_config *cfg);
diff --git a/include/osmocom/mgcp_client/mgcp_common.h b/include/osmocom/mgcp_client/mgcp_common.h
new file mode 100644
index 0000000..058d94a
--- /dev/null
+++ b/include/osmocom/mgcp_client/mgcp_common.h
@@ -0,0 +1,118 @@
+/*
+
+   DO NOT EDIT THIS FILE!
+   THIS IS OVERWRITTEN DURING BUILD
+   This is an automatic copy of <osmocom/mgcp/mgcp_common.h>
+
+ */
+/* MGCP common implementations.
+ * These are used in libosmo-mgcp as well as libosmo-mgcp-client.
+ * To avoid interdependency, these are implemented in .h file only. */
+
+/*
+ * (C) 2017 by sysmocom s.f.m.c. GmbH <info at sysmocom.de>
+ * (C) 2009-2012 by Holger Hans Peter Freyther <zecke at selfish.org>
+ * (C) 2009-2012 by On-Waves
+ * 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/>.
+ *
+ */
+
+/* Two copies of this file are kept in osmocom/mgcp/ and osmocom/mgcp_client/.
+ * Since both are by definition identical, use the old header exclusion ifdefs
+ * instead of '#pragma once' to avoid including both of these files.
+ * Though at the time of writing there are no such users, this allows including
+ * both libosmo-mgcp and libosmo-mgcp-client headers in the same file. */
+#ifndef OSMO_MGCP_COMMON_H
+#define OSMO_MGCP_COMMON_H
+
+#include <string.h>
+#include <errno.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/logging.h>
+
+#define for_each_non_empty_line(line, save)			\
+	for (line = strtok_r(NULL, "\r\n", &save); line;	\
+	     line = strtok_r(NULL, "\r\n", &save))
+
+enum mgcp_connection_mode {
+	MGCP_CONN_NONE = 0,
+	MGCP_CONN_RECV_ONLY = 1,
+	MGCP_CONN_SEND_ONLY = 2,
+	MGCP_CONN_RECV_SEND = MGCP_CONN_RECV_ONLY | MGCP_CONN_SEND_ONLY,
+	MGCP_CONN_LOOPBACK  = 4 | MGCP_CONN_RECV_SEND,
+};
+
+#define MGCP_X_OSMO_IGN_HEADER "X-Osmo-IGN:"
+#define MGCP_X_OSMO_OSMUX_HEADER "X-Osmux:"
+
+/* Values should be bitwise-OR-able */
+enum mgcp_x_osmo_ign {
+	MGCP_X_OSMO_IGN_NONE = 0,
+	MGCP_X_OSMO_IGN_CALLID = 1,
+};
+
+/* Codec parameters (communicated via SDP/fmtp) */
+struct mgcp_codec_param {
+	bool amr_octet_aligned_present;
+	bool amr_octet_aligned;
+};
+
+/* Ensure that the msg->l2h is NUL terminated. */
+static inline int mgcp_msg_terminate_nul(struct msgb *msg)
+{
+	unsigned char *tail = msg->l2h + msgb_l2len(msg); /* char after l2 data */
+	if (tail[-1] == '\0')
+		/* nothing to do */;
+	else if (msgb_tailroom(msg) > 0)
+		tail[0] = '\0';
+	else if (tail[-1] == '\r' || tail[-1] == '\n')
+		tail[-1] = '\0';
+	else {
+		LOGP(DLMGCP, LOGL_ERROR, "Cannot NUL terminate MGCP message: "
+		     "Length: %d, Buffer size: %d\n",
+		     msgb_l2len(msg), msg->data_len);
+		return -ENOTSUP;
+	}
+	return 0;
+}
+
+/* Maximum length of the comment field */
+#define MGCP_COMMENT_MAXLEN 256
+
+/* Maximum allowed String length of Connection Identifiers as per spec
+ * (see also RFC3435 2.1.3.2 Names of Connections), plus one for '\0'. */
+#define MGCP_CONN_ID_MAXLEN 32+1
+
+/* Deprecated: old name of MGCP_CONN_ID_MAXLEN. */
+#define MGCP_CONN_ID_LENGTH MGCP_CONN_ID_MAXLEN
+
+/* String length of Endpoint Identifiers.
+/  (see also RFC3435 section 3.2.1.3) */
+#define MGCP_ENDPOINT_MAXLEN (255*2+1+1)
+
+/* 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
+
+#endif
diff --git a/src/libosmo-mgcp/Makefile.am b/src/libosmo-mgcp/Makefile.am
index a0c015b..142ff75 100644
--- a/src/libosmo-mgcp/Makefile.am
+++ b/src/libosmo-mgcp/Makefile.am
@@ -40,5 +40,6 @@
 	mgcp_conn.c \
 	mgcp_stat.c \
 	mgcp_endp.c \
+	mgcp_trunk.c \
 	mgcp_ctrl.c \
 	$(NULL)
diff --git a/src/libosmo-mgcp/mgcp_codec.c b/src/libosmo-mgcp/mgcp_codec.c
index 9e55ab0..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
@@ -292,7 +293,7 @@
 	 * We do not compare to the full audio_name because we expect that
 	 * "GSM", "GSM/8000" and "GSM/8000/1" are all compatible when the
 	 * audio name of the codec is set to "GSM" */
-	if (sscanf(endp->tcfg->audio_name, "%63[^/]/%*d/%*d", codec_name) < 1)
+	if (sscanf(endp->trunk->audio_name, "%63[^/]/%*d/%*d", codec_name) < 1)
 		return false;
 
 	/* Finally we check if the subtype_name we have generated from the
@@ -302,9 +303,9 @@
 		return true;
 
 	/* FIXME: It is questinable that the method to pick a compatible
-	 * codec can work properly. Since this useses tcfg->audio_name, as
+	 * codec can work properly. Since this useses trunk->audio_name, as
 	 * a reference, which is set to "AMR/8000" permanently.
-	 * tcfg->audio_name must be updated by the first connection that
+	 * trunk->audio_name must be updated by the first connection that
 	 * has been made on an endpoint, so that the second connection
 	 * can make a meaningful decision here */
 
@@ -335,7 +336,7 @@
 	for (i = 0; i < rtp->codecs_assigned; i++) {
 		/* When no transcoding is available, avoid codecs that would
 		 * require transcoding. */
-		if (endp->tcfg->no_audio_transcoding && !is_codec_compatible(endp, &rtp->codecs[i])) {
+		if (endp->trunk->no_audio_transcoding && !is_codec_compatible(endp, &rtp->codecs[i])) {
 			LOGP(DLMGCP, LOGL_NOTICE, "transcoding not available, skipping codec: %d/%s\n",
 			     rtp->codecs[i].payload_type, rtp->codecs[i].subtype_name);
 			continue;
diff --git a/src/libosmo-mgcp/mgcp_conn.c b/src/libosmo-mgcp/mgcp_conn.c
index 7a86274..6bbcbbc 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>
@@ -255,7 +256,7 @@
 }
 
 static void
-aggregate_rtp_conn_stats(struct mgcp_trunk_config *trunk, struct mgcp_conn_rtp *conn_rtp)
+aggregate_rtp_conn_stats(struct mgcp_trunk *trunk, struct mgcp_conn_rtp *conn_rtp)
 {
 	struct rate_ctr_group *all_stats = trunk->all_rtp_conn_stats;
 	struct rate_ctr_group *conn_stats = conn_rtp->rate_ctr_group;
@@ -296,7 +297,7 @@
 
 	switch (conn->type) {
 	case MGCP_CONN_TYPE_RTP:
-		aggregate_rtp_conn_stats(endp->tcfg, &conn->u.rtp);
+		aggregate_rtp_conn_stats(endp->trunk, &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..80eb5b8 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,38 @@
 	.rtp.cleanup_cb = mgcp_cleanup_rtp_bridge_cb
 };
 
+/*! allocate an endpoint and set default values.
+ *  \param[in] trunk configuration
+ *  \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 +86,183 @@
 	endp->local_options.codec = NULL;
 	endp->wildcarded_req = false;
 }
+
+/* Check if the endpoint name contains the prefix, and chop it off, if it
+ * exists (per trunk the prefix is the same for all endpoints, so no ambiguity
+ * is introduced) */
+static const char *chop_endp_prefix(struct mgcp_trunk *trunk,
+				    const char *epname)
+{
+	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)
+			return epname + prefix_len;
+	case MGCP_TRUNK_E1:
+		prefix_len = sizeof(MGCP_ENDPOINT_PREFIX_E1_TRUNK) - 1;
+		if (strncmp
+		    (epname, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK,
+		     prefix_len) == 0)
+			return epname + prefix_len;
+	}
+
+	return epname;
+}
+
+/* Check for suffixes that can be omitted and return the true length of the
+ * endpoint name */
+static unsigned int ep_name_len(struct mgcp_trunk *trunk,
+				const char *epname)
+{
+	char *suffix_begin;
+
+	switch (trunk->trunk_type) {
+	case MGCP_TRUNK_VIRTUAL:
+		suffix_begin = strchr(epname, '@');
+		if (!suffix_begin)
+			return strlen(epname);
+		return suffix_begin - epname;
+	case MGCP_TRUNK_E1:
+		return strlen(epname);
+	}
+
+	return strlen(epname);
+}
+
+/*! 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 (may lack trunk prefix and domain name).
+ *  \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,
+					      struct mgcp_trunk *trunk)
+{
+	struct mgcp_endpoint *endp;
+	unsigned int i;
+	char epname_lc[MGCP_ENDPOINT_MAXLEN];
+	const char *epname_ch;
+	unsigned int epname_ch_len;
+
+	if (cause)
+		*cause = 0;
+
+	osmo_str_tolower_buf(epname_lc, sizeof(epname_lc), epname);
+	epname = epname_lc;
+	epname_ch = chop_endp_prefix(trunk, epname);
+	epname_ch_len = ep_name_len(trunk, epname_ch);
+
+	/* At the moment we only support a primitive ('*'-only) method of
+	 * wildarded endpoint searches that picks the next free endpoint on
+	 * a trunk. */
+	if (strncmp(epname_ch, "*", epname_ch_len) == 0) {
+		for (i = 0; i < trunk->number_endpoints; i++) {
+			endp = trunk->endpoints[i];
+			if (endp->callid == NULL) {
+				LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
+					 "(trunk:%i) found free endpoint: %s\n",
+					 trunk->trunk_nr, endp->name);
+				endp->wildcarded_req = true;
+				return endp;
+			}
+		}
+
+		LOGP(DLMGCP, LOGL_ERROR,
+		     "(trunk:%i) Not able to find a free endpoint\n",
+		     trunk->trunk_nr);
+		if (cause)
+			*cause = -403;
+		return NULL;
+	}
+
+	/* Find an enspoint by its name (if wildcarded request is not
+	 * applicable) */
+	for (i = 0; i < trunk->number_endpoints; i++) {
+		endp = trunk->endpoints[i];
+		if (strncmp
+		    (chop_endp_prefix(trunk, endp->name), epname_ch,
+		     epname_ch_len) == 0) {
+			LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
+				 "(trunk:%i) found endpoint: %s\n",
+				 trunk->trunk_nr, endp->name);
+			endp->wildcarded_req = false;
+			return endp;
+		}
+	}
+
+	LOGP(DLMGCP, LOGL_ERROR,
+	     "(trunk:%i) 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, "(endpoint:%s) missing domain name, 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, "(endpoint:%s) wrong domain name, 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(epname, cfg);
+	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 3e95ed1..019466e 100644
--- a/src/libosmo-mgcp/mgcp_msg.c
+++ b/src/libosmo-mgcp/mgcp_msg.c
@@ -129,166 +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_config *tcfg;
-	int trunk, endp;
-	struct mgcp_endpoint *endp_ptr;
-
-	trunk = strtoul(mgcp + 6, &rest, 10);
-	if (rest == NULL || rest[0] != '/' || trunk < 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;
-
-	tcfg = mgcp_trunk_num(cfg, trunk);
-	if (!tcfg) {
-		LOGP(DLMGCP, LOGL_ERROR, "The trunk %d is not declared.\n",
-		     trunk);
-		return NULL;
-	}
-
-	if (!tcfg->endpoints) {
-		LOGP(DLMGCP, LOGL_ERROR,
-		     "Endpoints of trunk %d not allocated.\n", trunk);
-		return NULL;
-	}
-
-	if (endp < 1 || endp >= tcfg->number_endpoints) {
-		LOGP(DLMGCP, LOGL_ERROR, "Failed to find endpoint '%s'\n",
-		     mgcp);
-		return NULL;
-	}
-
-	endp_ptr = &tcfg->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;
-
-	*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(cfg->trunk.endpoints,
-						  cfg->trunk.number_endpoints);
-			if (!endp)
-				*cause = -403;
-			return endp;
-		}
-		gw = strtoul(endpoint_number_str, &endptr, 16);
-		if (gw < cfg->trunk.number_endpoints && endptr[0] == '@') {
-			endp = &cfg->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 < cfg->trunk.number_endpoints && endptr[0] == '@') {
-		endp = &cfg->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
@@ -316,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);
@@ -390,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 608a93b..d3c7de8 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,7 +62,7 @@
 				 int id, int inc)
 {
 	struct rate_ctr_group *conn_stats = conn_rtp->rate_ctr_group;
-	struct rate_ctr_group *trunk_stats = endp->tcfg->all_rtp_conn_stats;
+	struct rate_ctr_group *trunk_stats = endp->trunk->all_rtp_conn_stats;
 
 	/* add to both the per-connection and the per-trunk global stats */
 	rate_ctr_add(&conn_stats->ctr[id], inc);
@@ -187,7 +188,7 @@
 	if (rc == -1)
 		goto failed;
 
-	if (endp->tcfg->omit_rtcp)
+	if (endp->trunk->omit_rtcp)
 		return rc;
 
 	was_rtcp = 1;
@@ -649,8 +650,8 @@
 
 #if 0
 	DEBUGP(DRTP,
-	       "endpoint:0x%x payload hdr payload %u -> endp payload %u\n",
-	       ENDPOINT_NUMBER(endp), rtp_hdr->payload_type, payload);
+	       "endpoint:%s payload hdr payload %u -> endp payload %u\n",
+	       endp->name, rtp_hdr->payload_type, payload);
 	rtp_hdr->payload_type = payload;
 #endif
 }
@@ -819,7 +820,7 @@
 	 *  connection in loopback mode exists), then the source connection
 	 *  shall be specified as destination connection */
 
-	struct mgcp_trunk_config *tcfg = endp->tcfg;
+	struct mgcp_trunk *trunk = endp->trunk;
 	struct mgcp_rtp_end *rtp_end;
 	struct mgcp_rtp_state *rtp_state;
 	char *dest_name;
@@ -835,7 +836,7 @@
 	}
 
 	LOGPENDP(endp, DRTP, LOGL_DEBUG, "loop:%d, mode:%d%s\n",
-		 tcfg->audio_loop, conn_src->conn->mode,
+		 trunk->audio_loop, conn_src->conn->mode,
 		 conn_src->conn->mode == MGCP_CONN_LOOPBACK ? " (loopback)" : "");
 
 	/* FIXME: It is legal that the payload type on the egress connection is
@@ -947,7 +948,7 @@
 			buflen = cont;
 		} while (buflen > 0);
 		return nbytes;
-	} else if (!tcfg->omit_rtcp) {
+	} else if (!trunk->omit_rtcp) {
 		LOGPENDP(endp, DRTP, LOGL_DEBUG,
 			 "send to %s %s rtp_port:%u rtcp_port:%u\n",
 			 dest_name, inet_ntoa(rtp_end->addr),
@@ -1150,12 +1151,12 @@
 {
 	struct mgcp_endpoint *endp;
 	struct mgcp_conn_rtp *conn;
-	struct mgcp_trunk_config *tcfg;
+	struct mgcp_trunk *trunk;
 	int rc;
 
 	conn = (struct mgcp_conn_rtp*) fd->data;
 	endp = conn->conn->endp;
-	tcfg = endp->tcfg;
+	trunk = endp->trunk;
 
 	LOGPCONN(conn->conn, DRTP, LOGL_DEBUG, "receiving RTP/RTCP packet...\n");
 
@@ -1190,7 +1191,7 @@
 	LOGPENDP(endp, DRTP, LOGL_DEBUG, "conn:%s\n", mgcp_conn_dump(conn->conn));
 
 	/* Check if the origin of the RTP packet seems plausible */
-	if (tcfg->rtp_accept_all == 0) {
+	if (trunk->rtp_accept_all == 0) {
 		if (check_rtp_origin(conn, addr) != 0)
 			return -1;
 	}
@@ -1436,11 +1437,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->trunk.endpoints[endpno];
 
 	if (mgcp_create_bind(source_addr, &rtp_end->rtp,
 			     rtp_end->local_port) != 0) {
@@ -1527,8 +1527,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 a1121de..29ad45f 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;
 
@@ -202,9 +203,9 @@
 	struct mgcp_conn_rtp * conn_rtp;
 	int i;
 
-	for (i=0; i<cfg->trunk.number_endpoints; i++) {
+	for (i=0; i<cfg->trunk->number_endpoints; i++) {
 
-		endp = &cfg->trunk.endpoints[i];
+		endp = cfg->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 82c10aa..c66d984 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);
@@ -254,11 +159,11 @@
 	 * Remember the last transmission per endpoint.
 	 */
 	if (endp) {
-		struct mgcp_trunk_config *tcfg = endp->tcfg;
+		struct mgcp_trunk *trunk = endp->trunk;
 		talloc_free(endp->last_response);
 		talloc_free(endp->last_trans);
-		endp->last_trans = talloc_strdup(tcfg->endpoints, trans);
-		endp->last_response = talloc_strndup(tcfg->endpoints,
+		endp->last_trans = talloc_strdup(trunk->endpoints, trans);
+		endp->last_response = talloc_strndup(trunk->endpoints,
 						     (const char *)res->l2h,
 						     msgb_l2len(res));
 	}
@@ -296,10 +201,8 @@
 
 	/* NOTE: Only in the virtual trunk we allow dynamic endpoint names */
 	if (endp->wildcarded_req
-	    && endp->tcfg->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);
+	    && endp->trunk->trunk_type == MGCP_TRUNK_VIRTUAL) {
+		rc = msgb_printf(msg, "Z: %s\r\n", endp->name);
 		if (rc < 0)
 			return -EINVAL;
 	}
@@ -379,8 +282,8 @@
  *   - or a response (three numbers, space, transaction id) */
 struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
 {
-	struct mgcp_trunk_config *tcfg = &cfg->trunk;
-	struct rate_ctr_group *rate_ctrs = tcfg->mgcp_general_ctr_group;
+	struct mgcp_trunk *trunk = cfg->trunk;
+	struct rate_ctr_group *rate_ctrs = trunk->mgcp_general_ctr_group;
 	struct mgcp_parse_data pdata;
 	int rc, i, code, handled = 0;
 	struct msgb *resp = NULL;
@@ -692,13 +595,13 @@
 void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
 			 struct mgcp_rtp_end *rtp)
 {
-	struct mgcp_trunk_config *tcfg = endp->tcfg;
+	struct mgcp_trunk *trunk = endp->trunk;
 
-	int patch_ssrc = expect_ssrc_change && tcfg->force_constant_ssrc;
+	int patch_ssrc = expect_ssrc_change && trunk->force_constant_ssrc;
 
-	rtp->force_aligned_timing = tcfg->force_aligned_timing;
+	rtp->force_aligned_timing = trunk->force_aligned_timing;
 	rtp->force_constant_ssrc = patch_ssrc ? 1 : 0;
-	rtp->rfc5993_hr_convert = tcfg->rfc5993_hr_convert;
+	rtp->rfc5993_hr_convert = trunk->rfc5993_hr_convert;
 
 	LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
 		 "Configuring RTP endpoint: local port %d%s%s\n",
@@ -830,9 +733,9 @@
 /* CRCX command handler, processes the received command */
 static struct msgb *handle_create_con(struct mgcp_parse_data *p)
 {
-	struct mgcp_trunk_config *tcfg = p->endp->tcfg;
+	struct mgcp_trunk *trunk = p->endp->trunk;
 	struct mgcp_endpoint *endp = p->endp;
-	struct rate_ctr_group *rate_ctrs = tcfg->mgcp_crcx_ctr_group;
+	struct rate_ctr_group *rate_ctrs = trunk->mgcp_crcx_ctr_group;
 	int error_code = 400;
 	const char *local_options = NULL;
 	const char *callid = NULL;
@@ -915,7 +818,7 @@
 		LOGPENDP(endp, DLMGCP, LOGL_ERROR,
 			"CRCX: endpoint full, max. %i connections allowed!\n",
 			endp->type->max_conns);
-		if (tcfg->force_realloc) {
+		if (trunk->force_realloc) {
 			/* There is no more room for a connection, make some
 			 * room by blindly tossing the oldest of the two two
 			 * connections */
@@ -934,7 +837,7 @@
 		LOGPENDP(endp, DLMGCP, LOGL_ERROR,
 			 "CRCX: already seized by other call (%s)\n",
 			 endp->callid);
-		if (tcfg->force_realloc)
+		if (trunk->force_realloc)
 			/* This is not our call, toss everything by releasing
 			 * the entire endpoint. (rude!) */
 			mgcp_endp_release(endp);
@@ -949,10 +852,10 @@
 	/* Set the callid, creation of another connection will only be possible
 	 * when the callid matches up. (Connections are distinguished by their
 	 * connection ids) */
-	endp->callid = talloc_strdup(tcfg->endpoints, callid);
+	endp->callid = talloc_strdup(trunk->endpoints, callid);
 
 	snprintf(conn_name, sizeof(conn_name), "%s", callid);
-	_conn = mgcp_conn_alloc(tcfg->endpoints, endp, MGCP_CONN_TYPE_RTP, conn_name);
+	_conn = mgcp_conn_alloc(trunk->endpoints, endp, MGCP_CONN_TYPE_RTP, conn_name);
 	if (!_conn) {
 		LOGPENDP(endp, DLMGCP, LOGL_ERROR,
 			 "CRCX: unable to allocate RTP connection\n");
@@ -987,7 +890,7 @@
 
 	/* Set local connection options, if present */
 	if (local_options) {
-		rc = set_local_cx_options(endp->tcfg->endpoints,
+		rc = set_local_cx_options(endp->trunk->endpoints,
 					  &endp->local_options, local_options);
 		if (rc != 0) {
 			LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
@@ -1007,8 +910,8 @@
 		goto error2;
 	}
 
-	conn->end.fmtp_extra = talloc_strdup(tcfg->endpoints,
-					     tcfg->audio_fmtp_extra);
+	conn->end.fmtp_extra = talloc_strdup(trunk->endpoints,
+					     trunk->audio_fmtp_extra);
 
 	if (p->cfg->force_ptime) {
 		conn->end.packet_duration_ms = p->cfg->force_ptime;
@@ -1043,8 +946,7 @@
 	/* policy CB */
 	if (p->cfg->policy_cb) {
 		int rc;
-		rc = p->cfg->policy_cb(tcfg, 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,12 +968,12 @@
 	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(tcfg, 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(tcfg->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
+	OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
 	if (conn->conn->mode & MGCP_CONN_RECV_ONLY
-	    && tcfg->keepalive_interval != MGCP_KEEPALIVE_NEVER)
+	    && trunk->keepalive_interval != MGCP_KEEPALIVE_NEVER)
 		send_dummy(endp, conn);
 
 	LOGPCONN(_conn, DLMGCP, LOGL_NOTICE,
@@ -1085,16 +987,12 @@
 	return create_err_response(endp, error_code, "CRCX", p->trans);
 }
 
-
-
-
-
 /* MDCX command handler, processes the received command */
 static struct msgb *handle_modify_con(struct mgcp_parse_data *p)
 {
-	struct mgcp_trunk_config *tcfg = p->endp->tcfg;
+	struct mgcp_trunk *trunk = p->endp->trunk;
 	struct mgcp_endpoint *endp = p->endp;
-	struct rate_ctr_group *rate_ctrs = tcfg->mgcp_mdcx_ctr_group;
+	struct rate_ctr_group *rate_ctrs = trunk->mgcp_mdcx_ctr_group;
 	int error_code = 500;
 	int silent = 0;
 	int have_sdp = 0;
@@ -1202,7 +1100,7 @@
 
 	/* Set local connection options, if present */
 	if (local_options) {
-		rc = set_local_cx_options(endp->tcfg->endpoints,
+		rc = set_local_cx_options(endp->trunk->endpoints,
 					  &endp->local_options, local_options);
 		if (rc != 0) {
 			LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
@@ -1261,8 +1159,7 @@
 	/* policy CB */
 	if (p->cfg->policy_cb) {
 		int rc;
-		rc = p->cfg->policy_cb(endp->tcfg, 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,
@@ -1291,13 +1188,12 @@
 	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->tcfg, 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->tcfg->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
+	OSMO_ASSERT(endp->trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
 	if (conn->conn->mode & MGCP_CONN_RECV_ONLY
-	    && endp->tcfg->keepalive_interval != MGCP_KEEPALIVE_NEVER)
+	    && endp->trunk->keepalive_interval != MGCP_KEEPALIVE_NEVER)
 		send_dummy(endp, conn);
 
 	rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_SUCCESS]);
@@ -1318,9 +1214,9 @@
 /* DLCX command handler, processes the received command */
 static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
 {
-	struct mgcp_trunk_config *tcfg = p->endp->tcfg;
+	struct mgcp_trunk *trunk = p->endp->trunk;
 	struct mgcp_endpoint *endp = p->endp;
-	struct rate_ctr_group *rate_ctrs = tcfg->mgcp_dlcx_ctr_group;
+	struct rate_ctr_group *rate_ctrs = trunk->mgcp_dlcx_ctr_group;
 	int error_code = 400;
 	int silent = 0;
 	char *line;
@@ -1381,8 +1277,7 @@
 	/* policy CB */
 	if (p->cfg->policy_cb) {
 		int rc;
-		rc = p->cfg->policy_cb(endp->tcfg, 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");
@@ -1446,8 +1341,7 @@
 	}
 
 	if (p->cfg->change_cb)
-		p->cfg->change_cb(endp->tcfg, 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)
@@ -1476,7 +1370,7 @@
 	LOGP(DLMGCP, LOGL_NOTICE, "RSIP: resetting all endpoints ...\n");
 
 	if (p->cfg->reset_cb)
-		p->cfg->reset_cb(p->endp->tcfg);
+		p->cfg->reset_cb(p->endp->trunk);
 	return NULL;
 }
 
@@ -1522,37 +1416,37 @@
 
 /* Connection keepalive timer, will take care that dummy packets are send
  * regularly, so that NAT connections stay open */
-static void mgcp_keepalive_timer_cb(void *_tcfg)
+static void mgcp_keepalive_timer_cb(void *_trunk)
 {
-	struct mgcp_trunk_config *tcfg = _tcfg;
+	struct mgcp_trunk *trunk = _trunk;
 	struct mgcp_conn *conn;
 	int i;
 
 	LOGP(DLMGCP, LOGL_DEBUG, "triggered trunk %d keepalive timer\n",
-	     tcfg->trunk_nr);
+	     trunk->trunk_nr);
 
 	/* Do not accept invalid configuration values
 	 * valid is MGCP_KEEPALIVE_NEVER, MGCP_KEEPALIVE_ONCE and
 	 * values greater 0 */
-	OSMO_ASSERT(tcfg->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
+	OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
 
 	/* The dummy packet functionality has been disabled, we will exit
 	 * immediately, no further timer is scheduled, which means we will no
 	 * longer send dummy packets even when we did before */
-	if (tcfg->keepalive_interval == MGCP_KEEPALIVE_NEVER)
+	if (trunk->keepalive_interval == MGCP_KEEPALIVE_NEVER)
 		return;
 
 	/* In cases where only one dummy packet is sent, we do not need
 	 * the timer since the functions that handle the CRCX and MDCX are
 	 * triggering the sending of the dummy packet. So we behave like in
 	 * the  MGCP_KEEPALIVE_NEVER case */
-	if (tcfg->keepalive_interval == MGCP_KEEPALIVE_ONCE)
+	if (trunk->keepalive_interval == MGCP_KEEPALIVE_ONCE)
 		return;
 
 	/* Send walk over all endpoints and send out dummy packets through
 	 * every connection present on each endpoint */
-	for (i = 1; i < tcfg->number_endpoints; ++i) {
-		struct mgcp_endpoint *endp = &tcfg->endpoints[i];
+	for (i = 1; i < trunk->number_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);
@@ -1561,77 +1455,21 @@
 
 	/* Schedule the keepalive timer for the next round */
 	LOGP(DLMGCP, LOGL_DEBUG, "rescheduling trunk %d keepalive timer\n",
-	     tcfg->trunk_nr);
-	osmo_timer_schedule(&tcfg->keepalive_timer, tcfg->keepalive_interval,
+	     trunk->trunk_nr);
+	osmo_timer_schedule(&trunk->keepalive_timer, trunk->keepalive_interval,
 			    0);
 }
 
-void mgcp_trunk_set_keepalive(struct mgcp_trunk_config *tcfg, int interval)
+void mgcp_trunk_set_keepalive(struct mgcp_trunk *trunk, int interval)
 {
-	tcfg->keepalive_interval = interval;
-	osmo_timer_setup(&tcfg->keepalive_timer, mgcp_keepalive_timer_cb, tcfg);
+	trunk->keepalive_interval = interval;
+	osmo_timer_setup(&trunk->keepalive_timer, mgcp_keepalive_timer_cb, trunk);
 
 	if (interval <= 0)
-		osmo_timer_del(&tcfg->keepalive_timer);
+		osmo_timer_del(&trunk->keepalive_timer);
 	else
-		osmo_timer_schedule(&tcfg->keepalive_timer,
-				    tcfg->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_config *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;
+		osmo_timer_schedule(&trunk->keepalive_timer,
+				    trunk->keepalive_interval, 0);
 }
 
 /*! allocate configuration with default values.
@@ -1661,113 +1499,19 @@
 
 	cfg->get_net_downlink_format_cb = &mgcp_get_net_downlink_format_default;
 
-	/* default trunk handling; TODO: avoid duplication with mgcp_trunk_alloc() below */
-	cfg->trunk.cfg = cfg;
-	cfg->trunk.trunk_nr = 0;
-	cfg->trunk.trunk_type = MGCP_TRUNK_VIRTUAL;
-	cfg->trunk.audio_name = talloc_strdup(cfg, "AMR/8000");
-	cfg->trunk.audio_payload = 126;
-	cfg->trunk.audio_send_ptime = 1;
-	cfg->trunk.audio_send_name = 1;
-	cfg->trunk.vty_number_endpoints = 33;
-	cfg->trunk.omit_rtcp = 0;
-	mgcp_trunk_set_keepalive(&cfg->trunk, MGCP_KEEPALIVE_ONCE);
-	if (alloc_mgcp_rate_counters(&cfg->trunk, cfg) < 0) {
+	/* Allocate virtual trunk */
+	cfg->trunk = mgcp_trunk_alloc(cfg, 0, MGCP_TRUNK_VIRTUAL);
+	if (!cfg->trunk) {
 		talloc_free(cfg);
 		return NULL;
 	}
 
+	/* Initalize list head for user configurable trunks */
 	INIT_LLIST_HEAD(&cfg->trunks);
 
 	return cfg;
 }
 
-/*! allocate configuration with default values.
- *  (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_config *mgcp_trunk_alloc(struct mgcp_config *cfg, int nr)
-{
-	struct mgcp_trunk_config *trunk;
-
-	trunk = talloc_zero(cfg, struct mgcp_trunk_config);
-	if (!trunk) {
-		LOGP(DLMGCP, LOGL_ERROR, "Failed to allocate.\n");
-		return NULL;
-	}
-
-	trunk->cfg = cfg;
-	trunk->trunk_type = MGCP_TRUNK_E1;
-	trunk->trunk_nr = nr;
-	trunk->audio_name = talloc_strdup(cfg, "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);
-	alloc_mgcp_rate_counters(trunk, trunk);
-	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_config *mgcp_trunk_num(struct mgcp_config *cfg, int index)
-{
-	struct mgcp_trunk_config *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] tcfg trunk configuration
- *  \returns 0 on success, -1 on failure */
-int mgcp_endpoints_allocate(struct mgcp_trunk_config *tcfg)
-{
-	int i;
-
-	tcfg->endpoints = _talloc_zero_array(tcfg->cfg,
-					     sizeof(struct mgcp_endpoint),
-					     tcfg->vty_number_endpoints,
-					     "endpoints");
-	if (!tcfg->endpoints)
-		return -1;
-
-	for (i = 0; i < tcfg->vty_number_endpoints; ++i) {
-		INIT_LLIST_HEAD(&tcfg->endpoints[i].conns);
-		tcfg->endpoints[i].cfg = tcfg->cfg;
-		tcfg->endpoints[i].tcfg = tcfg;
-
-		switch (tcfg->trunk_type) {
-		case MGCP_TRUNK_VIRTUAL:
-			tcfg->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",
-				   tcfg->trunk_type, __FILE__, __LINE__);
-		}
-	}
-
-	tcfg->number_endpoints = tcfg->vty_number_endpoints;
-	alloc_mgcp_rate_counters(tcfg, tcfg->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);
@@ -1798,17 +1542,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_sdp.c b/src/libosmo-mgcp/mgcp_sdp.c
index 01e7968..f811fac 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));
+			LOGP(DLMGCP, LOGL_NOTICE, "endpoint:%s, failed to add codec\n", endp->name);
 	}
 
 	talloc_free(tmp_ctx);
@@ -557,7 +559,7 @@
 		if (rc < 0)
 			goto buffer_too_small;
 
-		if (endp->tcfg->audio_send_name) {
+		if (endp->trunk->audio_send_name) {
 			rc = add_rtpmap(sdp, payload_type, audio_name);
 			if (rc < 0)
 				goto buffer_too_small;
@@ -573,7 +575,7 @@
 		if (rc < 0)
 			goto buffer_too_small;
 	}
-	if (conn->end.packet_duration_ms > 0 && endp->tcfg->audio_send_ptime) {
+	if (conn->end.packet_duration_ms > 0 && endp->trunk->audio_send_ptime) {
 		rc = msgb_printf(sdp, "a=ptime:%u\r\n",
 				 conn->end.packet_duration_ms);
 		if (rc < 0)
diff --git a/src/libosmo-mgcp/mgcp_trunk.c b/src/libosmo-mgcp/mgcp_trunk.c
new file mode 100644
index 0000000..41495e5
--- /dev/null
+++ b/src/libosmo-mgcp/mgcp_trunk.c
@@ -0,0 +1,339 @@
+/* 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/core/stats.h>
+#include <osmocom/mgcp/mgcp_internal.h>
+#include <osmocom/mgcp/mgcp_endp.h>
+#include <osmocom/mgcp/mgcp_trunk.h>
+#include <osmocom/mgcp/mgcp_stat.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;
+}
+
+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 trunk and at id (if required) to the trunk list
+ *  (called once at startup by VTY)
+ *  \param[in] cfg mgcp configuration
+ *  \param[in] nr trunk number
+ *  \param[in] type trunk type
+ *  \returns pointer to allocated trunk, NULL on failure */
+struct mgcp_trunk *mgcp_trunk_alloc(struct mgcp_config *cfg, int nr, int trunk_type)
+{
+	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 = trunk_type;
+	trunk->trunk_nr = nr;
+
+	trunk->audio_name = talloc_strdup(cfg, "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;
+	}
+
+	/* 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);
+
+	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;
+
+	/* (re)allocate pointer array for the endpoints */
+	trunk->number_endpoints = 0;
+	if (trunk->endpoints)
+		talloc_free(trunk->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(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 endpoint was not found. */
+struct mgcp_trunk *mgcp_trunk_by_name(const char *epname, struct mgcp_config *cfg)
+{
+	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->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:%s) e1 trunks 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, "(endpoint:%s) missing trunk prefix, assuming trunk \"%s\"!\n", epname,
+		     MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK);
+		return cfg->trunk;
+	}
+
+	LOGP(DLMGCP, LOGL_ERROR, "(endpoint:%s) unable to find trunk!\n", epname);
+	return NULL;
+}
diff --git a/src/libosmo-mgcp/mgcp_vty.c b/src/libosmo-mgcp/mgcp_vty.c
index 76d674f..0df805d 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>
@@ -42,14 +43,14 @@
 
 static struct mgcp_config *g_cfg = NULL;
 
-static struct mgcp_trunk_config *find_trunk(struct mgcp_config *cfg, int nr)
+static struct mgcp_trunk *find_trunk(struct mgcp_config *cfg, int nr)
 {
-	struct mgcp_trunk_config *trunk;
+	struct mgcp_trunk *trunk;
 
 	if (nr == 0)
-		trunk = &cfg->trunk;
+		trunk = cfg->trunk;
 	else
-		trunk = mgcp_trunk_num(cfg, nr);
+		trunk = mgcp_trunk_by_num(cfg, nr);
 
 	return trunk;
 }
@@ -85,50 +86,50 @@
 	else
 		vty_out(vty, "  no rtp ip-probing%s", VTY_NEWLINE);
 	vty_out(vty, "  rtp ip-dscp %d%s", g_cfg->endp_dscp, VTY_NEWLINE);
-	if (g_cfg->trunk.keepalive_interval == MGCP_KEEPALIVE_ONCE)
+	if (g_cfg->trunk->keepalive_interval == MGCP_KEEPALIVE_ONCE)
 		vty_out(vty, "  rtp keep-alive once%s", VTY_NEWLINE);
-	else if (g_cfg->trunk.keepalive_interval)
+	else if (g_cfg->trunk->keepalive_interval)
 		vty_out(vty, "  rtp keep-alive %d%s",
-			g_cfg->trunk.keepalive_interval, VTY_NEWLINE);
+			g_cfg->trunk->keepalive_interval, VTY_NEWLINE);
 	else
 		vty_out(vty, "  no rtp keep-alive%s", VTY_NEWLINE);
 
-	if (g_cfg->trunk.omit_rtcp)
+	if (g_cfg->trunk->omit_rtcp)
 		vty_out(vty, "  rtcp-omit%s", VTY_NEWLINE);
 	else
 		vty_out(vty, "  no rtcp-omit%s", VTY_NEWLINE);
-	if (g_cfg->trunk.force_constant_ssrc
-	    || g_cfg->trunk.force_aligned_timing
-	    || g_cfg->trunk.rfc5993_hr_convert) {
+	if (g_cfg->trunk->force_constant_ssrc
+	    || g_cfg->trunk->force_aligned_timing
+	    || g_cfg->trunk->rfc5993_hr_convert) {
 		vty_out(vty, "  %srtp-patch ssrc%s",
-			g_cfg->trunk.force_constant_ssrc ? "" : "no ",
+			g_cfg->trunk->force_constant_ssrc ? "" : "no ",
 			VTY_NEWLINE);
 		vty_out(vty, "  %srtp-patch timestamp%s",
-			g_cfg->trunk.force_aligned_timing ? "" : "no ",
+			g_cfg->trunk->force_aligned_timing ? "" : "no ",
 			VTY_NEWLINE);
 		vty_out(vty, "  %srtp-patch rfc5993hr%s",
-			g_cfg->trunk.rfc5993_hr_convert ? "" : "no ",
+			g_cfg->trunk->rfc5993_hr_convert ? "" : "no ",
 			VTY_NEWLINE);
 	} else
 		vty_out(vty, "  no rtp-patch%s", VTY_NEWLINE);
-	if (g_cfg->trunk.audio_payload != -1)
+	if (g_cfg->trunk->audio_payload != -1)
 		vty_out(vty, "  sdp audio-payload number %d%s",
-			g_cfg->trunk.audio_payload, VTY_NEWLINE);
-	if (g_cfg->trunk.audio_name)
+			g_cfg->trunk->audio_payload, VTY_NEWLINE);
+	if (g_cfg->trunk->audio_name)
 		vty_out(vty, "  sdp audio-payload name %s%s",
-			g_cfg->trunk.audio_name, VTY_NEWLINE);
-	if (g_cfg->trunk.audio_fmtp_extra)
+			g_cfg->trunk->audio_name, VTY_NEWLINE);
+	if (g_cfg->trunk->audio_fmtp_extra)
 		vty_out(vty, "  sdp audio fmtp-extra %s%s",
-			g_cfg->trunk.audio_fmtp_extra, VTY_NEWLINE);
+			g_cfg->trunk->audio_fmtp_extra, VTY_NEWLINE);
 	vty_out(vty, "  %ssdp audio-payload send-ptime%s",
-		g_cfg->trunk.audio_send_ptime ? "" : "no ", VTY_NEWLINE);
+		g_cfg->trunk->audio_send_ptime ? "" : "no ", VTY_NEWLINE);
 	vty_out(vty, "  %ssdp audio-payload send-name%s",
-		g_cfg->trunk.audio_send_name ? "" : "no ", VTY_NEWLINE);
-	vty_out(vty, "  loop %u%s", ! !g_cfg->trunk.audio_loop, VTY_NEWLINE);
+		g_cfg->trunk->audio_send_name ? "" : "no ", VTY_NEWLINE);
+	vty_out(vty, "  loop %u%s", ! !g_cfg->trunk->audio_loop, VTY_NEWLINE);
 	vty_out(vty, "  number endpoints %u%s",
-		g_cfg->trunk.vty_number_endpoints - 1, VTY_NEWLINE);
+		g_cfg->trunk->vty_number_endpoints - 1, VTY_NEWLINE);
 	vty_out(vty, "  %sallow-transcoding%s",
-		g_cfg->trunk.no_audio_transcoding ? "no " : "", VTY_NEWLINE);
+		g_cfg->trunk->no_audio_transcoding ? "no " : "", VTY_NEWLINE);
 	if (g_cfg->call_agent_addr)
 		vty_out(vty, "  call-agent ip %s%s", g_cfg->call_agent_addr,
 			VTY_NEWLINE);
@@ -206,15 +207,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);
@@ -242,7 +241,7 @@
 	}
 }
 
-static void dump_trunk(struct vty *vty, struct mgcp_trunk_config *cfg, int show_stats)
+static void dump_trunk(struct vty *vty, struct mgcp_trunk *cfg, int show_stats)
 {
 	int i;
 
@@ -256,8 +255,8 @@
 	}
 
 	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);
+		struct mgcp_endpoint *endp = cfg->endpoints[i];
+		dump_endpoint(vty, endp, cfg->trunk_nr, cfg->trunk_type, show_stats);
 		if (i < cfg->number_endpoints - 1)
 			vty_out(vty, "%s", VTY_NEWLINE);
 	}
@@ -296,10 +295,10 @@
       SHOW_MGCP_STR
       "Include Statistics\n")
 {
-	struct mgcp_trunk_config *trunk;
+	struct mgcp_trunk *trunk;
 	int show_stats = argc >= 1;
 
-	dump_trunk(vty, &g_cfg->trunk, show_stats);
+	dump_trunk(vty, g_cfg->trunk, show_stats);
 
 	llist_for_each_entry(trunk, &g_cfg->trunks, entry)
 	    dump_trunk(vty, trunk, show_stats);
@@ -312,34 +311,28 @@
 }
 
 static void
-dump_mgcp_endpoint(struct vty *vty, struct mgcp_trunk_config *trunk, const char *epname)
+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,
@@ -348,12 +341,7 @@
       SHOW_MGCP_STR
       "Display information about an endpoint\n" "The name of the endpoint\n")
 {
-	struct mgcp_trunk_config *trunk;
-
-	dump_mgcp_endpoint(vty, &g_cfg->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;
 }
 
@@ -364,7 +352,7 @@
       "Display information about a trunk\n" "Trunk number\n"
       "Display information about an endpoint\n" "The name of the endpoint\n")
 {
-	struct mgcp_trunk_config *trunk;
+	struct mgcp_trunk *trunk;
 	int trunkidx = atoi(argv[0]);
 
 	trunk = find_trunk(g_cfg, trunkidx);
@@ -561,7 +549,7 @@
 	if (!txt)
 		return CMD_WARNING;
 
-	osmo_talloc_replace_string(g_cfg, &g_cfg->trunk.audio_fmtp_extra, txt);
+	osmo_talloc_replace_string(g_cfg, &g_cfg->trunk->audio_fmtp_extra, txt);
 	talloc_free(txt);
 	return CMD_SUCCESS;
 }
@@ -570,7 +558,7 @@
       cfg_mgcp_allow_transcoding_cmd,
       "allow-transcoding", "Allow transcoding\n")
 {
-	g_cfg->trunk.no_audio_transcoding = 0;
+	g_cfg->trunk->no_audio_transcoding = 0;
 	return CMD_SUCCESS;
 }
 
@@ -578,7 +566,7 @@
       cfg_mgcp_no_allow_transcoding_cmd,
       "no allow-transcoding", NO_STR "Allow transcoding\n")
 {
-	g_cfg->trunk.no_audio_transcoding = 1;
+	g_cfg->trunk->no_audio_transcoding = 1;
 	return CMD_SUCCESS;
 }
 
@@ -590,7 +578,7 @@
       SDP_STR AUDIO_STR "Number\n" "Payload number\n")
 {
 	unsigned int payload = atoi(argv[0]);
-	g_cfg->trunk.audio_payload = payload;
+	g_cfg->trunk->audio_payload = payload;
 	return CMD_SUCCESS;
 }
 
@@ -604,7 +592,7 @@
       "sdp audio-payload name NAME",
       SDP_STR AUDIO_STR "Name\n" "Payload name\n")
 {
-	osmo_talloc_replace_string(g_cfg, &g_cfg->trunk.audio_name, argv[0]);
+	osmo_talloc_replace_string(g_cfg, &g_cfg->trunk->audio_name, argv[0]);
 	return CMD_SUCCESS;
 }
 
@@ -617,7 +605,7 @@
       "sdp audio-payload send-ptime",
       SDP_STR AUDIO_STR "Send SDP ptime (packet duration) attribute\n")
 {
-	g_cfg->trunk.audio_send_ptime = 1;
+	g_cfg->trunk->audio_send_ptime = 1;
 	return CMD_SUCCESS;
 }
 
@@ -626,7 +614,7 @@
       "no sdp audio-payload send-ptime",
       NO_STR SDP_STR AUDIO_STR "Send SDP ptime (packet duration) attribute\n")
 {
-	g_cfg->trunk.audio_send_ptime = 0;
+	g_cfg->trunk->audio_send_ptime = 0;
 	return CMD_SUCCESS;
 }
 
@@ -635,7 +623,7 @@
       "sdp audio-payload send-name",
       SDP_STR AUDIO_STR "Send SDP rtpmap with the audio name\n")
 {
-	g_cfg->trunk.audio_send_name = 1;
+	g_cfg->trunk->audio_send_name = 1;
 	return CMD_SUCCESS;
 }
 
@@ -644,7 +632,7 @@
       "no sdp audio-payload send-name",
       NO_STR SDP_STR AUDIO_STR "Send SDP rtpmap with the audio name\n")
 {
-	g_cfg->trunk.audio_send_name = 0;
+	g_cfg->trunk->audio_send_name = 0;
 	return CMD_SUCCESS;
 }
 
@@ -657,7 +645,7 @@
 		vty_out(vty, "Cannot use `loop' with `osmux'.%s", VTY_NEWLINE);
 		return CMD_WARNING;
 	}
-	g_cfg->trunk.audio_loop = atoi(argv[0]);
+	g_cfg->trunk->audio_loop = atoi(argv[0]);
 	return CMD_SUCCESS;
 }
 
@@ -667,7 +655,7 @@
       "Force endpoint reallocation when the endpoint is still seized\n"
       "Don't force reallocation\n" "force reallocation\n")
 {
-	g_cfg->trunk.force_realloc = atoi(argv[0]);
+	g_cfg->trunk->force_realloc = atoi(argv[0]);
 	return CMD_SUCCESS;
 }
 
@@ -677,7 +665,7 @@
       "Accept all RTP packets, even when the originating IP/Port does not match\n"
       "enable filter\n" "disable filter\n")
 {
-	g_cfg->trunk.rtp_accept_all = atoi(argv[0]);
+	g_cfg->trunk->rtp_accept_all = atoi(argv[0]);
 	return CMD_SUCCESS;
 }
 
@@ -687,20 +675,20 @@
       "Number options\n" "Endpoints available\n" "Number endpoints\n")
 {
 	/* + 1 as we start counting at one */
-	g_cfg->trunk.vty_number_endpoints = atoi(argv[0]) + 1;
+	g_cfg->trunk->vty_number_endpoints = atoi(argv[0]) + 1;
 	return CMD_SUCCESS;
 }
 
 DEFUN(cfg_mgcp_omit_rtcp, cfg_mgcp_omit_rtcp_cmd, "rtcp-omit", RTCP_OMIT_STR)
 {
-	g_cfg->trunk.omit_rtcp = 1;
+	g_cfg->trunk->omit_rtcp = 1;
 	return CMD_SUCCESS;
 }
 
 DEFUN(cfg_mgcp_no_omit_rtcp,
       cfg_mgcp_no_omit_rtcp_cmd, "no rtcp-omit", NO_STR RTCP_OMIT_STR)
 {
-	g_cfg->trunk.omit_rtcp = 0;
+	g_cfg->trunk->omit_rtcp = 0;
 	return CMD_SUCCESS;
 }
 
@@ -708,7 +696,7 @@
       cfg_mgcp_patch_rtp_ssrc_cmd,
       "rtp-patch ssrc", RTP_PATCH_STR "Force a fixed SSRC\n")
 {
-	g_cfg->trunk.force_constant_ssrc = 1;
+	g_cfg->trunk->force_constant_ssrc = 1;
 	return CMD_SUCCESS;
 }
 
@@ -716,7 +704,7 @@
       cfg_mgcp_no_patch_rtp_ssrc_cmd,
       "no rtp-patch ssrc", NO_STR RTP_PATCH_STR "Force a fixed SSRC\n")
 {
-	g_cfg->trunk.force_constant_ssrc = 0;
+	g_cfg->trunk->force_constant_ssrc = 0;
 	return CMD_SUCCESS;
 }
 
@@ -724,7 +712,7 @@
       cfg_mgcp_patch_rtp_ts_cmd,
       "rtp-patch timestamp", RTP_PATCH_STR "Adjust RTP timestamp\n")
 {
-	g_cfg->trunk.force_aligned_timing = 1;
+	g_cfg->trunk->force_aligned_timing = 1;
 	return CMD_SUCCESS;
 }
 
@@ -732,7 +720,7 @@
       cfg_mgcp_no_patch_rtp_ts_cmd,
       "no rtp-patch timestamp", NO_STR RTP_PATCH_STR "Adjust RTP timestamp\n")
 {
-	g_cfg->trunk.force_aligned_timing = 0;
+	g_cfg->trunk->force_aligned_timing = 0;
 	return CMD_SUCCESS;
 }
 
@@ -740,7 +728,7 @@
       cfg_mgcp_patch_rtp_rfc5993hr_cmd,
       "rtp-patch rfc5993hr", RTP_PATCH_STR RTP_TS101318_RFC5993_CONV_STR)
 {
-	g_cfg->trunk.rfc5993_hr_convert = true;
+	g_cfg->trunk->rfc5993_hr_convert = true;
 	return CMD_SUCCESS;
 }
 
@@ -748,16 +736,16 @@
       cfg_mgcp_no_patch_rtp_rfc5993hr_cmd,
       "no rtp-patch rfc5993hr", NO_STR RTP_PATCH_STR RTP_TS101318_RFC5993_CONV_STR)
 {
-	g_cfg->trunk.rfc5993_hr_convert = false;
+	g_cfg->trunk->rfc5993_hr_convert = false;
 	return CMD_SUCCESS;
 }
 
 DEFUN(cfg_mgcp_no_patch_rtp,
       cfg_mgcp_no_patch_rtp_cmd, "no rtp-patch", NO_STR RTP_PATCH_STR)
 {
-	g_cfg->trunk.force_constant_ssrc = 0;
-	g_cfg->trunk.force_aligned_timing = 0;
-	g_cfg->trunk.rfc5993_hr_convert = false;
+	g_cfg->trunk->force_constant_ssrc = 0;
+	g_cfg->trunk->force_aligned_timing = 0;
+	g_cfg->trunk->rfc5993_hr_convert = false;
 	return CMD_SUCCESS;
 }
 
@@ -766,7 +754,7 @@
       "rtp keep-alive <1-120>",
       RTP_STR RTP_KEEPALIVE_STR "Keep alive interval in secs\n")
 {
-	mgcp_trunk_set_keepalive(&g_cfg->trunk, atoi(argv[0]));
+	mgcp_trunk_set_keepalive(g_cfg->trunk, atoi(argv[0]));
 	return CMD_SUCCESS;
 }
 
@@ -775,7 +763,7 @@
       "rtp keep-alive once",
       RTP_STR RTP_KEEPALIVE_STR "Send dummy packet only once after CRCX/MDCX\n")
 {
-	mgcp_trunk_set_keepalive(&g_cfg->trunk, MGCP_KEEPALIVE_ONCE);
+	mgcp_trunk_set_keepalive(g_cfg->trunk, MGCP_KEEPALIVE_ONCE);
 	return CMD_SUCCESS;
 }
 
@@ -783,7 +771,7 @@
       cfg_mgcp_no_rtp_keepalive_cmd,
       "no rtp keep-alive", NO_STR RTP_STR RTP_KEEPALIVE_STR)
 {
-	mgcp_trunk_set_keepalive(&g_cfg->trunk, MGCP_KEEPALIVE_NEVER);
+	mgcp_trunk_set_keepalive(g_cfg->trunk, MGCP_KEEPALIVE_NEVER);
 	return CMD_SUCCESS;
 }
 
@@ -805,12 +793,12 @@
     DEFUN(cfg_mgcp_trunk, cfg_mgcp_trunk_cmd,
       "trunk <1-64>", "Configure a SS7 trunk\n" "Trunk Nr\n")
 {
-	struct mgcp_trunk_config *trunk;
+	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, index);
+		trunk = mgcp_trunk_alloc(g_cfg, index, MGCP_TRUNK_E1);
 
 	if (!trunk) {
 		vty_out(vty, "%%Unable to allocate trunk %u.%s",
@@ -825,7 +813,7 @@
 
 static int config_write_trunk(struct vty *vty)
 {
-	struct mgcp_trunk_config *trunk;
+	struct mgcp_trunk *trunk;
 
 	llist_for_each_entry(trunk, &g_cfg->trunks, entry) {
 		vty_out(vty, " trunk %d%s", trunk->trunk_nr, VTY_NEWLINE);
@@ -855,7 +843,7 @@
 		else
 			vty_out(vty, "  no rtcp-omit%s", VTY_NEWLINE);
 		if (trunk->force_constant_ssrc || trunk->force_aligned_timing
-		    || g_cfg->trunk.rfc5993_hr_convert) {
+		    || g_cfg->trunk->rfc5993_hr_convert) {
 			vty_out(vty, "  %srtp-patch ssrc%s",
 				trunk->force_constant_ssrc ? "" : "no ",
 				VTY_NEWLINE);
@@ -883,7 +871,7 @@
       "Add extra fmtp for the SDP file\n" "Audio\n" "Fmtp-extra\n"
       "Extra Information\n")
 {
-	struct mgcp_trunk_config *trunk = vty->index;
+	struct mgcp_trunk *trunk = vty->index;
 	char *txt = argv_concat(argv, argc, 0);
 	if (!txt)
 		return CMD_WARNING;
@@ -898,7 +886,7 @@
       "sdp audio-payload number <0-255>",
       SDP_STR AUDIO_STR "Number\n" "Payload Number\n")
 {
-	struct mgcp_trunk_config *trunk = vty->index;
+	struct mgcp_trunk *trunk = vty->index;
 	unsigned int payload = atoi(argv[0]);
 
 	trunk->audio_payload = payload;
@@ -914,7 +902,7 @@
       "sdp audio-payload name NAME",
       SDP_STR AUDIO_STR "Payload\n" "Payload Name\n")
 {
-	struct mgcp_trunk_config *trunk = vty->index;
+	struct mgcp_trunk *trunk = vty->index;
 
 	osmo_talloc_replace_string(g_cfg, &trunk->audio_name, argv[0]);
 	return CMD_SUCCESS;
@@ -929,7 +917,7 @@
       "loop (0|1)",
       "Loop audio for all endpoints on this trunk\n" "Don't Loop\n" "Loop\n")
 {
-	struct mgcp_trunk_config *trunk = vty->index;
+	struct mgcp_trunk *trunk = vty->index;
 
 	if (g_cfg->osmux) {
 		vty_out(vty, "Cannot use `loop' with `osmux'.%s", VTY_NEWLINE);
@@ -944,7 +932,7 @@
       "sdp audio-payload send-ptime",
       SDP_STR AUDIO_STR "Send SDP ptime (packet duration) attribute\n")
 {
-	struct mgcp_trunk_config *trunk = vty->index;
+	struct mgcp_trunk *trunk = vty->index;
 	trunk->audio_send_ptime = 1;
 	return CMD_SUCCESS;
 }
@@ -954,7 +942,7 @@
       "no sdp audio-payload send-ptime",
       NO_STR SDP_STR AUDIO_STR "Send SDP ptime (packet duration) attribute\n")
 {
-	struct mgcp_trunk_config *trunk = vty->index;
+	struct mgcp_trunk *trunk = vty->index;
 	trunk->audio_send_ptime = 0;
 	return CMD_SUCCESS;
 }
@@ -964,7 +952,7 @@
       "sdp audio-payload send-name",
       SDP_STR AUDIO_STR "Send SDP rtpmap with the audio name\n")
 {
-	struct mgcp_trunk_config *trunk = vty->index;
+	struct mgcp_trunk *trunk = vty->index;
 	trunk->audio_send_name = 1;
 	return CMD_SUCCESS;
 }
@@ -974,14 +962,14 @@
       "no sdp audio-payload send-name",
       NO_STR SDP_STR AUDIO_STR "Send SDP rtpmap with the audio name\n")
 {
-	struct mgcp_trunk_config *trunk = vty->index;
+	struct mgcp_trunk *trunk = vty->index;
 	trunk->audio_send_name = 0;
 	return CMD_SUCCESS;
 }
 
 DEFUN(cfg_trunk_omit_rtcp, cfg_trunk_omit_rtcp_cmd, "rtcp-omit", RTCP_OMIT_STR)
 {
-	struct mgcp_trunk_config *trunk = vty->index;
+	struct mgcp_trunk *trunk = vty->index;
 	trunk->omit_rtcp = 1;
 	return CMD_SUCCESS;
 }
@@ -989,7 +977,7 @@
 DEFUN(cfg_trunk_no_omit_rtcp,
       cfg_trunk_no_omit_rtcp_cmd, "no rtcp-omit", NO_STR RTCP_OMIT_STR)
 {
-	struct mgcp_trunk_config *trunk = vty->index;
+	struct mgcp_trunk *trunk = vty->index;
 	trunk->omit_rtcp = 0;
 	return CMD_SUCCESS;
 }
@@ -998,7 +986,7 @@
       cfg_trunk_patch_rtp_ssrc_cmd,
       "rtp-patch ssrc", RTP_PATCH_STR "Force a fixed SSRC\n")
 {
-	struct mgcp_trunk_config *trunk = vty->index;
+	struct mgcp_trunk *trunk = vty->index;
 	trunk->force_constant_ssrc = 1;
 	return CMD_SUCCESS;
 }
@@ -1007,7 +995,7 @@
       cfg_trunk_no_patch_rtp_ssrc_cmd,
       "no rtp-patch ssrc", NO_STR RTP_PATCH_STR "Force a fixed SSRC\n")
 {
-	struct mgcp_trunk_config *trunk = vty->index;
+	struct mgcp_trunk *trunk = vty->index;
 	trunk->force_constant_ssrc = 0;
 	return CMD_SUCCESS;
 }
@@ -1016,7 +1004,7 @@
       cfg_trunk_patch_rtp_ts_cmd,
       "rtp-patch timestamp", RTP_PATCH_STR "Adjust RTP timestamp\n")
 {
-	struct mgcp_trunk_config *trunk = vty->index;
+	struct mgcp_trunk *trunk = vty->index;
 	trunk->force_aligned_timing = 1;
 	return CMD_SUCCESS;
 }
@@ -1025,7 +1013,7 @@
       cfg_trunk_no_patch_rtp_ts_cmd,
       "no rtp-patch timestamp", NO_STR RTP_PATCH_STR "Adjust RTP timestamp\n")
 {
-	struct mgcp_trunk_config *trunk = vty->index;
+	struct mgcp_trunk *trunk = vty->index;
 	trunk->force_aligned_timing = 0;
 	return CMD_SUCCESS;
 }
@@ -1034,7 +1022,7 @@
       cfg_trunk_patch_rtp_rfc5993hr_cmd,
       "rtp-patch rfc5993hr", RTP_PATCH_STR RTP_TS101318_RFC5993_CONV_STR)
 {
-	struct mgcp_trunk_config *trunk = vty->index;
+	struct mgcp_trunk *trunk = vty->index;
 	trunk->rfc5993_hr_convert = true;
 	return CMD_SUCCESS;
 }
@@ -1043,7 +1031,7 @@
       cfg_trunk_no_patch_rtp_rfc5993hr_cmd,
       "no rtp-patch rfc5993hr", NO_STR RTP_PATCH_STR RTP_TS101318_RFC5993_CONV_STR)
 {
-	struct mgcp_trunk_config *trunk = vty->index;
+	struct mgcp_trunk *trunk = vty->index;
 	trunk->rfc5993_hr_convert = false;
 	return CMD_SUCCESS;
 }
@@ -1051,7 +1039,7 @@
 DEFUN(cfg_trunk_no_patch_rtp,
       cfg_trunk_no_patch_rtp_cmd, "no rtp-patch", NO_STR RTP_PATCH_STR)
 {
-	struct mgcp_trunk_config *trunk = vty->index;
+	struct mgcp_trunk *trunk = vty->index;
 	trunk->force_constant_ssrc = 0;
 	trunk->force_aligned_timing = 0;
 	trunk->rfc5993_hr_convert = false;
@@ -1063,7 +1051,7 @@
       "rtp keep-alive <1-120>",
       RTP_STR RTP_KEEPALIVE_STR "Keep-alive interval in secs\n")
 {
-	struct mgcp_trunk_config *trunk = vty->index;
+	struct mgcp_trunk *trunk = vty->index;
 	mgcp_trunk_set_keepalive(trunk, atoi(argv[0]));
 	return CMD_SUCCESS;
 }
@@ -1073,7 +1061,7 @@
       "rtp keep-alive once",
       RTP_STR RTP_KEEPALIVE_STR "Send dummy packet only once after CRCX/MDCX\n")
 {
-	struct mgcp_trunk_config *trunk = vty->index;
+	struct mgcp_trunk *trunk = vty->index;
 	mgcp_trunk_set_keepalive(trunk, MGCP_KEEPALIVE_ONCE);
 	return CMD_SUCCESS;
 }
@@ -1082,7 +1070,7 @@
       cfg_trunk_no_rtp_keepalive_cmd,
       "no rtp keep-alive", NO_STR RTP_STR RTP_KEEPALIVE_STR)
 {
-	struct mgcp_trunk_config *trunk = vty->index;
+	struct mgcp_trunk *trunk = vty->index;
 	mgcp_trunk_set_keepalive(trunk, 0);
 	return CMD_SUCCESS;
 }
@@ -1091,7 +1079,7 @@
       cfg_trunk_allow_transcoding_cmd,
       "allow-transcoding", "Allow transcoding\n")
 {
-	struct mgcp_trunk_config *trunk = vty->index;
+	struct mgcp_trunk *trunk = vty->index;
 	trunk->no_audio_transcoding = 0;
 	return CMD_SUCCESS;
 }
@@ -1100,7 +1088,7 @@
       cfg_trunk_no_allow_transcoding_cmd,
       "no allow-transcoding", NO_STR "Allow transcoding\n")
 {
-	struct mgcp_trunk_config *trunk = vty->index;
+	struct mgcp_trunk *trunk = vty->index;
 	trunk->no_audio_transcoding = 1;
 	return CMD_SUCCESS;
 }
@@ -1112,7 +1100,7 @@
       "The name in hex of the endpoint\n" "Disable the loop\n"
       "Enable the loop\n")
 {
-	struct mgcp_trunk_config *trunk;
+	struct mgcp_trunk *trunk;
 	struct mgcp_endpoint *endp;
 	struct mgcp_conn *conn;
 
@@ -1136,7 +1124,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)
@@ -1170,7 +1158,7 @@
       "destination IP of the data\n" "destination port\n")
 {
 	struct mgcp_rtp_tap *tap;
-	struct mgcp_trunk_config *trunk;
+	struct mgcp_trunk *trunk;
 	struct mgcp_endpoint *endp;
 	struct mgcp_conn_rtp *conn;
         const char *conn_id = NULL;
@@ -1195,7 +1183,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);
@@ -1225,7 +1213,7 @@
       "free-endpoint <0-64> NUMBER",
       "Free the given endpoint\n" "Trunk number\n" "Endpoint number in hex.\n")
 {
-	struct mgcp_trunk_config *trunk;
+	struct mgcp_trunk *trunk;
 	struct mgcp_endpoint *endp;
 
 	trunk = find_trunk(g_cfg, atoi(argv[0]));
@@ -1248,7 +1236,7 @@
 		return CMD_WARNING;
 	}
 
-	endp = &trunk->endpoints[endp_no];
+	endp = trunk->endpoints[endp_no];
 	mgcp_endp_release(endp);
 	return CMD_SUCCESS;
 }
@@ -1257,7 +1245,7 @@
       "reset-endpoint <0-64> NUMBER",
       "Reset the given endpoint\n" "Trunk number\n" "Endpoint number in hex.\n")
 {
-	struct mgcp_trunk_config *trunk;
+	struct mgcp_trunk *trunk;
 	struct mgcp_endpoint *endp;
 	int endp_no, rc;
 
@@ -1281,8 +1269,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;
@@ -1318,7 +1306,7 @@
 	else if (strcmp(argv[0], "only") == 0)
 		g_cfg->osmux = OSMUX_USAGE_ONLY;
 
-	if (g_cfg->trunk.audio_loop) {
+	if (g_cfg->trunk->audio_loop) {
 		vty_out(vty, "Cannot use `loop' with `osmux'.%s", VTY_NEWLINE);
 		return CMD_WARNING;
 	}
@@ -1500,7 +1488,7 @@
 		      enum mgcp_role role)
 {
 	int rc;
-	struct mgcp_trunk_config *trunk;
+	struct mgcp_trunk *trunk;
 
 	cfg->osmux_port = OSMUX_PORT;
 	cfg->osmux_batch = 4;
@@ -1519,15 +1507,15 @@
 		return -1;
 	}
 
-	if (mgcp_endpoints_allocate(&g_cfg->trunk) != 0) {
+	if (mgcp_trunk_alloc_endpts(g_cfg->trunk) != 0) {
 		LOGP(DLMGCP, LOGL_ERROR,
 		     "Failed to initialize the virtual trunk (%d endpoints)\n",
-		     g_cfg->trunk.number_endpoints);
+		     g_cfg->trunk->number_endpoints);
 		return -1;
 	}
 
 	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 48869c4..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>
@@ -71,7 +72,7 @@
 /* FIXME: Make use of the rtp proxy code */
 
 static struct mgcp_config *cfg;
-static struct mgcp_trunk_config *reset_trunk;
+static struct mgcp_trunk *reset_trunk;
 static int reset_endpoints = 0;
 static int daemonize = 0;
 
@@ -148,13 +149,13 @@
 
 /* Callback function to be called when the RSIP ("Reset in Progress") mgcp
  * command is received */
-static int mgcp_rsip_cb(struct mgcp_trunk_config *tcfg)
+static int mgcp_rsip_cb(struct mgcp_trunk *trunk)
 {
 	/* Set flag so that, when read_call_agent() is called next time
 	 * the reset can progress */
 	reset_endpoints = 1;
 
-	reset_trunk = tcfg;
+	reset_trunk = trunk;
 
 	return 0;
 }
@@ -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 c72382e..1f74b01 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_config *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;
 }
 
@@ -641,11 +652,11 @@
 	return real_clock_gettime(clk_id, tp);
 }
 
-static void mgcp_endpoints_release(struct mgcp_trunk_config *trunk)
+static void mgcp_endpoints_release(struct mgcp_trunk *trunk)
 {
 	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)
@@ -749,7 +760,7 @@
 {
 	struct mgcp_config *cfg;
 	struct mgcp_endpoint *endp;
-	struct mgcp_trunk_config *trunk2;
+	struct mgcp_trunk *trunk2;
 	int i;
 	struct mgcp_conn_rtp *conn = NULL;
 	char last_conn_id[256];
@@ -757,14 +768,14 @@
 
 	cfg = mgcp_config_alloc();
 
-	cfg->trunk.vty_number_endpoints = 64;
-	mgcp_endpoints_allocate(&cfg->trunk);
+	cfg->trunk->vty_number_endpoints = 64;
+        mgcp_trunk_alloc_endpts(cfg->trunk);
 	cfg->policy_cb = mgcp_test_policy_cb;
 
 	memset(last_conn_id, 0, sizeof(last_conn_id));
 
-	trunk2 = mgcp_trunk_alloc(cfg, 1);
-	mgcp_endpoints_allocate(trunk2);
+	trunk2 = mgcp_trunk_alloc(cfg, 1, MGCP_TRUNK_E1);
+        mgcp_trunk_alloc_endpts(trunk2);
 
 	for (i = 0; i < ARRAY_SIZE(tests); i++) {
 		const struct mgcp_test *t = &tests[i];
@@ -777,7 +788,7 @@
 		last_endpoint = -1;
 		dummy_packets = 0;
 
-		osmo_talloc_replace_string(cfg, &cfg->trunk.audio_fmtp_extra,
+		osmo_talloc_replace_string(cfg, &cfg->trunk->audio_fmtp_extra,
 					   t->extra_fmtp);
 
 		inp = create_msg(t->req, last_conn_id);
@@ -810,7 +821,7 @@
 			printf("Dummy packets: %d\n", dummy_packets);
 
 		if (last_endpoint != -1) {
-			endp = &cfg->trunk.endpoints[last_endpoint];
+			endp = cfg->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->trunk.endpoints[last_endpoint];
+			endp = cfg->trunk->endpoints[last_endpoint];
 
 			fprintf(stderr, "endpoint 0x%x: "
 				"payload type %d (expected %d)\n",
@@ -883,27 +894,27 @@
 	}
 
 	mgcp_endpoints_release(trunk2);
-	mgcp_endpoints_release(&cfg->trunk);
+	mgcp_endpoints_release(cfg->trunk);
 	talloc_free(cfg);
 }
 
 static void test_retransmission(void)
 {
 	struct mgcp_config *cfg;
-	struct mgcp_trunk_config *trunk2;
+	struct mgcp_trunk *trunk2;
 	int i;
 	char last_conn_id[256];
 	int rc;
 
 	cfg = mgcp_config_alloc();
 
-	cfg->trunk.vty_number_endpoints = 64;
-	mgcp_endpoints_allocate(&cfg->trunk);
+	cfg->trunk->vty_number_endpoints = 64;
+        mgcp_trunk_alloc_endpts(cfg->trunk);
 
 	memset(last_conn_id, 0, sizeof(last_conn_id));
 
-	trunk2 = mgcp_trunk_alloc(cfg, 1);
-	mgcp_endpoints_allocate(trunk2);
+	trunk2 = mgcp_trunk_alloc(cfg, 1, MGCP_TRUNK_E1);
+        mgcp_trunk_alloc_endpts(trunk2);
 
 	for (i = 0; i < ARRAY_SIZE(retransmit); i++) {
 		const struct mgcp_test *t = &retransmit[i];
@@ -944,7 +955,7 @@
 	}
 
 	mgcp_endpoints_release(trunk2);
-	mgcp_endpoints_release(&cfg->trunk);
+	mgcp_endpoints_release(cfg->trunk);
 	talloc_free(cfg);
 }
 
@@ -958,18 +969,18 @@
 static void test_rqnt_cb(void)
 {
 	struct mgcp_config *cfg;
-	struct mgcp_trunk_config *trunk2;
+	struct mgcp_trunk *trunk2;
 	struct msgb *inp, *msg;
 	char conn_id[256];
 
 	cfg = mgcp_config_alloc();
 	cfg->rqnt_cb = rqnt_cb;
 
-	cfg->trunk.vty_number_endpoints = 64;
-	mgcp_endpoints_allocate(&cfg->trunk);
+	cfg->trunk->vty_number_endpoints = 64;
+        mgcp_trunk_alloc_endpts(cfg->trunk);
 
-	trunk2 = mgcp_trunk_alloc(cfg, 1);
-	mgcp_endpoints_allocate(trunk2);
+	trunk2 = mgcp_trunk_alloc(cfg, 1, MGCP_TRUNK_E1);
+        mgcp_trunk_alloc_endpts(trunk2);
 
 	inp = create_msg(CRCX, NULL);
 	msg = mgcp_handle_message(cfg, inp);
@@ -999,7 +1010,7 @@
 	msgb_free(mgcp_handle_message(cfg, inp));
 	msgb_free(inp);
 	mgcp_endpoints_release(trunk2);
-	mgcp_endpoints_release(&cfg->trunk);
+	mgcp_endpoints_release(cfg->trunk);
 	talloc_free(cfg);
 }
 
@@ -1035,8 +1046,9 @@
 {
 	int i;
 	struct mgcp_endpoint endp;
+	struct mgcp_endpoint *endpoints[1];
 	struct mgcp_config cfg = {0};
-	struct mgcp_trunk_config trunk;
+	struct mgcp_trunk trunk;
 
 	printf("Testing packet loss calculation.\n");
 
@@ -1046,8 +1058,9 @@
 	endp.cfg = &cfg;
 	endp.type = &ep_typeset.rtp;
 	trunk.vty_number_endpoints = 1;
-	trunk.endpoints = &endp;
-	endp.tcfg = &trunk;
+	trunk.endpoints = endpoints;
+	trunk.endpoints[0] = &endp;
+	endp.trunk = &trunk;
 	INIT_LLIST_HEAD(&endp.conns);
 
 	for (i = 0; i < ARRAY_SIZE(pl_test_dat); ++i) {
@@ -1262,8 +1275,9 @@
 {
 	int i;
 
-	struct mgcp_trunk_config trunk;
+	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,11 +1310,12 @@
 	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;
 
-	endp.tcfg = &trunk;
+	endp.trunk = &trunk;
 
 	INIT_LLIST_HEAD(&endp.conns);
 	_conn = mgcp_conn_alloc(NULL, &endp, MGCP_CONN_TYPE_RTP,
@@ -1361,7 +1376,7 @@
 static void test_multilple_codec(void)
 {
 	struct mgcp_config *cfg;
-	struct mgcp_trunk_config *trunk2;
+	struct mgcp_trunk *trunk2;
 	struct mgcp_endpoint *endp;
 	struct msgb *inp, *resp;
 	struct in_addr addr;
@@ -1371,12 +1386,12 @@
 	printf("Testing multiple payload types\n");
 
 	cfg = mgcp_config_alloc();
-	cfg->trunk.vty_number_endpoints = 64;
-	mgcp_endpoints_allocate(&cfg->trunk);
+	cfg->trunk->vty_number_endpoints = 64;
+        mgcp_trunk_alloc_endpts(cfg->trunk);
 	cfg->policy_cb = mgcp_test_policy_cb;
 
-	trunk2 = mgcp_trunk_alloc(cfg, 1);
-	mgcp_endpoints_allocate(trunk2);
+	trunk2 = mgcp_trunk_alloc(cfg, 1, MGCP_TRUNK_E1);
+        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->trunk.endpoints[last_endpoint];
+	endp = cfg->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->trunk.endpoints[last_endpoint];
+	endp = cfg->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->trunk.endpoints[last_endpoint];
+	endp = cfg->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->trunk.endpoints[last_endpoint];
+	endp = cfg->trunk->endpoints[last_endpoint];
 	conn = mgcp_conn_get_rtp(endp, conn_id);
 	OSMO_ASSERT(conn);
 	OSMO_ASSERT(conn->end.codec->payload_type == 18);
@@ -1446,9 +1461,9 @@
 	/* Allocate 5 at mgw at select GSM.. */
 	last_endpoint = -1;
 	inp = create_msg(CRCX_MULT_GSM_EXACT, NULL);
-	talloc_free(cfg->trunk.audio_name);
-	cfg->trunk.audio_name = "GSM/8000";
-	cfg->trunk.no_audio_transcoding = 1;
+	talloc_free(cfg->trunk->audio_name);
+	cfg->trunk->audio_name = "GSM/8000";
+	cfg->trunk->no_audio_transcoding = 1;
 	resp = mgcp_handle_message(cfg, inp);
 	OSMO_ASSERT(get_conn_id_from_response(resp->data, conn_id,
 					      sizeof(conn_id)) == 0);
@@ -1456,7 +1471,7 @@
 	msgb_free(resp);
 
 	OSMO_ASSERT(last_endpoint == 5);
-	endp = &cfg->trunk.endpoints[last_endpoint];
+	endp = cfg->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->trunk.endpoints[last_endpoint];
+	endp = cfg->trunk->endpoints[last_endpoint];
 	conn = mgcp_conn_get_rtp(endp, conn_id);
 	OSMO_ASSERT(conn);
 	OSMO_ASSERT(conn->end.codec->payload_type == 3);
@@ -1489,7 +1504,7 @@
 
 	last_endpoint = -1;
 	inp = create_msg(CRCX_MULT_GSM_EXACT, NULL);
-	cfg->trunk.no_audio_transcoding = 0;
+	cfg->trunk->no_audio_transcoding = 0;
 	resp = mgcp_handle_message(cfg, inp);
 	OSMO_ASSERT(get_conn_id_from_response(resp->data, conn_id,
 					      sizeof(conn_id)) == 0);
@@ -1497,13 +1512,13 @@
 	msgb_free(resp);
 
 	OSMO_ASSERT(last_endpoint == 5);
-	endp = &cfg->trunk.endpoints[last_endpoint];
+	endp = cfg->trunk->endpoints[last_endpoint];
 	conn = mgcp_conn_get_rtp(endp, conn_id);
 	OSMO_ASSERT(conn);
 	OSMO_ASSERT(conn->end.codec->payload_type == 0);
 
 	mgcp_endpoints_release(trunk2);
-	mgcp_endpoints_release(&cfg->trunk);
+	mgcp_endpoints_release(cfg->trunk);
 	talloc_free(cfg);
 }
 
@@ -1517,10 +1532,10 @@
 	printf("Testing no sequence flow on initial packet\n");
 
 	cfg = mgcp_config_alloc();
-	cfg->trunk.vty_number_endpoints = 64;
-	mgcp_endpoints_allocate(&cfg->trunk);
+	cfg->trunk->vty_number_endpoints = 64;
+        mgcp_trunk_alloc_endpts(cfg->trunk);
 
-	endp = &cfg->trunk.endpoints[1];
+	endp = cfg->trunk->endpoints[1];
 
 	_conn = mgcp_conn_alloc(NULL, endp, MGCP_CONN_TYPE_RTP,
 				"test-connection");
@@ -1552,27 +1567,27 @@
 	OSMO_ASSERT(conn->state.stats.cycles == UINT16_MAX + 1);
 	OSMO_ASSERT(conn->state.stats.max_seq == 0);
 
-	mgcp_endpoints_release(&cfg->trunk);
+	mgcp_endpoints_release(cfg->trunk);
 	talloc_free(cfg);
 }
 
 static void test_no_name(void)
 {
-	struct mgcp_trunk_config *trunk2;
+	struct mgcp_trunk *trunk2;
 	struct mgcp_config *cfg;
 	struct msgb *inp, *msg;
 
 	printf("Testing no rtpmap name\n");
 	cfg = mgcp_config_alloc();
 
-	cfg->trunk.vty_number_endpoints = 64;
-	cfg->trunk.audio_send_name = 0;
-	mgcp_endpoints_allocate(&cfg->trunk);
+	cfg->trunk->vty_number_endpoints = 64;
+	cfg->trunk->audio_send_name = 0;
+        mgcp_trunk_alloc_endpts(cfg->trunk);
 
 	cfg->policy_cb = mgcp_test_policy_cb;
 
-	trunk2 = mgcp_trunk_alloc(cfg, 1);
-	mgcp_endpoints_allocate(trunk2);
+	trunk2 = mgcp_trunk_alloc(cfg, 1, MGCP_TRUNK_E1);
+        mgcp_trunk_alloc_endpts(trunk2);
 
 	inp = create_msg(CRCX, NULL);
 	msg = mgcp_handle_message(cfg, inp);
@@ -1586,7 +1601,7 @@
 	msgb_free(msg);
 
 	mgcp_endpoints_release(trunk2);
-	mgcp_endpoints_release(&cfg->trunk);
+	mgcp_endpoints_release(cfg->trunk);
 	talloc_free(cfg);
 }
 

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

Gerrit-Project: osmo-mgw
Gerrit-Branch: master
Gerrit-Change-Id: Ice8aaf03faa2fd99074f8665eea3a696d30c5eb3
Gerrit-Change-Number: 18372
Gerrit-PatchSet: 1
Gerrit-Owner: dexter <pmaier at sysmocom.de>
Gerrit-MessageType: newchange
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20200519/eed4e750/attachment.htm>


More information about the gerrit-log mailing list