diff options
-rw-r--r-- | openbsc/include/openbsc/Makefile.am | 2 | ||||
-rw-r--r-- | openbsc/include/openbsc/gsm_data.h | 2 | ||||
-rw-r--r-- | openbsc/include/openbsc/rtp_proxy.h | 70 | ||||
-rw-r--r-- | openbsc/src/Makefile.am | 2 | ||||
-rw-r--r-- | openbsc/src/bsc_hack.c | 7 | ||||
-rw-r--r-- | openbsc/src/gsm_04_08.c | 132 | ||||
-rw-r--r-- | openbsc/src/rtp_proxy.c | 446 |
7 files changed, 636 insertions, 25 deletions
diff --git a/openbsc/include/openbsc/Makefile.am b/openbsc/include/openbsc/Makefile.am index 86f056d26..adef573e5 100644 --- a/openbsc/include/openbsc/Makefile.am +++ b/openbsc/include/openbsc/Makefile.am @@ -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 diff --git a/openbsc/include/openbsc/gsm_data.h b/openbsc/include/openbsc/gsm_data.h index 5eac8a7d1..e6dffa8f2 100644 --- a/openbsc/include/openbsc/gsm_data.h +++ b/openbsc/include/openbsc/gsm_data.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]; diff --git a/openbsc/include/openbsc/rtp_proxy.h b/openbsc/include/openbsc/rtp_proxy.h new file mode 100644 index 000000000..e9fc157cf --- /dev/null +++ b/openbsc/include/openbsc/rtp_proxy.h @@ -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 */ diff --git a/openbsc/src/Makefile.am b/openbsc/src/Makefile.am index c6e9dae53..ed63c8e31 100644 --- a/openbsc/src/Makefile.am +++ b/openbsc/src/Makefile.am @@ -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 diff --git a/openbsc/src/bsc_hack.c b/openbsc/src/bsc_hack.c index 1bfb7068b..ee8bd1f6f 100644 --- a/openbsc/src/bsc_hack.c +++ b/openbsc/src/bsc_hack.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 */ diff --git a/openbsc/src/gsm_04_08.c b/openbsc/src/gsm_04_08.c index a9f2ebd8e..2d3a634e1 100644 --- a/openbsc/src/gsm_04_08.c +++ b/openbsc/src/gsm_04_08.c @@ -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); +} diff --git a/openbsc/src/rtp_proxy.c b/openbsc/src/rtp_proxy.c new file mode 100644 index 000000000..61fc38575 --- /dev/null +++ b/openbsc/src/rtp_proxy.c @@ -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; +} |