aboutsummaryrefslogtreecommitdiffstats
path: root/openbsc/src/rtp_proxy.c
diff options
context:
space:
mode:
authorHarald Welte <laforge@gnumonks.org>2009-07-28 18:25:29 +0200
committerHarald Welte <laforge@gnumonks.org>2009-07-28 18:25:29 +0200
commit805f64486b490d8c7042235e69565755074b768e (patch)
treed9e4fd82232b2dd80a19ecba40a1509a8fac2501 /openbsc/src/rtp_proxy.c
parentaea9a98dc2dfbbad0daf434c97bc6d2a4d3384f2 (diff)
add RTP proxy mode for ip.access
Up until now, we only supported direct RTP streams between ip.access BTS. With this commit, the user can specify '-P' to the command line to enable a RTP/RTCP proxy inside OpenBSC. The nanoBTS will then send all their voice data to OpenBSC, which will relay it to the respective destination BTS (which can be the same BTS). The default behaviour remains unchanged. Without '-P' on the command line, RTP/RTCP is exchanged directly.
Diffstat (limited to 'openbsc/src/rtp_proxy.c')
-rw-r--r--openbsc/src/rtp_proxy.c188
1 files changed, 176 insertions, 12 deletions
diff --git a/openbsc/src/rtp_proxy.c b/openbsc/src/rtp_proxy.c
index f7eb8b835..61fc38575 100644
--- a/openbsc/src/rtp_proxy.c
+++ b/openbsc/src/rtp_proxy.c
@@ -23,15 +23,21 @@
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
+#include <arpa/inet.h>
#include <openbsc/talloc.h>
#include <openbsc/gsm_data.h>
#include <openbsc/msgb.h>
#include <openbsc/select.h>
+#include <openbsc/debug.h>
#include <openbsc/rtp_proxy.h>
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,
@@ -40,6 +46,103 @@ enum rtp_bfd_priv {
#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
+
+/* iterate over all chunks in one RTCP message, kook 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;
+
+ /* 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 (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)
{
@@ -60,13 +163,21 @@ static int rtp_socket_read(struct rtp_socket *rs, struct rtp_sub_socket *rss)
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)
+ else if (rss->bfd.priv_nr == RTP_PRIV_RTCP) {
other_rss = &rs->proxy.other_sock->rtcp;
- else {
- msgb_free(msg);
- return -EINVAL;
+ /* 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;
@@ -75,6 +186,10 @@ static int rtp_socket_read(struct rtp_socket *rs, struct rtp_sub_socket *rss)
}
return rc;
+
+out_free:
+ msgb_free(msg);
+ return rc;
}
/* write from tx_queue to RTP/RTCP socket */
@@ -143,6 +258,8 @@ 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;
@@ -168,8 +285,16 @@ struct rtp_socket *rtp_socket_create(void)
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:
@@ -178,31 +303,48 @@ 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;
- return bind(rss->bfd.fd, (struct sockaddr *)&rss->sin_local,
- sizeof(rss->sin_local));
+ 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 u_int16_t next_udp_port = RTP_PORT_BASE;
+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;
+ 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 += 2) {
+ 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;
@@ -211,27 +353,45 @@ int rtp_socket_bind(struct rtp_socket *rs, u_int32_t ip)
if (rc == 0)
break;
}
- if (rc < 0)
+ 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);
- return connect(rss->bfd.fd, (struct sockaddr *) &rss->sin_remote,
- sizeof(rss->sin_remote));
+ 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)
@@ -243,6 +403,9 @@ int rtp_socket_connect(struct rtp_socket *rs, u_int32_t ip, u_int16_t port)
/* 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;
@@ -262,6 +425,7 @@ static void free_tx_queue(struct rtp_sub_socket *rss)
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 &&