wip

Change-Id: I4fdcebf3b006a72c5318a9105a2be2cc7057dca1
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
index 158b6a1..f792469 100644
--- a/include/osmocom/mgcp/iuup_cn_node.h
+++ b/include/osmocom/mgcp/iuup_cn_node.h
@@ -10,22 +10,23 @@
  *                                            -----------------
  */
 
-
 #pragma once
 
 struct osmo_iuup_cn;
 struct msgb;
 
-typedef int (*osmo_iuup_data_cb_t)(struct msgb *msg, void *node_priv, void *pdu_priv);
+typedef int (*osmo_iuup_data_cb_t)(struct msgb *msg, void *node_priv);
 
 struct osmo_iuup_cn_cfg {
 	void *node_priv;
 
-	/* When an IuUP PDU containing voice payload has been received, this callback is invoked to pass
-	 * the voice payload towards the Core Network, msgb_l3() pointing at the payload. */
+	/* 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 sends a PDU to the IuUP peer (e.g. the RNC) */
+	/* 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;
 };
 
@@ -35,12 +36,6 @@
 				       const char *name_fmt, ...);
 void osmo_iuup_cn_free(struct osmo_iuup_cn *cn);
 
-/* Encapsulate voice stream payload in IuUP and, if appropriate, call the tx_msg() to transmit the
- * resulting message to the IuUP peer. msgb_l3() should point at the payload data.
- * pdu_priv is transparently passed on to tx_msg().
- * Returns 0 on success, negative on error. */
-int osmo_iuup_cn_tx_payload(struct osmo_iuup_cn *cn, struct msgb *payload, void *pdu_priv);
+int osmo_iuup_cn_tx_payload(struct osmo_iuup_cn *cn, struct msgb *payload);
 
-/* 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, void *pdu_priv);
+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
index 108bcaa..f4aec1f 100644
--- a/include/osmocom/mgcp/iuup_protocol.h
+++ b/include/osmocom/mgcp/iuup_protocol.h
@@ -21,6 +21,9 @@
 
 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 {
@@ -40,10 +43,6 @@
 		payload_crc_hi:2;
 	uint8_t payload_crc_lo;
 	uint8_t payload[0];
-	uint8_t spare:3,
-		iptis_present:1,
-		subflows:3,
-		chain:1;
 #elif OSMO_IS_LITTLE_ENDIAN
 	uint8_t frame_nr:2,
 		ack_nack:2,
@@ -54,12 +53,38 @@
 		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
-} __attribute__((packed));
+	} 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
diff --git a/include/osmocom/mgcp/mgcp_endp.h b/include/osmocom/mgcp/mgcp_endp.h
index 469d431..04c3d18 100644
--- a/include/osmocom/mgcp/mgcp_endp.h
+++ b/include/osmocom/mgcp/mgcp_endp.h
@@ -23,18 +23,28 @@
 
 #pragma once
 
+#include <osmocom/core/msgb.h>
+
 struct sockaddr_in;
 struct mgcp_conn;
+struct mgcp_conn_rtp;
 struct mgcp_endpoint;
 
+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) */
-typedef int (*mgcp_dispatch_rtp_cb) (int proto, struct sockaddr_in *addr,
-				     struct msgb *payload,
-				     struct mgcp_conn *conn);
+/* Callback type for RTP dispatcher functions (e.g mgcp_dispatch_rtp_bridge_cb, see below).
+ * The fields OSMO_RTP_MSG_PROTO, OSMO_RTP_MSG_CONN_SRC, OSMO_RTP_MSG_FROM_ADDR 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 69ba4d4..51d3b9d 100644
--- a/include/osmocom/mgcp/mgcp_internal.h
+++ b/include/osmocom/mgcp/mgcp_internal.h
@@ -271,8 +271,7 @@
 	      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,
-				struct msgb *payload, 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);
diff --git a/src/libosmo-mgcp/iuup_cn_node.c b/src/libosmo-mgcp/iuup_cn_node.c
index 390dc1c..f555e36 100644
--- a/src/libosmo-mgcp/iuup_cn_node.c
+++ b/src/libosmo-mgcp/iuup_cn_node.c
@@ -39,12 +39,13 @@
 #include <osmocom/mgcp/debug.h>
 
 #define LOG_IUUP_CN(cn, level, fmt, args...) \
-		LOGP(DRTP, level, "(%s) " fmt, (cn)->name, ## args)
+		LOGP(DIUUP, level, "(%s) " fmt, (cn)->name, ## args)
 
 struct osmo_iuup_cn {
 	struct osmo_iuup_cn_cfg cfg;
 	char *name;
 	uint8_t next_frame_nr;
+	int rtp_payload_type;
 };
 
 struct osmo_iuup_cn *osmo_iuup_cn_init(void *ctx, struct osmo_iuup_cn_cfg *cfg,
@@ -63,8 +64,10 @@
 	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(DLGLOBAL, LOGL_ERROR, "Attempting to set illegal id for IuUP CN instance: %s\n",
+		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;
@@ -79,7 +82,7 @@
 }
 
 static int rx_data(struct osmo_iuup_cn *cn, struct msgb *pdu,
-		   struct osmo_iuup_hdr_data *hdr, void *pdu_priv)
+		   struct osmo_iuup_hdr_data *hdr)
 {
 	/* Remove the IuUP bit from the middle of the buffer by writing the RTP header forward. */
 	unsigned int pre_hdr_len = ((uint8_t*)hdr) - pdu->data;
@@ -87,42 +90,72 @@
 
 	msgb_pull(pdu, sizeof(*hdr));
 
-	cn->cfg.rx_payload(pdu, cn->cfg.node_priv, pdu_priv);
+	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, void *pdu_priv)
+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);
-	return cn->cfg.tx_msg(ack, cn->cfg.node_priv, pdu_priv);
+
+	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, void *pdu_priv)
+		      struct osmo_iuup_hdr_ctrl *hdr)
 {
 	switch (hdr->procedure) {
 	case OSMO_IUUP_PROC_INITIALIZATION:
 		switch (hdr->ack_nack) {
 		case OSMO_IUUP_ACKNACK_PROCEDURE:
-			return tx_init_ack(cn, pdu_priv);
+			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;
 		}
-		/* fall thru */
+		/* 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:
-		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;
+		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;
 }
 
-int osmo_iuup_cn_rx_pdu(struct osmo_iuup_cn *cn, struct msgb *pdu, void *pdu_priv)
+/* 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;
@@ -133,20 +166,23 @@
 		return rc;
 
 	if (is_ctrl)
-		return rx_control(cn, pdu, is_ctrl, pdu_priv);
+		return rx_control(cn, pdu, is_ctrl);
 	if (is_data)
-		return rx_data(cn, pdu, is_data, pdu_priv);
+		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 = (cn->next_frame_nr + 1) % 0x0f;
+	cn->next_frame_nr = (frame_nr + 1) & 0x0f;
 	return frame_nr;
 }
 
-int osmo_iuup_cn_tx_payload(struct osmo_iuup_cn *cn, struct msgb *pdu, void *pdu_priv)
+/* 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;
@@ -157,6 +193,10 @@
 	/* copy the RTP header part backwards by the size needed for the IuUP header */
 	rtp = (void*)msgb_push(pdu, sizeof(*iuup_hdr));
 	memmove(rtp, rtp_was, sizeof(*rtp));
+
+	/* Send the same payload type to the peer (erm...) */
+	rtp->payload_type = cn->rtp_payload_type;
+
 	iuup_hdr = (void*)rtp->data;
 
 	*iuup_hdr = (struct osmo_iuup_hdr_data){
@@ -166,6 +206,8 @@
 	};
 
 	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, pdu_priv);
+	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
index 6277abd..bfb009d 100644
--- a/src/libosmo-mgcp/iuup_protocol.c
+++ b/src/libosmo-mgcp/iuup_protocol.c
@@ -152,7 +152,7 @@
 
 /* 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 DRTP with the given log label for context. Return NULL in
+ * 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,
@@ -169,7 +169,7 @@
 
 #define ERR(fmt, args...) do { \
 			if (log_errors) \
-				LOGP(DRTP, LOGL_ERROR, "(%s) " fmt, log_label? : "-", ## args); \
+				LOGP(DIUUP, LOGL_ERROR, "(%s) " fmt, log_label? : "-", ## args); \
 			return -EINVAL; \
 		} while (0)
 
@@ -240,6 +240,7 @@
 		&& 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 */
@@ -256,3 +257,30 @@
 
 	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_network.c b/src/libosmo-mgcp/mgcp_network.c
index e4ee8ad..83c0570 100644
--- a/src/libosmo-mgcp/mgcp_network.c
+++ b/src/libosmo-mgcp/mgcp_network.c
@@ -57,10 +57,7 @@
 	MGCP_PROTO_RTCP,
 };
 
