RTP-enable MNCC API for LCR + ip.access

Instead of passing TRAU frames down the MNCC API to the call control
application like MNCC, we now decode the TRAU frame into the actual codec
frame.  We do the same with the RTP packets in case of ip.access and
thus have a unified format of passing codec data from the BTS to
an application, independent of the BTS type.

This is only implemented for V1 full-rate at the moment, and needs
to be fixed.
diff --git a/openbsc/src/gsm_04_08.c b/openbsc/src/gsm_04_08.c
index d59d7d7..f367239 100644
--- a/openbsc/src/gsm_04_08.c
+++ b/openbsc/src/gsm_04_08.c
@@ -1864,12 +1864,16 @@
 	return 0;
 }
 
+static int tch_recv_mncc(struct gsm_network *net, u_int32_t callref, int enable);
+
 /* some other part of the code sends us a signal */
 static int handle_abisip_signal(unsigned int subsys, unsigned int signal,
 				 void *handler_data, void *signal_data)
 {
 	struct gsm_lchan *lchan = signal_data;
 	int rc;
+	struct gsm_network *net;
+	struct gsm_trans *trans;
 
 	if (subsys != SS_ABISIP)
 		return 0;
@@ -1896,6 +1900,15 @@
 				   lchan->abis_ip.bound_port);
 		if (rc < 0)
 			goto out_err;
+		/* check if any transactions on this lchan still have
+		 * a tch_recv_mncc request pending */
+		net = lchan->ts->trx->bts->network;
+		llist_for_each_entry(trans, &net->trans_list, entry) {
+			if (trans->lchan == lchan && trans->tch_recv) {
+				DEBUGP(DCC, "pending tch_recv_mncc request\n");
+				tch_recv_mncc(net, trans->callref, 1);
+			}
+		}
 		break;
 	case S_ABISIP_DLCX_IND:
 		/* the BTS tells us a RTP stream has been disconnected */
@@ -1941,7 +1954,8 @@
 		DEBUGP(DCC, "Cannot switch calls between different BTS types yet\n");
 		return -EINVAL;
 	}
