aboutsummaryrefslogtreecommitdiffstats
path: root/openbsc/src/libmgcp
diff options
context:
space:
mode:
authorPablo Neira Ayuso <pablo@soleta.eu>2014-08-27 17:02:52 +0200
committerPablo Neira Ayuso <pablo@soleta.eu>2014-08-28 12:08:29 +0200
commitb769f3ce0bc1bf88f907856182f9fc6f871bbfef (patch)
tree34286cb9da1c64806be22fc93990c7d7e1264724 /openbsc/src/libmgcp
parent8be171e88f2a64c29f93cf325945a7b5156b87ec (diff)
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.
Diffstat (limited to 'openbsc/src/libmgcp')
-rw-r--r--openbsc/src/libmgcp/mgcp_protocol.c83
-rw-r--r--openbsc/src/libmgcp/osmux.c222
2 files changed, 192 insertions, 113 deletions
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));
+}