Change in ...osmo-mgw[master]: add simplistic IuUP FSM and strip/add IuUP headers

neels gerrit-no-reply at lists.osmocom.org
Mon Aug 12 22:22:07 UTC 2019


neels has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmo-mgw/+/15169


Change subject: add simplistic IuUP FSM and strip/add IuUP headers
......................................................................

add simplistic IuUP FSM and strip/add IuUP headers

This should really be using the FSM in libosmocore/laforge/iu_up: take the best
of both sides and integate in the libosmocore FSM implementation, then use it
here.
- in libosmocore, the FSM definition is nicer.
- here, we have correct header checksums.

Introduce using msgb to receive, pass and send RTP packets.

Add/strip IuUP from RTP data (for which msgb is particularly useful).

The payload type on an IuUP conn is maintained as negotiated in the IuUP
Initialization. For the pure RTP side, an SDP "AMR" ptmap attribute is looked
up, so that payload type numbers are translated between IuUP <-> RTP.

Change-Id: Ibc70e0aa00476926dd1f4ea8139c34f31f9cdfa3
---
M configure.ac
M include/osmocom/mgcp/debug.h
A include/osmocom/mgcp/iuup_cn_node.h
A include/osmocom/mgcp/iuup_protocol.h
M include/osmocom/mgcp/mgcp_codec.h
M include/osmocom/mgcp/mgcp_endp.h
M include/osmocom/mgcp/mgcp_internal.h
M src/libosmo-mgcp/Makefile.am
A src/libosmo-mgcp/iuup_cn_node.c
A src/libosmo-mgcp/iuup_protocol.c
M src/libosmo-mgcp/mgcp_codec.c
M src/libosmo-mgcp/mgcp_network.c
M src/libosmo-mgcp/mgcp_osmux.c
M src/osmo-mgw/mgw_main.c
M tests/Makefile.am
A tests/iuup/Makefile.am
A tests/iuup/iuup_test.c
A tests/iuup/iuup_test.err
A tests/iuup/iuup_test.ok
M tests/mgcp/mgcp_test.c
M tests/testsuite.at
21 files changed, 1,241 insertions(+), 196 deletions(-)



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

diff --git a/configure.ac b/configure.ac
index 124f1e6..b9e3b60 100644
--- a/configure.ac
+++ b/configure.ac
@@ -195,6 +195,7 @@
     tests/atlocal
     tests/mgcp_client/Makefile
     tests/mgcp/Makefile
+    tests/iuup/Makefile
     doc/Makefile
     doc/examples/Makefile
     doc/manuals/Makefile
diff --git a/include/osmocom/mgcp/debug.h b/include/osmocom/mgcp/debug.h
index ddeb0dc..1eed769 100644
--- a/include/osmocom/mgcp/debug.h
+++ b/include/osmocom/mgcp/debug.h
@@ -29,6 +29,7 @@
 /* Debug Areas of the code */
 enum {
 	DRTP,
+	DIUUP,
 	Debug_LastEntry,
 };
 
diff --git a/include/osmocom/mgcp/iuup_cn_node.h b/include/osmocom/mgcp/iuup_cn_node.h
new file mode 100644
index 0000000..ca69b4d
--- /dev/null
+++ b/include/osmocom/mgcp/iuup_cn_node.h
@@ -0,0 +1,47 @@
+/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
+/* IuUP CN node, minimal implementation */
+
+/*                                            _____IuUP_CN_____
+ *                                            |               |
+ * UE <--> RNC --PDU-> osmo_iuup_cn_rx_pdu() -+->           ---+-> rx_payload()
+ *          |                                 |               |
+ *          |  <-PDU-- tx_msg() <-------------+--           <-+--- osmo_iuup_cn_tx_payload()
+ *                                            |               |
+ *                                            -----------------
+ */
+
+#pragma once
+
+struct msgb;
+
+typedef int (*osmo_iuup_data_cb_t)(struct msgb *msg, void *node_priv);
+
+struct osmo_iuup_cn_cfg {
+	void *node_priv;
+
+	/* When the IuUP peer sent a voice packet, the clean RTP without the IuUP header is fed to this
+	 * callback. */
+	osmo_iuup_data_cb_t rx_payload;
+
+	/* IuUP handler requests that a PDU shall be sent to the IuUP peer (e.g. the RNC).
+	 * It is guaranteed that the msgb->dst pointer is preserved or copied from the msgb that
+	 * originated the request. */
+	osmo_iuup_data_cb_t tx_msg;
+};
+
+struct osmo_iuup_cn {
+	struct osmo_iuup_cn_cfg cfg;
+	char *name;
+	uint8_t next_frame_nr;
+	int rtp_payload_type;
+};
+
+bool osmo_iuup_cn_is_iuup_init(struct msgb *msg);
+
+struct osmo_iuup_cn *osmo_iuup_cn_init(void *ctx, struct osmo_iuup_cn_cfg *cfg,
+				       const char *name_fmt, ...);
+void osmo_iuup_cn_free(struct osmo_iuup_cn *cn);
+
+int osmo_iuup_cn_tx_payload(struct osmo_iuup_cn *cn, struct msgb *payload);
+
+int osmo_iuup_cn_rx_pdu(struct osmo_iuup_cn *cn, struct msgb *pdu);
diff --git a/include/osmocom/mgcp/iuup_protocol.h b/include/osmocom/mgcp/iuup_protocol.h
new file mode 100644
index 0000000..f4aec1f
--- /dev/null
+++ b/include/osmocom/mgcp/iuup_protocol.h
@@ -0,0 +1,117 @@
+/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
+/* IuUP protocol handling, minimal implementation */
+
+#pragma once
+
+#include <osmocom/core/endian.h>
+#include <osmocom/core/msgb.h>
+
+#define OSMO_IUUP_HEADROOM 32
+
+enum osmo_iuup_pdu_type {
+	OSMO_IUUP_PDU_DATA_WITH_CRC = 0,
+	OSMO_IUUP_PDU_CONTROL_PROCEDURE = 14,
+};
+
+enum osmo_iuup_acknack {
+	OSMO_IUUP_ACKNACK_PROCEDURE = 0,
+	OSMO_IUUP_ACKNACK_ACK = 1,
+	OSMO_IUUP_ACKNACK_NACK = 2,
+};
+
+enum osmo_iuup_procedure {
+	OSMO_IUUP_PROC_INITIALIZATION = 0,
+	OSMO_IUUP_PROC_RATE_CONTROL = 1,
+	OSMO_IUUP_PROC_TIME_ALIGNMENT = 2,
+	OSMO_IUUP_PROC_ERROR_EVENT = 3,
+};
+
+enum osmo_iuup_frame_good {
+	OSMO_IUUP_FRAME_GOOD = 0,
+	OSMO_IUUP_FRAME_BAD = 1,
+	OSMO_IUUP_FRAME_BAD_DUE_TO_RADIO = 2,
+};
+
+struct osmo_iuup_hdr_ctrl {
+#if OSMO_IS_BIG_ENDIAN
+	uint8_t pdu_type:4,
+		ack_nack:2,
+		frame_nr:2;
+	uint8_t mode_version:4,
+		procedure:4;
+	uint8_t header_crc:6,
+		payload_crc_hi:2;
+	uint8_t payload_crc_lo;
+	uint8_t payload[0];
+#elif OSMO_IS_LITTLE_ENDIAN
+	uint8_t frame_nr:2,
+		ack_nack:2,
+		pdu_type:4;
+	uint8_t procedure:4,
+		mode_version:4;
+	uint8_t payload_crc_hi:2,
+		header_crc:6;
+	uint8_t payload_crc_lo;
+	uint8_t payload[0];
+#endif
+} __attribute__((packed));
+
+union osmo_iuup_hdr_ctrl_payload {
+	struct {
+#if OSMO_IS_BIG_ENDIAN
+	uint8_t spare:3,
+		iptis_present:1,
+		subflows:3,
+		chain:1;
+#elif OSMO_IS_LITTLE_ENDIAN
+	uint8_t spare:3,
+		iptis_present:1,
+		subflows:3,
+		chain:1;
+#endif
+	} initialization;
+
+	struct {
+#if OSMO_IS_BIG_ENDIAN
+	uint8_t error_distance:2,
+		error_cause:6;
+#elif OSMO_IS_LITTLE_ENDIAN
+	uint8_t error_cause:6,
+		error_distance:2;
+#endif
+	} error_event;
+};
+
+extern const struct value_string osmo_iuup_error_cause_names[];
+static inline const char *osmo_iuup_error_cause_name(uint8_t val)
+{ return get_value_string(osmo_iuup_error_cause_names, val); }
+
+struct osmo_iuup_hdr_data {
+#if OSMO_IS_BIG_ENDIAN
+	uint8_t pdu_type:4,
+		frame_nr:4;
+	uint8_t frame_good:2,
+		rfci:6;
+	uint8_t header_crc:6,
+		payload_crc_hi:2;
+	uint8_t payload_crc_lo;
+#elif OSMO_IS_LITTLE_ENDIAN
+	uint8_t frame_nr:4,
+		pdu_type:4;
+	uint8_t rfci:6,
+		frame_good:2;
+	uint8_t payload_crc_hi:2,
+		header_crc:6;
+	uint8_t payload_crc_lo;
+#endif
+	uint8_t payload[0];
+} __attribute__((packed));
+
+int osmo_iuup_classify(bool log_errors,
+		       const char *log_label,
+		       struct msgb *pdu,
+		       struct osmo_iuup_hdr_ctrl **is_ctrl,
+		       struct osmo_iuup_hdr_data **is_data);
+bool osmo_iuup_is_init(struct msgb *pdu);
+void osmo_iuup_make_init_ack(struct msgb *ack);
+void osmo_iuup_set_checksums(uint8_t *iuup_header_and_payload, unsigned int header_and_payload_len);
diff --git a/include/osmocom/mgcp/mgcp_codec.h b/include/osmocom/mgcp/mgcp_codec.h
index 3ead60a..caeecb0 100644
--- a/include/osmocom/mgcp/mgcp_codec.h
+++ b/include/osmocom/mgcp/mgcp_codec.h
@@ -5,3 +5,5 @@
 int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name, const struct mgcp_codec_param *param);
 int mgcp_codec_decide(struct mgcp_conn_rtp *conn);
 int mgcp_codec_pt_translate(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst, int payload_type);
+const struct mgcp_rtp_codec *mgcp_codec_pt_find_by_subtype_name(struct mgcp_conn_rtp *conn,
+								const char *subtype_name, unsigned int match_nr);
diff --git a/include/osmocom/mgcp/mgcp_endp.h b/include/osmocom/mgcp/mgcp_endp.h
index 75f093d..9c0dc8c 100644
--- a/include/osmocom/mgcp/mgcp_endp.h
+++ b/include/osmocom/mgcp/mgcp_endp.h
@@ -23,15 +23,27 @@
 
 #pragma once
 
