diff options
author | Neels Hofmeyr <nhofmeyr@sysmocom.de> | 2016-10-14 17:56:17 +0200 |
---|---|---|
committer | Neels Hofmeyr <nhofmeyr@sysmocom.de> | 2017-03-16 15:32:33 +0100 |
commit | 7c8a62a9406ab33feecd890b044f001aee7fadd9 (patch) | |
tree | c48a4097dace537bf4809e0914cc316349b5faf6 /openbsc/src/libmgcp | |
parent | 978e72e500ac5544ee9fbddbac03125f60fceef8 (diff) |
mgcp: handle responses from the MGCP GW
Change-Id: I5c0493feaec775461b5a017c36b93cc2ad63c896
Diffstat (limited to 'openbsc/src/libmgcp')
-rw-r--r-- | openbsc/src/libmgcp/mgcp_common.c | 21 | ||||
-rw-r--r-- | openbsc/src/libmgcp/mgcp_protocol.c | 16 | ||||
-rw-r--r-- | openbsc/src/libmgcp/mgcpgw_client.c | 207 |
3 files changed, 206 insertions, 38 deletions
diff --git a/openbsc/src/libmgcp/mgcp_common.c b/openbsc/src/libmgcp/mgcp_common.c index 9f104739a..4d7d36ab8 100644 --- a/openbsc/src/libmgcp/mgcp_common.c +++ b/openbsc/src/libmgcp/mgcp_common.c @@ -20,6 +20,8 @@ * */ +#include <errno.h> + #include <osmocom/core/utils.h> #include <openbsc/mgcp.h> @@ -30,3 +32,22 @@ const struct value_string mgcp_connection_mode_strs[] = { { MGCP_CONN_RECV_ONLY, "recvonly" }, { MGCP_CONN_LOOPBACK, "loopback" }, }; + +/* Ensure that the msg->l2h is NUL terminated. */ +int mgcp_msg_terminate_nul(struct msgb *msg) +{ + unsigned char *tail = msg->l2h + msgb_l2len(msg); /* char after l2 data */ + if (tail[-1] == '\0') + /* nothing to do */; + else if (msgb_tailroom(msg) > 0) + tail[0] = '\0'; + else if (tail[-1] == '\r' || tail[-1] == '\n') + tail[-1] = '\0'; + else { + LOGP(DMGCP, LOGL_ERROR, "Cannot NUL terminate MGCP message: " + "Length: %d, Buffer size: %d\n", + msgb_l2len(msg), msg->data_len); + return -ENOTSUP; + } + return 0; +} diff --git a/openbsc/src/libmgcp/mgcp_protocol.c b/openbsc/src/libmgcp/mgcp_protocol.c index 0eed5a3f7..39d9ff56e 100644 --- a/openbsc/src/libmgcp/mgcp_protocol.c +++ b/openbsc/src/libmgcp/mgcp_protocol.c @@ -318,29 +318,17 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg) int i, code, handled = 0; struct msgb *resp = NULL; char *data; - unsigned char *tail = msg->l2h + msgb_l2len(msg); /* char after l2 data */ if (msgb_l2len(msg) < 4) { LOGP(DMGCP, LOGL_ERROR, "msg too short: %d\n", msg->len); return NULL; } - /* Ensure that the msg->l2h is NUL terminated. */ - if (tail[-1] == '\0') - /* nothing to do */; - else if (msgb_tailroom(msg) > 0) - tail[0] = '\0'; - else if (tail[-1] == '\r' || tail[-1] == '\n') - tail[-1] = '\0'; - else { - LOGP(DMGCP, LOGL_ERROR, "Cannot NUL terminate MGCP message: " - "Length: %d, Buffer size: %d\n", - msgb_l2len(msg), msg->data_len); + if (mgcp_msg_terminate_nul(msg)) return NULL; - } /* attempt to treat it as a response */ - if (sscanf((const char *)&msg->l2h[0], "%3d %*s", &code) == 1) { + if (sscanf((const char *)&msg->l2h[0], "%3d %u*s", &code) == 1) { LOGP(DMGCP, LOGL_DEBUG, "Response: Code: %d\n", code); return NULL; } diff --git a/openbsc/src/libmgcp/mgcpgw_client.c b/openbsc/src/libmgcp/mgcpgw_client.c index 025bed136..7b50007d6 100644 --- a/openbsc/src/libmgcp/mgcpgw_client.c +++ b/openbsc/src/libmgcp/mgcpgw_client.c @@ -25,6 +25,7 @@ #include <openbsc/mgcpgw_client.h> #include <openbsc/mgcp.h> +#include <openbsc/mgcp_internal.h> #include <openbsc/debug.h> #include <netinet/in.h> @@ -38,10 +39,9 @@ struct mgcpgw_client { struct mgcpgw_client_conf actual; uint32_t remote_addr; struct osmo_wqueue wq; - mgcp_rx_cb_t rx_cb; - void *rx_cb_priv; unsigned int next_trans_id; uint16_t next_endpoint; + struct llist_head responses_pending; }; void mgcpgw_client_conf_init(struct mgcpgw_client_conf *conf) @@ -60,6 +60,138 @@ unsigned int mgcpgw_client_next_endpoint(struct mgcpgw_client *client) return client->next_endpoint ++; } +static void mgcpgw_client_handle_response(struct mgcpgw_client *mgcp, + struct mgcp_response_pending *pending, + struct mgcp_response *response) +{ + if (!pending) + return; + if (pending->response_cb) + pending->response_cb(response, pending->priv); + else + LOGP(DMGCP, LOGL_INFO, "MGCP response ignored (NULL cb)\n"); + talloc_free(pending); +} + +static int mgcp_response_parse_head(struct mgcp_response *r, struct msgb *msg) +{ + int comment_pos; + + if (mgcp_msg_terminate_nul(msg)) + goto response_parse_failure; + + r->data = (char *)msg->data; + + if (sscanf(r->data, "%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; + return 0; + +response_parse_failure: + LOGP(DMGCP, LOGL_ERROR, + "Failed to parse MGCP response header\n"); + return -EINVAL; +} + +/* TODO undup against mgcp_protocol.c:mgcp_check_param() */ +static bool mgcp_line_is_valid(const char *line) +{ + const size_t line_len = strlen(line); + if (line[0] == '\0') + return true; + + if (line_len < 2 + || line[1] != '=') { + LOGP(DMGCP, LOGL_ERROR, + "Wrong MGCP option format: '%s'\n", + line); + return false; + } + + return true; +} + +/* Parse a line like "m=audio 16002 RTP/AVP 98" */ +static int mgcp_parse_audio(struct mgcp_response *r, const char *line) +{ + if (sscanf(line, "m=audio %hu", + &r->audio_port) != 1) + goto response_parse_failure; + + return 0; + +response_parse_failure: + LOGP(DMGCP, LOGL_ERROR, + "Failed to parse MGCP response header\n"); + return -EINVAL; +} + +int mgcp_response_parse_params(struct mgcp_response *r) +{ + char *line; + char *data = r->data; + int rc; + for_each_line(line, data) { + if (!mgcp_line_is_valid(line)) + return -EINVAL; + + switch (line[0]) { + case 'm': + rc = mgcp_parse_audio(r, line); + if (rc) + return rc; + break; + default: + /* skip unhandled parameters */ + break; + } + } + return 0; +} + +static struct mgcp_response_pending *mgcpgw_client_response_pending_get( + struct mgcpgw_client *mgcp, + struct mgcp_response *r) +{ + struct mgcp_response_pending *pending; + if (!r) + return NULL; + llist_for_each_entry(pending, &mgcp->responses_pending, entry) { + if (pending->trans_id == r->head.trans_id) { + llist_del(&pending->entry); + return pending; + } + } + return NULL; +} + +static int mgcpgw_client_read(struct mgcpgw_client *mgcp, struct msgb *msg) +{ + struct mgcp_response r; + struct mgcp_response_pending *pending; + int rc; + + rc = mgcp_response_parse_head(&r, msg); + if (rc) { + LOGP(DMGCP, LOGL_ERROR, "Cannot parse MGCP response\n"); + return -1; + } + + pending = mgcpgw_client_response_pending_get(mgcp, &r); + if (!pending) { + LOGP(DMGCP, LOGL_ERROR, + "Cannot find matching MGCP transaction for trans_id %d\n", + r.head.trans_id); + return -1; + } + + mgcpgw_client_handle_response(mgcp, pending, &r); + return 0; +} + static int mgcp_do_read(struct osmo_fd *fd) { struct mgcpgw_client *mgcp = fd->data; @@ -84,9 +216,9 @@ static int mgcp_do_read(struct osmo_fd *fd) } msg->l2h = msgb_put(msg, ret); - if (mgcp->rx_cb) - mgcp->rx_cb(msg, mgcp->rx_cb_priv); - return 0; + ret = mgcpgw_client_read(mgcp, msg); + talloc_free(msg); + return ret; } static int mgcp_do_write(struct osmo_fd *fd, struct msgb *msg) @@ -109,8 +241,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, - mgcp_rx_cb_t rx_cb, void *rx_cb_priv) + struct mgcpgw_client_conf *conf) { int on; struct sockaddr_in addr; @@ -119,6 +250,8 @@ struct mgcpgw_client *mgcpgw_client_init(void *ctx, mgcp = talloc_zero(ctx, struct mgcpgw_client); + INIT_LLIST_HEAD(&mgcp->responses_pending); + mgcp->next_trans_id = 1; mgcp->next_endpoint = 1; @@ -132,8 +265,6 @@ 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; - mgcp->rx_cb = rx_cb; - mgcp->rx_cb_priv = rx_cb_priv; wq = &mgcp->wq; wq->bfd.fd = socket(AF_INET, SOCK_DGRAM, 0); @@ -214,30 +345,48 @@ uint32_t mgcpgw_client_remote_addr_n(struct mgcpgw_client *mgcp) return mgcp->remote_addr; } -int mgcpgw_client_tx(struct mgcpgw_client *mgcp, struct msgb *msg) +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 *pending; int rc; + pending = talloc_zero(mgcp, struct mgcp_response_pending); + pending->trans_id = trans_id; + pending->response_cb = response_cb; + pending->priv = priv; + llist_add_tail(&pending->entry, &mgcp->responses_pending); + if (msgb_l2len(msg) > 4096) { LOGP(DMGCP, LOGL_ERROR, "Cannot send, MGCP message too large: %u\n", msgb_l2len(msg)); msgb_free(msg); - return -EINVAL; + rc = -EINVAL; + goto mgcp_tx_error; } rc = osmo_wqueue_enqueue(&mgcp->wq, msg); if (rc) { LOGP(DMGCP, LOGL_FATAL, "Could not queue message to MGCP GW\n"); msgb_free(msg); - return rc; + goto mgcp_tx_error; } else LOGP(DMGCP, LOGL_INFO, "Queued %u bytes for MGCP GW\n", msgb_l2len(msg)); return 0; + +mgcp_tx_error: + /* Pass NULL to response cb to indicate an error */ + mgcpgw_client_handle_response(mgcp, pending, NULL); + return -1; } -int mgcpgw_client_tx_buf(struct mgcpgw_client *mgcp, const char *buf, int len) +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) { struct msgb *msg; @@ -254,10 +403,13 @@ int mgcpgw_client_tx_buf(struct mgcpgw_client *mgcp, const char *buf, int len) memcpy(dst, buf, len); msg->l2h = msg->data; - return mgcpgw_client_tx(mgcp, msg); + return mgcpgw_client_tx(mgcp, response_cb, priv, msg, trans_id); } -int mgcpgw_client_tx_str(struct mgcpgw_client *mgcp, const char *fmt, ...) +int mgcpgw_client_tx_str(struct mgcpgw_client *mgcp, + mgcp_response_cb_t response_cb, void *priv, + unsigned int trans_id, + const char *fmt, ...) { char compose[4096 - 128]; va_list ap; @@ -271,37 +423,44 @@ int mgcpgw_client_tx_str(struct mgcpgw_client *mgcp, const char *fmt, ...) return -EMSGSIZE; if (len < 1) return -EIO; - return mgcpgw_client_tx_buf(mgcp, compose, len); + return mgcpgw_client_tx_buf(mgcp, response_cb, priv, compose, len, trans_id); } -int mgcpgw_client_tx_crcx(struct mgcpgw_client *client, +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) { - return mgcpgw_client_tx_str(client, + unsigned int trans_id = mgcp->next_trans_id ++; + return mgcpgw_client_tx_str(mgcp, + response_cb, priv, trans_id, "CRCX %u %x@mgw MGCP 1.0\r\n" "C: %x\r\n" "L: p:20, a:AMR, nt:IN\r\n" "M: %s\r\n" , - client->next_trans_id ++, + trans_id, rtp_endpoint, call_id, mgcp_cmode_name(mode)); } -int mgcpgw_client_tx_mdcx(struct mgcpgw_client *client, uint16_t rtp_endpoint, - const char *rtp_conn_addr, uint16_t rtp_port, - enum mgcp_connection_mode 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) + { - return mgcpgw_client_tx_str(client, + unsigned int trans_id = mgcp->next_trans_id ++; + return mgcpgw_client_tx_str(mgcp, + response_cb, priv, trans_id, "MDCX %u %x@mgw MGCP 1.0\r\n" "M: %s\r\n" "\r\n" "c=IN IP4 %s\r\n" "m=audio %u RTP/AVP 255\r\n" , - client->next_trans_id ++, + trans_id, rtp_endpoint, mgcp_cmode_name(mode), rtp_conn_addr, |