mgcp: permit wildcarded endpoint assignment (CRCX)

The mgcp protocol in general allows wildcarded endpoints on CRCX.
osmo-mgw does not support this feature yet.

- when the endpoint name contains a wildcard character, search
  a free endpoint automatically

- return the resulting endpoint name in the parameter section of
  the mgcp response

- add parsing support for the returned endpoint names

- Be more concious about the parameters that are returned with
  each response. Do not unnecessarily attach known parameters.
  Return the connection ID only on CRCX commands. Only return
  the endpoint ID on CRCX commands that are wildcarded.

Change-Id: Iebc95043569191b6f5fbc8fe266b13fcfcab2e48
related: OS#2631
This commit is contained in:
Philipp Maier
2018-01-15 14:00:28 +01:00
committed by Harald Welte
parent 9d25d7a2e6
commit 55295f7b07
6 changed files with 138 additions and 40 deletions

View File

@@ -260,6 +260,10 @@ struct mgcp_endpoint {
/* fields for re-transmission */
char *last_trans;
char *last_response;
/* Memorize if this endpoint was choosen by the MGW (wildcarded, true)
* or if the user has choosen the particular endpoint explicitly */
bool wildcarded_crcx;
};

View File

@@ -31,6 +31,7 @@ struct mgcp_response_head {
mgcp_trans_id_t trans_id;
const char *comment;
char conn_id[MGCP_CONN_ID_LENGTH];
char endpoint[MGCP_ENDPOINT_MAXLEN];
};
struct mgcp_response {

View File

@@ -146,8 +146,6 @@ static int mgcp_response_parse_head(struct mgcp_response *r, struct msgb *msg)
/* Mark the end of the comment */
*end = '\0';
r->body = end + 1;
if (r->body[0] == '\n')
r->body ++;
return 0;
response_parse_failure:
@@ -247,6 +245,10 @@ int mgcp_response_parse_params(struct mgcp_response *r)
OSMO_ASSERT(r->body);
char *data = mgcp_find_section_end(r->body);
/* Warning: This function performs a destructive parsing on r->body.
* Since this function is called at the very end of the persing
* process, destructive parsing is acceptable. */
if (!data) {
LOGP(DLMGCP, LOGL_ERROR,
"MGCP response: cannot find start of parameters\n");
@@ -282,21 +284,29 @@ int mgcp_response_parse_params(struct mgcp_response *r)
return 0;
}
/* Parse a line like "I: 0cedfd5a19542d197af9afe5231f1d61" */
static int mgcp_parse_conn_id(struct mgcp_response *r, const char *line)
/* Parse a line like "X: something" */
static int mgcp_parse_head_param(char *result, unsigned int result_len,
char label, const char *line)
{
char label_string[4];
/* Detect empty parameters */
if (strlen(line) < 4)
goto response_parse_failure;
if (memcmp("I: ", line, 3) != 0)
/* Check if the label matches */
snprintf(label_string, sizeof(label_string), "%c: ", label);
if (memcmp(label_string, line, 3) != 0)
goto response_parse_failure;
osmo_strlcpy(r->head.conn_id, line + 3, sizeof(r->head.conn_id));
/* Copy payload part of the string to destinations (the label string
* is always 3 chars long) */
osmo_strlcpy(result, line + 3, result_len);
return 0;
response_parse_failure:
LOGP(DLMGCP, LOGL_ERROR,
"Failed to parse MGCP response (connectionIdentifier)\n");
"Failed to parse MGCP response (parameter label: %c)\n", label);
return -EINVAL;
}
@@ -306,18 +316,37 @@ static int parse_head_params(struct mgcp_response *r)
char *line;
int rc = 0;
OSMO_ASSERT(r->body);
char *data = r->body;
char *data_end = mgcp_find_section_end(r->body);
char *data;
char *data_ptr;
char *data_end;
/* Protect SDP body, for_each_non_empty_line() will
* only parse until it hits \0 mark. */
/* Since this functions performs a destructive parsing, we create a
* local copy of the body data */
data = talloc_zero_size(NULL, strlen(r->body)+1);
OSMO_ASSERT(data);
data_ptr = data;
osmo_strlcpy(data, r->body, strlen(r->body));
/* If there is an SDP body attached, prevent for_each_non_empty_line()
* into running in there, we are not yet interested in the parameters
* stored there. */
data_end = mgcp_find_section_end(data);
if (data_end)
*data_end = '\0';
for_each_non_empty_line(line, data) {
for_each_non_empty_line(line, data_ptr) {
switch (line[0]) {
case 'Z':
rc = mgcp_parse_head_param(r->head.endpoint,
sizeof(r->head.endpoint),
'Z', line);
if (rc)
goto exit;
break;
case 'I':
rc = mgcp_parse_conn_id(r, line);
rc = mgcp_parse_head_param(r->head.conn_id,
sizeof(r->head.conn_id),
'I', line);
if (rc)
goto exit;
break;
@@ -327,10 +356,7 @@ static int parse_head_params(struct mgcp_response *r)
}
}
exit:
/* Restore original state */
if (data_end)
*data_end = '\n';
talloc_free(data);
return rc;
}