+#include <osmocom/core/msgb.h>
+
 struct sockaddr_in;
 struct mgcp_conn;
+struct mgcp_conn_rtp;
 struct mgcp_endpoint;
 
-/* Callback type for RTP dispatcher functions
-   (e.g mgcp_dispatch_rtp_bridge_cb, see below) */
-typedef int (*mgcp_dispatch_rtp_cb) (int proto, struct sockaddr_in *addr,
-				     char *buf, unsigned int buf_size,
-				     struct mgcp_conn *conn);
+struct osmo_rtp_msg_ctx {
+	int proto;
+	struct mgcp_conn_rtp *conn_src;
+	struct sockaddr_in *from_addr;
+};
+
+#define OSMO_RTP_MSG_CTX(MSGB) (*(struct osmo_rtp_msg_ctx**)&((MSGB)->dst))
+
+#define LOG_ENDP(endp, level, fmt, args...) \
+	LOGP(DRTP, level, "%x@ " fmt, ENDPOINT_NUMBER(endp), ## args)
+
+/* Callback type for RTP dispatcher functions (e.g mgcp_dispatch_rtp_bridge_cb, see below).
+ * The OSMO_RTP_MSG_CTX() should be set appropriately on the msg. */
+typedef int (*mgcp_dispatch_rtp_cb) (struct msgb *msg);
 
 /* Callback type for endpoint specific cleanup actions. This function
  * is automatically executed when a connection is freed (see mgcp_conn_free()
diff --git a/include/osmocom/mgcp/mgcp_internal.h b/include/osmocom/mgcp/mgcp_internal.h
index e9d5d2d..3e83bdf 100644
--- a/include/osmocom/mgcp/mgcp_internal.h
+++ b/include/osmocom/mgcp/mgcp_internal.h
@@ -37,6 +37,13 @@
 #define CONN_ID_BTS "0"
 #define CONN_ID_NET "1"
 
+#define LOG_CONN(conn, level, fmt, args...) \
+	LOGP(DRTP, level, "(%d@ I:%s) " fmt, \
+	     ENDPOINT_NUMBER((conn)->endp), (conn)->id, ## args)
+
+#define LOG_CONN_RTP(conn_rtp, level, fmt, args...) \
+	LOG_CONN(conn_rtp->conn, level, fmt, ## args)
+
 enum mgcp_trunk_type {
 	MGCP_TRUNK_VIRTUAL,
 	MGCP_TRUNK_E1,
@@ -204,6 +211,8 @@
 	} osmux;
 
 	struct rate_ctr_group *rate_ctr_group;
+
+	struct osmo_iuup_cn *iuup;
 };
 
 /*! Connection type, specifies which member of the union "u" in mgcp_conn
@@ -266,11 +275,10 @@
 };
 
 int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
-	      char *buf, int rc, struct mgcp_conn_rtp *conn_src,
+	      struct msgb *msg, struct mgcp_conn_rtp *conn_src,
 	      struct mgcp_conn_rtp *conn_dst);
 int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn);
-int mgcp_dispatch_rtp_bridge_cb(int proto, struct sockaddr_in *addr, char *buf,
-				unsigned int buf_size, struct mgcp_conn *conn);
+int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg);
 void mgcp_cleanup_rtp_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *conn);
 int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port,
 			   struct mgcp_conn_rtp *conn);
@@ -350,3 +358,8 @@
 LOGPENDP((conn)->endp, cat, level, "CI:%s " fmt, \
          (conn)->id, \
          ## args)
+
+void mgcp_patch_and_count(struct mgcp_endpoint *endp,
+			  struct mgcp_rtp_state *state,
+			  struct mgcp_rtp_end *rtp_end,
+			  struct sockaddr_in *addr, struct msgb *msg);
diff --git a/src/libosmo-mgcp/Makefile.am b/src/libosmo-mgcp/Makefile.am
index 587bdd4..7314b22 100644
--- a/src/libosmo-mgcp/Makefile.am
+++ b/src/libosmo-mgcp/Makefile.am
@@ -40,4 +40,6 @@
 	mgcp_conn.c \
 	mgcp_stat.c \
 	mgcp_endp.c \
+	iuup_protocol.c \
+	iuup_cn_node.c \
 	$(NULL)
diff --git a/src/libosmo-mgcp/iuup_cn_node.c b/src/libosmo-mgcp/iuup_cn_node.c
new file mode 100644
index 0000000..239a159
--- /dev/null
+++ b/src/libosmo-mgcp/iuup_cn_node.c
@@ -0,0 +1,211 @@
+/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
+/* IuUP Core Network side protocol handling, minimal implementation */
+
+/*
+ * (C) 2018 by sysmocom - s.f.m.c. GmbH <info at sysmocom.de>
+ * All Rights Reserved
+ *
+ * Author: Neels Hofmeyr <neels at hofmeyr.de>
+ *
+ * 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 <talloc.h>
+#include <errno.h>
+#include <string.h>
+#include <arpa/inet.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/msgb.h>
+
+#include <osmocom/netif/rtp.h>
+
+#include <osmocom/mgcp/iuup_cn_node.h>
+#include <osmocom/mgcp/iuup_protocol.h>
+
+#include <osmocom/mgcp/debug.h>
+
+#define LOG_IUUP_CN(cn, level, fmt, args...) \
+		LOGP(DIUUP, level, "(%s) " fmt, (cn)->name, ## args)
+
+struct osmo_iuup_cn *osmo_iuup_cn_init(void *ctx, struct osmo_iuup_cn_cfg *cfg,
+				       const char *name_fmt, ...)
+{
+	va_list ap;
+	struct osmo_iuup_cn *cn = talloc_zero(ctx, struct osmo_iuup_cn);
+	OSMO_ASSERT(cn);
+
+	cn->cfg = *cfg;
+
+	if (!name_fmt)
+		name_fmt = "-";
+
+	va_start(ap, name_fmt);
+	cn->name = talloc_vasprintf(cn, name_fmt, ap);
+	va_end(ap);
+
+	LOGP(DIUUP, LOGL_INFO, "(%s) Initializing IuUP node\n", cn->name);
+
+	if (!osmo_identifier_valid(cn->name)) {
+		LOGP(DIUUP, LOGL_ERROR, "Attempting to set illegal id for IuUP CN instance: %s\n",
+		     osmo_quote_str(cn->name, -1));
+		talloc_free(cn);
+		return NULL;
+	}
+
+	return cn;
+}
+
+void osmo_iuup_cn_free(struct osmo_iuup_cn *cn)
+{
+	talloc_free(cn);
+}
+
+static int rx_data(struct osmo_iuup_cn *cn, struct msgb *pdu,
+		   struct osmo_iuup_hdr_data *hdr)
+{
+	/* Remove the IuUP bit from the middle of the buffer by writing the RTP header forward. */
+	/* And append AMR 12.2k header "0xf03c". - AD HOC fix */
+	unsigned int pre_hdr_len = ((uint8_t*)hdr) - pdu->data;
+	memmove(pdu->data + sizeof(*hdr) - 2, pdu->data, pre_hdr_len);
+	((uint8_t*)hdr)[2] = 0xf0;
+	((uint8_t*)hdr)[3] = 0x3c;
+	msgb_pull(pdu, sizeof(*hdr) - 2);
+
+	LOGP(DIUUP, LOGL_DEBUG, "(%s) IuUP stripping IuUP header from RTP data\n", cn->name);
+	cn->cfg.rx_payload(pdu, cn->cfg.node_priv);
+
+	return 0;
+}
+
+static int tx_init_ack(struct osmo_iuup_cn *cn, struct msgb *src_pdu)
+{
+	/* Send Initialization Ack PDU back to the sender */
+	int rc;
+	struct msgb *ack = msgb_alloc(4096, "IuUP Initialization Ack");
+	OSMO_ASSERT(ack);
+	ack->dst = src_pdu->dst;
+
+	/* Just copy the RTP header that was sent... TODO: tweak some RTP values?? */
+	memcpy(msgb_put(ack, sizeof(struct rtp_hdr)), src_pdu->data, sizeof(struct rtp_hdr));
+
+	osmo_iuup_make_init_ack(ack);
+
+	LOGP(DIUUP, LOGL_DEBUG, "(%s) Sending Initialization ACK %p\n", cn->name, cn->cfg.node_priv);
+	rc = cn->cfg.tx_msg(ack, cn->cfg.node_priv);
+	msgb_free(ack);
+	return rc;
+}
+
+static int rx_control(struct osmo_iuup_cn *cn, struct msgb *pdu,
+		      struct osmo_iuup_hdr_ctrl *hdr)
+{
+	switch (hdr->procedure) {
+	case OSMO_IUUP_PROC_INITIALIZATION:
+		switch (hdr->ack_nack) {
+		case OSMO_IUUP_ACKNACK_PROCEDURE:
+			LOGP(DIUUP, LOGL_INFO, "(%s) Rx IuUP Initialization, sending ACK\n", cn->name);
+			cn->rtp_payload_type = ((struct rtp_hdr*)pdu->data)->payload_type;
+			return tx_init_ack(cn, pdu);
+
+		default:
+			LOGP(DIUUP, LOGL_DEBUG, "(%s) Rx IuUP Initialization, unhandled ack_nack = %d\n",
+			     cn->name, hdr->ack_nack);
+			break;
+		}
+		/* Continue to log "unexpected procedure" below. */
+		break;
+
+	case OSMO_IUUP_PROC_ERROR_EVENT:
+		{
+			union osmo_iuup_hdr_ctrl_payload *p = (void*)hdr->payload;
+			LOGP(DIUUP, LOGL_ERROR,
+			     "(%s) Rx IuUP Error Event: distance=%u, cause=%u=\"%s\"\n",
+			     cn->name, p->error_event.error_distance, p->error_event.error_cause,
+			     osmo_iuup_error_cause_name(p->error_event.error_cause));
+			return 0;
+		}
+
+	default:
+		break;
+	}
+	LOG_IUUP_CN(cn, LOGL_ERROR,
+		    "Rx control PDU with unexpected procedure: 0x%x acknack=0x%x\n",
+		    hdr->procedure, hdr->ack_nack);
+	return -EINVAL;
+}
+
+/* Feed a received PDU to the IuUP CN node. This function takes ownership of the msgb, it must not be
+ * freed by the caller. */
+int osmo_iuup_cn_rx_pdu(struct osmo_iuup_cn *cn, struct msgb *pdu)
+{
+	struct osmo_iuup_hdr_ctrl *is_ctrl;
+	struct osmo_iuup_hdr_data *is_data;
+	int rc;
+
+	rc = osmo_iuup_classify(true, cn->name, pdu, &is_ctrl, &is_data);
+	if (rc)
+		return rc;
+
+	if (is_ctrl)
+		return rx_control(cn, pdu, is_ctrl);
+	if (is_data)
+		return rx_data(cn, pdu, is_data);
+	return rc;
+}
+
+static uint8_t next_frame_nr(struct osmo_iuup_cn *cn)
+{
+	uint8_t frame_nr = cn->next_frame_nr;
+	cn->next_frame_nr = (frame_nr + 1) & 0x0f;
+	return frame_nr;
+}
+
+/* Send this RTP packet to the IuUP peer: add IuUP header and call the tx_msg() to transmit the resulting
+ * message to the IuUP peer.
+ * Returns 0 on success, negative on error. */
+int osmo_iuup_cn_tx_payload(struct osmo_iuup_cn *cn, struct msgb *pdu)
+{
+	struct rtp_hdr *rtp_was, *rtp;
+	struct osmo_iuup_hdr_data *iuup_hdr;
+
+	/* Splice an IuUP header in between RTP header and payload data */
+	rtp_was = (void*)pdu->data;
+
+	/* copy the RTP header part backwards by the size needed for the IuUP header */
+	/* also strips 2 bytes from the front of RTP payload - AMR header - AD HOC fix */
+	rtp = (void*)msgb_push(pdu, sizeof(*iuup_hdr) - 2);
+	memmove(rtp, rtp_was, sizeof(*rtp));
+
+	/* The IuUP side negotiated a payload type number to use during Initialization. The RTP packet going to the IuUP
+	 * peer should reflect this payload_type. This should already have happened in mgcp_patch_pt(), but can't hurt
+	 * to patch over it again. */
+	rtp->payload_type = cn->rtp_payload_type;
+
+	iuup_hdr = (void*)rtp->data;
+
+	*iuup_hdr = (struct osmo_iuup_hdr_data){
+		.pdu_type = OSMO_IUUP_PDU_DATA_WITH_CRC,
+		.frame_nr = next_frame_nr(cn),
+		.frame_good = OSMO_IUUP_FRAME_GOOD,
+	};
+
+	osmo_iuup_set_checksums((uint8_t*)iuup_hdr, pdu->tail - (uint8_t*)iuup_hdr);
+	LOGP(DIUUP, LOGL_DEBUG, "(%s) IuUP inserting IuUP header in RTP data (frame nr %u)\n",
+	     cn->name, iuup_hdr->frame_nr);
+
+	return cn->cfg.tx_msg(pdu, cn->cfg.node_priv);
+}
diff --git a/src/libosmo-mgcp/iuup_protocol.c b/src/libosmo-mgcp/iuup_protocol.c
new file mode 100644
index 0000000..bfb009d
--- /dev/null
+++ b/src/libosmo-mgcp/iuup_protocol.c
@@ -0,0 +1,286 @@
+/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
+/* IuUP Core Network side protocol, minimal implementation */
+
+/*
+ * (C) 2018 by sysmocom - s.f.m.c. GmbH <info at sysmocom.de>
+ * All Rights Reserved
+ *
+ * Author: Neels Hofmeyr <neels at hofmeyr.de>
+ *
+ * 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/mgcp/iuup_protocol.h>
+#include <osmocom/mgcp/debug.h>
+#include <osmocom/netif/rtp.h>
+
+/* Calculating two bytes of CRC is ok to do by a loop */
+static uint8_t header_crc6(const uint8_t *hdr)
+{
+    int bit;
+    /* Polynomial: D^6 + D^5 + D^3 + D^2 + D^1 + 1
+     * that's 1101111 or 0x6f;
+     * align its lowest bit with a uint16_t's highest bit: */
+    uint32_t polynomial = 0x6f << 15; // 00110111 10000000 00000000
+    uint32_t remainder = ( ((uint32_t)hdr[0]) << 8 | hdr[1] ) << 6;
+
+    for (bit = 15; bit >= 0; bit--)
+    {
+        if (remainder & (0x40 << bit))
+            remainder ^= polynomial;
+        polynomial >>= 1;
+    }
+
+    return remainder;
+}
+
+/*
+ * Charles Michael Heard's CRC-10 code, from
+ *
+ *      http://web.archive.org/web/20061005231950/http://cell-relay.indiana.edu/cell-relay/publications/software/CRC/crc10.html
+ *
+ * with the CRC table initialized with values computed by
+ * his "gen_byte_crc10_table()" routine, rather than by calling that
+ * routine at run time, and with various data type cleanups.
+ */
+static const uint16_t byte_crc10_table[256] = {
+	0x0000, 0x0233, 0x0255, 0x0066, 0x0299, 0x00aa, 0x00cc, 0x02ff,
+	0x0301, 0x0132, 0x0154, 0x0367, 0x0198, 0x03ab, 0x03cd, 0x01fe,
+	0x0031, 0x0202, 0x0264, 0x0057, 0x02a8, 0x009b, 0x00fd, 0x02ce,
+	0x0330, 0x0103, 0x0165, 0x0356, 0x01a9, 0x039a, 0x03fc, 0x01cf,
+	0x0062, 0x0251, 0x0237, 0x0004, 0x02fb, 0x00c8, 0x00ae, 0x029d,
+	0x0363, 0x0150, 0x0136, 0x0305, 0x01fa, 0x03c9, 0x03af, 0x019c,
+	0x0053, 0x0260, 0x0206, 0x0035, 0x02ca, 0x00f9, 0x009f, 0x02ac,
+	0x0352, 0x0161, 0x0107, 0x0334, 0x01cb, 0x03f8, 0x039e, 0x01ad,
+	0x00c4, 0x02f7, 0x0291, 0x00a2, 0x025d, 0x006e, 0x0008, 0x023b,
+	0x03c5, 0x01f6, 0x0190, 0x03a3, 0x015c, 0x036f, 0x0309, 0x013a,
+	0x00f5, 0x02c6, 0x02a0, 0x0093, 0x026c, 0x005f, 0x0039, 0x020a,
+	0x03f4, 0x01c7, 0x01a1, 0x0392, 0x016d, 0x035e, 0x0338, 0x010b,
+	0x00a6, 0x0295, 0x02f3, 0x00c0, 0x023f, 0x000c, 0x006a, 0x0259,
+	0x03a7, 0x0194, 0x01f2, 0x03c1, 0x013e, 0x030d, 0x036b, 0x0158,
+	0x0097, 0x02a4, 0x02c2, 0x00f1, 0x020e, 0x003d, 0x005b, 0x0268,
+	0x0396, 0x01a5, 0x01c3, 0x03f0, 0x010f, 0x033c, 0x035a, 0x0169,
+	0x0188, 0x03bb, 0x03dd, 0x01ee, 0x0311, 0x0122, 0x0144, 0x0377,
+	0x0289, 0x00ba, 0x00dc, 0x02ef, 0x0010, 0x0223, 0x0245, 0x0076,
+	0x01b9, 0x038a, 0x03ec, 0x01df, 0x0320, 0x0113, 0x0175, 0x0346,
+	0x02b8, 0x008b, 0x00ed, 0x02de, 0x0021, 0x0212, 0x0274, 0x0047,
+	0x01ea, 0x03d9, 0x03bf, 0x018c, 0x0373, 0x0140, 0x0126, 0x0315,
+	0x02eb, 0x00d8, 0x00be, 0x028d, 0x0072, 0x0241, 0x0227, 0x0014,
+	0x01db, 0x03e8, 0x038e, 0x01bd, 0x0342, 0x0171, 0x0117, 0x0324,
+	0x02da, 0x00e9, 0x008f, 0x02bc, 0x0043, 0x0270, 0x0216, 0x0025,
+	0x014c, 0x037f, 0x0319, 0x012a, 0x03d5, 0x01e6, 0x0180, 0x03b3,
+	0x024d, 0x007e, 0x0018, 0x022b, 0x00d4, 0x02e7, 0x0281, 0x00b2,
+	0x017d, 0x034e, 0x0328, 0x011b, 0x03e4, 0x01d7, 0x01b1, 0x0382,
+	0x027c, 0x004f, 0x0029, 0x021a, 0x00e5, 0x02d6, 0x02b0, 0x0083,
+	0x012e, 0x031d, 0x037b, 0x0148, 0x03b7, 0x0184, 0x01e2, 0x03d1,
+	0x022f, 0x001c, 0x007a, 0x0249, 0x00b6, 0x0285, 0x02e3, 0x00d0,
+	0x011f, 0x032c, 0x034a, 0x0179, 0x0386, 0x01b5, 0x01d3, 0x03e0,
+	0x021e, 0x002d, 0x004b, 0x0278, 0x0087, 0x02b4, 0x02d2, 0x00e1
+};
+
+static uint16_t crc10(uint16_t crc10_accum, const uint8_t *payload, unsigned int payload_len)
+{
+	int i;
+
+	for (i = 0; i < payload_len; i++) {
+		crc10_accum = ((crc10_accum << 8) & 0x300)
+			^ byte_crc10_table[(crc10_accum >> 2) & 0xff]
+			^ payload[i];
+	}
+	return crc10_accum;
+}
+
+/* When a payload of a multiple of bytes has run through, we need to still feed 10 bits of zeros into the
+ * CRC10 to get the payload's checksum result that we can send to a peer. That can't be done with above
+ * table, because it acts as if full 16 bits are fed. This stops after 10 bits. */
+static uint16_t crc10_remainder(uint16_t crc10_accum)
+{
+    int bit;
+    /* Polynomial: D^10 + D^9 + D^5 + D^4 + D^1 + 1
+     * that's 11000110011 or 0x633;
+     * align its lowest bit with a 10bit value's highest bit: */
+    uint32_t polynomial = 0x633 << 9; // 1100 01100110 00000000
+    uint32_t remainder = ((uint32_t)crc10_accum) << 10;
+
+    /* Run on 10 bits */
+    for (bit = 9; bit >= 0; bit--)
+    {
+        if (remainder & ((1 << 10) << bit))
+            remainder ^= polynomial;
+        polynomial >>= 1;
+    }
+
+    return remainder & 0x3ff;
+}
+
+static uint16_t payload_crc10(const uint8_t *payload, unsigned int payload_len)
+{
+	uint16_t crc10_accum = crc10(0, payload, payload_len);
+	return crc10_remainder(crc10_accum);
+}
+
+/* Given an IuUP PDU data block, write the correct header and payload CRC checksums at the right places.
+ */
+void osmo_iuup_set_checksums(uint8_t *iuup_header_and_payload, unsigned int header_and_payload_len)
+{
+	/* For both data and ctrl, the checksums and payload are at the same offset */
+	struct osmo_iuup_hdr_data *hdr = (void*)iuup_header_and_payload;
+	uint16_t crc;
+	unsigned int payload_len;
+
+	hdr->header_crc = header_crc6(iuup_header_and_payload);
+
+	payload_len = iuup_header_and_payload + header_and_payload_len - hdr->payload;
+	crc = payload_crc10(hdr->payload, payload_len);
+	hdr->payload_crc_hi = (crc >> 8) & 0x3;
+	hdr->payload_crc_lo = crc & 0xff;
+
+}
+
+/* Validate minimum message sizes, IuUP PDU type, header- and payload checksums. If it is a Control
+ * Procedure PDU, return the header position in is_ctrl, if it is a Data PDU, return the header position
+ * in is_data. If log_errors is true, log on DIUUP with the given log label for context. Return NULL in
+ * both is_ctrl and is_data, and return a negative error code if the PDU could not be identified as a
+ * valid RTP PDU containing an IuUP part. */
+int osmo_iuup_classify(bool log_errors,
+		       const char *log_label,
+		       struct msgb *pdu,
+		       struct osmo_iuup_hdr_ctrl **is_ctrl,
+		       struct osmo_iuup_hdr_data **is_data)
+{
+	struct rtp_hdr *rtp = (void*)pdu->data;
+	struct osmo_iuup_hdr_ctrl *hdr = (void*)rtp->data;
+	unsigned int payload_len;
+	uint16_t crc_calculated;
+	uint16_t crc_from_peer;
+
+#define ERR(fmt, args...) do { \
+			if (log_errors) \
+				LOGP(DIUUP, LOGL_ERROR, "(%s) " fmt, log_label? : "-", ## args); \
+			return -EINVAL; \
+		} while (0)
+
+	if (is_ctrl)
+		*is_ctrl = NULL;
+	if (is_data)
+		*is_data = NULL;
+
+	/* We need at least a header of 4 bytes. The osmo_iuup_hdr_ctrl already includes a byte of
+	 * payload, so use osmo_iuup_hdr_data to check the minimum here. */
+	if (pdu->len < (sizeof(*rtp) + sizeof(struct osmo_iuup_hdr_data)))
+		ERR("IuUP PDU too short: %u\n", pdu->len);
+
+	/* Let's not validate checksums if the header type isn't sane */
+	switch (hdr->pdu_type) {
+	case OSMO_IUUP_PDU_DATA_WITH_CRC:
+		/* If the caller isn't interested in data PDUs, cut short here. */
+		if (!is_data)
+			return 0;
+		break;
+	case OSMO_IUUP_PDU_CONTROL_PROCEDURE:
+		/* If the caller isn't interested in control PDUs, cut short here. */
+		if (!is_ctrl)
+			return 0;
+		if (pdu->len < (sizeof(*rtp) + sizeof(struct osmo_iuup_hdr_ctrl)))
+			ERR("IuUP control PDU too short: %u\n", pdu->len);
+		break;
+	default:
+		ERR("IuUP with invalid type: %u\n", hdr->pdu_type);
+	}
+
+	/* For both data and ctrl, the checksums and payload are at the same offset */
+
+	crc_calculated = header_crc6((uint8_t*)hdr);
+	if (crc_calculated != hdr->header_crc)
+		ERR("IuUP PDU with invalid header CRC (peer sent 0x%x, calculated 0x%x)\n",
+		    hdr->header_crc, crc_calculated);
+
+	payload_len = pdu->tail - hdr->payload;
+	crc_calculated = payload_crc10(hdr->payload, payload_len);
+	crc_from_peer = (((uint16_t)hdr->payload_crc_hi) << 8) | hdr->payload_crc_lo;
+	if (crc_from_peer != crc_calculated)
+		ERR("IuUP PDU with invalid payload CRC (peer sent 0x%x, calculated 0x%x)\n",
+		    crc_from_peer, crc_calculated);
+
+	switch (hdr->pdu_type) {
+	case OSMO_IUUP_PDU_DATA_WITH_CRC:
+		if (is_data)
+			*is_data = (void*)hdr;
+		return 0;
+	case OSMO_IUUP_PDU_CONTROL_PROCEDURE:
+		if (is_ctrl)
+			*is_ctrl = hdr;
+		return 0;
+	default:
+		ERR("IuUP with invalid type: %u\n", hdr->pdu_type);
+	}
+#undef ERR
+}
+
+/* Return true if this RTP packet contains an IuUP Initialization header (detect IuUP peer). */
+bool osmo_iuup_is_init(struct msgb *pdu)
+{
+ 	struct osmo_iuup_hdr_ctrl *is_ctrl;
+	osmo_iuup_classify(false, NULL, pdu, &is_ctrl, NULL);
+	return is_ctrl
+		&& is_ctrl->procedure == OSMO_IUUP_PROC_INITIALIZATION
+		&& is_ctrl->ack_nack == OSMO_IUUP_ACKNACK_PROCEDURE;
+}
+
+/* Append an IuUP Initialization ACK message */
+void osmo_iuup_make_init_ack(struct msgb *ack)
+{
+	/* Send Initialization Ack PDU back to the sender */
+ 	struct osmo_iuup_hdr_ctrl *hdr;
+	OSMO_ASSERT(ack);
+
+	hdr = (void*)msgb_put(ack, sizeof(*hdr));
+
+	*hdr = (struct osmo_iuup_hdr_ctrl){
+		.pdu_type = OSMO_IUUP_PDU_CONTROL_PROCEDURE,
+		.ack_nack = OSMO_IUUP_ACKNACK_ACK,
+		.procedure = OSMO_IUUP_PROC_INITIALIZATION,
+	};
+
+	osmo_iuup_set_checksums((uint8_t*)hdr, sizeof(*hdr));
+}
+
+const struct value_string osmo_iuup_error_cause_names[] = {
+	{ 0, "CRC error of frame header" },
+	{ 1, "CRC error of frame payload" },
+	{ 2, "Unexpected frame number" },
+	{ 3, "Frame loss" },
+	{ 4, "PDU type unknown" },
+	{ 5, "Unknown procedure" },
+	{ 6, "Unknown reserved value" },
+	{ 7, "Unknown field" },
+	{ 8, "Frame too short" },
+	{ 9, "Missing fields" },
+	{ 16, "Unexpected PDU type" },
+	{ 17, "spare" },
+	{ 18, "Unexpected procedure" },
+	{ 19, "Unexpected RFCI" },
+	{ 20, "Unexpected value" },
+	{ 42, "Initialisation failure" },
+	{ 43, "Initialisation failure (network error, timer expiry)" },
+	{ 44, "Initialisation failure (Iu UP function error, repeated NACK)" },
+	{ 45, "Rate control failure" },
+	{ 46, "Error event failure" },
+	{ 47, "Time Alignment not supported" },
+	{ 48, "Requested Time Alignment not possible" },
+	{ 49, "Iu UP Mode version not supported" },
+	{}
+};
diff --git a/src/libosmo-mgcp/mgcp_codec.c b/src/libosmo-mgcp/mgcp_codec.c
index 93a8157..cf9f942 100644
--- a/src/libosmo-mgcp/mgcp_codec.c
+++ b/src/libosmo-mgcp/mgcp_codec.c
@@ -454,3 +454,19 @@
 
 	return codec_dst->payload_type;
 }
