aboutsummaryrefslogtreecommitdiffstats
path: root/openbsc/src/libmgcp/mgcpgw_client.c
diff options
context:
space:
mode:
authorNeels Hofmeyr <nhofmeyr@sysmocom.de>2016-10-18 18:38:59 +0200
committerNeels Hofmeyr <nhofmeyr@sysmocom.de>2016-11-12 16:08:22 +0100
commitc692fc5e2eb62eb86c85847c79dedb4d87ad564c (patch)
tree5460cc1d7bae6ebb573fb892a436085661f6a2c4 /openbsc/src/libmgcp/mgcpgw_client.c
parentd9e229b08871c6be76e97e7d7281a824c1bdb231 (diff)
mgcp parsing, mgcp test
Diffstat (limited to 'openbsc/src/libmgcp/mgcpgw_client.c')
-rw-r--r--openbsc/src/libmgcp/mgcpgw_client.c185
1 files changed, 133 insertions, 52 deletions
diff --git a/openbsc/src/libmgcp/mgcpgw_client.c b/openbsc/src/libmgcp/mgcpgw_client.c
index 5c08843b0..28c4cb9d1 100644
--- a/openbsc/src/libmgcp/mgcpgw_client.c
+++ b/openbsc/src/libmgcp/mgcpgw_client.c
@@ -39,7 +39,7 @@ struct mgcpgw_client {
struct mgcpgw_client_conf actual;
uint32_t remote_addr;
struct osmo_wqueue wq;
- unsigned int next_trans_id;
+ mgcp_trans_id_t next_trans_id;
uint16_t next_endpoint;
struct llist_head responses_pending;
};
@@ -64,8 +64,11 @@ static void mgcpgw_client_handle_response(struct mgcpgw_client *mgcp,
struct mgcp_response_pending *pending,
struct mgcp_response *response)
{
- if (!pending)
+ if (!pending) {
+ LOGP(DMGCP, LOGL_ERROR,
+ "Cannot handle NULL response\n");
return;
+ }
if (pending->response_cb)
pending->response_cb(response, pending->priv);
else
@@ -76,18 +79,27 @@ static void mgcpgw_client_handle_response(struct mgcpgw_client *mgcp,
static int mgcp_response_parse_head(struct mgcp_response *r, struct msgb *msg)
{
int comment_pos;
+ char *end;
if (mgcp_msg_terminate_nul(msg))
goto response_parse_failure;
- r->data = (char *)msg->data;
+ r->body = (char *)msg->data;
- if (sscanf(r->data, "%3d %u %n",
+ if (sscanf(r->body, "%3d %u %n",
&r->head.response_code, &r->head.trans_id,
&comment_pos) != 2)
goto response_parse_failure;
- r->head.comment = r->data + comment_pos;
+ r->head.comment = r->body + comment_pos;
+ end = strchr(r->head.comment, '\r');
+ if (!end)
+ goto response_parse_failure;
+ /* Mark the end of the comment */
+ *end = '\0';
+ r->body = end + 1;
+ if (r->body[0] == '\n')
+ r->body ++;
return 0;
response_parse_failure:
@@ -132,8 +144,22 @@ response_parse_failure:
int mgcp_response_parse_params(struct mgcp_response *r)
{
char *line;
- char *data = r->data;
int rc;
+ OSMO_ASSERT(r->body);
+ char *data = strstr(r->body, "\n\n");
+
+ if (!data) {
+ LOGP(DMGCP, LOGL_ERROR,
+ "MGCP response: cannot find start of parameters\n");
+ return -EINVAL;
+ }
+
+ /* Advance to after the \n\n, replace the second \n with \0. That's
+ * where the parameters start. */
+ data ++;
+ *data = '\0';
+ data ++;
+
for_each_line(line, data) {
if (!mgcp_line_is_valid(line))
return -EINVAL;
@@ -168,9 +194,15 @@ static struct mgcp_response_pending *mgcpgw_client_response_pending_get(
return NULL;
}
-static int mgcpgw_client_read(struct mgcpgw_client *mgcp, struct msgb *msg)
+/* Feed an MGCP message into the receive processing.
+ * Parse the head and call any callback registered for the transaction id found
+ * in the MGCP message. This is normally called directly from the internal
+ * mgcp_do_read that reads from the socket connected to the MGCP gateway. This
+ * function is published mainly to be able to feed data from the test suite.
+ */
+int mgcpgw_client_rx(struct mgcpgw_client *mgcp, struct msgb *msg)
{
- struct mgcp_response r;
+ struct mgcp_response r = { 0 };
struct mgcp_response_pending *pending;
int rc;
@@ -216,7 +248,7 @@ static int mgcp_do_read(struct osmo_fd *fd)
}
msg->l2h = msgb_put(msg, ret);
- ret = mgcpgw_client_read(mgcp, msg);
+ ret = mgcpgw_client_rx(mgcp, msg);
talloc_free(msg);
return ret;
}
@@ -243,10 +275,7 @@ static int mgcp_do_write(struct osmo_fd *fd, struct msgb *msg)
struct mgcpgw_client *mgcpgw_client_init(void *ctx,
struct mgcpgw_client_conf *conf)
{
- int on;
- struct sockaddr_in addr;
struct mgcpgw_client *mgcp;
- struct osmo_wqueue *wq;
mgcp = talloc_zero(ctx, struct mgcpgw_client);
@@ -265,12 +294,27 @@ struct mgcpgw_client *mgcpgw_client_init(void *ctx,
mgcp->actual.remote_port = conf->remote_port >= 0 ? (uint16_t)conf->remote_port :
MGCPGW_CLIENT_REMOTE_PORT_DEFAULT;
+ return mgcp;
+}
+
+int mgcpgw_client_connect(struct mgcpgw_client *mgcp)
+{
+ int on;
+ struct sockaddr_in addr;
+ struct osmo_wqueue *wq;
+ int rc;
+
+ if (!mgcp) {
+ LOGP(DMGCP, LOGL_FATAL, "MGCPGW client not initialized properly\n");
+ return -EINVAL;
+ }
+
wq = &mgcp->wq;
wq->bfd.fd = socket(AF_INET, SOCK_DGRAM, 0);
if (wq->bfd.fd < 0) {
LOGP(DMGCP, LOGL_FATAL, "Failed to create UDP socket errno: %d\n", errno);
- goto error_free;
+ return -errno;
}
on = 1;
@@ -278,6 +322,7 @@ struct mgcpgw_client *mgcpgw_client_init(void *ctx,
LOGP(DMGCP, LOGL_FATAL,
"Failed to initialize socket for MGCP GW: %s\n",
strerror(errno));
+ rc = -errno;
goto error_close_fd;
}
@@ -290,6 +335,7 @@ struct mgcpgw_client *mgcpgw_client_init(void *ctx,
LOGP(DMGCP, LOGL_FATAL,
"Failed to bind for MGCP GW to %s %u\n",
mgcp->actual.local_addr, mgcp->actual.local_port);
+ rc = -errno;
goto error_close_fd;
}
@@ -301,6 +347,7 @@ struct mgcpgw_client *mgcpgw_client_init(void *ctx,
"Failed to connect to MGCP GW at %s %u: %s\n",
mgcp->actual.remote_addr, mgcp->actual.remote_port,
strerror(errno));
+ rc = -errno;
goto error_close_fd;
}
@@ -314,19 +361,18 @@ struct mgcpgw_client *mgcpgw_client_init(void *ctx,
if (osmo_fd_register(&wq->bfd) != 0) {
LOGP(DMGCP, LOGL_FATAL, "Failed to register BFD\n");
+ rc = -EIO;
goto error_close_fd;
}
LOGP(DMGCP, LOGL_INFO, "MGCP GW connection: %s:%u -> %s:%u\n",
mgcp->actual.local_addr, mgcp->actual.local_port,
mgcp->actual.remote_addr, mgcp->actual.remote_port);
- return mgcp;
+ return 0;
error_close_fd:
close(wq->bfd.fd);
wq->bfd.fd = -1;
-error_free:
- talloc_free(mgcp);
- return NULL;
+ return rc;
}
const char *mgcpgw_client_remote_addr_str(struct mgcpgw_client *mgcp)
@@ -345,12 +391,13 @@ uint32_t mgcpgw_client_remote_addr_n(struct mgcpgw_client *mgcp)
return mgcp->remote_addr;
}
-int mgcpgw_client_tx(struct mgcpgw_client *mgcp,
- mgcp_response_cb_t response_cb, void *priv,
- struct msgb *msg, unsigned int trans_id)
+struct mgcp_response_pending * mgcpgw_client_pending_add(
+ struct mgcpgw_client *mgcp,
+ mgcp_trans_id_t trans_id,
+ mgcp_response_cb_t response_cb,
+ void *priv)
{
struct mgcp_response_pending *pending;
- int rc;
pending = talloc_zero(mgcp, struct mgcp_response_pending);
pending->trans_id = trans_id;
@@ -358,6 +405,31 @@ int mgcpgw_client_tx(struct mgcpgw_client *mgcp,
pending->priv = priv;
llist_add_tail(&pending->entry, &mgcp->responses_pending);
+ return pending;
+}
+
+/* Send the MGCP message in msg to the MGCP GW and handle a response with
+ * response_cb. NOTE: the response_cb still needs to call
+ * mgcp_response_parse_params(response) to get the parsed parameters -- to
+ * potentially save some CPU cycles, only the head line has been parsed when
+ * the response_cb is invoked. */
+int mgcpgw_client_tx(struct mgcpgw_client *mgcp, struct msgb *msg,
+ mgcp_response_cb_t response_cb, void *priv)
+{
+ struct mgcp_response_pending *pending;
+ mgcp_trans_id_t trans_id;
+ int rc;
+
+ trans_id = msg->cb[MSGB_CB_MGCP_TRANS_ID];
+ if (!trans_id) {
+ LOGP(DMGCP, LOGL_ERROR,
+ "Unset transaction id in mgcp send request\n");
+ talloc_free(msg);
+ return -EINVAL;
+ }
+
+ pending = mgcpgw_client_pending_add(mgcp, trans_id, response_cb, priv);
+
if (msgb_l2len(msg) > 4096) {
LOGP(DMGCP, LOGL_ERROR,
"Cannot send, MGCP message too large: %u\n",
@@ -383,35 +455,32 @@ mgcp_tx_error:
return -1;
}
-int mgcpgw_client_tx_buf(struct mgcpgw_client *mgcp,
- mgcp_response_cb_t response_cb, void *priv,
- const char *buf, int len,
- unsigned int trans_id)
+static struct msgb *mgcp_msg_from_buf(mgcp_trans_id_t trans_id,
+ const char *buf, int len)
{
struct msgb *msg;
if (len > (4096 - 128)) {
LOGP(DMGCP, LOGL_ERROR, "Cannot send to MGCP GW:"
" message too large: %d\n", len);
- return -ENOTSUP;
+ return NULL;
}
- msg = msgb_alloc_headroom(4096, 128, "MGCP Tx");
+ msg = msgb_alloc_headroom(4096, 128, "MGCP tx");
OSMO_ASSERT(msg);
char *dst = (char*)msgb_put(msg, len);
memcpy(dst, buf, len);
msg->l2h = msg->data;
+ msg->cb[MSGB_CB_MGCP_TRANS_ID] = trans_id;
- return mgcpgw_client_tx(mgcp, response_cb, priv, msg, trans_id);
+ return msg;
}
-int mgcpgw_client_tx_str(struct mgcpgw_client *mgcp,
- mgcp_response_cb_t response_cb, void *priv,
- unsigned int trans_id,
- const char *fmt, ...)
+static struct msgb *mgcp_msg_from_str(mgcp_trans_id_t trans_id,
+ const char *fmt, ...)
{
- char compose[4096 - 128];
+ static char compose[4096 - 128];
va_list ap;
int len;
OSMO_ASSERT(fmt);
@@ -419,21 +488,35 @@ int mgcpgw_client_tx_str(struct mgcpgw_client *mgcp,
va_start(ap, fmt);
len = vsnprintf(compose, sizeof(compose), fmt, ap);
va_end(ap);
- if (len >= sizeof(compose))
- return -EMSGSIZE;
- if (len < 1)
- return -EIO;
- return mgcpgw_client_tx_buf(mgcp, response_cb, priv, compose, len, trans_id);
+ if (len >= sizeof(compose)) {
+ LOGP(DMGCP, LOGL_ERROR,
+ "Message too large: trans_id=%u len=%d\n",
+ trans_id, len);
+ return NULL;
+ }
+ if (len < 1) {
+ LOGP(DMGCP, LOGL_ERROR,
+ "Failed to compose message: trans_id=%u len=%d\n",
+ trans_id, len);
+ return NULL;
+ }
+ return mgcp_msg_from_buf(trans_id, compose, len);
+}
+
+static mgcp_trans_id_t mgcpgw_client_next_trans_id(struct mgcpgw_client *mgcp)
+{
+ /* avoid zero trans_id to distinguish from unset trans_id */
+ if (!mgcp->next_trans_id)
+ mgcp->next_trans_id ++;
+ return mgcp->next_trans_id ++;
}
-int mgcpgw_client_tx_crcx(struct mgcpgw_client *mgcp,
- mgcp_response_cb_t response_cb, void *priv,
- uint16_t rtp_endpoint, unsigned int call_id,
- enum mgcp_connection_mode mode)
+struct msgb *mgcp_msg_crcx(struct mgcpgw_client *mgcp,
+ uint16_t rtp_endpoint, unsigned int call_id,
+ enum mgcp_connection_mode mode)
{
- unsigned int trans_id = mgcp->next_trans_id ++;
- return mgcpgw_client_tx_str(mgcp,
- response_cb, priv, trans_id,
+ mgcp_trans_id_t trans_id = mgcpgw_client_next_trans_id(mgcp);
+ return mgcp_msg_from_str(trans_id,
"CRCX %u %x@mgw MGCP 1.0\r\n"
"C: %x\r\n"
"L: p:20, a:AMR, nt:IN\r\n"
@@ -445,15 +528,13 @@ int mgcpgw_client_tx_crcx(struct mgcpgw_client *mgcp,
mgcp_cmode_name(mode));
}
-int mgcpgw_client_tx_mdcx(struct mgcpgw_client *mgcp,
- mgcp_response_cb_t response_cb, void *priv,
- uint16_t rtp_endpoint, const char *rtp_conn_addr,
- uint16_t rtp_port, enum mgcp_connection_mode mode)
+struct msgb *mgcp_msg_mdcx(struct mgcpgw_client *mgcp,
+ uint16_t rtp_endpoint, const char *rtp_conn_addr,
+ uint16_t rtp_port, enum mgcp_connection_mode mode)
{
- unsigned int trans_id = mgcp->next_trans_id ++;
- return mgcpgw_client_tx_str(mgcp,
- response_cb, priv, trans_id,
+ mgcp_trans_id_t trans_id = mgcpgw_client_next_trans_id(mgcp);
+ return mgcp_msg_from_str(trans_id,
"MDCX %u %x@mgw MGCP 1.0\r\n"
"M: %s\r\n"
"\r\n"