View File

@@ -181,6 +181,30 @@ static struct mgcp_endpoint *find_e1_endpoint(struct mgcp_config *cfg,
return &tcfg->endpoints[endp];
}
/* Find an endpoint that is not in use. Do this by going through the endpoint
* array, check the callid. A callid nullpointer indicates that the endpoint
* is free */
static struct mgcp_endpoint *find_free_endpoint(struct mgcp_endpoint *endpoints,
unsigned int number_endpoints)
{
struct mgcp_endpoint *endp;
unsigned int i;
for (i = 0; i < number_endpoints; i++) {
if (endpoints[i].callid == NULL) {
endp = &endpoints[i];
LOGP(DLMGCP, LOGL_DEBUG,
"endpoint:0x%x found free endpoint\n",
ENDPOINT_NUMBER(endp));
endp->wildcarded_crcx = true;
return endp;
}
}
LOGP(DLMGCP, LOGL_ERROR, "Not able to find a free endpoint");
return NULL;
}
/* Check if the domain name, which is supplied with the endpoint name
* matches the configuration. */
static int check_domain_name(struct mgcp_config *cfg, const char *mgcp)
@@ -213,6 +237,11 @@ static struct mgcp_endpoint *find_endpoint(struct mgcp_config *cfg,
if (strncmp(mgcp, "ds/e1", 5) == 0)
return find_e1_endpoint(cfg, mgcp);
if (strncmp(mgcp, "*", 1) == 0) {
return find_free_endpoint(cfg->trunk.endpoints,
cfg->trunk.number_endpoints);
}
gw = strtoul(mgcp, &endptr, 16);
if (gw < cfg->trunk.number_endpoints && endptr[0] == '@')
return &cfg->trunk.endpoints[gw];

View File

@@ -192,11 +192,32 @@ static struct msgb *create_err_response(struct mgcp_endpoint *endp,
return create_resp(endp, code, " FAIL", msg, trans, NULL, NULL);
}
/* Add MGCP parameters to a message buffer */
static int add_params(struct msgb *msg, const struct mgcp_endpoint *endp,
const struct mgcp_conn_rtp *conn)
{
int rc;
if (endp->wildcarded_crcx) {
rc = msgb_printf(msg, "Z: %u@%s\n", ENDPOINT_NUMBER(endp),
endp->cfg->domain);
if (rc < 0)
return -EINVAL;
}
rc = msgb_printf(msg, "I: %s\n", conn->conn->id);
if (rc < 0)
return -EINVAL;
return 0;
}
/* Format MGCP response string (with SDP attached) */
static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp,
struct mgcp_conn_rtp *conn,
const char *msg,
const char *trans_id)
const char *trans_id,
bool add_conn_params)
{
const char *addr = endp->cfg->local_ip;
struct msgb *sdp;
@@ -221,7 +242,14 @@ static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp,
osmux_extension[0] = '\0';
}
rc = msgb_printf(sdp, "I: %s%s\n\n", conn->conn->id, osmux_extension);
/* Attach optional connection parameters */
if (add_conn_params) {
rc = add_params(sdp, endp, conn);
if (rc < 0)
goto error;
}
rc = msgb_printf(sdp, "%s\n", osmux_extension);
if (rc < 0)
goto error;
@@ -648,7 +676,7 @@ mgcp_header_done:
LOGP(DLMGCP, LOGL_NOTICE,
"CRCX: endpoint:0x%x connection successfully created\n",
ENDPOINT_NUMBER(endp));
return create_response_with_sdp(endp, conn, "CRCX", p->trans);
return create_response_with_sdp(endp, conn, "CRCX", p->trans, true);
error2:
mgcp_release_endp(endp);
LOGP(DLMGCP, LOGL_NOTICE,
@@ -801,7 +829,7 @@ mgcp_header_done:
LOGP(DLMGCP, LOGL_NOTICE,
"MDCX: endpoint:0x%x connection successfully modified\n",
ENDPOINT_NUMBER(endp));
return create_response_with_sdp(endp, conn, "MDCX", p->trans);
return create_response_with_sdp(endp, conn, "MDCX", p->trans, false);
error3:
return create_err_response(endp, error_code, "MDCX", p->trans);
@@ -1196,6 +1224,7 @@ void mgcp_release_endp(struct mgcp_endpoint *endp)
endp->local_options.string = NULL;
talloc_free(endp->local_options.codec);
endp->local_options.codec = NULL;
endp->wildcarded_crcx = false;
}
static int send_agent(struct mgcp_config *cfg, const char *buf, int len)