+
+const struct mgcp_rtp_codec *mgcp_codec_pt_find_by_subtype_name(struct mgcp_conn_rtp *conn,
+								const char *subtype_name, unsigned int match_nr)
+{
+	int i;
+	for (i = 0; i < conn->end.codecs_assigned; i++) {
+		if (!strcmp(conn->end.codecs[i].subtype_name, subtype_name)) {
+			if (match_nr) {
+				match_nr--;
+				continue;
+			}
+			return &conn->end.codecs[i];
+		}
+	}
+	return NULL;
+}
diff --git a/src/libosmo-mgcp/mgcp_network.c b/src/libosmo-mgcp/mgcp_network.c
index bb29d2b..af2a647 100644
--- a/src/libosmo-mgcp/mgcp_network.c
+++ b/src/libosmo-mgcp/mgcp_network.c
@@ -45,6 +45,8 @@
 #include <osmocom/mgcp/mgcp_codec.h>
 #include <osmocom/mgcp/debug.h>
 #include <osmocom/codec/codec.h>
+#include <osmocom/mgcp/iuup_cn_node.h>
+#include <osmocom/mgcp/iuup_protocol.h>
 
 
 #define RTP_SEQ_MOD		(1 << 16)
@@ -52,11 +54,13 @@
 #define RTP_MAX_MISORDER	100
 #define RTP_BUF_SIZE		4096
 