-static int rx_rtp(struct mgcp_conn_rtp *conn_src, struct msgb *payload,
-		  enum rtp_proto proto, struct sockaddr_in *from_addr);
-static int tx_rtp(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst,
-		  enum rtp_proto proto, struct sockaddr_in *from_addr, struct msgb *payload);
+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
@@ -494,8 +491,8 @@
 	uint8_t pt_in;
 	int pt_out;
 
-	OSMO_ASSERT(msgb_l3len(msg) >= sizeof(struct rtp_hdr));
-	rtp_hdr = (struct rtp_hdr *)msgb_l3(msg);
+	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);
@@ -523,12 +520,12 @@
 	uint32_t timestamp, ssrc;
 	struct rtp_hdr *rtp_hdr;
 	int payload = rtp_end->codec->payload_type;
-	unsigned int len = msgb_l3len(msg);
+	unsigned int len = msg->len;
 
 	if (len < sizeof(*rtp_hdr))
 		return;
 
-	rtp_hdr = (struct rtp_hdr *)msgb_l3(msg);
+	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);
@@ -668,7 +665,7 @@
 	if (!tap->enabled)
 		return;
 
-	rc = sendto(fd, msgb_l3(msg), msgb_l3len(msg), 0, (struct sockaddr *)&tap->forward,
+	rc = sendto(fd, msg->data, msg->len, 0, (struct sockaddr *)&tap->forward,
 		    sizeof(tap->forward));
 
 	if (rc < 0)
@@ -754,11 +751,11 @@
 	} else if (is_rtp) {
 		int cont;
 		int nbytes = 0;
-		int buflen = msgb_l3len(msg);
+		int buflen = msg->len;
 		do {
 			/* Run transcoder */
 			cont = endp->cfg->rtp_processing_cb(endp, rtp_end,
-							    msgb_l3(msg), &buflen,
+							    (char*)msg->data, &buflen,
 							    RTP_BUF_SIZE);
 			if (cont < 0)
 				break;
@@ -778,6 +775,7 @@
 			forward_data(rtp_end->rtp.fd, &conn_src->tap_out,
 				     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
@@ -785,7 +783,7 @@
 			 */
 			if (!rtp_state->patched_first_rtp_payload
 			    && conn_src->conn->mode == MGCP_CONN_LOOPBACK) {
-				uint8_t *data = ((uint8_t *)msgb_l3(msg)) + 12;
+				uint8_t *data = msg->data + 12;
 				if (data[0] == 0xe0) {
 					data[0] = 0xe4;
 					data[1] = 0x00;
@@ -796,11 +794,13 @@
 					     ENDPOINT_NUMBER(endp));
 				}
 			}
+#endif
 
-			len = mgcp_udp_send(rtp_end->rtp.fd,
-					    &rtp_end->addr,
-					    rtp_end->rtp_port,
-					    msgb_l3(msg), msgb_l3len(msg));
+			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;
@@ -823,7 +823,7 @@
 
 		len = mgcp_udp_send(rtp_end->rtcp.fd,
 				    &rtp_end->addr,
-				    rtp_end->rtcp_port, msgb_l3(msg), msgb_l3len(msg));
+				    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);
@@ -914,7 +914,7 @@
 	 * and IP-address for outgoing data. */
 	if (strcmp(inet_ntoa(conn->end.addr), "0.0.0.0") == 0 && conn->end.rtp_port == 0) {
 		LOGP(DRTP, LOGL_DEBUG,
-		     "endpoint:0x%x destination IP-address and rtp port is (not yet) known\n",
+		     "endpoint:0x%x destination IP-address and rtp port is not known (yet)\n",
 		     ENDPOINT_NUMBER(endp));
 		return -1;
 	}
@@ -943,23 +943,22 @@
 	struct rtcp_hdr *hdr;
 	unsigned int len;
 	uint8_t type;
-	unsigned int buf_size = msgb_l3len(msg);
 
 	/* 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",
-			     buf_size, sizeof(struct rtcp_hdr));
+			     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 *)msgb_l3(msg);
+	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, buf_size);
+			     len, msg->len);
 		return -EINVAL;
 	}
 
@@ -981,9 +980,9 @@
 	size_t min_size = sizeof(struct rtp_hdr);
 	if (conn_src->iuup)
 		min_size += sizeof(struct osmo_iuup_hdr_data);
-	if (msgb_l3len(msg) < min_size) {
+	if (msg->len < min_size) {
 		LOG_CONN_RTP(conn_src, LOGL_ERROR, "RTP packet too short (%u < %zu)\n",
-			     msgb_l3len(msg), min_size);
+			     msg->len, min_size);
 		return -1;
 	}
 
@@ -998,11 +997,11 @@
 
 /* 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,
-			 struct msgb *payload,
-			 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)
 {
+	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;
 
 	LOGP(DRTP, LOGL_DEBUG, "endpoint:0x%x destin conn:%s\n",
@@ -1023,14 +1022,14 @@
 		     "using mgcp_send() to forward data directly\n",
 		     ENDPOINT_NUMBER(endp));
 		return mgcp_send(endp, proto == MGCP_PROTO_RTP,
-				 addr, payload, conn_src, conn_dst);
+				 from_addr, msg, conn_src, conn_dst);
 	case MGCP_OSMUX_BSC_NAT:
 	case MGCP_OSMUX_BSC:
 		LOGP(DRTP, LOGL_DEBUG,
 		     "endpoint:0x%x endpoint type is MGCP_OSMUX_BSC_NAT, "
 		     "using osmux_xfrm_to_osmux() to forward data through OSMUX\n",
 		     ENDPOINT_NUMBER(endp));
-		return osmux_xfrm_to_osmux(msgb_l3(payload), msgb_l3len(payload), 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
@@ -1050,9 +1049,10 @@
  *  \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,
-				struct msgb *payload, 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 mgcp_endpoint *endp;
 	endp = conn->endp;
@@ -1096,7 +1096,7 @@
 	}
 
 	/* Dispatch RTP packet to destination RTP connection */
-	return tx_rtp(&conn->u.rtp, &conn_dst->u.rtp, proto, addr, payload);
+	return mgcp_send_rtp(&conn_dst->u.rtp, msg);
 }
 
 /*! cleanup an endpoint when a connection on an RTP bridge endpoint is removed.
@@ -1120,14 +1120,7 @@
 
 static bool is_dummy_msg(enum rtp_proto proto, struct msgb *msg)
 {
-	return msgb_l3len(msg) == 1 && ((char*)msgb_l3(msg))[0] == MGCP_DUMMY_LOAD;
-}
-
-int rx_rtp_from_iuup(struct msgb *msg, void *node_priv, void *pdu_priv)
-{
-	struct mgcp_conn_rtp *conn_src = node_priv;
-	struct sockaddr_in *from_addr = pdu_priv;
-	return rx_rtp(conn_src, msg, MGCP_PROTO_RTP, from_addr);
+	return msg->len == 1 && msg->data[0] == MGCP_DUMMY_LOAD;
 }
 
 struct pdu_ctx {
@@ -1135,23 +1128,64 @@
 	struct mgcp_conn_rtp *conn_src;
 };
 
-int tx_pdu_from_iuup(struct msgb *msg, void *node_priv, void *pdu_priv)
+/* 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_dst = node_priv;
-	struct pdu_ctx *p = pdu_priv;
-	return mgcp_send_rtp(MGCP_PROTO_RTP, p->from_addr, msg, p->conn_src, conn_dst);
+	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);
 }
 
-static void init_iuup(struct mgcp_conn_rtp *conn_src)
+/* 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 = rx_rtp_from_iuup,
-		.tx_msg = tx_pdu_from_iuup,
+		.rx_payload = iuup_rx_payload,
+		.tx_msg = iuup_tx_msg,
 	};
 
-	osmo_iuup_cn_init(conn_src, &cfg, "%d@ I:%s",
-			  ENDPOINT_NUMBER(conn_src->conn->endp), conn_src->conn->id);
+	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 */
@@ -1170,6 +1204,7 @@
 	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");
 
