aboutsummaryrefslogtreecommitdiffstats
path: root/openbsc/src
diff options
context:
space:
mode:
Diffstat (limited to 'openbsc/src')
-rw-r--r--openbsc/src/libmgcp/Makefile.am8
-rw-r--r--openbsc/src/libmgcp/mgcp_network.c62
-rw-r--r--openbsc/src/libmgcp/mgcp_protocol.c42
-rw-r--r--openbsc/src/libmgcp/mgcp_vty.c44
-rw-r--r--openbsc/src/libmgcp/osmux.c398
-rw-r--r--openbsc/src/osmo-bsc_mgcp/Makefile.am3
-rw-r--r--openbsc/src/osmo-bsc_nat/Makefile.am5
-rw-r--r--openbsc/src/osmo-bsc_nat/bsc_mgcp_utils.c32
-rw-r--r--openbsc/src/osmo-bsc_nat/bsc_nat_vty.c19
9 files changed, 571 insertions, 42 deletions
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 <openbsc/mgcp.h>
#include <openbsc/mgcp_internal.h>
+#include <openbsc/osmux.h>
+
#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 <pablo@gnumonks.org>
+ * (C) 2012-2013 by On Waves ehf <http://www.on-waves.com>
+ * 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 <stdio.h> /* for printf */
+#include <string.h> /* for memcpy */
+#include <stdlib.h> /* for abs */
+#include <netinet/in.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/talloc.h>
+
+#include <osmocom/netif/osmux.h>
+#include <osmocom/netif/rtp.h>
+
+#include <openbsc/mgcp.h>
+#include <openbsc/mgcp_internal.h>
+#include <openbsc/osmux.h>
+
+#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; i<cfg->trunk.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();