-	
+
+	// todo: map between different bts types
 	switch (bts->type) {
 	case GSM_BTS_TYPE_NANOBTS:
 		if (!ipacc_rtp_direct) {
@@ -1950,6 +1964,7 @@
 			if (rc < 0)
 				return rc;
 			rc = ipacc_connect_proxy_bind(remote_lchan);
+#warning do we need a check of rc here?
 
 			/* connect them with each other */
 			rtp_socket_proxy(lchan->abis_ip.rtp_socket,
@@ -1971,8 +1986,7 @@
 		break;
 	default:
 		DEBUGP(DCC, "Unknown BTS type %u\n", bts->type);
-		rc = -EINVAL;
-		break;
+		return -EINVAL;
 	}
 
 	return 0;
@@ -1994,45 +2008,61 @@
 	return tch_map(trans1->lchan, trans2->lchan);
 }
 
-/* enable receive of channels to upqueue */
-static int tch_recv(struct gsm_network *net, struct gsm_mncc *data, int enable)
+/* enable receive of channels to MNCC upqueue */
+static int tch_recv_mncc(struct gsm_network *net, u_int32_t callref, int enable)
 {
 	struct gsm_trans *trans;
+	struct gsm_lchan *lchan;
+	struct gsm_bts *bts;
+	int rc;
 
 	/* Find callref */
-	trans = trans_find_by_callref(net, data->callref);
+	trans = trans_find_by_callref(net, callref);
 	if (!trans)
 		return -EIO;
 	if (!trans->lchan)
 		return 0;
+	lchan = trans->lchan;
+	bts = lchan->ts->trx->bts;
 
-	// todo IPACCESS
-	if (enable)
-		return trau_recv_lchan(trans->lchan, data->callref);
-	return trau_mux_unmap(NULL, data->callref);
+	switch (bts->type) {
+	case GSM_BTS_TYPE_NANOBTS:
+		if (ipacc_rtp_direct) {
+			DEBUGP(DCC, "Error: RTP proxy is disabled\n");
+			return -EINVAL;
+		}
+		/* in case, we don't have a RTP socket yet, we note this
+		 * in the transaction and try later */
+		if (!lchan->abis_ip.rtp_socket) {
+			trans->tch_recv = enable;
+			DEBUGP(DCC, "queue tch_recv_mncc request (%d)\n", enable);
+			return 0;
+		}
+		if (enable) {
+			/* connect the TCH's to our RTP proxy */
+			rc = ipacc_connect_proxy_bind(lchan);
+			if (rc < 0)
+				return rc;
+			/* assign socket to application interface */
+			rtp_socket_upstream(lchan->abis_ip.rtp_socket,
+				net, callref);
+		} else
+			rtp_socket_upstream(lchan->abis_ip.rtp_socket,
+				net, 0);
+		break;
+	case GSM_BTS_TYPE_BS11:
+		if (enable)
+			return trau_recv_lchan(lchan, callref);
+		return trau_mux_unmap(NULL, callref);
+		break;
+	default:
+		DEBUGP(DCC, "Unknown BTS type %u\n", bts->type);
+		return -EINVAL;
+	}
+
+	return 0;
 }
 
-/* send a frame to channel */
-static int tch_frame(struct gsm_network *net, struct gsm_trau_frame *frame)
-{
-	struct gsm_trans *trans;
-
-	/* Find callref */
-	trans = trans_find_by_callref(net, frame->callref);
-	if (!trans)
-		return -EIO;
-	if (!trans->lchan)
-		return 0;
-	if (trans->lchan->type != GSM_LCHAN_TCH_F &&
-	    trans->lchan->type != GSM_LCHAN_TCH_H)
-		return 0;
-
-	// todo IPACCESS
-	return trau_send_lchan(trans->lchan, 
-				(struct decoded_trau_frame *)frame->data);
-}
-
-
 static int gsm48_cc_rx_status_enq(struct gsm_trans *trans, struct msgb *msg)
 {
 	DEBUGP(DCC, "-> STATUS ENQ\n");
@@ -3262,11 +3292,30 @@
 	case MNCC_BRIDGE:
 		return tch_bridge(net, arg);
 	case MNCC_FRAME_DROP:
-		return tch_recv(net, arg, 0);
+		return tch_recv_mncc(net, data->callref, 0);
 	case MNCC_FRAME_RECV:
-		return tch_recv(net, arg, 1);
-	case GSM_TRAU_FRAME:
-		return tch_frame(net, arg);
+		return tch_recv_mncc(net, data->callref, 1);
+	case GSM_TCHF_FRAME:
+		/* Find callref */
+		trans = trans_find_by_callref(net, data->callref);
+		if (!trans)
+			return -EIO;
+		if (!trans->lchan)
+			return 0;
+		if (trans->lchan->type != GSM_LCHAN_TCH_F)
+			return 0;
+		bts = trans->lchan->ts->trx->bts;
+		switch (bts->type) {
+		case GSM_BTS_TYPE_NANOBTS:
+			if (!trans->lchan->abis_ip.rtp_socket)
+				return 0;
+			return rtp_send_frame(trans->lchan->abis_ip.rtp_socket, arg);
+		case GSM_BTS_TYPE_BS11:
+			return trau_send_frame(trans->lchan, arg);
+		default:
+			DEBUGP(DCC, "Unknown BTS type %u\n", bts->type);
+		}
+		return -EINVAL;
 	}
 
 	memset(&rel, 0, sizeof(struct gsm_mncc));
diff --git a/openbsc/src/rtp_proxy.c b/openbsc/src/rtp_proxy.c
index bfd4948..f9207d6 100644
--- a/openbsc/src/rtp_proxy.c
+++ b/openbsc/src/rtp_proxy.c
@@ -24,6 +24,10 @@
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
+#include <sys/time.h>    /* gettimeofday() */
+#include <unistd.h>      /* get..() */
+#include <time.h>        /* clock() */
+#include <sys/utsname.h> /* uname() */
 
 #include <openbsc/talloc.h>
 #include <openbsc/gsm_data.h>
@@ -57,6 +61,169 @@
 	
 #define RTCP_IE_CNAME	1
 
+/* according to RFC 3550 */
+struct rtp_hdr {
+	u_int8_t  csrc_count:4,
+		  extension:1,
+		  padding:1,
+		  version:2;
+	u_int8_t  payload_type:7,
+		  marker:1;
+	u_int16_t sequence;
+	u_int32_t timestamp;
+	u_int32_t ssrc;
+} __attribute__((packed));
+
+struct rtp_x_hdr {
+	u_int16_t by_profile;
+	u_int16_t length;
+} __attribute__((packed));
+
+#define RTP_VERSION	2
+
+#define RTP_PT_GSM_FULL	3
+
+/* decode an rtp frame and create a new buffer with payload */
+static int rtp_decode(struct msgb *msg, u_int32_t callref, struct msgb **data)
+{
+	struct msgb *new_msg;
+	struct gsm_data_frame *frame;
+	struct rtp_hdr *rtph = (struct rtp_hdr *)msg->data;
+	struct rtp_x_hdr *rtpxh;
+	u_int8_t *payload;
+	int payload_len;
+	int msg_type;
+	int x_len;
+
+	if (msg->len < 12) {
+		DEBUGPC(DMUX, "received RTP frame too short (len = %d)\n",
+			msg->len);
+		return -EINVAL;
+	}
+	if (rtph->version != RTP_VERSION) {
+		DEBUGPC(DMUX, "received RTP version %d not supported.\n",
+			rtph->version);
+		return -EINVAL;
+	}
+	payload = msg->data + sizeof(struct rtp_hdr) + (rtph->csrc_count << 2);
+	payload_len = msg->len - sizeof(struct rtp_hdr) - (rtph->csrc_count << 2);
+	if (payload_len < 0) {
+		DEBUGPC(DMUX, "received RTP frame too short (len = %d, "
+			"csrc count = %d)\n", msg->len, rtph->csrc_count);
+		return -EINVAL;
+	}
+	if (rtph->extension) {
+		if (payload_len < sizeof(struct rtp_x_hdr)) {
+			DEBUGPC(DMUX, "received RTP frame too short for "
+				"extension header\n");
+			return -EINVAL;
+		}
+		rtpxh = (struct rtp_x_hdr *)payload;
+		x_len = ntohs(rtpxh->length) * 4 + sizeof(struct rtp_x_hdr);
+		payload += x_len;
+		payload_len -= x_len;
+		if (payload_len < 0) {
+			DEBUGPC(DMUX, "received RTP frame too short, "
+				"extension header exceeds frame length\n");
+			return -EINVAL;
+		}
+	}
+	if (rtph->padding) {
+		if (payload_len < 0) {
+			DEBUGPC(DMUX, "received RTP frame too short for "
+				"padding length\n");
+			return -EINVAL;
+		}
+		payload_len -= payload[payload_len - 1];
+		if (payload_len < 0) {
+			DEBUGPC(DMUX, "received RTP frame with padding "
+				"greater than payload\n");
+			return -EINVAL;
+		}
+	}
+
+	switch (rtph->payload_type) {
+	case RTP_PT_GSM_FULL:
+		msg_type = GSM_TCHF_FRAME;
+		if (payload_len != 33) {
+			DEBUGPC(DMUX, "received RTP full rate frame with "
+				"payload length != 32 (len = %d)\n",
+				payload_len);
+			return -EINVAL;
+		}
+		break;
+	default:
+		DEBUGPC(DMUX, "received RTP frame with unknown payload "
+			"type %d\n", rtph->payload_type);
+		return -EINVAL;
+	}
+
+	new_msg = msgb_alloc(sizeof(struct gsm_data_frame) + payload_len,
+				"GSM-DATA");
+	if (!new_msg)
+		return -ENOMEM;
+	frame = (struct gsm_data_frame *)(new_msg->data);
+	frame->msg_type = msg_type;
+	frame->callref = callref;
+	memcpy(frame->data, payload, payload_len);
+	msgb_put(new_msg, sizeof(struct gsm_data_frame) + payload_len);
+
+	*data = new_msg;
+	return 0;
+}
+
+/* encode and send a rtp frame */
+int rtp_send_frame(struct rtp_socket *rs, struct gsm_data_frame *frame)
+{
+	struct rtp_sub_socket *rss = &rs->rtp;
+	struct msgb *msg;
+	struct rtp_hdr *rtph;
+	int payload_type;
+	int payload_len;
+	int duration; /* in samples */
+
+	if (rs->tx_action != RTP_SEND_DOWNSTREAM) {
+		/* initialize sequences */
+		rs->tx_action = RTP_SEND_DOWNSTREAM;
+		rs->transmit.ssrc = rand();
+		rs->transmit.sequence = random();
+		rs->transmit.timestamp = random();
+	}
+
+	switch (frame->msg_type) {
+	case GSM_TCHF_FRAME:
+		payload_type = RTP_PT_GSM_FULL;
+		payload_len = 33;
+		duration = 160;
+		break;
+	default:
+		DEBUGPC(DMUX, "unsupported message type %d\n",
+			frame->msg_type);
+		return -EINVAL;
+	}
+
+	msg = msgb_alloc(sizeof(struct rtp_hdr) + payload_len, "RTP-GSM-FULL");
+	if (!msg)
+		return -ENOMEM;
+	rtph = (struct rtp_hdr *)msg->data;
+	rtph->version = RTP_VERSION;
+	rtph->padding = 0;
+	rtph->extension = 0;
+	rtph->csrc_count = 0;
+	rtph->marker = 0;
+	rtph->payload_type = payload_type;
+	rtph->sequence = htons(rs->transmit.sequence++);
+	rtph->timestamp = htonl(rs->transmit.timestamp);
+	rs->transmit.timestamp += duration;
+	rtph->ssrc = htonl(rs->transmit.ssrc);
+	memcpy(msg->data + sizeof(struct rtp_hdr), frame->data, payload_len);
+	msgb_put(msg, sizeof(struct rtp_hdr) + payload_len);
+	msgb_enqueue(&rss->tx_queue, msg);
+	rss->bfd.when |= BSC_FD_WRITE;
+
+	return 0;
+}
+
 /* iterate over all chunks in one RTCP message, look for CNAME IEs and
  * replace all of those with 'new_cname' */
 static int rtcp_sdes_cname_mangle(struct msgb *msg, struct rtcp_hdr *rh,
@@ -123,10 +290,16 @@
 	if (!mangle_rtcp_cname)
 		return 0;
 
+	printf("RTCP\n");
 	/* iterate over list of RTCP messages */
 	rtph = (struct rtcp_hdr *)msg->data;
-	while ((void *)rtph + sizeof(*rtph) < (void *)msg->data + msg->len) {
+	while ((void *)rtph + sizeof(*rtph) <= (void *)msg->data + msg->len) {
 		old_len = (ntohs(rtph->length) + 1) * 4;
+		if ((void *)rtph + old_len > (void *)msg->data + msg->len) {
+			DEBUGPC(DMUX, "received RTCP packet too short for "
+				"length element\n");
+			return -EINVAL;
+		}
 		if (rtph->type == RTCP_TYPE_SDES) {
 			char new_cname[255];
 			strncpy(new_cname, inet_ntoa(rss->sin_local.sin_addr),
@@ -148,6 +321,7 @@
 {
 	int rc;
 	struct msgb *msg = msgb_alloc(RTP_ALLOC_SIZE, "RTP/RTCP");
+	struct msgb *new_msg;
 	struct rtp_sub_socket *other_rss;
 
 	if (!msg)
@@ -184,13 +358,40 @@
 		break;
 
 	case RTP_RECV_UPSTREAM:
-	case RTP_NONE:
-		/* FIXME: other cases */
-		DEBUGP(DMUX, "unhandled action: %d\n", rs->rx_action);
+		if (!rs->receive.callref || !rs->receive.net) {
+			rc = -EIO;
+			goto out_free;
+		}
+		if (rss->bfd.priv_nr == RTP_PRIV_RTCP) {
+			if (!mangle_rtcp_cname) {
+				msgb_free(msg);
+				break;
+			}
+			/* modify RTCP SDES CNAME */
+			rc = rtcp_mangle(msg, rs);
+			if (rc < 0)
+				goto out_free;
+			msgb_enqueue(&rss->tx_queue, msg);
+			rss->bfd.when |= BSC_FD_WRITE;
+			break;
+		}
+		if (rss->bfd.priv_nr != RTP_PRIV_RTP) {
+			rc = -EINVAL;
+			goto out_free;
+		}
+		rc = rtp_decode(msg, rs->receive.callref, &new_msg);
+		if (rc < 0)
+			goto out_free;
+		msgb_free(msg);
+		msgb_enqueue(&rs->receive.net->upqueue, new_msg);
+		break;
+
+	case RTP_NONE: /* if socket exists, but disabled by app */
+		msgb_free(msg);
 		break;
 	}
 
-	return rc;
+	return 0;
 
 out_free:
 	msgb_free(msg);
@@ -420,6 +621,22 @@
 	return 0;
 }
 
+/* bind RTP/RTCP socket to application */
+int rtp_socket_upstream(struct rtp_socket *this, struct gsm_network *net, u_int32_t callref)
+{
+	DEBUGP(DMUX, "rtp_socket_proxy(this=%p, callref=%lu)\n",
+		this, callref);
+
+	if (callref) {
+		this->rx_action = RTP_RECV_UPSTREAM;
+		this->receive.net = net;
+		this->receive.callref = callref;
+	} else
+		this->rx_action = RTP_NONE;
+
+	return 0;
+}
+
 static void free_tx_queue(struct rtp_sub_socket *rss)
 {
 	struct msgb *msg;
diff --git a/openbsc/src/trau_mux.c b/openbsc/src/trau_mux.c
index 7ea65ce..477ea21 100644
--- a/openbsc/src/trau_mux.c
+++ b/openbsc/src/trau_mux.c
@@ -32,6 +32,19 @@
 #include <openbsc/debug.h>
 #include <openbsc/talloc.h>
 
+u_int8_t gsm_fr_map[] = {
+	6, 6, 5, 5, 4, 4, 3, 3,
+	7, 2, 2, 6, 3, 3, 3, 3,
+	3, 3, 3, 3, 3, 3, 3, 3,
+	3, 7, 2, 2, 6, 3, 3, 3,
+	3, 3, 3, 3, 3, 3, 3, 3,
+	3, 3, 7, 2, 2, 6, 3, 3,
+	3, 3, 3, 3, 3, 3, 3, 3,
+	3, 3, 3, 7, 2, 2, 6, 3,
+	3, 3, 3, 3, 3, 3, 3, 3,
+	3, 3, 3, 3
+};
+
 struct map_entry {
 	struct llist_head list;
 	struct gsm_e1_subslot src, dst;
@@ -144,6 +157,8 @@
 	return NULL;
 }
 
+static const u_int8_t c_bits_check[] = { 0, 0, 0, 1, 0 };
+
 /* we get called by subchan_demux */
 int trau_mux_input(struct gsm_e1_subslot *src_e1_ss,
 		   const u_int8_t *trau_bits, int num_bits)
@@ -153,8 +168,6 @@
 	struct gsm_e1_subslot *dst_e1_ss = lookup_trau_mux_map(src_e1_ss);
 	struct subch_mux *mx;
 	struct upqueue_entry *ue;
-	struct msgb *msg;
-	struct gsm_trau_frame *frame;
 	int rc;
 
 	/* decode TRAU, change it to downlink, re-encode */
@@ -163,19 +176,44 @@
 		return rc;
 
 	if (!dst_e1_ss) {
+		struct msgb *msg;
+		struct gsm_data_frame *frame;
+		unsigned char *data;
+		int i, j, k, l, o;
 		/* frame shall be sent to upqueue */
 		if (!(ue = lookup_trau_upqueue(src_e1_ss)))
 			return -EINVAL;
 		if (!ue->callref)
 			return -EINVAL;
-		msg = msgb_alloc(sizeof(struct gsm_trau_frame) + sizeof(tf),
-				 "TRAU");
+		if (memcpy(tf.c_bits, c_bits_check, sizeof(c_bits_check)))
+			DEBUGPC(DMUX, "illegal trau (C1-C5) %s\n",
+				hexdump(tf.c_bits, sizeof(c_bits_check)));
+		msg = msgb_alloc(sizeof(struct gsm_data_frame) + 33,
+				 "GSM-DATA");
 		if (!msg)
 			return -ENOMEM;
-		frame = (struct gsm_trau_frame *)msg->data;
-		frame->msg_type = GSM_TRAU_FRAME;
+
+		frame = (struct gsm_data_frame *)msg->data;
+		memset(frame, 0, sizeof(struct gsm_data_frame));
+		data = frame->data;
+		data[0] = 0xd << 4;
+		/* reassemble d-bits */
+		i = 0; /* counts bits */
+		j = 4; /* counts output bits */
+		k = gsm_fr_map[0]-1; /* current number bit in element */
+		l = 0; /* counts element bits */
+		o = 0; /* offset input bits */
+		while (i < 260) {
+			data[j/8] |= (tf.d_bits[k+o] << (7-(j%8)));
+			if (--k < 0) {
+				o += gsm_fr_map[l];
+				k = gsm_fr_map[++l]-1;
+			}
+			i++;
+			j++;
+		}
+		frame->msg_type = GSM_TCHF_FRAME;
 		frame->callref = ue->callref;
-		memcpy(frame->data, &tf, sizeof(tf));
 		msgb_enqueue(&ue->net->upqueue, msg);
 
 		return 0;
@@ -221,17 +259,53 @@
 	return 0;
 }
 
-int trau_send_lchan(struct gsm_lchan *lchan, struct decoded_trau_frame *tf)
+int trau_send_frame(struct gsm_lchan *lchan, struct gsm_data_frame *frame)
 {
 	u_int8_t trau_bits_out[TRAU_FRAME_BITS];
 	struct gsm_e1_subslot *dst_e1_ss = &lchan->ts->e1_link;
 	struct subch_mux *mx;
+	int i, j, k, l, o;
+	unsigned char *data = frame->data;
+	struct decoded_trau_frame tf;
 
 	mx = e1inp_get_mux(dst_e1_ss->e1_nr, dst_e1_ss->e1_ts);
 	if (!mx)
 		return -EINVAL;
 
-	encode_trau_frame(trau_bits_out, tf);
+	switch (frame->msg_type) {
+	case GSM_TCHF_FRAME:
+		/* set c-bits and t-bits */
+		tf.c_bits[0] = 1;
+		tf.c_bits[1] = 1;
+		tf.c_bits[2] = 1;
+		tf.c_bits[3] = 0;
+		tf.c_bits[4] = 0;
+		memset(&tf.c_bits[5], 0, 6);
+		memset(&tf.c_bits[11], 1, 10);
+		memset(&tf.t_bits[0], 1, 4);
+		/* reassemble d-bits */
+		i = 0; /* counts bits */
+		j = 4; /* counts input bits */
+		k = gsm_fr_map[0]-1; /* current number bit in element */
+		l = 0; /* counts element bits */
+		o = 0; /* offset output bits */
+		while (i < 260) {
+			tf.d_bits[k+o] = (data[j/8] >> (7-(j%8))) & 1;
+			if (--k < 0) {
+				o += gsm_fr_map[l];
+				k = gsm_fr_map[++l]-1;
+			}
+			i++;
+			j++;
+		}
+		break;
+	default:
+		DEBUGPC(DMUX, "unsupported message type %d\n",
+			frame->msg_type);
+		return -EINVAL;
+	}
+
+	encode_trau_frame(trau_bits_out, &tf);
 
 	/* and send it to the muxer */
 	return subchan_mux_enqueue(mx, dst_e1_ss->e1_ts_ss, trau_bits_out,