-enum {
+enum rtp_proto {
 	MGCP_PROTO_RTP,
 	MGCP_PROTO_RTCP,
 };
 
+static int rx_rtp(struct msgb *msg);
+
 /*! Determine the local rtp bind IP-address.
  *  \param[out] addr caller provided memory to store the resulting IP-Address
  *  \param[in] endp mgcp endpoint, that holds a copy of the VTY parameters
@@ -469,19 +473,38 @@
  * Patch the payload type of an RTP packet so that it uses the payload type
  * that is valid for the destination connection (conn_dst) */
 static int mgcp_patch_pt(struct mgcp_conn_rtp *conn_src,
-			 struct mgcp_conn_rtp *conn_dst, char *data, int len)
+			 struct mgcp_conn_rtp *conn_dst, struct msgb *msg)
 {
 	struct rtp_hdr *rtp_hdr;
 	uint8_t pt_in;
 	int pt_out;
 
-	OSMO_ASSERT(len >= sizeof(struct rtp_hdr));
-	rtp_hdr = (struct rtp_hdr *)data;
+	OSMO_ASSERT(msg->len >= sizeof(struct rtp_hdr));
+	rtp_hdr = (struct rtp_hdr *)msg->data;
 
-	pt_in = rtp_hdr->payload_type;
-	pt_out = mgcp_codec_pt_translate(conn_src, conn_dst, pt_in);
-	if (pt_out < 0)
-		return -EINVAL;
+	if (conn_src->iuup) {
+		/* The source is an IuUP payload. We have received a dynamic payload type number on the IuUP side, and
+		 * towards the pure RTP side it should go out as "AMR/8000". Make sure that the payload type number in
+		 * the RTP packet matches the a=rtpmap:N payload type number configured for AMR. */
+		const struct mgcp_rtp_codec *amr_codec = mgcp_codec_pt_find_by_subtype_name(conn_dst, "AMR", 0);
+
+		if (!amr_codec) {
+			/* There is no AMR codec configured on the outgoing conn. */
+			return -EINVAL;
+		}
+
+		pt_out = amr_codec->payload_type;
+	} else if (conn_dst->iuup) {
+		/* The destination is an IuUP payload. Use whatever payload number was negotiated during IuUP
+		 * Initialization. */
+		pt_out = conn_dst->iuup->rtp_payload_type;
+	} else {
+		/* Both sides are normal RTP payloads. Consult the rtpmap settings received by SDP. */
+		pt_in = rtp_hdr->payload_type;
+		pt_out = mgcp_codec_pt_translate(conn_src, conn_dst, pt_in);
+		if (pt_out < 0)
+			return -EINVAL;
+	}
 
 	rtp_hdr->payload_type = (uint8_t) pt_out;
 	return 0;
@@ -496,7 +519,7 @@
 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)