@@ -1188,12 +1223,11 @@
 		return -1;
 	}
 
-	/* By default, indicate that RTP payload starts right from the buffer's beginning. */
-	msg->l3h = msgb_put(msg, ret);
+	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",
-		     msgb_l3len(msg), inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
+		     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))) {
@@ -1206,32 +1240,47 @@
 		return 0;
 	}
 
+	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], msgb_l3len(msg));
+	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))
-		init_iuup(conn_src);
+		iuup_init(conn_src);
 
 	if (conn_src->iuup && proto == MGCP_PROTO_RTP)
-		return osmo_iuup_cn_rx_pdu(conn_src->iuup, msg, &addr);
+		return osmo_iuup_cn_rx_pdu(conn_src->iuup, msg);
 	else
-		return rx_rtp(conn_src, msg, proto, &addr);
+		return rx_rtp(msg);
+	msgb_free(msg);
 }
 
-static int rx_rtp(struct mgcp_conn_rtp *conn_src, struct msgb *payload,
-		  enum rtp_proto proto, struct sockaddr_in *from_addr)
+static int rx_rtp(struct msgb *msg)
 {
-	struct mgcp_endpoint *endp = conn_src->conn->endp;
-	struct mgcp_trunk_config *tcfg = endp->tcfg;
+	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);
 
 	/* Check if the connection is in loopback mode, if yes, just send the
 	 * incoming data back to the origin */
