diff options
-rw-r--r-- | openbsc/include/openbsc/mgcp_internal.h | 7 | ||||
-rw-r--r-- | openbsc/include/openbsc/osmux.h | 13 | ||||
-rw-r--r-- | openbsc/src/libmgcp/mgcp_protocol.c | 83 | ||||
-rw-r--r-- | openbsc/src/libmgcp/osmux.c | 222 | ||||
-rw-r--r-- | openbsc/src/osmo-bsc_nat/bsc_mgcp_utils.c | 96 |
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)); |