View File

@@ -86,7 +86,6 @@ static void test_strline(void)
#define MDCX3_RET \
"200 18983215 OK\r\n" \
"I: %s\n" \
"\n" \
"v=0\r\n" \
"o=- %s 23 IN IP4 0.0.0.0\r\n" \
@@ -99,7 +98,6 @@ static void test_strline(void)
#define MDCX3A_RET \
"200 18983215 OK\r\n" \
"I: %s\n" \
"\n" \
"v=0\r\n" \
"o=- %s 23 IN IP4 0.0.0.0\r\n" \
@@ -112,7 +110,6 @@ static void test_strline(void)
#define MDCX3_FMTP_RET \
"200 18983215 OK\r\n" \
"I: %s\n" \
"\n" \
"v=0\r\n" \
"o=- %s 23 IN IP4 0.0.0.0\r\n" \
@@ -141,7 +138,6 @@ static void test_strline(void)
#define MDCX4_RET(Ident) \
"200 " Ident " OK\r\n" \
"I: %s\n" \
"\n" \
"v=0\r\n" \
"o=- %s 23 IN IP4 0.0.0.0\r\n" \
@@ -154,7 +150,6 @@ static void test_strline(void)
#define MDCX4_RO_RET(Ident) \
"200 " Ident " OK\r\n" \
"I: %s\n" \
"\n" \
"v=0\r\n" \
"o=- %s 23 IN IP4 0.0.0.0\r\n" \
@@ -561,25 +556,39 @@ static int get_conn_id_from_response(uint8_t *resp, char *conn_id,
{
char *conn_id_ptr;
int i;
bool got_conn_id = false;
/* First try to get the conn_id from the I: parameter */
conn_id_ptr = strstr((char *)resp, "I: ");
if (!conn_id_ptr)
return -EINVAL;
memset(conn_id, 0, conn_id_len);
memcpy(conn_id, conn_id_ptr + 3, 32);
for (i = 0; i < conn_id_len; i++) {
if (conn_id[i] == '\n' || conn_id[i] == '\r')
conn_id[i] = '\0';
if (conn_id_ptr) {
memset(conn_id, 0, conn_id_len);
memcpy(conn_id, conn_id_ptr + 3, 32);
got_conn_id = true;
} else {
/* Alternatively try to extract the conn_id from the o=- SDP
* parameter */
conn_id_ptr = strstr((char *)resp, "o=- ");
if(conn_id_ptr) {
memset(conn_id, 0, conn_id_len);
memcpy(conn_id, conn_id_ptr + 4, 32);
got_conn_id = true;
}
}
/* A valid conn_id must at least contain one digit, and must
* not exceed a length of 32 digits */
OSMO_ASSERT(strlen(conn_id) <= 32);
OSMO_ASSERT(strlen(conn_id) > 0);
if (got_conn_id) {
for (i = 0; i < conn_id_len; i++) {
if (conn_id[i] == '\n' || conn_id[i] == '\r')
conn_id[i] = '\0';
}
return 0;
/* A valid conn_id must at least contain one digit, and must
* not exceed a length of 32 digits */
OSMO_ASSERT(strlen(conn_id) <= 32);
OSMO_ASSERT(strlen(conn_id) > 0);
return 0;
}
return -EINVAL;
}
/* Check response, automatically patch connection ID if needed */