-	if (conn_src->conn->mode == MGCP_CONN_LOOPBACK) {
+	if (conn->mode == MGCP_CONN_LOOPBACK) {
 		/* When we are in loopback mode, we loop back all incoming
 		 * packets back to their origin. We will use the originating
 		 * address data from the UDP packet header to patch the
@@ -1240,7 +1289,7 @@
 			conn_src->end.addr = from_addr->sin_addr;
 			conn_src->end.rtp_port = from_addr->sin_port;
 		}
-		return tx_rtp(conn_src, conn_src, proto, from_addr, payload);
+		return mgcp_send_rtp(conn_src, msg);
 	}
 
 	/* Check if the origin of the RTP packet seems plausible */
@@ -1249,16 +1298,7 @@
 
 	/* Execute endpoint specific implementation that handles the
 	 * dispatching of the RTP data */
-	return conn_src->conn->endp->type->dispatch_rtp_cb(proto, from_addr, payload, conn_src->conn);
-}
-
-static int tx_rtp(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst,
-		  enum rtp_proto proto, struct sockaddr_in *from_addr, struct msgb *payload)
-{
-	if (conn_dst->iuup && proto == MGCP_PROTO_RTP)
-		return osmo_iuup_cn_tx_payload(conn_dst->iuup, payload, from_addr);
-	else
-		return mgcp_send_rtp(proto, from_addr, payload, conn_src, conn_dst);
+	return conn->endp->type->dispatch_rtp_cb(msg);
 }
 
 /*! set IP Type of Service parameter.
diff --git a/src/osmo-mgw/mgw_main.c b/src/osmo-mgw/mgw_main.c
index de094c1..4abee75 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/iuup/iuup_test.c b/tests/iuup/iuup_test.c
index 8f32a95..8d96f06 100644
--- a/tests/iuup/iuup_test.c
+++ b/tests/iuup/iuup_test.c
@@ -27,12 +27,12 @@
 }
 
 const char *expect_rx_payload = NULL;
-int rx_payload(struct msgb *msg, void *node_priv, void *pdu_priv)
+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 pdu_priv=%p\n", node_priv, pdu_priv);
+	printf("node_priv=%p\n", node_priv);
 	if (!expect_rx_payload) {
 		printf("ERROR: did not expect rx_payload()\n");
 		exit(-1);
@@ -41,18 +41,17 @@
 		exit(-1);
 	} else
 		printf("ok: matches expected msg\n");
-	msgb_free(msg);
 	expect_rx_payload = NULL;
 	return 0;
 }
 
 const char *expect_tx_msg = NULL;
-int tx_msg(struct msgb *msg, void *node_priv, void *pdu_priv)
+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 pdu_priv=%p\n", node_priv, pdu_priv);
+	printf("node_priv=%p\n", node_priv);
 	if (!expect_tx_msg) {
 		printf("ERROR: did not expect tx_msg()\n");
 		exit(-1);
@@ -61,29 +60,26 @@
 		exit(-1);
 	} else
 		printf("ok: matches expected msg\n");
-	msgb_free(msg);
 	expect_tx_msg = NULL;
 	return 0;
 }
 
-static int rx_pdu(struct osmo_iuup_cn *cn, struct msgb *msg, void *pdu_priv)
+static int rx_pdu(struct osmo_iuup_cn *cn, struct msgb *msg)
 {
 	int rc;
 	printf(" -PDU-> [IuUP]\n");
 	printf("%s\n", dump(msg));
-	printf("pdu_priv=%p\n", pdu_priv);
-	rc = osmo_iuup_cn_rx_pdu(cn, msg, pdu_priv);
+	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, void *pdu_priv)
+static int tx_payload(struct osmo_iuup_cn *cn, struct msgb *msg)
 {
 	int rc;
 	printf("        [IuUP] <-RTP-\n");
 	printf("%s\n", dump(msg));
-	printf("pdu_priv=%p\n", pdu_priv);
-	rc = osmo_iuup_cn_tx_payload(cn, msg, pdu_priv);
+	rc = osmo_iuup_cn_tx_payload(cn, msg);
 	printf("rc=%d\n", rc);
 	return rc;
 }
@@ -91,7 +87,6 @@
 void test_cn_session()
 {
 	void *node_priv = (void*)0x2342;
-	void *pdu_priv = (void*)0xfeed;
 
 	struct osmo_iuup_cn_cfg cfg = {
 		.node_priv = node_priv,
@@ -103,13 +98,13 @@
 	OSMO_ASSERT(cn);
 
 	printf("\nSend IuUP Initialization. Expecting direct tx_msg() of the Initialization Ack\n");
-	expect_tx_msg = "e400240000";
+	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 */),
-	       pdu_priv);
+			     "160051673c01270000820000001710000100" /* IuUP params */));
 
 #define RTP_HEADER "8060944c6256042c00010102"
 #define IUUP_HEADER "0100e2b3"