+			  struct sockaddr_in *addr, struct msgb *msg)
 {
 	uint32_t arrival_time;
 	int32_t transit;
@@ -504,11 +527,12 @@
 	uint32_t timestamp, ssrc;
 	struct rtp_hdr *rtp_hdr;
 	int payload = rtp_end->codec->payload_type;
+	unsigned int len = msg->len;
 
 	if (len < sizeof(*rtp_hdr))
 		return;
 
-	rtp_hdr = (struct rtp_hdr *)data;
+	rtp_hdr = (struct rtp_hdr *)msg->data;
 	seq = ntohs(rtp_hdr->sequence);
 	timestamp = ntohl(rtp_hdr->timestamp);
 	arrival_time = get_current_ts(rtp_end->codec->rate);
@@ -751,15 +775,14 @@
 
 /* Forward data to a debug tap. This is debug function that is intended for
  * debugging the voice traffic with tools like gstreamer */
-static void forward_data(int fd, struct mgcp_rtp_tap *tap, const char *buf,
-			 int len)
+static void forward_data(int fd, struct mgcp_rtp_tap *tap, struct msgb *msg)
 {
 	int rc;
 
 	if (!tap->enabled)
 		return;
 
-	rc = sendto(fd, buf, len, 0, (struct sockaddr *)&tap->forward,
+	rc = sendto(fd, msg->data, msg->len, 0, (struct sockaddr *)&tap->forward,
 		    sizeof(tap->forward));
 
 	if (rc < 0)
@@ -777,7 +800,7 @@
  *  \param[in] conn_dst associated destination connection
  *  \returns 0 on success, -1 on ERROR */
 int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
-	      char *buf, int len, struct mgcp_conn_rtp *conn_src,
+	      struct msgb *msg, struct mgcp_conn_rtp *conn_src,
 	      struct mgcp_conn_rtp *conn_dst)
 {
 	/*! When no destination connection is available (e.g. when only one
@@ -789,6 +812,7 @@
 	struct mgcp_rtp_state *rtp_state;
 	char *dest_name;
 	int rc;
+	int len;
 
 	OSMO_ASSERT(conn_src);
 	OSMO_ASSERT(conn_dst);
@@ -812,7 +836,7 @@
 	 * should not occur if transcoding is consequently avoided. Until
 	 * we have transcoding support in osmo-mgw we can not resolve this. */
 	if (is_rtp) {
-		rc = mgcp_patch_pt(conn_src, conn_dst, buf, len);
+		rc = mgcp_patch_pt(conn_src, conn_dst, msg);
 		if (rc < 0) {
 			LOGPENDP(endp, DRTP, LOGL_DEBUG,
 				 "can not patch PT because no suitable egress codec was found.\n");
@@ -837,27 +861,27 @@
 	} else if (is_rtp) {
 		int cont;
 		int nbytes = 0;
-		int buflen = len;
+		int buflen = msg->len;
 		do {
 			/* Run transcoder */
 			cont = endp->cfg->rtp_processing_cb(endp, rtp_end,
-							    buf, &buflen,
+							    (char*)msg->data, &buflen,
 							    RTP_BUF_SIZE);
 			if (cont < 0)
 				break;
 
 			if (addr)
 				mgcp_patch_and_count(endp, rtp_state, rtp_end,
-						     addr, buf, buflen);
+						     addr, msg);
 
 			if (amr_oa_bwe_convert_indicated(conn_dst->end.codec)) {
-				amr_oa_bwe_convert(endp, buf, &buflen,
+				amr_oa_bwe_convert(endp, (char*)msg->data, &buflen,
 						   conn_dst->end.codec->param.amr_octet_aligned);
 			}
 			else if (rtp_end->rfc5993_hr_convert
 			    && strcmp(conn_src->end.codec->subtype_name,
 				      "GSM-HR-08") == 0)
-				rfc5993_hr_convert(endp, buf, &buflen);
+				rfc5993_hr_convert(endp, (char*)msg->data, &buflen);
 
 			LOGPENDP(endp, DRTP, LOGL_DEBUG,
 				 "process/send to %s %s "
@@ -868,8 +892,9 @@
 
 			/* Forward a copy of the RTP data to a debug ip/port */
 			forward_data(rtp_end->rtp.fd, &conn_src->tap_out,
-				     buf, buflen);
+				     msg);
 
+#if 0
 			/* 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
@@ -877,7 +902,7 @@
 			 */
 			if (!rtp_state->patched_first_rtp_payload
 			    && conn_src->conn->mode == MGCP_CONN_LOOPBACK) {
-				uint8_t *data = (uint8_t *) & buf[12];
+				uint8_t *data = msg->data + 12;
 				if (data[0] == 0xe0) {
 					data[0] = 0xe4;
 					data[1] = 0x00;
@@ -887,10 +912,13 @@
 						 " to fake an IuUP Initialization Ack\n");
 				}
 			}
+#endif
 
-			len = mgcp_udp_send(rtp_end->rtp.fd,
-					    &rtp_end->addr,
-					    rtp_end->rtp_port, buf, buflen);
+			if (conn_dst->iuup)
+				len = osmo_iuup_cn_tx_payload(conn_dst->iuup, msg);
+			else
+				len = mgcp_udp_send(rtp_end->rtp.fd, &rtp_end->addr, rtp_end->rtp_port,
+						    (char*)msg->data, msg->len);
 
 			if (len <= 0)
 				return len;
@@ -911,7 +939,7 @@
 
 		len = mgcp_udp_send(rtp_end->rtcp.fd,
 				    &rtp_end->addr,
-				    rtp_end->rtcp_port, buf, len);
+				    rtp_end->rtcp_port, (char*)msg->data, msg->len);
 
 		rate_ctr_inc(&conn_dst->rate_ctr_group->ctr[RTP_PACKETS_TX_CTR]);
 		rate_ctr_add(&conn_dst->rate_ctr_group->ctr[RTP_OCTETS_TX_CTR], len);
@@ -922,45 +950,6 @@
 	return 0;
 }
 
-/* Helper function for mgcp_recv(),
-   Receive one RTP Packet + Originating address from file descriptor */
-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);
-	struct sockaddr_in addr_sink;
-	char buf_sink[RTP_BUF_SIZE];
-	bool tossed = false;
-
-	if (!addr)
-		addr = &addr_sink;
-	if (!buf) {
-		tossed = true;
-		buf = buf_sink;
-		bufsize = sizeof(buf_sink);
-	}
-
-	rc = recvfrom(fd, buf, bufsize, 0, (struct sockaddr *)addr, &slen);
-
-	LOGPENDP(endp, DRTP, LOGL_DEBUG,
-	     "receiving %u bytes length packet from %s:%u ...\n",
-	     rc, inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
-
-	if (rc < 0) {
-		LOGPENDP(endp, DRTP, LOGL_ERROR,
-			 "failed to receive packet, errno: %d/%s\n",
-			 errno, strerror(errno));
-		return -1;
-	}
-
-	if (tossed) {
-		LOGPENDP(endp, DRTP, LOGL_ERROR, "packet tossed\n");
-	}
-
-	return rc;
-}
-
 /* Check if the origin (addr) matches the address/port data of the RTP
  * connections. */
 static int check_rtp_origin(struct mgcp_conn_rtp *conn,
@@ -1053,7 +1042,7 @@
 
 /* Do some basic checks to make sure that the RTCP packets we are going to
  * process are not complete garbage */
-static int check_rtcp(char *buf, unsigned int buf_size)
+static int check_rtcp(struct mgcp_conn_rtp *conn_src, struct msgb *msg)
 {
 	struct rtcp_hdr *hdr;
 	unsigned int len;
@@ -1061,33 +1050,45 @@
 
 	/* RTPC packets that are just a header without data do not make
 	 * any sense. */
-	if (buf_size < sizeof(struct rtcp_hdr))
+	if (msg->len < sizeof(struct rtcp_hdr)) {
+		LOG_CONN_RTP(conn_src, LOGL_ERROR, "RTCP packet too short (%u < %zu)\n",
+			     msg->len, sizeof(struct rtcp_hdr));
 		return -EINVAL;
+	}
 
 	/* Make sure that the length of the received packet does not exceed
 	 * the available buffer size */
-	hdr = (struct rtcp_hdr *)buf;
+	hdr = (struct rtcp_hdr *)msg->data;
 	len = (osmo_ntohs(hdr->length) + 1) * 4;
-	if (len > buf_size)
+	if (len > msg->len) {
+		LOG_CONN_RTP(conn_src, LOGL_ERROR, "RTCP header length exceeds packet size (%u > %u)\n",
+			     len, msg->len);
 		return -EINVAL;
+	}
 
 	/* Make sure we accept only packets that have a proper packet type set
 	 * See also: http://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml */
 	type = hdr->type;
-	if ((type < 192 || type > 195) && (type < 200 || type > 213))
+	if ((type < 192 || type > 195) && (type < 200 || type > 213)) {
+		LOG_CONN_RTP(conn_src, LOGL_ERROR, "RTCP header: invalid type: %u\n", type);
 		return -EINVAL;
+	}
 
 	return 0;
 }
 
 /* Do some basic checks to make sure that the RTP packets we are going to
  * process are not complete garbage */
-static int check_rtp(char *buf, unsigned int buf_size)
+static int check_rtp(struct mgcp_conn_rtp *conn_src, struct msgb *msg)
 {
-	/* RTP packets that are just a header without data do not make
-	 * any sense. */
-	if (buf_size < sizeof(struct rtp_hdr))
-		return -EINVAL;
+	size_t min_size = sizeof(struct rtp_hdr);
+	if (conn_src->iuup)
+		min_size += sizeof(struct osmo_iuup_hdr_data);
+	if (msg->len < min_size) {
+		LOG_CONN_RTP(conn_src, LOGL_ERROR, "RTP packet too short (%u < %zu)\n",
+			     msg->len, min_size);
+		return -1;
+	}
 
 	/* FIXME: Add more checks, the reason why we do not check more than
 	 * the length is because we currently handle IUUP packets as RTP
@@ -1098,86 +1099,14 @@
 	return 0;
 }
 
-/* Receive RTP data from a specified source connection and dispatch it to a
- * destination connection. */
-static int mgcp_recv(int *proto, struct sockaddr_in *addr, char *buf,
-		     unsigned int buf_size, struct osmo_fd *fd)
-{
-	struct mgcp_endpoint *endp;
-	struct mgcp_conn_rtp *conn;
-	struct mgcp_trunk_config *tcfg;
-	int rc;
-
-	conn = (struct mgcp_conn_rtp*) fd->data;
-	endp = conn->conn->endp;
-	tcfg = endp->tcfg;
-
-	LOGPCONN(conn->conn, DRTP, LOGL_DEBUG, "receiving RTP/RTCP packet...\n");
-
-	rc = receive_from(endp, fd->fd, addr, buf, buf_size);
-	if (rc <= 0)
-		return -1;
-
-	/* FIXME: The way how we detect the protocol looks odd. We should look
-	 * into the packet header. Also we should introduce a packet type
-	 * MGCP_PROTO_IUUP because currently we handle IUUP packets like RTP
-	 * packets which is problematic. */
-	*proto = fd == &conn->end.rtp ? MGCP_PROTO_RTP : MGCP_PROTO_RTCP;
-
-	if (*proto == MGCP_PROTO_RTP) {
-		if (check_rtp(buf, rc) < 0) {
-			LOGPCONN(conn->conn, DRTP, LOGL_ERROR,
-				 "invalid RTP packet received -- packet tossed\n");
-			return -1;
-		}
-	} else if (*proto == MGCP_PROTO_RTCP) {
-		if (check_rtcp(buf, rc) < 0) {
-			LOGPCONN(conn->conn, DRTP, LOGL_ERROR,
-				 "invalid RTCP packet received -- packet tossed\n");
-			return -1;
-		}
-	}
-
-	LOGPCONN(conn->conn, DRTP, LOGL_DEBUG, "");
-	LOGPC(DRTP, LOGL_DEBUG, "receiving from %s %s %d\n",
-	      conn->conn->name, inet_ntoa(addr->sin_addr),
-	      ntohs(addr->sin_port));
-	LOGPENDP(endp, DRTP, LOGL_DEBUG, "conn:%s\n", mgcp_conn_dump(conn->conn));
-
-	/* Check if the origin of the RTP packet seems plausible */
-	if (tcfg->rtp_accept_all == 0) {
-		if (check_rtp_origin(conn, addr) != 0)
-			return -1;
-	}
-
-	/* Filter out dummy message */
-	if (rc == 1 && buf[0] == MGCP_DUMMY_LOAD) {
-		LOGPCONN(conn->conn, DRTP, LOGL_NOTICE,
-			 "dummy message received\n");
-		LOGPCONN(conn->conn, DRTP, LOGL_ERROR,
-			 "packet tossed\n");
-		return 0;
-	}
-
-	/* Increment RX statistics */
-	rate_ctr_inc(&conn->rate_ctr_group->ctr[RTP_PACKETS_RX_CTR]);
-	rate_ctr_add(&conn->rate_ctr_group->ctr[RTP_OCTETS_RX_CTR], rc);
-
-	/* Forward a copy of the RTP data to a debug ip/port */
-	forward_data(fd->fd, &conn->tap_in, buf, rc);
-
-	return rc;
-}
-
 /* Send RTP data. Possible options are standard RTP packet
  * transmission or trsmission via an osmux connection */
-static int mgcp_send_rtp(int proto, struct sockaddr_in *addr, char *buf,
-			 unsigned int buf_size,
-			 struct mgcp_conn_rtp *conn_src,
-			 struct mgcp_conn_rtp *conn_dst)
+static int mgcp_send_rtp(struct mgcp_conn_rtp *conn_dst, struct msgb *msg)
 {
-	struct mgcp_endpoint *endp;
-	endp = conn_src->conn->endp;
+	enum rtp_proto proto = OSMO_RTP_MSG_CTX(msg)->proto;
+	struct mgcp_conn_rtp *conn_src = OSMO_RTP_MSG_CTX(msg)->conn_src;
+	struct sockaddr_in *from_addr = OSMO_RTP_MSG_CTX(msg)->from_addr;
+	struct mgcp_endpoint *endp = conn_src->conn->endp;
 
 	LOGPENDP(endp, DRTP, LOGL_DEBUG, "destin conn:%s\n",
 		 mgcp_conn_dump(conn_dst->conn));
@@ -1196,13 +1125,13 @@
 			 "endpoint type is MGCP_RTP_DEFAULT, "
 			 "using mgcp_send() to forward data directly\n");
 		return mgcp_send(endp, proto == MGCP_PROTO_RTP,
-				 addr, buf, buf_size, conn_src, conn_dst);
+				 from_addr, msg, conn_src, conn_dst);
 	case MGCP_OSMUX_BSC_NAT:
 	case MGCP_OSMUX_BSC:
 		LOGPENDP(endp, DRTP, LOGL_DEBUG,
 			 "endpoint type is MGCP_OSMUX_BSC_NAT, "
 			 "using osmux_xfrm_to_osmux() to forward data through OSMUX\n");
-		return osmux_xfrm_to_osmux(buf, buf_size, conn_dst);
+		return osmux_xfrm_to_osmux((char*)msg->data, msg->len, conn_dst);
 	}
 
 	/* If the data has not been handled/forwarded until here, it will
@@ -1220,10 +1149,12 @@
  *  \param[in] buf_size size data length of buf
  *  \param[in] conn originating connection
  *  \returns 0 on success, -1 on ERROR */
-int mgcp_dispatch_rtp_bridge_cb(int proto, struct sockaddr_in *addr, char *buf,
-				unsigned int buf_size, struct mgcp_conn *conn)
+int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg)
 {
+	struct mgcp_conn_rtp *conn_src = OSMO_RTP_MSG_CTX(msg)->conn_src;
+	struct mgcp_conn *conn = conn_src->conn;
 	struct mgcp_conn *conn_dst;
+	struct sockaddr_in *from_addr = OSMO_RTP_MSG_CTX(msg)->from_addr;
 
 	/*! NOTE: This callback function implements the endpoint specific
 	 *  dispatch bahviour of an rtp bridge/proxy endpoint. It is assumed
@@ -1242,11 +1173,10 @@
 		 * address data from the UDP packet header to patch the
 		 * outgoing address in connection on the fly */
 		if (conn->u.rtp.end.rtp_port == 0) {
-			conn->u.rtp.end.addr = addr->sin_addr;
-			conn->u.rtp.end.rtp_port = addr->sin_port;
+			conn->u.rtp.end.addr = from_addr->sin_addr;
+			conn->u.rtp.end.rtp_port = from_addr->sin_port;
 		}
-		return mgcp_send_rtp(proto, addr, buf,
-				     buf_size, &conn->u.rtp, &conn->u.rtp);
+		return mgcp_send_rtp(conn_src, msg);
 	}
 
 	/* Find a destination connection. */
@@ -1278,9 +1208,7 @@
 	}
 
 	/* Dispatch RTP packet to destination RTP connection */
