aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--openbsc/include/openbsc/mgcp_internal.h7
-rw-r--r--openbsc/include/openbsc/osmux.h13
-rw-r--r--openbsc/src/libmgcp/mgcp_protocol.c83
-rw-r--r--openbsc/src/libmgcp/osmux.c222
-rw-r--r--openbsc/src/osmo-bsc_nat/bsc_mgcp_utils.c96
5 files changed, 285 insertions, 136 deletions
diff --git a/openbsc/include/openbsc/mgcp_internal.h b/openbsc/include/openbsc/mgcp_internal.h
index 2236b2a9b..3d308835e 100644
--- a/openbsc/include/openbsc/mgcp_internal.h
+++ b/openbsc/include/openbsc/mgcp_internal.h
@@ -174,7 +174,12 @@ struct mgcp_endpoint {
struct mgcp_rtp_tap taps[MGCP_TAP_COUNT];
struct {
- int enable;
+ /* Osmux state: disabled, activating, active */
+ enum osmux_state state;
+ /* Allocated Osmux circuit ID for this endpoint */
+ uint8_t cid;
+ /* handle to batch messages */
+ struct osmux_in_handle *in;
/* handle to unbatch messages */
struct osmux_out_handle out;
} osmux;
diff --git a/openbsc/include/openbsc/osmux.h b/openbsc/include/openbsc/osmux.h
index 33456b76d..f4cb17abd 100644
--- a/openbsc/include/openbsc/osmux.h
+++ b/openbsc/include/openbsc/osmux.h
@@ -9,11 +9,22 @@ enum {
};
int osmux_init(int role, struct mgcp_config *cfg);
-int osmux_enable_endpoint(struct mgcp_endpoint *endp, int role);
+int osmux_enable_endpoint(struct mgcp_endpoint *endp, int role,
+ struct in_addr *addr, uint16_t port);
+void osmux_disable_endpoint(struct mgcp_endpoint *endp);
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);
+int osmux_get_cid(void);
+void osmux_put_cid(uint8_t osmux_cid);
+
+enum osmux_state {
+ OSMUX_STATE_DISABLED = 0,
+ OSMUX_STATE_ACTIVATING,
+ OSMUX_STATE_ENABLED,
+};
+
#endif
diff --git a/openbsc/src/libmgcp/mgcp_protocol.c b/openbsc/src/libmgcp/mgcp_protocol.c
index 0681c1038..db8354abf 100644
--- a/openbsc/src/libmgcp/mgcp_protocol.c
+++ b/openbsc/src/libmgcp/mgcp_protocol.c
@@ -320,16 +320,18 @@ static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp,
char sdp_record[4096];
int len;
int nchars;
+ char osmux_extension[strlen("\nX-Osmux: 255") + 1];
if (!addr)
addr = endp->cfg->source_addr;
- len = snprintf(sdp_record, sizeof(sdp_record),
- "I: %u%s\n\n",
- endp->ci,
- endp->cfg->osmux && endp->osmux.enable ?
- "\nX-Osmux: On" : "");
+ if (endp->osmux.state == OSMUX_STATE_ACTIVATING)
+ sprintf(osmux_extension, "\nX-Osmux: %u", endp->osmux.cid);
+ else
+ osmux_extension[0] = '\0';
+ len = snprintf(sdp_record, sizeof(sdp_record),
+ "I: %u%s\n\n", endp->ci, osmux_extension);
if (len < 0)
return NULL;
@@ -347,7 +349,7 @@ static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp,
static void send_dummy(struct mgcp_endpoint *endp)
{
- if (endp->osmux.enable)
+ if (endp->osmux.state != OSMUX_STATE_DISABLED)
osmux_send_dummy(endp);
else
mgcp_send_dummy(endp);
@@ -879,7 +881,22 @@ uint32_t mgcp_rtp_packet_duration(struct mgcp_endpoint *endp,
return rtp->rate * f * rtp->frame_duration_num / rtp->frame_duration_den;
}
-static int mgcp_osmux_setup(struct mgcp_endpoint *endp)
+static int mgcp_parse_osmux_cid(const char *line)
+{
+ uint32_t osmux_cid;
+
+ sscanf(line + 2, "Osmux: %u", &osmux_cid);
+ if (osmux_cid > OSMUX_CID_MAX) {
+ LOGP(DMGCP, LOGL_ERROR, "Osmux ID too large: %u > %u\n",
+ osmux_cid, OSMUX_CID_MAX);
+ return -1;
+ }
+ LOGP(DMGCP, LOGL_DEBUG, "bsc-nat offered Osmux CID %u\n", osmux_cid);
+
+ return osmux_cid;
+}
+
+static int mgcp_osmux_setup(struct mgcp_endpoint *endp, const char *line)
{
if (!endp->cfg->osmux_init) {
if (osmux_init(OSMUX_ROLE_BSC, endp->cfg) < 0) {
@@ -889,12 +906,7 @@ static int mgcp_osmux_setup(struct mgcp_endpoint *endp)
LOGP(DMGCP, LOGL_NOTICE, "OSMUX socket has been set up\n");
}
- if (osmux_enable_endpoint(endp, OSMUX_ROLE_BSC) < 0) {
- LOGP(DMGCP, LOGL_ERROR,
- "Could not activate Osmux in endpoint %d\n",
- ENDPOINT_NUMBER(endp));
- }
- return 0;
+ return mgcp_parse_osmux_cid(line);
}
static struct msgb *handle_create_con(struct mgcp_parse_data *p)
@@ -907,7 +919,7 @@ static struct msgb *handle_create_con(struct mgcp_parse_data *p)
const char *callid = NULL;
const char *mode = NULL;
char *line;
- int have_sdp = 0;
+ int have_sdp = 0, osmux_cid = -1;
if (p->found != 0)
return create_err_response(NULL, 510, "CRCX", p->trans);
@@ -928,8 +940,14 @@ static struct msgb *handle_create_con(struct mgcp_parse_data *p)
mode = (const char *) line + 3;
break;
case 'X':
- if (strcmp("Osmux: on", line + 2) == 0)
- mgcp_osmux_setup(endp);
+ /* Osmux is not enabled in this bsc, ignore it so the
+ * bsc-nat knows that we don't want to use Osmux.
+ */
+ if (!p->endp->cfg->osmux)
+ break;
+
+ if (strncmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0)
+ osmux_cid = mgcp_osmux_setup(endp, line);
break;
case '\0':
have_sdp = 1;
@@ -993,6 +1011,15 @@ mgcp_header_done:
if (endp->ci == CI_UNUSED)
goto error2;
+ /* Annotate Osmux circuit ID and set it to activating state until this
+ * is fully set up from the dummy load.
+ */
+ endp->osmux.state = OSMUX_STATE_DISABLED;
+ if (osmux_cid >= 0) {
+ endp->osmux.cid = osmux_cid;
+ endp->osmux.state = OSMUX_STATE_ACTIVATING;
+ }
+
endp->allocated = 1;
/* set up RTP media parameters */
@@ -1057,7 +1084,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, osmux = 0;
+ int silent = 0;
int have_sdp = 0;
char *line;
const char *local_options = NULL;
@@ -1096,10 +1123,6 @@ 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;
@@ -1118,21 +1141,6 @@ 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);
@@ -1528,6 +1536,9 @@ void mgcp_release_endp(struct mgcp_endpoint *endp)
endp->conn_mode = endp->orig_mode = MGCP_CONN_NONE;
+ if (endp->osmux.state == OSMUX_STATE_ENABLED)
+ osmux_disable_endpoint(endp);
+
memset(&endp->taps, 0, sizeof(endp->taps));
}
diff --git a/openbsc/src/libmgcp/osmux.c b/openbsc/src/libmgcp/osmux.c
index 1370c9f21..0a58a2edb 100644
--- a/openbsc/src/libmgcp/osmux.c
+++ b/openbsc/src/libmgcp/osmux.c
@@ -27,7 +27,6 @@
static struct osmo_fd osmux_fd;
-/* TODO: expire old handles.. */
static LLIST_HEAD(osmux_handle_list);
struct osmux_handle {
@@ -35,6 +34,7 @@ struct osmux_handle {
struct osmux_in_handle *in;
struct in_addr rem_addr;
int rem_port;
+ int refcnt;
};
static void *osmux;
@@ -70,6 +70,7 @@ osmux_handle_find_get(struct in_addr *addr, int rem_port)
LOGP(DMGCP, LOGL_DEBUG, "using existing OSMUX handle "
"for addr=%s:%d\n",
inet_ntoa(*addr), ntohs(rem_port));
+ h->refcnt++;
return h;
}
}
@@ -77,6 +78,27 @@ osmux_handle_find_get(struct in_addr *addr, int rem_port)
return NULL;
}
+static void osmux_handle_put(struct osmux_in_handle *in)
+{
+ struct osmux_handle *h;
+
+ /* Lookup for existing OSMUX handle for this destination address. */
+ llist_for_each_entry(h, &osmux_handle_list, head) {
+ if (h->in == in) {
+ if (--h->refcnt == 0) {
+ LOGP(DMGCP, LOGL_DEBUG,
+ "Releasing unused osmux handle for %s:%d\n",
+ inet_ntoa(h->rem_addr),
+ ntohs(h->rem_port));
+ llist_del(&h->head);
+ talloc_free(h);
+ }
+ return;
+ }
+ }
+ LOGP(DMGCP, LOGL_ERROR, "cannot find Osmux input handle %p\n", in);
+}
+
static struct osmux_handle *
osmux_handle_alloc(struct mgcp_config *cfg, struct in_addr *addr, int rem_port)
{
@@ -87,6 +109,7 @@ osmux_handle_alloc(struct mgcp_config *cfg, struct in_addr *addr, int rem_port)
return NULL;
h->rem_addr = *addr;
h->rem_port = rem_port;
+ h->refcnt++;
h->in = talloc_zero(h, struct osmux_in_handle);
if (!h->in) {
@@ -126,10 +149,8 @@ osmux_handle_lookup(struct mgcp_config *cfg, struct in_addr *addr, int rem_port)
int osmux_xfrm_to_osmux(int type, char *buf, int rc, struct mgcp_endpoint *endp)
{
- int ret, port;
+ int ret;
struct msgb *msg;
- struct in_addr *addr;
- struct osmux_in_handle *in;
msg = msgb_alloc(4096, "RTP");
if (!msg)
@@ -138,43 +159,12 @@ int osmux_xfrm_to_osmux(int type, char *buf, int rc, struct mgcp_endpoint *endp)
memcpy(msg->data, buf, rc);
msgb_put(msg, rc);
- switch(type) {
- case MGCP_DEST_NET:
- addr = &endp->net_end.addr;
- port = htons(OSMUX_PORT);
- break;
- case MGCP_DEST_BTS:
- addr = &endp->bts_end.addr;
- port = endp->bts_end.rtp_port;
- break;
- default:
- /* Should not ever happen */
- LOGP(DMGCP, LOGL_ERROR, "Bad type %d. Fix your code.\n", type);
- msgb_free(msg);
- return 0;
- }
+ LOGP(DMGCP, LOGL_DEBUG, "Osmux uses CID %u from endpoint=%d (active=%d)\n",
+ endp->osmux.cid, ENDPOINT_NUMBER(endp), endp->allocated);
- if (port == 0) {
- LOGP(DMGCP, LOGL_ERROR, "0x%x remote end not known yet.\n",
- ENDPOINT_NUMBER(endp));
- msgb_free(msg);
- return 0;
- }
-
- /* Lookup for osmux input handle that munches this RTP frame */
- in = osmux_handle_lookup(endp->cfg, addr, port);
- if (!in) {
- LOGP(DMGCP, LOGL_ERROR, "No osmux handle, aborting\n");
- msgb_free(msg);
- 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) {
+ while ((ret = osmux_xfrm_input(endp->osmux.in, msg, endp->osmux.cid)) > 0) {
/* batch full, build and deliver it */
- osmux_xfrm_input_deliver(in);
+ osmux_xfrm_input_deliver(endp->osmux.in);
}
return 0;
}
@@ -208,7 +198,7 @@ endpoint_lookup(struct mgcp_config *cfg, int cid,
return NULL;
}
- if ((tmp->ci & 0xFF) == cid && this->s_addr == from_addr->s_addr)
+ if (tmp->osmux.cid == cid && this->s_addr == from_addr->s_addr)
return tmp;
}
@@ -304,34 +294,51 @@ out:
return 0;
}
-/*
- * Try to figure out where it came from and enter the rtp_port
- */
-static int osmux_handle_dummy(struct mgcp_config *cfg,
- struct sockaddr_in *addr, struct msgb *msg)
+/* This is called from the bsc-nat */
+static int osmux_handle_dummy(struct mgcp_config *cfg, struct sockaddr_in *addr,
+ struct msgb *msg)
{
struct mgcp_endpoint *endp;
- uint32_t ci;
+ uint8_t osmux_cid;
+
+ if (msg->len < 1 + sizeof(osmux_cid)) {
+ LOGP(DMGCP, LOGL_ERROR,
+ "Discarding truncated Osmux dummy load\n");
+ goto out;
+ }
- if (msg->len < 1 + sizeof(ci))
+ LOGP(DMGCP, LOGL_DEBUG, "Received Osmux dummy load from %s\n",
+ inet_ntoa(addr->sin_addr));
+
+ if (!cfg->osmux) {
+ LOGP(DMGCP, LOGL_ERROR,
+ "bsc wants to use Osmux but bsc-nat did not request it\n");
goto out;
+ }
- /* extract the CI from the dummy message */
- memcpy(&ci, &msg->data[1], sizeof(ci));
- ci = ntohl(ci);
+ /* extract the osmux CID from the dummy message */
+ memcpy(&osmux_cid, &msg->data[1], sizeof(osmux_cid));
- endp = endpoint_lookup(cfg, ci & 0xff, &addr->sin_addr, MGCP_DEST_BTS);
+ endp = endpoint_lookup(cfg, osmux_cid, &addr->sin_addr, MGCP_DEST_BTS);
if (!endp) {
- LOGP(DMGCP, LOGL_ERROR, "Can not find CI=%d\n", ci & 0xff);
+ LOGP(DMGCP, LOGL_ERROR,
+ "Cannot find endpoint for Osmux CID %d\n", osmux_cid);
goto out;
}
- if (endp->bts_end.rtp_port == 0) {
- endp->bts_end.rtp_port = addr->sin_port;
- LOGP(DMGCP, LOGL_NOTICE, "0x%x found BTS on endpoint %s:%d\n",
- ENDPOINT_NUMBER(endp),
- inet_ntoa(addr->sin_addr), htons(addr->sin_port));
+ if (endp->osmux.state == OSMUX_STATE_ENABLED)
+ goto out;
+
+ if (osmux_enable_endpoint(endp, OSMUX_ROLE_BSC_NAT,
+ &addr->sin_addr, addr->sin_port) < 0 ){
+ LOGP(DMGCP, LOGL_ERROR,
+ "Could not update osmux in endpoint %d\n",
+ ENDPOINT_NUMBER(endp));
}
+
+ LOGP(DMGCP, LOGL_INFO, "Enabling osmux in endpoint %d for %s:%u\n",
+ ENDPOINT_NUMBER(endp), inet_ntoa(addr->sin_addr),
+ ntohs(addr->sin_port));
out:
msgb_free(msg);
return 0;
@@ -373,13 +380,6 @@ int osmux_read_from_bsc_cb(struct osmo_fd *ofd, unsigned int what)
goto out;
}
- if (endp->bts_end.rtp_port == 0) {
- endp->bts_end.rtp_port = addr.sin_port;
- LOGP(DMGCP, LOGL_NOTICE, "0x%x found BTS on endpoint %s:%d\n",
- ENDPOINT_NUMBER(endp),
- inet_ntoa(addr.sin_addr), htons(addr.sin_port));
- }
-
LOGP(DMGCP, LOGL_DEBUG,
"sending extracted RTP from OSMUX to MSC via endpoint=%u "
"(allocated=%d)\n", ENDPOINT_NUMBER(endp), endp->allocated);
@@ -426,23 +426,36 @@ int osmux_init(int role, struct mgcp_config *cfg)
return 0;
}
-int osmux_enable_endpoint(struct mgcp_endpoint *endp, int role)
+int osmux_enable_endpoint(struct mgcp_endpoint *endp, int role,
+ struct in_addr *addr, uint16_t port)
{
/* 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
+ * allocated based on the circuit ID (endp->osmux.cid), 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->osmux.state == OSMUX_STATE_DISABLED) {
+ LOGP(DMGCP, LOGL_ERROR, "Endpoint %u didn't request Osmux\n",
+ ENDPOINT_NUMBER(endp));
+ return -1;
+ }
+
osmux_xfrm_output_init(&endp->osmux.out,
- (endp->ci * rtp_ssrc_winlen) +
+ (endp->osmux.cid * rtp_ssrc_winlen) +
(random() % rtp_ssrc_winlen));
+ endp->osmux.in = osmux_handle_lookup(endp->cfg, addr, port);
+ if (!endp->osmux.in) {
+ LOGP(DMGCP, LOGL_ERROR, "Cannot allocate input osmux handle\n");
+ return -1;
+ }
+
switch (endp->cfg->role) {
case MGCP_BSC_NAT:
endp->type = MGCP_OSMUX_BSC_NAT;
@@ -451,27 +464,82 @@ int osmux_enable_endpoint(struct mgcp_endpoint *endp, int role)
endp->type = MGCP_OSMUX_BSC;
break;
}
- endp->osmux.enable = 1;
+ endp->osmux.state = OSMUX_STATE_ENABLED;
return 0;
}
+void osmux_disable_endpoint(struct mgcp_endpoint *endp)
+{
+ LOGP(DMGCP, LOGL_INFO, "Releasing endpoint %u using Osmux CID %u\n",
+ ENDPOINT_NUMBER(endp), endp->osmux.cid);
+ endp->osmux.state = OSMUX_STATE_DISABLED;
+ endp->osmux.cid = -1;
+ osmux_handle_put(endp->osmux.in);
+}
+
/* 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)
{
- uint32_t ci_be;
- char buf[1 + sizeof(uint32_t)];
+ char buf[1 + sizeof(uint8_t)];
+ struct in_addr addr_unset = {};
- ci_be = htonl(endp->ci);
buf[0] = MGCP_DUMMY_LOAD;
- memcpy(&buf[1], &ci_be, sizeof(ci_be));
+ memcpy(&buf[1], &endp->osmux.cid, sizeof(endp->osmux.cid));
+
+ /* Wait until we have the connection information from MDCX */
+ if (memcmp(&endp->net_end.addr, &addr_unset, sizeof(addr_unset)) == 0)
+ return 0;
- LOGP(DMGCP, LOGL_DEBUG, "sending OSMUX dummy load to %s\n",
- inet_ntoa(endp->net_end.addr));
+ if (endp->osmux.state == OSMUX_STATE_ACTIVATING) {
+ if (osmux_enable_endpoint(endp, OSMUX_ROLE_BSC,
+ &endp->net_end.addr,
+ htons(OSMUX_PORT)) < 0) {
+ LOGP(DMGCP, LOGL_ERROR,
+ "Could not activate osmux in endpoint %d\n",
+ ENDPOINT_NUMBER(endp));
+ }
+ LOGP(DMGCP, LOGL_ERROR,
+ "Osmux CID %u for %s:%u is now enabled\n",
+ endp->osmux.cid, inet_ntoa(endp->net_end.addr),
+ OSMUX_PORT);
+ }
+ LOGP(DMGCP, LOGL_DEBUG,
+ "sending OSMUX dummy load to %s CID %u\n",
+ inet_ntoa(endp->net_end.addr), endp->osmux.cid);
return mgcp_udp_send(osmux_fd.fd, &endp->net_end.addr,
htons(OSMUX_PORT), buf, sizeof(buf));
}
+
+/* bsc-nat allocates/releases the Osmux circuit ID */
+static uint8_t osmux_cid_bitmap[16];
+
+int osmux_get_cid(void)
+{
+ int i, j;
+
+ for (i = 0; i < sizeof(osmux_cid_bitmap) / 8; i++) {
+ for (j = 0; j < 8; j++) {
+ if (osmux_cid_bitmap[i] & (1 << j))
+ continue;
+
+ osmux_cid_bitmap[i] |= (1 << j);
+ LOGP(DMGCP, LOGL_DEBUG,
+ "Allocating Osmux CID %u from pool\n", (i * 8) + j);
+ return (i * 8) + j;
+ }
+ }
+
+ LOGP(DMGCP, LOGL_ERROR, "All Osmux circuits are in use!\n");
+ return -1;
+}
+
+void osmux_put_cid(uint8_t osmux_cid)
+{
+ LOGP(DMGCP, LOGL_DEBUG, "Osmux CID %u is back to the pool\n", osmux_cid);
+ osmux_cid_bitmap[osmux_cid / 8] &= ~(1 << (osmux_cid % 8));
+}
diff --git a/openbsc/src/osmo-bsc_nat/bsc_mgcp_utils.c b/openbsc/src/osmo-bsc_nat/bsc_mgcp_utils.c
index 15c4767b8..606ac92df 100644
--- a/openbsc/src/osmo-bsc_nat/bsc_mgcp_utils.c
+++ b/openbsc/src/osmo-bsc_nat/bsc_mgcp_utils.c
@@ -50,6 +50,8 @@
#include <openbsc/ipaccess.h>
#include <openbsc/mgcp.h>
#include <openbsc/mgcp_internal.h>
+#include <openbsc/osmux.h>
+
#include <osmocom/ctrl/control_cmd.h>
#include <osmocom/sccp/sccp.h>
@@ -262,13 +264,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%s\r\n"
+ "MDCX 23 %x@mgw MGCP 1.0\r\n"
"Z: noanswer\r\n"
"\r\n"
"c=IN IP4 %s\r\n"
"m=audio %d RTP/AVP 255\r\n",
- port, bsc->cfg->osmux ? "\nX-Osmux: on" : "",
- bsc->nat->mgcp_cfg->source_addr,
+ port, bsc->nat->mgcp_cfg->source_addr,
endp->bts_end.local_port);
if (len < 0) {
LOGP(DMGCP, LOGL_ERROR, "snprintf for MDCX failed.\n");
@@ -505,6 +506,7 @@ static int bsc_mgcp_policy_cb(struct mgcp_trunk_config *tcfg, int endpoint, int
struct nat_sccp_connection *sccp;
struct mgcp_endpoint *mgcp_endp;
struct msgb *bsc_msg;
+ int osmux_cid = -1;
nat = tcfg->cfg->data;
bsc_endp = &nat->bsc_endpoints[endpoint];
@@ -541,11 +543,15 @@ static int bsc_mgcp_policy_cb(struct mgcp_trunk_config *tcfg, int endpoint, int
}
}
+ /* Allocate a Osmux circuit ID */
+ if (state == MGCP_ENDP_CRCX &&
+ nat->mgcp_cfg->osmux && sccp->bsc->cfg->osmux)
+ osmux_cid = osmux_get_cid();
+
/* we need to generate a new and patched message */
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->bts_end.local_port, osmux_cid,
&mgcp_endp->net_end.payload_type);
if (!bsc_msg) {
LOGP(DMGCP, LOGL_ERROR, "Failed to patch the msg.\n");
@@ -560,14 +566,14 @@ 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));
- }
+ /* Annotate the allocated Osmux CID until the bsc confirms that
+ * it agrees to use Osmux for this voice flow.
+ */
+ if (osmux_cid >= 0 &&
+ mgcp_endp->osmux.state != OSMUX_STATE_ENABLED) {
+ mgcp_endp->osmux.state = OSMUX_STATE_ACTIVATING;
+ mgcp_endp->osmux.cid = osmux_cid;
}
socklen_t len = sizeof(sock);
@@ -586,6 +592,11 @@ static int bsc_mgcp_policy_cb(struct mgcp_trunk_config *tcfg, int endpoint, int
/* we will free the endpoint now and send a DLCX to the BSC */
msgb_free(bsc_msg);
bsc_mgcp_dlcx(sccp);
+
+ /* libmgcp clears the MGCP endpoint for us */
+ if (mgcp_endp->osmux.state == OSMUX_STATE_ENABLED)
+ osmux_put_cid(mgcp_endp->osmux.cid);
+
return MGCP_POLICY_CONT;
} else {
bsc_write(sccp->bsc, bsc_msg, IPAC_PROTO_MGCP_OLD);
@@ -624,6 +635,42 @@ static void free_chan_downstream(struct mgcp_endpoint *endp, struct bsc_endpoint
mgcp_release_endp(endp);
}
+static void bsc_mgcp_osmux_confirm(struct mgcp_endpoint *endp, const char *str)
+{
+ unsigned int osmux_cid;
+ char *res;
+
+ res = strstr(str, "X-Osmux: ");
+ if (!res) {
+ LOGP(DMGCP, LOGL_INFO,
+ "BSC doesn't want to use Osmux, failing back to RTP\n");
+ goto err;
+ }
+
+ if (sscanf(res, "X-Osmux: %u", &osmux_cid) != 1) {
+ LOGP(DMGCP, LOGL_ERROR, "Failed to parse Osmux CID '%s'\n",
+ str);
+ goto err;
+ }
+
+ if (endp->osmux.cid != osmux_cid) {
+ LOGP(DMGCP, LOGL_INFO,
+ "BSC sent us wrong CID %u, we expected %u",
+ osmux_cid, endp->osmux.cid);
+ goto err;
+ }
+
+ LOGP(DMGCP, LOGL_NOTICE, "bsc accepted to use Osmux (cid=%u)\n",
+ osmux_cid);
+ return;
+err:
+ LOGP(DMGCP, LOGL_NOTICE, "bsc didn't accept to use Osmux (cid=%u)\n",
+ osmux_cid);
+ osmux_put_cid(endp->osmux.cid);
+ endp->osmux.cid = -1;
+ endp->osmux.state = OSMUX_STATE_DISABLED;
+}
+
/*
* We have received a msg from the BSC. We will see if we know
* this transaction and if it belongs to the BSC. Then we will
@@ -685,6 +732,9 @@ void bsc_mgcp_forward(struct bsc_connection *bsc, struct msgb *msg)
return;
}
+ if (endp->osmux.state == OSMUX_STATE_ACTIVATING)
+ bsc_mgcp_osmux_confirm(endp, (const char *) msg->l2h);
+
/* free some stuff */
talloc_free(bsc_endp->transaction_id);
bsc_endp->transaction_id = NULL;
@@ -697,8 +747,7 @@ 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->net_end.local_port, -1,
&endp->bts_end.payload_type);
if (!output) {
LOGP(DMGCP, LOGL_ERROR, "Failed to rewrite MGCP msg.\n");
@@ -738,11 +787,12 @@ 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 osmux)
+ int endp, int len, int cr, int osmux_cid)
{
int slen;
int ret;
char buf[40];
+ char osmux_extension[strlen("X-Osmux: 255")];
buf[0] = buf[39] = '\0';
ret = sscanf(tok, "%*s %s", buf);
@@ -752,15 +802,19 @@ static void patch_mgcp(struct msgb *output, const char *op, const char *tok,
return;
}
+ if (osmux_cid >= 0)
+ sprintf(osmux_extension, "\nX-Osmux: %u", osmux_cid);
+ else
+ osmux_extension[0] = '\0';
+
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");
+ op, buf, endp, osmux_extension, 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, int osmux,
+ const char *ip, int port, int osmux_cid,
int *payload_type)
{
static const char crcx_str[] = "CRCX ";
@@ -799,11 +853,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, osmux);
+ patch_mgcp(output, "CRCX", token, endpoint, len, cr, osmux_cid);
} else if (strncmp(dlcx_str, token, (sizeof dlcx_str) - 1) == 0) {
- patch_mgcp(output, "DLCX", token, endpoint, len, cr, 0);
+ patch_mgcp(output, "DLCX", token, endpoint, len, cr, -1);
} else if (strncmp(mdcx_str, token, (sizeof mdcx_str) - 1) == 0) {
- patch_mgcp(output, "MDCX", token, endpoint, len, cr, osmux);
+ patch_mgcp(output, "MDCX", token, endpoint, len, cr, -1);
} 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));