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 | |
parent | 978e72e500ac5544ee9fbddbac03125f60fceef8 (diff) |
mgcp: handle responses from the MGCP GW
Change-Id: I5c0493feaec775461b5a017c36b93cc2ad63c896
Diffstat (limited to 'openbsc/src')
-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 | ||||
-rw-r--r-- | openbsc/src/libmsc/msc_ifaces.c | 48 | ||||
-rw-r--r-- | openbsc/src/osmo-msc/msc_main.c | 13 |
5 files changed, 251 insertions, 54 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, diff --git a/openbsc/src/libmsc/msc_ifaces.c b/openbsc/src/libmsc/msc_ifaces.c index ea68af9cb..f75d425ca 100644 --- a/openbsc/src/libmsc/msc_ifaces.c +++ b/openbsc/src/libmsc/msc_ifaces.c @@ -128,6 +128,32 @@ static int iu_rab_act_cs(struct ue_conn_ctx *uectx, uint8_t rab_id, return iu_rab_act(uectx, msg); } +static void mgcp_response_rab_act_cs_crcx(struct mgcp_response *r, void *priv) +{ + struct gsm_trans *trans = priv; + int rc; + + if (r->head.response_code != 200) { + LOGP(DMGCP, LOGL_ERROR, + "MGCPGW response yields error: %d %s\n", + r->head.response_code, r->head.comment); + goto rab_act_cs_error; + } + + rc = mgcp_response_parse_params(r); + if (rc) { + LOGP(DMGCP, LOGL_ERROR, + "Cannot parse MGCP response, for %s\n", + subscr_name(trans->subscr)); + goto rab_act_cs_error; + } + + +rab_act_cs_error: + /* FIXME abort call, invalidate conn, ... */ + return; +} + static int conn_iu_rab_act_cs(struct gsm_trans *trans) { struct gsm_subscriber_connection *conn = trans->conn; @@ -149,6 +175,7 @@ static int conn_iu_rab_act_cs(struct gsm_trans *trans) * The MDCX will patch through to the counterpart. TODO: play a ring * tone instead. */ mgcpgw_client_tx_crcx(conn->network->mgcpgw.client, + mgcp_response_rab_act_cs_crcx, trans, conn->iu.mgcp_rtp_endpoint, trans->callref, MGCP_CONN_LOOPBACK); @@ -190,6 +217,11 @@ int msc_call_assignment(struct gsm_trans *trans) } } +static void mgcp_response_bridge_mdcx(struct mgcp_response *r, void *priv) +{ + /* TODO */ +} + int msc_call_bridge(struct gsm_trans *trans1, struct gsm_trans *trans2) { struct gsm_subscriber_connection *conn1 = trans1->conn; @@ -202,17 +234,25 @@ int msc_call_bridge(struct gsm_trans *trans1, struct gsm_trans *trans2) /* First setup the counterparts' endpoints, so that when transmission * starts the originating addresses are already known to be valid. */ - mgcpgw_client_tx_mdcx(mgcp, conn1->iu.mgcp_rtp_endpoint, + mgcpgw_client_tx_mdcx(mgcp, + mgcp_response_bridge_mdcx, trans1, + conn1->iu.mgcp_rtp_endpoint, ip, conn2->iu.mgcp_rtp_port_cn, MGCP_CONN_LOOPBACK); - mgcpgw_client_tx_mdcx(mgcp, conn2->iu.mgcp_rtp_endpoint, + mgcpgw_client_tx_mdcx(mgcp, + mgcp_response_bridge_mdcx, trans2, + conn2->iu.mgcp_rtp_endpoint, ip, conn1->iu.mgcp_rtp_port_cn, MGCP_CONN_LOOPBACK); /* Now enable sending to and receiving from the peer. */ - mgcpgw_client_tx_mdcx(mgcp, conn1->iu.mgcp_rtp_endpoint, + mgcpgw_client_tx_mdcx(mgcp, + mgcp_response_bridge_mdcx, trans1, + conn1->iu.mgcp_rtp_endpoint, ip, conn2->iu.mgcp_rtp_port_cn, MGCP_CONN_RECV_SEND); - mgcpgw_client_tx_mdcx(mgcp, conn2->iu.mgcp_rtp_endpoint, + mgcpgw_client_tx_mdcx(mgcp, + mgcp_response_bridge_mdcx, trans2, + conn2->iu.mgcp_rtp_endpoint, ip, conn1->iu.mgcp_rtp_port_cn, MGCP_CONN_RECV_SEND); diff --git a/openbsc/src/osmo-msc/msc_main.c b/openbsc/src/osmo-msc/msc_main.c index 6a8297f8d..f1128988e 100644 --- a/openbsc/src/osmo-msc/msc_main.c +++ b/openbsc/src/osmo-msc/msc_main.c @@ -96,16 +96,6 @@ void *tall_map_ctx = NULL; void *tall_upq_ctx = NULL; /* end deps from libbsc legacy. */ -static void mgcp_rx_cb(struct msgb *msg, void *priv) -{ - static char strbuf[4096]; - unsigned int l = msg->len < sizeof(strbuf)-1 ? msg->len : sizeof(strbuf)-1; - strncpy(strbuf, (const char*)msg->data, l); - strbuf[l] = '\0'; - DEBUGP(DMGCP, "Rx MGCP msg from MGCP GW: '%s'\n", strbuf); - talloc_free(msg); -} - static struct { const char *database_name; const char *config_file; @@ -470,8 +460,7 @@ TODO: we probably want some of the _net_ ctrl commands from bsc_base_ctrl_cmds_i * should try to use the nanoseconds part of the current time. */ msc_network->mgcpgw.client = mgcpgw_client_init( - msc_network, &msc_network->mgcpgw.conf, - mgcp_rx_cb, NULL); + msc_network, &msc_network->mgcpgw.conf); if (db_init(msc_cmdline_config.database_name)) { printf("DB: Failed to init database: %s\n", |