-	return mgcp_send_rtp(proto, addr, buf,
-			     buf_size, &conn->u.rtp, &conn_dst->u.rtp);
-
+	return mgcp_send_rtp(&conn_dst->u.rtp, msg);
 }
 
 /*! cleanup an endpoint when a connection on an RTP bridge endpoint is removed.
@@ -1302,6 +1230,76 @@
 	}
 }
 
+static bool is_dummy_msg(enum rtp_proto proto, struct msgb *msg)
+{
+	return msg->len == 1 && msg->data[0] == MGCP_DUMMY_LOAD;
+}
+
+struct pdu_ctx {
+	struct sockaddr_in *from_addr;
+	struct mgcp_conn_rtp *conn_src;
+};
+
+/* IuUP CN node has stripped an IuUP header and forwards RTP data to distribute to the peers. */
+int iuup_rx_payload(struct msgb *msg, void *node_priv)
+{
+	struct mgcp_conn_rtp *conn_src = OSMO_RTP_MSG_CTX(msg)->conn_src;
+	LOG_CONN_RTP(conn_src, LOGL_DEBUG, "iuup_rx_payload(%u bytes)\n", msg->len);
+	return rx_rtp(msg);
+}
+
+/* IuUP CN node has composed a message that contains an IuUP header and asks us to send to the IuUP peer.
+ */
+int iuup_tx_msg(struct msgb *msg, void *node_priv)
+{
+	const struct in_addr zero_addr = {};
+	struct mgcp_conn_rtp *conn_src = OSMO_RTP_MSG_CTX(msg)->conn_src;
+	struct mgcp_conn_rtp *conn_dst = node_priv;
+	struct sockaddr_in *from_addr = OSMO_RTP_MSG_CTX(msg)->from_addr;
+	struct mgcp_rtp_end *rtp_end = &conn_dst->end;
+	struct in_addr to_addr = rtp_end->addr;
+	uint16_t to_port = rtp_end->rtp_port;
+
+	if (conn_src == conn_dst
+	    && !memcmp(&zero_addr, &to_addr, sizeof(zero_addr)) && !to_port) {
+		LOG_CONN_RTP(conn_dst, LOGL_DEBUG, "iuup_tx_msg(): direct IuUP reply\n");
+		/* IuUP wants to send a message back to the same peer that sent an RTP package, but there
+		 * is no address configured for that peer yet. It is probably an IuUP Initialization ACK
+		 * reply. Use the sender address to send the reply.
+		 *
+		 * During 3G RAB Assignment, a 3G cell might first probe the MGW and expect an IuUP
+		 * Initialization ACK before it replies to the MSC with a successful RAB Assignment; only
+		 * after that reply does MSC officially know which RTP address+port the 3G cell wants to
+		 * use and can tell this MGW about it, so this "loopback" is, for some 3G cells, the only
+		 * chance we have to get a successful RAB Assignment done (particularly the nano3G does
+		 * this). */
+		to_addr = from_addr->sin_addr;
+		to_port = from_addr->sin_port;
+	}
+
+	LOG_CONN_RTP(conn_dst, LOGL_DEBUG, "iuup_tx_msg(%u bytes) to %s:%u\n", msg->len,
+		     inet_ntoa(to_addr), ntohs(to_port));
+
+	return mgcp_udp_send(rtp_end->rtp.fd, &to_addr, to_port, (char*)msg->data, msg->len);
+}
+
+static void iuup_init(struct mgcp_conn_rtp *conn_src)
+{
+	struct osmo_iuup_cn_cfg cfg = {
+		.node_priv = conn_src,
+		.rx_payload = iuup_rx_payload,
+		.tx_msg = iuup_tx_msg,
+	};
+
+	if (conn_src->iuup) {
+		LOG_CONN_RTP(conn_src, LOGL_NOTICE, "Rx IuUP init, but already initialized. Ignoring.\n");
+		return;
+	}
+
+	conn_src->iuup = osmo_iuup_cn_init(conn_src->conn, &cfg, "endp_%d_conn_%s",
+					   ENDPOINT_NUMBER(conn_src->conn->endp), conn_src->conn->id);
+}
+
 /* Handle incoming RTP data from NET */
 static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
 {
@@ -1315,23 +1313,88 @@
 	struct mgcp_conn_rtp *conn_src;
 	struct mgcp_endpoint *endp;
 	struct sockaddr_in addr;
-
-	char buf[RTP_BUF_SIZE];
-	int proto;
-	int len;
+	socklen_t slen = sizeof(addr);
+	int ret;
+	enum rtp_proto proto;
+	struct osmo_rtp_msg_ctx mc;
+	struct msgb *msg = msgb_alloc_headroom(RTP_BUF_SIZE + OSMO_IUUP_HEADROOM,
+					       OSMO_IUUP_HEADROOM, "RTP-rx");
+	int rc;
 
 	conn_src = (struct mgcp_conn_rtp *)fd->data;
 	OSMO_ASSERT(conn_src);
 	endp = conn_src->conn->endp;
 	OSMO_ASSERT(endp);
 
-	LOGPENDP(endp, DRTP, LOGL_DEBUG, "source conn:%s\n",
-		 mgcp_conn_dump(conn_src->conn));
+	proto = (fd == &conn_src->end.rtp)? MGCP_PROTO_RTP : MGCP_PROTO_RTCP;
 
-	/* Receive packet */
-	len = mgcp_recv(&proto, &addr, buf, sizeof(buf), fd);
-	if (len < 0)
-		return -1;
+	ret = recvfrom(fd->fd, msg->data, msg->data_len, 0, (struct sockaddr *)&addr, &slen);
+
+	if (ret <= 0) {
+		LOG_CONN_RTP(conn_src, LOGL_ERROR, "recvfrom error: %s\n", strerror(errno));
+		rc = -1;
+		goto out;
+	}
+
+	msgb_put(msg, ret);
+
+	LOG_CONN_RTP(conn_src, LOGL_DEBUG, "%s: rx %u bytes from %s:%u\n",
+		     proto == MGCP_PROTO_RTP ? "RTP" : "RTPC",
+		     msg->len, inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
+
+	if ((proto == MGCP_PROTO_RTP && check_rtp(conn_src, msg))
+	    || (proto == MGCP_PROTO_RTCP && check_rtcp(conn_src, msg))) {
+		/* Logging happened in the two check_ functions */
+		rc = -1;
+		goto out;
+	}
+
+	if (is_dummy_msg(proto, msg)) {
+		LOG_CONN_RTP(conn_src, LOGL_DEBUG, "rx dummy packet (dropped)\n");
+		rc = 0;
+		goto out;
+	}
+
+	mc = (struct osmo_rtp_msg_ctx){
+		.proto = proto,
+		.conn_src = conn_src,
+		.from_addr = &addr,
+	};
+	OSMO_RTP_MSG_CTX(msg) = &mc;
+	LOG_CONN_RTP(conn_src, LOGL_DEBUG, "msg ctx: %d %p %s\n",
+		     OSMO_RTP_MSG_CTX(msg)->proto,
+		     OSMO_RTP_MSG_CTX(msg)->conn_src,
+		     osmo_hexdump((void*)OSMO_RTP_MSG_CTX(msg)->from_addr, sizeof(struct sockaddr_in)));
+
+	/* Increment RX statistics */
+	rate_ctr_inc(&conn_src->rate_ctr_group->ctr[RTP_PACKETS_RX_CTR]);
+	rate_ctr_add(&conn_src->rate_ctr_group->ctr[RTP_OCTETS_RX_CTR], msg->len);
+	/* FIXME: count RTP and RTCP separately, also count IuUP payload-less separately */
+
+	/* Forward a copy of the RTP data to a debug ip/port */
+	forward_data(fd->fd, &conn_src->tap_in, msg);
+
+	if (proto == MGCP_PROTO_RTP && osmo_iuup_is_init(msg))
+		iuup_init(conn_src);
+
+	if (conn_src->iuup && proto == MGCP_PROTO_RTP)
+		rc = osmo_iuup_cn_rx_pdu(conn_src->iuup, msg);
+	else
+		rc = rx_rtp(msg);
+
+out:
+	msgb_free(msg);
+	return rc;
+}
+
+static int rx_rtp(struct msgb *msg)
+{
+	struct mgcp_conn_rtp *conn_src = OSMO_RTP_MSG_CTX(msg)->conn_src;
+	struct sockaddr_in *from_addr = OSMO_RTP_MSG_CTX(msg)->from_addr;
+	struct mgcp_conn *conn = conn_src->conn;
+	struct mgcp_trunk_config *tcfg = conn->endp->tcfg;
+
+	LOG_CONN_RTP(conn_src, LOGL_DEBUG, "rx_rtp(%u bytes)\n", msg->len);
 
 	mgcp_conn_watchdog_kick(conn_src->conn);
 
@@ -1340,14 +1403,17 @@
 	 * define, then we check if the incoming payload matches that
 	 * expectation. */
 	if (amr_oa_bwe_convert_indicated(conn_src->end.codec)) {
-		if (amr_oa_check(buf, len) != conn_src->end.codec->param.amr_octet_aligned)
+		if (amr_oa_check((char*)msg->data, msg->len) != conn_src->end.codec->param.amr_octet_aligned)
 			return -1;
 	}
 
+	/* Check if the origin of the RTP packet seems plausible */
+	if (!tcfg->rtp_accept_all && check_rtp_origin(conn_src, from_addr))
+		return -1;
+
 	/* Execute endpoint specific implementation that handles the
 	 * dispatching of the RTP data */
-	return endp->type->dispatch_rtp_cb(proto, &addr, buf, len,
-					   conn_src->conn);
+	return conn->endp->type->dispatch_rtp_cb(msg);
 }
 
 /*! set IP Type of Service parameter.
diff --git a/src/libosmo-mgcp/mgcp_osmux.c b/src/libosmo-mgcp/mgcp_osmux.c
index 72d3b91..885e2ae 100644
--- a/src/libosmo-mgcp/mgcp_osmux.c
+++ b/src/libosmo-mgcp/mgcp_osmux.c
@@ -234,13 +234,8 @@
 {
 	struct mgcp_conn_rtp *conn = data;
 	struct mgcp_endpoint *endp = conn->conn->endp;
-	struct sockaddr_in addr = {
-		.sin_addr = conn->end.addr,
-		.sin_port = conn->end.rtp_port,
-	}; /* FIXME: not set/used in cb */
 
-
-	endp->type->dispatch_rtp_cb(MGCP_PROTO_RTP, &addr, (char *)msg->data, msg->len, conn->conn);
+	endp->type->dispatch_rtp_cb(msg);
 	msgb_free(msg);
 }
 
