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
diff --git a/include/osmocom/mgcp/Makefile.am b/include/osmocom/mgcp/Makefile.am
index 65ca670..a618b94 100644
--- a/include/osmocom/mgcp/Makefile.am
+++ b/include/osmocom/mgcp/Makefile.am
@@ -7,4 +7,6 @@
 	mgcp_sdp.h \
 	mgcp_codec.h \
 	debug.h \
+	iuup_cn_node.h \
+	iuup_protocol.h \
 	$(NULL)
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);