diff options
Diffstat (limited to 'openbsc/src')
-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 | 188 |
4 files changed, 293 insertions, 36 deletions
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 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 && |