diff --git a/src/osmo-mgw/mgw_main.c b/src/osmo-mgw/mgw_main.c
index adc1751..0b2ecce 100644
--- a/src/osmo-mgw/mgw_main.c
+++ b/src/osmo-mgw/mgw_main.c
@@ -244,6 +244,12 @@
 		  .color = "\033[1;30m",
 		  .enabled = 1,.loglevel = LOGL_NOTICE,
 		  },
+	[DIUUP] = {
+		  .name = "DIUUP",
+		  .description = "IuUP within RTP stream handling",
+		  .color = "\033[1;31m",
+		  .enabled = 1,.loglevel = LOGL_NOTICE,
+		  },
 };
 
 const struct log_info log_info = {
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 49a659f..302fa52 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1,6 +1,7 @@
 SUBDIRS = \
 	mgcp_client \
 	mgcp \
+	iuup \
 	$(NULL)
 
 # The `:;' works around a Bash 3.2 bug when the output is not writeable.
diff --git a/tests/iuup/Makefile.am b/tests/iuup/Makefile.am
new file mode 100644
index 0000000..12806b1
--- /dev/null
+++ b/tests/iuup/Makefile.am
@@ -0,0 +1,45 @@
+AM_CPPFLAGS = \
+	$(all_includes) \
+	-I$(top_srcdir)/include \
+	-I$(top_srcdir) \
+	$(NULL)
+
+AM_CFLAGS = \
+	-Wall \
+	-ggdb3 \
+	$(LIBOSMOCORE_CFLAGS) \
+	$(LIBOSMOVTY_CFLAGS) \
+	$(LIBOSMOGSM_CFLAGS) \
+	$(LIBOSMONETIF_CFLAGS) \
+	$(COVERAGE_CFLAGS) \
+	$(NULL)
+
+AM_LDFLAGS = \
+	$(COVERAGE_LDFLAGS) \
+	$(NULL)
+
+EXTRA_DIST = \
+	iuup_test.ok \
+	iuup_test.err \
+	$(NULL)
+
+noinst_PROGRAMS = \
+	iuup_test \
+	$(NULL)
+
+iuup_test_SOURCES = \
+	iuup_test.c \
+	$(NULL)
+
+iuup_test_LDADD = \
+	$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.a \
+	$(LIBOSMOCORE_LIBS) \
+	$(LIBOSMOVTY_LIBS) \
+	$(LIBOSMOGSM_LIBS) \
+	$(LIBRARY_DL) \
+	$(LIBOSMONETIF_LIBS) \
+	-lm  \
+	$(NULL)
+
+update_exp:
+	$(builddir)/iuup_test >$(srcdir)/iuup_test.ok 2>$(srcdir)/iuup_test.err
diff --git a/tests/iuup/iuup_test.c b/tests/iuup/iuup_test.c
new file mode 100644
index 0000000..e6f2ca5
--- /dev/null
+++ b/tests/iuup/iuup_test.c
@@ -0,0 +1,156 @@
+#include <stdint.h>
+#include <string.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/application.h>
+#include <osmocom/core/logging.h>
+
+#include <osmocom/mgcp/iuup_cn_node.h>
+#include <osmocom/mgcp/iuup_protocol.h>
+
+void *ctx = NULL;
+
+static const char *dump(struct msgb *msg)
+{
+	return osmo_hexdump_nospc(msg->data, msg->len);
+}
+
+struct msgb *msgb_from_hex(const char *label, const char *hex)
+{
+	struct msgb *msg = msgb_alloc_headroom(4096 + OSMO_IUUP_HEADROOM,
+					       OSMO_IUUP_HEADROOM, label);
+	unsigned char *rc;
+	msg->l2h = msg->data;
+	rc = msgb_put(msg, osmo_hexparse(hex, msg->data, msgb_tailroom(msg)));
+	OSMO_ASSERT(rc == msg->l2h);
+	return msg;
+}
+
+const char *expect_rx_payload = NULL;
+int rx_payload(struct msgb *msg, void *node_priv)
+{
+	printf("rx_payload() invoked by iuup_cn!\n");
+	printf("        [IuUP] -RTP->\n");
+	printf("%s\n", dump(msg));
+	printf("node_priv=%p\n", node_priv);
+	if (!expect_rx_payload) {
+		printf("ERROR: did not expect rx_payload()\n");
+		exit(-1);
+	} else if (strcmp(expect_rx_payload, dump(msg))) {
+		printf("ERROR: mismatches expected msg %s\n", expect_rx_payload);
+		exit(-1);
+	} else
+		printf("ok: matches expected msg\n");
+	expect_rx_payload = NULL;
+	return 0;
+}
+
+const char *expect_tx_msg = NULL;
+int tx_msg(struct msgb *msg, void *node_priv)
+{
+	printf("tx_msg() invoked by iuup_cn!\n");
+	printf(" <-PDU- [IuUP]\n");
+	printf("%s\n", dump(msg));
+	printf("node_priv=%p\n", node_priv);
+	if (!expect_tx_msg) {
+		printf("ERROR: did not expect tx_msg()\n");
+		exit(-1);
+	} else if (strcmp(expect_tx_msg, dump(msg))) {
+		printf("ERROR: mismatches expected msg %s\n", expect_tx_msg);
+		exit(-1);
+	} else
+		printf("ok: matches expected msg\n");
+	expect_tx_msg = NULL;
+	return 0;
+}
+
+static int rx_pdu(struct osmo_iuup_cn *cn, struct msgb *msg)
+{
+	int rc;
+	printf(" -PDU-> [IuUP]\n");
+	printf("%s\n", dump(msg));
+	rc = osmo_iuup_cn_rx_pdu(cn, msg);
+	printf("rc=%d\n", rc);
+	return rc;
+}
+
+static int tx_payload(struct osmo_iuup_cn *cn, struct msgb *msg)
+{
+	int rc;
+	printf("        [IuUP] <-RTP-\n");
+	printf("%s\n", dump(msg));
+	rc = osmo_iuup_cn_tx_payload(cn, msg);
+	printf("rc=%d\n", rc);
+	return rc;
+}
+
+void test_cn_session()
+{
+	void *node_priv = (void*)0x2342;
+
+	struct osmo_iuup_cn_cfg cfg = {
+		.node_priv = node_priv,
+		.rx_payload = rx_payload,
+		.tx_msg = tx_msg,
+	};
+
+	struct osmo_iuup_cn *cn = osmo_iuup_cn_init(ctx, &cfg, __func__);
+	OSMO_ASSERT(cn);
+
+	printf("\nSend IuUP Initialization. Expecting direct tx_msg() of the Initialization Ack\n");
+	expect_tx_msg = "8060dc5219495e3f00010111" /* RTP header */
+			"e4002400"; /* IuUP Init Ack */
+	rx_pdu(cn,
+	       msgb_from_hex("IuUP-Init",
+			     "8060dc5219495e3f00010111" /* <- RTP header */
+			     "e000df99" /* <- IuUP header */
+			     "160051673c01270000820000001710000100" /* IuUP params */));
+
+#define RTP_HEADER "8060944c6256042c00010102"
+#define IUUP_HEADER "0100e2b3"
+#define RTP_PAYLOAD "6cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0"
+	printf("\nReceive payload encapsulated in IuUP. Expecting rx_payload() of just RTP packet\n");
+	printf("i.e. should strip away " IUUP_HEADER "\n");
+	expect_rx_payload = RTP_HEADER "f03c" RTP_PAYLOAD;
+	rx_pdu(cn,
+	       msgb_from_hex("IuUP-Data",
+			     RTP_HEADER IUUP_HEADER RTP_PAYLOAD));
+
+	printf("\nTransmit RTP. Expecting tx_msg() with inserted IuUP header\n");
+	expect_tx_msg = RTP_HEADER "000002b3" RTP_PAYLOAD;
+	tx_payload(cn,
+		   msgb_from_hex("RTP data", RTP_HEADER "f03c" RTP_PAYLOAD));
+
+	printf("\nMore RTP, each time the Frame Nr advances, causing a new header CRC.\n");
+	expect_tx_msg = RTP_HEADER "0100e2b3" RTP_PAYLOAD;
+	tx_payload(cn,
+		   msgb_from_hex("RTP data", RTP_HEADER "f03c" RTP_PAYLOAD));
+	expect_tx_msg = RTP_HEADER "02007eb3" RTP_PAYLOAD;
+	tx_payload(cn,
+		   msgb_from_hex("RTP data", RTP_HEADER "f03c" RTP_PAYLOAD));
+	expect_tx_msg = RTP_HEADER "03009eb3" RTP_PAYLOAD;
+	tx_payload(cn,
+		   msgb_from_hex("RTP data", RTP_HEADER "f03c" RTP_PAYLOAD));
+
+	printf("All done.\n");
+}
+
+static const struct log_info_cat log_categories[] = {
+};
+
+const struct log_info log_info = {
+	.cat = log_categories,
+	.num_cat = ARRAY_SIZE(log_categories),
+};
+
+int main(void)
+{
+	ctx = talloc_named_const(NULL, 0, __FILE__);
+	void *msgb_ctx = msgb_talloc_ctx_init(ctx, 0);
+	osmo_init_logging2(ctx, &log_info);
+
+	test_cn_session();
+
+	talloc_free(msgb_ctx);
+	return 0;
+}
diff --git a/tests/iuup/iuup_test.err b/tests/iuup/iuup_test.err
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/iuup/iuup_test.err
diff --git a/tests/iuup/iuup_test.ok b/tests/iuup/iuup_test.ok
new file mode 100644
index 0000000..8c473d6
--- /dev/null
+++ b/tests/iuup/iuup_test.ok
@@ -0,0 +1,58 @@
+
+Send IuUP Initialization. Expecting direct tx_msg() of the Initialization Ack
+ -PDU-> [IuUP]
+8060dc5219495e3f00010111e000df99160051673c01270000820000001710000100
+tx_msg() invoked by iuup_cn!
+ <-PDU- [IuUP]
+8060dc5219495e3f00010111e4002400
+node_priv=0x2342
+ok: matches expected msg
+rc=0
+
+Receive payload encapsulated in IuUP. Expecting rx_payload() of just RTP packet
+i.e. should strip away 0100e2b3
+ -PDU-> [IuUP]
+8060944c6256042c000101020100e2b36cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
+rx_payload() invoked by iuup_cn!
+        [IuUP] -RTP->
+8060944c6256042c00010102f03c6cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
+node_priv=0x2342
+ok: matches expected msg
+rc=0
+
+Transmit RTP. Expecting tx_msg() with inserted IuUP header
+        [IuUP] <-RTP-
+8060944c6256042c00010102f03c6cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
+tx_msg() invoked by iuup_cn!
+ <-PDU- [IuUP]
+8060944c6256042c00010102000002b36cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
+node_priv=0x2342
+ok: matches expected msg
+rc=0
+
+More RTP, each time the Frame Nr advances, causing a new header CRC.
+        [IuUP] <-RTP-
+8060944c6256042c00010102f03c6cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
+tx_msg() invoked by iuup_cn!
+ <-PDU- [IuUP]
+8060944c6256042c000101020100e2b36cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
+node_priv=0x2342
+ok: matches expected msg
+rc=0
+        [IuUP] <-RTP-
+8060944c6256042c00010102f03c6cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
+tx_msg() invoked by iuup_cn!
+ <-PDU- [IuUP]
+8060944c6256042c0001010202007eb36cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
+node_priv=0x2342
+ok: matches expected msg
+rc=0
+        [IuUP] <-RTP-
+8060944c6256042c00010102f03c6cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
+tx_msg() invoked by iuup_cn!
+ <-PDU- [IuUP]
+8060944c6256042c0001010203009eb36cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
+node_priv=0x2342
+ok: matches expected msg
+rc=0
+All done.
diff --git a/tests/mgcp/mgcp_test.c b/tests/mgcp/mgcp_test.c
index 8a37ef0..5074de5 100644
--- a/tests/mgcp/mgcp_test.c
+++ b/tests/mgcp/mgcp_test.c
@@ -28,6 +28,7 @@
 #include <osmocom/mgcp/mgcp_endp.h>
 #include <osmocom/mgcp/mgcp_sdp.h>
 #include <osmocom/mgcp/mgcp_codec.h>
+#include <osmocom/mgcp/mgcp_internal.h>
 
 #include <osmocom/core/application.h>
 #include <osmocom/core/talloc.h>
@@ -1237,7 +1238,7 @@
 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);
