From a496b6c690e9daeb1bee5350df7f9fa164027ebd Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 19 Feb 2013 14:31:31 +0100 Subject: mgcp: add voice muxer support This patch adds the voice muxer. You can use this to batch RTP traffic to reduce bandwidth comsuption. You can enable it via config file or the VTY interface via: mgcp ... osmux on osmux batch-factor 4 Port UDP/1984 is used for the muxer traffic between osmo-bsc_nat and osmo-bsc_mgcp (in the BSC side). The batching factor is optional, default value is 4. This requires libosmo-netif to be installed. --- openbsc/configure.ac | 1 + openbsc/include/openbsc/mgcp.h | 13 ++ openbsc/include/openbsc/mgcp_internal.h | 17 +- openbsc/include/openbsc/osmux.h | 22 ++ openbsc/src/libmgcp/Makefile.am | 8 +- openbsc/src/libmgcp/mgcp_network.c | 126 +++++++---- openbsc/src/libmgcp/mgcp_protocol.c | 24 ++- openbsc/src/libmgcp/mgcp_vty.c | 54 +++++ openbsc/src/libmgcp/osmux.c | 362 ++++++++++++++++++++++++++++++++ openbsc/src/osmo-bsc_mgcp/Makefile.am | 3 +- openbsc/src/osmo-bsc_mgcp/mgcp_main.c | 17 +- openbsc/src/osmo-bsc_nat/Makefile.am | 5 +- openbsc/src/osmo-bsc_nat/bsc_nat.c | 10 + openbsc/tests/bsc-nat/Makefile.am | 4 +- openbsc/tests/mgcp/Makefile.am | 3 +- 15 files changed, 615 insertions(+), 54 deletions(-) create mode 100644 openbsc/include/openbsc/osmux.h create mode 100644 openbsc/src/libmgcp/osmux.c diff --git a/openbsc/configure.ac b/openbsc/configure.ac index cdefcaf04..cab955feb 100644 --- a/openbsc/configure.ac +++ b/openbsc/configure.ac @@ -24,6 +24,7 @@ PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.3.0) PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 0.6.0) PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 0.1.0) PKG_CHECK_MODULES(LIBOSMOGB, libosmogb >= 0.5.2) +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/mgcp.h b/openbsc/include/openbsc/mgcp.h index 811dcfda7..2db2786a0 100644 --- a/openbsc/include/openbsc/mgcp.h +++ b/openbsc/include/openbsc/mgcp.h @@ -162,6 +162,11 @@ struct mgcp_config { /* only used for start with a static configuration */ int last_net_port; int last_bts_port; + + /* osmux translator: 0 means disabled, 1 means enabled */ + int osmux; + /* osmux batch factor: from 0 to 4 maximum */ + int osmux_batch; }; /* config management */ @@ -199,5 +204,13 @@ static inline void mgcp_endpoint_to_timeslot(int endpoint, int *multiplex, int * int mgcp_send_reset_ep(struct mgcp_endpoint *endp, int endpoint); int mgcp_send_reset_all(struct mgcp_config *cfg); +enum { + MGCP_DEST_NET = 0, + MGCP_DEST_BTS = 1, +}; + +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 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 455bb53df..b49d5ea3c 100644 --- a/openbsc/include/openbsc/mgcp_internal.h +++ b/openbsc/include/openbsc/mgcp_internal.h @@ -96,6 +96,15 @@ struct mgcp_rtp_tap { struct sockaddr_in forward; }; +enum mgcp_type { + MGCP_RTP = 0, /* default */ + MGCP_RTP_TRANSCODED, + MGCP_OSMUX_BSC, + MGCP_OSMUX_BSC_NAT, +}; + +#include + struct mgcp_endpoint { int allocated; uint32_t ci; @@ -119,7 +128,9 @@ struct mgcp_endpoint { */ struct mgcp_rtp_end trans_bts; struct mgcp_rtp_end trans_net; - int is_transcoded; + + /* see enum mgcp_type */ + enum mgcp_type type; /* sequence bits */ struct mgcp_rtp_state net_state; @@ -134,6 +145,9 @@ struct mgcp_endpoint { /* tap for the endpoint */ struct mgcp_rtp_tap taps[MGCP_TAP_COUNT]; + + /* osmux internal to unbatch messages for this endpoint */ + struct osmux osmux; }; #define ENDPOINT_NUMBER(endp) abs(endp - endp->tcfg->endpoints) @@ -163,5 +177,6 @@ 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 *); +#define DUMMY_LOAD 0x23 #endif diff --git a/openbsc/include/openbsc/osmux.h b/openbsc/include/openbsc/osmux.h new file mode 100644 index 000000000..7eddd59ac --- /dev/null +++ b/openbsc/include/openbsc/osmux.h @@ -0,0 +1,22 @@ +#ifndef _OPENBSC_OSMUX_H_ +#define _OPENBSC_OSMUX_H_ + +#include + +enum { + OSMUX_ROLE_BSC = 0, + OSMUX_ROLE_BSC_NAT, +}; + +struct osmux { + struct osmux_out_handle out; +}; + +int osmux_init(int role, struct mgcp_config *cfg); + +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_change_cb(struct mgcp_trunk_config *cfg, int endpoint, int state); + +#endif diff --git a/openbsc/src/libmgcp/Makefile.am b/openbsc/src/libmgcp/Makefile.am index 72f625dd4..27194db1b 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 a2cfc2385..2d4755b84 100644 --- a/openbsc/src/libmgcp/mgcp_network.c +++ b/openbsc/src/libmgcp/mgcp_network.c @@ -35,6 +35,9 @@ #include #include +#include +#include + #warning "Make use of the rtp proxy code" /* attempt to determine byte order */ @@ -78,20 +81,11 @@ struct rtp_hdr { #define RTP_MAX_DROPOUT 3000 #define RTP_MAX_MISORDER 100 - -enum { - DEST_NETWORK = 0, - DEST_BTS = 1, -}; - enum { PROTO_RTP, PROTO_RTCP, }; -#define DUMMY_LOAD 0x23 - - /** * This does not need to be a precision timestamp and * is allowed to wrap quite fast. The returned value is @@ -115,7 +109,7 @@ uint32_t get_current_ts(void) return ret; } -static int udp_send(int fd, struct in_addr *addr, int port, char *buf, int len) +int udp_send(int fd, struct in_addr *addr, int port, char *buf, int len) { struct sockaddr_in out; out.sin_family = AF_INET; @@ -263,8 +257,8 @@ static int send_transcoder(struct mgcp_rtp_end *end, struct mgcp_config *cfg, return rc; } -static int send_to(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; /* For loop toggle the destination and then dispatch. */ @@ -275,13 +269,19 @@ static int send_to(struct mgcp_endpoint *endp, int dest, int is_rtp, if (endp->conn_mode == MGCP_CONN_LOOPBACK) dest = !dest; - if (dest == DEST_NETWORK) { + if (dest == MGCP_DEST_NET) { if (is_rtp) { patch_and_count(endp, &endp->bts_state, endp->net_end.payload_type, addr, buf, rc); forward_data(endp->net_end.rtp.fd, &endp->taps[MGCP_TAP_NET_OUT], buf, rc); + + 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)); + return udp_send(endp->net_end.rtp.fd, &endp->net_end.addr, endp->net_end.rtp_port, buf, rc); } else if (!tcfg->omit_rtcp) { @@ -295,6 +295,12 @@ static int send_to(struct mgcp_endpoint *endp, int dest, int is_rtp, addr, buf, rc); forward_data(endp->bts_end.rtp.fd, &endp->taps[MGCP_TAP_BTS_OUT], buf, rc); + + 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)); + return udp_send(endp->bts_end.rtp.fd, &endp->bts_end.addr, endp->bts_end.rtp_port, buf, rc); } else if (!tcfg->omit_rtcp) { @@ -351,12 +357,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: + 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 */ @@ -371,10 +385,23 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what) endp->net_end.octets += rc; forward_data(fd->fd, &endp->taps[MGCP_TAP_NET_IN], buf, rc); - if (endp->is_transcoded) - return send_transcoder(&endp->trans_net, endp->cfg, proto == PROTO_RTP, &buf[0], rc); - else - return send_to(endp, DEST_BTS, proto == PROTO_RTP, &addr, &buf[0], rc); + switch(endp->type) { + case MGCP_RTP_TRANSCODED: + rc = send_transcoder(&endp->trans_net, endp->cfg, proto == PROTO_RTP, &buf[0], rc); + break; + case MGCP_RTP: + rc = mgcp_send(endp, MGCP_DEST_BTS, proto == PROTO_RTP, &addr, &buf[0], rc); + break; + case MGCP_OSMUX_BSC_NAT: + /* BSC-NAT -> BSC: translate to OSMUX */ + rc = osmux_xfrm_to_osmux(MGCP_DEST_BTS, buf, rc, endp); + break; + default: + LOGP(DMGCP, LOGL_ERROR, "unknown endpoint type %d\n", + endp->type); + break; + } + return rc; } static void discover_bts(struct mgcp_endpoint *endp, int proto, struct sockaddr_in *addr) @@ -430,12 +457,20 @@ static int rtp_data_bts(struct osmo_fd *fd, unsigned int what) return -1; } - if (endp->bts_end.rtp_port != addr.sin_port && - endp->bts_end.rtcp_port != addr.sin_port) { - LOGP(DMGCP, LOGL_ERROR, - "Data from wrong bts source port %d on 0x%x\n", - ntohs(addr.sin_port), ENDPOINT_NUMBER(endp)); - return -1; + switch(endp->type) { + case MGCP_RTP: + case MGCP_RTP_TRANSCODED: + if (endp->bts_end.rtp_port != addr.sin_port && + endp->bts_end.rtcp_port != addr.sin_port) { + LOGP(DMGCP, LOGL_ERROR, + "Data from wrong bts 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 */ @@ -450,10 +485,23 @@ static int rtp_data_bts(struct osmo_fd *fd, unsigned int what) endp->bts_end.octets += rc; forward_data(fd->fd, &endp->taps[MGCP_TAP_BTS_IN], buf, rc); - if (endp->is_transcoded) - return send_transcoder(&endp->trans_bts, endp->cfg, proto == PROTO_RTP, &buf[0], rc); - else - return send_to(endp, DEST_NETWORK, proto == PROTO_RTP, &addr, &buf[0], rc); + switch(endp->type) { + case MGCP_RTP_TRANSCODED: + rc = send_transcoder(&endp->trans_bts, endp->cfg, proto == PROTO_RTP, &buf[0], rc); + break; + case MGCP_RTP: + rc = mgcp_send(endp, MGCP_DEST_NET, proto == PROTO_RTP, &addr, &buf[0], rc); + break; + case MGCP_OSMUX_BSC: + /* BTS -> BSC, translate to OSMUX */ + rc = osmux_xfrm_to_osmux(MGCP_DEST_NET, buf, rc, endp); + break; + default: + LOGP(DMGCP, LOGL_ERROR, "unknown endpoit type %d\n", + endp->type); + break; + } + return rc; } static int rtp_data_transcoder(struct mgcp_rtp_end *end, struct mgcp_endpoint *_endp, @@ -494,7 +542,7 @@ static int rtp_data_transcoder(struct mgcp_rtp_end *end, struct mgcp_endpoint *_ } end->packets += 1; - return send_to(_endp, dest, proto == PROTO_RTP, &addr, &buf[0], rc); + return mgcp_send(_endp, dest, proto == PROTO_RTP, &addr, &buf[0], rc); } static int rtp_data_trans_net(struct osmo_fd *fd, unsigned int what) @@ -502,7 +550,7 @@ static int rtp_data_trans_net(struct osmo_fd *fd, unsigned int what) struct mgcp_endpoint *endp; endp = (struct mgcp_endpoint *) fd->data; - return rtp_data_transcoder(&endp->trans_net, endp, DEST_NETWORK, fd); + return rtp_data_transcoder(&endp->trans_net, endp, MGCP_DEST_NET, fd); } static int rtp_data_trans_bts(struct osmo_fd *fd, unsigned int what) @@ -510,10 +558,10 @@ static int rtp_data_trans_bts(struct osmo_fd *fd, unsigned int what) struct mgcp_endpoint *endp; endp = (struct mgcp_endpoint *) fd->data; - return rtp_data_transcoder(&endp->trans_bts, endp, DEST_BTS, fd); + return rtp_data_transcoder(&endp->trans_bts, endp, MGCP_DEST_BTS, fd); } -static int 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; @@ -549,13 +597,13 @@ static int set_ip_tos(int fd, int tos) static int bind_rtp(struct mgcp_config *cfg, struct mgcp_rtp_end *rtp_end, int endpno) { - if (create_bind(cfg->source_addr, &rtp_end->rtp, rtp_end->local_port) != 0) { + if (mgcp_create_bind(cfg->source_addr, &rtp_end->rtp, rtp_end->local_port) != 0) { LOGP(DMGCP, LOGL_ERROR, "Failed to create RTP port: %s:%d on 0x%x\n", cfg->source_addr, rtp_end->local_port, endpno); goto cleanup0; } - if (create_bind(cfg->source_addr, &rtp_end->rtcp, rtp_end->local_port + 1) != 0) { + if (mgcp_create_bind(cfg->source_addr, &rtp_end->rtcp, rtp_end->local_port + 1) != 0) { LOGP(DMGCP, LOGL_ERROR, "Failed to create RTCP port: %s:%d on 0x%x\n", cfg->source_addr, rtp_end->local_port + 1, endpno); goto cleanup1; diff --git a/openbsc/src/libmgcp/mgcp_protocol.c b/openbsc/src/libmgcp/mgcp_protocol.c index d38bbf3c6..2bd9d7390 100644 --- a/openbsc/src/libmgcp/mgcp_protocol.c +++ b/openbsc/src/libmgcp/mgcp_protocol.c @@ -500,7 +500,7 @@ static int allocate_ports(struct mgcp_endpoint *endp) } /* remember that we have set up transcoding */ - endp->is_transcoded = 1; + endp->type = MGCP_RTP_TRANSCODED; } return 0; @@ -588,6 +588,23 @@ static struct msgb *handle_create_con(struct mgcp_parse_data *p) if (endp->ci == CI_UNUSED) goto error2; + /* If osmux is enabled, initialize. RTP SSRC is allocated per + * circuit ID. We cannot use endp->ci since two BSCs may select + * the same, so divide the RTP SSRC space (2^32) by the 256 possible + * circuit IDs, then randomly select one value from that window. + * + * This is the probability of having a clash: + * 1/16777216 = 0.00000005960464477539 + */ + if (tcfg->cfg->osmux) { + static uint32_t rtp_ssrc_winlen = UINT32_MAX / 256; + + srand(time(NULL)); + osmux_xfrm_output_init(&endp->osmux.out, + (endp->ci * rtp_ssrc_winlen) + + (random() % rtp_ssrc_winlen)); + } + endp->allocated = 1; endp->bts_end.payload_type = tcfg->audio_payload; endp->bts_end.fmtp_extra = talloc_strdup(tcfg->endpoints, @@ -1014,7 +1031,6 @@ void mgcp_free_endp(struct mgcp_endpoint *endp) mgcp_rtp_end_reset(&endp->net_end); mgcp_rtp_end_reset(&endp->trans_net); mgcp_rtp_end_reset(&endp->trans_bts); - endp->is_transcoded = 0; memset(&endp->net_state, 0, sizeof(endp->net_state)); memset(&endp->bts_state, 0, sizeof(endp->bts_state)); @@ -1118,7 +1134,7 @@ static void create_transcoder(struct mgcp_endpoint *endp) int in_endp = ENDPOINT_NUMBER(endp); int out_endp = endp_back_channel(in_endp); - if (!endp->is_transcoded) + if (endp->type != MGCP_RTP_TRANSCODED) return; send_msg(endp, in_endp, endp->trans_bts.local_port, "CRCX", "sendrecv"); @@ -1140,7 +1156,7 @@ static void delete_transcoder(struct mgcp_endpoint *endp) int in_endp = ENDPOINT_NUMBER(endp); int out_endp = endp_back_channel(in_endp); - if (!endp->is_transcoded) + if (endp->type != MGCP_RTP_TRANSCODED) return; send_dlcx(endp, in_endp); diff --git a/openbsc/src/libmgcp/mgcp_vty.c b/openbsc/src/libmgcp/mgcp_vty.c index 69984ee92..948dd078b 100644 --- a/openbsc/src/libmgcp/mgcp_vty.c +++ b/openbsc/src/libmgcp/mgcp_vty.c @@ -773,6 +773,46 @@ DEFUN(reset_all_endp, reset_all_endp_cmd, return CMD_SUCCESS; } +DEFUN(cfg_mgcp_osmux, + cfg_mgcp_osmux_cmd, + "osmux (on|off)", + "Set on/off osmux translator\n") +{ + if (strcmp(argv[0], "on") == 0) + g_cfg->osmux = 1; + else if (strcmp(argv[0], "off") == 0) + g_cfg->osmux = 0; + else { + vty_out(vty, "Only on/off allowed for osmux option.%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_mgcp_osmux_batch_factor, + cfg_mgcp_osmux_batch_factor_cmd, + "osmux batch-factor <1-4>", + "Set osmux batch factor\n") +{ + char *end = NULL; + uint64_t ret; + + ret = strtoull(argv[0], &end, 10); + if (ret == UINT64_MAX) { + vty_out(vty, "%s out of valid batch factor range <1-4>.%s", + argv[0], VTY_NEWLINE); + return CMD_WARNING; + } else if (*end) { + vty_out(vty, "%s cannot be converted to number.%s", + argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + g_cfg->osmux_batch = ret; + + return CMD_SUCCESS; +} int mgcp_vty_init(void) { @@ -817,6 +857,8 @@ int mgcp_vty_init(void) install_element(MGCP_NODE, &cfg_mgcp_omit_rtcp_cmd); install_element(MGCP_NODE, &cfg_mgcp_no_omit_rtcp_cmd); install_element(MGCP_NODE, &cfg_mgcp_sdp_fmtp_extra_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); @@ -893,6 +935,15 @@ static int allocate_trunk(struct mgcp_trunk_config *trunk) } endp->trans_bts.local_alloc = PORT_ALLOC_STATIC; } + if (cfg->osmux) { + /* If there is any call-agent address set, we assume + * this mgcp code runs from BSC-NAT. + */ + if (cfg->call_agent_addr) + endp->type = MGCP_OSMUX_BSC_NAT; + else + endp->type = MGCP_OSMUX_BSC; + } } return 0; @@ -918,6 +969,9 @@ int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg) fprintf(stderr, "You need to specify a bind address.\n"); return -1; } + /* If no batch factor specified for OSMUX, default to 4 messages */ + if (g_cfg->osmux && !g_cfg->osmux_batch) + g_cfg->osmux_batch = 4; /* initialize the last ports */ g_cfg->last_bts_port = rtp_calculate_port(0, g_cfg->bts_ports.base_port); diff --git a/openbsc/src/libmgcp/osmux.c b/openbsc/src/libmgcp/osmux.c new file mode 100644 index 000000000..5469b52de --- /dev/null +++ b/openbsc/src/libmgcp/osmux.c @@ -0,0 +1,362 @@ +/* + * (C) 2012-2013 by Pablo Neira Ayuso + * (C) 2012-2013 by On Waves ehf + * + * 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. + */ + +#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 == NULL) + return NULL; + + h->in = talloc_zero(osmux, struct osmux_in_handle); + if (h->in == NULL) { + 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 == NULL) + 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 == NULL) { + 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, *endp = 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 0; + } + + if (tmp->ci == cid && + memcmp(this, from_addr, sizeof(struct in_addr)) == 0) { + endp = tmp; + LOGP(DMGCP, LOGL_NOTICE, "found endpoint with cid=%d\n!", + cid); + break; + } + } + + return endp; +} + +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); +} + +int read_bsc_cb(struct osmo_fd *ofd, unsigned int what) +{ + struct msgb *msg; + struct osmux_hdr *osmuxh; + struct llist_head list; + struct sockaddr_in addr; + socklen_t slen = sizeof(addr); + struct mgcp_config *cfg = ofd->data; + char buf[4096]; + int ret; + + msg = msgb_alloc(4096, "OSMUX from BSC NAT"); + if (msg == NULL) { + LOGP(DMGCP, LOGL_ERROR, "cannot allocate message\n"); + return -1; + } + 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 -1; + } + msgb_put(msg, ret); + + /* not any further processing dummy messages */ + if (msg->data[0] == 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; + + /* Lookup for the endpoint that corresponds to this CI */ + endp = endpoint_lookup(cfg, osmuxh->circuit_id, + &addr.sin_addr, MGCP_DEST_NET); + if (endp == NULL) { + 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 read_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; + socklen_t slen = sizeof(addr); + struct mgcp_config *cfg = ofd->data; + char buf[4096]; + int ret; + + msg = msgb_alloc(4096, "OSMUX from BSC"); + if (msg == NULL) { + LOGP(DMGCP, LOGL_ERROR, "cannot allocate message\n"); + return -1; + } + 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 -1; + } + msgb_put(msg, ret); + + /* not any further processing dummy messages */ + if (msg->data[0] == 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; + + /* Lookup for the endpoint that corresponds to this CI */ + endp = endpoint_lookup(cfg, osmuxh->circuit_id, + &addr.sin_addr, MGCP_DEST_BTS); + if (endp == NULL) { + 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 = read_bsc_cb; + break; + case OSMUX_ROLE_BSC_NAT: + osmux_fd.cb = read_bsc_nat_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; + } + + return 1; +} + +int osmux_change_cb(struct mgcp_trunk_config *cfg, int endpoint, int state) +{ + static char buf[] = { DUMMY_LOAD }; + struct mgcp_endpoint *endp = &cfg->endpoints[endpoint]; + + if (state != MGCP_ENDP_MDCX) + return 0; + + LOGP(DMGCP, LOGL_DEBUG, "sending OSMUX dummy load to %s\n", + inet_ntoa(endp->net_end.addr)); + + return 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_mgcp/mgcp_main.c b/openbsc/src/osmo-bsc_mgcp/mgcp_main.c index 540f7602b..131895809 100644 --- a/openbsc/src/osmo-bsc_mgcp/mgcp_main.c +++ b/openbsc/src/osmo-bsc_mgcp/mgcp_main.c @@ -47,6 +47,8 @@ #include +#include + #include "../../bscconfig.h" /* this is here for the vty... it will never be called */ @@ -233,7 +235,12 @@ int main(int argc, char **argv) /* set some callbacks */ cfg->reset_cb = mgcp_rsip_cb; - cfg->change_cb = mgcp_change_cb; + + /* Osmux needs a custom function to push holes in the firewall */ + if (!cfg->osmux) + cfg->change_cb = mgcp_change_cb; + else + cfg->change_cb = osmux_change_cb; /* we need to bind a socket */ if (rc == 0) { @@ -283,6 +290,14 @@ int main(int argc, char **argv) LOGP(DMGCP, LOGL_NOTICE, "Configured for MGCP.\n"); } + if (cfg->osmux) { + if (!osmux_init(OSMUX_ROLE_BSC, cfg)) { + LOGP(DMGCP, LOGL_ERROR, "Cannot init OSMUX\n"); + return -1; + } + LOGP(DMGCP, LOGL_NOTICE, "OSMUX has been ENABLED.\n"); + } + /* initialisation */ srand(time(NULL)); diff --git a/openbsc/src/osmo-bsc_nat/Makefile.am b/openbsc/src/osmo-bsc_nat/Makefile.am index e2ba551a0..008f8c681 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_nat.c b/openbsc/src/osmo-bsc_nat/bsc_nat.c index 27ac74766..be8d56af2 100644 --- a/openbsc/src/osmo-bsc_nat/bsc_nat.c +++ b/openbsc/src/osmo-bsc_nat/bsc_nat.c @@ -62,6 +62,8 @@ #include +#include + #include "../../bscconfig.h" #define SCCP_CLOSE_TIME 20 @@ -1524,6 +1526,14 @@ int main(int argc, char **argv) return -3; } + if (nat->mgcp_cfg->osmux) { + if (!osmux_init(OSMUX_ROLE_BSC_NAT, nat->mgcp_cfg)) { + LOGP(DMGCP, LOGL_ERROR, "Cannot init OSMUX\n"); + return -1; + } + LOGP(DMGCP, LOGL_NOTICE, "OSMUX has been ENABLED.\n"); + } + /* over rule the VTY config */ if (msc_ip) bsc_nat_set_msc_ip(nat, msc_ip); diff --git a/openbsc/tests/bsc-nat/Makefile.am b/openbsc/tests/bsc-nat/Makefile.am index 084767829..4fdbe1a4b 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 @@ -20,4 +20,4 @@ bsc_nat_test_LDADD = $(top_builddir)/src/libbsc/libbsc.a \ $(top_srcdir)/src/libcommon/libcommon.a \ $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) -lrt \ $(LIBOSMOSCCP_LIBS) $(LIBOSMOVTY_LIBS) \ - $(LIBOSMOABIS_LIBS) + $(LIBOSMOABIS_LIBS) $(LIBOSMONETIF_LIBS) diff --git a/openbsc/tests/mgcp/Makefile.am b/openbsc/tests/mgcp/Makefile.am index 8c365b416..5f515f1d1 100644 --- a/openbsc/tests/mgcp/Makefile.am +++ b/openbsc/tests/mgcp/Makefile.am @@ -11,4 +11,5 @@ mgcp_test_SOURCES = mgcp_test.c mgcp_test_LDADD = $(top_builddir)/src/libbsc/libbsc.a \ $(top_builddir)/src/libmgcp/libmgcp.a \ $(top_builddir)/src/libcommon/libcommon.a \ - $(LIBOSMOCORE_LIBS) -lrt $(LIBOSMOSCCP_LIBS) $(LIBOSMOVTY_LIBS) + $(LIBOSMOCORE_LIBS) -lrt $(LIBOSMOSCCP_LIBS) \ + $(LIBOSMOVTY_LIBS) $(LIBOSMONETIF_LIBS) -- cgit v1.2.3