@@ -119,28 +114,23 @@
 	expect_rx_payload = RTP_HEADER RTP_PAYLOAD;
 	rx_pdu(cn,
 	       msgb_from_hex("IuUP-Data",
-			     RTP_HEADER IUUP_HEADER RTP_PAYLOAD),
-	       pdu_priv);
+			     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 RTP_PAYLOAD),
-		   pdu_priv);
+		   msgb_from_hex("RTP data", RTP_HEADER 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 RTP_PAYLOAD),
-		   pdu_priv);
+		   msgb_from_hex("RTP data", RTP_HEADER RTP_PAYLOAD));
 	expect_tx_msg = RTP_HEADER "02007eb3" RTP_PAYLOAD;
 	tx_payload(cn,
-		   msgb_from_hex("RTP data", RTP_HEADER RTP_PAYLOAD),
-		   pdu_priv);
+		   msgb_from_hex("RTP data", RTP_HEADER RTP_PAYLOAD));
 	expect_tx_msg = RTP_HEADER "03009eb3" RTP_PAYLOAD;
 	tx_payload(cn,
-		   msgb_from_hex("RTP data", RTP_HEADER RTP_PAYLOAD),
-		   pdu_priv);
+		   msgb_from_hex("RTP data", RTP_HEADER RTP_PAYLOAD));
 
 	printf("All done.\n");
 }