+			  struct sockaddr_in *addr, struct msgb *msg);
 
 static void test_packet_error_detection(int patch_ssrc, int patch_ts)
 {
@@ -1249,7 +1250,6 @@
 	struct mgcp_rtp_state state;
 	struct mgcp_rtp_end *rtp;
 	struct sockaddr_in addr = { 0 };
-	char buffer[4096];
 	uint32_t last_ssrc = 0;
 	uint32_t last_timestamp = 0;
 	uint32_t last_seqno = 0;
@@ -1297,16 +1297,17 @@
 
 	for (i = 0; i < ARRAY_SIZE(test_rtp_packets1); ++i) {
 		struct rtp_packet_info *info = test_rtp_packets1 + i;
+		struct msgb *msg = msgb_alloc(4096, __func__);
 
 		force_monotonic_time_us = round(1000000.0 * info->txtime);
 
-		OSMO_ASSERT(info->len <= sizeof(buffer));
+		OSMO_ASSERT(info->len <= msgb_tailroom(msg));
 		OSMO_ASSERT(info->len >= 0);
-		memmove(buffer, info->data, info->len);
+		msg->l3h = msgb_put(msg, info->len);
+		memcpy((char*)msgb_l3(msg), info->data, info->len);
 		mgcp_rtp_end_config(&endp, 1, rtp);
 
-		mgcp_patch_and_count(&endp, &state, rtp, &addr,
-				     buffer, info->len);
+		mgcp_patch_and_count(&endp, &state, rtp, &addr, msg);
 
 		if (state.out_stream.ssrc != last_ssrc) {
 			printf("Output SSRC changed to %08x\n",
@@ -1333,6 +1334,8 @@
 		last_out_ts_err_cnt = state.out_stream.err_ts_ctr->current;
 		last_timestamp = state.out_stream.last_timestamp;
 		last_seqno = state.out_stream.last_seq;
+
+		msgb_free(msg);
 	}
 
 	force_monotonic_time_us = -1;
diff --git a/tests/testsuite.at b/tests/testsuite.at
index 3585bf0..0c3f802 100644
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -13,3 +13,10 @@
 cat $abs_srcdir/mgcp/mgcp_test.ok > expout
 AT_CHECK([$abs_top_builddir/tests/mgcp/mgcp_test], [], [expout], [ignore])
 AT_CLEANUP
+
+AT_SETUP([iuup])
+AT_KEYWORDS([iuup])
+cat $abs_srcdir/iuup/iuup_test.ok > expout
+cat $abs_srcdir/iuup/iuup_test.err > experr
+AT_CHECK([$abs_top_builddir/tests/iuup/iuup_test], [], [expout], [experr])
+AT_CLEANUP

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

Gerrit-Project: osmo-mgw
Gerrit-Branch: master
Gerrit-Change-Id: Ibc70e0aa00476926dd1f4ea8139c34f31f9cdfa3
Gerrit-Change-Number: 15169
Gerrit-PatchSet: 1
Gerrit-Owner: neels <nhofmeyr at sysmocom.de>
Gerrit-MessageType: newchange
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20190812/f1a7fd17/attachment-0001.html>


More information about the gerrit-log mailing list