Merge branch 'rtp_proxy'

This commit is contained in:
Harald Welte 2009-07-28 18:40:08 +02:00
commit 15efcd2e27
7 changed files with 636 additions and 25 deletions

View File

@ -2,4 +2,4 @@ noinst_HEADERS = abis_nm.h abis_rsl.h debug.h db.h gsm_04_08.h gsm_data.h \
gsm_subscriber.h linuxlist.h msgb.h select.h tlv.h gsm_04_11.h \
timer.h misdn.h chan_alloc.h telnet_interface.h paging.h \
subchan_demux.h trau_frame.h e1_input.h trau_mux.h signal.h \
gsm_utils.h ipaccess.h rs232.h openbscdefines.h
gsm_utils.h ipaccess.h rs232.h openbscdefines.h rtp_proxy.h

View File

@ -98,6 +98,7 @@ struct gsm_bts_link {
struct gsm_lchan;
struct gsm_subscriber;
struct gsm_mncc;
struct rtp_socket;
/* One transaction */
struct gsm_trans {
@ -214,6 +215,7 @@ struct gsm_bts_trx_ts {
u_int16_t bound_port;
u_int8_t rtp_payload2;
u_int16_t conn_id;
struct rtp_socket *rtp_socket;
} abis_ip;
struct gsm_lchan lchan[TS_MAX_LCHAN];

View File

@ -0,0 +1,70 @@
#ifndef _RTP_PROXY_H
#define _RTP_PROXY_H
/* 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 General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include <netinet/in.h>
#include <openbsc/linuxlist.h>
#include <openbsc/select.h>
enum rtp_rx_action {
RTP_NONE,
RTP_PROXY,
RTP_RECV_UPSTREAM,
};
struct rtp_sub_socket {
struct sockaddr_in sin_local;
struct sockaddr_in sin_remote;
struct bsc_fd bfd;
/* linked list of to-be-transmitted msgb's */
struct llist_head tx_queue;
};
struct rtp_socket {
struct llist_head list;
struct rtp_sub_socket rtp;
struct rtp_sub_socket rtcp;
/* what should we do on receive? */
enum rtp_rx_action rx_action;
union {
struct {
struct rtp_socket *other_sock;
} proxy;
struct {
void (*recv_cb)(struct msgb *msg);
} receive;
};
};
struct rtp_socket *rtp_socket_create(void);
int rtp_socket_bind(struct rtp_socket *rs, u_int32_t ip);
int rtp_socket_connect(struct rtp_socket *rs, u_int32_t ip, u_int16_t port);
int rtp_socket_proxy(struct rtp_socket *this, struct rtp_socket *other);
int rtp_socket_free(struct rtp_socket *rs);
#endif /* _RTP_PROXY_H */

View File

@ -10,7 +10,7 @@ libbsc_a_SOURCES = abis_rsl.c abis_nm.c gsm_04_08.c gsm_data.c mncc.c \
gsm_04_11.c telnet_interface.c subchan_demux.c \
trau_frame.c trau_mux.c paging.c e1_config.c e1_input.c tlv_parser.c \
input/misdn.c input/ipaccess.c signal.c gsm_utils.c talloc.c \
transaction.c
transaction.c rtp_proxy.c
libvty_a_SOURCES = vty/buffer.c vty/command.c vty/vector.c vty/vty.c

View File

@ -64,6 +64,7 @@ static int release_l2 = 0;
static enum gsm_bts_type BTS_TYPE = GSM_BTS_TYPE_BS11;
static enum gsm_band BAND = GSM_BAND_900;
static const char *database_name = "hlr.sqlite3";
extern int ipacc_rtp_direct;
struct nano_bts_id {
struct llist_head entry;
@ -1113,10 +1114,11 @@ static void handle_options(int argc, char** argv)
{"bts-id", 1, 0, 'i'},
{"tsc", 1, 0, 'S'},
{"bsic", 1, 0, 'B'},
{"rtp-proxy", 0, 0, 'P'},
{0, 0, 0, 0}
};
c = getopt_long(argc, argv, "hc:n:d:sar:p:f:t:C:RL:l:Tb:i:S:B:",
c = getopt_long(argc, argv, "hc:n:d:sar:p:f:t:C:RL:l:Tb:i:S:B:P",
long_options, &option_index);
if (c == -1)
break;
@ -1187,6 +1189,9 @@ static void handle_options(int argc, char** argv)
case 'B':
BSIC = atoi(optarg);
break;
case 'P':
ipacc_rtp_direct = 0;
break;
}
default:
/* ignore */

View File

@ -44,6 +44,7 @@
#include <openbsc/signal.h>
#include <openbsc/trau_frame.h>
#include <openbsc/trau_mux.h>
#include <openbsc/rtp_proxy.h>
#include <openbsc/talloc.h>
#include <openbsc/transaction.h>
@ -56,6 +57,10 @@
static void *tall_locop_ctx;
/* should ip.access BTS use direct RTP streams between each other (1),
* or should OpenBSC always act as RTP relay/proxy in between (0) ? */
int ipacc_rtp_direct = 1;
static const struct tlv_definition rsl_att_tlvdef = {
.def = {
[GSM48_IE_MOBILE_ID] = { TLV_TYPE_TLV },
@ -399,17 +404,6 @@ static int gsm0408_handle_lchan_signal(unsigned int subsys, unsigned int signal,
return 0;
}
/*
* This will be ran by the linker when loading the DSO. We use it to
* do system initialization, e.g. registration of signal handlers.
*/
static __attribute__((constructor)) void on_dso_load_0408(void)
{
tall_locop_ctx = talloc_named_const(tall_bsc_ctx, 1,
"loc_updating_oper");
register_signal_handler(SS_LCHAN, gsm0408_handle_lchan_signal, NULL);
}
static void to_bcd(u_int8_t *bcd, u_int16_t val)
{
bcd[2] = val % 10;
@ -1973,12 +1967,80 @@ static int setup_trig_pag_evt(unsigned int hooknum, unsigned int event,
return 0;
}
/* 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;
struct gsm_bts_trx_ts *ts;
int rc;
if (subsys != SS_ABISIP)
return 0;
/* in case we use direct BTS-to-BTS RTP */
if (ipacc_rtp_direct)
return 0;
ts = lchan->ts;
switch (signal) {
case S_ABISIP_BIND_ACK:
/* the BTS has successfully bound a TCH to a local ip/port,
* which means we can connect our UDP socket to it */
if (ts->abis_ip.rtp_socket) {
rtp_socket_free(ts->abis_ip.rtp_socket);
ts->abis_ip.rtp_socket = NULL;
}
ts->abis_ip.rtp_socket = rtp_socket_create();
if (!ts->abis_ip.rtp_socket)
goto out_err;
rc = rtp_socket_connect(ts->abis_ip.rtp_socket,
ts->abis_ip.bound_ip,
ts->abis_ip.bound_port);
if (rc < 0)
goto out_err;
break;
case S_ABISIP_DISC_IND:
/* the BTS tells us a RTP stream has been disconnected */
if (ts->abis_ip.rtp_socket) {
rtp_socket_free(ts->abis_ip.rtp_socket);
ts->abis_ip.rtp_socket = NULL;
}
break;
}
return 0;
out_err:
/* FIXME: do something */
return 0;
}
/* bind rtp proxy to local IP/port and tell BTS to connect to it */
static int ipacc_connect_proxy_bind(struct gsm_lchan *lchan)
{
struct gsm_bts_trx_ts *ts = lchan->ts;
struct rtp_socket *rs = ts->abis_ip.rtp_socket;
int rc;
rc = rsl_ipacc_connect(lchan, ntohl(rs->rtp.sin_local.sin_addr.s_addr),
ntohs(rs->rtp.sin_local.sin_port),
ts->abis_ip.conn_id,
/* FIXME: use RTP payload of bound socket, not BTS*/
ts->abis_ip.rtp_payload2);
return rc;
}
/* map two ipaccess RTP streams onto each other */
static int tch_map(struct gsm_lchan *lchan, struct gsm_lchan *remote_lchan)
{
struct gsm_bts *bts = lchan->ts->trx->bts;
struct gsm_bts *remote_bts = remote_lchan->ts->trx->bts;
struct gsm_bts_trx_ts *ts;
int rc;
DEBUGP(DCC, "Setting up TCH map between (bts=%u,trx=%u,ts=%u) and (bts=%u,trx=%u,ts=%u)\n",
bts->nr, lchan->ts->trx->nr, lchan->ts->nr,
@ -1992,23 +2054,38 @@ static int tch_map(struct gsm_lchan *lchan, struct gsm_lchan *remote_lchan)
switch (bts->type) {
case GSM_BTS_TYPE_NANOBTS_900:
case GSM_BTS_TYPE_NANOBTS_1800:
ts = remote_lchan->ts;
rsl_ipacc_connect(lchan, ts->abis_ip.bound_ip,
ts->abis_ip.bound_port,
lchan->ts->abis_ip.conn_id,
ts->abis_ip.rtp_payload2);
ts = lchan->ts;
rsl_ipacc_connect(remote_lchan, ts->abis_ip.bound_ip,
ts->abis_ip.bound_port,
remote_lchan->ts->abis_ip.conn_id,
ts->abis_ip.rtp_payload2);
if (!ipacc_rtp_direct) {
/* connect the TCH's to our RTP proxy */
rc = ipacc_connect_proxy_bind(lchan);
if (rc < 0)
return rc;
rc = ipacc_connect_proxy_bind(remote_lchan);
/* connect them with each other */
rtp_socket_proxy(lchan->ts->abis_ip.rtp_socket,
remote_lchan->ts->abis_ip.rtp_socket);
} else {
/* directly connect TCH RTP streams to each other */
ts = remote_lchan->ts;
rc = rsl_ipacc_connect(lchan, ts->abis_ip.bound_ip,
ts->abis_ip.bound_port,
lchan->ts->abis_ip.conn_id,
ts->abis_ip.rtp_payload2);
if (rc < 0)
return rc;
ts = lchan->ts;
rc = rsl_ipacc_connect(remote_lchan, ts->abis_ip.bound_ip,
ts->abis_ip.bound_port,
remote_lchan->ts->abis_ip.conn_id,
ts->abis_ip.rtp_payload2);
}
break;
case GSM_BTS_TYPE_BS11:
trau_mux_map_lchan(lchan, remote_lchan);
break;
default:
DEBUGP(DCC, "Unknown BTS type %u\n", bts->type);
rc = -EINVAL;
break;
}
@ -3732,3 +3809,14 @@ int bsc_upqueue(struct gsm_network *net)
return work;
}
/*
* This will be ran by the linker when loading the DSO. We use it to
* do system initialization, e.g. registration of signal handlers.
*/
static __attribute__((constructor)) void on_dso_load_0408(void)
{
tall_locop_ctx = talloc_named_const(tall_bsc_ctx, 1,
"loc_updating_oper");
register_signal_handler(SS_LCHAN, gsm0408_handle_lchan_signal, NULL);
register_signal_handler(SS_ABISIP, handle_abisip_signal, NULL);
}

446
openbsc/src/rtp_proxy.c Normal file
View File

@ -0,0 +1,446 @@
/* 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 General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include <errno.h>
#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,
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
/* 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)
{
int rc;
struct msgb *msg = msgb_alloc(RTP_ALLOC_SIZE, "RTP/RTCP");
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;
/* FIXME: other cases */
}
return rc;
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) {
perror("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;
}
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;
}