pespin has uploaded this change for review. (
https://gerrit.osmocom.org/c/osmo-mgw/+/39723?usp=email )
Change subject: mgw: Split MGCP LocalConnectionOptions parsing from updating endpoint
......................................................................
mgw: Split MGCP LocalConnectionOptions parsing from updating endpoint
Split current code into 2 steps, so first read-only parsing can be done,
then when all parsing and checks have been done, finally can be applied
o the object.
Change-Id: If75731fb92329dca6d092ffc7ff17cb354d28c0d
---
M include/osmocom/mgcp/mgcp_endp.h
M include/osmocom/mgcp/mgcp_protocol.h
M src/libosmo-mgcp/mgcp_endp.c
M src/libosmo-mgcp/mgcp_msg.c
M src/libosmo-mgcp/mgcp_protocol.c
5 files changed, 259 insertions(+), 244 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/osmo-mgw refs/changes/23/39723/1
diff --git a/include/osmocom/mgcp/mgcp_endp.h b/include/osmocom/mgcp/mgcp_endp.h
index fbe0850..8a2c8a2 100644
--- a/include/osmocom/mgcp/mgcp_endp.h
+++ b/include/osmocom/mgcp/mgcp_endp.h
@@ -149,6 +149,7 @@
const struct mgcp_trunk *trunk);
struct mgcp_endpoint *mgcp_endp_find_specific(const char *epname,
const struct mgcp_trunk *trunk);
+void mgcp_endp_update_lco(struct mgcp_endpoint *endp, const struct mgcp_lco *lco);
void mgcp_endp_release(struct mgcp_endpoint *endp);
struct mgcp_conn *mgcp_endp_get_conn(struct mgcp_endpoint *endp, const char *id);
diff --git a/include/osmocom/mgcp/mgcp_protocol.h b/include/osmocom/mgcp/mgcp_protocol.h
index 2ef4c3e..9f79334 100644
--- a/include/osmocom/mgcp/mgcp_protocol.h
+++ b/include/osmocom/mgcp/mgcp_protocol.h
@@ -42,12 +42,26 @@
mgcp_codecset_reset(&sdp->cset);
}
+/* Local connection options */
+struct mgcp_lco {
+ bool present;
+ char *codec; /* talloc-allocated to some parent */
+ int pkt_period_min; /* time in ms */
+ int pkt_period_max; /* time in ms */
+};
+static inline void mgcp_lco_init(struct mgcp_lco *lco)
+{
+ *lco = (struct mgcp_lco){};
+}
+char *get_lco_identifier(const char *options);
+int check_local_cx_options(void *ctx, const char *options);
#define MGCP_PARSE_HDR_PARS_OSMUX_CID_UNSET (-2)
#define MGCP_PARSE_HDR_PARS_OSMUX_CID_WILDCARD (-1)
struct mgcp_parse_hdr_pars {
- const char *local_options;
+ const char *lco_string;
+ struct mgcp_lco lco;
const char *callid;
const char *connid;
enum mgcp_connection_mode mode;
@@ -59,15 +73,14 @@
static inline void mgcp_parse_hdr_pars_init(struct mgcp_parse_hdr_pars *hpars)
{
- *hpars = (struct mgcp_parse_hdr_pars){
- .local_options = NULL,
- .callid = NULL,
- .connid = NULL,
- .mode = MGCP_CONN_NONE,
- .remote_osmux_cid = MGCP_PARSE_HDR_PARS_OSMUX_CID_UNSET,
- .have_sdp = false,
- .x_osmo_ign = 0,
- };
+ hpars->lco_string = NULL;
+ mgcp_lco_init(&hpars->lco);
+ hpars->callid = NULL;
+ hpars->connid = NULL;
+ hpars->mode = MGCP_CONN_NONE;
+ hpars->remote_osmux_cid = MGCP_PARSE_HDR_PARS_OSMUX_CID_UNSET;
+ hpars->have_sdp = false;
+ hpars->x_osmo_ign = 0;
}
/* Internal structure while parsing a request */
@@ -111,18 +124,8 @@
int mgcp_cause;
};
-/* Local connection options */
-struct mgcp_lco {
- char *string;
- char *codec;
- int pkt_period_min; /* time in ms */
- int pkt_period_max; /* time in ms */
-};
-
char *mgcp_debug_get_last_endpoint_name(void);
-char *get_lco_identifier(const char *options);
-int check_local_cx_options(void *ctx, const char *options);
struct mgcp_rtp_end;
struct mgcp_endpoint;
diff --git a/src/libosmo-mgcp/mgcp_endp.c b/src/libosmo-mgcp/mgcp_endp.c
index 1d2287c..a4ab860 100644
--- a/src/libosmo-mgcp/mgcp_endp.c
+++ b/src/libosmo-mgcp/mgcp_endp.c
@@ -764,6 +764,22 @@
return NULL;
}
+/* Helps assigning a new lco structure, since "codec" is talloc allocated. */
+void mgcp_endp_update_lco(struct mgcp_endpoint *endp, const struct mgcp_lco *lco)
+{
+ /* First free old talloc allocated codec string: */
+ talloc_free(endp->local_options.codec);
+ endp->local_options.codec = NULL;
+
+ if (lco) {
+ endp->local_options = *lco;
+ if (lco->codec)
+ endp->local_options.codec = talloc_strdup(endp, lco->codec);
+ } else {
+ endp->local_options = (struct mgcp_lco){0};
+ }
+}
+
/*! release endpoint, all open connections are closed.
* \param[in] endp endpoint to release */
void mgcp_endp_release(struct mgcp_endpoint *endp)
@@ -785,10 +801,7 @@
/* Reset endpoint parameters and states */
talloc_free(endp->callid);
endp->callid = NULL;
- talloc_free(endp->local_options.string);
- endp->local_options.string = NULL;
- talloc_free(endp->local_options.codec);
- endp->local_options.codec = NULL;
+ mgcp_endp_update_lco(endp, NULL);
if (endp->trunk->trunk_type == MGCP_TRUNK_E1) {
uint8_t ts = e1_ts_nr_from_epname(endp->name);
diff --git a/src/libosmo-mgcp/mgcp_msg.c b/src/libosmo-mgcp/mgcp_msg.c
index af4f9d0..afeadd5 100644
--- a/src/libosmo-mgcp/mgcp_msg.c
+++ b/src/libosmo-mgcp/mgcp_msg.c
@@ -101,6 +101,197 @@
return MGCP_CONN_NONE;
}
+/*! Helper function for check_local_cx_options() to get a pointer of the next
+ * lco option identifier
+ * \param[in] lco string
+ * \returns pointer to the beginning of the LCO identifier, NULL on failure */
+char *get_lco_identifier(const char *options)
+{
+ char *ptr;
+ unsigned int count = 0;
+
+ /* Jump to the end of the lco identifier */
+ ptr = strstr(options, ":");
+ if (!ptr)
+ return NULL;
+
+ /* Walk backwards until the pointer points to the beginning of the
+ * lco identifier. We know that we stand at the beginning when we
+ * are either at the beginning of the memory or see a space or
+ * comma. (this is tolerant, it will accept a:10, b:11 as well as
+ * a:10,b:11) */
+ while (1) {
+ /* Endless loop protection */
+ if (count > 10000)
+ return NULL;
+ else if (ptr < options || *ptr == ' ' || *ptr == ',') {
+ ptr++;
+ break;
+ }
+ ptr--;
+ count++;
+ }
+
+ /* Check if we got any result */
+ if (*ptr == ':')
+ return NULL;
+
+ return ptr;
+}
+
+/*! Check the LCO option. This function checks for multiple appearance of LCO
+* options, which is illegal
+* \param[in] ctx talloc context
+* \param[in] lco string
+* \returns 0 on success, -1 on failure */
+int check_local_cx_options(void *ctx, const char *options)
+{
+ int i;
+ char *options_copy;
+ char *lco_identifier;
+ char *lco_identifier_end;
+ char *next_lco_identifier;
+
+ char **lco_seen;
+ unsigned int lco_seen_n = 0;
+
+ if (!options)
+ return -1;
+
+ lco_seen =
+ (char **)talloc_zero_size(ctx, strlen(options) * sizeof(char *));
+ options_copy = talloc_strdup(ctx, options);
+ lco_identifier = options_copy;
+
+ do {
+ /* Move the lco_identifier pointer to the beginning of the
+ * current lco option identifier */
+ lco_identifier = get_lco_identifier(lco_identifier);
+ if (!lco_identifier)
+ goto error;
+
+ /* Look ahead to the next LCO option early, since we
+ * will parse destructively */
+ next_lco_identifier = strstr(lco_identifier + 1, ",");
+
+ /* Pinch off the end of the lco field identifier name
+ * and see if we still got something, also check if
+ * there is some value after the colon. */
+ lco_identifier_end = strstr(lco_identifier, ":");
+ if (!lco_identifier_end)
+ goto error;
+ if (*(lco_identifier_end + 1) == ' '
+ || *(lco_identifier_end + 1) == ','
+ || *(lco_identifier_end + 1) == '\0')
+ goto error;
+ *lco_identifier_end = '\0';
+ if (strlen(lco_identifier) == 0)
+ goto error;
+
+ /* Check if we have already seen the current field identifier
+ * before. If yes, we must bail, an LCO must only appear once
+ * in the LCO string */
+ for (i = 0; i < lco_seen_n; i++) {
+ if (strcasecmp(lco_seen[i], lco_identifier) == 0)
+ goto error;
+ }
+ lco_seen[lco_seen_n] = lco_identifier;
+ lco_seen_n++;
+
+ /* The first identifier must always be found at the beginnning
+ * of the LCO string */
+ if (lco_seen[0] != options_copy)
+ goto error;
+
+ /* Go to the next lco option */
+ lco_identifier = next_lco_identifier;
+ } while (lco_identifier);
+
+ talloc_free(lco_seen);
+ talloc_free(options_copy);
+ return 0;
+error:
+ talloc_free(lco_seen);
+ talloc_free(options_copy);
+ return -1;
+}
+
+/* Set the LCO from a string (see RFC 3435).
+* The string is stored in the 'string' field. A NULL string is handled exactly
+* like an empty string, the 'string' field is never NULL after this function
+* has been called. */
+static int mgcp_parse_lco(void *ctx, struct mgcp_lco *lco, const char *options)
+{
+ const char *lco_id;
+ char codec[17];
+ char nt[17];
+ int len;
+
+ if (!options)
+ return 0;
+ if (strlen(options) == 0)
+ return 0;
+
+ lco->present = true;
+
+ /* Make sure the encoding of the LCO is consistent before we proceed */
+ if (check_local_cx_options(ctx, options) != 0) {
+ LOGP(DLMGCP, LOGL_ERROR,
+ "local CX options: Internal inconsistency in Local Connection
Options!\n");
+ return -524;
+ }
+
+ lco_id = options;
+ while ((lco_id = get_lco_identifier(lco_id))) {
+ switch (tolower(lco_id[0])) {
+ case 'p':
+ if (sscanf(lco_id + 1, ":%d-%d",
+ &lco->pkt_period_min, &lco->pkt_period_max) == 1)
+ lco->pkt_period_max = lco->pkt_period_min;
+ break;
+ case 'a':
+ /* FIXME: LCO also supports the negotiation of more than one codec.
+ * (e.g. a:PCMU;G726-32) But this implementation only supports a single
+ * codec only. Ignoring all but the first codec. */
+ if (sscanf(lco_id + 1, ":%16[^,;]", codec) == 1) {
+ talloc_free(lco->codec);
+ /* MGCP header is case insensive, and we'll need
+ codec in uppercase when using it later: */
+ len = strlen(codec);
+ lco->codec = talloc_size(ctx, len + 1);
+ osmo_str_toupper_buf(lco->codec, len + 1, codec);
+ }
+ break;
+ case 'n':
+ if (lco_id[1] == 't' && sscanf(lco_id + 2, ":%16[^,]", nt)
== 1)
+ break;
+ /* else: fall through to print notice log */
+ default:
+ LOGP(DLMGCP, LOGL_NOTICE,
+ "LCO: unhandled option: '%c'/%d in \"%s\"\n",
+ *lco_id, *lco_id, options);
+ break;
+ }
+
+ lco_id = strchr(lco_id, ',');
+ if (!lco_id)
+ break;
+ }
+
+ LOGP(DLMGCP, LOGL_DEBUG,
+ "local CX options: lco->pkt_period_max: %d, lco->codec: %s\n",
+ lco->pkt_period_max, lco->codec);
+
+ /* Check if the packetization fits the 20ms raster */
+ if (lco->pkt_period_min % 20 && lco->pkt_period_max % 20) {
+ LOGP(DLMGCP, LOGL_ERROR,
+ "local CX options: packetization interval is not a multiple of
20ms!\n");
+ return -535;
+ }
+
+ return 0;
+}
+
/*! Analyze and parse the the hader of an MGCP messeage string.
* \param[out] pdata caller provided memory to store the parsing results.
* \param[in] data mgcp message string.
@@ -177,6 +368,7 @@
{
struct mgcp_parse_hdr_pars *hp = &pdata->hpars;
char *line;
+ int rc;
mgcp_parse_hdr_pars_init(hp);
@@ -188,7 +380,24 @@
switch (toupper(line[0])) {
case 'L':
- hp->local_options = (const char *)line + 3;
+ hp->lco_string = (const char *)line + 3;
+ rc = mgcp_parse_lco(pdata, &hp->lco, hp->lco_string);
+ if (rc < 0) {
+ LOG_MGCP_PDATA(pdata, LOGL_NOTICE, "Invalid LocalConnectionOptions line:
'%s'", line);
+ switch (pdata->rq->verb) {
+ case MGCP_VERB_CRCX:
+ rate_ctr_inc(rate_ctr_group_get_ctr(pdata->rq->trunk->ratectr.mgcp_crcx_ctr_group,
+ MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS));
+ break;
+ case MGCP_VERB_MDCX:
+ rate_ctr_inc(rate_ctr_group_get_ctr(pdata->rq->trunk->ratectr.mgcp_mdcx_ctr_group,
+ MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS));
+ break;
+ default:
+ break;
+ }
+ return rc;
+ }
break;
case 'C':
hp->callid = (const char *)line + 3;
diff --git a/src/libosmo-mgcp/mgcp_protocol.c b/src/libosmo-mgcp/mgcp_protocol.c
index 06da91a..49a4e83 100644
--- a/src/libosmo-mgcp/mgcp_protocol.c
+++ b/src/libosmo-mgcp/mgcp_protocol.c
@@ -455,199 +455,6 @@
return create_ok_response(rq->trunk, rq->endp, 200, "AUEP",
rq->pdata->trans);
}
-/*! Helper function for check_local_cx_options() to get a pointer of the next
- * lco option identifier
- * \param[in] lco string
- * \returns pointer to the beginning of the LCO identifier, NULL on failure */
-char *get_lco_identifier(const char *options)
-{
- char *ptr;
- unsigned int count = 0;
-
- /* Jump to the end of the lco identifier */
- ptr = strstr(options, ":");
- if (!ptr)
- return NULL;
-
- /* Walk backwards until the pointer points to the beginning of the
- * lco identifier. We know that we stand at the beginning when we
- * are either at the beginning of the memory or see a space or
- * comma. (this is tolerant, it will accept a:10, b:11 as well as
- * a:10,b:11) */
- while (1) {
- /* Endless loop protection */
- if (count > 10000)
- return NULL;
- else if (ptr < options || *ptr == ' ' || *ptr == ',') {
- ptr++;
- break;
- }
- ptr--;
- count++;
- }
-
- /* Check if we got any result */
- if (*ptr == ':')
- return NULL;
-
- return ptr;
-}
-
-/*! Check the LCO option. This function checks for multiple appearence of LCO
- * options, which is illegal
- * \param[in] ctx talloc context
- * \param[in] lco string
- * \returns 0 on success, -1 on failure */
-int check_local_cx_options(void *ctx, const char *options)
-{
- int i;
- char *options_copy;
- char *lco_identifier;
- char *lco_identifier_end;
- char *next_lco_identifier;
-
- char **lco_seen;
- unsigned int lco_seen_n = 0;
-
- if (!options)
- return -1;
-
- lco_seen =
- (char **)talloc_zero_size(ctx, strlen(options) * sizeof(char *));
- options_copy = talloc_strdup(ctx, options);
- lco_identifier = options_copy;
-
- do {
- /* Move the lco_identifier pointer to the beginning of the
- * current lco option identifier */
- lco_identifier = get_lco_identifier(lco_identifier);
- if (!lco_identifier)
- goto error;
-
- /* Look ahead to the next LCO option early, since we
- * will parse destructively */
- next_lco_identifier = strstr(lco_identifier + 1, ",");
-
- /* Pinch off the end of the lco field identifier name
- * and see if we still got something, also check if
- * there is some value after the colon. */
- lco_identifier_end = strstr(lco_identifier, ":");
- if (!lco_identifier_end)
- goto error;
- if (*(lco_identifier_end + 1) == ' '
- || *(lco_identifier_end + 1) == ','
- || *(lco_identifier_end + 1) == '\0')
- goto error;
- *lco_identifier_end = '\0';
- if (strlen(lco_identifier) == 0)
- goto error;
-
- /* Check if we have already seen the current field identifier
- * before. If yes, we must bail, an LCO must only appear once
- * in the LCO string */
- for (i = 0; i < lco_seen_n; i++) {
- if (strcasecmp(lco_seen[i], lco_identifier) == 0)
- goto error;
- }
- lco_seen[lco_seen_n] = lco_identifier;
- lco_seen_n++;
-
- /* The first identifier must always be found at the beginnning
- * of the LCO string */
- if (lco_seen[0] != options_copy)
- goto error;
-
- /* Go to the next lco option */
- lco_identifier = next_lco_identifier;
- } while (lco_identifier);
-
- talloc_free(lco_seen);
- talloc_free(options_copy);
- return 0;
-error:
- talloc_free(lco_seen);
- talloc_free(options_copy);
- return -1;
-}
-
-/* Set the LCO from a string (see RFC 3435).
- * The string is stored in the 'string' field. A NULL string is handled exactly
- * like an empty string, the 'string' field is never NULL after this function
- * has been called. */
-static int set_local_cx_options(void *ctx, struct mgcp_lco *lco,
- const char *options)
-{
- char *lco_id;
- char codec[17];
- char nt[17];
- int len;
-
- if (!options)
- return 0;
- if (strlen(options) == 0)
- return 0;
-
- /* Make sure the encoding of the LCO is consistant before we proceed */
- if (check_local_cx_options(ctx, options) != 0) {
- LOGP(DLMGCP, LOGL_ERROR,
- "local CX options: Internal inconsistency in Local Connection
Options!\n");
- return 524;
- }
-
- talloc_free(lco->string);
- lco->string = talloc_strdup(ctx, options);
-
- lco_id = lco->string;
- while ((lco_id = get_lco_identifier(lco_id))) {
- switch (tolower(lco_id[0])) {
- case 'p':
- if (sscanf(lco_id + 1, ":%d-%d",
- &lco->pkt_period_min, &lco->pkt_period_max) == 1)
- lco->pkt_period_max = lco->pkt_period_min;
- break;
- case 'a':
- /* FIXME: LCO also supports the negotiation of more than one codec.
- * (e.g. a:PCMU;G726-32) But this implementation only supports a single
- * codec only. Ignoring all but the first codec. */
- if (sscanf(lco_id + 1, ":%16[^,;]", codec) == 1) {
- talloc_free(lco->codec);
- /* MGCP header is case insensive, and we'll need
- codec in uppercase when using it later: */
- len = strlen(codec);
- lco->codec = talloc_size(ctx, len + 1);
- osmo_str_toupper_buf(lco->codec, len + 1, codec);
- }
- break;
- case 'n':
- if (lco_id[1] == 't' && sscanf(lco_id + 2, ":%16[^,]", nt)
== 1)
- break;
- /* else: fall throught to print notice log */
- default:
- LOGP(DLMGCP, LOGL_NOTICE,
- "LCO: unhandled option: '%c'/%d in \"%s\"\n",
- *lco_id, *lco_id, lco->string);
- break;
- }
-
- lco_id = strchr(lco_id, ',');
- if (!lco_id)
- break;
- }
-
- LOGP(DLMGCP, LOGL_DEBUG,
- "local CX options: lco->pkt_period_max: %i, lco->codec: %s\n",
- lco->pkt_period_max, lco->codec);
-
- /* Check if the packetization fits the 20ms raster */
- if (lco->pkt_period_min % 20 && lco->pkt_period_max % 20) {
- LOGP(DLMGCP, LOGL_ERROR,
- "local CX options: packetization interval is not a multiple of
20ms!\n");
- return 535;
- }
-
- return 0;
-}
-
uint32_t mgcp_rtp_packet_duration(const struct mgcp_endpoint *endp,
const struct mgcp_rtp_end *rtp)
{
@@ -907,6 +714,10 @@
/* Update endp->x_osmo_ign: */
endp->x_osmo_ign |= hpars->x_osmo_ign;
+ /* Set local connection options, if present */
+ if (hpars->lco.present)
+ mgcp_endp_update_lco(endp, &hpars->lco);
+
if (!endp->callid) {
/* Claim endpoint resources. This will also set the callid,
* creating additional connections will only be possible if
@@ -953,19 +764,6 @@
} /* else: -1 (wildcard) */
}
- /* Set local connection options, if present */
- if (hpars->local_options) {
- rc = set_local_cx_options(trunk->endpoints,
- &endp->local_options, hpars->local_options);
- if (rc != 0) {
- LOGPCONN(conn, DLMGCP, LOGL_ERROR,
- "CRCX: invalid local connection options!\n");
- error_code = rc;
- rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS));
- goto error2;
- }
- }
-
/* Handle codec information and decide for a suitable codec */
rc = handle_codec_info(conn_rtp, rq);
mgcp_codecset_summary(&conn_rtp->end.cset, mgcp_conn_dump(conn));
@@ -1120,6 +918,10 @@
return create_err_response(endp, endp, 400, "MDCX", pdata->trans);
}
+ /* Set local connection options, if present */
+ if (hpars->lco.present)
+ mgcp_endp_update_lco(endp, &hpars->lco);
+
mgcp_conn_watchdog_kick(conn);
if (hpars->mode == MGCP_CONN_NONE) {
@@ -1130,19 +932,6 @@
return create_err_response(endp, endp, 517, "MDCX", pdata->trans);
}
- /* Set local connection options, if present */
- if (hpars->local_options) {
- rc = set_local_cx_options(trunk->endpoints,
- &endp->local_options, hpars->local_options);
- if (rc != 0) {
- LOGPCONN(conn, DLMGCP, LOGL_ERROR,
- "MDCX: invalid local connection options!\n");
- error_code = rc;
- rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS));
- goto error3;
- }
- }
-
conn_rtp = mgcp_conn_get_conn_rtp(conn);
OSMO_ASSERT(conn_rtp);
--
To view, visit
https://gerrit.osmocom.org/c/osmo-mgw/+/39723?usp=email
To unsubscribe, or for help writing mail filters, visit
https://gerrit.osmocom.org/settings?usp=email
Gerrit-MessageType: newchange
Gerrit-Project: osmo-mgw
Gerrit-Branch: master
Gerrit-Change-Id: If75731fb92329dca6d092ffc7ff17cb354d28c0d
Gerrit-Change-Number: 39723
Gerrit-PatchSet: 1
Gerrit-Owner: pespin <pespin(a)sysmocom.de>