create libosmo-mgcp and osmo-mgw by copying legacy code

This a cosmetic commit, copying libosmo-legacy-mgcp to libosmo-mgcp and
osmo-bsc_mgcp to osmo-mgw 1:1 at first, to provide a basis for next patches
that highlight the changes from legacy to new code.

Until osmo-msc and osmo-bsc are adjusted to operate with the new code, we will
keep the legacy code alongside the new code. The legacy code might be dropped
later.

Change-Id: Idf54481754a1765bdb2d0d7033bc0d7dc2018024
diff --git a/src/libosmo-mgcp/Makefile.am b/src/libosmo-mgcp/Makefile.am
new file mode 100644
index 0000000..bcf6242
--- /dev/null
+++ b/src/libosmo-mgcp/Makefile.am
@@ -0,0 +1,50 @@
+AM_CPPFLAGS = \
+	$(all_includes) \
+	-I$(top_srcdir)/include \
+	-I$(top_builddir) \
+	$(NULL)
+
+AM_CFLAGS = \
+	-Wall \
+	$(LIBOSMOCORE_CFLAGS) \
+	$(LIBOSMOVTY_CFLAGS) \
+	$(LIBOSMONETIF_CFLAGS) \
+	$(COVERAGE_CFLAGS) \
+	$(LIBBCG729_CFLAGS) \
+	$(NULL)
+
+AM_LDFLAGS = \
+	$(LIBOSMOCORE_LIBS) \
+	$(LIBOSMOGSM_LIBS) \
+	$(LIBOSMONETIF_LIBS) \
+	$(COVERAGE_LDFLAGS) \
+	$(LIBBCG729_LIBS) \
+	$(NULL)
+
+# This is not at all related to the release version, but a range of supported
+# API versions. Read TODO_RELEASE in the source tree's root!
+LEGACY_MGCP_LIBVERSION=0:0:0
+
+lib_LTLIBRARIES = \
+	libosmo-legacy-mgcp.la \
+	$(NULL)
+
+noinst_HEADERS = \
+	g711common.h \
+	$(NULL)
+
+libosmo_legacy_mgcp_la_SOURCES = \
+	mgcp_common.c \
+	mgcp_protocol.c \
+	mgcp_network.c \
+	mgcp_vty.c \
+	mgcp_osmux.c \
+	mgcp_sdp.c \
+	$(NULL)
+if BUILD_MGCP_TRANSCODING
+libosmo_legacy_mgcp_la_SOURCES += \
+	mgcp_transcode.c \
+	$(NULL)
+endif
+
+libosmo_legacy_mgcp_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LEGACY_MGCP_LIBVERSION)
diff --git a/src/libosmo-mgcp/g711common.h b/src/libosmo-mgcp/g711common.h
new file mode 100644
index 0000000..cb35fc6
--- /dev/null
+++ b/src/libosmo-mgcp/g711common.h
@@ -0,0 +1,187 @@
+/*
+ *  PCM - A-Law conversion
+ *  Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
+ *
+ *  Wrapper for linphone Codec class by Simon Morlat <simon.morlat@linphone.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+static inline int val_seg(int val)
+{
+	int r = 0;
+	val >>= 7; /*7 = 4 + 3*/
+	if (val & 0xf0) {
+		val >>= 4;
+		r += 4;
+	}
+	if (val & 0x0c) {
+		val >>= 2;
+		r += 2;
+	}
+	if (val & 0x02)
+		r += 1;
+	return r;
+}
+
+/*
+ * s16_to_alaw() - Convert a 16-bit linear PCM value to 8-bit A-law
+ *
+ * s16_to_alaw() accepts an 16-bit integer and encodes it as A-law data.
+ *
+ *		Linear Input Code	Compressed Code
+ *	------------------------	---------------
+ *	0000000wxyza			000wxyz
+ *	0000001wxyza			001wxyz
+ *	000001wxyzab			010wxyz
+ *	00001wxyzabc			011wxyz
+ *	0001wxyzabcd			100wxyz
+ *	001wxyzabcde			101wxyz
+ *	01wxyzabcdef			110wxyz
+ *	1wxyzabcdefg			111wxyz
+ *
+ * For further information see John C. Bellamy's Digital Telephony, 1982,
+ * John Wiley & Sons, pps 98-111 and 472-476.
+ * G711 is designed for 13 bits input signal, this function add extra shifting to take this into account.
+ */
+
+static inline unsigned char s16_to_alaw(int pcm_val)
+{
+	int		mask;
+	int		seg;
+	unsigned char	aval;
+
+	if (pcm_val >= 0) {
+		mask = 0xD5;
+	} else {
+		mask = 0x55;
+		pcm_val = -pcm_val;
+		if (pcm_val > 0x7fff)
+			pcm_val = 0x7fff;
+	}
+
+	if (pcm_val < 256) /*256 = 32 << 3*/
+		aval = pcm_val >> 4; /*4 = 1 + 3*/
+	else {
+		/* Convert the scaled magnitude to segment number. */
+		seg = val_seg(pcm_val);
+		aval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0x0f);
+	}
+	return aval ^ mask;
+}
+
+/*
+ * alaw_to_s16() - Convert an A-law value to 16-bit linear PCM
+ *
+ */
+static inline int alaw_to_s16(unsigned char a_val)
+{
+	int		t;
+	int		seg;
+
+	a_val ^= 0x55;
+	t = a_val & 0x7f;
+	if (t < 16)
+		t = (t << 4) + 8;
+	else {
+		seg = (t >> 4) & 0x07;
+		t = ((t & 0x0f) << 4) + 0x108;
+		t <<= seg -1;
+	}
+	return ((a_val & 0x80) ? t : -t);
+}
+/*
+ * s16_to_ulaw() - Convert a linear PCM value to u-law
+ *
+ * In order to simplify the encoding process, the original linear magnitude
+ * is biased by adding 33 which shifts the encoding range from (0 - 8158) to
+ * (33 - 8191). The result can be seen in the following encoding table:
+ *
+ *	Biased Linear Input Code	Compressed Code
+ *	------------------------	---------------
+ *	00000001wxyza			000wxyz
+ *	0000001wxyzab			001wxyz
+ *	000001wxyzabc			010wxyz
+ *	00001wxyzabcd			011wxyz
+ *	0001wxyzabcde			100wxyz
+ *	001wxyzabcdef			101wxyz
+ *	01wxyzabcdefg			110wxyz
+ *	1wxyzabcdefgh			111wxyz
+ *
+ * Each biased linear code has a leading 1 which identifies the segment
+ * number. The value of the segment number is equal to 7 minus the number
+ * of leading 0's. The quantization interval is directly available as the
+ * four bits wxyz.  * The trailing bits (a - h) are ignored.
+ *
+ * Ordinarily the complement of the resulting code word is used for
+ * transmission, and so the code word is complemented before it is returned.
+ *
+ * For further information see John C. Bellamy's Digital Telephony, 1982,
+ * John Wiley & Sons, pps 98-111 and 472-476.
+ */
+
+static inline unsigned char s16_to_ulaw(int pcm_val)	/* 2's complement (16-bit range) */
+{
+	int mask;
+	int seg;
+	unsigned char uval;
+
+	if (pcm_val < 0) {
+		pcm_val = 0x84 - pcm_val;
+		mask = 0x7f;
+	} else {
+		pcm_val += 0x84;
+		mask = 0xff;
+	}
+	if (pcm_val > 0x7fff)
+		pcm_val = 0x7fff;
+
+	/* Convert the scaled magnitude to segment number. */
+	seg = val_seg(pcm_val);
+
+	/*
+	 * Combine the sign, segment, quantization bits;
+	 * and complement the code word.
+	 */
+	uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0x0f);
+	return uval ^ mask;
+}
+
+/*
+ * ulaw_to_s16() - Convert a u-law value to 16-bit linear PCM
+ *
+ * First, a biased linear code is derived from the code word. An unbiased
+ * output can then be obtained by subtracting 33 from the biased code.
+ *
+ * Note that this function expects to be passed the complement of the
+ * original code word. This is in keeping with ISDN conventions.
+ */
+static inline int ulaw_to_s16(unsigned char u_val)
+{
+	int t;
+
+	/* Complement to obtain normal u-law value. */
+	u_val = ~u_val;
+
+	/*
+	 * Extract and bias the quantization bits. Then
+	 * shift up by the segment number and subtract out the bias.
+	 */
+	t = ((u_val & 0x0f) << 3) + 0x84;
+	t <<= (u_val & 0x70) >> 4;
+
+	return ((u_val & 0x80) ? (0x84 - t) : (t - 0x84));
+}
diff --git a/src/libosmo-mgcp/mgcp_common.c b/src/libosmo-mgcp/mgcp_common.c
new file mode 100644
index 0000000..444a060
--- /dev/null
+++ b/src/libosmo-mgcp/mgcp_common.c
@@ -0,0 +1,54 @@
+/* Media Gateway Control Protocol Media Gateway: RFC 3435 */
+/* Implementations useful both for the MGCP GW as well as MGCP GW clients */
+
+/*
+ * (C) 2016 by sysmocom s.m.f.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <errno.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/legacy_mgcp/mgcp.h>
+
+const struct value_string mgcp_connection_mode_strs[] = {
+	{ MGCP_CONN_NONE, "none" },
+	{ MGCP_CONN_RECV_SEND, "sendrecv" },
+	{ MGCP_CONN_SEND_ONLY, "sendonly" },
+	{ MGCP_CONN_RECV_ONLY, "recvonly" },
+	{ MGCP_CONN_LOOPBACK, "loopback" },
+	{ 0, NULL }
+};
+
+/* Ensure that the msg->l2h is NUL terminated. */
+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;
+}
diff --git a/src/libosmo-mgcp/mgcp_network.c b/src/libosmo-mgcp/mgcp_network.c
new file mode 100644
index 0000000..8ccfb42
--- /dev/null
+++ b/src/libosmo-mgcp/mgcp_network.c
@@ -0,0 +1,1166 @@
+/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
+/* The protocol implementation */
+
+/*
+ * (C) 2009-2012 by Holger Hans Peter Freyther <zecke@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/>.
+ *
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <time.h>
+#include <limits.h>
+
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/select.h>
+
+#include <osmocom/netif/rtp.h>
+
+#include <osmocom/legacy_mgcp/mgcp.h>
+#include <osmocom/legacy_mgcp/mgcp_internal.h>
+
+#include <osmocom/legacy_mgcp/osmux.h>
+
+#warning "Make use of the rtp proxy code"
+
+
+#define RTP_SEQ_MOD		(1 << 16)
+#define RTP_MAX_DROPOUT		3000
+#define RTP_MAX_MISORDER	100
+#define RTP_BUF_SIZE		4096
+
+enum {
+	MGCP_PROTO_RTP,
+	MGCP_PROTO_RTCP,
+};
+
+/**
+ * This does not need to be a precision timestamp and
+ * is allowed to wrap quite fast. The returned value is
+ * 1/unit seconds.
+ */
+static uint32_t get_current_ts(unsigned unit)
+{
+	struct timespec tp;
+	uint64_t ret;
+
+	if (!unit)
+		return 0;
+
+	memset(&tp, 0, sizeof(tp));
+	if (clock_gettime(CLOCK_MONOTONIC, &tp) != 0)
+		LOGP(DLMGCP, LOGL_NOTICE,
+			"Getting the clock failed.\n");
+
+	/* convert it to 1/unit seconds */
+	ret = tp.tv_sec;
+	ret *= unit;
+	ret += (int64_t)tp.tv_nsec * unit / 1000 / 1000 / 1000;
+
+	return ret;
+}
+
+int mgcp_udp_send(int fd, struct in_addr *addr, int port, char *buf, int len)
+{
+	struct sockaddr_in out;
+	out.sin_family = AF_INET;
+	out.sin_port = port;
+	memcpy(&out.sin_addr, addr, sizeof(*addr));
+
+	return sendto(fd, buf, len, 0, (struct sockaddr *)&out, sizeof(out));
+}
+
+int mgcp_send_dummy(struct mgcp_endpoint *endp)
+{
+	static char buf[] = { MGCP_DUMMY_LOAD };
+	int rc;
+	int was_rtcp = 0;
+
+	rc = mgcp_udp_send(endp->net_end.rtp.fd, &endp->net_end.addr,
+			   endp->net_end.rtp_port, buf, 1);
+
+	if (rc == -1)
+		goto failed;
+
+	if (endp->tcfg->omit_rtcp)
+		return rc;
+
+	was_rtcp = 1;
+	rc = mgcp_udp_send(endp->net_end.rtcp.fd, &endp->net_end.addr,
+			   endp->net_end.rtcp_port, buf, 1);
+
+	if (rc >= 0)
+		return rc;
+
+failed:
+	LOGP(DLMGCP, LOGL_ERROR,
+		"Failed to send dummy %s packet: %s on: 0x%x to %s:%d\n",
+		was_rtcp ? "RTCP" : "RTP",
+		strerror(errno), ENDPOINT_NUMBER(endp), inet_ntoa(endp->net_end.addr),
+		was_rtcp ? endp->net_end.rtcp_port : endp->net_end.rtp_port);
+
+	return -1;
+}
+
+static int32_t compute_timestamp_aligment_error(struct mgcp_rtp_stream_state *sstate,
+						int ptime, uint32_t timestamp)
+{
+	int32_t timestamp_delta;
+
+	if (ptime == 0)
+		return 0;
+
+	/* Align according to: T - Tlast = k * Tptime */
+	timestamp_delta = timestamp - sstate->last_timestamp;
+
+	return timestamp_delta % ptime;
+}
+
+static int check_rtp_timestamp(struct mgcp_endpoint *endp,
+			       struct mgcp_rtp_state *state,
+			       struct mgcp_rtp_stream_state *sstate,
+			       struct mgcp_rtp_end *rtp_end,
+			       struct sockaddr_in *addr,
+			       uint16_t seq, uint32_t timestamp,
+			       const char *text, int32_t *tsdelta_out)
+{
+	int32_t tsdelta;
+	int32_t timestamp_error;
+
+	/* Not fully intialized, skip */
+	if (sstate->last_tsdelta == 0 && timestamp == sstate->last_timestamp)
+		return 0;
+
+	if (seq == sstate->last_seq) {
+		if (timestamp != sstate->last_timestamp) {
+			sstate->err_ts_counter += 1;
+			LOGP(DLMGCP, LOGL_ERROR,
+			     "The %s timestamp delta is != 0 but the sequence "
+			     "number %d is the same, "
+			     "TS offset: %d, SeqNo offset: %d "
+			     "on 0x%x SSRC: %u timestamp: %u "
+			     "from %s:%d in %d\n",
+			     text, seq,
+			     state->timestamp_offset, state->seq_offset,
+			     ENDPOINT_NUMBER(endp), sstate->ssrc, timestamp,
+			     inet_ntoa(addr->sin_addr), ntohs(addr->sin_port),
+			     endp->conn_mode);
+		}
+		return 0;
+	}
+
+	tsdelta =
+		(int32_t)(timestamp - sstate->last_timestamp) /
+		(int16_t)(seq - sstate->last_seq);
+
+	if (tsdelta == 0) {
+		/* Don't update *tsdelta_out */
+		LOGP(DLMGCP, LOGL_NOTICE,
+		     "The %s timestamp delta is %d "
+		     "on 0x%x SSRC: %u timestamp: %u "
+		     "from %s:%d in %d\n",
+		     text, tsdelta,
+		     ENDPOINT_NUMBER(endp), sstate->ssrc, timestamp,
+		     inet_ntoa(addr->sin_addr), ntohs(addr->sin_port),
+		     endp->conn_mode);
+
+		return 0;
+	}
+
+	if (sstate->last_tsdelta != tsdelta) {
+		if (sstate->last_tsdelta) {
+			LOGP(DLMGCP, LOGL_INFO,
+			     "The %s timestamp delta changes from %d to %d "
+			     "on 0x%x SSRC: %u timestamp: %u from %s:%d in %d\n",
+			     text, sstate->last_tsdelta, tsdelta,
+			     ENDPOINT_NUMBER(endp), sstate->ssrc, timestamp,
+			     inet_ntoa(addr->sin_addr), ntohs(addr->sin_port),
+			     endp->conn_mode);
+		}
+	}
+
+	if (tsdelta_out)
+		*tsdelta_out = tsdelta;
+
+	timestamp_error =
+		compute_timestamp_aligment_error(sstate, state->packet_duration,
+						 timestamp);
+
+	if (timestamp_error) {
+		sstate->err_ts_counter += 1;
+		LOGP(DLMGCP, LOGL_NOTICE,
+		     "The %s timestamp has an alignment error of %d "
+		     "on 0x%x SSRC: %u "
+		     "SeqNo delta: %d, TS delta: %d, dTS/dSeq: %d "
+		     "from %s:%d in mode %d. ptime: %d\n",
+		     text, timestamp_error,
+		     ENDPOINT_NUMBER(endp), sstate->ssrc,
+		     (int16_t)(seq - sstate->last_seq),
+		     (int32_t)(timestamp - sstate->last_timestamp),
+		     tsdelta,
+		     inet_ntoa(addr->sin_addr), ntohs(addr->sin_port),
+		     endp->conn_mode, state->packet_duration);
+	}
+	return 1;
+}
+
+/* Set the timestamp offset according to the packet duration. */
+static int adjust_rtp_timestamp_offset(struct mgcp_endpoint *endp,
+				       struct mgcp_rtp_state *state,
+				       struct mgcp_rtp_end *rtp_end,
+				       struct sockaddr_in *addr,
+				       int16_t delta_seq, uint32_t in_timestamp)
+{
+	int32_t tsdelta = state->packet_duration;
+	int timestamp_offset;
+	uint32_t out_timestamp;
+
+	if (tsdelta == 0) {
+		tsdelta = state->out_stream.last_tsdelta;
+		if (tsdelta != 0) {
+			LOGP(DLMGCP, LOGL_NOTICE,
+			     "A fixed packet duration is not available on 0x%x, "
+			     "using last output timestamp delta instead: %d "
+			     "from %s:%d in %d\n",
+			     ENDPOINT_NUMBER(endp), tsdelta,
+			     inet_ntoa(addr->sin_addr), ntohs(addr->sin_port),
+			     endp->conn_mode);
+		} else {
+			tsdelta = rtp_end->codec.rate * 20 / 1000;
+			LOGP(DLMGCP, LOGL_NOTICE,
+			     "Fixed packet duration and last timestamp delta "
+			     "are not available on 0x%x, "
+			     "using fixed 20ms instead: %d "
+			     "from %s:%d in %d\n",
+			     ENDPOINT_NUMBER(endp), tsdelta,
+			     inet_ntoa(addr->sin_addr), ntohs(addr->sin_port),
+			     endp->conn_mode);
+		}
+	}
+
+	out_timestamp = state->out_stream.last_timestamp + delta_seq * tsdelta;
+	timestamp_offset = out_timestamp - in_timestamp;
+
+	if (state->timestamp_offset != timestamp_offset) {
+		state->timestamp_offset = timestamp_offset;
+
+		LOGP(DLMGCP, LOGL_NOTICE,
+		     "Timestamp offset change on 0x%x SSRC: %u "
+		     "SeqNo delta: %d, TS offset: %d, "
+		     "from %s:%d in %d\n",
+		     ENDPOINT_NUMBER(endp), state->in_stream.ssrc,
+		     delta_seq, state->timestamp_offset,
+		     inet_ntoa(addr->sin_addr), ntohs(addr->sin_port),
+		     endp->conn_mode);
+	}
+
+	return timestamp_offset;
+}
+
+/* Set the timestamp offset according to the packet duration. */
+static int align_rtp_timestamp_offset(struct mgcp_endpoint *endp,
+				      struct mgcp_rtp_state *state,
+				      struct mgcp_rtp_end *rtp_end,
+				      struct sockaddr_in *addr,
+				      uint32_t timestamp)
+{
+	int timestamp_error = 0;
+	int ptime = state->packet_duration;
+
+	/* Align according to: T + Toffs - Tlast = k * Tptime */
+
+	timestamp_error = compute_timestamp_aligment_error(
+		&state->out_stream, ptime,
+		timestamp + state->timestamp_offset);
+
+	if (timestamp_error) {
+		state->timestamp_offset += ptime - timestamp_error;
+
+		LOGP(DLMGCP, LOGL_NOTICE,
+		     "Corrected timestamp alignment error of %d on 0x%x SSRC: %u "
+		     "new TS offset: %d, "
+		     "from %s:%d in %d\n",
+		     timestamp_error,
+		     ENDPOINT_NUMBER(endp), state->in_stream.ssrc,
+		     state->timestamp_offset, inet_ntoa(addr->sin_addr),
+		     ntohs(addr->sin_port), endp->conn_mode);
+	}
+
+	OSMO_ASSERT(compute_timestamp_aligment_error(&state->out_stream, ptime,
+						     timestamp + state->timestamp_offset) == 0);
+
+	return timestamp_error;
+}
+
+int mgcp_rtp_processing_default(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end,
+				char *data, int *len, int buf_size)
+{
+	return 0;
+}
+
+int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp,
+				      struct mgcp_rtp_end *dst_end,
+				      struct mgcp_rtp_end *src_end)
+{
+	return 0;
+}
+
+void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp,
+					  int *payload_type,
+					  const char**audio_name,
+					  const char**fmtp_extra)
+{
+	/* Use the BTS side parameters when passing the SDP data (for
+	 * downlink) to the net peer.
+	 */
+	*payload_type = endp->bts_end.codec.payload_type;
+	*audio_name = endp->bts_end.codec.audio_name;
+	*fmtp_extra = endp->bts_end.fmtp_extra;
+}
+
+
+void mgcp_rtp_annex_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *state,
+			const uint16_t seq, const int32_t transit,
+			const uint32_t ssrc)
+{
+	int32_t d;
+
+	/* initialize or re-initialize */
+	if (!state->stats_initialized || state->stats_ssrc != ssrc) {
+		state->stats_initialized = 1;
+		state->stats_base_seq = seq;
+		state->stats_max_seq = seq - 1;
+		state->stats_ssrc = ssrc;
+		state->stats_jitter = 0;
+		state->stats_transit = transit;
+		state->stats_cycles = 0;
+	} else {
+		uint16_t udelta;
+
+		/*
+		 * The below takes the shape of the validation of
+		 * Appendix A. Check if there is something weird with
+		 * the sequence number, otherwise check for a wrap
+		 * around in the sequence number.
+		 * It can't wrap during the initialization so let's
+		 * skip it here. The Appendix A probably doesn't have
+		 * this issue because of the probation.
+		 */
+		udelta = seq - state->stats_max_seq;
+		if (udelta < RTP_MAX_DROPOUT) {
+			if (seq < state->stats_max_seq)
+				state->stats_cycles += RTP_SEQ_MOD;
+		} else if (udelta <= RTP_SEQ_MOD - RTP_MAX_MISORDER) {
+			LOGP(DLMGCP, LOGL_NOTICE,
+				"RTP seqno made a very large jump on 0x%x delta: %u\n",
+				ENDPOINT_NUMBER(endp), udelta);
+		}
+	}
+
+	/*
+	 * Calculate the jitter between the two packages. The TS should be
+	 * taken closer to the read function. This was taken from the
+	 * Appendix A of RFC 3550. Timestamp and arrival_time have a 1/rate
+	 * resolution.
+	 */
+	d = transit - state->stats_transit;
+	state->stats_transit = transit;
+	if (d < 0)
+		d = -d;
+	state->stats_jitter += d - ((state->stats_jitter + 8) >> 4);
+	state->stats_max_seq = seq;
+}
+
+
+
+/**
+ * The RFC 3550 Appendix A assumes there are multiple sources but
+ * some of the supported endpoints (e.g. the nanoBTS) can only handle
+ * one source and this code will patch RTP header to appear as if there
+ * is only one source.
+ * There is also no probation period for new sources. Every RTP header
+ * we receive will be seen as a switch in streams.
+ */
+void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *state,
+			  struct mgcp_rtp_end *rtp_end, struct sockaddr_in *addr,
+			  char *data, int len)
+{
+	uint32_t arrival_time;
+	int32_t transit;
+	uint16_t seq;
+	uint32_t timestamp, ssrc;
+	struct rtp_hdr *rtp_hdr;
+	int payload = rtp_end->codec.payload_type;
+
+	if (len < sizeof(*rtp_hdr))
+		return;
+
+	rtp_hdr = (struct rtp_hdr *) data;
+	seq = ntohs(rtp_hdr->sequence);
+	timestamp = ntohl(rtp_hdr->timestamp);
+	arrival_time = get_current_ts(rtp_end->codec.rate);
+	ssrc = ntohl(rtp_hdr->ssrc);
+	transit = arrival_time - timestamp;
+
+	mgcp_rtp_annex_count(endp, state, seq, transit, ssrc);
+
+	if (!state->initialized) {
+		state->initialized = 1;
+		state->in_stream.last_seq = seq - 1;
+		state->in_stream.ssrc = state->orig_ssrc = ssrc;
+		state->in_stream.last_tsdelta = 0;
+		state->packet_duration = mgcp_rtp_packet_duration(endp, rtp_end);
+		state->out_stream = state->in_stream;
+		state->out_stream.last_timestamp = timestamp;
+		state->out_stream.ssrc = ssrc - 1; /* force output SSRC change */
+		LOGP(DLMGCP, LOGL_INFO,
+			"Initializing stream on 0x%x SSRC: %u timestamp: %u "
+			"pkt-duration: %d, from %s:%d in %d\n",
+			ENDPOINT_NUMBER(endp), state->in_stream.ssrc,
+			state->seq_offset, state->packet_duration,
+			inet_ntoa(addr->sin_addr), ntohs(addr->sin_port),
+			endp->conn_mode);
+		if (state->packet_duration == 0) {
+			state->packet_duration = rtp_end->codec.rate * 20 / 1000;
+			LOGP(DLMGCP, LOGL_NOTICE,
+			     "Fixed packet duration is not available on 0x%x, "
+			     "using fixed 20ms instead: %d from %s:%d in %d\n",
+			     ENDPOINT_NUMBER(endp), state->packet_duration,
+			     inet_ntoa(addr->sin_addr), ntohs(addr->sin_port),
+			     endp->conn_mode);
+		}
+	} else if (state->in_stream.ssrc != ssrc) {
+		LOGP(DLMGCP, LOGL_NOTICE,
+			"The SSRC changed on 0x%x: %u -> %u  "
+			"from %s:%d in %d\n",
+			ENDPOINT_NUMBER(endp),
+			state->in_stream.ssrc, rtp_hdr->ssrc,
+			inet_ntoa(addr->sin_addr), ntohs(addr->sin_port),
+			endp->conn_mode);
+
+		state->in_stream.ssrc = ssrc;
+		if (rtp_end->force_constant_ssrc) {
+			int16_t delta_seq;
+
+			/* Always increment seqno by 1 */
+			state->seq_offset =
+				(state->out_stream.last_seq + 1) - seq;
+
+			/* Estimate number of packets that would have been sent */
+			delta_seq =
+				(arrival_time - state->in_stream.last_arrival_time
+				 + state->packet_duration/2) /
+				state->packet_duration;
+
+			adjust_rtp_timestamp_offset(endp, state, rtp_end, addr,
+						    delta_seq, timestamp);
+
+			state->patch_ssrc = 1;
+			ssrc = state->orig_ssrc;
+			if (rtp_end->force_constant_ssrc != -1)
+				rtp_end->force_constant_ssrc -= 1;
+
+			LOGP(DLMGCP, LOGL_NOTICE,
+			     "SSRC patching enabled on 0x%x SSRC: %u "
+			     "SeqNo offset: %d, TS offset: %d "
+			     "from %s:%d in %d\n",
+			     ENDPOINT_NUMBER(endp), state->in_stream.ssrc,
+			     state->seq_offset, state->timestamp_offset,
+			     inet_ntoa(addr->sin_addr), ntohs(addr->sin_port),
+			     endp->conn_mode);
+		}
+
+		state->in_stream.last_tsdelta = 0;
+	} else {
+		/* Compute current per-packet timestamp delta */
+		check_rtp_timestamp(endp, state, &state->in_stream, rtp_end, addr,
+				    seq, timestamp, "input",
+				    &state->in_stream.last_tsdelta);
+
+		if (state->patch_ssrc)
+			ssrc = state->orig_ssrc;
+	}
+
+	/* Save before patching */
+	state->in_stream.last_timestamp = timestamp;
+	state->in_stream.last_seq = seq;
+	state->in_stream.last_arrival_time = arrival_time;
+
+	if (rtp_end->force_aligned_timing &&
+	    state->out_stream.ssrc == ssrc && state->packet_duration)
+		/* Align the timestamp offset */
+		align_rtp_timestamp_offset(endp, state, rtp_end, addr, timestamp);
+
+	/* Store the updated SSRC back to the packet */
+	if (state->patch_ssrc)
+		rtp_hdr->ssrc = htonl(ssrc);
+
+	/* Apply the offset and store it back to the packet.
+	 * This won't change anything if the offset is 0, so the conditional is
+	 * omitted. */
+	seq += state->seq_offset;
+	rtp_hdr->sequence = htons(seq);
+	timestamp += state->timestamp_offset;
+	rtp_hdr->timestamp = htonl(timestamp);
+
+	/* Check again, whether the timestamps are still valid */
+	if (state->out_stream.ssrc == ssrc)
+		check_rtp_timestamp(endp, state, &state->out_stream, rtp_end,
+				    addr, seq, timestamp, "output",
+				    &state->out_stream.last_tsdelta);
+
+	/* Save output values */
+	state->out_stream.last_seq = seq;
+	state->out_stream.last_timestamp = timestamp;
+	state->out_stream.ssrc = ssrc;
+
+	if (payload < 0)
+		return;
+
+#if 0
+	DEBUGP(DLMGCP, "Payload hdr payload %u -> endp payload %u\n",
+	       rtp_hdr->payload_type, payload);
+	rtp_hdr->payload_type = payload;
+#endif
+}
+
+/*
+ * The below code is for dispatching. We have a dedicated port for
+ * the data coming from the net and one to discover the BTS.
+ */
+static int forward_data(int fd, struct mgcp_rtp_tap *tap, const char *buf, int len)
+{
+	if (!tap->enabled)
+		return 0;
+
+	return sendto(fd, buf, len, 0,
+		      (struct sockaddr *)&tap->forward, sizeof(tap->forward));
+}
+
+static int mgcp_send_transcoder(struct mgcp_rtp_end *end,
+				struct mgcp_config *cfg, int is_rtp,
+				const char *buf, int len)
+{
+	int rc;
+	int port;
+	struct sockaddr_in addr;
+
+	port = is_rtp ? end->rtp_port : end->rtcp_port;
+
+	addr.sin_family = AF_INET;
+	addr.sin_addr = cfg->transcoder_in;
+	addr.sin_port = port;
+
+	rc = sendto(is_rtp ?
+		end->rtp.fd :
+		end->rtcp.fd, buf, len, 0,
+		(struct sockaddr *) &addr, sizeof(addr));
+
+	if (rc != len)
+		LOGP(DLMGCP, LOGL_ERROR,
+			"Failed to send data to the transcoder: %s\n",
+			strerror(errno));
+
+	return rc;
+}
+
+int mgcp_send(struct mgcp_endpoint *endp, int dest, int is_rtp,
+	      struct sockaddr_in *addr, char *buf, int rc)
+{
+	struct mgcp_trunk_config *tcfg = endp->tcfg;
+	struct mgcp_rtp_end *rtp_end;
+	struct mgcp_rtp_state *rtp_state;
+	int tap_idx;
+
+	LOGP(DLMGCP, LOGL_DEBUG,
+	     "endpoint %x dest %s tcfg->audio_loop %d endp->conn_mode %d (== loopback: %d)\n",
+	     ENDPOINT_NUMBER(endp),
+	     dest == MGCP_DEST_NET? "net" : "bts",
+	     tcfg->audio_loop,
+	     endp->conn_mode,
+	     endp->conn_mode == MGCP_CONN_LOOPBACK);
+
+	/* For loop toggle the destination and then dispatch. */
+	if (tcfg->audio_loop)
+		dest = !dest;
+
+	/* Loop based on the conn_mode, maybe undoing the above */
+	if (endp->conn_mode == MGCP_CONN_LOOPBACK)
+		dest = !dest;
+
+	if (dest == MGCP_DEST_NET) {
+		rtp_end = &endp->net_end;
+		rtp_state = &endp->bts_state;
+		tap_idx = MGCP_TAP_NET_OUT;
+	} else {
+		rtp_end = &endp->bts_end;
+		rtp_state = &endp->net_state;
+		tap_idx = MGCP_TAP_BTS_OUT;
+	}
+	LOGP(DLMGCP, LOGL_DEBUG,
+	     "endpoint %x dest %s net_end %s %d %d bts_end %s %d %d rtp_end %s %d %d\n",
+	     ENDPOINT_NUMBER(endp),
+	     dest == MGCP_DEST_NET? "net" : "bts",
+
+	     inet_ntoa(endp->net_end.addr),
+	     ntohs(endp->net_end.rtp_port),
+	     ntohs(endp->net_end.rtcp_port),
+
+	     inet_ntoa(endp->bts_end.addr),
+	     ntohs(endp->bts_end.rtp_port),
+	     ntohs(endp->bts_end.rtcp_port),
+
+	     inet_ntoa(rtp_end->addr),
+	     ntohs(rtp_end->rtp_port),
+	     ntohs(rtp_end->rtcp_port)
+	    );
+
+	if (!rtp_end->output_enabled) {
+		rtp_end->dropped_packets += 1;
+		LOGP(DLMGCP, LOGL_DEBUG,
+		     "endpoint %x output disabled, drop to %s %s %d %d\n",
+		     ENDPOINT_NUMBER(endp),
+		     dest == MGCP_DEST_NET? "net" : "bts",
+		     inet_ntoa(rtp_end->addr),
+		     ntohs(rtp_end->rtp_port),
+		     ntohs(rtp_end->rtcp_port)
+		    );
+	} else if (is_rtp) {
+		int cont;
+		int nbytes = 0;
+		int len = rc;
+		do {
+			cont = endp->cfg->rtp_processing_cb(endp, rtp_end,
+							buf, &len, RTP_BUF_SIZE);
+			if (cont < 0)
+				break;
+
+			mgcp_patch_and_count(endp, rtp_state, rtp_end, addr, buf, len);
+		LOGP(DLMGCP, LOGL_DEBUG,
+		     "endpoint %x process/send to %s %s %d %d\n",
+		     ENDPOINT_NUMBER(endp),
+		     (dest == MGCP_DEST_NET)? "net" : "bts",
+		     inet_ntoa(rtp_end->addr),
+		     ntohs(rtp_end->rtp_port),
+		     ntohs(rtp_end->rtcp_port)
+		    );
+			forward_data(rtp_end->rtp.fd, &endp->taps[tap_idx],
+				     buf, len);
+
+			/* FIXME: HACK HACK HACK. See OS#2459.
+			 * The ip.access nano3G needs the first RTP payload's first two bytes to read hex
+			 * 'e400', or it will reject the RAB assignment. It seems to not harm other femto
+			 * cells (as long as we patch only the first RTP payload in each stream).
+			 */
+			if (tap_idx == MGCP_TAP_BTS_OUT
+			    && !rtp_state->patched_first_rtp_payload) {
+				uint8_t *data = (uint8_t*)&buf[12];
+				data[0] = 0xe4;
+				data[1] = 0x00;
+				rtp_state->patched_first_rtp_payload = true;
+			}
+
+			rc = mgcp_udp_send(rtp_end->rtp.fd,
+					   &rtp_end->addr,
+					   rtp_end->rtp_port, buf, len);
+
+			if (rc <= 0)
+				return rc;
+			nbytes += rc;
+			len = cont;
+		} while (len > 0);
+		return nbytes;
+	} else if (!tcfg->omit_rtcp) {
+		LOGP(DLMGCP, LOGL_DEBUG,
+		     "endpoint %x send to %s %s %d %d\n",
+		     ENDPOINT_NUMBER(endp),
+		     dest == MGCP_DEST_NET? "net" : "bts",
+		     inet_ntoa(rtp_end->addr),
+		     ntohs(rtp_end->rtp_port),
+		     ntohs(rtp_end->rtcp_port)
+		    );
+
+		return mgcp_udp_send(rtp_end->rtcp.fd,
+				     &rtp_end->addr,
+				     rtp_end->rtcp_port, buf, rc);
+	}
+
+	return 0;
+}
+
+static int receive_from(struct mgcp_endpoint *endp, int fd, struct sockaddr_in *addr,
+			char *buf, int bufsize)
+{
+	int rc;
+	socklen_t slen = sizeof(*addr);
+
+	rc = recvfrom(fd, buf, bufsize, 0,
+			    (struct sockaddr *) addr, &slen);
+	if (rc < 0) {
+		LOGP(DLMGCP, LOGL_ERROR, "Failed to receive message on: 0x%x errno: %d/%s\n",
+			ENDPOINT_NUMBER(endp), errno, strerror(errno));
+		return -1;
+	}
+
+	/* do not forward aynthing... maybe there is a packet from the bts */
+	if (!endp->allocated)
+		return -1;
+
+	#warning "Slight spec violation. With connection mode recvonly we should attempt to forward."
+
+	return rc;
+}
+
+static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
+{
+	char buf[RTP_BUF_SIZE];
+	struct sockaddr_in addr;
+	struct mgcp_endpoint *endp;
+	int rc, proto;
+
+	endp = (struct mgcp_endpoint *) fd->data;
+
+	rc = receive_from(endp, fd->fd, &addr, buf, sizeof(buf));
+	if (rc <= 0)
+		return -1;
+
+	LOGP(DLMGCP, LOGL_DEBUG,
+	     "endpoint %x",
+	     ENDPOINT_NUMBER(endp));
+	LOGPC(DLMGCP, LOGL_DEBUG,
+	      " from net %s %d",
+	      inet_ntoa(addr.sin_addr),
+	      ntohs(addr.sin_port));
+	LOGPC(DLMGCP, LOGL_DEBUG,
+	      " net_end %s %d %d",
+	      inet_ntoa(endp->net_end.addr),
+	      ntohs(endp->net_end.rtp_port),
+	      ntohs(endp->net_end.rtcp_port));
+	LOGPC(DLMGCP, LOGL_DEBUG,
+	      " bts_end %s %d %d\n",
+	      inet_ntoa(endp->bts_end.addr),
+	      ntohs(endp->bts_end.rtp_port),
+	      ntohs(endp->bts_end.rtcp_port)
+	     );
+
+	if (memcmp(&addr.sin_addr, &endp->net_end.addr, sizeof(addr.sin_addr)) != 0) {
+		LOGP(DLMGCP, LOGL_ERROR,
+			"rtp_data_net: Endpoint 0x%x data from wrong address %s vs. ",
+			ENDPOINT_NUMBER(endp), inet_ntoa(addr.sin_addr));
+		LOGPC(DLMGCP, LOGL_ERROR,
+			"%s\n", inet_ntoa(endp->net_end.addr));
+		return -1;
+	}
+
+	switch(endp->type) {
+	case MGCP_RTP_DEFAULT:
+	case MGCP_RTP_TRANSCODED:
+		if (endp->net_end.rtp_port != addr.sin_port &&
+		    endp->net_end.rtcp_port != addr.sin_port) {
+			LOGP(DLMGCP, LOGL_ERROR,
+				"rtp_data_net: Data from wrong source port %d on 0x%x\n",
+				ntohs(addr.sin_port), ENDPOINT_NUMBER(endp));
+			return -1;
+		}
+		break;
+	case MGCP_OSMUX_BSC:
+	case MGCP_OSMUX_BSC_NAT:
+		break;
+	}
+
+	LOGP(DLMGCP, LOGL_DEBUG,
+	     "rtp_data_net: Endpoint %x data from %s %d\n",
+	     ENDPOINT_NUMBER(endp),
+	     inet_ntoa(addr.sin_addr),
+	     ntohs(addr.sin_port));
+
+	/* throw away the dummy message */
+	if (rc == 1 && buf[0] == MGCP_DUMMY_LOAD) {
+		LOGP(DLMGCP, LOGL_NOTICE, "Filtered dummy from network on 0x%x\n",
+			ENDPOINT_NUMBER(endp));
+		return 0;
+	}
+
+	proto = fd == &endp->net_end.rtp ? MGCP_PROTO_RTP : MGCP_PROTO_RTCP;
+	endp->net_end.packets += 1;
+	endp->net_end.octets += rc;
+
+	forward_data(fd->fd, &endp->taps[MGCP_TAP_NET_IN], buf, rc);
+
+	switch (endp->type) {
+	case MGCP_RTP_DEFAULT:
+		return mgcp_send(endp, MGCP_DEST_BTS, proto == MGCP_PROTO_RTP,
+				 &addr, buf, rc);
+	case MGCP_RTP_TRANSCODED:
+		return mgcp_send_transcoder(&endp->trans_net, endp->cfg,
+					    proto == MGCP_PROTO_RTP, buf, rc);
+	case MGCP_OSMUX_BSC_NAT:
+		return osmux_xfrm_to_osmux(MGCP_DEST_BTS, buf, rc, endp);
+	case MGCP_OSMUX_BSC:	/* Should not happen */
+		break;
+	}
+
+	LOGP(DLMGCP, LOGL_ERROR, "Bad MGCP type %u on endpoint %u\n",
+	     endp->type, ENDPOINT_NUMBER(endp));
+	return 0;
+}
+
+static void discover_bts(struct mgcp_endpoint *endp, int proto, struct sockaddr_in *addr)
+{
+	struct mgcp_config *cfg = endp->cfg;
+
+	if (proto == MGCP_PROTO_RTP && endp->bts_end.rtp_port == 0) {
+		if (!cfg->bts_ip ||
+		    memcmp(&addr->sin_addr,
+			   &cfg->bts_in, sizeof(cfg->bts_in)) == 0 ||
+		    memcmp(&addr->sin_addr,
+			   &endp->bts_end.addr, sizeof(endp->bts_end.addr)) == 0) {
+
+			endp->bts_end.rtp_port = addr->sin_port;
+			endp->bts_end.addr = addr->sin_addr;
+
+			LOGP(DLMGCP, LOGL_NOTICE,
+				"Found BTS for endpoint: 0x%x on port: %d/%d of %s\n",
+				ENDPOINT_NUMBER(endp), ntohs(endp->bts_end.rtp_port),
+				ntohs(endp->bts_end.rtcp_port), inet_ntoa(addr->sin_addr));
+		}
+	} else if (proto == MGCP_PROTO_RTCP && endp->bts_end.rtcp_port == 0) {
+		if (memcmp(&endp->bts_end.addr, &addr->sin_addr,
+				sizeof(endp->bts_end.addr)) == 0) {
+			endp->bts_end.rtcp_port = addr->sin_port;
+		}
+	}
+}
+
+static int rtp_data_bts(struct osmo_fd *fd, unsigned int what)
+{
+	char buf[RTP_BUF_SIZE];
+	struct sockaddr_in addr;
+	struct mgcp_endpoint *endp;
+	int rc, proto;
+
+	endp = (struct mgcp_endpoint *) fd->data;
+
+	rc = receive_from(endp, fd->fd, &addr, buf, sizeof(buf));
+	if (rc <= 0)
+		return -1;
+
+	proto = fd == &endp->bts_end.rtp ? MGCP_PROTO_RTP : MGCP_PROTO_RTCP;
+
+	/* We have no idea who called us, maybe it is the BTS. */
+	/* it was the BTS... */
+	discover_bts(endp, proto, &addr);
+
+	if (memcmp(&endp->bts_end.addr, &addr.sin_addr, sizeof(addr.sin_addr)) != 0) {
+		LOGP(DLMGCP, LOGL_ERROR,
+			"rtp_data_bts: Data from wrong bts %s on 0x%x\n",
+			inet_ntoa(addr.sin_addr), ENDPOINT_NUMBER(endp));
+		return -1;
+	}
+
+	if (endp->bts_end.rtp_port != addr.sin_port &&
+	    endp->bts_end.rtcp_port != addr.sin_port) {
+		LOGP(DLMGCP, LOGL_ERROR,
+			"rtp_data_bts: ata from wrong bts source port %d on 0x%x\n",
+			ntohs(addr.sin_port), ENDPOINT_NUMBER(endp));
+		return -1;
+	}
+
+	LOGP(DLMGCP, LOGL_DEBUG,
+	     "rtp_data_bts: Endpoint %x data from %s %d\n",
+	     ENDPOINT_NUMBER(endp),
+	     inet_ntoa(addr.sin_addr),
+	     ntohs(addr.sin_port));
+
+	/* throw away the dummy message */
+	if (rc == 1 && buf[0] == MGCP_DUMMY_LOAD) {
+		LOGP(DLMGCP, LOGL_NOTICE, "Filtered dummy from bts on 0x%x\n",
+			ENDPOINT_NUMBER(endp));
+		return 0;
+	}
+
+	/* do this before the loop handling */
+	endp->bts_end.packets += 1;
+	endp->bts_end.octets += rc;
+
+	forward_data(fd->fd, &endp->taps[MGCP_TAP_BTS_IN], buf, rc);
+
+	switch (endp->type) {
+	case MGCP_RTP_DEFAULT:
+		LOGP(DLMGCP, LOGL_DEBUG,
+		     "rtp_data_bts: Endpoint %x MGCP_RTP_DEFAULT\n",
+		     ENDPOINT_NUMBER(endp));
+		return mgcp_send(endp, MGCP_DEST_NET, proto == MGCP_PROTO_RTP,
+				 &addr, buf, rc);
+	case MGCP_RTP_TRANSCODED:
+		return mgcp_send_transcoder(&endp->trans_bts, endp->cfg,
+					    proto == MGCP_PROTO_RTP, buf, rc);
+	case MGCP_OSMUX_BSC:
+		/* OSMUX translation: BTS -> BSC */
+		return osmux_xfrm_to_osmux(MGCP_DEST_NET, buf, rc, endp);
+	case MGCP_OSMUX_BSC_NAT:
+		break;	/* Should not happen */
+	}
+
+	LOGP(DLMGCP, LOGL_ERROR, "Bad MGCP type %u on endpoint %u\n",
+	     endp->type, ENDPOINT_NUMBER(endp));
+	return 0;
+}
+
+static int rtp_data_transcoder(struct mgcp_rtp_end *end, struct mgcp_endpoint *_endp,
+			      int dest, struct osmo_fd *fd)
+{
+	char buf[RTP_BUF_SIZE];
+	struct sockaddr_in addr;
+	struct mgcp_config *cfg;
+	int rc, proto;
+
+	cfg = _endp->cfg;
+	rc = receive_from(_endp, fd->fd, &addr, buf, sizeof(buf));
+	if (rc <= 0)
+		return -1;
+
+	proto = fd == &end->rtp ? MGCP_PROTO_RTP : MGCP_PROTO_RTCP;
+
+	if (memcmp(&addr.sin_addr, &cfg->transcoder_in, sizeof(addr.sin_addr)) != 0) {
+		LOGP(DLMGCP, LOGL_ERROR,
+			"Data not coming from transcoder dest: %d %s on 0x%x\n",
+			dest, inet_ntoa(addr.sin_addr), ENDPOINT_NUMBER(_endp));
+		return -1;
+	}
+
+	if (end->rtp_port != addr.sin_port &&
+	    end->rtcp_port != addr.sin_port) {
+		LOGP(DLMGCP, LOGL_ERROR,
+			"Data from wrong transcoder dest %d source port %d on 0x%x\n",
+			dest, ntohs(addr.sin_port), ENDPOINT_NUMBER(_endp));
+		return -1;
+	}
+
+	/* throw away the dummy message */
+	if (rc == 1 && buf[0] == MGCP_DUMMY_LOAD) {
+		LOGP(DLMGCP, LOGL_NOTICE, "Filtered dummy from transcoder dest %d on 0x%x\n",
+			dest, ENDPOINT_NUMBER(_endp));
+		return 0;
+	}
+
+	end->packets += 1;
+	return mgcp_send(_endp, dest, proto == MGCP_PROTO_RTP, &addr, buf, rc);
+}
+
+static int rtp_data_trans_net(struct osmo_fd *fd, unsigned int what)
+{
+	struct mgcp_endpoint *endp;
+	endp = (struct mgcp_endpoint *) fd->data;
+
+	return rtp_data_transcoder(&endp->trans_net, endp, MGCP_DEST_NET, fd);
+}
+
+static int rtp_data_trans_bts(struct osmo_fd *fd, unsigned int what)
+{
+	struct mgcp_endpoint *endp;
+	endp = (struct mgcp_endpoint *) fd->data;
+
+	return rtp_data_transcoder(&endp->trans_bts, endp, MGCP_DEST_BTS, fd);
+}
+
+int mgcp_create_bind(const char *source_addr, struct osmo_fd *fd, int port)
+{
+	struct sockaddr_in addr;
+	int on = 1;
+
+	fd->fd = socket(AF_INET, SOCK_DGRAM, 0);
+	if (fd->fd < 0) {
+		LOGP(DLMGCP, LOGL_ERROR, "Failed to create UDP port.\n");
+		return -1;
+	}
+
+	setsockopt(fd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+	memset(&addr, 0, sizeof(addr));
+	addr.sin_family = AF_INET;
+	addr.sin_port = htons(port);
+	inet_aton(source_addr, &addr.sin_addr);
+
+	if (bind(fd->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		close(fd->fd);
+		fd->fd = -1;
+		return -1;
+	}
+
+	return 0;
+}
+
+int mgcp_set_ip_tos(int fd, int tos)
+{
+	int ret;
+	ret = setsockopt(fd, IPPROTO_IP, IP_TOS,
+			 &tos, sizeof(tos));
+	return ret != 0;
+}
+
+static int bind_rtp(struct mgcp_config *cfg, const char *source_addr,
+			struct mgcp_rtp_end *rtp_end, int endpno)
+{
+	if (mgcp_create_bind(source_addr, &rtp_end->rtp,
+			     rtp_end->local_port) != 0) {
+		LOGP(DLMGCP, LOGL_ERROR, "Failed to create RTP port: %s:%d on 0x%x\n",
+		       source_addr, rtp_end->local_port, endpno);
+		goto cleanup0;
+	}
+
+	if (mgcp_create_bind(source_addr, &rtp_end->rtcp,
+			     rtp_end->local_port + 1) != 0) {
+		LOGP(DLMGCP, LOGL_ERROR, "Failed to create RTCP port: %s:%d on 0x%x\n",
+		       source_addr, rtp_end->local_port + 1, endpno);
+		goto cleanup1;
+	}
+
+	mgcp_set_ip_tos(rtp_end->rtp.fd, cfg->endp_dscp);
+	mgcp_set_ip_tos(rtp_end->rtcp.fd, cfg->endp_dscp);
+
+	rtp_end->rtp.when = BSC_FD_READ;
+	if (osmo_fd_register(&rtp_end->rtp) != 0) {
+		LOGP(DLMGCP, LOGL_ERROR, "Failed to register RTP port %d on 0x%x\n",
+			rtp_end->local_port, endpno);
+		goto cleanup2;
+	}
+
+	rtp_end->rtcp.when = BSC_FD_READ;
+	if (osmo_fd_register(&rtp_end->rtcp) != 0) {
+		LOGP(DLMGCP, LOGL_ERROR, "Failed to register RTCP port %d on 0x%x\n",
+			rtp_end->local_port + 1, endpno);
+		goto cleanup3;
+	}
+
+	return 0;
+
+cleanup3:
+	osmo_fd_unregister(&rtp_end->rtp);
+cleanup2:
+	close(rtp_end->rtcp.fd);
+	rtp_end->rtcp.fd = -1;
+cleanup1:
+	close(rtp_end->rtp.fd);
+	rtp_end->rtp.fd = -1;
+cleanup0:
+	return -1;
+}
+
+static int int_bind(const char *port,
+		    struct mgcp_rtp_end *end, int (*cb)(struct osmo_fd *, unsigned),
+		    struct mgcp_endpoint *_endp,
+		    const char *source_addr, int rtp_port)
+{
+	if (end->rtp.fd != -1 || end->rtcp.fd != -1) {
+		LOGP(DLMGCP, LOGL_ERROR, "Previous %s was still bound on %d\n",
+			port, ENDPOINT_NUMBER(_endp));
+		mgcp_free_rtp_port(end);
+	}
+
+	end->local_port = rtp_port;
+	end->rtp.cb = cb;
+	end->rtp.data = _endp;
+	end->rtcp.data = _endp;
+	end->rtcp.cb = cb;
+	return bind_rtp(_endp->cfg, source_addr, end, ENDPOINT_NUMBER(_endp));
+}
+
+int mgcp_bind_bts_rtp_port(struct mgcp_endpoint *endp, int rtp_port)
+{
+	return int_bind("bts-port", &endp->bts_end,
+			rtp_data_bts, endp,
+			mgcp_bts_src_addr(endp), rtp_port);
+}
+
+int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port)
+{
+	return int_bind("net-port", &endp->net_end,
+			rtp_data_net, endp,
+			mgcp_net_src_addr(endp), rtp_port);
+}
+
+int mgcp_bind_trans_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port)
+{
+	return int_bind("trans-net", &endp->trans_net,
+			rtp_data_trans_net, endp,
+			endp->cfg->source_addr, rtp_port);
+}
+
+int mgcp_bind_trans_bts_rtp_port(struct mgcp_endpoint *endp, int rtp_port)
+{
+	return int_bind("trans-bts", &endp->trans_bts,
+			rtp_data_trans_bts, endp,
+			endp->cfg->source_addr, rtp_port);
+}
+
+int mgcp_free_rtp_port(struct mgcp_rtp_end *end)
+{
+	if (end->rtp.fd != -1) {
+		close(end->rtp.fd);
+		end->rtp.fd = -1;
+		osmo_fd_unregister(&end->rtp);
+	}
+
+	if (end->rtcp.fd != -1) {
+		close(end->rtcp.fd);
+		end->rtcp.fd = -1;
+		osmo_fd_unregister(&end->rtcp);
+	}
+
+	return 0;
+}
+
+
+void mgcp_state_calc_loss(struct mgcp_rtp_state *state,
+			struct mgcp_rtp_end *end, uint32_t *expected,
+			int *loss)
+{
+	*expected = state->stats_cycles + state->stats_max_seq;
+	*expected = *expected - state->stats_base_seq + 1;
+
+	if (!state->stats_initialized) {
+		*expected = 0;
+		*loss = 0;
+		return;
+	}
+
+	/*
+	 * Make sure the sign is correct and use the biggest
+	 * positive/negative number that fits.
+	 */
+	*loss = *expected - end->packets;
+	if (*expected < end->packets) {
+		if (*loss > 0)
+			*loss = INT_MIN;
+	} else {
+		if (*loss < 0)
+			*loss = INT_MAX;
+	}
+}
+
+uint32_t mgcp_state_calc_jitter(struct mgcp_rtp_state *state)
+{
+	if (!state->stats_initialized)
+		return 0;
+	return state->stats_jitter >> 4;
+}
diff --git a/src/libosmo-mgcp/mgcp_osmux.c b/src/libosmo-mgcp/mgcp_osmux.c
new file mode 100644
index 0000000..743d3f9
--- /dev/null
+++ b/src/libosmo-mgcp/mgcp_osmux.c
@@ -0,0 +1,583 @@
+/*
+ * (C) 2012-2013 by Pablo Neira Ayuso <pablo@gnumonks.org>
+ * (C) 2012-2013 by On Waves ehf <http://www.on-waves.com>
+ * All rights not specifically granted under this license are 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.
+ */
+
+#include <stdio.h> /* for printf */
+#include <string.h> /* for memcpy */
+#include <stdlib.h> /* for abs */
+#include <inttypes.h> /* for PRIu64 */
+#include <netinet/in.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/talloc.h>
+
+#include <osmocom/netif/osmux.h>
+#include <osmocom/netif/rtp.h>
+
+#include <osmocom/legacy_mgcp/mgcp.h>
+#include <osmocom/legacy_mgcp/mgcp_internal.h>
+#include <osmocom/legacy_mgcp/osmux.h>
+
+static struct osmo_fd osmux_fd;
+
+static LLIST_HEAD(osmux_handle_list);
+
+struct osmux_handle {
+	struct llist_head head;
+	struct osmux_in_handle *in;
+	struct in_addr rem_addr;
+	int rem_port;
+	int refcnt;
+};
+
+static void *osmux;
+
+static void osmux_deliver(struct msgb *batch_msg, void *data)
+{
+	struct osmux_handle *handle = data;
+	struct sockaddr_in out = {
+		.sin_family = AF_INET,
+		.sin_port = handle->rem_port,
+	};
+
+	memcpy(&out.sin_addr, &handle->rem_addr, sizeof(handle->rem_addr));
+	sendto(osmux_fd.fd, batch_msg->data, batch_msg->len, 0,
+		(struct sockaddr *)&out, sizeof(out));
+	msgb_free(batch_msg);
+}
+
+static struct osmux_handle *
+osmux_handle_find_get(struct in_addr *addr, int rem_port)
+{
+	struct osmux_handle *h;
+
+	/* Lookup for existing OSMUX handle for this destination address. */
+	llist_for_each_entry(h, &osmux_handle_list, head) {
+		if (memcmp(&h->rem_addr, addr, sizeof(struct in_addr)) == 0 &&
+		    h->rem_port == rem_port) {
+			LOGP(DLMGCP, LOGL_DEBUG, "using existing OSMUX handle "
+						"for addr=%s:%d\n",
+				inet_ntoa(*addr), ntohs(rem_port));
+			h->refcnt++;
+			return h;
+		}
+	}
+
+	return NULL;
+}
+
+static void osmux_handle_put(struct osmux_in_handle *in)
+{
+	struct osmux_handle *h;
+
+	/* Lookup for existing OSMUX handle for this destination address. */
+	llist_for_each_entry(h, &osmux_handle_list, head) {
+		if (h->in == in) {
+			if (--h->refcnt == 0) {
+				LOGP(DLMGCP, LOGL_INFO,
+				     "Releasing unused osmux handle for %s:%d\n",
+				     inet_ntoa(h->rem_addr),
+				     ntohs(h->rem_port));
+				LOGP(DLMGCP, LOGL_INFO, "Stats: "
+				     "input RTP msgs: %u bytes: %"PRIu64" "
+				     "output osmux msgs: %u bytes: %"PRIu64"\n",
+				     in->stats.input_rtp_msgs,
+				     in->stats.input_rtp_bytes,
+				     in->stats.output_osmux_msgs,
+				     in->stats.output_osmux_bytes);
+				llist_del(&h->head);
+				osmux_xfrm_input_fini(h->in);
+				talloc_free(h);
+			}
+			return;
+		}
+	}
+	LOGP(DLMGCP, LOGL_ERROR, "cannot find Osmux input handle %p\n", in);
+}
+
+static struct osmux_handle *
+osmux_handle_alloc(struct mgcp_config *cfg, struct in_addr *addr, int rem_port)
+{
+	struct osmux_handle *h;
+
+	h = talloc_zero(osmux, struct osmux_handle);
+	if (!h)
+		return NULL;
+	h->rem_addr = *addr;
+	h->rem_port = rem_port;
+	h->refcnt++;
+
+	h->in = talloc_zero(h, struct osmux_in_handle);
+	if (!h->in) {
+		talloc_free(h);
+		return NULL;
+	}
+
+	h->in->osmux_seq = 0; /* sequence number to start OSmux message from */
+	h->in->batch_factor = cfg->osmux_batch;
+	/* If batch size is zero, the library defaults to 1470 bytes. */
+	h->in->batch_size = cfg->osmux_batch_size;
+	h->in->deliver = osmux_deliver;
+	osmux_xfrm_input_init(h->in);
+	h->in->data = h;
+
+	llist_add(&h->head, &osmux_handle_list);
+
+	LOGP(DLMGCP, LOGL_DEBUG, "created new OSMUX handle for addr=%s:%d\n",
+		inet_ntoa(*addr), ntohs(rem_port));
+
+	return h;
+}
+
+static struct osmux_in_handle *
+osmux_handle_lookup(struct mgcp_config *cfg, struct in_addr *addr, int rem_port)
+{
+	struct osmux_handle *h;
+
+	h = osmux_handle_find_get(addr, rem_port);
+	if (h != NULL)
+		return h->in;
+
+	h = osmux_handle_alloc(cfg, addr, rem_port);
+	if (h == NULL)
+		return NULL;
+
+	return h->in;
+}
+
+int osmux_xfrm_to_osmux(int type, char *buf, int rc, struct mgcp_endpoint *endp)
+{
+	int ret;
+	struct msgb *msg;
+
+	msg = msgb_alloc(4096, "RTP");
+	if (!msg)
+		return 0;
+
+	memcpy(msg->data, buf, rc);
+	msgb_put(msg, rc);
+
+	while ((ret = osmux_xfrm_input(endp->osmux.in, msg, endp->osmux.cid)) > 0) {
+		/* batch full, build and deliver it */
+		osmux_xfrm_input_deliver(endp->osmux.in);
+	}
+	return 0;
+}
+
+static struct mgcp_endpoint *
+endpoint_lookup(struct mgcp_config *cfg, int cid,
+		struct in_addr *from_addr, int type)
+{
+	struct mgcp_endpoint *tmp = NULL;
+	int i;
+
+	/* Lookup for the endpoint that corresponds to this port */
+	for (i=0; i<cfg->trunk.number_endpoints; i++) {
+		struct in_addr *this;
+
+		tmp = &cfg->trunk.endpoints[i];
+
+		if (!tmp->allocated)
+			continue;
+
+		switch(type) {
+		case MGCP_DEST_NET:
+			this = &tmp->net_end.addr;
+			break;
+		case MGCP_DEST_BTS:
+			this = &tmp->bts_end.addr;
+			break;
+		default:
+			/* Should not ever happen */
+			LOGP(DLMGCP, LOGL_ERROR, "Bad type %d. Fix your code.\n", type);
+			return NULL;
+		}
+
+		if (tmp->osmux.cid == cid && this->s_addr == from_addr->s_addr)
+			return tmp;
+	}
+
+	LOGP(DLMGCP, LOGL_ERROR, "Cannot find endpoint with cid=%d\n", cid);
+
+	return NULL;
+}
+
+static void scheduled_tx_net_cb(struct msgb *msg, void *data)
+{
+	struct mgcp_endpoint *endp = data;
+	struct sockaddr_in addr = {
+		.sin_addr = endp->net_end.addr,
+		.sin_port = endp->net_end.rtp_port,
+	};
+
+	endp->bts_end.octets += msg->len;
+	endp->bts_end.packets++;
+
+	mgcp_send(endp, MGCP_DEST_NET, 1, &addr, (char *)msg->data, msg->len);
+	msgb_free(msg);
+}
+
+static void scheduled_tx_bts_cb(struct msgb *msg, void *data)
+{
+	struct mgcp_endpoint *endp = data;
+	struct sockaddr_in addr = {
+		.sin_addr = endp->bts_end.addr,
+		.sin_port = endp->bts_end.rtp_port,
+	};
+
+	endp->net_end.octets += msg->len;
+	endp->net_end.packets++;
+
+	mgcp_send(endp, MGCP_DEST_BTS, 1, &addr, (char *)msg->data, msg->len);
+	msgb_free(msg);
+}
+
+static struct msgb *osmux_recv(struct osmo_fd *ofd, struct sockaddr_in *addr)
+{
+	struct msgb *msg;
+	socklen_t slen = sizeof(*addr);
+	int ret;
+
+	msg = msgb_alloc(4096, "OSMUX");
+	if (!msg) {
+		LOGP(DLMGCP, LOGL_ERROR, "cannot allocate message\n");
+		return NULL;
+	}
+	ret = recvfrom(ofd->fd, msg->data, msg->data_len, 0,
+			(struct sockaddr *)addr, &slen);
+	if (ret <= 0) {
+		msgb_free(msg);
+		LOGP(DLMGCP, LOGL_ERROR, "cannot receive message\n");
+		return NULL;
+	}
+	msgb_put(msg, ret);
+
+	return msg;
+}
+
+#define osmux_chunk_length(msg, rem) (rem - msg->len);
+
+int osmux_read_from_bsc_nat_cb(struct osmo_fd *ofd, unsigned int what)
+{
+	struct msgb *msg;
+	struct osmux_hdr *osmuxh;
+	struct llist_head list;
+	struct sockaddr_in addr;
+	struct mgcp_config *cfg = ofd->data;
+	uint32_t rem;
+
+	msg = osmux_recv(ofd, &addr);
+	if (!msg)
+		return -1;
+
+	/* not any further processing dummy messages */
+	if (msg->data[0] == MGCP_DUMMY_LOAD)
+		goto out;
+
+	rem = msg->len;
+	while((osmuxh = osmux_xfrm_output_pull(msg)) != NULL) {
+		struct mgcp_endpoint *endp;
+
+		/* Yes, we use MGCP_DEST_NET to locate the origin */
+		endp = endpoint_lookup(cfg, osmuxh->circuit_id,
+				       &addr.sin_addr, MGCP_DEST_NET);
+		if (!endp) {
+			LOGP(DLMGCP, LOGL_ERROR,
+			     "Cannot find an endpoint for circuit_id=%d\n",
+			     osmuxh->circuit_id);
+			goto out;
+		}
+		endp->osmux.stats.octets += osmux_chunk_length(msg, rem);
+		endp->osmux.stats.chunks++;
+		rem = msg->len;
+
+		osmux_xfrm_output(osmuxh, &endp->osmux.out, &list);
+		osmux_tx_sched(&list, scheduled_tx_bts_cb, endp);
+	}
+out:
+	msgb_free(msg);
+	return 0;
+}
+
+/* This is called from the bsc-nat */
+static int osmux_handle_dummy(struct mgcp_config *cfg, struct sockaddr_in *addr,
+			      struct msgb *msg)
+{
+	struct mgcp_endpoint *endp;
+	uint8_t osmux_cid;
+
+	if (msg->len < 1 + sizeof(osmux_cid)) {
+		LOGP(DLMGCP, LOGL_ERROR,
+		     "Discarding truncated Osmux dummy load\n");
+		goto out;
+	}
+
+	LOGP(DLMGCP, LOGL_DEBUG, "Received Osmux dummy load from %s\n",
+	     inet_ntoa(addr->sin_addr));
+
+	if (!cfg->osmux) {
+		LOGP(DLMGCP, LOGL_ERROR,
+		     "bsc wants to use Osmux but bsc-nat did not request it\n");
+		goto out;
+	}
+
+	/* extract the osmux CID from the dummy message */
+	memcpy(&osmux_cid, &msg->data[1], sizeof(osmux_cid));
+
+	endp = endpoint_lookup(cfg, osmux_cid, &addr->sin_addr, MGCP_DEST_BTS);
+	if (!endp) {
+		LOGP(DLMGCP, LOGL_ERROR,
+		     "Cannot find endpoint for Osmux CID %d\n", osmux_cid);
+		goto out;
+	}
+
+	if (endp->osmux.state == OSMUX_STATE_ENABLED)
+		goto out;
+
+	if (osmux_enable_endpoint(endp, &addr->sin_addr, addr->sin_port) < 0 ) {
+		LOGP(DLMGCP, LOGL_ERROR,
+		     "Could not enable osmux in endpoint %d\n",
+		     ENDPOINT_NUMBER(endp));
+		goto out;
+	}
+
+	LOGP(DLMGCP, LOGL_INFO, "Enabling osmux in endpoint %d for %s:%u\n",
+	     ENDPOINT_NUMBER(endp), inet_ntoa(addr->sin_addr),
+	     ntohs(addr->sin_port));
+out:
+	msgb_free(msg);
+	return 0;
+}
+
+int osmux_read_from_bsc_cb(struct osmo_fd *ofd, unsigned int what)
+{
+	struct msgb *msg;
+	struct osmux_hdr *osmuxh;
+	struct llist_head list;
+	struct sockaddr_in addr;
+	struct mgcp_config *cfg = ofd->data;
+	uint32_t rem;
+
+	msg = osmux_recv(ofd, &addr);
+	if (!msg)
+		return -1;
+
+	/* not any further processing dummy messages */
+	if (msg->data[0] == MGCP_DUMMY_LOAD)
+		return osmux_handle_dummy(cfg, &addr, msg);
+
+	rem = msg->len;
+	while((osmuxh = osmux_xfrm_output_pull(msg)) != NULL) {
+		struct mgcp_endpoint *endp;
+
+		/* Yes, we use MGCP_DEST_BTS to locate the origin */
+		endp = endpoint_lookup(cfg, osmuxh->circuit_id,
+				       &addr.sin_addr, MGCP_DEST_BTS);
+		if (!endp) {
+			LOGP(DLMGCP, LOGL_ERROR,
+			     "Cannot find an endpoint for circuit_id=%d\n",
+			     osmuxh->circuit_id);
+			goto out;
+		}
+		endp->osmux.stats.octets += osmux_chunk_length(msg, rem);
+		endp->osmux.stats.chunks++;
+		rem = msg->len;
+
+		osmux_xfrm_output(osmuxh, &endp->osmux.out, &list);
+		osmux_tx_sched(&list, scheduled_tx_net_cb, endp);
+	}
+out:
+	msgb_free(msg);
+	return 0;
+}
+
+int osmux_init(int role, struct mgcp_config *cfg)
+{
+	int ret;
+
+	switch(role) {
+	case OSMUX_ROLE_BSC:
+		osmux_fd.cb = osmux_read_from_bsc_nat_cb;
+		break;
+	case OSMUX_ROLE_BSC_NAT:
+		osmux_fd.cb = osmux_read_from_bsc_cb;
+		break;
+	default:
+		LOGP(DLMGCP, LOGL_ERROR, "wrong role for OSMUX\n");
+		return -1;
+	}
+	osmux_fd.data = cfg;
+
+	ret = mgcp_create_bind(cfg->osmux_addr, &osmux_fd, cfg->osmux_port);
+	if (ret < 0) {
+		LOGP(DLMGCP, LOGL_ERROR, "cannot bind OSMUX socket\n");
+		return ret;
+	}
+	mgcp_set_ip_tos(osmux_fd.fd, cfg->endp_dscp);
+	osmux_fd.when |= BSC_FD_READ;
+
+	ret = osmo_fd_register(&osmux_fd);
+	if (ret < 0) {
+		LOGP(DLMGCP, LOGL_ERROR, "cannot register OSMUX socket\n");
+		return ret;
+	}
+	cfg->osmux_init = 1;
+
+	return 0;
+}
+
+int osmux_enable_endpoint(struct mgcp_endpoint *endp, struct in_addr *addr, uint16_t port)
+{
+	/* If osmux is enabled, initialize the output handler. This handler is
+	 * used to reconstruct the RTP flow from osmux. The RTP SSRC is
+	 * allocated based on the circuit ID (endp->osmux.cid), which is unique
+	 * in the local scope to the BSC/BSC-NAT. We use it to divide the RTP
+	 * SSRC space (2^32) by the 256 possible circuit IDs, then randomly
+	 * select one value from that window. Thus, we have no chance to have
+	 * overlapping RTP SSRC traveling to the BTSes behind the BSC,
+	 * similarly, for flows traveling to the MSC.
+	 */
+	static const uint32_t rtp_ssrc_winlen = UINT32_MAX / 256;
+
+	if (endp->osmux.state == OSMUX_STATE_DISABLED) {
+		LOGP(DLMGCP, LOGL_ERROR, "Endpoint %u didn't request Osmux\n",
+		     ENDPOINT_NUMBER(endp));
+		return -1;
+	}
+
+	osmux_xfrm_output_init(&endp->osmux.out,
+			       (endp->osmux.cid * rtp_ssrc_winlen) +
+			       (random() % rtp_ssrc_winlen));
+
+	endp->osmux.in = osmux_handle_lookup(endp->cfg, addr, port);
+	if (!endp->osmux.in) {
+		LOGP(DLMGCP, LOGL_ERROR, "Cannot allocate input osmux handle\n");
+		return -1;
+	}
+	if (!osmux_xfrm_input_open_circuit(endp->osmux.in, endp->osmux.cid,
+					   endp->cfg->osmux_dummy)) {
+		LOGP(DLMGCP, LOGL_ERROR, "Cannot open osmux circuit %u\n",
+		     endp->osmux.cid);
+		return -1;
+	}
+
+	switch (endp->cfg->role) {
+		case MGCP_BSC_NAT:
+			endp->type = MGCP_OSMUX_BSC_NAT;
+			break;
+		case MGCP_BSC:
+			endp->type = MGCP_OSMUX_BSC;
+			break;
+	}
+	endp->osmux.state = OSMUX_STATE_ENABLED;
+
+	return 0;
+}
+
+void osmux_disable_endpoint(struct mgcp_endpoint *endp)
+{
+	LOGP(DLMGCP, LOGL_INFO, "Releasing endpoint %u using Osmux CID %u\n",
+	     ENDPOINT_NUMBER(endp), endp->osmux.cid);
+	osmux_xfrm_input_close_circuit(endp->osmux.in, endp->osmux.cid);
+	endp->osmux.state = OSMUX_STATE_DISABLED;
+	endp->osmux.cid = -1;
+	osmux_handle_put(endp->osmux.in);
+}
+
+void osmux_release_cid(struct mgcp_endpoint *endp)
+{
+	if (endp->osmux.allocated_cid >= 0)
+		osmux_put_cid(endp->osmux.allocated_cid);
+	endp->osmux.allocated_cid = -1;
+}
+
+void osmux_allocate_cid(struct mgcp_endpoint *endp)
+{
+	osmux_release_cid(endp);
+	endp->osmux.allocated_cid = osmux_get_cid();
+}
+
+/* We don't need to send the dummy load for osmux so often as another endpoint
+ * may have already punched the hole in the firewall. This approach is simple
+ * though.
+ */
+int osmux_send_dummy(struct mgcp_endpoint *endp)
+{
+	char buf[1 + sizeof(uint8_t)];
+	struct in_addr addr_unset = {};
+
+	buf[0] = MGCP_DUMMY_LOAD;
+	memcpy(&buf[1], &endp->osmux.cid, sizeof(endp->osmux.cid));
+
+	/* Wait until we have the connection information from MDCX */
+	if (memcmp(&endp->net_end.addr, &addr_unset, sizeof(addr_unset)) == 0)
+		return 0;
+
+	if (endp->osmux.state == OSMUX_STATE_ACTIVATING) {
+		if (osmux_enable_endpoint(endp, &endp->net_end.addr,
+					  htons(endp->cfg->osmux_port)) < 0) {
+			LOGP(DLMGCP, LOGL_ERROR,
+			     "Could not activate osmux in endpoint %d\n",
+			     ENDPOINT_NUMBER(endp));
+		}
+		LOGP(DLMGCP, LOGL_ERROR,
+		     "Osmux CID %u for %s:%u is now enabled\n",
+		     endp->osmux.cid, inet_ntoa(endp->net_end.addr),
+		     endp->cfg->osmux_port);
+	}
+	LOGP(DLMGCP, LOGL_DEBUG,
+	     "sending OSMUX dummy load to %s CID %u\n",
+	     inet_ntoa(endp->net_end.addr), endp->osmux.cid);
+
+	return mgcp_udp_send(osmux_fd.fd, &endp->net_end.addr,
+			     htons(endp->cfg->osmux_port), buf, sizeof(buf));
+}
+
+/* bsc-nat allocates/releases the Osmux circuit ID */
+static uint8_t osmux_cid_bitmap[(OSMUX_CID_MAX + 1) / 8];
+
+int osmux_used_cid(void)
+{
+	int i, j, used = 0;
+
+	for (i = 0; i < sizeof(osmux_cid_bitmap); i++) {
+		for (j = 0; j < 8; j++) {
+			if (osmux_cid_bitmap[i] & (1 << j))
+				used += 1;
+		}
+	}
+
+	return used;
+}
+
+int osmux_get_cid(void)
+{
+	int i, j;
+
+	for (i = 0; i < sizeof(osmux_cid_bitmap); i++) {
+		for (j = 0; j < 8; j++) {
+			if (osmux_cid_bitmap[i] & (1 << j))
+				continue;
+
+			osmux_cid_bitmap[i] |= (1 << j);
+			LOGP(DLMGCP, LOGL_DEBUG,
+			     "Allocating Osmux CID %u from pool\n", (i * 8) + j);
+			return (i * 8) + j;
+		}
+	}
+
+	LOGP(DLMGCP, LOGL_ERROR, "All Osmux circuits are in use!\n");
+	return -1;
+}
+
+void osmux_put_cid(uint8_t osmux_cid)
+{
+	LOGP(DLMGCP, LOGL_DEBUG, "Osmux CID %u is back to the pool\n", osmux_cid);
+	osmux_cid_bitmap[osmux_cid / 8] &= ~(1 << (osmux_cid % 8));
+}
diff --git a/src/libosmo-mgcp/mgcp_protocol.c b/src/libosmo-mgcp/mgcp_protocol.c
new file mode 100644
index 0000000..bc1ec0d
--- /dev/null
+++ b/src/libosmo-mgcp/mgcp_protocol.c
@@ -0,0 +1,1640 @@
+/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
+/* The protocol implementation */
+
+/*
+ * (C) 2009-2012 by Holger Hans Peter Freyther <zecke@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/>.
+ *
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <limits.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/select.h>
+
+#include <osmocom/legacy_mgcp/mgcp.h>
+#include <osmocom/legacy_mgcp/mgcp_internal.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))
+
+
+static void mgcp_rtp_end_reset(struct mgcp_rtp_end *end);
+
+struct mgcp_request {
+	char *name;
+	struct msgb *(*handle_request) (struct mgcp_parse_data *data);
+	char *debug_name;
+};
+
+#define MGCP_REQUEST(NAME, REQ, DEBUG_NAME) \
+	{ .name = NAME, .handle_request = REQ, .debug_name = DEBUG_NAME },
+
+static struct msgb *handle_audit_endpoint(struct mgcp_parse_data *data);
+static struct msgb *handle_create_con(struct mgcp_parse_data *data);
+static struct msgb *handle_delete_con(struct mgcp_parse_data *data);
+static struct msgb *handle_modify_con(struct mgcp_parse_data *data);
+static struct msgb *handle_rsip(struct mgcp_parse_data *data);
+static struct msgb *handle_noti_req(struct mgcp_parse_data *data);
+
+static void create_transcoder(struct mgcp_endpoint *endp);
+static void delete_transcoder(struct mgcp_endpoint *endp);
+
+static int setup_rtp_processing(struct mgcp_endpoint *endp);
+
+static int mgcp_analyze_header(struct mgcp_parse_data *parse, char *data);
+
+/* Display an mgcp message on the log output */
+void display_mgcp_message(unsigned char *message, unsigned int len,
+			  char *preamble)
+{
+	unsigned char line[80];
+	unsigned char *ptr;
+	unsigned int consumed = 0;
+	unsigned int consumed_line = 0;
+	unsigned int line_count = 0;
+
+	if (!log_check_level(DLMGCP, LOGL_DEBUG))
+		return;
+
+	while (1) {
+		memset(line, 0, sizeof(line));
+		ptr = line;
+		consumed_line = 0;
+		do {
+			if (*message != '\n' && *message != '\r') {
+				*ptr = *message;
+				ptr++;
+			}
+			message++;
+			consumed++;
+			consumed_line++;
+		} while (*message != '\n' && consumed < len
+			 && consumed_line < sizeof(line));
+
+		if (strlen((const char *)line)) {
+			LOGP(DLMGCP, LOGL_DEBUG, "%s: line #%02u: %s\n",
+			     preamble, line_count, line);
+			line_count++;
+		}
+
+		if (consumed >= len)
+			return;
+	}
+}
+
+static int mgcp_check_param(const struct mgcp_endpoint *endp, const char *line)
+{
+	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));
+		return 0;
+	}
+
+	return 1;
+}
+
+static uint32_t generate_call_id(struct mgcp_config *cfg)
+{
+	int i;
+
+	/* use the call id */
+	++cfg->last_call_id;
+
+	/* handle wrap around */
+	if (cfg->last_call_id == CI_UNUSED)
+		++cfg->last_call_id;
+
+	/* callstack can only be of size number_of_endpoints */
+	/* verify that the call id is free, e.g. in case of overrun */
+	for (i = 1; i < cfg->trunk.number_endpoints; ++i)
+		if (cfg->trunk.endpoints[i].ci == cfg->last_call_id)
+			return generate_call_id(cfg);
+
+	return cfg->last_call_id;
+}
+
+/*
+ * array of function pointers for handling various
+ * messages. In the future this might be binary sorted
+ * for performance reasons.
+ */
+static const struct mgcp_request mgcp_requests [] = {
+	MGCP_REQUEST("AUEP", handle_audit_endpoint, "AuditEndpoint")
+	MGCP_REQUEST("CRCX", handle_create_con, "CreateConnection")
+	MGCP_REQUEST("DLCX", handle_delete_con, "DeleteConnection")
+	MGCP_REQUEST("MDCX", handle_modify_con, "ModifiyConnection")
+	MGCP_REQUEST("RQNT", handle_noti_req, "NotificationRequest")
+
+	/* SPEC extension */
+	MGCP_REQUEST("RSIP", handle_rsip, "ReSetInProgress")
+};
+
+static struct msgb *mgcp_msgb_alloc(void)
+{
+	struct msgb *msg;
+	msg = msgb_alloc_headroom(4096, 128, "MGCP msg");
+	if (!msg)
+	    LOGP(DLMGCP, LOGL_ERROR, "Failed to msgb for MGCP data.\n");
+
+	return msg;
+}
+
+static struct msgb *do_retransmission(const struct mgcp_endpoint *endp)
+{
+	struct msgb *msg = mgcp_msgb_alloc();
+	if (!msg)
+		return NULL;
+
+	msg->l2h = msgb_put(msg, strlen(endp->last_response));
+	memcpy(msg->l2h, endp->last_response, msgb_l2len(msg));
+	display_mgcp_message(msg->l2h, msgb_l2len(msg), "Retransmitted response");
+	return msg;
+}
+
+static struct msgb *create_resp(struct mgcp_endpoint *endp, int code,
+				const char *txt, const char *msg,
+				const char *trans, const char *param,
+				const char *sdp)
+{
+	int len;
+	struct msgb *res;
+
+	res = mgcp_msgb_alloc();
+	if (!res)
+		return NULL;
+
+	len = snprintf((char *) res->data, 2048, "%d %s%s%s\r\n%s",
+			code, trans, txt, param ? param : "", sdp ? sdp : "");
+	if (len < 0) {
+		LOGP(DLMGCP, LOGL_ERROR, "Failed to sprintf MGCP response.\n");
+		msgb_free(res);
+		return NULL;
+	}
+
+	res->l2h = msgb_put(res, len);
+	LOGP(DLMGCP, LOGL_DEBUG, "Generated response: code=%d\n", code);
+	display_mgcp_message(res->l2h, msgb_l2len(res), "Generated response");
+
+	/*
+	 * Remember the last transmission per endpoint.
+	 */
+	if (endp) {
+		struct mgcp_trunk_config *tcfg = endp->tcfg;
+		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,
+						(const char *) res->l2h,
+						msgb_l2len(res));
+	}
+
+	return res;
+}
+
+static struct msgb *create_ok_resp_with_param(struct mgcp_endpoint *endp,
+					int code, const char *msg,
+					const char *trans, const char *param)
+{
+	return create_resp(endp, code, " OK", msg, trans, param, NULL);
+}
+
+static struct msgb *create_ok_response(struct mgcp_endpoint *endp,
+					int code, const char *msg, const char *trans)
+{
+	return create_ok_resp_with_param(endp, code, msg, trans, NULL);
+}
+
+static struct msgb *create_err_response(struct mgcp_endpoint *endp,
+					int code, const char *msg, const char *trans)
+{
+	return create_resp(endp, code, " FAIL", msg, trans, NULL, NULL);
+}
+
+static int write_response_sdp(struct mgcp_endpoint *endp,
+			      char *sdp_record, size_t size, const char *addr)
+{
+	const char *fmtp_extra;
+	const char *audio_name;
+	int payload_type;
+	int len;
+	int nchars;
+
+	endp->cfg->get_net_downlink_format_cb(endp, &payload_type,
+					      &audio_name, &fmtp_extra);
+
+	len = snprintf(sdp_record, size,
+			"v=0\r\n"
+			"o=- %u 23 IN IP4 %s\r\n"
+			"s=-\r\n"
+			"c=IN IP4 %s\r\n"
+			"t=0 0\r\n",
+			endp->ci, addr, addr);
+
+	if (len < 0 || len >= size)
+		goto buffer_too_small;
+
+	if (payload_type >= 0) {
+		nchars = snprintf(sdp_record + len, size - len,
+				  "m=audio %d RTP/AVP %d\r\n",
+				  endp->net_end.local_port, payload_type);
+		if (nchars < 0 || nchars >= size - len)
+			goto buffer_too_small;
+
+		len += nchars;
+
+		if (audio_name && endp->tcfg->audio_send_name) {
+			nchars = snprintf(sdp_record + len, size - len,
+					  "a=rtpmap:%d %s\r\n",
+					  payload_type, audio_name);
+
+			if (nchars < 0 || nchars >= size - len)
+				goto buffer_too_small;
+
+			len += nchars;
+		}
+
+		if (fmtp_extra) {
+			nchars = snprintf(sdp_record + len, size - len,
+					  "%s\r\n", fmtp_extra);
+
+			if (nchars < 0 || nchars >= size - len)
+				goto buffer_too_small;
+
+			len += nchars;
+		}
+	}
+	if (endp->bts_end.packet_duration_ms > 0 && endp->tcfg->audio_send_ptime) {
+		nchars = snprintf(sdp_record + len, size - len,
+				  "a=ptime:%d\r\n",
+				  endp->bts_end.packet_duration_ms);
+		if (nchars < 0 || nchars >= size - len)
+			goto buffer_too_small;
+
+		len += nchars;
+	}
+
+	return len;
+
+buffer_too_small:
+	LOGP(DLMGCP, LOGL_ERROR, "SDP buffer too small: %zu (needed %d)\n",
+	     size, len);
+	return -1;
+}
+
+static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp,
+					     const char *msg, const char *trans_id)
+{
+	const char *addr = endp->cfg->local_ip;
+	char sdp_record[4096];
+	int len;
+	int nchars;
+	char osmux_extension[strlen("\nX-Osmux: 255") + 1];
+
+	if (!addr)
+		addr = mgcp_net_src_addr(endp);
+
+	if (endp->osmux.state == OSMUX_STATE_NEGOTIATING) {
+		sprintf(osmux_extension, "\nX-Osmux: %u", endp->osmux.cid);
+		endp->osmux.state = OSMUX_STATE_ACTIVATING;
+	} else {
+		osmux_extension[0] = '\0';
+	}
+
+	len = snprintf(sdp_record, sizeof(sdp_record),
+		       "I: %u%s\n\n", endp->ci, osmux_extension);
+	if (len < 0)
+		return NULL;
+
+	nchars = write_response_sdp(endp, sdp_record + len,
+				    sizeof(sdp_record) - len - 1, addr);
+	if (nchars < 0)
+		return NULL;
+
+	len += nchars;
+
+	sdp_record[sizeof(sdp_record) - 1] = '\0';
+
+	return create_resp(endp, 200, " OK", msg, trans_id, NULL, sdp_record);
+}
+
+static void send_dummy(struct mgcp_endpoint *endp)
+{
+	if (endp->osmux.state != OSMUX_STATE_DISABLED)
+		osmux_send_dummy(endp);
+	else
+		mgcp_send_dummy(endp);
+}
+
+/*
+ * handle incoming messages:
+ *   - this can be a command (four letters, space, transaction id)
+ *   - or a response (three numbers, space, transaction id)
+ */
+struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
+{
+	struct mgcp_parse_data pdata;
+	int i, code, handled = 0;
+	struct msgb *resp = NULL;
+	char *data;
+
+	if (msgb_l2len(msg) < 4) {
+		LOGP(DLMGCP, LOGL_ERROR, "msg too short: %d\n", msg->len);
+		return NULL;
+	}
+
+	if (mgcp_msg_terminate_nul(msg))
+		return NULL;
+
+	display_mgcp_message(msg->l2h, msgb_l2len(msg), "Received message");
+
+        /* attempt to treat it as a response */
+        if (sscanf((const char *)&msg->l2h[0], "%3d %*s", &code) == 1) {
+		LOGP(DLMGCP, LOGL_DEBUG, "Response: Code: %d\n", code);
+		return NULL;
+	}
+
+	msg->l3h = &msg->l2h[4];
+
+
+	/*
+	 * Check for a duplicate message and respond.
+	 */
+	memset(&pdata, 0, sizeof(pdata));
+	pdata.cfg = cfg;
+	data = strline_r((char *) msg->l3h, &pdata.save);
+	pdata.found = mgcp_analyze_header(&pdata, data);
+	if (pdata.endp && pdata.trans
+			&& pdata.endp->last_trans
+			&& strcmp(pdata.endp->last_trans, pdata.trans) == 0) {
+		return do_retransmission(pdata.endp);
+	}
+
+	for (i = 0; i < ARRAY_SIZE(mgcp_requests); ++i) {
+		if (strncmp(mgcp_requests[i].name, (const char *) &msg->l2h[0], 4) == 0) {
+			handled = 1;
+			resp = mgcp_requests[i].handle_request(&pdata);
+			break;
+		}
+	}
+
+	if (!handled)
+		LOGP(DLMGCP, LOGL_NOTICE, "MSG with type: '%.4s' not handled\n", &msg->l2h[0]);
+
+	return resp;
+}
+
+/**
+ * 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.
+ */
+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;
+
+	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;
+	}
+
+	return &tcfg->endpoints[endp];
+}
+
+static struct mgcp_endpoint *find_endpoint(struct mgcp_config *cfg, const char *mgcp)
+{
+	char *endptr = NULL;
+	unsigned int gw = INT_MAX;
+
+	if (strncmp(mgcp, "ds/e1", 5) == 0)
+		return find_e1_endpoint(cfg, mgcp);
+
+	gw = strtoul(mgcp, &endptr, 16);
+	if (gw > 0 && gw < cfg->trunk.number_endpoints && endptr[0] == '@')
+		return &cfg->trunk.endpoints[gw];
+
+	LOGP(DLMGCP, LOGL_ERROR, "Not able to find the endpoint: '%s'\n", mgcp);
+	return NULL;
+}
+
+/**
+ * @returns 0 when the status line was complete and transaction_id and
+ * endp out parameters are set.
+ */
+static int mgcp_analyze_header(struct mgcp_parse_data *pdata, char *data)
+{
+	int i = 0;
+	char *elem, *save = NULL;
+
+	OSMO_ASSERT(data);
+	pdata->trans = "000000";
+
+	for (elem = strtok_r(data, " ", &save); elem;
+	     elem = strtok_r(NULL, " ", &save)) {
+		switch (i) {
+		case 0:
+			pdata->trans = elem;
+			break;
+		case 1:
+			pdata->endp = find_endpoint(pdata->cfg, elem);
+			if (!pdata->endp) {
+				LOGP(DLMGCP, LOGL_ERROR,
+				     "Unable to find Endpoint `%s'\n", elem);
+				return -1;
+			}
+			break;
+		case 2:
+			if (strcmp("MGCP", elem)) {
+				LOGP(DLMGCP, LOGL_ERROR,
+				     "MGCP header parsing error\n");
+				return -1;
+			}
+			break;
+		case 3:
+			if (strcmp("1.0", elem)) {
+				LOGP(DLMGCP, LOGL_ERROR, "MGCP version `%s' "
+					"not supported\n", elem);
+				return -1;
+			}
+			break;
+		}
+		i++;
+	}
+
+	if (i != 4) {
+		LOGP(DLMGCP, LOGL_ERROR, "MGCP status line too short.\n");
+		pdata->trans = "000000";
+		pdata->endp = NULL;
+		return -1;
+	}
+
+	return 0;
+}
+
+static int verify_call_id(const struct mgcp_endpoint *endp,
+			  const char *callid)
+{
+	if (strcmp(endp->callid, callid) != 0) {
+		LOGP(DLMGCP, LOGL_ERROR, "CallIDs does not match on 0x%x. '%s' != '%s'\n",
+			ENDPOINT_NUMBER(endp), endp->callid, callid);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int verify_ci(const struct mgcp_endpoint *endp,
+		     const char *_ci)
+{
+	uint32_t ci = strtoul(_ci, NULL, 10);
+
+	if (ci != endp->ci) {
+		LOGP(DLMGCP, LOGL_ERROR, "ConnectionIdentifiers do not match on 0x%x. %u != %s\n",
+			ENDPOINT_NUMBER(endp), endp->ci, _ci);
+		return -1;
+	}
+
+	return 0;
+}
+
+static struct msgb *handle_audit_endpoint(struct mgcp_parse_data *p)
+{
+	if (p->found != 0)
+		return create_err_response(NULL, 500, "AUEP", p->trans);
+	else
+		return create_ok_response(p->endp, 200, "AUEP", p->trans);
+}
+
+static int parse_conn_mode(const char *msg, struct mgcp_endpoint *endp)
+{
+	int ret = 0;
+	if (strcmp(msg, "recvonly") == 0)
+		endp->conn_mode = MGCP_CONN_RECV_ONLY;
+	else if (strcmp(msg, "sendrecv") == 0)
+		endp->conn_mode = MGCP_CONN_RECV_SEND;
+	else if (strcmp(msg, "sendonly") == 0)
+		endp->conn_mode = MGCP_CONN_SEND_ONLY;
+	else if (strcmp(msg, "loopback") == 0)
+		endp->conn_mode = MGCP_CONN_LOOPBACK;
+	else {
+		LOGP(DLMGCP, LOGL_ERROR, "Unknown connection mode: '%s'\n", msg);
+		ret = -1;
+	}
+
+	endp->net_end.output_enabled =
+		endp->conn_mode & MGCP_CONN_SEND_ONLY ? 1 : 0;
+	endp->bts_end.output_enabled =
+		endp->conn_mode & MGCP_CONN_RECV_ONLY ? 1 : 0;
+
+	LOGP(DLMGCP, LOGL_DEBUG, "endpoint %x connection mode '%s' %d output_enabled net %d bts %d\n",
+	     ENDPOINT_NUMBER(endp),
+	     msg, endp->conn_mode, endp->net_end.output_enabled,
+	     endp->bts_end.output_enabled);
+
+	return ret;
+}
+
+static int allocate_port(struct mgcp_endpoint *endp, struct mgcp_rtp_end *end,
+			 struct mgcp_port_range *range,
+			 int (*alloc)(struct mgcp_endpoint *endp, int port))
+{
+	int i;
+
+	if (range->mode == PORT_ALLOC_STATIC) {
+		end->local_alloc = PORT_ALLOC_STATIC;
+		return 0;
+	}
+
+	/* attempt to find a port */
+	for (i = 0; i < 200; ++i) {
+		int rc;
+
+		if (range->last_port >= range->range_end)
+			range->last_port = range->range_start;
+
+		rc = alloc(endp, range->last_port);
+
+		range->last_port += 2;
+		if (rc == 0) {
+			end->local_alloc = PORT_ALLOC_DYNAMIC;
+			return 0;
+		}
+
+	}
+
+	LOGP(DLMGCP, LOGL_ERROR, "Allocating a RTP/RTCP port failed 200 times 0x%x.\n",
+	     ENDPOINT_NUMBER(endp));
+	return -1;
+}
+
+static int allocate_ports(struct mgcp_endpoint *endp)
+{
+	if (allocate_port(endp, &endp->net_end, &endp->cfg->net_ports,
+			  mgcp_bind_net_rtp_port) != 0)
+		return -1;
+
+	if (allocate_port(endp, &endp->bts_end, &endp->cfg->bts_ports,
+			  mgcp_bind_bts_rtp_port) != 0) {
+		mgcp_rtp_end_reset(&endp->net_end);
+		return -1;
+	}
+
+	if (endp->cfg->transcoder_ip && endp->tcfg->trunk_type == MGCP_TRUNK_VIRTUAL) {
+		if (allocate_port(endp, &endp->trans_net,
+				  &endp->cfg->transcoder_ports,
+				  mgcp_bind_trans_net_rtp_port) != 0) {
+			mgcp_rtp_end_reset(&endp->net_end);
+			mgcp_rtp_end_reset(&endp->bts_end);
+			return -1;
+		}
+
+		if (allocate_port(endp, &endp->trans_bts,
+				  &endp->cfg->transcoder_ports,
+				  mgcp_bind_trans_bts_rtp_port) != 0) {
+			mgcp_rtp_end_reset(&endp->net_end);
+			mgcp_rtp_end_reset(&endp->bts_end);
+			mgcp_rtp_end_reset(&endp->trans_net);
+			return -1;
+		}
+
+		/* remember that we have set up transcoding */
+		endp->type = MGCP_RTP_TRANSCODED;
+	}
+
+	return 0;
+}
+
+/* Set the LCO from a string (see RFC 3435).
+ * The string is stored in the 'string' field. A NULL string is handled excatly
+ * like an empty string, the 'string' field is never NULL after this function
+ * has been called. */
+static void set_local_cx_options(void *ctx, struct mgcp_lco *lco,
+				 const char *options)
+{
+	char *p_opt, *a_opt;
+	char codec[9];
+
+	talloc_free(lco->string);
+	talloc_free(lco->codec);
+	lco->codec = NULL;
+	lco->pkt_period_min = lco->pkt_period_max = 0;
+	lco->string = talloc_strdup(ctx, options ? options : "");
+
+	p_opt = strstr(lco->string, "p:");
+	if (p_opt && sscanf(p_opt, "p:%d-%d",
+			    &lco->pkt_period_min, &lco->pkt_period_max) == 1)
+		lco->pkt_period_max = lco->pkt_period_min;
+
+	a_opt = strstr(lco->string, "a:");
+	if (a_opt && sscanf(a_opt, "a:%8[^,]", codec) == 1)
+		lco->codec = talloc_strdup(ctx, codec);
+}
+
+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;
+
+	int patch_ssrc = expect_ssrc_change && tcfg->force_constant_ssrc;
+
+	rtp->force_aligned_timing = tcfg->force_aligned_timing;
+	rtp->force_constant_ssrc = patch_ssrc ? 1 : 0;
+
+	LOGP(DLMGCP, LOGL_DEBUG,
+	     "Configuring RTP endpoint: local port %d%s%s\n",
+	     ntohs(rtp->rtp_port),
+	     rtp->force_aligned_timing ? ", force constant timing" : "",
+	     rtp->force_constant_ssrc ? ", force constant ssrc" : "");
+}
+
+uint32_t mgcp_rtp_packet_duration(struct mgcp_endpoint *endp,
+				  struct mgcp_rtp_end *rtp)
+{
+	int f = 0;
+
+	/* Get the number of frames per channel and packet */
+	if (rtp->frames_per_packet)
+		f = rtp->frames_per_packet;
+	else if (rtp->packet_duration_ms && rtp->codec.frame_duration_num) {
+		int den = 1000 * rtp->codec.frame_duration_num;
+		f = (rtp->packet_duration_ms * rtp->codec.frame_duration_den + den/2)
+			/ den;
+	}
+
+	return rtp->codec.rate * f * rtp->codec.frame_duration_num / rtp->codec.frame_duration_den;
+}
+
+static int mgcp_parse_osmux_cid(const char *line)
+{
+	int osmux_cid;
+
+	if (sscanf(line + 2, "Osmux: %u", &osmux_cid) != 1)
+		return -1;
+
+	if (osmux_cid > OSMUX_CID_MAX) {
+		LOGP(DLMGCP, LOGL_ERROR, "Osmux ID too large: %u > %u\n",
+		     osmux_cid, OSMUX_CID_MAX);
+		return -1;
+	}
+	LOGP(DLMGCP, LOGL_DEBUG, "bsc-nat offered Osmux CID %u\n", osmux_cid);
+
+	return osmux_cid;
+}
+
+static int mgcp_osmux_setup(struct mgcp_endpoint *endp, const char *line)
+{
+	if (!endp->cfg->osmux_init) {
+		if (osmux_init(OSMUX_ROLE_BSC, endp->cfg) < 0) {
+			LOGP(DLMGCP, LOGL_ERROR, "Cannot init OSMUX\n");
+			return -1;
+		}
+		LOGP(DLMGCP, LOGL_NOTICE, "OSMUX socket has been set up\n");
+	}
+
+	return mgcp_parse_osmux_cid(line);
+}
+
+static struct msgb *handle_create_con(struct mgcp_parse_data *p)
+{
+	struct mgcp_trunk_config *tcfg;
+	struct mgcp_endpoint *endp = p->endp;
+	int error_code = 400;
+
+	const char *local_options = NULL;
+	const char *callid = NULL;
+	const char *mode = NULL;
+	char *line;
+	int have_sdp = 0, osmux_cid = -1;
+
+	if (p->found != 0)
+		return create_err_response(NULL, 510, "CRCX", p->trans);
+
+	/* parse CallID C: and LocalParameters L: */
+	for_each_line(line, p->save) {
+		if (!mgcp_check_param(endp, line))
+			continue;
+
+		switch (line[0]) {
+		case 'L':
+			local_options = (const char *) line + 3;
+			break;
+		case 'C':
+			callid = (const char *) line + 3;
+			break;
+		case 'M':
+			mode = (const char *) line + 3;
+			break;
+		case 'X':
+			/* Osmux is not enabled in this bsc, ignore it so the
+			 * bsc-nat knows that we don't want to use Osmux.
+			 */
+			if (!p->endp->cfg->osmux)
+				break;
+
+			if (strncmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0)
+				osmux_cid = mgcp_osmux_setup(endp, line);
+			break;
+		case '\0':
+			have_sdp = 1;
+			goto mgcp_header_done;
+		default:
+			LOGP(DLMGCP, LOGL_NOTICE, "Unhandled option: '%c'/%d on 0x%x\n",
+				*line, *line, ENDPOINT_NUMBER(endp));
+			break;
+		}
+	}
+
+mgcp_header_done:
+	tcfg = p->endp->tcfg;
+
+	/* Check required data */
+	if (!callid || !mode) {
+		LOGP(DLMGCP, LOGL_ERROR, "Missing callid and mode in CRCX on 0x%x\n",
+		     ENDPOINT_NUMBER(endp));
+		return create_err_response(endp, 400, "CRCX", p->trans);
+	}
+
+	if (endp->allocated) {
+		if (tcfg->force_realloc) {
+			LOGP(DLMGCP, LOGL_NOTICE, "Endpoint 0x%x already allocated. Forcing realloc.\n",
+			    ENDPOINT_NUMBER(endp));
+			mgcp_release_endp(endp);
+			if (p->cfg->realloc_cb)
+				p->cfg->realloc_cb(tcfg, ENDPOINT_NUMBER(endp));
+		} else {
+			LOGP(DLMGCP, LOGL_ERROR, "Endpoint is already used. 0x%x\n",
+			     ENDPOINT_NUMBER(endp));
+			return create_err_response(endp, 400, "CRCX", p->trans);
+		}
+	}
+
+	/* copy some parameters */
+	endp->callid = talloc_strdup(tcfg->endpoints, callid);
+
+	set_local_cx_options(endp->tcfg->endpoints, &endp->local_options,
+			     local_options);
+
+	if (parse_conn_mode(mode, endp) != 0) {
+		    error_code = 517;
+		    goto error2;
+	}
+
+	/* initialize */
+	endp->net_end.rtp_port = endp->net_end.rtcp_port = endp->bts_end.rtp_port = endp->bts_end.rtcp_port = 0;
+	mgcp_rtp_end_config(endp, 0, &endp->net_end);
+	mgcp_rtp_end_config(endp, 0, &endp->bts_end);
+
+	/* set to zero until we get the info */
+	memset(&endp->net_end.addr, 0, sizeof(endp->net_end.addr));
+
+	/* bind to the port now */
+	if (allocate_ports(endp) != 0)
+		goto error2;
+
+	/* assign a local call identifier or fail */
+	endp->ci = generate_call_id(p->cfg);
+	if (endp->ci == CI_UNUSED)
+		goto error2;
+
+	/* Annotate Osmux circuit ID and set it to negotiating state until this
+	 * is fully set up from the dummy load.
+	 */
+	endp->osmux.state = OSMUX_STATE_DISABLED;
+	if (osmux_cid >= 0) {
+		endp->osmux.cid = osmux_cid;
+		endp->osmux.state = OSMUX_STATE_NEGOTIATING;
+	} else if (endp->cfg->osmux == OSMUX_USAGE_ONLY) {
+		LOGP(DLMGCP, LOGL_ERROR,
+			"Osmux only and no osmux offered on 0x%x\n", ENDPOINT_NUMBER(endp));
+		goto error2;
+	}
+
+	endp->allocated = 1;
+
+	/* set up RTP media parameters */
+	mgcp_set_audio_info(p->cfg, &endp->bts_end.codec, tcfg->audio_payload, tcfg->audio_name);
+	endp->bts_end.fmtp_extra = talloc_strdup(tcfg->endpoints,
+						tcfg->audio_fmtp_extra);
+	if (have_sdp)
+		mgcp_parse_sdp_data(endp, &endp->net_end, p);
+	else if (endp->local_options.codec)
+		mgcp_set_audio_info(p->cfg, &endp->net_end.codec,
+			       PTYPE_UNDEFINED, endp->local_options.codec);
+
+	if (p->cfg->bts_force_ptime) {
+		endp->bts_end.packet_duration_ms = p->cfg->bts_force_ptime;
+		endp->bts_end.force_output_ptime = 1;
+	}
+
+	if (setup_rtp_processing(endp) != 0)
+		goto error2;
+
+	/* policy CB */
+	if (p->cfg->policy_cb) {
+		int rc;
+		rc = p->cfg->policy_cb(tcfg, ENDPOINT_NUMBER(endp),
+				MGCP_ENDP_CRCX, p->trans);
+		switch (rc) {
+		case MGCP_POLICY_REJECT:
+			LOGP(DLMGCP, LOGL_NOTICE, "CRCX rejected by policy on 0x%x\n",
+			     ENDPOINT_NUMBER(endp));
+			mgcp_release_endp(endp);
+			return create_err_response(endp, 400, "CRCX", p->trans);
+			break;
+		case MGCP_POLICY_DEFER:
+			/* stop processing */
+			create_transcoder(endp);
+			return NULL;
+			break;
+		case MGCP_POLICY_CONT:
+			/* just continue */
+			break;
+		}
+	}
+
+	LOGP(DLMGCP, LOGL_DEBUG, "Creating endpoint on: 0x%x CI: %u port: %u/%u\n",
+		ENDPOINT_NUMBER(endp), endp->ci,
+		endp->net_end.local_port, endp->bts_end.local_port);
+	if (p->cfg->change_cb)
+		p->cfg->change_cb(tcfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_CRCX);
+
+	if (endp->conn_mode & MGCP_CONN_RECV_ONLY && tcfg->keepalive_interval != 0) {
+		send_dummy(endp);
+	}
+
+	create_transcoder(endp);
+	return create_response_with_sdp(endp, "CRCX", p->trans);
+error2:
+	mgcp_release_endp(endp);
+	LOGP(DLMGCP, LOGL_NOTICE, "Resource error on 0x%x\n", ENDPOINT_NUMBER(endp));
+	return create_err_response(endp, error_code, "CRCX", p->trans);
+}
+
+static struct msgb *handle_modify_con(struct mgcp_parse_data *p)
+{
+	struct mgcp_endpoint *endp = p->endp;
+	int error_code = 500;
+	int silent = 0;
+	int have_sdp = 0;
+	char *line;
+	const char *local_options = NULL;
+
+	if (p->found != 0)
+		return create_err_response(NULL, 510, "MDCX", p->trans);
+
+	if (endp->ci == CI_UNUSED) {
+		LOGP(DLMGCP, LOGL_ERROR, "Endpoint is not "
+			"holding a connection. 0x%x\n", ENDPOINT_NUMBER(endp));
+		return create_err_response(endp, 400, "MDCX", p->trans);
+	}
+
+	for_each_line(line, p->save) {
+		if (!mgcp_check_param(endp, line))
+			continue;
+
+		switch (line[0]) {
+		case 'C': {
+			if (verify_call_id(endp, line + 3) != 0)
+				goto error3;
+			break;
+		}
+		case 'I': {
+			if (verify_ci(endp, line + 3) != 0)
+				goto error3;
+			break;
+		}
+		case 'L':
+			local_options = (const char *) line + 3;
+			break;
+		case 'M':
+			if (parse_conn_mode(line + 3, endp) != 0) {
+			    error_code = 517;
+			    goto error3;
+			}
+			endp->orig_mode = endp->conn_mode;
+			break;
+		case 'Z':
+			silent = strcmp("noanswer", line + 3) == 0;
+			break;
+		case '\0':
+			/* SDP file begins */
+			have_sdp = 1;
+			mgcp_parse_sdp_data(endp, &endp->net_end, p);
+			/* This will exhaust p->save, so the loop will
+			 * terminate next time.
+			 */
+			break;
+		default:
+			LOGP(DLMGCP, LOGL_NOTICE, "Unhandled MGCP option: '%c'/%d on 0x%x\n",
+				line[0], line[0], ENDPOINT_NUMBER(endp));
+			break;
+		}
+	}
+
+	set_local_cx_options(endp->tcfg->endpoints, &endp->local_options,
+			     local_options);
+
+	if (!have_sdp && endp->local_options.codec)
+		mgcp_set_audio_info(p->cfg, &endp->net_end.codec,
+			       PTYPE_UNDEFINED, endp->local_options.codec);
+
+	if (setup_rtp_processing(endp) != 0)
+		goto error3;
+
+	/* policy CB */
+	if (p->cfg->policy_cb) {
+		int rc;
+		rc = p->cfg->policy_cb(endp->tcfg, ENDPOINT_NUMBER(endp),
+						MGCP_ENDP_MDCX, p->trans);
+		switch (rc) {
+		case MGCP_POLICY_REJECT:
+			LOGP(DLMGCP, LOGL_NOTICE, "MDCX rejected by policy on 0x%x\n",
+			     ENDPOINT_NUMBER(endp));
+			if (silent)
+				goto out_silent;
+			return create_err_response(endp, 400, "MDCX", p->trans);
+			break;
+		case MGCP_POLICY_DEFER:
+			/* stop processing */
+			LOGP(DLMGCP, LOGL_DEBUG, "endp %x MDCX defer\n",
+			     ENDPOINT_NUMBER(endp));
+			return NULL;
+			break;
+		case MGCP_POLICY_CONT:
+			/* just continue */
+			break;
+		}
+	}
+
+	mgcp_rtp_end_config(endp, 1, &endp->net_end);
+	mgcp_rtp_end_config(endp, 1, &endp->bts_end);
+
+	/* modify */
+	LOGP(DLMGCP, LOGL_DEBUG, "Modified endpoint on: 0x%x Server: %s:%u\n",
+		ENDPOINT_NUMBER(endp), inet_ntoa(endp->net_end.addr), ntohs(endp->net_end.rtp_port));
+	if (p->cfg->change_cb)
+		p->cfg->change_cb(endp->tcfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_MDCX);
+
+	if (endp->conn_mode & MGCP_CONN_RECV_ONLY &&
+	    endp->tcfg->keepalive_interval != 0)
+		send_dummy(endp);
+
+	if (silent)
+		goto out_silent;
+
+	return create_response_with_sdp(endp, "MDCX", p->trans);
+
+error3:
+	return create_err_response(endp, error_code, "MDCX", p->trans);
+
+
+out_silent:
+	LOGP(DLMGCP, LOGL_DEBUG, "endp %x Modify endpoint: silent exit\n",
+	     ENDPOINT_NUMBER(endp));
+	return NULL;
+}
+
+static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
+{
+	struct mgcp_endpoint *endp = p->endp;
+	int error_code = 400;
+	int silent = 0;
+	char *line;
+	char stats[1048];
+
+	if (p->found != 0)
+		return create_err_response(NULL, error_code, "DLCX", p->trans);
+
+	if (!p->endp->allocated) {
+		LOGP(DLMGCP, LOGL_ERROR, "Endpoint is not used. 0x%x\n",
+			ENDPOINT_NUMBER(endp));
+		return create_err_response(endp, 400, "DLCX", p->trans);
+	}
+
+	for_each_line(line, p->save) {
+		if (!mgcp_check_param(endp, line))
+			continue;
+
+		switch (line[0]) {
+		case 'C':
+			if (verify_call_id(endp, line + 3) != 0)
+				goto error3;
+			break;
+		case 'I':
+			if (verify_ci(endp, line + 3) != 0)
+				goto error3;
+			break;
+		case 'Z':
+			silent = strcmp("noanswer", line + 3) == 0;
+			break;
+		default:
+			LOGP(DLMGCP, LOGL_NOTICE, "Unhandled option: '%c'/%d on 0x%x\n",
+				line[0], line[0], ENDPOINT_NUMBER(endp));
+			break;
+		}
+	}
+
+	/* policy CB */
+	if (p->cfg->policy_cb) {
+		int rc;
+		rc = p->cfg->policy_cb(endp->tcfg, ENDPOINT_NUMBER(endp),
+						MGCP_ENDP_DLCX, p->trans);
+		switch (rc) {
+		case MGCP_POLICY_REJECT:
+			LOGP(DLMGCP, LOGL_NOTICE, "DLCX rejected by policy on 0x%x\n",
+			     ENDPOINT_NUMBER(endp));
+			if (silent)
+				goto out_silent;
+			return create_err_response(endp, 400, "DLCX", p->trans);
+			break;
+		case MGCP_POLICY_DEFER:
+			/* stop processing */
+			delete_transcoder(endp);
+			return NULL;
+			break;
+		case MGCP_POLICY_CONT:
+			/* just continue */
+			break;
+		}
+	}
+
+	/* free the connection */
+	LOGP(DLMGCP, LOGL_DEBUG, "Deleted endpoint on: 0x%x Server: %s:%u\n",
+		ENDPOINT_NUMBER(endp), inet_ntoa(endp->net_end.addr), ntohs(endp->net_end.rtp_port));
+
+	/* save the statistics of the current call */
+	mgcp_format_stats(endp, stats, sizeof(stats));
+
+	delete_transcoder(endp);
+	mgcp_release_endp(endp);
+	if (p->cfg->change_cb)
+		p->cfg->change_cb(endp->tcfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_DLCX);
+
+	if (silent)
+		goto out_silent;
+	return create_ok_resp_with_param(endp, 250, "DLCX", p->trans, stats);
+
+error3:
+	return create_err_response(endp, error_code, "DLCX", p->trans);
+
+out_silent:
+	return NULL;
+}
+
+static struct msgb *handle_rsip(struct mgcp_parse_data *p)
+{
+	if (p->found != 0) {
+		LOGP(DLMGCP, LOGL_ERROR, "Failed to find the endpoint.\n");
+		return NULL;
+	}
+
+	if (p->cfg->reset_cb)
+		p->cfg->reset_cb(p->endp->tcfg);
+	return NULL;
+}
+
+static char extract_tone(const char *line)
+{
+	const char *str = strstr(line, "D/");
+	if (!str)
+		return CHAR_MAX;
+
+	return str[2];
+}
+
+/*
+ * This can request like DTMF detection and forward, fax detection... it
+ * can also request when the notification should be send and such. We don't
+ * do this right now.
+ */
+static struct msgb *handle_noti_req(struct mgcp_parse_data *p)
+{
+	int res = 0;
+	char *line;
+	char tone = CHAR_MAX;
+
+	if (p->found != 0)
+		return create_err_response(NULL, 400, "RQNT", p->trans);
+
+	for_each_line(line, p->save) {
+		switch (line[0]) {
+		case 'S':
+			tone = extract_tone(line);
+			break;
+		}
+	}
+
+	/* we didn't see a signal request with a tone */
+	if (tone == CHAR_MAX)
+		return create_ok_response(p->endp, 200, "RQNT", p->trans);
+
+	if (p->cfg->rqnt_cb)
+		res = p->cfg->rqnt_cb(p->endp, tone);
+
+	return res == 0 ?
+		create_ok_response(p->endp, 200, "RQNT", p->trans) :
+		create_err_response(p->endp, res, "RQNT", p->trans);
+}
+
+static void mgcp_keepalive_timer_cb(void *_tcfg)
+{
+	struct mgcp_trunk_config *tcfg = _tcfg;
+	int i;
+	LOGP(DLMGCP, LOGL_DEBUG, "Triggered trunk %d keepalive timer.\n",
+	     tcfg->trunk_nr);
+
+	if (tcfg->keepalive_interval <= 0)
+		return;
+
+	for (i = 1; i < tcfg->number_endpoints; ++i) {
+		struct mgcp_endpoint *endp = &tcfg->endpoints[i];
+		if (endp->conn_mode == MGCP_CONN_RECV_ONLY)
+			send_dummy(endp);
+	}
+
+	LOGP(DLMGCP, LOGL_DEBUG, "Rescheduling trunk %d keepalive timer.\n",
+	     tcfg->trunk_nr);
+	osmo_timer_schedule(&tcfg->keepalive_timer, tcfg->keepalive_interval, 0);
+}
+
+void mgcp_trunk_set_keepalive(struct mgcp_trunk_config *tcfg, int interval)
+{
+	tcfg->keepalive_interval = interval;
+	osmo_timer_setup(&tcfg->keepalive_timer, mgcp_keepalive_timer_cb, tcfg);
+
+	if (interval <= 0)
+		osmo_timer_del(&tcfg->keepalive_timer);
+	else
+		osmo_timer_schedule(&tcfg->keepalive_timer,
+				    tcfg->keepalive_interval, 0);
+}
+
+struct mgcp_config *mgcp_config_alloc(void)
+{
+	struct mgcp_config *cfg;
+
+	cfg = talloc_zero(NULL, struct mgcp_config);
+	if (!cfg) {
+		LOGP(DLMGCP, LOGL_FATAL, "Failed to allocate config.\n");
+		return NULL;
+	}
+
+	cfg->source_port = 2427;
+	cfg->source_addr = talloc_strdup(cfg, "0.0.0.0");
+	cfg->osmux_addr = talloc_strdup(cfg, "0.0.0.0");
+
+	cfg->transcoder_remote_base = 4000;
+
+	cfg->bts_ports.base_port = RTP_PORT_DEFAULT;
+	cfg->net_ports.base_port = RTP_PORT_NET_DEFAULT;
+
+	cfg->rtp_processing_cb = &mgcp_rtp_processing_default;
+	cfg->setup_rtp_processing_cb = &mgcp_setup_rtp_processing_default;
+
+	cfg->get_net_downlink_format_cb = &mgcp_get_net_downlink_format_default;
+
+	/* default trunk handling */
+	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.omit_rtcp = 0;
+	mgcp_trunk_set_keepalive(&cfg->trunk, MGCP_KEEPALIVE_ONCE);
+
+	INIT_LLIST_HEAD(&cfg->trunks);
+
+	return cfg;
+}
+
+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->number_endpoints = 33;
+	trunk->omit_rtcp = 0;
+	mgcp_trunk_set_keepalive(trunk, MGCP_KEEPALIVE_ONCE);
+	llist_add_tail(&trunk->entry, &cfg->trunks);
+	return trunk;
+}
+
+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;
+}
+
+static void mgcp_rtp_codec_reset(struct mgcp_rtp_codec *codec)
+{
+	codec->payload_type = -1;
+	talloc_free(codec->subtype_name);
+	codec->subtype_name = NULL;
+	talloc_free(codec->audio_name);
+	codec->audio_name = NULL;
+	codec->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM;
+	codec->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN;
+	codec->rate               = DEFAULT_RTP_AUDIO_DEFAULT_RATE;
+	codec->channels           = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS;
+}
+
+static void mgcp_rtp_end_reset(struct mgcp_rtp_end *end)
+{
+	if (end->local_alloc == PORT_ALLOC_DYNAMIC) {
+		mgcp_free_rtp_port(end);
+		end->local_port = 0;
+	}
+
+	end->packets = 0;
+	end->octets = 0;
+	end->dropped_packets = 0;
+	memset(&end->addr, 0, sizeof(end->addr));
+	end->rtp_port = end->rtcp_port = 0;
+	end->local_alloc = -1;
+	talloc_free(end->fmtp_extra);
+	end->fmtp_extra = NULL;
+	talloc_free(end->rtp_process_data);
+	end->rtp_process_data = NULL;
+
+	/* Set default values */
+	end->frames_per_packet  = 0; /* unknown */
+	end->packet_duration_ms = DEFAULT_RTP_AUDIO_PACKET_DURATION_MS;
+	end->output_enabled	= 0;
+
+	mgcp_rtp_codec_reset(&end->codec);
+	mgcp_rtp_codec_reset(&end->alt_codec);
+}
+
+static void mgcp_rtp_end_init(struct mgcp_rtp_end *end)
+{
+	mgcp_rtp_end_reset(end);
+	end->rtp.fd = -1;
+	end->rtcp.fd = -1;
+}
+
+int mgcp_endpoints_allocate(struct mgcp_trunk_config *tcfg)
+{
+	int i;
+
+	/* Initialize all endpoints */
+	tcfg->endpoints = _talloc_zero_array(tcfg->cfg,
+				       sizeof(struct mgcp_endpoint),
+				       tcfg->number_endpoints, "endpoints");
+	if (!tcfg->endpoints)
+		return -1;
+
+	for (i = 0; i < tcfg->number_endpoints; ++i) {
+		tcfg->endpoints[i].osmux.allocated_cid = -1;
+		tcfg->endpoints[i].ci = CI_UNUSED;
+		tcfg->endpoints[i].cfg = tcfg->cfg;
+		tcfg->endpoints[i].tcfg = tcfg;
+		mgcp_rtp_end_init(&tcfg->endpoints[i].net_end);
+		mgcp_rtp_end_init(&tcfg->endpoints[i].bts_end);
+		mgcp_rtp_end_init(&tcfg->endpoints[i].trans_net);
+		mgcp_rtp_end_init(&tcfg->endpoints[i].trans_bts);
+	}
+
+	return 0;
+}
+
+void mgcp_release_endp(struct mgcp_endpoint *endp)
+{
+	LOGP(DLMGCP, LOGL_DEBUG, "Releasing endpoint on: 0x%x\n", ENDPOINT_NUMBER(endp));
+	endp->ci = CI_UNUSED;
+	endp->allocated = 0;
+
+	talloc_free(endp->callid);
+	endp->callid = NULL;
+
+	talloc_free(endp->local_options.string);
+	endp->local_options.string = NULL;
+	talloc_free(endp->local_options.codec);
+	endp->local_options.codec = NULL;
+
+	mgcp_rtp_end_reset(&endp->bts_end);
+	mgcp_rtp_end_reset(&endp->net_end);
+	mgcp_rtp_end_reset(&endp->trans_net);
+	mgcp_rtp_end_reset(&endp->trans_bts);
+	endp->type = MGCP_RTP_DEFAULT;
+
+	memset(&endp->net_state, 0, sizeof(endp->net_state));
+	memset(&endp->bts_state, 0, sizeof(endp->bts_state));
+
+	endp->conn_mode = endp->orig_mode = MGCP_CONN_NONE;
+
+	if (endp->osmux.state == OSMUX_STATE_ENABLED)
+		osmux_disable_endpoint(endp);
+
+	/* release the circuit ID if it had been allocated */
+	osmux_release_cid(endp);
+
+	memset(&endp->taps, 0, sizeof(endp->taps));
+}
+
+void mgcp_initialize_endp(struct mgcp_endpoint *endp)
+{
+	return mgcp_release_endp(endp);
+}
+
+static int send_trans(struct mgcp_config *cfg, const char *buf, int len)
+{
+	struct sockaddr_in addr;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sin_family = AF_INET;
+	addr.sin_addr = cfg->transcoder_in;
+	addr.sin_port = htons(2427);
+	return sendto(cfg->gw_fd.bfd.fd, buf, len, 0,
+		      (struct sockaddr *) &addr, sizeof(addr));
+}
+
+static void send_msg(struct mgcp_endpoint *endp, int endpoint, int port,
+		     const char *msg, const char *mode)
+{
+	char buf[2096];
+	int len;
+	int nchars;
+
+	/* hardcoded to AMR right now, we do not know the real type at this point */
+	len = snprintf(buf, sizeof(buf),
+			"%s 42 %x@mgw MGCP 1.0\r\n"
+			"C: 4256\r\n"
+			"M: %s\r\n"
+			"\r\n",
+			msg, endpoint, mode);
+
+	if (len < 0)
+		return;
+
+	nchars = write_response_sdp(endp, buf + len, sizeof(buf) + len - 1, NULL);
+	if (nchars < 0)
+		return;
+
+	len += nchars;
+
+	buf[sizeof(buf) - 1] = '\0';
+
+	send_trans(endp->cfg, buf, len);
+}
+
+static void send_dlcx(struct mgcp_endpoint *endp, int endpoint)
+{
+	char buf[2096];
+	int len;
+
+	len = snprintf(buf, sizeof(buf),
+			"DLCX 43 %x@mgw MGCP 1.0\r\n"
+			"C: 4256\r\n"
+			, endpoint);
+
+	if (len < 0)
+		return;
+
+	buf[sizeof(buf) - 1] = '\0';
+
+	send_trans(endp->cfg, buf, len);
+}
+
+static int send_agent(struct mgcp_config *cfg, const char *buf, int len)
+{
+	return write(cfg->gw_fd.bfd.fd, buf, len);
+}
+
+int mgcp_send_reset_all(struct mgcp_config *cfg)
+{
+	static const char mgcp_reset[] = {
+	    "RSIP 1 *@mgw MGCP 1.0\r\n"
+	};
+
+	return send_agent(cfg, mgcp_reset, sizeof mgcp_reset -1);
+}
+
+int mgcp_send_reset_ep(struct mgcp_endpoint *endp, int endpoint)
+{
+	char buf[128];
+	int len;
+
+	len = snprintf(buf, sizeof(buf),
+			"RSIP 39 %x@mgw MGCP 1.0\r\n"
+			, endpoint);
+	if (len < 0)
+		return len;
+
+	buf[sizeof(buf) - 1] = '\0';
+
+	return send_agent(endp->cfg, buf, len);
+}
+
+static int setup_rtp_processing(struct mgcp_endpoint *endp)
+{
+	int rc = 0;
+	struct mgcp_config *cfg = endp->cfg;
+
+	if (endp->type != MGCP_RTP_DEFAULT)
+		return 0;
+
+	if (endp->conn_mode == MGCP_CONN_LOOPBACK)
+		return 0;
+
+	if (endp->conn_mode & MGCP_CONN_SEND_ONLY)
+		rc |= cfg->setup_rtp_processing_cb(endp, &endp->net_end, &endp->bts_end);
+	else
+		rc |= cfg->setup_rtp_processing_cb(endp, &endp->net_end, NULL);
+
+	if (endp->conn_mode & MGCP_CONN_RECV_ONLY)
+		rc |= cfg->setup_rtp_processing_cb(endp, &endp->bts_end, &endp->net_end);
+	else
+		rc |= cfg->setup_rtp_processing_cb(endp, &endp->bts_end, NULL);
+	return rc;
+}
+
+static void create_transcoder(struct mgcp_endpoint *endp)
+{
+	int port;
+	int in_endp = ENDPOINT_NUMBER(endp);
+	int out_endp = endp_back_channel(in_endp);
+
+	if (endp->type != MGCP_RTP_TRANSCODED)
+		return;
+
+	send_msg(endp, in_endp, endp->trans_bts.local_port, "CRCX", "sendrecv");
+	send_msg(endp, in_endp, endp->trans_bts.local_port, "MDCX", "sendrecv");
+	send_msg(endp, out_endp, endp->trans_net.local_port, "CRCX", "sendrecv");
+	send_msg(endp, out_endp, endp->trans_net.local_port, "MDCX", "sendrecv");
+
+	port = rtp_calculate_port(in_endp, endp->cfg->transcoder_remote_base);
+	endp->trans_bts.rtp_port = htons(port);
+	endp->trans_bts.rtcp_port = htons(port + 1);
+
+	port = rtp_calculate_port(out_endp, endp->cfg->transcoder_remote_base);
+	endp->trans_net.rtp_port = htons(port);
+	endp->trans_net.rtcp_port = htons(port + 1);
+}
+
+static void delete_transcoder(struct mgcp_endpoint *endp)
+{
+	int in_endp = ENDPOINT_NUMBER(endp);
+	int out_endp = endp_back_channel(in_endp);
+
+	if (endp->type != MGCP_RTP_TRANSCODED)
+		return;
+
+	send_dlcx(endp, in_endp);
+	send_dlcx(endp, out_endp);
+}
+
+int mgcp_reset_transcoder(struct mgcp_config *cfg)
+{
+	if (!cfg->transcoder_ip)
+		return 0;
+
+	static const char mgcp_reset[] = {
+	    "RSIP 1 13@mgw MGCP 1.0\r\n"
+	};
+
+	return send_trans(cfg, mgcp_reset, sizeof mgcp_reset -1);
+}
+
+void mgcp_format_stats(struct mgcp_endpoint *endp, char *msg, size_t size)
+{
+	uint32_t expected, jitter;
+	int ploss;
+	int nchars;
+	mgcp_state_calc_loss(&endp->net_state, &endp->net_end,
+				&expected, &ploss);
+	jitter = mgcp_state_calc_jitter(&endp->net_state);
+
+	nchars = snprintf(msg, size,
+			  "\r\nP: PS=%u, OS=%u, PR=%u, OR=%u, PL=%d, JI=%u",
+			  endp->bts_end.packets, endp->bts_end.octets,
+			  endp->net_end.packets, endp->net_end.octets,
+			  ploss, jitter);
+	if (nchars < 0 || nchars >= size)
+		goto truncate;
+
+	msg += nchars;
+	size -= nchars;
+
+	/* Error Counter */
+	nchars = snprintf(msg, size,
+			  "\r\nX-Osmo-CP: EC TIS=%u, TOS=%u, TIR=%u, TOR=%u",
+			  endp->net_state.in_stream.err_ts_counter,
+			  endp->net_state.out_stream.err_ts_counter,
+			  endp->bts_state.in_stream.err_ts_counter,
+			  endp->bts_state.out_stream.err_ts_counter);
+	if (nchars < 0 || nchars >= size)
+		goto truncate;
+
+	msg += nchars;
+	size -= nchars;
+
+	if (endp->osmux.state == OSMUX_STATE_ENABLED) {
+		snprintf(msg, size,
+			 "\r\nX-Osmux-ST: CR=%u, BR=%u",
+			 endp->osmux.stats.chunks,
+			 endp->osmux.stats.octets);
+	}
+truncate:
+	msg[size - 1] = '\0';
+}
+
+int mgcp_parse_stats(struct msgb *msg, uint32_t *ps, uint32_t *os,
+		uint32_t *pr, uint32_t *_or, int *loss, uint32_t *jitter)
+{
+	char *line, *save;
+	int rc;
+
+	/* initialize with bad values */
+	*ps = *os = *pr = *_or = *jitter = UINT_MAX;
+	*loss = INT_MAX;
+
+
+	line = strtok_r((char *) msg->l2h, "\r\n", &save);
+	if (!line)
+		return -1;
+
+	/* this can only parse the message that is created above... */
+	for_each_non_empty_line(line, save) {
+		switch (line[0]) {
+		case 'P':
+			rc = sscanf(line, "P: PS=%u, OS=%u, PR=%u, OR=%u, PL=%d, JI=%u",
+					ps, os, pr, _or, loss, jitter);
+			return rc == 6 ? 0 : -1;
+		}
+	}
+
+	return -1;
+}
diff --git a/src/libosmo-mgcp/mgcp_sdp.c b/src/libosmo-mgcp/mgcp_sdp.c
new file mode 100644
index 0000000..055e1af
--- /dev/null
+++ b/src/libosmo-mgcp/mgcp_sdp.c
@@ -0,0 +1,305 @@
+/*
+ * Some SDP file parsing...
+ *
+ * (C) 2009-2015 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2009-2014 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/>.
+ *
+ */
+
+#include <osmocom/legacy_mgcp/mgcp.h>
+#include <osmocom/legacy_mgcp/mgcp_internal.h>
+
+#include <errno.h>
+
+struct sdp_rtp_map {
+	/* the type */
+	int payload_type;
+	/* null, static or later dynamic codec name */
+	char *codec_name;
+	/* A pointer to the original line for later parsing */
+	char *map_line;
+
+	int rate;
+	int channels;
+};
+
+int mgcp_set_audio_info(void *ctx, struct mgcp_rtp_codec *codec,
+			int payload_type, const char *audio_name)
+{
+	int rate = codec->rate;
+	int channels = codec->channels;
+	char audio_codec[64];
+
+	talloc_free(codec->subtype_name);
+	codec->subtype_name = NULL;
+	talloc_free(codec->audio_name);
+	codec->audio_name = NULL;
+
+	if (payload_type != PTYPE_UNDEFINED)
+		codec->payload_type = payload_type;
+
+	if (!audio_name) {
+		switch (payload_type) {
+		case 0: audio_name = "PCMU/8000/1"; break;
+		case 3: audio_name = "GSM/8000/1"; break;
+		case 8: audio_name = "PCMA/8000/1"; break;
+		case 18: audio_name = "G729/8000/1"; break;
+		default:
+			 /* Payload type is unknown, don't change rate and
+			  * channels. */
+			 /* TODO: return value? */
+			 return 0;
+		}
+	}
+
+	if (sscanf(audio_name, "%63[^/]/%d/%d",
+		   audio_codec, &rate, &channels) < 1)
+		return -EINVAL;
+
+	codec->rate = rate;
+	codec->channels = channels;
+	codec->subtype_name = talloc_strdup(ctx, audio_codec);
+	codec->audio_name = talloc_strdup(ctx, audio_name);
+
+	if (!strcmp(audio_codec, "G729")) {
+		codec->frame_duration_num = 10;
+		codec->frame_duration_den = 1000;
+	} else {
+		codec->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM;
+		codec->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN;
+	}
+
+	if (payload_type < 0) {
+		payload_type = 96;
+		if (rate == 8000 && channels == 1) {
+			if (!strcmp(audio_codec, "GSM"))
+				payload_type = 3;
+			else if (!strcmp(audio_codec, "PCMA"))
+				payload_type = 8;
+			else if (!strcmp(audio_codec, "PCMU"))
+				payload_type = 0;
+			else if (!strcmp(audio_codec, "G729"))
+				payload_type = 18;
+		}
+
+		codec->payload_type = payload_type;
+	}
+
+	if (channels != 1)
+		LOGP(DLMGCP, LOGL_NOTICE,
+		     "Channels != 1 in SDP: '%s'\n", audio_name);
+
+	return 0;
+}
+
+void codecs_initialize(void *ctx, struct sdp_rtp_map *codecs, int used)
+{
+	int i;
+
+	for (i = 0; i < used; ++i) {
+		switch (codecs[i].payload_type) {
+		case 0:
+			codecs[i].codec_name = "PCMU";
+			codecs[i].rate = 8000;
+			codecs[i].channels = 1;
+			break;
+		case 3:
+			codecs[i].codec_name = "GSM";
+			codecs[i].rate = 8000;
+			codecs[i].channels = 1;
+			break;
+		case 8:
+			codecs[i].codec_name = "PCMA";
+			codecs[i].rate = 8000;
+			codecs[i].channels = 1;
+			break;
+		case 18:
+			codecs[i].codec_name = "G729";
+			codecs[i].rate = 8000;
+			codecs[i].channels = 1;
+			break;
+		}
+	}
+}
+
+void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used, int payload, char *audio_name)
+{
+	int i;
+
+	for (i = 0; i < used; ++i) {
+		char audio_codec[64];
+		int rate = -1;
+		int channels = -1;
+		if (codecs[i].payload_type != payload)
+			continue;
+		if (sscanf(audio_name, "%63[^/]/%d/%d",
+				audio_codec, &rate, &channels) < 1) {
+			LOGP(DLMGCP, LOGL_ERROR, "Failed to parse '%s'\n", audio_name);
+			continue;
+		}
+
+		codecs[i].map_line = talloc_strdup(ctx, audio_name);
+		codecs[i].codec_name = talloc_strdup(ctx, audio_codec);
+		codecs[i].rate = rate;
+		codecs[i].channels = channels;
+		return;
+	}
+
+	LOGP(DLMGCP, LOGL_ERROR, "Unconfigured PT(%d) with %s\n", payload, audio_name);
+}
+
+int is_codec_compatible(struct mgcp_endpoint *endp, struct sdp_rtp_map *codec)
+{
+	char *bts_codec;
+	char audio_codec[64];
+
+	if (!codec->codec_name)
+		return 0;
+
+	/*
+	 * GSM, GSM/8000 and GSM/8000/1 should all be compatible.. let's go
+	 * by name first.
+	 */
+	bts_codec = endp->tcfg->audio_name;
+	if (sscanf(bts_codec, "%63[^/]/%*d/%*d", audio_codec) < 1)
+		return 0;
+
+	return strcasecmp(audio_codec, codec->codec_name) == 0;
+}
+
+int mgcp_parse_sdp_data(struct mgcp_endpoint *endp, struct mgcp_rtp_end *rtp, struct mgcp_parse_data *p)
+{
+	struct sdp_rtp_map codecs[10];
+	int codecs_used = 0;
+	char *line;
+	int maxptime = -1;
+	int i;
+	int codecs_assigned = 0;
+	void *tmp_ctx = talloc_new(NULL);
+
+	memset(&codecs, 0, sizeof(codecs));
+
+	for_each_line(line, p->save) {
+		switch (line[0]) {
+		case 'o':
+		case 's':
+		case 't':
+		case 'v':
+			/* skip these SDP attributes */
+			break;
+		case 'a': {
+			int payload;
+			int ptime, ptime2 = 0;
+			char audio_name[64];
+
+
+			if (sscanf(line, "a=rtpmap:%d %63s",
+				   &payload, audio_name) == 2) {
+				codecs_update(tmp_ctx, codecs, codecs_used, payload, audio_name);
+			} else if (sscanf(line, "a=ptime:%d-%d",
+					  &ptime, &ptime2) >= 1) {
+				if (ptime2 > 0 && ptime2 != ptime)
+					rtp->packet_duration_ms = 0;
+				else
+					rtp->packet_duration_ms = ptime;
+			} else if (sscanf(line, "a=maxptime:%d", &ptime2) == 1) {
+				maxptime = ptime2;
+			}
+			break;
+		}
+		case 'm': {
+			int port, rc;
+
+			rc = sscanf(line, "m=audio %d RTP/AVP %d %d %d %d %d %d %d %d %d %d",
+					&port,
+					&codecs[0].payload_type,
+					&codecs[1].payload_type,
+					&codecs[2].payload_type,
+					&codecs[3].payload_type,
+					&codecs[4].payload_type,
+					&codecs[5].payload_type,
+					&codecs[6].payload_type,
+					&codecs[7].payload_type,
+					&codecs[8].payload_type,
+					&codecs[9].payload_type);
+			if (rc >= 2) {
+				rtp->rtp_port = htons(port);
+				rtp->rtcp_port = htons(port + 1);
+				codecs_used = rc - 1;
+				codecs_initialize(tmp_ctx, codecs, codecs_used);
+			}
+			break;
+		}
+		case 'c': {
+			char ipv4[16];
+
+			if (sscanf(line, "c=IN IP4 %15s", ipv4) == 1) {
+				inet_aton(ipv4, &rtp->addr);
+			}
+			break;
+		}
+		default:
+			if (p->endp)
+				LOGP(DLMGCP, LOGL_NOTICE,
+				     "Unhandled SDP option: '%c'/%d on 0x%x\n",
+				     line[0], line[0], ENDPOINT_NUMBER(p->endp));
+			else
+				LOGP(DLMGCP, LOGL_NOTICE,
+				     "Unhandled SDP option: '%c'/%d\n",
+				     line[0], line[0]);
+			break;
+		}
+	}
+
+	/* Now select the primary and alt_codec */
+	for (i = 0; i < codecs_used && codecs_assigned < 2; ++i) {
+		struct mgcp_rtp_codec *codec = codecs_assigned == 0 ?
+					&rtp->codec : &rtp->alt_codec;
+
+		if (endp->tcfg->no_audio_transcoding &&
+			!is_codec_compatible(endp, &codecs[i])) {
+			LOGP(DLMGCP, LOGL_NOTICE, "Skipping codec %s\n",
+				codecs[i].codec_name);
+			continue;
+		}
+
+		mgcp_set_audio_info(p->cfg, codec,
+					codecs[i].payload_type,
+					codecs[i].map_line);
+		codecs_assigned += 1;
+	}
+
+	if (codecs_assigned > 0) {
+		/* TODO/XXX: Store this per codec and derive it on use */
+		if (maxptime >= 0 && maxptime * rtp->codec.frame_duration_den >
+				rtp->codec.frame_duration_num * 1500) {
+			/* more than 1 frame */
+			rtp->packet_duration_ms = 0;
+		}
+
+		LOGP(DLMGCP, LOGL_NOTICE,
+		     "Got media info via SDP: port %d, payload %d (%s), "
+		     "duration %d, addr %s\n",
+		     ntohs(rtp->rtp_port), rtp->codec.payload_type,
+		     rtp->codec.subtype_name ? rtp->codec.subtype_name : "unknown",
+		     rtp->packet_duration_ms, inet_ntoa(rtp->addr));
+	}
+
+	talloc_free(tmp_ctx);
+	return codecs_assigned > 0;
+}
+
diff --git a/src/libosmo-mgcp/mgcp_transcode.c b/src/libosmo-mgcp/mgcp_transcode.c
new file mode 100644
index 0000000..cbabdd1
--- /dev/null
+++ b/src/libosmo-mgcp/mgcp_transcode.c
@@ -0,0 +1,611 @@
+/*
+ * (C) 2014 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/>.
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+
+#include "g711common.h"
+
+#include <osmocom/legacy_mgcp/mgcp.h>
+#include <osmocom/legacy_mgcp/mgcp_internal.h>
+#include <osmocom/legacy_mgcp/mgcp_transcode.h>
+
+#include <osmocom/core/talloc.h>
+#include <osmocom/netif/rtp.h>
+
+int mgcp_transcoding_get_frame_size(void *state_, int nsamples, int dst)
+{
+	struct mgcp_process_rtp_state *state = state_;
+	if (dst)
+		return (nsamples >= 0 ?
+			nsamples / state->dst_samples_per_frame :
+			1) * state->dst_frame_size;
+	else
+		return (nsamples >= 0 ?
+			nsamples / state->src_samples_per_frame :
+			1) * state->src_frame_size;
+}
+
+static enum audio_format get_audio_format(const struct mgcp_rtp_codec *codec)
+{
+	if (codec->subtype_name) {
+		if (!strcasecmp("GSM", codec->subtype_name))
+			return AF_GSM;
+		if (!strcasecmp("PCMA", codec->subtype_name))
+			return AF_PCMA;
+		if (!strcasecmp("PCMU", codec->subtype_name))
+			return AF_PCMU;
+#ifdef HAVE_BCG729
+		if (!strcasecmp("G729", codec->subtype_name))
+			return AF_G729;
+#endif
+		if (!strcasecmp("L16", codec->subtype_name))
+			return AF_L16;
+	}
+
+	switch (codec->payload_type) {
+	case 0 /* PCMU */:
+		return AF_PCMU;
+	case 3 /* GSM */:
+		return AF_GSM;
+	case 8 /* PCMA */:
+		return AF_PCMA;
+#ifdef HAVE_BCG729
+	case 18 /* G.729 */:
+		return AF_G729;
+#endif
+	case 11 /* L16 */:
+		return AF_L16;
+	default:
+		return AF_INVALID;
+	}
+}
+
+static void l16_encode(short *sample, unsigned char *buf, size_t n)
+{
+	for (; n > 0; --n, ++sample, buf += 2) {
+		buf[0] = sample[0] >> 8;
+		buf[1] = sample[0] & 0xff;
+	}
+}
+
+static void l16_decode(unsigned char *buf, short *sample, size_t n)
+{
+	for (; n > 0; --n, ++sample, buf += 2)
+		sample[0] = ((short)buf[0] << 8) | buf[1];
+}
+
+static void alaw_encode(short *sample, unsigned char *buf, size_t n)
+{
+	for (; n > 0; --n)
+		*(buf++) = s16_to_alaw(*(sample++));
+}
+
+static void alaw_decode(unsigned char *buf, short *sample, size_t n)
+{
+	for (; n > 0; --n)
+		*(sample++) = alaw_to_s16(*(buf++));
+}
+
+static void ulaw_encode(short *sample, unsigned char *buf, size_t n)
+{
+	for (; n > 0; --n)
+		*(buf++) = s16_to_ulaw(*(sample++));
+}
+
+static void ulaw_decode(unsigned char *buf, short *sample, size_t n)
+{
+	for (; n > 0; --n)
+		*(sample++) = ulaw_to_s16(*(buf++));
+}
+
+static int processing_state_destructor(struct mgcp_process_rtp_state *state)
+{
+	switch (state->src_fmt) {
+	case AF_GSM:
+		if (state->src.gsm_handle)
+			gsm_destroy(state->src.gsm_handle);
+		break;
+#ifdef HAVE_BCG729
+	case AF_G729:
+		if (state->src.g729_dec)
+			closeBcg729DecoderChannel(state->src.g729_dec);
+		break;
+#endif
+	default:
+		break;
+	}
+	switch (state->dst_fmt) {
+	case AF_GSM:
+		if (state->dst.gsm_handle)
+			gsm_destroy(state->dst.gsm_handle);
+		break;
+#ifdef HAVE_BCG729
+	case AF_G729:
+		if (state->dst.g729_enc)
+			closeBcg729EncoderChannel(state->dst.g729_enc);
+		break;
+#endif
+	default:
+		break;
+	}
+	return 0;
+}
+
+int mgcp_transcoding_setup(struct mgcp_endpoint *endp,
+			   struct mgcp_rtp_end *dst_end,
+			   struct mgcp_rtp_end *src_end)
+{
+	struct mgcp_process_rtp_state *state;
+	enum audio_format src_fmt, dst_fmt;
+	const struct mgcp_rtp_codec *dst_codec = &dst_end->codec;
+
+	/* cleanup first */
+	if (dst_end->rtp_process_data) {
+		talloc_free(dst_end->rtp_process_data);
+		dst_end->rtp_process_data = NULL;
+	}
+
+	if (!src_end)
+		return 0;
+
+	const struct mgcp_rtp_codec *src_codec = &src_end->codec;
+
+	if (endp->tcfg->no_audio_transcoding) {
+		LOGP(DLMGCP, LOGL_NOTICE,
+			"Transcoding disabled on endpoint 0x%x\n",
+			ENDPOINT_NUMBER(endp));
+		return 0;
+	}
+
+	src_fmt = get_audio_format(src_codec);
+	dst_fmt = get_audio_format(dst_codec);
+
+	LOGP(DLMGCP, LOGL_ERROR,
+	     "Checking transcoding: %s (%d) -> %s (%d)\n",
+	     src_codec->subtype_name, src_codec->payload_type,
+	     dst_codec->subtype_name, dst_codec->payload_type);
+
+	if (src_fmt == AF_INVALID || dst_fmt == AF_INVALID) {
+		if (!src_codec->subtype_name || !dst_codec->subtype_name)
+			/* Not enough info, do nothing */
+			return 0;
+
+		if (strcasecmp(src_codec->subtype_name, dst_codec->subtype_name) == 0)
+			/* Nothing to do */
+			return 0;
+
+		LOGP(DLMGCP, LOGL_ERROR,
+		     "Cannot transcode: %s codec not supported (%s -> %s).\n",
+		     src_fmt != AF_INVALID ? "destination" : "source",
+		     src_codec->audio_name, dst_codec->audio_name);
+		return -EINVAL;
+	}
+
+	if (src_codec->rate && dst_codec->rate && src_codec->rate != dst_codec->rate) {
+		LOGP(DLMGCP, LOGL_ERROR,
+		     "Cannot transcode: rate conversion (%d -> %d) not supported.\n",
+		     src_codec->rate, dst_codec->rate);
+		return -EINVAL;
+	}
+
+	state = talloc_zero(endp->tcfg->cfg, struct mgcp_process_rtp_state);
+	talloc_set_destructor(state, processing_state_destructor);
+	dst_end->rtp_process_data = state;
+
+	state->src_fmt = src_fmt;
+
+	switch (state->src_fmt) {
+	case AF_L16:
+	case AF_S16:
+		state->src_frame_size = 80 * sizeof(short);
+		state->src_samples_per_frame = 80;
+		break;
+	case AF_GSM:
+		state->src_frame_size = sizeof(gsm_frame);
+		state->src_samples_per_frame = 160;
+		state->src.gsm_handle = gsm_create();
+		if (!state->src.gsm_handle) {
+			LOGP(DLMGCP, LOGL_ERROR,
+			     "Failed to initialize GSM decoder.\n");
+			return -EINVAL;
+		}
+		break;
+#ifdef HAVE_BCG729
+	case AF_G729:
+		state->src_frame_size = 10;
+		state->src_samples_per_frame = 80;
+		state->src.g729_dec = initBcg729DecoderChannel();
+		if (!state->src.g729_dec) {
+			LOGP(DLMGCP, LOGL_ERROR,
+			     "Failed to initialize G.729 decoder.\n");
+			return -EINVAL;
+		}
+		break;
+#endif
+	case AF_PCMU:
+	case AF_PCMA:
+		state->src_frame_size = 80;
+		state->src_samples_per_frame = 80;
+		break;
+	default:
+		break;
+	}
+
+	state->dst_fmt = dst_fmt;
+
+	switch (state->dst_fmt) {
+	case AF_L16:
+	case AF_S16:
+		state->dst_frame_size = 80*sizeof(short);
+		state->dst_samples_per_frame = 80;
+		break;
+	case AF_GSM:
+		state->dst_frame_size = sizeof(gsm_frame);
+		state->dst_samples_per_frame = 160;
+		state->dst.gsm_handle = gsm_create();
+		if (!state->dst.gsm_handle) {
+			LOGP(DLMGCP, LOGL_ERROR,
+			     "Failed to initialize GSM encoder.\n");
+			return -EINVAL;
+		}
+		break;
+#ifdef HAVE_BCG729
+	case AF_G729:
+		state->dst_frame_size = 10;
+		state->dst_samples_per_frame = 80;
+		state->dst.g729_enc = initBcg729EncoderChannel();
+		if (!state->dst.g729_enc) {
+			LOGP(DLMGCP, LOGL_ERROR,
+			     "Failed to initialize G.729 decoder.\n");
+			return -EINVAL;
+		}
+		break;
+#endif
+	case AF_PCMU:
+	case AF_PCMA:
+		state->dst_frame_size = 80;
+		state->dst_samples_per_frame = 80;
+		break;
+	default:
+		break;
+	}
+
+	if (dst_end->force_output_ptime)
+		state->dst_packet_duration = mgcp_rtp_packet_duration(endp, dst_end);
+
+	LOGP(DLMGCP, LOGL_INFO,
+	     "Initialized RTP processing on: 0x%x "
+	     "conv: %d (%d, %d, %s) -> %d (%d, %d, %s)\n",
+	     ENDPOINT_NUMBER(endp),
+	     src_fmt, src_codec->payload_type, src_codec->rate, src_end->fmtp_extra,
+	     dst_fmt, dst_codec->payload_type, dst_codec->rate, dst_end->fmtp_extra);
+
+	return 0;
+}
+
+void mgcp_transcoding_net_downlink_format(struct mgcp_endpoint *endp,
+					  int *payload_type,
+					  const char**audio_name,
+					  const char**fmtp_extra)
+{
+	struct mgcp_process_rtp_state *state = endp->net_end.rtp_process_data;
+	struct mgcp_rtp_codec *net_codec = &endp->net_end.codec;
+	struct mgcp_rtp_codec *bts_codec = &endp->bts_end.codec;
+
+	if (!state || net_codec->payload_type < 0) {
+		*payload_type = bts_codec->payload_type;
+		*audio_name = bts_codec->audio_name;
+		*fmtp_extra = endp->bts_end.fmtp_extra;
+		return;
+	}
+
+	*payload_type = net_codec->payload_type;
+	*audio_name = net_codec->audio_name;
+	*fmtp_extra = endp->net_end.fmtp_extra;
+}
+
+static int decode_audio(struct mgcp_process_rtp_state *state,
+			uint8_t **src, size_t *nbytes)
+{
+	while (*nbytes >= state->src_frame_size) {
+		if (state->sample_cnt + state->src_samples_per_frame > ARRAY_SIZE(state->samples)) {
+			LOGP(DLMGCP, LOGL_ERROR,
+			     "Sample buffer too small: %zu > %zu.\n",
+			     state->sample_cnt + state->src_samples_per_frame,
+			     ARRAY_SIZE(state->samples));
+			return -ENOSPC;
+		}
+		switch (state->src_fmt) {
+		case AF_GSM:
+			if (gsm_decode(state->src.gsm_handle,
+				       (gsm_byte *)*src, state->samples + state->sample_cnt) < 0) {
+				LOGP(DLMGCP, LOGL_ERROR,
+				     "Failed to decode GSM.\n");
+				return -EINVAL;
+			}
+			break;
+#ifdef HAVE_BCG729
+		case AF_G729:
+			bcg729Decoder(state->src.g729_dec, *src, 0, state->samples + state->sample_cnt);
+			break;
+#endif
+		case AF_PCMU:
+			ulaw_decode(*src, state->samples + state->sample_cnt,
+				    state->src_samples_per_frame);
+			break;
+		case AF_PCMA:
+			alaw_decode(*src, state->samples + state->sample_cnt,
+				    state->src_samples_per_frame);
+			break;
+		case AF_S16:
+			memmove(state->samples + state->sample_cnt, *src,
+				state->src_frame_size);
+			break;
+		case AF_L16:
+			l16_decode(*src, state->samples + state->sample_cnt,
+				   state->src_samples_per_frame);
+			break;
+		default:
+			break;
+		}
+		*src        += state->src_frame_size;
+		*nbytes     -= state->src_frame_size;
+		state->sample_cnt += state->src_samples_per_frame;
+	}
+	return 0;
+}
+
+static int encode_audio(struct mgcp_process_rtp_state *state,
+			uint8_t *dst, size_t buf_size, size_t max_samples)
+{
+	int nbytes = 0;
+	size_t nsamples = 0;
+	/* Encode samples into dst */
+	while (nsamples + state->dst_samples_per_frame <= max_samples) {
+		if (nbytes + state->dst_frame_size > buf_size) {
+			if (nbytes > 0)
+				break;
+
+			/* Not even one frame fits into the buffer */
+			LOGP(DLMGCP, LOGL_INFO,
+			     "Encoding (RTP) buffer too small: %zu > %zu.\n",
+			     nbytes + state->dst_frame_size, buf_size);
+			return -ENOSPC;
+		}
+		switch (state->dst_fmt) {
+		case AF_GSM:
+			gsm_encode(state->dst.gsm_handle,
+				   state->samples + state->sample_offs, dst);
+			break;
+#ifdef HAVE_BCG729
+		case AF_G729:
+			bcg729Encoder(state->dst.g729_enc,
+				      state->samples + state->sample_offs, dst);
+			break;
+#endif
+		case AF_PCMU:
+			ulaw_encode(state->samples + state->sample_offs, dst,
+				    state->src_samples_per_frame);
+			break;
+		case AF_PCMA:
+			alaw_encode(state->samples + state->sample_offs, dst,
+				    state->src_samples_per_frame);
+			break;
+		case AF_S16:
+			memmove(dst, state->samples + state->sample_offs,
+				state->dst_frame_size);
+			break;
+		case AF_L16:
+			l16_encode(state->samples + state->sample_offs, dst,
+				   state->src_samples_per_frame);
+			break;
+		default:
+			break;
+		}
+		dst        += state->dst_frame_size;
+		nbytes     += state->dst_frame_size;
+		state->sample_offs += state->dst_samples_per_frame;
+		nsamples   += state->dst_samples_per_frame;
+	}
+	state->sample_cnt -= nsamples;
+	return nbytes;
+}
+
+static struct mgcp_rtp_end *source_for_dest(struct mgcp_endpoint *endp,
+					struct mgcp_rtp_end *dst_end)
+{
+	if (&endp->bts_end == dst_end)
+		return &endp->net_end;
+	else if (&endp->net_end == dst_end)
+		return &endp->bts_end;
+	OSMO_ASSERT(0);
+}
+
+/*
+ * With some modems we get offered multiple codecs
+ * and we have selected one of them. It might not
+ * be the right one and we need to detect this with
+ * the first audio packets. One difficulty is that
+ * we patch the rtp payload type in place, so we
+ * need to discuss this.
+ */
+struct mgcp_process_rtp_state *check_transcode_state(
+				struct mgcp_endpoint *endp,
+				struct mgcp_rtp_end *dst_end,
+				struct rtp_hdr *rtp_hdr)
+{
+	struct mgcp_rtp_end *src_end;
+
+	/* Only deal with messages from net to bts */
+	if (&endp->bts_end != dst_end)
+		goto done;
+
+	src_end = source_for_dest(endp, dst_end);
+
+	/* Already patched */
+	if (rtp_hdr->payload_type == dst_end->codec.payload_type)
+		goto done;
+	/* The payload we expect */
+	if (rtp_hdr->payload_type == src_end->codec.payload_type)
+		goto done;
+	/* The matching alternate payload type? Then switch */
+	if (rtp_hdr->payload_type == src_end->alt_codec.payload_type) {
+		struct mgcp_config *cfg = endp->cfg;
+		struct mgcp_rtp_codec tmp_codec = src_end->alt_codec;
+		src_end->alt_codec = src_end->codec;
+		src_end->codec = tmp_codec;
+		cfg->setup_rtp_processing_cb(endp, &endp->net_end, &endp->bts_end);
+		cfg->setup_rtp_processing_cb(endp, &endp->bts_end, &endp->net_end);
+	}
+
+done:
+	return dst_end->rtp_process_data;
+}
+
+int mgcp_transcoding_process_rtp(struct mgcp_endpoint *endp,
+				struct mgcp_rtp_end *dst_end,
+			     char *data, int *len, int buf_size)
+{
+	struct mgcp_process_rtp_state *state;
+	const size_t rtp_hdr_size = sizeof(struct rtp_hdr);
+	struct rtp_hdr *rtp_hdr = (struct rtp_hdr *) data;
+	char *payload_data = (char *) &rtp_hdr->data[0];
+	int payload_len = *len - rtp_hdr_size;
+	uint8_t *src = (uint8_t *)payload_data;
+	uint8_t *dst = (uint8_t *)payload_data;
+	size_t nbytes = payload_len;
+	size_t nsamples;
+	size_t max_samples;
+	uint32_t ts_no;
+	int rc;
+
+	state = check_transcode_state(endp, dst_end, rtp_hdr);
+	if (!state)
+		return 0;
+
+	if (state->src_fmt == state->dst_fmt) {
+		if (!state->dst_packet_duration)
+			return 0;
+
+		/* TODO: repackage without transcoding */
+	}
+
+	/* If the remaining samples do not fit into a fixed ptime,
+	 * a) discard them, if the next packet is much later
+	 * b) add silence and * send it, if the current packet is not
+	 *    yet too late
+	 * c) append the sample data, if the timestamp matches exactly
+	 */
+
+	/* TODO: check payload type (-> G.711 comfort noise) */
+
+	if (payload_len > 0) {
+		ts_no = ntohl(rtp_hdr->timestamp);
+		if (!state->is_running) {
+			state->next_seq = ntohs(rtp_hdr->sequence);
+			state->next_time = ts_no;
+			state->is_running = 1;
+		}
+
+
+		if (state->sample_cnt > 0) {
+			int32_t delta = ts_no - state->next_time;
+			/* TODO: check sequence? reordering? packet loss? */
+
+			if (delta > state->sample_cnt) {
+				/* There is a time gap between the last packet
+				 * and the current one. Just discard the
+				 * partial data that is left in the buffer.
+				 * TODO: This can be improved by adding silence
+				 * instead if the delta is small enough.
+				 */
+				LOGP(DLMGCP, LOGL_NOTICE,
+					"0x%x dropping sample buffer due delta=%d sample_cnt=%zu\n",
+					ENDPOINT_NUMBER(endp), delta, state->sample_cnt);
+				state->sample_cnt = 0;
+				state->next_time = ts_no;
+			} else if (delta < 0) {
+				LOGP(DLMGCP, LOGL_NOTICE,
+				     "RTP time jumps backwards, delta = %d, "
+				     "discarding buffered samples\n",
+				     delta);
+				state->sample_cnt = 0;
+				state->sample_offs = 0;
+				return -EAGAIN;
+			}
+
+			/* Make sure the samples start without offset */
+			if (state->sample_offs && state->sample_cnt)
+				memmove(&state->samples[0],
+					&state->samples[state->sample_offs],
+					state->sample_cnt *
+					sizeof(state->samples[0]));
+		}
+
+		state->sample_offs = 0;
+
+		/* Append decoded audio to samples */
+		decode_audio(state, &src, &nbytes);
+
+		if (nbytes > 0)
+			LOGP(DLMGCP, LOGL_NOTICE,
+			     "Skipped audio frame in RTP packet: %zu octets\n",
+			     nbytes);
+	} else
+		ts_no = state->next_time;
+
+	if (state->sample_cnt < state->dst_packet_duration)
+		return -EAGAIN;
+
+	max_samples =
+		state->dst_packet_duration ?
+		state->dst_packet_duration : state->sample_cnt;
+
+	nsamples = state->sample_cnt;
+
+	rc = encode_audio(state, dst, buf_size, max_samples);
+	/*
+	 * There were no samples to encode?
+	 * TODO: how does this work for comfort noise?
+	 */
+	if (rc == 0)
+		return -ENOMSG;
+	/* Any other error during the encoding */
+	if (rc < 0)
+		return rc;
+
+	nsamples -= state->sample_cnt;
+
+	*len = rtp_hdr_size + rc;
+	rtp_hdr->sequence = htons(state->next_seq);
+	rtp_hdr->timestamp = htonl(ts_no);
+
+	state->next_seq += 1;
+	state->next_time = ts_no + nsamples;
+
+	/*
+	 * XXX: At this point we should always have consumed
+	 * samples. So doing OSMO_ASSERT(nsamples > 0) and returning
+	 * rtp_hdr_size should be fine.
+	 */
+	return nsamples ? rtp_hdr_size : 0;
+}
diff --git a/src/libosmo-mgcp/mgcp_vty.c b/src/libosmo-mgcp/mgcp_vty.c
new file mode 100644
index 0000000..a48e205
--- /dev/null
+++ b/src/libosmo-mgcp/mgcp_vty.c
@@ -0,0 +1,1555 @@
+/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
+/* The protocol implementation */
+
+/*
+ * (C) 2009-2014 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2009-2011 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/>.
+ *
+ */
+
+
+#include <osmocom/core/talloc.h>
+
+#include <osmocom/legacy_mgcp/mgcp.h>
+#include <osmocom/legacy_mgcp/mgcp_internal.h>
+#include <osmocom/legacy_mgcp/vty.h>
+
+#include <string.h>
+
+#define RTCP_OMIT_STR "Drop RTCP packets in both directions\n"
+#define RTP_PATCH_STR "Modify RTP packet header in both directions\n"
+#define RTP_KEEPALIVE_STR "Send dummy UDP packet to net RTP destination\n"
+
+static struct mgcp_config *g_cfg = NULL;
+
+static struct mgcp_trunk_config *find_trunk(struct mgcp_config *cfg, int nr)
+{
+	struct mgcp_trunk_config *trunk;
+
+	if (nr == 0)
+		trunk = &cfg->trunk;
+	else
+		trunk = mgcp_trunk_num(cfg, nr);
+
+	return trunk;
+}
+
+/*
+ * vty code for mgcp below
+ */
+struct cmd_node mgcp_node = {
+	MGCP_NODE,
+	"%s(config-mgcp)# ",
+	1,
+};
+
+struct cmd_node trunk_node = {
+	TRUNK_NODE,
+	"%s(config-mgcp-trunk)# ",
+	1,
+};
+
+static int config_write_mgcp(struct vty *vty)
+{
+	vty_out(vty, "mgcp%s", VTY_NEWLINE);
+	if (g_cfg->local_ip)
+		vty_out(vty, "  local ip %s%s", g_cfg->local_ip, VTY_NEWLINE);
+	if (g_cfg->bts_ip && strlen(g_cfg->bts_ip) != 0)
+		vty_out(vty, "  bts ip %s%s", g_cfg->bts_ip, VTY_NEWLINE);
+	vty_out(vty, "  bind ip %s%s", g_cfg->source_addr, VTY_NEWLINE);
+	vty_out(vty, "  bind port %u%s", g_cfg->source_port, VTY_NEWLINE);
+
+	if (g_cfg->bts_ports.mode == PORT_ALLOC_STATIC)
+		vty_out(vty, "  rtp bts-base %u%s", g_cfg->bts_ports.base_port, VTY_NEWLINE);
+	else
+		vty_out(vty, "  rtp bts-range %u %u%s",
+			g_cfg->bts_ports.range_start, g_cfg->bts_ports.range_end, VTY_NEWLINE);
+	if (g_cfg->bts_ports.bind_addr)
+		vty_out(vty, "  rtp bts-bind-ip %s%s", g_cfg->bts_ports.bind_addr, VTY_NEWLINE);
+
+	if (g_cfg->net_ports.mode == PORT_ALLOC_STATIC)
+		vty_out(vty, "  rtp net-base %u%s", g_cfg->net_ports.base_port, VTY_NEWLINE);
+	else
+		vty_out(vty, "  rtp net-range %u %u%s",
+			g_cfg->net_ports.range_start, g_cfg->net_ports.range_end, VTY_NEWLINE);
+	if (g_cfg->net_ports.bind_addr)
+		vty_out(vty, "  rtp net-bind-ip %s%s", g_cfg->net_ports.bind_addr, 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)
+		vty_out(vty, "  rtp keep-alive once%s", VTY_NEWLINE);
+	else if (g_cfg->trunk.keepalive_interval)
+		vty_out(vty, "  rtp keep-alive %d%s",
+			g_cfg->trunk.keepalive_interval, VTY_NEWLINE);
+	else
+		vty_out(vty, "  no rtp keep-alive%s", VTY_NEWLINE);
+
+	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) {
+		vty_out(vty, "  %srtp-patch ssrc%s",
+			g_cfg->trunk.force_constant_ssrc ? "" : "no ", VTY_NEWLINE);
+		vty_out(vty, "  %srtp-patch timestamp%s",
+			g_cfg->trunk.force_aligned_timing ? "" : "no ", VTY_NEWLINE);
+	} else
+		vty_out(vty, "  no rtp-patch%s", VTY_NEWLINE);
+	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)
+		vty_out(vty, "  sdp audio-payload name %s%s",
+			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);
+	vty_out(vty, "  %ssdp audio-payload send-ptime%s",
+		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);
+	vty_out(vty, "  number endpoints %u%s", g_cfg->trunk.number_endpoints - 1, VTY_NEWLINE);
+	vty_out(vty, "  %sallow-transcoding%s",
+		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);
+	if (g_cfg->transcoder_ip)
+		vty_out(vty, "  transcoder-mgw %s%s", g_cfg->transcoder_ip, VTY_NEWLINE);
+
+	if (g_cfg->transcoder_ports.mode == PORT_ALLOC_STATIC)
+		vty_out(vty, "  rtp transcoder-base %u%s", g_cfg->transcoder_ports.base_port, VTY_NEWLINE);
+	else
+		vty_out(vty, "  rtp transcoder-range %u %u%s",
+			g_cfg->transcoder_ports.range_start, g_cfg->transcoder_ports.range_end, VTY_NEWLINE);
+	if (g_cfg->bts_force_ptime > 0)
+		vty_out(vty, "  rtp force-ptime %d%s", g_cfg->bts_force_ptime, VTY_NEWLINE);
+	vty_out(vty, "  transcoder-remote-base %u%s", g_cfg->transcoder_remote_base, VTY_NEWLINE);
+
+	switch (g_cfg->osmux) {
+	case OSMUX_USAGE_ON:
+		vty_out(vty, "  osmux on%s", VTY_NEWLINE);
+		break;
+	case OSMUX_USAGE_ONLY:
+		vty_out(vty, "  osmux only%s", VTY_NEWLINE);
+		break;
+	case OSMUX_USAGE_OFF:
+	default:
+		vty_out(vty, "  osmux off%s", VTY_NEWLINE);
+		break;
+	}
+	if (g_cfg->osmux) {
+		vty_out(vty, "  osmux bind-ip %s%s",
+			g_cfg->osmux_addr, VTY_NEWLINE);
+		vty_out(vty, "  osmux batch-factor %d%s",
+			g_cfg->osmux_batch, VTY_NEWLINE);
+		vty_out(vty, "  osmux batch-size %u%s",
+			g_cfg->osmux_batch_size, VTY_NEWLINE);
+		vty_out(vty, "  osmux port %u%s",
+			g_cfg->osmux_port, VTY_NEWLINE);
+		vty_out(vty, "  osmux dummy %s%s",
+			g_cfg->osmux_dummy ? "on" : "off", VTY_NEWLINE);
+	}
+	return CMD_SUCCESS;
+}
+
+static void dump_rtp_end(const char *end_name, struct vty *vty,
+			struct mgcp_rtp_state *state, struct mgcp_rtp_end *end)
+{
+	struct mgcp_rtp_codec *codec = &end->codec;
+
+	vty_out(vty,
+		"  %s%s"
+		"   Timestamp Errs: %d->%d%s"
+		"   Dropped Packets: %d%s"
+		"   Payload Type: %d Rate: %u Channels: %d %s"
+		"   Frame Duration: %u Frame Denominator: %u%s"
+		"   FPP: %d Packet Duration: %u%s"
+		"   FMTP-Extra: %s Audio-Name: %s Sub-Type: %s%s"
+		"   Output-Enabled: %d Force-PTIME: %d%s",
+		end_name, VTY_NEWLINE,
+		state->in_stream.err_ts_counter,
+		state->out_stream.err_ts_counter, VTY_NEWLINE,
+		end->dropped_packets, VTY_NEWLINE,
+		codec->payload_type, codec->rate, codec->channels, VTY_NEWLINE,
+		codec->frame_duration_num, codec->frame_duration_den, VTY_NEWLINE,
+		end->frames_per_packet, end->packet_duration_ms, VTY_NEWLINE,
+		end->fmtp_extra, codec->audio_name, codec->subtype_name, VTY_NEWLINE,
+		end->output_enabled, end->force_output_ptime, VTY_NEWLINE);
+}
+
+static void dump_trunk(struct vty *vty, struct mgcp_trunk_config *cfg, int verbose)
+{
+	int i;
+
+	vty_out(vty, "%s trunk nr %d with %d endpoints:%s",
+		cfg->trunk_type == MGCP_TRUNK_VIRTUAL ? "Virtual" : "E1",
+		cfg->trunk_nr, cfg->number_endpoints - 1, VTY_NEWLINE);
+
+	if (!cfg->endpoints) {
+		vty_out(vty, "No endpoints allocated yet.%s", VTY_NEWLINE);
+		return;
+	}
+
+	for (i = 1; i < cfg->number_endpoints; ++i) {
+		struct mgcp_endpoint *endp = &cfg->endpoints[i];
+		vty_out(vty,
+			" Endpoint 0x%.2x: CI: %d net: %u/%u bts: %u/%u on %s "
+			"traffic received bts: %u  remote: %u transcoder: %u/%u%s",
+			i, endp->ci,
+			ntohs(endp->net_end.rtp_port), ntohs(endp->net_end.rtcp_port),
+			ntohs(endp->bts_end.rtp_port), ntohs(endp->bts_end.rtcp_port),
+			inet_ntoa(endp->bts_end.addr),
+			endp->bts_end.packets, endp->net_end.packets,
+			endp->trans_net.packets, endp->trans_bts.packets,
+			VTY_NEWLINE);
+
+		if (verbose && endp->allocated) {
+			dump_rtp_end("Net->BTS", vty, &endp->bts_state, &endp->bts_end);
+			dump_rtp_end("BTS->Net", vty, &endp->net_state, &endp->net_end);
+		}
+	}
+}
+
+DEFUN(show_mcgp, show_mgcp_cmd,
+      "show mgcp [stats]",
+      SHOW_STR
+      "Display information about the MGCP Media Gateway\n"
+      "Include Statistics\n")
+{
+	struct mgcp_trunk_config *trunk;
+	int show_stats = argc >= 1;
+
+	dump_trunk(vty, &g_cfg->trunk, show_stats);
+
+	llist_for_each_entry(trunk, &g_cfg->trunks, entry)
+		dump_trunk(vty, trunk, show_stats);
+
+	if (g_cfg->osmux)
+		vty_out(vty, "Osmux used CID: %d%s", osmux_used_cid(), VTY_NEWLINE);
+
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp,
+      cfg_mgcp_cmd,
+      "mgcp",
+      "Configure the MGCP")
+{
+	vty->node = MGCP_NODE;
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_local_ip,
+      cfg_mgcp_local_ip_cmd,
+      "local ip A.B.C.D",
+      "Local options for the SDP record\n"
+      IP_STR
+      "IPv4 Address to use in SDP record\n")
+{
+	osmo_talloc_replace_string(g_cfg, &g_cfg->local_ip, argv[0]);
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_bts_ip,
+      cfg_mgcp_bts_ip_cmd,
+      "bts ip A.B.C.D",
+      "BTS Audio source/destination options\n"
+      IP_STR
+      "IPv4 Address of the BTS\n")
+{
+	osmo_talloc_replace_string(g_cfg, &g_cfg->bts_ip, argv[0]);
+	inet_aton(g_cfg->bts_ip, &g_cfg->bts_in);
+	return CMD_SUCCESS;
+}
+
+#define BIND_STR "Listen/Bind related socket option\n"
+DEFUN(cfg_mgcp_bind_ip,
+      cfg_mgcp_bind_ip_cmd,
+      "bind ip A.B.C.D",
+      BIND_STR
+      IP_STR
+      "IPv4 Address to bind to\n")
+{
+	osmo_talloc_replace_string(g_cfg, &g_cfg->source_addr, argv[0]);
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_bind_port,
+      cfg_mgcp_bind_port_cmd,
+      "bind port <0-65534>",
+      BIND_STR
+      "Port information\n"
+      "UDP port to listen for MGCP messages\n")
+{
+	unsigned int port = atoi(argv[0]);
+	g_cfg->source_port = port;
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_bind_early,
+      cfg_mgcp_bind_early_cmd,
+      "bind early (0|1)",
+      BIND_STR
+      "Bind local ports on start up\n"
+      "Bind on demand\n" "Bind on startup\n")
+{
+	vty_out(vty, "bind early is deprecated, remove it from the config.\n");
+	return CMD_WARNING;
+}
+
+static void parse_base(struct mgcp_port_range *range, const char **argv)
+{
+	unsigned int port = atoi(argv[0]);
+	range->mode = PORT_ALLOC_STATIC;
+	range->base_port = port;
+}
+
+static void parse_range(struct mgcp_port_range *range, const char **argv)
+{
+	range->mode = PORT_ALLOC_DYNAMIC;
+	range->range_start = atoi(argv[0]);
+	range->range_end = atoi(argv[1]);
+	range->last_port = g_cfg->bts_ports.range_start;
+}
+
+
+#define RTP_STR "RTP configuration\n"
+#define BTS_START_STR "First UDP port allocated for the BTS side\n"
+#define NET_START_STR "First UDP port allocated for the NET side\n"
+#define UDP_PORT_STR "UDP Port number\n"
+DEFUN(cfg_mgcp_rtp_bts_base_port,
+      cfg_mgcp_rtp_bts_base_port_cmd,
+      "rtp bts-base <0-65534>",
+      RTP_STR
+      BTS_START_STR
+      UDP_PORT_STR)
+{
+	parse_base(&g_cfg->bts_ports, argv);
+	return CMD_SUCCESS;
+}
+
+#define RANGE_START_STR "Start of the range of ports\n"
+#define RANGE_END_STR "End of the range of ports\n"
+DEFUN(cfg_mgcp_rtp_bts_range,
+      cfg_mgcp_rtp_bts_range_cmd,
+      "rtp bts-range <0-65534> <0-65534>",
+      RTP_STR "Range of ports to use for the BTS side\n"
+      RANGE_START_STR RANGE_END_STR)
+{
+	parse_range(&g_cfg->bts_ports, argv);
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_rtp_net_range,
+      cfg_mgcp_rtp_net_range_cmd,
+      "rtp net-range <0-65534> <0-65534>",
+      RTP_STR "Range of ports to use for the NET side\n"
+      RANGE_START_STR RANGE_END_STR)
+{
+	parse_range(&g_cfg->net_ports, argv);
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_rtp_net_base_port,
+      cfg_mgcp_rtp_net_base_port_cmd,
+      "rtp net-base <0-65534>",
+      RTP_STR NET_START_STR UDP_PORT_STR)
+{
+	parse_base(&g_cfg->net_ports, argv);
+	return CMD_SUCCESS;
+}
+
+ALIAS_DEPRECATED(cfg_mgcp_rtp_bts_base_port, cfg_mgcp_rtp_base_port_cmd,
+      "rtp base <0-65534>",
+      RTP_STR BTS_START_STR UDP_PORT_STR)
+
+DEFUN(cfg_mgcp_rtp_transcoder_range,
+      cfg_mgcp_rtp_transcoder_range_cmd,
+      "rtp transcoder-range <0-65534> <0-65534>",
+      RTP_STR "Range of ports to use for the Transcoder\n"
+      RANGE_START_STR RANGE_END_STR)
+{
+	parse_range(&g_cfg->transcoder_ports, argv);
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_rtp_transcoder_base,
+      cfg_mgcp_rtp_transcoder_base_cmd,
+      "rtp transcoder-base <0-65534>",
+      RTP_STR "First UDP port allocated for the Transcoder side\n"
+      UDP_PORT_STR)
+{
+	parse_base(&g_cfg->transcoder_ports, argv);
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_rtp_bts_bind_ip,
+      cfg_mgcp_rtp_bts_bind_ip_cmd,
+      "rtp bts-bind-ip A.B.C.D",
+      RTP_STR "Bind endpoints facing the BTS\n" "Address to bind to\n")
+{
+	osmo_talloc_replace_string(g_cfg, &g_cfg->bts_ports.bind_addr, argv[0]);
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_rtp_no_bts_bind_ip,
+      cfg_mgcp_rtp_no_bts_bind_ip_cmd,
+      "no rtp bts-bind-ip",
+      NO_STR RTP_STR "Bind endpoints facing the BTS\n" "Address to bind to\n")
+{
+	talloc_free(g_cfg->bts_ports.bind_addr);
+	g_cfg->bts_ports.bind_addr = NULL;
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_rtp_net_bind_ip,
+      cfg_mgcp_rtp_net_bind_ip_cmd,
+      "rtp net-bind-ip A.B.C.D",
+      RTP_STR "Bind endpoints facing the Network\n" "Address to bind to\n")
+{
+	osmo_talloc_replace_string(g_cfg, &g_cfg->net_ports.bind_addr, argv[0]);
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_rtp_no_net_bind_ip,
+      cfg_mgcp_rtp_no_net_bind_ip_cmd,
+      "no rtp net-bind-ip",
+      NO_STR RTP_STR "Bind endpoints facing the Network\n" "Address to bind to\n")
+{
+	talloc_free(g_cfg->net_ports.bind_addr);
+	g_cfg->net_ports.bind_addr = NULL;
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_rtp_ip_dscp,
+      cfg_mgcp_rtp_ip_dscp_cmd,
+      "rtp ip-dscp <0-255>",
+      RTP_STR
+      "Apply IP_TOS to the audio stream (including Osmux)\n" "The DSCP value\n")
+{
+	int dscp = atoi(argv[0]);
+	g_cfg->endp_dscp = dscp;
+	return CMD_SUCCESS;
+}
+
+ALIAS_DEPRECATED(cfg_mgcp_rtp_ip_dscp, cfg_mgcp_rtp_ip_tos_cmd,
+      "rtp ip-tos <0-255>",
+      RTP_STR
+      "Apply IP_TOS to the audio stream\n" "The DSCP value\n")
+
+#define FORCE_PTIME_STR "Force a fixed ptime for packets sent to the BTS"
+DEFUN(cfg_mgcp_rtp_force_ptime,
+      cfg_mgcp_rtp_force_ptime_cmd,
+      "rtp force-ptime (10|20|40)",
+      RTP_STR FORCE_PTIME_STR
+      "The required ptime (packet duration) in ms\n"
+      "10 ms\n20 ms\n40 ms\n")
+{
+	g_cfg->bts_force_ptime = atoi(argv[0]);
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_no_rtp_force_ptime,
+      cfg_mgcp_no_rtp_force_ptime_cmd,
+      "no rtp force-ptime",
+      NO_STR RTP_STR FORCE_PTIME_STR)
+{
+	g_cfg->bts_force_ptime = 0;
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_sdp_fmtp_extra,
+      cfg_mgcp_sdp_fmtp_extra_cmd,
+      "sdp audio fmtp-extra .NAME",
+      "Add extra fmtp for the SDP file\n" "Audio\n" "Fmtp-extra\n"
+      "Extra Information\n")
+{
+	char *txt = argv_concat(argv, argc, 0);
+	if (!txt)
+		return CMD_WARNING;
+
+	osmo_talloc_replace_string(g_cfg, &g_cfg->trunk.audio_fmtp_extra, txt);
+	talloc_free(txt);
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_allow_transcoding,
+      cfg_mgcp_allow_transcoding_cmd,
+      "allow-transcoding",
+      "Allow transcoding\n")
+{
+	g_cfg->trunk.no_audio_transcoding = 0;
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_no_allow_transcoding,
+      cfg_mgcp_no_allow_transcoding_cmd,
+      "no allow-transcoding",
+      NO_STR "Allow transcoding\n")
+{
+	g_cfg->trunk.no_audio_transcoding = 1;
+	return CMD_SUCCESS;
+}
+
+#define SDP_STR "SDP File related options\n"
+#define AUDIO_STR "Audio payload options\n"
+DEFUN(cfg_mgcp_sdp_payload_number,
+      cfg_mgcp_sdp_payload_number_cmd,
+      "sdp audio-payload number <0-255>",
+      SDP_STR AUDIO_STR
+      "Number\n" "Payload number\n")
+{
+	unsigned int payload = atoi(argv[0]);
+	g_cfg->trunk.audio_payload = payload;
+	return CMD_SUCCESS;
+}
+
+ALIAS_DEPRECATED(cfg_mgcp_sdp_payload_number, cfg_mgcp_sdp_payload_number_cmd_old,
+      "sdp audio payload number <0-255>",
+      SDP_STR AUDIO_STR AUDIO_STR "Number\n" "Payload number\n")
+      
+
+DEFUN(cfg_mgcp_sdp_payload_name,
+      cfg_mgcp_sdp_payload_name_cmd,
+      "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]);
+	return CMD_SUCCESS;
+}
+
+ALIAS_DEPRECATED(cfg_mgcp_sdp_payload_name, cfg_mgcp_sdp_payload_name_cmd_old,
+      "sdp audio payload name NAME",
+      SDP_STR AUDIO_STR AUDIO_STR "Name\n" "Payload name\n")
+
+DEFUN(cfg_mgcp_sdp_payload_send_ptime,
+      cfg_mgcp_sdp_payload_send_ptime_cmd,
+      "sdp audio-payload send-ptime",
+      SDP_STR AUDIO_STR
+      "Send SDP ptime (packet duration) attribute\n")
+{
+	g_cfg->trunk.audio_send_ptime = 1;
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_no_sdp_payload_send_ptime,
+      cfg_mgcp_no_sdp_payload_send_ptime_cmd,
+      "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;
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_sdp_payload_send_name,
+      cfg_mgcp_sdp_payload_send_name_cmd,
+      "sdp audio-payload send-name",
+      SDP_STR AUDIO_STR
+      "Send SDP rtpmap with the audio name\n")
+{
+	g_cfg->trunk.audio_send_name = 1;
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_no_sdp_payload_send_name,
+      cfg_mgcp_no_sdp_payload_send_name_cmd,
+      "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;
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_loop,
+      cfg_mgcp_loop_cmd,
+      "loop (0|1)",
+      "Loop audio for all endpoints on main trunk\n"
+      "Don't Loop\n" "Loop\n")
+{
+	if (g_cfg->osmux) {
+		vty_out(vty, "Cannot use `loop' with `osmux'.%s", VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+	g_cfg->trunk.audio_loop = atoi(argv[0]);
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_force_realloc,
+      cfg_mgcp_force_realloc_cmd,
+      "force-realloc (0|1)",
+      "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]);
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_number_endp,
+      cfg_mgcp_number_endp_cmd,
+      "number endpoints <0-65534>",
+      "Number options\n" "Endpoints available\n" "Number endpoints\n")
+{
+	/* + 1 as we start counting at one */
+	g_cfg->trunk.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;
+	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;
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_patch_rtp_ssrc,
+      cfg_mgcp_patch_rtp_ssrc_cmd,
+      "rtp-patch ssrc",
+      RTP_PATCH_STR
+      "Force a fixed SSRC\n"
+      )
+{
+	g_cfg->trunk.force_constant_ssrc = 1;
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_no_patch_rtp_ssrc,
+      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;
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_patch_rtp_ts,
+      cfg_mgcp_patch_rtp_ts_cmd,
+      "rtp-patch timestamp",
+      RTP_PATCH_STR
+      "Adjust RTP timestamp\n"
+      )
+{
+	g_cfg->trunk.force_aligned_timing = 1;
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_no_patch_rtp_ts,
+      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;
+	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;
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_rtp_keepalive,
+      cfg_mgcp_rtp_keepalive_cmd,
+      "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]));
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_rtp_keepalive_once,
+      cfg_mgcp_rtp_keepalive_once_cmd,
+      "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);
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_no_rtp_keepalive,
+      cfg_mgcp_no_rtp_keepalive_cmd,
+      "no rtp keep-alive",
+      NO_STR RTP_STR RTP_KEEPALIVE_STR
+      )
+{
+	mgcp_trunk_set_keepalive(&g_cfg->trunk, 0);
+	return CMD_SUCCESS;
+}
+
+
+
+#define CALL_AGENT_STR "Callagent information\n"
+DEFUN(cfg_mgcp_agent_addr,
+      cfg_mgcp_agent_addr_cmd,
+      "call-agent ip A.B.C.D",
+      CALL_AGENT_STR IP_STR
+      "IPv4 Address of the callagent\n")
+{
+	osmo_talloc_replace_string(g_cfg, &g_cfg->call_agent_addr, argv[0]);
+	return CMD_SUCCESS;
+}
+
+ALIAS_DEPRECATED(cfg_mgcp_agent_addr, cfg_mgcp_agent_addr_cmd_old,
+      "call agent ip A.B.C.D",
+      CALL_AGENT_STR CALL_AGENT_STR IP_STR
+      "IPv4 Address of the callagent\n")
+      
+
+DEFUN(cfg_mgcp_transcoder,
+      cfg_mgcp_transcoder_cmd,
+      "transcoder-mgw A.B.C.D",
+      "Use a MGW to detranscoder RTP\n"
+      "The IP address of the MGW")
+{
+	osmo_talloc_replace_string(g_cfg, &g_cfg->transcoder_ip, argv[0]);
+	inet_aton(g_cfg->transcoder_ip, &g_cfg->transcoder_in);
+
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_no_transcoder,
+      cfg_mgcp_no_transcoder_cmd,
+      "no transcoder-mgw",
+      NO_STR "Disable the transcoding\n")
+{
+	if (g_cfg->transcoder_ip) {
+		LOGP(DLMGCP, LOGL_NOTICE, "Disabling transcoding on future calls.\n");
+		talloc_free(g_cfg->transcoder_ip);
+		g_cfg->transcoder_ip = NULL;
+	}
+
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_transcoder_remote_base,
+      cfg_mgcp_transcoder_remote_base_cmd,
+      "transcoder-remote-base <0-65534>",
+      "Set the base port for the transcoder\n" "The RTP base port on the transcoder")
+{
+	g_cfg->transcoder_remote_base = atoi(argv[0]);
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_trunk, cfg_mgcp_trunk_cmd,
+      "trunk <1-64>",
+      "Configure a SS7 trunk\n" "Trunk Nr\n")
+{
+	struct mgcp_trunk_config *trunk;
+	int index = atoi(argv[0]);
+
+	trunk = mgcp_trunk_num(g_cfg, index);
+	if (!trunk)
+		trunk = mgcp_trunk_alloc(g_cfg, index);
+
+	if (!trunk) {
+		vty_out(vty, "%%Unable to allocate trunk %u.%s",
+			index, VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	vty->node = TRUNK_NODE;
+	vty->index = trunk;
+	return CMD_SUCCESS;
+}
+
+static int config_write_trunk(struct vty *vty)
+{
+	struct mgcp_trunk_config *trunk;
+
+	llist_for_each_entry(trunk, &g_cfg->trunks, entry) {
+		vty_out(vty, " trunk %d%s", trunk->trunk_nr, VTY_NEWLINE);
+		vty_out(vty, "  sdp audio-payload number %d%s",
+			trunk->audio_payload, VTY_NEWLINE);
+		vty_out(vty, "  sdp audio-payload name %s%s",
+			trunk->audio_name, VTY_NEWLINE);
+		vty_out(vty, "  %ssdp audio-payload send-ptime%s",
+			trunk->audio_send_ptime ? "" : "no ", VTY_NEWLINE);
+		vty_out(vty, "  %ssdp audio-payload send-name%s",
+			trunk->audio_send_name ? "" : "no ", VTY_NEWLINE);
+
+		if (trunk->keepalive_interval == MGCP_KEEPALIVE_ONCE)
+			vty_out(vty, "  rtp keep-alive once%s", VTY_NEWLINE);
+		else if (trunk->keepalive_interval)
+			vty_out(vty, "  rtp keep-alive %d%s",
+				trunk->keepalive_interval, VTY_NEWLINE);
+		else
+			vty_out(vty, "  no rtp keep-alive%s", VTY_NEWLINE);
+		vty_out(vty, "  loop %d%s",
+			trunk->audio_loop, VTY_NEWLINE);
+		vty_out(vty, "  force-realloc %d%s",
+			trunk->force_realloc, VTY_NEWLINE);
+		if (trunk->omit_rtcp)
+			vty_out(vty, "  rtcp-omit%s", VTY_NEWLINE);
+		else
+			vty_out(vty, "  no rtcp-omit%s", VTY_NEWLINE);
+		if (trunk->force_constant_ssrc || trunk->force_aligned_timing) {
+			vty_out(vty, "  %srtp-patch ssrc%s",
+				trunk->force_constant_ssrc ? "" : "no ", VTY_NEWLINE);
+			vty_out(vty, "  %srtp-patch timestamp%s",
+				trunk->force_aligned_timing ? "" : "no ", VTY_NEWLINE);
+		} else
+			vty_out(vty, "  no rtp-patch%s", VTY_NEWLINE);
+		if (trunk->audio_fmtp_extra)
+			vty_out(vty, "   sdp audio fmtp-extra %s%s",
+				trunk->audio_fmtp_extra, VTY_NEWLINE);
+		vty_out(vty, "  %sallow-transcoding%s",
+			trunk->no_audio_transcoding ? "no " : "", VTY_NEWLINE);
+	}
+
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_trunk_sdp_fmtp_extra,
+      cfg_trunk_sdp_fmtp_extra_cmd,
+      "sdp audio fmtp-extra .NAME",
+      "Add extra fmtp for the SDP file\n" "Audio\n" "Fmtp-extra\n"
+      "Extra Information\n")
+{
+	struct mgcp_trunk_config *trunk = vty->index;
+	char *txt = argv_concat(argv, argc, 0);
+	if (!txt)
+		return CMD_WARNING;
+
+	osmo_talloc_replace_string(g_cfg, &trunk->audio_fmtp_extra, txt);
+	talloc_free(txt);
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_trunk_payload_number,
+      cfg_trunk_payload_number_cmd,
+      "sdp audio-payload number <0-255>",
+      SDP_STR AUDIO_STR "Number\n" "Payload Number\n")
+{
+	struct mgcp_trunk_config *trunk = vty->index;
+	unsigned int payload = atoi(argv[0]);
+
+	trunk->audio_payload = payload;
+	return CMD_SUCCESS;
+}
+
+ALIAS_DEPRECATED(cfg_trunk_payload_number, cfg_trunk_payload_number_cmd_old,
+      "sdp audio payload number <0-255>",
+      SDP_STR AUDIO_STR AUDIO_STR "Number\n" "Payload Number\n")
+
+DEFUN(cfg_trunk_payload_name,
+      cfg_trunk_payload_name_cmd,
+      "sdp audio-payload name NAME",
+       SDP_STR AUDIO_STR "Payload\n" "Payload Name\n")
+{
+	struct mgcp_trunk_config *trunk = vty->index;
+
+	osmo_talloc_replace_string(g_cfg, &trunk->audio_name, argv[0]);
+	return CMD_SUCCESS;
+}
+
+ALIAS_DEPRECATED(cfg_trunk_payload_name, cfg_trunk_payload_name_cmd_old,
+      "sdp audio payload name NAME",
+       SDP_STR AUDIO_STR AUDIO_STR "Payload\n" "Payload Name\n")
+
+
+DEFUN(cfg_trunk_loop,
+      cfg_trunk_loop_cmd,
+      "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;
+
+	if (g_cfg->osmux) {
+		vty_out(vty, "Cannot use `loop' with `osmux'.%s", VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+	trunk->audio_loop = atoi(argv[0]);
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_trunk_sdp_payload_send_ptime,
+      cfg_trunk_sdp_payload_send_ptime_cmd,
+      "sdp audio-payload send-ptime",
+      SDP_STR AUDIO_STR
+      "Send SDP ptime (packet duration) attribute\n")
+{
+	struct mgcp_trunk_config *trunk = vty->index;
+	trunk->audio_send_ptime = 1;
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_trunk_no_sdp_payload_send_ptime,
+      cfg_trunk_no_sdp_payload_send_ptime_cmd,
+      "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;
+	trunk->audio_send_ptime = 0;
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_trunk_sdp_payload_send_name,
+      cfg_trunk_sdp_payload_send_name_cmd,
+      "sdp audio-payload send-name",
+      SDP_STR AUDIO_STR
+      "Send SDP rtpmap with the audio name\n")
+{
+	struct mgcp_trunk_config *trunk = vty->index;
+	trunk->audio_send_name = 1;
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_trunk_no_sdp_payload_send_name,
+      cfg_trunk_no_sdp_payload_send_name_cmd,
+      "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;
+	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;
+	trunk->omit_rtcp = 1;
+	return CMD_SUCCESS;
+}
+
+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;
+	trunk->omit_rtcp = 0;
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_trunk_patch_rtp_ssrc,
+      cfg_trunk_patch_rtp_ssrc_cmd,
+      "rtp-patch ssrc",
+      RTP_PATCH_STR
+      "Force a fixed SSRC\n"
+      )
+{
+	struct mgcp_trunk_config *trunk = vty->index;
+	trunk->force_constant_ssrc = 1;
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_trunk_no_patch_rtp_ssrc,
+      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;
+	trunk->force_constant_ssrc = 0;
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_trunk_patch_rtp_ts,
+      cfg_trunk_patch_rtp_ts_cmd,
+      "rtp-patch timestamp",
+      RTP_PATCH_STR
+      "Adjust RTP timestamp\n"
+      )
+{
+	struct mgcp_trunk_config *trunk = vty->index;
+	trunk->force_aligned_timing = 1;
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_trunk_no_patch_rtp_ts,
+      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;
+	trunk->force_aligned_timing = 0;
+	return CMD_SUCCESS;
+}
+
+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;
+	trunk->force_constant_ssrc = 0;
+	trunk->force_aligned_timing = 0;
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_trunk_rtp_keepalive,
+      cfg_trunk_rtp_keepalive_cmd,
+      "rtp keep-alive <1-120>",
+      RTP_STR RTP_KEEPALIVE_STR
+      "Keep-alive interval in secs\n"
+      )
+{
+	struct mgcp_trunk_config *trunk = vty->index;
+	mgcp_trunk_set_keepalive(trunk, atoi(argv[0]));
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_trunk_rtp_keepalive_once,
+      cfg_trunk_rtp_keepalive_once_cmd,
+      "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;
+	mgcp_trunk_set_keepalive(trunk, MGCP_KEEPALIVE_ONCE);
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_trunk_no_rtp_keepalive,
+      cfg_trunk_no_rtp_keepalive_cmd,
+      "no rtp keep-alive",
+      NO_STR RTP_STR RTP_KEEPALIVE_STR
+      )
+{
+	struct mgcp_trunk_config *trunk = vty->index;
+	mgcp_trunk_set_keepalive(trunk, 0);
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_trunk_allow_transcoding,
+      cfg_trunk_allow_transcoding_cmd,
+      "allow-transcoding",
+      "Allow transcoding\n")
+{
+	struct mgcp_trunk_config *trunk = vty->index;
+	trunk->no_audio_transcoding = 0;
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_trunk_no_allow_transcoding,
+      cfg_trunk_no_allow_transcoding_cmd,
+      "no allow-transcoding",
+      NO_STR "Allow transcoding\n")
+{
+	struct mgcp_trunk_config *trunk = vty->index;
+	trunk->no_audio_transcoding = 1;
+	return CMD_SUCCESS;
+}
+
+DEFUN(loop_endp,
+      loop_endp_cmd,
+      "loop-endpoint <0-64> NAME (0|1)",
+      "Loop a given endpoint\n" "Trunk number\n"
+      "The name in hex of the endpoint\n" "Disable the loop\n" "Enable the loop\n")
+{
+	struct mgcp_trunk_config *trunk;
+	struct mgcp_endpoint *endp;
+
+	trunk = find_trunk(g_cfg, atoi(argv[0]));
+	if (!trunk) {
+		vty_out(vty, "%%Trunk %d not found in the config.%s",
+			atoi(argv[0]), VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	if (!trunk->endpoints) {
+		vty_out(vty, "%%Trunk %d has no endpoints allocated.%s",
+			trunk->trunk_nr, VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	int endp_no = strtoul(argv[1], NULL, 16);
+	if (endp_no < 1 || endp_no >= trunk->number_endpoints) {
+		vty_out(vty, "Loopback number %s/%d is invalid.%s",
+		argv[1], endp_no, VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+
+	endp = &trunk->endpoints[endp_no];
+	int loop = atoi(argv[2]);
+
+	if (loop)
+		endp->conn_mode = MGCP_CONN_LOOPBACK;
+	else
+		endp->conn_mode = endp->orig_mode;
+
+	/* Handle it like a MDCX, switch on SSRC patching if enabled */
+	mgcp_rtp_end_config(endp, 1, &endp->bts_end);
+	mgcp_rtp_end_config(endp, 1, &endp->net_end);
+
+	return CMD_SUCCESS;
+}
+
+DEFUN(tap_call,
+      tap_call_cmd,
+      "tap-call <0-64> ENDPOINT (bts-in|bts-out|net-in|net-out) A.B.C.D <0-65534>",
+      "Forward data on endpoint to a different system\n" "Trunk number\n"
+      "The endpoint in hex\n"
+      "Forward the data coming from the bts\n"
+      "Forward the data coming from the bts leaving to the network\n"
+      "Forward the data coming from the net\n"
+      "Forward the data coming from the net leaving to the bts\n"
+      "destination IP of the data\n" "destination port\n")
+{
+	struct mgcp_rtp_tap *tap;
+	struct mgcp_trunk_config *trunk;
+	struct mgcp_endpoint *endp;
+	int port = 0;
+
+	trunk = find_trunk(g_cfg, atoi(argv[0]));
+	if (!trunk) {
+		vty_out(vty, "%%Trunk %d not found in the config.%s",
+			atoi(argv[0]), VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	if (!trunk->endpoints) {
+		vty_out(vty, "%%Trunk %d has no endpoints allocated.%s",
+			trunk->trunk_nr, VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	int endp_no = strtoul(argv[1], NULL, 16);
+	if (endp_no < 1 || endp_no >= trunk->number_endpoints) {
+		vty_out(vty, "Endpoint number %s/%d is invalid.%s",
+		argv[1], endp_no, VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	endp = &trunk->endpoints[endp_no];
+
+	if (strcmp(argv[2], "bts-in") == 0) {
+		port = MGCP_TAP_BTS_IN;
+	} else if (strcmp(argv[2], "bts-out") == 0) {
+		port = MGCP_TAP_BTS_OUT;
+	} else if (strcmp(argv[2], "net-in") == 0) {
+		port = MGCP_TAP_NET_IN;
+	} else if (strcmp(argv[2], "net-out") == 0) {
+		port = MGCP_TAP_NET_OUT;
+	} else {
+		vty_out(vty, "Unknown mode... tricked vty?%s", VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	tap = &endp->taps[port];
+	memset(&tap->forward, 0, sizeof(tap->forward));
+	inet_aton(argv[3], &tap->forward.sin_addr);
+	tap->forward.sin_port = htons(atoi(argv[4]));
+	tap->enabled = 1;
+	return CMD_SUCCESS;
+}
+
+DEFUN(free_endp, free_endp_cmd,
+      "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_endpoint *endp;
+
+	trunk = find_trunk(g_cfg, atoi(argv[0]));
+	if (!trunk) {
+		vty_out(vty, "%%Trunk %d not found in the config.%s",
+			atoi(argv[0]), VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	if (!trunk->endpoints) {
+		vty_out(vty, "%%Trunk %d has no endpoints allocated.%s",
+			trunk->trunk_nr, VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	int endp_no = strtoul(argv[1], NULL, 16);
+	if (endp_no < 1 || endp_no >= trunk->number_endpoints) {
+		vty_out(vty, "Endpoint number %s/%d is invalid.%s",
+		argv[1], endp_no, VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	endp = &trunk->endpoints[endp_no];
+	mgcp_release_endp(endp);
+	return CMD_SUCCESS;
+}
+
+DEFUN(reset_endp, reset_endp_cmd,
+      "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_endpoint *endp;
+	int endp_no, rc;
+
+	trunk = find_trunk(g_cfg, atoi(argv[0]));
+	if (!trunk) {
+		vty_out(vty, "%%Trunk %d not found in the config.%s",
+			atoi(argv[0]), VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	if (!trunk->endpoints) {
+		vty_out(vty, "%%Trunk %d has no endpoints allocated.%s",
+			trunk->trunk_nr, VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	endp_no = strtoul(argv[1], NULL, 16);
+	if (endp_no < 1 || endp_no >= trunk->number_endpoints) {
+		vty_out(vty, "Endpoint number %s/%d is invalid.%s",
+		argv[1], endp_no, VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	endp = &trunk->endpoints[endp_no];
+	rc = mgcp_send_reset_ep(endp, ENDPOINT_NUMBER(endp));
+	if (rc < 0) {
+		vty_out(vty, "Error %d sending reset.%s", rc, VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+	return CMD_SUCCESS;
+}
+
+DEFUN(reset_all_endp, reset_all_endp_cmd,
+      "reset-all-endpoints",
+      "Reset all endpoints\n")
+{
+	int rc;
+
+	rc = mgcp_send_reset_all(g_cfg);
+	if (rc < 0) {
+		vty_out(vty, "Error %d during endpoint reset.%s",
+			rc, VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+	return CMD_SUCCESS;
+}
+
+#define OSMUX_STR "RTP multiplexing\n"
+DEFUN(cfg_mgcp_osmux,
+      cfg_mgcp_osmux_cmd,
+      "osmux (on|off|only)",
+       OSMUX_STR "Enable OSMUX\n" "Disable OSMUX\n" "Only use OSMUX\n")
+{
+	if (strcmp(argv[0], "off") == 0) {
+		g_cfg->osmux = OSMUX_USAGE_OFF;
+		return CMD_SUCCESS;
+	}
+
+	if (strcmp(argv[0], "on") == 0)
+		g_cfg->osmux = OSMUX_USAGE_ON;
+	else if (strcmp(argv[0], "only") == 0)
+		g_cfg->osmux = OSMUX_USAGE_ONLY;
+
+	if (g_cfg->trunk.audio_loop) {
+		vty_out(vty, "Cannot use `loop' with `osmux'.%s",
+			VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_osmux_ip,
+      cfg_mgcp_osmux_ip_cmd,
+      "osmux bind-ip A.B.C.D",
+      OSMUX_STR IP_STR "IPv4 Address to bind to\n")
+{
+	osmo_talloc_replace_string(g_cfg, &g_cfg->osmux_addr, argv[0]);
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_osmux_batch_factor,
+      cfg_mgcp_osmux_batch_factor_cmd,
+      "osmux batch-factor <1-8>",
+      OSMUX_STR "Batching factor\n" "Number of messages in the batch\n")
+{
+	g_cfg->osmux_batch = atoi(argv[0]);
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_osmux_batch_size,
+      cfg_mgcp_osmux_batch_size_cmd,
+      "osmux batch-size <1-65535>",
+      OSMUX_STR "batch size\n" "Batch size in bytes\n")
+{
+	g_cfg->osmux_batch_size = atoi(argv[0]);
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_osmux_port,
+      cfg_mgcp_osmux_port_cmd,
+      "osmux port <1-65535>",
+      OSMUX_STR "port\n" "UDP port\n")
+{
+	g_cfg->osmux_port = atoi(argv[0]);
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_osmux_dummy,
+      cfg_mgcp_osmux_dummy_cmd,
+      "osmux dummy (on|off)",
+      OSMUX_STR "Dummy padding\n" "Enable dummy padding\n" "Disable dummy padding\n")
+{
+	if (strcmp(argv[0], "on") == 0)
+		g_cfg->osmux_dummy = 1;
+	else if (strcmp(argv[0], "off") == 0)
+		g_cfg->osmux_dummy = 0;
+
+	return CMD_SUCCESS;
+}
+
+int mgcp_vty_init(void)
+{
+	install_element_ve(&show_mgcp_cmd);
+	install_element(ENABLE_NODE, &loop_endp_cmd);
+	install_element(ENABLE_NODE, &tap_call_cmd);
+	install_element(ENABLE_NODE, &free_endp_cmd);
+	install_element(ENABLE_NODE, &reset_endp_cmd);
+	install_element(ENABLE_NODE, &reset_all_endp_cmd);
+
+	install_element(CONFIG_NODE, &cfg_mgcp_cmd);
+	install_node(&mgcp_node, config_write_mgcp);
+
+	vty_install_default(MGCP_NODE);
+	install_element(MGCP_NODE, &cfg_mgcp_local_ip_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_bts_ip_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_bind_ip_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_bind_port_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_bind_early_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_rtp_base_port_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_rtp_bts_base_port_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_rtp_net_base_port_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_rtp_bts_range_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_rtp_bts_bind_ip_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_rtp_no_bts_bind_ip_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_rtp_net_range_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_rtp_net_bind_ip_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_rtp_no_net_bind_ip_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_rtp_transcoder_range_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_rtp_transcoder_base_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_rtp_ip_dscp_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_rtp_ip_tos_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_rtp_force_ptime_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_no_rtp_force_ptime_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_rtp_keepalive_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_rtp_keepalive_once_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_no_rtp_keepalive_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_agent_addr_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_agent_addr_cmd_old);
+	install_element(MGCP_NODE, &cfg_mgcp_transcoder_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_no_transcoder_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_transcoder_remote_base_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_sdp_payload_number_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_sdp_payload_name_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_sdp_payload_number_cmd_old);
+	install_element(MGCP_NODE, &cfg_mgcp_sdp_payload_name_cmd_old);
+	install_element(MGCP_NODE, &cfg_mgcp_loop_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_force_realloc_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_number_endp_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_omit_rtcp_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_no_omit_rtcp_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_patch_rtp_ssrc_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_no_patch_rtp_ssrc_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_patch_rtp_ts_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_no_patch_rtp_ts_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_no_patch_rtp_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_sdp_fmtp_extra_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_sdp_payload_send_ptime_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_no_sdp_payload_send_ptime_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_sdp_payload_send_name_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_no_sdp_payload_send_name_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_osmux_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_osmux_ip_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_osmux_batch_factor_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_osmux_batch_size_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_osmux_port_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_osmux_dummy_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_allow_transcoding_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_no_allow_transcoding_cmd);
+
+
+	install_element(MGCP_NODE, &cfg_mgcp_trunk_cmd);
+	install_node(&trunk_node, config_write_trunk);
+	vty_install_default(TRUNK_NODE);
+	install_element(TRUNK_NODE, &cfg_trunk_rtp_keepalive_cmd);
+	install_element(TRUNK_NODE, &cfg_trunk_rtp_keepalive_once_cmd);
+	install_element(TRUNK_NODE, &cfg_trunk_no_rtp_keepalive_cmd);
+	install_element(TRUNK_NODE, &cfg_trunk_payload_number_cmd);
+	install_element(TRUNK_NODE, &cfg_trunk_payload_name_cmd);
+	install_element(TRUNK_NODE, &cfg_trunk_payload_number_cmd_old);
+	install_element(TRUNK_NODE, &cfg_trunk_payload_name_cmd_old);
+	install_element(TRUNK_NODE, &cfg_trunk_loop_cmd);
+	install_element(TRUNK_NODE, &cfg_trunk_omit_rtcp_cmd);
+	install_element(TRUNK_NODE, &cfg_trunk_no_omit_rtcp_cmd);
+	install_element(TRUNK_NODE, &cfg_trunk_patch_rtp_ssrc_cmd);
+	install_element(TRUNK_NODE, &cfg_trunk_no_patch_rtp_ssrc_cmd);
+	install_element(TRUNK_NODE, &cfg_trunk_patch_rtp_ts_cmd);
+	install_element(TRUNK_NODE, &cfg_trunk_no_patch_rtp_ts_cmd);
+	install_element(TRUNK_NODE, &cfg_trunk_no_patch_rtp_cmd);
+	install_element(TRUNK_NODE, &cfg_trunk_sdp_fmtp_extra_cmd);
+	install_element(TRUNK_NODE, &cfg_trunk_sdp_payload_send_ptime_cmd);
+	install_element(TRUNK_NODE, &cfg_trunk_no_sdp_payload_send_ptime_cmd);
+	install_element(TRUNK_NODE, &cfg_trunk_sdp_payload_send_name_cmd);
+	install_element(TRUNK_NODE, &cfg_trunk_no_sdp_payload_send_name_cmd);
+	install_element(TRUNK_NODE, &cfg_trunk_allow_transcoding_cmd);
+	install_element(TRUNK_NODE, &cfg_trunk_no_allow_transcoding_cmd);
+
+	return 0;
+}
+
+static int allocate_trunk(struct mgcp_trunk_config *trunk)
+{
+	int i;
+	struct mgcp_config *cfg = trunk->cfg;
+
+	if (mgcp_endpoints_allocate(trunk) != 0) {
+		LOGP(DLMGCP, LOGL_ERROR,
+		     "Failed to allocate %d endpoints on trunk %d.\n",
+		     trunk->number_endpoints, trunk->trunk_nr);
+		return -1;
+	}
+
+	/* early bind */
+	for (i = 1; i < trunk->number_endpoints; ++i) {
+		struct mgcp_endpoint *endp = &trunk->endpoints[i];
+
+		if (cfg->bts_ports.mode == PORT_ALLOC_STATIC) {
+			cfg->last_bts_port += 2;
+			if (mgcp_bind_bts_rtp_port(endp, cfg->last_bts_port) != 0) {
+				LOGP(DLMGCP, LOGL_FATAL,
+				     "Failed to bind: %d\n", cfg->last_bts_port);
+				return -1;
+			}
+			endp->bts_end.local_alloc = PORT_ALLOC_STATIC;
+		}
+
+		if (cfg->net_ports.mode == PORT_ALLOC_STATIC) {
+			cfg->last_net_port += 2;
+			if (mgcp_bind_net_rtp_port(endp, cfg->last_net_port) != 0) {
+				LOGP(DLMGCP, LOGL_FATAL,
+				     "Failed to bind: %d\n", cfg->last_net_port);
+				return -1;
+			}
+			endp->net_end.local_alloc = PORT_ALLOC_STATIC;
+		}
+
+		if (trunk->trunk_type == MGCP_TRUNK_VIRTUAL &&
+		    cfg->transcoder_ip && cfg->transcoder_ports.mode == PORT_ALLOC_STATIC) {
+			int rtp_port;
+
+			/* network side */
+			rtp_port = rtp_calculate_port(ENDPOINT_NUMBER(endp),
+						      cfg->transcoder_ports.base_port);
+			if (mgcp_bind_trans_net_rtp_port(endp, rtp_port) != 0) {
+				LOGP(DLMGCP, LOGL_FATAL, "Failed to bind: %d\n", rtp_port);
+				return -1;
+			}
+			endp->trans_net.local_alloc = PORT_ALLOC_STATIC;
+
+			/* bts side */
+			rtp_port = rtp_calculate_port(endp_back_channel(ENDPOINT_NUMBER(endp)),
+						      cfg->transcoder_ports.base_port);
+			if (mgcp_bind_trans_bts_rtp_port(endp, rtp_port) != 0) {
+				LOGP(DLMGCP, LOGL_FATAL, "Failed to bind: %d\n", rtp_port);
+				return -1;
+			}
+			endp->trans_bts.local_alloc = PORT_ALLOC_STATIC;
+		}
+	}
+
+	return 0;
+}
+
+int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg,
+		      enum mgcp_role role)
+{
+	int rc;
+	struct mgcp_trunk_config *trunk;
+
+	cfg->osmux_port = OSMUX_PORT;
+	cfg->osmux_batch = 4;
+	cfg->osmux_batch_size = OSMUX_BATCH_DEFAULT_MAX;
+
+	g_cfg = cfg;
+	rc = vty_read_config_file(config_file, NULL);
+	if (rc < 0) {
+		fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file);
+		return rc;
+	}
+
+
+	if (!g_cfg->bts_ip)
+		fprintf(stderr, "No BTS ip address specified. This will allow everyone to connect.\n");
+
+	if (!g_cfg->source_addr) {
+		fprintf(stderr, "You need to specify a bind address.\n");
+		return -1;
+	}
+
+	/* initialize the last ports */
+	g_cfg->last_bts_port = rtp_calculate_port(0, g_cfg->bts_ports.base_port);
+	g_cfg->last_net_port = rtp_calculate_port(0, g_cfg->net_ports.base_port);
+
+	if (allocate_trunk(&g_cfg->trunk) != 0) {
+		LOGP(DLMGCP, LOGL_ERROR, "Failed to initialize the virtual trunk.\n");
+		return -1;
+	}
+
+	llist_for_each_entry(trunk, &g_cfg->trunks, entry) {
+		if (allocate_trunk(trunk) != 0) {
+			LOGP(DLMGCP, LOGL_ERROR,
+			     "Failed to initialize E1 trunk %d.\n", trunk->trunk_nr);
+			return -1;
+		}
+	}
+	cfg->role = role;
+
+	return 0;
+}
+
diff --git a/src/osmo-mgw/Makefile.am b/src/osmo-mgw/Makefile.am
new file mode 100644
index 0000000..3e3511d
--- /dev/null
+++ b/src/osmo-mgw/Makefile.am
@@ -0,0 +1,31 @@
+AM_CPPFLAGS = \
+	$(all_includes) \
+	-I$(top_srcdir)/include \
+	-I$(top_builddir) \
+	$(NULL)
+
+AM_CFLAGS = \
+	-Wall \
+	$(LIBOSMOCORE_CFLAGS) \
+	$(LIBOSMOVTY_CFLAGS) \
+	$(LIBOSMONETIF_CFLAGS) \
+	$(LIBBCG729_CFLAGS) \
+	$(COVERAGE_CFLAGS) \
+	$(NULL)
+
+bin_PROGRAMS = \
+	osmo-bsc_mgcp \
+	$(NULL)
+
+osmo_bsc_mgcp_SOURCES = \
+	mgcp_main.c \
+	$(NULL)
+
+osmo_bsc_mgcp_LDADD = \
+	$(top_builddir)/src/libosmo-legacy-mgcp/libosmo-legacy-mgcp.la \
+	$(LIBOSMOCORE_LIBS) \
+	$(LIBOSMONETIF_LIBS) \
+	$(LIBOSMOVTY_LIBS) \
+	$(LIBBCG729_LIBS) \
+	$(LIBRARY_GSM) \
+	$(NULL)
diff --git a/src/osmo-mgw/mgcp_main.c b/src/osmo-mgw/mgcp_main.c
new file mode 100644
index 0000000..48241a6
--- /dev/null
+++ b/src/osmo-mgw/mgcp_main.c
@@ -0,0 +1,351 @@
+/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
+/* The main method to drive it as a standalone process      */
+
+/*
+ * (C) 2009-2011 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2009-2011 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/>.
+ *
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <limits.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <sys/socket.h>
+
+#include <osmocom/legacy_mgcp/mgcp.h>
+#include <osmocom/legacy_mgcp/mgcp_internal.h>
+#include <osmocom/legacy_mgcp/vty.h>
+
+#include <osmocom/core/application.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/stats.h>
+#include <osmocom/core/rate_ctr.h>
+#include <osmocom/core/logging.h>
+
+#include <osmocom/vty/telnet_interface.h>
+#include <osmocom/vty/logging.h>
+#include <osmocom/vty/ports.h>
+#include <osmocom/vty/command.h>
+#include <osmocom/vty/stats.h>
+
+#include "../../bscconfig.h"
+
+#ifdef BUILD_MGCP_TRANSCODING
+#include <osmocom/legacy_mgcp/mgcp_transcode.h>
+#endif
+
+#define _GNU_SOURCE
+#include <getopt.h>
+
+#warning "Make use of the rtp proxy code"
+
+static struct mgcp_config *cfg;
+static struct mgcp_trunk_config *reset_trunk;
+static int reset_endpoints = 0;
+static int daemonize = 0;
+
+const char *openbsc_copyright =
+	"Copyright (C) 2009-2010 Holger Freyther and On-Waves\r\n"
+	"Contributions by Daniel Willmann, Jan Lübbe, Stefan Schmidt\r\n"
+	"Dieter Spaar, Andreas Eversberg, Harald Welte\r\n\r\n"
+	"License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n"
+	"This is free software: you are free to change and redistribute it.\r\n"
+	"There is NO WARRANTY, to the extent permitted by law.\r\n";
+
+static char *config_file = "mgcp.cfg";
+
+/* used by msgb and mgcp */
+void *tall_bsc_ctx = NULL;
+
+static void print_help()
+{
+	printf("Some useful help...\n");
+	printf(" -h --help is printing this text.\n");
+	printf(" -c --config-file filename The config file to use.\n");
+	printf(" -s --disable-color\n");
+	printf(" -D --daemonize Fork the process into a background daemon\n");
+	printf(" -V --version Print the version number\n");
+}
+
+static void handle_options(int argc, char **argv)
+{
+	while (1) {
+		int option_index = 0, c;
+		static struct option long_options[] = {
+			{"help", 0, 0, 'h'},
+			{"config-file", 1, 0, 'c'},
+			{"daemonize", 0, 0, 'D'},
+			{"version", 0, 0, 'V'},
+			{"disable-color", 0, 0, 's'},
+			{0, 0, 0, 0},
+		};
+
+		c = getopt_long(argc, argv, "hc:VD", long_options, &option_index);
+
+		if (c == -1)
+			break;
+
+		switch(c) {
+		case 'h':
+			print_help();
+			exit(0);
+			break;
+		case 'c':
+			config_file = talloc_strdup(tall_bsc_ctx, optarg);
+			break;
+		case 's':
+			log_set_use_color(osmo_stderr_target, 0);
+			break;
+		case 'V':
+			print_version(1);
+			exit(0);
+			break;
+		case 'D':
+			daemonize = 1;
+			break;
+		default:
+			/* ignore */
+			break;
+		};
+	}
+}
+
+/* simply remember this */
+static int mgcp_rsip_cb(struct mgcp_trunk_config *tcfg)
+{
+	reset_endpoints = 1;
+	reset_trunk = tcfg;
+
+	return 0;
+}
+
+static int read_call_agent(struct osmo_fd *fd, unsigned int what)
+{
+	struct sockaddr_in addr;
+	socklen_t slen = sizeof(addr);
+	struct msgb *msg;
+	struct msgb *resp;
+	int i;
+
+	msg = (struct msgb *) fd->data;
+
+	/* read one less so we can use it as a \0 */
+	int rc = recvfrom(cfg->gw_fd.bfd.fd, msg->data, msg->data_len - 1, 0,
+		(struct sockaddr *) &addr, &slen);
+	if (rc < 0) {
+		perror("Gateway failed to read");
+		return -1;
+	} else if (slen > sizeof(addr)) {
+		fprintf(stderr, "Gateway received message from outerspace: %zu %zu\n",
+			(size_t) slen, sizeof(addr));
+		return -1;
+	}
+
+	/* handle message now */
+	msg->l2h = msgb_put(msg, rc);
+	resp = mgcp_handle_message(cfg, msg);
+	msgb_reset(msg);
+
+	if (resp) {
+		sendto(cfg->gw_fd.bfd.fd, resp->l2h, msgb_l2len(resp), 0, (struct sockaddr *) &addr, sizeof(addr));
+		msgb_free(resp);
+	}
+
+	if (reset_endpoints) {
+		LOGP(DLMGCP, LOGL_NOTICE,
+		     "Asked to reset endpoints: %d/%d\n",
+		     reset_trunk->trunk_nr, reset_trunk->trunk_type);
+		reset_endpoints = 0;
+
+		/* is checking in_addr.s_addr == INADDR_LOOPBACK making it more secure? */
+		for (i = 1; i < reset_trunk->number_endpoints; ++i)
+			mgcp_release_endp(&reset_trunk->endpoints[i]);
+	}
+
+	return 0;
+}
+
+int mgcp_vty_is_config_node(struct vty *vty, int node)
+{
+        switch (node) {
+        case CONFIG_NODE:
+                return 0;
+
+        default:
+                return 1;
+        }
+}
+
+int mgcp_vty_go_parent(struct vty *vty)
+{
+        switch (vty->node) {
+        case TRUNK_NODE:
+                vty->node = MGCP_NODE;
+                vty->index = NULL;
+                break;
+        case MGCP_NODE:
+        default:
+                if (mgcp_vty_is_config_node(vty, vty->node))
+                        vty->node = CONFIG_NODE;
+                else
+                        vty->node = ENABLE_NODE;
+
+                vty->index = NULL;
+        }
+
+        return vty->node;
+}
+
+
+static struct vty_app_info vty_info = {
+	.name 		= "OpenBSC MGCP",
+	.version	= PACKAGE_VERSION,
+	.go_parent_cb	= mgcp_vty_go_parent,
+	.is_config_node	= mgcp_vty_is_config_node,
+};
+
+static const struct log_info_cat log_categories[] = {
+	/* DLMGCP is provided by the MGCP library */
+};
+
+const struct log_info log_info = {
+        .cat = log_categories,
+        .num_cat = ARRAY_SIZE(log_categories),
+};
+
+int main(int argc, char **argv)
+{
+	struct sockaddr_in addr;
+	int on = 1, rc;
+
+	tall_bsc_ctx = talloc_named_const(NULL, 1, "mgcp-callagent");
+	msgb_talloc_ctx_init(tall_bsc_ctx, 0);
+
+	osmo_init_ignore_signals();
+	osmo_init_logging(&log_info);
+
+	cfg = mgcp_config_alloc();
+	if (!cfg)
+		return -1;
+
+#ifdef BUILD_MGCP_TRANSCODING
+	cfg->setup_rtp_processing_cb = &mgcp_transcoding_setup;
+	cfg->rtp_processing_cb = &mgcp_transcoding_process_rtp;
+	cfg->get_net_downlink_format_cb = &mgcp_transcoding_net_downlink_format;
+#endif
+
+	cfg->trunk.force_realloc = 1;
+
+	vty_info.copyright = openbsc_copyright;
+	vty_init(&vty_info);
+	logging_vty_add_cmds(NULL);
+	osmo_stats_vty_add_cmds(&log_info);
+	mgcp_vty_init();
+
+	handle_options(argc, argv);
+
+	rate_ctr_init(tall_bsc_ctx);
+	osmo_stats_init(tall_bsc_ctx);
+
+	rc = mgcp_parse_config(config_file, cfg, MGCP_BSC);
+	if (rc < 0)
+		return rc;
+
+	/* start telnet after reading config for vty_get_bind_addr() */
+	rc = telnet_init_dynif(tall_bsc_ctx, NULL,
+			       vty_get_bind_addr(), OSMO_VTY_PORT_BSC_MGCP);
+	if (rc < 0)
+		return rc;
+
+	/* set some callbacks */
+	cfg->reset_cb = mgcp_rsip_cb;
+
+        /* we need to bind a socket */
+        if (rc == 0) {
+		cfg->gw_fd.bfd.when = BSC_FD_READ;
+		cfg->gw_fd.bfd.cb = read_call_agent;
+		cfg->gw_fd.bfd.fd = socket(AF_INET, SOCK_DGRAM, 0);
+		if (cfg->gw_fd.bfd.fd < 0) {
+			perror("Gateway failed to listen");
+			return -1;
+		}
+
+		setsockopt(cfg->gw_fd.bfd.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+
+		memset(&addr, 0, sizeof(addr));
+		addr.sin_family = AF_INET;
+		addr.sin_port = htons(cfg->source_port);
+		inet_aton(cfg->source_addr, &addr.sin_addr);
+
+		if (bind(cfg->gw_fd.bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+			perror("Gateway failed to bind");
+			return -1;
+		}
+
+		cfg->gw_fd.bfd.data = msgb_alloc(4096, "mgcp-msg");
+		if (!cfg->gw_fd.bfd.data) {
+			fprintf(stderr, "Gateway memory error.\n");
+			return -1;
+		}
+
+		if (cfg->call_agent_addr) {
+			addr.sin_port = htons(2727);
+			inet_aton(cfg->call_agent_addr, &addr.sin_addr);
+			if (connect(cfg->gw_fd.bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+				LOGP(DLMGCP, LOGL_ERROR, "Failed to connect to: '%s'. errno: %d\n",
+				     cfg->call_agent_addr, errno);
+				close(cfg->gw_fd.bfd.fd);
+				cfg->gw_fd.bfd.fd = -1;
+				return -1;
+			}
+		}
+
+		if (osmo_fd_register(&cfg->gw_fd.bfd) != 0) {
+			LOGP(DLMGCP, LOGL_FATAL, "Failed to register the fd\n");
+			return -1;
+		}
+
+		LOGP(DLMGCP, LOGL_NOTICE, "Configured for MGCP.\n");
+	}
+
+	/* initialisation */
+	srand(time(NULL));
+
+	if (daemonize) {
+		rc = osmo_daemonize();
+		if (rc < 0) {
+			perror("Error during daemonize");
+			exit(1);
+		}
+	}
+
+	/* main loop */
+	while (1) {
+		osmo_select_main(0);
+	}
+
+
+	return 0;
+}