summaryrefslogtreecommitdiff
path: root/src/libosmo-mgcp/mgcp_protocol.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libosmo-mgcp/mgcp_protocol.c')
-rw-r--r--src/libosmo-mgcp/mgcp_protocol.c301
1 files changed, 254 insertions, 47 deletions
diff --git a/src/libosmo-mgcp/mgcp_protocol.c b/src/libosmo-mgcp/mgcp_protocol.c
index ea86672..ea80907 100644
--- a/src/libosmo-mgcp/mgcp_protocol.c
+++ b/src/libosmo-mgcp/mgcp_protocol.c
@@ -40,6 +40,7 @@
#include <osmocom/mgcp/mgcp_msg.h>
#include <osmocom/mgcp/mgcp_endp.h>
#include <osmocom/mgcp/mgcp_sdp.h>
+#include <osmocom/mgcp/mgcp_codec.h>
struct mgcp_request {
char *name;
@@ -88,8 +89,7 @@ static int setup_rtp_processing(struct mgcp_endpoint *endp,
}
}
- return cfg->setup_rtp_processing_cb(endp, &conn_dst->end,
- &conn_src->end);
+ return cfg->setup_rtp_processing_cb(endp, conn_dst, conn_src);
}
/* array of function pointers for handling various
@@ -356,13 +356,15 @@ static struct msgb *handle_audit_endpoint(struct mgcp_parse_data *p)
/* Try to find a free port by attempting to bind on it. Also handle the
* counter that points on the next free port. Since we have a pointer
- * to the next free port, binding should work on the first attempt,
- * nevertheless, try at least the next 200 ports before giving up */
+ * to the next free port, binding should in work on the first attempt in
+ * general. In case of failure the next port is tryed until the whole port
+ * range is tryed once. */
static int allocate_port(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
{
int i;
struct mgcp_rtp_end *end;
struct mgcp_port_range *range;
+ unsigned int tries;
OSMO_ASSERT(conn);
end = &conn->end;
@@ -371,7 +373,8 @@ static int allocate_port(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
range = &endp->cfg->net_ports;
/* attempt to find a port */
- for (i = 0; i < 200; ++i) {
+ tries = (range->range_end - range->range_start) / 2;
+ for (i = 0; i < tries; ++i) {
int rc;
if (range->last_port >= range->range_end)
@@ -387,8 +390,123 @@ static int allocate_port(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
}
LOGP(DLMGCP, LOGL_ERROR,
- "Allocating a RTP/RTCP port failed 200 times 0x%x.\n",
- ENDPOINT_NUMBER(endp));
+ "Allocating a RTP/RTCP port failed %u times 0x%x.\n",
+ tries, ENDPOINT_NUMBER(endp));
+ return -1;
+}
+
+/*! 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 (strcmp(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;
}
@@ -402,20 +520,34 @@ static int set_local_cx_options(void *ctx, struct mgcp_lco *lco,
char *p_opt, *a_opt;
char codec[9];
+ 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);
- talloc_free(lco->codec);
- lco->codec = NULL;
- lco->pkt_period_min = lco->pkt_period_max = 0;
- lco->string = talloc_strdup(ctx, options ? options : "");
+ lco->string = talloc_strdup(ctx, options);
p_opt = strstr(lco->string, "p:");
if (p_opt && sscanf(p_opt, "p:%d-%d",
&lco->pkt_period_min, &lco->pkt_period_max) == 1)
lco->pkt_period_max = lco->pkt_period_min;
+ /* FIXME: LCO also supports the negotiation of more then one codec.
+ * (e.g. a:PCMU;G726-32) But this implementation only supports a single
+ * codec only. */
a_opt = strstr(lco->string, "a:");
- if (a_opt && sscanf(a_opt, "a:%8[^,]", codec) == 1)
+ if (a_opt && sscanf(a_opt, "a:%8[^,]", codec) == 1) {
+ talloc_free(lco->codec);
lco->codec = talloc_strdup(ctx, codec);
+ }
LOGP(DLMGCP, LOGL_DEBUG,
"local CX options: lco->pkt_period_max: %i, lco->codec: %s\n",
@@ -456,15 +588,15 @@ uint32_t mgcp_rtp_packet_duration(struct mgcp_endpoint *endp,
/* Get the number of frames per channel and packet */
if (rtp->frames_per_packet)
f = rtp->frames_per_packet;
- else if (rtp->packet_duration_ms && rtp->codec.frame_duration_num) {
- int den = 1000 * rtp->codec.frame_duration_num;
- f = (rtp->packet_duration_ms * rtp->codec.frame_duration_den +
+ else if (rtp->packet_duration_ms && rtp->codec->frame_duration_num) {
+ int den = 1000 * rtp->codec->frame_duration_num;
+ f = (rtp->packet_duration_ms * rtp->codec->frame_duration_den +
den / 2)
/ den;
}
- return rtp->codec.rate * f * rtp->codec.frame_duration_num /
- rtp->codec.frame_duration_den;
+ return rtp->codec->rate * f * rtp->codec->frame_duration_num /
+ rtp->codec->frame_duration_den;
}
static int mgcp_osmux_setup(struct mgcp_endpoint *endp, const char *line)
@@ -480,6 +612,68 @@ static int mgcp_osmux_setup(struct mgcp_endpoint *endp, const char *line)
return mgcp_parse_osmux_cid(line);
}
+/* Process codec information contained in CRCX/MDCX */
+static int handle_codec_info(struct mgcp_conn_rtp *conn,
+ struct mgcp_parse_data *p, int have_sdp, bool crcx)
+{
+ struct mgcp_endpoint *endp = p->endp;
+ int rc;
+ char *cmd;
+
+ if (crcx)
+ cmd = "CRCX";
+ else
+ cmd = "MDCX";
+
+ /* Collect codec information */
+ if (have_sdp) {
+ /* If we have SDP, we ignore the local connection options and
+ * use only the SDP information. */
+ mgcp_codec_reset_all(conn);
+ rc = mgcp_parse_sdp_data(endp, conn, p);
+ if (rc != 0) {
+ LOGP(DLMGCP, LOGL_ERROR,
+ "%s: endpoint:%x sdp not parseable\n", cmd,
+ ENDPOINT_NUMBER(endp));
+
+ /* See also RFC 3661: Protocol error */
+ return 510;
+ }
+ } else if (endp->local_options.codec) {
+ /* When no SDP is available, we use the codec information from
+ * the local connection options (if present) */
+ mgcp_codec_reset_all(conn);
+ rc = mgcp_codec_add(conn, PTYPE_UNDEFINED, endp->local_options.codec);
+ if (rc != 0)
+ goto error;
+ }
+
+ /* Make sure we always set a sane default codec */
+ if (conn->end.codecs_assigned == 0) {
+ /* When SDP and/or LCO did not supply any codec information,
+ * than it makes sense to pick a sane default: (payload-type 0,
+ * PCMU), see also: OS#2658 */
+ mgcp_codec_reset_all(conn);
+ rc = mgcp_codec_add(conn, 0, NULL);
+ if (rc != 0)
+ goto error;
+ }
+
+ /* Make codec decision */
+ if (mgcp_codec_decide(conn) != 0)
+ goto error;
+
+ return 0;
+
+error:
+ LOGP(DLMGCP, LOGL_ERROR,
+ "%s: endpoint:0x%x codec negotiation failure\n", cmd,
+ ENDPOINT_NUMBER(endp));
+
+ /* See also RFC 3661: Codec negotiation failure */
+ return 534;
+}
+
/* CRCX command handler, processes the received command */
static struct msgb *handle_create_con(struct mgcp_parse_data *p)
{
@@ -597,17 +791,6 @@ mgcp_header_done:
* connection ids) */
endp->callid = talloc_strdup(tcfg->endpoints, callid);
- /* Extract audio codec information */
- rc = set_local_cx_options(endp->tcfg->endpoints, &endp->local_options,
- local_options);
- if (rc != 0) {
- LOGP(DLMGCP, LOGL_ERROR,
- "CRCX: endpoint:%x inavlid local connection options!\n",
- ENDPOINT_NUMBER(endp));
- error_code = rc;
- goto error2;
- }
-
snprintf(conn_name, sizeof(conn_name), "%s", callid);
_conn = mgcp_conn_alloc(NULL, endp, MGCP_CONN_TYPE_RTP, conn_name);
if (!_conn) {
@@ -638,12 +821,27 @@ mgcp_header_done:
goto error2;
}
- /* set up RTP media parameters */
- if (have_sdp)
- mgcp_parse_sdp_data(endp, conn, p);
- else if (endp->local_options.codec)
- mgcp_set_audio_info(p->cfg, &conn->end.codec,
- PTYPE_UNDEFINED, endp->local_options.codec);
+ /* Set local connection options, if present */
+ if (local_options) {
+ rc = set_local_cx_options(endp->tcfg->endpoints,
+ &endp->local_options, local_options);
+ if (rc != 0) {
+ LOGP(DLMGCP, LOGL_ERROR,
+ "CRCX: endpoint:%x inavlid local connection options!\n",
+ ENDPOINT_NUMBER(endp));
+ error_code = rc;
+ goto error2;
+ }
+ }
+
+ /* Handle codec information and decide for a suitable codec */
+ rc = handle_codec_info(conn, p, have_sdp, true);
+ mgcp_codec_summary(conn);
+ if (rc) {
+ error_code = rc;
+ goto error2;
+ }
+
conn->end.fmtp_extra = talloc_strdup(tcfg->endpoints,
tcfg->audio_fmtp_extra);
@@ -718,11 +916,15 @@ mgcp_header_done:
error2:
mgcp_endp_release(endp);
LOGP(DLMGCP, LOGL_NOTICE,
- "CRCX: endpoint:0x%x unable to create connection resource error\n",
+ "CRCX: endpoint:0x%x unable to create connection\n",
ENDPOINT_NUMBER(endp));
return create_err_response(endp, error_code, "CRCX", p->trans);
}
+
+
+
+
/* MDCX command handler, processes the received command */
static struct msgb *handle_modify_con(struct mgcp_parse_data *p)
{
@@ -814,23 +1016,27 @@ mgcp_header_done:
} else
conn->conn->mode = conn->conn->mode_orig;
- if (have_sdp)
- mgcp_parse_sdp_data(endp, conn, p);
+ /* Set local connection options, if present */
+ if (local_options) {
+ rc = set_local_cx_options(endp->tcfg->endpoints,
+ &endp->local_options, local_options);
+ if (rc != 0) {
+ LOGP(DLMGCP, LOGL_ERROR,
+ "MDCX: endpoint:%x inavlid local connection options!\n",
+ ENDPOINT_NUMBER(endp));
+ error_code = rc;
+ goto error3;
+ }
+ }
- rc = set_local_cx_options(endp->tcfg->endpoints, &endp->local_options,
- local_options);
- if (rc != 0) {
- LOGP(DLMGCP, LOGL_ERROR,
- "MDCX: endpoint:%x inavlid local connection options!\n",
- ENDPOINT_NUMBER(endp));
+ /* Handle codec information and decide for a suitable codec */
+ rc = handle_codec_info(conn, p, have_sdp, false);
+ mgcp_codec_summary(conn);
+ if (rc) {
error_code = rc;
goto error3;
}
- if (!have_sdp && endp->local_options.codec)
- mgcp_set_audio_info(p->cfg, &conn->end.codec,
- PTYPE_UNDEFINED, endp->local_options.codec);
-
/* check connection mode setting */
if (conn->conn->mode != MGCP_CONN_LOOPBACK
&& conn->conn->mode != MGCP_CONN_RECV_ONLY
@@ -842,6 +1048,7 @@ mgcp_header_done:
goto error3;
}
+
if (setup_rtp_processing(endp, conn) != 0)
goto error3;