From b769f3ce0bc1bf88f907856182f9fc6f871bbfef Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Wed, 27 Aug 2014 17:02:52 +0200 Subject: osmux: add osmux circuit ID management and resolve NAT problems This patch includes several osmux fixes that are interdependent: 1) This adds Osmux circuit ID, this is allocated from the bsc-nat. This announces the circuit ID in the CRCX MGCP message. This aims to resolve the lack of uniqueness due to the use of endp->ci, which is local to the bsc. This ID is notified via X-Osmux: NUM where NUM is the osmux circuit ID. 2) The dummy load routines are now used to setup osmux both in bsc and bsc-nat to resolve source port NAT issues as suggested by Holger. The source port that is used from the bsc is not known until the first voice message is sent to the bsc-nat, therefore enabling osmux from the MGCP plane breaks when a different source port is used. 3) Add refcnt to struct osmux_handle, several endpoints can be using the same input RTP osmux handle to perform the batching. Remove it from the osmux handle list once nobody is using it anymore to clean it up. 4) Add a simple Osmux state-machine with three states. The initial state is disabled, then if the bsc-nat requests Osmux, both sides enters activating. The final enabled state is reached once the bsc-nat sees the dummy load message that tells what source port is used by the bsc. 5) The osmux input handle (which transforms RTP messages to one Osmux batch) is now permanently attached to the endpoint when Osmux is set up from the dummy load path, so we skip a lookup for each message. This simplifies osmux_xfrm_to_osmux(). After this patch, the workflow to setup Osmux is the following: bsc bsc-nat | | |<------ CRCX ----------| | X-Osmux: 3 | (where 3 is the Osmux circuit ID | | that the bsc-nat has allocated) |------- resp --------->| | X-Osmux: 3 | (the bsc confirm that it can | | use Osmux). . . | | setup osmux |----- dummy load ----->| setup osmux | Osmux CID: 3 | In two steps: 1st) Allocate the Osmux Circuit ID (CID): The bsc-nat allocates an unique Osmux CID that is notified to the bsc through the 'X-Osmux:' extension. The bsc-nat annotates this circuit ID in the endpoint object. The bsc replies back with the 'X-Osmux:' to confirm that it agrees to use Osmux. If the bsc doesn't want to use Osmux, it doesn't include the extension so the bsc-nat knows that it has to use to RTP. 2nd) The dummy load is used to convey the Osmux CID. This needs to happen at this stage since the bsc-nat needs to know what source port the bsc uses to get this working since the bsc may use a different source port due to NAT. Unfortunately, this can't be done from the MGCP signal plane since the real source port is not known that the bsc uses is not known. This patch also reverts the MDCX handling until it is clear that we need this special handling for this case. --- openbsc/src/osmo-bsc_nat/bsc_mgcp_utils.c | 96 ++++++++++++++++++++++++------- 1 file changed, 75 insertions(+), 21 deletions(-) (limited to 'openbsc/src/osmo-bsc_nat/bsc_mgcp_utils.c') 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 #include #include +#include + #include #include @@ -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)); -- cgit v1.2.3