Merge branch 'rtp_proxy'
This commit is contained in:
commit
15efcd2e27
|
@ -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
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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 */
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue