diff --git a/openbsc/configure.ac b/openbsc/configure.ac index 5eb8bf9c2..26a7b6209 100644 --- a/openbsc/configure.ac +++ b/openbsc/configure.ac @@ -27,6 +27,7 @@ PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.3.0) PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 0.6.0) PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 0.2.0) PKG_CHECK_MODULES(LIBOSMOGB, libosmogb >= 0.6.4) +PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 0.0.1) # Enabke/disable the NAT? AC_ARG_ENABLE([nat], [AS_HELP_STRING([--enable-nat], [Build the BSC NAT. Requires SCCP])], diff --git a/openbsc/include/openbsc/Makefile.am b/openbsc/include/openbsc/Makefile.am index 8f7c1c423..d902315b5 100644 --- a/openbsc/include/openbsc/Makefile.am +++ b/openbsc/include/openbsc/Makefile.am @@ -13,7 +13,8 @@ noinst_HEADERS = abis_nm.h abis_rsl.h db.h gsm_04_08.h gsm_data.h \ osmo_bsc_rf.h osmo_bsc.h network_listen.h bsc_nat_sccp.h \ osmo_msc_data.h osmo_bsc_grace.h sms_queue.h abis_om2000.h \ bss.h gsm_data_shared.h control_cmd.h ipaccess.h mncc_int.h \ - arfcn_range_encode.h nat_rewrite_trie.h bsc_nat_callstats.h + arfcn_range_encode.h nat_rewrite_trie.h bsc_nat_callstats.h \ + osmux.h openbsc_HEADERS = gsm_04_08.h meas_rep.h bsc_api.h openbscdir = $(includedir)/openbsc diff --git a/openbsc/include/openbsc/bsc_nat.h b/openbsc/include/openbsc/bsc_nat.h index 7bd582cf6..7a161ff27 100644 --- a/openbsc/include/openbsc/bsc_nat.h +++ b/openbsc/include/openbsc/bsc_nat.h @@ -176,6 +176,9 @@ struct bsc_config { struct bsc_config_stats stats; struct llist_head lac_list; + + /* Osmux is enabled/disabled per BSC */ + int osmux; }; struct bsc_lac_entry { @@ -418,7 +421,7 @@ int bsc_mgcp_nat_init(struct bsc_nat *nat); struct nat_sccp_connection *bsc_mgcp_find_con(struct bsc_nat *, int endpoint_number); struct msgb *bsc_mgcp_rewrite(char *input, int length, int endp, const char *ip, - int port, int *payload_type); + int port, int osmux, int *payload_type); void bsc_mgcp_forward(struct bsc_connection *bsc, struct msgb *msg); void bsc_mgcp_clear_endpoints_for(struct bsc_connection *bsc); diff --git a/openbsc/include/openbsc/mgcp.h b/openbsc/include/openbsc/mgcp.h index 1d7407820..939e7eed1 100644 --- a/openbsc/include/openbsc/mgcp.h +++ b/openbsc/include/openbsc/mgcp.h @@ -181,6 +181,15 @@ struct mgcp_config { int last_bts_port; enum mgcp_role role; + + /* osmux translator: 0 means disabled, 1 means enabled */ + int osmux; + /* The BSC-NAT may ask for enabling osmux on demand. This tells us if + * the osmux socket is already initialized. + */ + int osmux_init; + /* osmux batch factor: from 1 to 4 maximum */ + int osmux_batch; }; /* config management */ @@ -222,4 +231,8 @@ int mgcp_send_reset_ep(struct mgcp_endpoint *endp, int endpoint); int mgcp_send_reset_all(struct mgcp_config *cfg); +int mgcp_create_bind(const char *source_addr, struct osmo_fd *fd, int port); +int mgcp_send(struct mgcp_endpoint *endp, int dest, int is_rtp, struct sockaddr_in *addr, char *buf, int rc); +int mgcp_udp_send(int fd, struct in_addr *addr, int port, char *buf, int len); + #endif diff --git a/openbsc/include/openbsc/mgcp_internal.h b/openbsc/include/openbsc/mgcp_internal.h index 9b9716539..8fe9f8182 100644 --- a/openbsc/include/openbsc/mgcp_internal.h +++ b/openbsc/include/openbsc/mgcp_internal.h @@ -126,8 +126,12 @@ struct mgcp_lco { enum mgcp_type { MGCP_RTP_DEFAULT = 0, MGCP_RTP_TRANSCODED, + MGCP_OSMUX_BSC, + MGCP_OSMUX_BSC_NAT, }; +#include + struct mgcp_endpoint { int allocated; uint32_t ci; @@ -163,6 +167,11 @@ struct mgcp_endpoint { /* tap for the endpoint */ struct mgcp_rtp_tap taps[MGCP_TAP_COUNT]; + + /* osmux is enabled/disabled */ + int osmux; + /* osmux internal to unbatch messages for this endpoint */ + struct osmux_out_handle osmux_out; }; #define ENDPOINT_NUMBER(endp) abs(endp - endp->tcfg->endpoints) @@ -197,5 +206,11 @@ void mgcp_state_calc_loss(struct mgcp_rtp_state *s, struct mgcp_rtp_end *, uint32_t *expected, int *loss); uint32_t mgcp_state_calc_jitter(struct mgcp_rtp_state *); +enum { + MGCP_DEST_NET = 0, + MGCP_DEST_BTS, +}; + +#define MGCP_DUMMY_LOAD 0x23 #endif diff --git a/openbsc/include/openbsc/osmux.h b/openbsc/include/openbsc/osmux.h new file mode 100644 index 000000000..33456b76d --- /dev/null +++ b/openbsc/include/openbsc/osmux.h @@ -0,0 +1,19 @@ +#ifndef _OPENBSC_OSMUX_H_ +#define _OPENBSC_OSMUX_H_ + +#include + +enum { + OSMUX_ROLE_BSC = 0, + OSMUX_ROLE_BSC_NAT, +}; + +int osmux_init(int role, struct mgcp_config *cfg); +int osmux_enable_endpoint(struct mgcp_endpoint *endp, int role); + +int osmux_xfrm_to_rtp(struct mgcp_endpoint *endp, int type, char *buf, int rc); +int osmux_xfrm_to_osmux(int type, char *buf, int rc, struct mgcp_endpoint *endp); + +int osmux_send_dummy(struct mgcp_endpoint *endp); + +#endif diff --git a/openbsc/src/libmgcp/Makefile.am b/openbsc/src/libmgcp/Makefile.am index 72f625dd4..262ad34a2 100644 --- a/openbsc/src/libmgcp/Makefile.am +++ b/openbsc/src/libmgcp/Makefile.am @@ -1,7 +1,9 @@ AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) -AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(COVERAGE_CFLAGS) -AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(COVERAGE_LDFLAGS) +AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) \ + $(LIBOSMONETIF_CFLAGS) $(COVERAGE_CFLAGS) +AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) \ + $(LIBOSMONETIF_LIBS) $(COVERAGE_LDFLAGS) noinst_LIBRARIES = libmgcp.a -libmgcp_a_SOURCES = mgcp_protocol.c mgcp_network.c mgcp_vty.c +libmgcp_a_SOURCES = mgcp_protocol.c mgcp_network.c mgcp_vty.c osmux.c diff --git a/openbsc/src/libmgcp/mgcp_network.c b/openbsc/src/libmgcp/mgcp_network.c index 8a5656a83..7c6926d28 100644 --- a/openbsc/src/libmgcp/mgcp_network.c +++ b/openbsc/src/libmgcp/mgcp_network.c @@ -35,6 +35,8 @@ #include #include +#include + #warning "Make use of the rtp proxy code" /* attempt to determine byte order */ @@ -78,20 +80,11 @@ struct rtp_hdr { #define RTP_MAX_DROPOUT 3000 #define RTP_MAX_MISORDER 100 - -enum { - MGCP_DEST_NET = 0, - MGCP_DEST_BTS, -}; - enum { MGCP_PROTO_RTP, MGCP_PROTO_RTCP, }; -#define MGCP_DUMMY_LOAD 0x23 - - /** * This does not need to be a precision timestamp and * is allowed to wrap quite fast. The returned value is @@ -118,8 +111,7 @@ static uint32_t get_current_ts(unsigned unit) return ret; } -static int mgcp_udp_send(int fd, struct in_addr *addr, int port, char *buf, - int len) +int mgcp_udp_send(int fd, struct in_addr *addr, int port, char *buf, int len) { struct sockaddr_in out; out.sin_family = AF_INET; @@ -567,8 +559,8 @@ static int mgcp_send_transcoder(struct mgcp_rtp_end *end, return rc; } -static int mgcp_send(struct mgcp_endpoint *endp, int dest, int is_rtp, - struct sockaddr_in *addr, char *buf, int rc) +int mgcp_send(struct mgcp_endpoint *endp, int dest, int is_rtp, + struct sockaddr_in *addr, char *buf, int rc) { struct mgcp_trunk_config *tcfg = endp->tcfg; struct mgcp_rtp_end *rtp_end; @@ -587,10 +579,22 @@ static int mgcp_send(struct mgcp_endpoint *endp, int dest, int is_rtp, rtp_end = &endp->net_end; rtp_state = &endp->bts_state; tap_idx = MGCP_TAP_NET_OUT; + + LOGP(DMGCP, LOGL_DEBUG, "delivering RTP to Network" + "via addr=%s, port=%u\n", + inet_ntoa(endp->net_end.addr), + ntohs(endp->net_end.rtp_port)); + } else { rtp_end = &endp->bts_end; rtp_state = &endp->net_state; tap_idx = MGCP_TAP_BTS_OUT; + + LOGP(DMGCP, LOGL_DEBUG, "delivering RTP to BTS" + "via addr=%s, port=%u\n", + inet_ntoa(endp->bts_end.addr), + ntohs(endp->bts_end.rtp_port)); + } if (!rtp_end->output_enabled) @@ -655,12 +659,20 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what) return -1; } - if (endp->net_end.rtp_port != addr.sin_port && - endp->net_end.rtcp_port != addr.sin_port) { - LOGP(DMGCP, LOGL_ERROR, - "Data from wrong source port %d on 0x%x\n", - ntohs(addr.sin_port), ENDPOINT_NUMBER(endp)); - return -1; + switch(endp->type) { + case MGCP_RTP_DEFAULT: + case MGCP_RTP_TRANSCODED: + if (endp->net_end.rtp_port != addr.sin_port && + endp->net_end.rtcp_port != addr.sin_port) { + LOGP(DMGCP, LOGL_ERROR, + "Data from wrong source port %d on 0x%x\n", + ntohs(addr.sin_port), ENDPOINT_NUMBER(endp)); + return -1; + } + break; + case MGCP_OSMUX_BSC: + case MGCP_OSMUX_BSC_NAT: + break; } /* throw away the dummy message */ @@ -683,6 +695,10 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what) case MGCP_RTP_TRANSCODED: return mgcp_send_transcoder(&endp->trans_net, endp->cfg, proto == MGCP_PROTO_RTP, buf, rc); + case MGCP_OSMUX_BSC_NAT: + return osmux_xfrm_to_osmux(MGCP_DEST_BTS, buf, rc, endp); + case MGCP_OSMUX_BSC: /* Should not happen */ + break; } LOGP(DMGCP, LOGL_ERROR, "Bad MGCP type %u on endpoint %u\n", @@ -771,6 +787,11 @@ static int rtp_data_bts(struct osmo_fd *fd, unsigned int what) case MGCP_RTP_TRANSCODED: return mgcp_send_transcoder(&endp->trans_bts, endp->cfg, proto == MGCP_PROTO_RTP, buf, rc); + case MGCP_OSMUX_BSC: + /* OSMUX translation: BTS -> BSC */ + return osmux_xfrm_to_osmux(MGCP_DEST_NET, buf, rc, endp); + case MGCP_OSMUX_BSC_NAT: + break; /* Should not happen */ } LOGP(DMGCP, LOGL_ERROR, "Bad MGCP type %u on endpoint %u\n", @@ -835,8 +856,7 @@ static int rtp_data_trans_bts(struct osmo_fd *fd, unsigned int what) return rtp_data_transcoder(&endp->trans_bts, endp, MGCP_DEST_BTS, fd); } -static int mgcp_create_bind(const char *source_addr, struct osmo_fd *fd, - int port) +int mgcp_create_bind(const char *source_addr, struct osmo_fd *fd, int port) { struct sockaddr_in addr; int on = 1; diff --git a/openbsc/src/libmgcp/mgcp_protocol.c b/openbsc/src/libmgcp/mgcp_protocol.c index 5c88c9dd3..1480acca3 100644 --- a/openbsc/src/libmgcp/mgcp_protocol.c +++ b/openbsc/src/libmgcp/mgcp_protocol.c @@ -236,7 +236,7 @@ static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp, addr = endp->cfg->source_addr; len = snprintf(sdp_record, sizeof(sdp_record) - 1, - "I: %u\n\n" + "I: %u%s\n\n" "v=0\r\n" "o=- %u 23 IN IP4 %s\r\n" "c=IN IP4 %s\r\n" @@ -244,7 +244,8 @@ static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp, "m=audio %d RTP/AVP %d\r\n" "a=rtpmap:%d %s\r\n" "%s%s", - endp->ci, endp->ci, addr, addr, + endp->ci, endp->cfg->osmux && endp->osmux ? "\nX-Osmux: On" : "", + endp->ci, addr, addr, endp->net_end.local_port, endp->bts_end.payload_type, endp->bts_end.payload_type, endp->tcfg->audio_name, fmtp_extra ? fmtp_extra : "", fmtp_extra ? "\r\n" : ""); @@ -759,6 +760,14 @@ static struct msgb *handle_create_con(struct mgcp_parse_data *p) case 'M': mode = (const char *) line + 3; break; + case 'X': + if (strcmp("Osmux: on", line + 2) == 0 && + osmux_enable_endpoint(endp, OSMUX_ROLE_BSC) < 0) { + LOGP(DMGCP, LOGL_ERROR, + "Could not activate osmux in endpoint %d\n", + ENDPOINT_NUMBER(endp)); + } + break; case '\0': have_sdp = 1; goto mgcp_header_done; @@ -859,8 +868,12 @@ mgcp_header_done: if (p->cfg->change_cb) p->cfg->change_cb(tcfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_CRCX); - if (endp->conn_mode & MGCP_CONN_RECV_ONLY && tcfg->keepalive_interval != 0) - mgcp_send_dummy(endp); + if (endp->conn_mode & MGCP_CONN_RECV_ONLY && tcfg->keepalive_interval != 0) { + if (endp->osmux) + osmux_send_dummy(endp); + else + mgcp_send_dummy(endp); + } create_transcoder(endp); return create_response_with_sdp(endp, "CRCX", p->trans); @@ -874,7 +887,7 @@ static struct msgb *handle_modify_con(struct mgcp_parse_data *p) { struct mgcp_endpoint *endp = p->endp; int error_code = 500; - int silent = 0; + int silent = 0, osmux = 0; char *line; const char *local_options = NULL; @@ -909,6 +922,10 @@ static struct msgb *handle_modify_con(struct mgcp_parse_data *p) } endp->orig_mode = endp->conn_mode; break; + case 'X': + if (strcmp("Osmux: on", line + 2) == 0) + osmux = 1; + break; case 'Z': silent = strcmp("noanswer", line + 3) == 0; break; @@ -926,6 +943,21 @@ static struct msgb *handle_modify_con(struct mgcp_parse_data *p) } } + /* Re-enable Osmux if we receive a MDCX, we have to set up a new + * RTP flow: this generates a randomly allocated RTP SSRC and sequence + * number. + */ + if (osmux) { + if (osmux_enable_endpoint(endp, OSMUX_ROLE_BSC) < 0) { + LOGP(DMGCP, LOGL_ERROR, + "Could not update osmux in endpoint %d\n", + ENDPOINT_NUMBER(endp)); + } + LOGP(DMGCP, LOGL_NOTICE, + "Re-enabling osmux in endpoint %d, we got updated\n", + ENDPOINT_NUMBER(endp)); + } + set_local_cx_options(endp->tcfg->endpoints, &endp->local_options, local_options); diff --git a/openbsc/src/libmgcp/mgcp_vty.c b/openbsc/src/libmgcp/mgcp_vty.c index 953d34b67..e9b7d1227 100644 --- a/openbsc/src/libmgcp/mgcp_vty.c +++ b/openbsc/src/libmgcp/mgcp_vty.c @@ -129,6 +129,10 @@ static int config_write_mgcp(struct vty *vty) vty_out(vty, " rtp transcoder-range %u %u%s", g_cfg->transcoder_ports.range_start, g_cfg->transcoder_ports.range_end, VTY_NEWLINE); vty_out(vty, " transcoder-remote-base %u%s", g_cfg->transcoder_remote_base, VTY_NEWLINE); + vty_out(vty, " osmux %s%s", + g_cfg->osmux == 1 ? "on" : "off", VTY_NEWLINE); + vty_out(vty, " osmux batch-factor %d%s", + g_cfg->osmux_batch, VTY_NEWLINE); return CMD_SUCCESS; } @@ -434,6 +438,10 @@ DEFUN(cfg_mgcp_loop, "Loop audio for all endpoints on main trunk\n" "Don't Loop\n" "Loop\n") { + if (g_cfg->osmux) { + vty_out(vty, "Cannot use `loop' with `osmux'.%s", VTY_NEWLINE); + return CMD_WARNING; + } g_cfg->trunk.audio_loop = atoi(argv[0]); return CMD_SUCCESS; } @@ -726,6 +734,10 @@ DEFUN(cfg_trunk_loop, { struct mgcp_trunk_config *trunk = vty->index; + if (g_cfg->osmux) { + vty_out(vty, "Cannot use `loop' with `osmux'.%s", VTY_NEWLINE); + return CMD_WARNING; + } trunk->audio_loop = atoi(argv[0]); return CMD_SUCCESS; } @@ -1056,6 +1068,33 @@ DEFUN(reset_all_endp, reset_all_endp_cmd, return CMD_SUCCESS; } +#define OSMUX_STR "RTP multiplexing" +DEFUN(cfg_mgcp_osmux, + cfg_mgcp_osmux_cmd, + "osmux (on|off)", + OSMUX_STR "Enable OSMUX\n" "Disable OSMUX\n") +{ + if (strcmp(argv[0], "on") == 0) { + g_cfg->osmux = 1; + if (g_cfg->trunk.audio_loop) { + vty_out(vty, "Cannot use `loop' with `osmux'.%s", + VTY_NEWLINE); + return CMD_WARNING; + } + } else if (strcmp(argv[0], "off") == 0) + g_cfg->osmux = 0; + + return CMD_SUCCESS; +} + +DEFUN(cfg_mgcp_osmux_batch_factor, + cfg_mgcp_osmux_batch_factor_cmd, + "osmux batch-factor <1-4>", + OSMUX_STR "Batching factor\n" "Number of messages in the batch\n") +{ + g_cfg->osmux_batch = atoi(argv[0]); + return CMD_SUCCESS; +} int mgcp_vty_init(void) { @@ -1108,6 +1147,8 @@ int mgcp_vty_init(void) install_element(MGCP_NODE, &cfg_mgcp_sdp_fmtp_extra_cmd); install_element(MGCP_NODE, &cfg_mgcp_sdp_payload_send_ptime_cmd); install_element(MGCP_NODE, &cfg_mgcp_no_sdp_payload_send_ptime_cmd); + install_element(MGCP_NODE, &cfg_mgcp_osmux_cmd); + install_element(MGCP_NODE, &cfg_mgcp_osmux_batch_factor_cmd); install_element(MGCP_NODE, &cfg_mgcp_trunk_cmd); install_node(&trunk_node, config_write_trunk); @@ -1203,6 +1244,9 @@ int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg, int rc; struct mgcp_trunk_config *trunk; + /* Default to 4 messages */ + cfg->osmux_batch = 4; + g_cfg = cfg; rc = vty_read_config_file(config_file, NULL); if (rc < 0) { diff --git a/openbsc/src/libmgcp/osmux.c b/openbsc/src/libmgcp/osmux.c new file mode 100644 index 000000000..3fdc652b0 --- /dev/null +++ b/openbsc/src/libmgcp/osmux.c @@ -0,0 +1,398 @@ +/* + * (C) 2012-2013 by Pablo Neira Ayuso + * (C) 2012-2013 by On Waves ehf + * All rights not specifically granted under this license are reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by the + * Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. + */ + +#include /* for printf */ +#include /* for memcpy */ +#include /* for abs */ +#include +#include +#include + +#include +#include + +#include +#include +#include + +#define OSMUX_PORT 1984 + +static struct osmo_fd osmux_fd; + +static LLIST_HEAD(osmux_handle_list); + +struct osmux_handle { + struct llist_head head; + struct osmux_in_handle *in; +}; + +static void *osmux; + +static void osmux_deliver(struct msgb *batch_msg, void *data) +{ + struct in_addr *addr = data; + struct sockaddr_in out = { + .sin_family = AF_INET, + .sin_port = htons(OSMUX_PORT), + }; + char buf[4096]; + + memcpy(&out.sin_addr, addr, sizeof(*addr)); + + osmux_snprintf(buf, sizeof(buf), batch_msg); + LOGP(DMGCP, LOGL_DEBUG, "OSMUX delivering batch to addr=%s: %s\n", + inet_ntoa(out.sin_addr), buf); + + sendto(osmux_fd.fd, batch_msg->data, batch_msg->len, 0, + (struct sockaddr *)&out, sizeof(out)); +} + +static struct osmux_in_handle * +osmux_handle_lookup(struct mgcp_config *cfg, struct in_addr *addr) +{ + struct osmux_handle *h; + + /* Lookup for existing OSMUX handle for this destination address. */ + llist_for_each_entry(h, &osmux_handle_list, head) { + if (memcmp(h->in->data, addr, sizeof(struct in_addr)) == 0) { + LOGP(DMGCP, LOGL_DEBUG, "using existing OSMUX handle " + "for addr=%s\n", + inet_ntoa(*addr)); + goto out; + } + } + + /* Does not exist, allocate it. */ + h = talloc_zero(osmux, struct osmux_handle); + if (!h) + return NULL; + + h->in = talloc_zero(osmux, struct osmux_in_handle); + if (!h->in) { + talloc_free(h); + return NULL; + } + + h->in->osmux_seq = 0; /* sequence number to start OSmux message from */ + h->in->batch_factor = cfg->osmux_batch; + h->in->deliver = osmux_deliver; + osmux_xfrm_input_init(h->in); + h->in->data = addr; + + llist_add(&h->head, &osmux_handle_list); + + LOGP(DMGCP, LOGL_DEBUG, "creating new OSMUX handle for addr=%s\n", + inet_ntoa(*addr)); +out: + return h->in; +} + +int osmux_xfrm_to_osmux(int type, char *buf, int rc, struct mgcp_endpoint *endp) +{ + int ret; + struct msgb *msg; + struct in_addr *addr; + struct osmux_in_handle *in; + + msg = msgb_alloc(4096, "RTP"); + if (!msg) + return 0; + + memcpy(msg->data, buf, rc); + msgb_put(msg, rc); + + switch(type) { + case MGCP_DEST_NET: + addr = &endp->net_end.addr; + break; + case MGCP_DEST_BTS: + addr = &endp->bts_end.addr; + break; + default: + /* Should not ever happen */ + LOGP(DMGCP, LOGL_ERROR, "Bad type %d. Fix your code.\n", type); + return 0; + } + + /* Lookup for osmux input handle that munches this RTP frame */ + in = osmux_handle_lookup(endp->cfg, addr); + if (!in) { + LOGP(DMGCP, LOGL_ERROR, "No osmux handle, aborting\n"); + return 0; + } + + LOGP(DMGCP, LOGL_DEBUG, "Osmux uses cid=%u from endpoint=%d (active=%d)\n", + endp->ci, ENDPOINT_NUMBER(endp), endp->allocated); + + while ((ret = osmux_xfrm_input(in, msg, endp->ci)) > 0) { + /* batch full, build and deliver it */ + osmux_xfrm_input_deliver(in); + } + return 0; +} + +static struct mgcp_endpoint * +endpoint_lookup(struct mgcp_config *cfg, int cid, + struct in_addr *from_addr, int type) +{ + struct mgcp_endpoint *tmp = NULL; + int i; + + /* Lookup for the endpoint that corresponds to this port */ + for (i=0; itrunk.number_endpoints; i++) { + struct in_addr *this; + + tmp = &cfg->trunk.endpoints[i]; + + if (!tmp->allocated) + continue; + + switch(type) { + case MGCP_DEST_NET: + this = &tmp->net_end.addr; + break; + case MGCP_DEST_BTS: + this = &tmp->bts_end.addr; + break; + default: + /* Should not ever happen */ + LOGP(DMGCP, LOGL_ERROR, "Bad type %d. Fix your code.\n", type); + return NULL; + } + + if (tmp->ci == cid && this->s_addr == from_addr->s_addr) + return tmp; + } + + LOGP(DMGCP, LOGL_ERROR, "Cannot find endpoint with cid=%d\n!\n", cid); + + return NULL; +} + +static void scheduled_tx_net_cb(struct msgb *msg, void *data) +{ + struct mgcp_endpoint *endp = data; + struct sockaddr_in addr; + + mgcp_send(endp, MGCP_DEST_NET, 1, &addr, (char *)msg->data, msg->len); + msgb_free(msg); +} + +static void scheduled_tx_bts_cb(struct msgb *msg, void *data) +{ + struct mgcp_endpoint *endp = data; + struct sockaddr_in addr; + + mgcp_send(endp, MGCP_DEST_BTS, 1, &addr, (char *)msg->data, msg->len); + msgb_free(msg); +} + +static struct msgb *osmux_recv(struct osmo_fd *ofd, struct sockaddr_in *addr) +{ + struct msgb *msg; + socklen_t slen = sizeof(*addr); + int ret; + + msg = msgb_alloc(4096, "OSMUX"); + if (!msg) { + LOGP(DMGCP, LOGL_ERROR, "cannot allocate message\n"); + return NULL; + } + ret = recvfrom(ofd->fd, msg->data, msg->data_len, 0, + (struct sockaddr *)addr, &slen); + if (ret <= 0) { + msgb_free(msg); + LOGP(DMGCP, LOGL_ERROR, "cannot receive message\n"); + return NULL; + } + msgb_put(msg, ret); + + return msg; +} + +int osmux_read_from_bsc_nat_cb(struct osmo_fd *ofd, unsigned int what) +{ + struct msgb *msg; + struct osmux_hdr *osmuxh; + struct llist_head list; + struct sockaddr_in addr; + struct mgcp_config *cfg = ofd->data; + char buf[4096]; + + msg = osmux_recv(ofd, &addr); + if (!msg) + return -1; + + /* not any further processing dummy messages */ + if (msg->data[0] == MGCP_DUMMY_LOAD) + goto out; + + osmux_snprintf(buf, sizeof(buf), msg); + LOGP(DMGCP, LOGL_DEBUG, "received OSMUX message from " + "BSC NAT (len=%d) %s\n", msg->len, buf); + + while((osmuxh = osmux_xfrm_output_pull(msg)) != NULL) { + struct mgcp_endpoint *endp; + + /* Yes, we use MGCP_DEST_NET to locate the origin */ + endp = endpoint_lookup(cfg, osmuxh->circuit_id, + &addr.sin_addr, MGCP_DEST_NET); + if (!endp) { + LOGP(DMGCP, LOGL_ERROR, + "Cannot find an endpoint for circuit_id=%d\n", + osmuxh->circuit_id); + goto out; + } + + LOGP(DMGCP, LOGL_DEBUG, + "sending extracted RTP from OSMUX to BSC via endpoint=%u " + "(allocated=%d)\n", ENDPOINT_NUMBER(endp), endp->allocated); + + osmux_xfrm_output(osmuxh, &endp->osmux_out, &list); + osmux_tx_sched(&list, scheduled_tx_bts_cb, endp); + } +out: + msgb_free(msg); + return 0; +} + +int osmux_read_from_bsc_cb(struct osmo_fd *ofd, unsigned int what) +{ + struct msgb *msg; + struct osmux_hdr *osmuxh; + struct llist_head list; + struct sockaddr_in addr; + struct mgcp_config *cfg = ofd->data; + char buf[4096]; + + msg = osmux_recv(ofd, &addr); + if (!msg) + return -1; + + /* not any further processing dummy messages */ + if (msg->data[0] == MGCP_DUMMY_LOAD) + goto out; + + osmux_snprintf(buf, sizeof(buf), msg); + LOGP(DMGCP, LOGL_DEBUG, "received OSMUX message " + "from BSC (len=%d) %s\n", msg->len, buf); + + while((osmuxh = osmux_xfrm_output_pull(msg)) != NULL) { + struct mgcp_endpoint *endp; + + /* Yes, we use MGCP_DEST_BTS to locate the origin */ + endp = endpoint_lookup(cfg, osmuxh->circuit_id, + &addr.sin_addr, MGCP_DEST_BTS); + if (!endp) { + LOGP(DMGCP, LOGL_ERROR, + "Cannot find an endpoint for circuit_id=%d\n", + osmuxh->circuit_id); + goto out; + } + + LOGP(DMGCP, LOGL_DEBUG, + "sending extracted RTP from OSMUX to MSC via endpoint=%u " + "(allocated=%d)\n", ENDPOINT_NUMBER(endp), endp->allocated); + + osmux_xfrm_output(osmuxh, &endp->osmux_out, &list); + osmux_tx_sched(&list, scheduled_tx_net_cb, endp); + } +out: + msgb_free(msg); + return 0; +} + +int osmux_init(int role, struct mgcp_config *cfg) +{ + int ret; + + switch(role) { + case OSMUX_ROLE_BSC: + osmux_fd.cb = osmux_read_from_bsc_nat_cb; + break; + case OSMUX_ROLE_BSC_NAT: + osmux_fd.cb = osmux_read_from_bsc_cb; + break; + default: + LOGP(DMGCP, LOGL_ERROR, "wrong role for OSMUX\n"); + return -1; + } + osmux_fd.data = cfg; + + ret = mgcp_create_bind("0.0.0.0", &osmux_fd, OSMUX_PORT); + if (ret < 0) { + LOGP(DMGCP, LOGL_ERROR, "cannot bind OSMUX socket\n"); + return ret; + } + osmux_fd.when |= BSC_FD_READ; + + ret = osmo_fd_register(&osmux_fd); + if (ret < 0) { + LOGP(DMGCP, LOGL_ERROR, "cannot register OSMUX socket\n"); + return ret; + } + cfg->osmux_init = 1; + + return 0; +} + +int osmux_enable_endpoint(struct mgcp_endpoint *endp, int role) +{ + /* If osmux is enabled, initialize the output handler. This handler is + * used to reconstruct the RTP flow from osmux. The RTP SSRC is + * allocated based on the circuit ID (endp->ci), which is unique in the + * local scope to the BSC/BSC-NAT. We use it to divide the RTP SSRC + * space (2^32) by the 256 possible circuit IDs, then randomly select + * one value from that window. Thus, we have no chance to have + * overlapping RTP SSRC traveling to the BTSes behind the BSC, + * similarly, for flows traveling to the MSC. + */ + static const uint32_t rtp_ssrc_winlen = UINT32_MAX / 256; + + if (!endp->cfg->osmux_init) { + if (osmux_init(role, endp->cfg) < 0) { + LOGP(DMGCP, LOGL_ERROR, "Cannot init OSMUX\n"); + return -1; + } + LOGP(DMGCP, LOGL_NOTICE, "OSMUX requested, ENABLING.\n"); + } + + osmux_xfrm_output_init(&endp->osmux_out, + (endp->ci * rtp_ssrc_winlen) + + (random() % rtp_ssrc_winlen)); + + switch (endp->cfg->role) { + case MGCP_BSC_NAT: + endp->type = MGCP_OSMUX_BSC_NAT; + break; + case MGCP_BSC: + endp->type = MGCP_OSMUX_BSC; + break; + } + endp->osmux = 1; + + return 0; +} + +/* We don't need to send the dummy load for osmux so often as another endpoint + * may have already punched the hole in the firewall. This approach is simple + * though. + */ +int osmux_send_dummy(struct mgcp_endpoint *endp) +{ + static char buf[] = { MGCP_DUMMY_LOAD }; + + LOGP(DMGCP, LOGL_DEBUG, "sending OSMUX dummy load to %s\n", + inet_ntoa(endp->net_end.addr)); + + return mgcp_udp_send(osmux_fd.fd, &endp->net_end.addr, + htons(OSMUX_PORT), buf, 1); +} diff --git a/openbsc/src/osmo-bsc_mgcp/Makefile.am b/openbsc/src/osmo-bsc_mgcp/Makefile.am index 0456cf108..7b6262142 100644 --- a/openbsc/src/osmo-bsc_mgcp/Makefile.am +++ b/openbsc/src/osmo-bsc_mgcp/Makefile.am @@ -7,4 +7,5 @@ bin_PROGRAMS = osmo-bsc_mgcp osmo_bsc_mgcp_SOURCES = mgcp_main.c osmo_bsc_mgcp_LDADD = $(top_builddir)/src/libcommon/libcommon.a \ $(top_builddir)/src/libmgcp/libmgcp.a -lrt \ - $(LIBOSMOVTY_LIBS) $(LIBOSMOCORE_LIBS) + $(LIBOSMOVTY_LIBS) $(LIBOSMOCORE_LIBS) \ + $(LIBOSMONETIF_LIBS) diff --git a/openbsc/src/osmo-bsc_nat/Makefile.am b/openbsc/src/osmo-bsc_nat/Makefile.am index aae6d6966..c9ea3e9a3 100644 --- a/openbsc/src/osmo-bsc_nat/Makefile.am +++ b/openbsc/src/osmo-bsc_nat/Makefile.am @@ -1,5 +1,5 @@ AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) -AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOSCCP_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS) +AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOSCCP_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(LIBOSMONETIF_CFLAGS) $(COVERAGE_CFLAGS) AM_LDFLAGS = $(COVERAGE_LDFLAGS) bin_PROGRAMS = osmo-bsc_nat @@ -14,4 +14,5 @@ osmo_bsc_nat_LDADD = $(top_builddir)/src/libcommon/libcommon.a \ $(top_builddir)/src/libtrau/libtrau.a \ $(top_builddir)/src/libctrl/libctrl.a \ -lrt $(LIBOSMOSCCP_LIBS) $(LIBOSMOCORE_LIBS) \ - $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOABIS_LIBS) + $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOABIS_LIBS) \ + $(LIBOSMONETIF_LIBS) diff --git a/openbsc/src/osmo-bsc_nat/bsc_mgcp_utils.c b/openbsc/src/osmo-bsc_nat/bsc_mgcp_utils.c index 3dad39628..22b8a3504 100644 --- a/openbsc/src/osmo-bsc_nat/bsc_mgcp_utils.c +++ b/openbsc/src/osmo-bsc_nat/bsc_mgcp_utils.c @@ -262,12 +262,12 @@ static void bsc_mgcp_send_mdcx(struct bsc_connection *bsc, int port, struct mgcp int len; len = snprintf(buf, sizeof(buf), - "MDCX 23 %x@mgw MGCP 1.0\r\n" + "MDCX 23 %x@mgw MGCP 1.0%s\r\n" "Z: noanswer\r\n" "\r\n" "c=IN IP4 %s\r\n" "m=audio %d RTP/AVP 255\r\n", - port, + port, bsc->cfg->osmux ? "\nX-Osmux: on" : "", bsc->nat->mgcp_cfg->source_addr, endp->bts_end.local_port); if (len < 0) { @@ -545,6 +545,7 @@ static int bsc_mgcp_policy_cb(struct mgcp_trunk_config *tcfg, int endpoint, int bsc_msg = bsc_mgcp_rewrite((char *) nat->mgcp_msg, nat->mgcp_length, sccp->bsc_endp, nat->mgcp_cfg->source_addr, mgcp_endp->bts_end.local_port, + nat->mgcp_cfg->osmux ? sccp->bsc->cfg->osmux : 0, &mgcp_endp->net_end.payload_type); if (!bsc_msg) { LOGP(DMGCP, LOGL_ERROR, "Failed to patch the msg.\n"); @@ -559,6 +560,16 @@ static int bsc_mgcp_policy_cb(struct mgcp_trunk_config *tcfg, int endpoint, int /* we need to update some bits */ if (state == MGCP_ENDP_CRCX) { struct sockaddr_in sock; + struct mgcp_endpoint *endp = &nat->mgcp_cfg->trunk.endpoints[endpoint]; + + if (nat->mgcp_cfg->osmux ? sccp->bsc->cfg->osmux : 0) { + if (osmux_enable_endpoint(endp, OSMUX_ROLE_BSC_NAT) < 0) { + LOGP(DMGCP, LOGL_ERROR, + "Could not activate osmux in endpoint %d\n", + ENDPOINT_NUMBER(endp)); + } + } + socklen_t len = sizeof(sock); if (getpeername(sccp->bsc->write_queue.bfd.fd, (struct sockaddr *) &sock, &len) != 0) { LOGP(DMGCP, LOGL_ERROR, "Can not get the peername...%d/%s\n", @@ -687,8 +698,8 @@ void bsc_mgcp_forward(struct bsc_connection *bsc, struct msgb *msg) output = bsc_mgcp_rewrite((char * ) msg->l2h, msgb_l2len(msg), -1, bsc->nat->mgcp_cfg->source_addr, endp->net_end.local_port, + bsc->nat->mgcp_cfg->osmux ? bsc_endp->bsc->cfg->osmux : 0, &endp->bts_end.payload_type); - if (!output) { LOGP(DMGCP, LOGL_ERROR, "Failed to rewrite MGCP msg.\n"); return; @@ -727,7 +738,7 @@ uint32_t bsc_mgcp_extract_ci(const char *str) * Create a new MGCPCommand based on the input and endpoint from a message */ static void patch_mgcp(struct msgb *output, const char *op, const char *tok, - int endp, int len, int cr) + int endp, int len, int cr, int osmux) { int slen; int ret; @@ -741,14 +752,15 @@ static void patch_mgcp(struct msgb *output, const char *op, const char *tok, return; } - slen = sprintf((char *) output->l3h, "%s %s %x@mgw MGCP 1.0%s", - op, buf, endp, cr ? "\r\n" : "\n"); + slen = sprintf((char *) output->l3h, "%s %s %x@mgw MGCP 1.0%s%s", + op, buf, endp, osmux ? "\nX-Osmux: on" : "", + cr ? "\r\n" : "\n"); output->l3h = msgb_put(output, slen); } /* we need to replace some strings... */ struct msgb *bsc_mgcp_rewrite(char *input, int length, int endpoint, - const char *ip, int port, + const char *ip, int port, int osmux, int *payload_type) { static const char crcx_str[] = "CRCX "; @@ -787,11 +799,11 @@ struct msgb *bsc_mgcp_rewrite(char *input, int length, int endpoint, cr = len > 0 && token[len - 1] == '\r'; if (strncmp(crcx_str, token, (sizeof crcx_str) - 1) == 0) { - patch_mgcp(output, "CRCX", token, endpoint, len, cr); + patch_mgcp(output, "CRCX", token, endpoint, len, cr, osmux); } else if (strncmp(dlcx_str, token, (sizeof dlcx_str) - 1) == 0) { - patch_mgcp(output, "DLCX", token, endpoint, len, cr); + patch_mgcp(output, "DLCX", token, endpoint, len, cr, 0); } else if (strncmp(mdcx_str, token, (sizeof mdcx_str) - 1) == 0) { - patch_mgcp(output, "MDCX", token, endpoint, len, cr); + patch_mgcp(output, "MDCX", token, endpoint, len, cr, osmux); } else if (strncmp(ip_str, token, (sizeof ip_str) - 1) == 0) { output->l3h = msgb_put(output, strlen(ip_str)); memcpy(output->l3h, ip_str, strlen(ip_str)); diff --git a/openbsc/src/osmo-bsc_nat/bsc_nat_vty.c b/openbsc/src/osmo-bsc_nat/bsc_nat_vty.c index 261a194c3..d5903d87e 100644 --- a/openbsc/src/osmo-bsc_nat/bsc_nat_vty.c +++ b/openbsc/src/osmo-bsc_nat/bsc_nat_vty.c @@ -172,6 +172,8 @@ static void config_write_bsc_single(struct vty *vty, struct bsc_config *bsc) if (bsc->paging_group != -1) vty_out(vty, " paging group %d%s", bsc->paging_group, VTY_NEWLINE); vty_out(vty, " paging forbidden %d%s", bsc->forbid_paging, VTY_NEWLINE); + if (bsc->osmux) + vty_out(vty, " osmux on%s", VTY_NEWLINE); } static int config_write_bsc(struct vty *vty) @@ -1175,6 +1177,22 @@ DEFUN(show_ussd_connection, return CMD_SUCCESS; } +#define OSMUX_STR "RTP multiplexing" +DEFUN(cfg_bsc_osmux, + cfg_bsc_osmux_cmd, + "osmux (on|off)", + OSMUX_STR "Enable OSMUX\n" "Disable OSMUX\n") +{ + struct bsc_config *conf = vty->index; + + if (strcmp(argv[0], "on") == 0) + conf->osmux = 1; + else if (strcmp(argv[0], "off") == 0) + conf->osmux = 0; + + return CMD_SUCCESS; +} + int bsc_nat_vty_init(struct bsc_nat *nat) { _nat = nat; @@ -1260,6 +1278,7 @@ int bsc_nat_vty_init(struct bsc_nat *nat) install_element(NAT_BSC_NODE, &cfg_bsc_old_grp_cmd); install_element(NAT_BSC_NODE, &cfg_bsc_paging_grp_cmd); install_element(NAT_BSC_NODE, &cfg_bsc_no_paging_grp_cmd); + install_element(NAT_BSC_NODE, &cfg_bsc_osmux_cmd); mgcp_vty_init(); diff --git a/openbsc/tests/bsc-nat/Makefile.am b/openbsc/tests/bsc-nat/Makefile.am index d773f2664..0d145a664 100644 --- a/openbsc/tests/bsc-nat/Makefile.am +++ b/openbsc/tests/bsc-nat/Makefile.am @@ -1,5 +1,5 @@ AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOSCCP_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS) +AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOSCCP_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(LIBOSMONETIF_CFLAGS) $(COVERAGE_CFLAGS) AM_LDFLAGS = $(COVERAGE_LDFLAGS) EXTRA_DIST = bsc_nat_test.ok bsc_data.c barr.cfg barr_dup.cfg prefixes.csv @@ -21,4 +21,4 @@ bsc_nat_test_LDADD = $(top_builddir)/src/libbsc/libbsc.a \ $(top_builddir)/src/libcommon/libcommon.a \ $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) -lrt \ $(LIBOSMOSCCP_LIBS) $(LIBOSMOVTY_LIBS) \ - $(LIBOSMOABIS_LIBS) + $(LIBOSMOABIS_LIBS) $(LIBOSMONETIF_LIBS) diff --git a/openbsc/tests/bsc-nat/bsc_nat_test.c b/openbsc/tests/bsc-nat/bsc_nat_test.c index bed5b8079..e9567c708 100644 --- a/openbsc/tests/bsc-nat/bsc_nat_test.c +++ b/openbsc/tests/bsc-nat/bsc_nat_test.c @@ -630,7 +630,7 @@ static void test_mgcp_rewrite(void) char *input = strdup(orig); output = bsc_mgcp_rewrite(input, strlen(input), 0x1e, - ip, port, &payload_type); + ip, port, 0, &payload_type); if (payload_type != -1) { fprintf(stderr, "Found media payload type %d in SDP data\n", diff --git a/openbsc/tests/mgcp/Makefile.am b/openbsc/tests/mgcp/Makefile.am index 71bf8c089..79e0bf49d 100644 --- a/openbsc/tests/mgcp/Makefile.am +++ b/openbsc/tests/mgcp/Makefile.am @@ -12,4 +12,4 @@ mgcp_test_LDADD = $(top_builddir)/src/libbsc/libbsc.a \ $(top_builddir)/src/libmgcp/libmgcp.a \ $(top_builddir)/src/libcommon/libcommon.a \ $(LIBOSMOCORE_LIBS) -lrt -lm $(LIBOSMOSCCP_LIBS) $(LIBOSMOVTY_LIBS) \ - $(LIBRARY_DL) + $(LIBRARY_DL) $(LIBOSMONETIF_LIBS)