diff --git a/tests/iuup/iuup_test.ok b/tests/iuup/iuup_test.ok
index fa46635..2b09c66 100644
--- a/tests/iuup/iuup_test.ok
+++ b/tests/iuup/iuup_test.ok
@@ -2,11 +2,10 @@
 Send IuUP Initialization. Expecting direct tx_msg() of the Initialization Ack
  -PDU-> [IuUP]
 8060dc5219495e3f00010111e000df99160051673c01270000820000001710000100
-pdu_priv=0xfeed
 tx_msg() invoked by iuup_cn!
  <-PDU- [IuUP]
-e400240000
-node_priv=0x2342 pdu_priv=0xfeed
+8060dc5219495e3f00010111e4002400
+node_priv=0x2342
 ok: matches expected msg
 rc=0
 
@@ -14,51 +13,46 @@
 i.e. should strip away 0100e2b3
  -PDU-> [IuUP]
 8060944c6256042c000101020100e2b36cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
-pdu_priv=0xfeed
 rx_payload() invoked by iuup_cn!
         [IuUP] -RTP->
 8060944c6256042c000101026cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
-node_priv=0x2342 pdu_priv=0xfeed
+node_priv=0x2342
 ok: matches expected msg
 rc=0
 
 Transmit RTP. Expecting tx_msg() with inserted IuUP header
         [IuUP] <-RTP-
 8060944c6256042c000101026cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
