diff options
author | Neels Hofmeyr <nhofmeyr@sysmocom.de> | 2016-10-18 18:38:59 +0200 |
---|---|---|
committer | Neels Hofmeyr <nhofmeyr@sysmocom.de> | 2016-11-12 16:08:22 +0100 |
commit | c692fc5e2eb62eb86c85847c79dedb4d87ad564c (patch) | |
tree | 5460cc1d7bae6ebb573fb892a436085661f6a2c4 /openbsc/src | |
parent | d9e229b08871c6be76e97e7d7281a824c1bdb231 (diff) |
mgcp parsing, mgcp test
Change-Id: Ibe2ab17b3fa3a506a2e841ba979ea4175e3a21e8
Diffstat (limited to 'openbsc/src')
-rw-r--r-- | openbsc/src/libmgcp/mgcpgw_client.c | 185 | ||||
-rw-r--r-- | openbsc/src/libmsc/msc_ifaces.c | 144 | ||||
-rw-r--r-- | openbsc/src/osmo-cscn/cscn_main.c | 11 |
3 files changed, 236 insertions, 104 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" diff --git a/openbsc/src/libmsc/msc_ifaces.c b/openbsc/src/libmsc/msc_ifaces.c index 49de9b9b9..8c5876741 100644 --- a/openbsc/src/libmsc/msc_ifaces.c +++ b/openbsc/src/libmsc/msc_ifaces.c @@ -110,27 +110,35 @@ int msc_tx_common_id(struct gsm_subscriber_connection *conn) } #ifdef BUILD_IU -static int iu_rab_act_cs(struct ue_conn_ctx *uectx, uint8_t rab_id, - uint32_t rtp_ip, uint16_t rtp_port) +static void iu_rab_act_cs(struct ue_conn_ctx *uectx, uint8_t rab_id, + uint32_t rtp_ip, uint16_t rtp_port) { struct msgb *msg; bool use_x213_nsap; + uint32_t conn_id = uectx->conn_id; use_x213_nsap = (uectx->rab_assign_addr_enc == NSAP_ADDR_ENC_X213); - LOGP(DIUCS, LOGL_DEBUG, "Assigning RAB: rab_id=%d, rtp=%x:%u," - " use_x213_nsap=%d\n", rab_id, rtp_ip, rtp_port, use_x213_nsap); + LOGP(DIUCS, LOGL_DEBUG, "Assigning RAB: conn_id=%u, rab_id=%d," + " rtp=%x:%u, use_x213_nsap=%d\n", conn_id, rab_id, rtp_ip, + rtp_port, use_x213_nsap); msg = ranap_new_msg_rab_assign_voice(rab_id, rtp_ip, rtp_port, use_x213_nsap); msg->l2h = msg->data; - return iu_rab_act(uectx, msg); + if (iu_rab_act(uectx, msg)) + LOGP(DIUCS, LOGL_ERROR, "Failed to send RAB Assignment:" + " conn_id=%d rab_id=%d rtp=%x:%u\n", + conn_id, rab_id, rtp_ip, rtp_port); } static void mgcp_response_rab_act_cs_crcx(struct mgcp_response *r, void *priv) { struct gsm_trans *trans = priv; + struct gsm_subscriber_connection *conn = trans->conn; + struct ue_conn_ctx *uectx = conn->iu.ue_ctx; + uint32_t rtp_ip; int rc; if (r->head.response_code != 200) { @@ -148,6 +156,10 @@ static void mgcp_response_rab_act_cs_crcx(struct mgcp_response *r, void *priv) goto rab_act_cs_error; } + rtp_ip = mgcpgw_client_remote_addr_n(conn->network->mgcpgw.client); + iu_rab_act_cs(uectx, conn->iu.rab_id, rtp_ip, + conn->iu.mgcp_rtp_port_ue); + /* use_x213_nsap == 0 for ip.access nano3G */ rab_act_cs_error: /* FIXME abort call, invalidate conn, ... */ @@ -157,7 +169,8 @@ rab_act_cs_error: static int conn_iu_rab_act_cs(struct gsm_trans *trans) { struct gsm_subscriber_connection *conn = trans->conn; - struct ue_conn_ctx *uectx = conn->iu.ue_ctx; + struct mgcpgw_client *mgcp = conn->network->mgcpgw.client; + struct msgb *msg; /* HACK. where to scope the RAB Id? At the conn / subscriber / * ue_conn_ctx? */ @@ -174,17 +187,9 @@ static int conn_iu_rab_act_cs(struct gsm_trans *trans) /* Establish the RTP stream first as looping back to the originator. * 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); - - uint32_t rtp_ip = - mgcpgw_client_remote_addr_n(conn->network->mgcpgw.client); - - return iu_rab_act_cs(uectx, conn->iu.rab_id, rtp_ip, - conn->iu.mgcp_rtp_port_ue); - /* use_x213_nsap == 0 for ip.access nano3G */ + msg = mgcp_msg_crcx(mgcp, conn->iu.mgcp_rtp_endpoint, trans->callref, + MGCP_CONN_LOOPBACK); + return mgcpgw_client_tx(mgcp, msg, mgcp_response_rab_act_cs_crcx, trans); } #endif @@ -217,44 +222,85 @@ int msc_call_assignment(struct gsm_trans *trans) } } +static void mgcp_response_bridge_mdcx(struct mgcp_response *r, void *priv); + +static void mgcp_bridge(struct gsm_trans *from, struct gsm_trans *to, + enum bridge_state state, + enum mgcp_connection_mode mode) +{ + struct gsm_subscriber_connection *conn1 = from->conn; + struct gsm_subscriber_connection *conn2 = to->conn; + struct mgcpgw_client *mgcp = conn1->network->mgcpgw.client; + const char *ip; + struct msgb *msg; + + OSMO_ASSERT(mgcp); + + from->bridge.peer = to; + from->bridge.state = state; + + /* Loop back to the same MGCP GW */ + ip = mgcpgw_client_remote_addr_str(mgcp); + + msg = mgcp_msg_mdcx(mgcp, + conn1->iu.mgcp_rtp_endpoint, + ip, conn2->iu.mgcp_rtp_port_cn, + mode); + if (mgcpgw_client_tx(mgcp, msg, mgcp_response_bridge_mdcx, from)) + LOGP(DMGCP, LOGL_ERROR, + "Failed to send MDCX message for %s\n", + subscr_name(from->subscr)); +} + static void mgcp_response_bridge_mdcx(struct mgcp_response *r, void *priv) { - /* TODO */ + struct gsm_trans *trans = priv; + struct gsm_trans *peer = trans->bridge.peer; + + switch (trans->bridge.state) { + case BRIDGE_STATE_LOOPBACK_PENDING: + trans->bridge.state = BRIDGE_STATE_LOOPBACK_ESTABLISHED; + + switch (peer->bridge.state) { + case BRIDGE_STATE_LOOPBACK_PENDING: + /* Wait until the other is done as well. */ + return; + case BRIDGE_STATE_LOOPBACK_ESTABLISHED: + /* Now that both are in loopback, switch both to + * forwarding. */ + mgcp_bridge(trans, peer, BRIDGE_STATE_BRIDGE_PENDING, + MGCP_CONN_RECV_SEND); + mgcp_bridge(peer, trans, BRIDGE_STATE_BRIDGE_PENDING, + MGCP_CONN_RECV_SEND); + break; + default: + LOGP(DMGCP, LOGL_ERROR, + "Unexpected bridge state: %d for %s\n", + trans->bridge.state, subscr_name(trans->subscr)); + break; + } + + case BRIDGE_STATE_BRIDGE_PENDING: + trans->bridge.state = BRIDGE_STATE_BRIDGE_ESTABLISHED; + break; + + default: + LOGP(DMGCP, LOGL_ERROR, + "Unexpected bridge state: %d for %s\n", + trans->bridge.state, subscr_name(trans->subscr)); + break; + } } int msc_call_bridge(struct gsm_trans *trans1, struct gsm_trans *trans2) { - struct gsm_subscriber_connection *conn1 = trans1->conn; - struct gsm_subscriber_connection *conn2 = trans2->conn; - - struct mgcpgw_client *mgcp = conn1->network->mgcpgw.client; - OSMO_ASSERT(mgcp); - - const char *ip = mgcpgw_client_remote_addr_str(mgcp); - - /* First setup the counterparts' endpoints, so that when transmission - * starts the originating addresses are already known to be valid. */ - 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, - 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, - 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, - mgcp_response_bridge_mdcx, trans2, - conn2->iu.mgcp_rtp_endpoint, - ip, conn1->iu.mgcp_rtp_port_cn, - MGCP_CONN_RECV_SEND); + /* First setup as loopback and configure the counterparts' endpoints, + * so that when transmission starts the originating addresses are + * already known to be valid. The callback will continue. */ + mgcp_bridge(trans1, trans2, BRIDGE_STATE_LOOPBACK_PENDING, + MGCP_CONN_LOOPBACK); + mgcp_bridge(trans2, trans1, BRIDGE_STATE_LOOPBACK_PENDING, + MGCP_CONN_LOOPBACK); return 0; } diff --git a/openbsc/src/osmo-cscn/cscn_main.c b/openbsc/src/osmo-cscn/cscn_main.c index 3bdeffa6b..47a4b8c4c 100644 --- a/openbsc/src/osmo-cscn/cscn_main.c +++ b/openbsc/src/osmo-cscn/cscn_main.c @@ -459,9 +459,6 @@ TODO: we probably want some of the _net_ ctrl commands from bsc_base_ctrl_cmds_i /* TODO: is this used for crypto?? Improve randomness, at least we * should try to use the nanoseconds part of the current time. */ - cscn_network->mgcpgw.client = mgcpgw_client_init( - cscn_network, &cscn_network->mgcpgw.conf); - if (db_init(cscn_cmdline_config.database_name)) { printf("DB: Failed to init database: %s\n", cscn_cmdline_config.database_name); @@ -492,6 +489,14 @@ TODO: we probably want some of the _net_ ctrl commands from bsc_base_ctrl_cmds_i if (sms_queue_start(cscn_network, 20) != 0) return -1; + cscn_network->mgcpgw.client = mgcpgw_client_init( + cscn_network, &cscn_network->mgcpgw.conf); + + if (mgcpgw_client_connect(cscn_network->mgcpgw.client)) { + printf("MGCPGW connect failed\n"); + return 7; + } + /* Set up A-Interface */ /* TODO: implement A-Interface and remove above legacy stuff. */ |