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.

This patch here also adds RTP header stripping/adding functionality, after
introducing using msgb to pass data around.

Change-Id: Ibc70e0aa00476926dd1f4ea8139c34f31f9cdfa3
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..8d96f06
--- /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 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 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));
+	expect_tx_msg = RTP_HEADER "02007eb3" RTP_PAYLOAD;
+	tx_payload(cn,
+		   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));
+
+	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..2b09c66
--- /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->
+8060944c6256042c000101026cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
+node_priv=0x2342
+ok: matches expected msg
+rc=0
+
+Transmit RTP. Expecting tx_msg() with inserted IuUP header
+        [IuUP] <-RTP-
+8060944c6256042c000101026cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
+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-
+8060944c6256042c000101026cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
+tx_msg() invoked by iuup_cn!
+ <-PDU- [IuUP]
+8060944c6256042c000101020100e2b36cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
+node_priv=0x2342
+ok: matches expected msg
+rc=0
+        [IuUP] <-RTP-
+8060944c6256042c000101026cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
+tx_msg() invoked by iuup_cn!
+ <-PDU- [IuUP]
+8060944c6256042c0001010202007eb36cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
+node_priv=0x2342
+ok: matches expected msg
+rc=0
+        [IuUP] <-RTP-
+8060944c6256042c000101026cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
+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 a540056..ebcad8e 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>
@@ -1189,7 +1190,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)
 {
@@ -1200,7 +1201,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;
@@ -1247,16 +1247,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",
@@ -1283,6 +1284,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