-pdu_priv=0xfeed
 tx_msg() invoked by iuup_cn!
  <-PDU- [IuUP]
 8060944c6256042c00010102000002b36cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
-node_priv=0x2342 pdu_priv=0xfeed
+node_priv=0x2342
 ok: matches expected msg
 rc=0
 
 More RTP, each time the Frame Nr advances, causing a new header CRC.
         [IuUP] <-RTP-
 8060944c6256042c000101026cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
-pdu_priv=0xfeed
 tx_msg() invoked by iuup_cn!
  <-PDU- [IuUP]
 8060944c6256042c000101020100e2b36cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
-node_priv=0x2342 pdu_priv=0xfeed
+node_priv=0x2342
 ok: matches expected msg
 rc=0
         [IuUP] <-RTP-
 8060944c6256042c000101026cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
-pdu_priv=0xfeed
 tx_msg() invoked by iuup_cn!
  <-PDU- [IuUP]
 8060944c6256042c0001010202007eb36cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
-node_priv=0x2342 pdu_priv=0xfeed
+node_priv=0x2342
 ok: matches expected msg
 rc=0
         [IuUP] <-RTP-
 8060944c6256042c000101026cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
-pdu_priv=0xfeed
 tx_msg() invoked by iuup_cn!
  <-PDU- [IuUP]
 8060944c6256042c0001010203009eb36cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
-node_priv=0x2342 pdu_priv=0xfeed
+node_priv=0x2342
 ok: matches expected msg
 rc=0
 All done.