re-structure the OpenBSC directory layout

The new structure divides the code into a number of libraries
for the BSC core functionality, MSC core functionality, Abis transport,
TRAU and other bits.

This doesn't introduce any functional code change but simply moves
around files and alters Makefile.am accordingly.

Next step would be to disentangle a lot of the inter-library
dependencies and make the individual bits of code more independent.
diff --git a/openbsc/src/trau/rtp_proxy.c b/openbsc/src/trau/rtp_proxy.c
new file mode 100644
index 0000000..eefc0e1
--- /dev/null
+++ b/openbsc/src/trau/rtp_proxy.c
@@ -0,0 +1,728 @@
+/* RTP proxy handling for ip.access nanoBTS */
+
+/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ * All Rights Reserved
+ *
+ * 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 <unistd.h>
+#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 <osmocore/talloc.h>
+#include <openbsc/gsm_data.h>
+#include <osmocore/msgb.h>
+#include <osmocore/select.h>
+#include <openbsc/debug.h>
+#include <openbsc/rtp_proxy.h>
+
+/* attempt to determine byte order */
+#include <sys/types.h>
+#include <sys/param.h>
+#include <limits.h>
+
+#ifndef __BYTE_ORDER
+#error "__BYTE_ORDER should be defined by someone"
+#endif
+
+static LLIST_HEAD(rtp_sockets);
+
+/* should we mangle the CNAME inside SDES of RTCP packets? We disable
+ * this by default, as it seems to be not needed */
+static int mangle_rtcp_cname = 0;
+
+enum rtp_bfd_priv {
+	RTP_PRIV_NONE,
+	RTP_PRIV_RTP,
+	RTP_PRIV_RTCP
+};
+
+#define RTP_ALLOC_SIZE	1500
+
+/* according to RFC 1889 */
+struct rtcp_hdr {
+	u_int8_t byte0;
+	u_int8_t type;
+	u_int16_t length;
+} __attribute__((packed));
+
+#define RTCP_TYPE_SDES	202
+	
+#define RTCP_IE_CNAME	1
+
+/* according to RFC 3550 */
+struct rtp_hdr {
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+	u_int8_t  csrc_count:4,
+		  extension:1,
+		  padding:1,
+		  version:2;
+	u_int8_t  payload_type:7,
+		  marker:1;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+	u_int8_t  version:2,
+		  padding:1,
+		  extension:1,
+		  csrc_count:4;
+	u_int8_t  marker:1,
+		  payload_type:7;
+#endif
+	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
+
+/* 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;
+	case RTP_PT_GSM_EFR:
+		msg_type = GSM_TCHF_FRAME_EFR;
+		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;
+}
+
+/* "to - from" */
+static void tv_difference(struct timeval *diff, const struct timeval *from,
+			  const struct timeval *__to)
+{
+	struct timeval _to = *__to, *to = &_to;
+
+	if (to->tv_usec < from->tv_usec) {
+		to->tv_sec -= 1;
+		to->tv_usec += 1000000;
+	}
+
+	diff->tv_usec = to->tv_usec - from->tv_usec;
+	diff->tv_sec = to->tv_sec - from->tv_sec;
+}
+
+/* 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;
+	case GSM_TCHF_FRAME_EFR:
+		payload_type = RTP_PT_GSM_EFR;
+		payload_len = 31;
+		duration = 160;
+		break;
+	default:
+		DEBUGPC(DMUX, "unsupported message type %d\n",
+			frame->msg_type);
+		return -EINVAL;
+	}
+
+	{
+		struct timeval tv, tv_diff;
+		long int usec_diff, frame_diff;
+
+		gettimeofday(&tv, NULL);
+		tv_difference(&tv_diff, &rs->transmit.last_tv, &tv);
+		rs->transmit.last_tv = tv;
+
+		usec_diff = tv_diff.tv_sec * 1000000 + tv_diff.tv_usec;
+		frame_diff = (usec_diff / 20000);
+
+		if (abs(frame_diff) > 1) {
+			long int frame_diff_excess = frame_diff - 1;
+
+			LOGP(DMUX, LOGL_NOTICE,
+				"Correcting frame difference of %ld frames\n", frame_diff_excess);
+			rs->transmit.sequence += frame_diff_excess;
+			rs->transmit.timestamp += frame_diff_excess * duration;
+		}
+	}
+
+	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,
+				  u_int16_t *rtcp_len, const char *new_cname)
+{
+	u_int8_t *rtcp_end;
+	u_int8_t *cur = (u_int8_t *) rh;
+	u_int8_t tag, len = 0;
+
+	rtcp_end = cur + *rtcp_len;
+	/* move cur to end of RTP header */
+	cur += sizeof(*rh);
+
+	/* iterate over Chunks */
+	while (cur+4 < rtcp_end) {
+		/* skip four bytes SSRC/CSRC */
+		cur += 4;
+	
+		/* iterate over IE's inside the chunk */
+		while (cur+1 < rtcp_end) {
+			tag = *cur++;
+			if (tag == 0) {
+				/* end of chunk, skip additional zero */
+				while (*cur++ == 0) { }
+				break;
+			}
+			len = *cur++;
+	
+			if (tag == RTCP_IE_CNAME) {
+				/* we've found the CNAME, lets mangle it */
+				if (len < strlen(new_cname)) {
+					/* we need to make more space */
+					int increase = strlen(new_cname) - len;
+
+					msgb_push(msg, increase);
+					memmove(cur+len+increase, cur+len,
+						rtcp_end - (cur+len));
+					/* FIXME: we have to respect RTCP
+					 * padding/alignment rules! */
+					len += increase;
+					*(cur-1) += increase;
+					rtcp_end += increase;
+					*rtcp_len += increase;
+				}
+				/* copy new CNAME into message */
+				memcpy(cur, new_cname, strlen(new_cname));
+				/* FIXME: zero the padding in case new CNAME
+				 * is smaller than old one !!! */
+			}
+			cur += len;
+		}
+	}
+
+	return 0;
+}
+
+static int rtcp_mangle(struct msgb *msg, struct rtp_socket *rs)
+{
+	struct rtp_sub_socket *rss = &rs->rtcp;
+	struct rtcp_hdr *rtph;
+	u_int16_t old_len;
+	int rc;
+
+	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) {
+		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),
+				sizeof(new_cname));
+			new_cname[sizeof(new_cname)-1] = '\0';
+			rc = rtcp_sdes_cname_mangle(msg, rtph, &old_len,
+						    new_cname);
+			if (rc < 0)
+				return rc;
+		}
+		rtph = (void *)rtph + old_len;
+	}
+
+	return 0;
+}
+
+/* read from incoming RTP/RTCP socket */
+static int rtp_socket_read(struct rtp_socket *rs, struct rtp_sub_socket *rss)
+{
+	int rc;
+	struct msgb *msg = msgb_alloc(RTP_ALLOC_SIZE, "RTP/RTCP");
+	struct msgb *new_msg;
+	struct rtp_sub_socket *other_rss;
+
+	if (!msg)
+		return -ENOMEM;
+
+	rc = read(rss->bfd.fd, msg->data, RTP_ALLOC_SIZE);
+	if (rc <= 0) {
+		rss->bfd.when &= ~BSC_FD_READ;
+		return rc;
+	}
+
+	msgb_put(msg, rc);
+
+	switch (rs->rx_action) {
+	case RTP_PROXY:
+		if (!rs->proxy.other_sock) {
+			rc = -EIO;
+			goto out_free;
+		}
+		if (rss->bfd.priv_nr == RTP_PRIV_RTP)
+			other_rss = &rs->proxy.other_sock->rtp;
+		else if (rss->bfd.priv_nr == RTP_PRIV_RTCP) {
+			other_rss = &rs->proxy.other_sock->rtcp;
+			/* modify RTCP SDES CNAME */
+			rc = rtcp_mangle(msg, rs);
+			if (rc < 0)
+				goto out_free;
+		} else {
+			rc = -EINVAL;
+			goto out_free;
+		}
+		msgb_enqueue(&other_rss->tx_queue, msg);
+		other_rss->bfd.when |= BSC_FD_WRITE;
+		break;
+
+	case RTP_RECV_UPSTREAM:
+		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);
+		trau_tx_to_mncc(rs->receive.net, new_msg);
+		break;
+
+	case RTP_NONE: /* if socket exists, but disabled by app */
+		msgb_free(msg);
+		break;
+	}
+
+	return 0;
+
+out_free:
+	msgb_free(msg);
+	return rc;
+}
+
+/* write from tx_queue to RTP/RTCP socket */
+static int rtp_socket_write(struct rtp_socket *rs, struct rtp_sub_socket *rss)
+{
+	struct msgb *msg;
+	int written;
+
+	msg = msgb_dequeue(&rss->tx_queue);
+	if (!msg) {
+		rss->bfd.when &= ~BSC_FD_WRITE;
+		return 0;
+	}
+
+	written = write(rss->bfd.fd, msg->data, msg->len);
+	if (written < msg->len) {
+		LOGP(DMIB, LOGL_ERROR, "short write");
+		msgb_free(msg);
+		return -EIO;
+	}
+
+	msgb_free(msg);
+
+	return 0;
+}
+
+
+/* callback for the select.c:bfd_* layer */
+static int rtp_bfd_cb(struct bsc_fd *bfd, unsigned int flags)
+{
+	struct rtp_socket *rs = bfd->data;
+	struct rtp_sub_socket *rss;
+
+	switch (bfd->priv_nr) {
+	case RTP_PRIV_RTP:
+		rss = &rs->rtp;
+		break;
+	case RTP_PRIV_RTCP:
+		rss = &rs->rtcp;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (flags & BSC_FD_READ)
+		rtp_socket_read(rs, rss);
+
+	if (flags & BSC_FD_WRITE)
+		rtp_socket_write(rs, rss);
+
+	return 0;
+}
+
+static void init_rss(struct rtp_sub_socket *rss,
+		     struct rtp_socket *rs, int fd, int priv_nr)
+{
+	/* initialize bfd */
+	rss->bfd.fd = fd;
+	rss->bfd.data = rs;
+	rss->bfd.priv_nr = priv_nr;
+	rss->bfd.cb = rtp_bfd_cb;
+}
+
+struct rtp_socket *rtp_socket_create(void)
+{
+	int rc;
+	struct rtp_socket *rs;
+
+	DEBUGP(DMUX, "rtp_socket_create(): ");
+
+	rs = talloc_zero(tall_bsc_ctx, struct rtp_socket);
+	if (!rs)
+		return NULL;
+
+	INIT_LLIST_HEAD(&rs->rtp.tx_queue);
+	INIT_LLIST_HEAD(&rs->rtcp.tx_queue);
+
+	rc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+	if (rc < 0)
+		goto out_free;
+
+	init_rss(&rs->rtp, rs, rc, RTP_PRIV_RTP);
+	rc = bsc_register_fd(&rs->rtp.bfd);
+	if (rc < 0)
+		goto out_rtp_socket;
+
+	rc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+	if (rc < 0)
+		goto out_rtp_bfd;
+
+	init_rss(&rs->rtcp, rs, rc, RTP_PRIV_RTCP);
+	rc = bsc_register_fd(&rs->rtcp.bfd);
+	if (rc < 0)
+		goto out_rtcp_socket;
+
+	DEBUGPC(DMUX, "success\n");
+
+	rc = rtp_socket_bind(rs, INADDR_ANY);
+	if (rc < 0)
+		goto out_rtcp_bfd;
+
+	return rs;
+
+out_rtcp_bfd:
+	bsc_unregister_fd(&rs->rtcp.bfd);
+out_rtcp_socket:
+	close(rs->rtcp.bfd.fd);
+out_rtp_bfd:
+	bsc_unregister_fd(&rs->rtp.bfd);
+out_rtp_socket:
+	close(rs->rtp.bfd.fd);
+out_free:
+	talloc_free(rs);
+	DEBUGPC(DMUX, "failed\n");
+	return NULL;
+}
+
+static int rtp_sub_socket_bind(struct rtp_sub_socket *rss, u_int32_t ip,
+				u_int16_t port)
+{
+	int rc;
+	socklen_t alen = sizeof(rss->sin_local);
+
+	rss->sin_local.sin_family = AF_INET;
+	rss->sin_local.sin_addr.s_addr = htonl(ip);
+	rss->sin_local.sin_port = htons(port);
+	rss->bfd.when |= BSC_FD_READ;
+
+	rc = bind(rss->bfd.fd, (struct sockaddr *)&rss->sin_local,
+		  sizeof(rss->sin_local));
+	if (rc < 0)
+		return rc;
+
+	/* retrieve the address we actually bound to, in case we
+	 * passed INADDR_ANY as IP address */
+	return getsockname(rss->bfd.fd, (struct sockaddr *)&rss->sin_local,
+			   &alen);
+}
+
+#define RTP_PORT_BASE	30000
+static unsigned int next_udp_port = RTP_PORT_BASE;
+
+/* bind a RTP socket to a local address */
+int rtp_socket_bind(struct rtp_socket *rs, u_int32_t ip)
+{
+	int rc = -EIO;
+	struct in_addr ia;
+
+	ia.s_addr = htonl(ip);
+	DEBUGP(DMUX, "rtp_socket_bind(rs=%p, IP=%s): ", rs,
+		inet_ntoa(ia));
+
+	/* try to bind to a consecutive pair of ports */
+	for (next_udp_port = next_udp_port % 0xffff;
+	     next_udp_port < 0xffff; next_udp_port += 2) {
+		rc = rtp_sub_socket_bind(&rs->rtp, ip, next_udp_port);
+		if (rc != 0)
+			continue;
+
+		rc = rtp_sub_socket_bind(&rs->rtcp, ip, next_udp_port+1);
+		if (rc == 0)
+			break;
+	}
+	if (rc < 0) {
+		DEBUGPC(DMUX, "failed\n");
+		return rc;
+	}
+
+	ia.s_addr = rs->rtp.sin_local.sin_addr.s_addr;
+	DEBUGPC(DMUX, "BOUND_IP=%s, BOUND_PORT=%u\n",
+		inet_ntoa(ia), ntohs(rs->rtp.sin_local.sin_port));
+	return ntohs(rs->rtp.sin_local.sin_port);
+}
+
+static int rtp_sub_socket_connect(struct rtp_sub_socket *rss,
+				  u_int32_t ip, u_int16_t port)
+{
+	int rc;
+	socklen_t alen = sizeof(rss->sin_local);
+
+	rss->sin_remote.sin_family = AF_INET;
+	rss->sin_remote.sin_addr.s_addr = htonl(ip);
+	rss->sin_remote.sin_port = htons(port);
+
+	rc = connect(rss->bfd.fd, (struct sockaddr *) &rss->sin_remote,
+		     sizeof(rss->sin_remote));
+	if (rc < 0)
+		return rc;
+
+	return getsockname(rss->bfd.fd, (struct sockaddr *)&rss->sin_local,
+			   &alen);
+}
+
+/* 'connect' a RTP socket to a remote peer */
+int rtp_socket_connect(struct rtp_socket *rs, u_int32_t ip, u_int16_t port)
+{
+	int rc;
+	struct in_addr ia;
+
+	ia.s_addr = htonl(ip);
+	DEBUGP(DMUX, "rtp_socket_connect(rs=%p, ip=%s, port=%u)\n",
+		rs, inet_ntoa(ia), port);
+
+	rc = rtp_sub_socket_connect(&rs->rtp, ip, port);
+	if (rc < 0)
+		return rc;
+
+	return rtp_sub_socket_connect(&rs->rtcp, ip, port+1);
+}
+
+/* bind two RTP/RTCP sockets together */
+int rtp_socket_proxy(struct rtp_socket *this, struct rtp_socket *other)
+{
+	DEBUGP(DMUX, "rtp_socket_proxy(this=%p, other=%p)\n",
+		this, other);
+
+	this->rx_action = RTP_PROXY;
+	this->proxy.other_sock = other;
+
+	other->rx_action = RTP_PROXY;
+	other->proxy.other_sock = this;
+
+	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=%u)\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;
+	
+	while ((msg = msgb_dequeue(&rss->tx_queue)))
+		msgb_free(msg);
+}
+
+int rtp_socket_free(struct rtp_socket *rs)
+{
+	DEBUGP(DMUX, "rtp_socket_free(rs=%p)\n", rs);
+
+	/* make sure we don't leave references dangling to us */
+	if (rs->rx_action == RTP_PROXY &&
+	    rs->proxy.other_sock)
+		rs->proxy.other_sock->proxy.other_sock = NULL;
+
+	bsc_unregister_fd(&rs->rtp.bfd);
+	close(rs->rtp.bfd.fd);
+	free_tx_queue(&rs->rtp);
+
+	bsc_unregister_fd(&rs->rtcp.bfd);
+	close(rs->rtcp.bfd.fd);
+	free_tx_queue(&rs->rtcp);
+
+	talloc_free(rs);
+
+	return 0;
+}