From c692fc5e2eb62eb86c85847c79dedb4d87ad564c Mon Sep 17 00:00:00 2001 From: Neels Hofmeyr Date: Tue, 18 Oct 2016 18:38:59 +0200 Subject: mgcp parsing, mgcp test Change-Id: Ibe2ab17b3fa3a506a2e841ba979ea4175e3a21e8 --- openbsc/include/openbsc/mgcpgw_client.h | 49 ++++---- openbsc/include/openbsc/transaction.h | 13 +++ openbsc/src/libmgcp/mgcpgw_client.c | 185 +++++++++++++++++++++--------- openbsc/src/libmsc/msc_ifaces.c | 144 +++++++++++++++-------- openbsc/src/osmo-cscn/cscn_main.c | 11 +- openbsc/tests/db/db_test.c | 21 ++-- openbsc/tests/mgcp/Makefile.am | 20 ++++ openbsc/tests/mgcp/mgcpgw_client_test.c | 165 ++++++++++++++++++++++++++ openbsc/tests/mgcp/mgcpgw_client_test.err | 1 + openbsc/tests/mgcp/mgcpgw_client_test.ok | 31 +++++ openbsc/tests/testsuite.at | 7 ++ 11 files changed, 510 insertions(+), 137 deletions(-) create mode 100644 openbsc/tests/mgcp/mgcpgw_client_test.c create mode 100644 openbsc/tests/mgcp/mgcpgw_client_test.err create mode 100644 openbsc/tests/mgcp/mgcpgw_client_test.ok diff --git a/openbsc/include/openbsc/mgcpgw_client.h b/openbsc/include/openbsc/mgcpgw_client.h index 60e648d9b..b353db0a4 100644 --- a/openbsc/include/openbsc/mgcpgw_client.h +++ b/openbsc/include/openbsc/mgcpgw_client.h @@ -8,12 +8,17 @@ enum mgcp_connection_mode; struct msgb; struct mgcpgw_client; +struct vty; #define MGCPGW_CLIENT_LOCAL_ADDR_DEFAULT "0.0.0.0" #define MGCPGW_CLIENT_LOCAL_PORT_DEFAULT 0 #define MGCPGW_CLIENT_REMOTE_ADDR_DEFAULT "127.0.0.1" #define MGCPGW_CLIENT_REMOTE_PORT_DEFAULT 2427 +#define MSGB_CB_MGCP_TRANS_ID 0 + +typedef unsigned int mgcp_trans_id_t; + struct mgcpgw_client_conf { const char *local_addr; int local_port; @@ -23,12 +28,12 @@ struct mgcpgw_client_conf { struct mgcp_response_head { int response_code; - unsigned int trans_id; + mgcp_trans_id_t trans_id; const char *comment; }; struct mgcp_response { - char *data; + char *body; struct mgcp_response_head head; uint16_t audio_port; }; @@ -40,7 +45,7 @@ typedef void (* mgcp_response_cb_t )(struct mgcp_response *response, void *priv) struct mgcp_response_pending { struct llist_head entry; - unsigned int trans_id; + mgcp_trans_id_t trans_id; mgcp_response_cb_t response_cb; void *priv; }; @@ -50,6 +55,7 @@ void mgcpgw_client_conf_init(struct mgcpgw_client_conf *conf); struct mgcpgw_client *mgcpgw_client_init(void *ctx, struct mgcpgw_client_conf *conf); +int mgcpgw_client_connect(struct mgcpgw_client *mgcp); const char *mgcpgw_client_remote_addr_str(struct mgcpgw_client *mgcp); uint16_t mgcpgw_client_remote_port(struct mgcpgw_client *mgcp); @@ -59,26 +65,23 @@ unsigned int mgcpgw_client_next_endpoint(struct mgcpgw_client *client); int mgcp_response_parse_params(struct mgcp_response *r); -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); -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); - -int mgcpgw_client_tx_str(struct mgcpgw_client *mgcp, - mgcp_response_cb_t response_cb, void *priv, - unsigned int trans_id, - const char *fmt, ...); -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); -int mgcpgw_client_tx(struct mgcpgw_client *mgcp, - mgcp_response_cb_t response_cb, void *priv, - struct msgb *msg, unsigned int trans_id); +int mgcpgw_client_tx(struct mgcpgw_client *mgcp, struct msgb *msg, + mgcp_response_cb_t response_cb, void *priv); + +struct msgb *mgcp_msg_crcx(struct mgcpgw_client *mgcp, + uint16_t rtp_endpoint, unsigned int call_id, + 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); void mgcpgw_client_vty_init(int node, struct mgcpgw_client_conf *conf); int mgcpgw_client_config_write(struct vty *vty, const char *indent); + +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); +int mgcpgw_client_rx(struct mgcpgw_client *mgcp, struct msgb *msg); diff --git a/openbsc/include/openbsc/transaction.h b/openbsc/include/openbsc/transaction.h index 9a87d04e4..13482522a 100644 --- a/openbsc/include/openbsc/transaction.h +++ b/openbsc/include/openbsc/transaction.h @@ -9,6 +9,14 @@ #include #include +enum bridge_state { + BRIDGE_STATE_NONE, + BRIDGE_STATE_LOOPBACK_PENDING, + BRIDGE_STATE_LOOPBACK_ESTABLISHED, + BRIDGE_STATE_BRIDGE_PENDING, + BRIDGE_STATE_BRIDGE_ESTABLISHED, +}; + /* One transaction */ struct gsm_trans { /* Entry in list of all transactions */ @@ -57,6 +65,11 @@ struct gsm_trans { struct gsm_sms *sms; } sms; }; + + struct { + struct gsm_trans *peer; + enum bridge_state state; + } bridge; }; 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. */ diff --git a/openbsc/tests/db/db_test.c b/openbsc/tests/db/db_test.c index 4868be8df..79d132333 100644 --- a/openbsc/tests/db/db_test.c +++ b/openbsc/tests/db/db_test.c @@ -257,16 +257,17 @@ int main() void vty_out() {} unsigned int mgcpgw_client_next_endpoint(struct mgcpgw_client *client) { return 0; } -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 -ENOTSUP; } -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 -ENOTSUP; } +struct msgb *mgcp_msg_crcx(struct mgcpgw_client *mgcp, + uint16_t rtp_endpoint, unsigned int call_id, + enum mgcp_connection_mode mode) +{ return NULL; } +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) +{ return NULL; } +int mgcpgw_client_tx(struct mgcpgw_client *mgcp, struct msgb *msg, + mgcp_response_cb_t response_cb, void *priv) +{ return -EINVAL; } const char *mgcpgw_client_remote_addr_str(struct mgcpgw_client *mgcp) { return "0.0.0.0"; } uint32_t mgcpgw_client_remote_addr_n(struct mgcpgw_client *mgcp) diff --git a/openbsc/tests/mgcp/Makefile.am b/openbsc/tests/mgcp/Makefile.am index 4b18036cc..31d8a51f8 100644 --- a/openbsc/tests/mgcp/Makefile.am +++ b/openbsc/tests/mgcp/Makefile.am @@ -22,10 +22,12 @@ AM_LDFLAGS = \ EXTRA_DIST = \ mgcp_test.ok \ mgcp_transcoding_test.ok \ + mgcpgw_client_test.ok \ $(NULL) noinst_PROGRAMS = \ mgcp_test \ + mgcpgw_client_test \ $(NULL) if BUILD_MGCP_TRANSCODING noinst_PROGRAMS += \ @@ -70,3 +72,21 @@ mgcp_transcoding_test_LDADD = \ -lrt \ -lm \ $(NULL) + +mgcpgw_client_test_SOURCES = \ + mgcpgw_client_test.c \ + $(NULL) + +mgcpgw_client_test_LDADD = \ + $(top_builddir)/src/libbsc/libbsc.a \ + $(top_builddir)/src/libmgcp/libmgcp.a \ + $(top_builddir)/src/libcommon/libcommon.a \ + $(LIBOSMOCORE_LIBS) \ + $(LIBOSMOGSM_LIBS) \ + $(LIBOSMOSCCP_LIBS) \ + $(LIBOSMOVTY_LIBS) \ + $(LIBRARY_DL) \ + $(LIBOSMONETIF_LIBS) \ + -lrt \ + -lm \ + $(NULL) diff --git a/openbsc/tests/mgcp/mgcpgw_client_test.c b/openbsc/tests/mgcp/mgcpgw_client_test.c new file mode 100644 index 000000000..10d109b47 --- /dev/null +++ b/openbsc/tests/mgcp/mgcpgw_client_test.c @@ -0,0 +1,165 @@ +/* + * (C) 2016 by sysmocom s.f.m.c. GmbH + * All Rights Reserved + * + * Author: Neels Hofmeyr + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include + +#include +#include +#include +#include + +void *ctx; + +#define buf_len 4096 + +#if 0 +static struct msgb *from_hex(const char *hex) +{ + struct msgb *msg = msgb_alloc(buf_len, "mgcpgw_test_from_hex"); + unsigned int l = osmo_hexparse(hex, msg->data, buf_len); + msg->l2h = msgb_put(msg, l); + return msg; +} + +static struct msgb *mgcp_from_str(const char *head, const char *params) +{ + struct msgb *msg = msgb_alloc(buf_len, "mgcp_from_str"); + unsigned int l; + char *data; + l = strlen(head); + msg->l2h = msgb_put(msg, l); + data = (char*)msgb_l2(msg); + strncpy(data, head, l); + + data = (char*)msgb_put(msg, 1); + *data = '\n'; + + l = strlen(params); + data = (char*)msgb_put(msg, l); + strncpy(data, params, l); + + return msg; +} +#endif + +static struct msgb *from_str(const char *str) +{ + struct msgb *msg = msgb_alloc(buf_len, "from_str"); + unsigned int l = strlen(str); + char *data; + msg->l2h = msgb_put(msg, l); + data = (char*)msgb_l2(msg); + strncpy(data, str, l); + return msg; +} + +static struct mgcpgw_client_conf conf; +struct mgcpgw_client *mgcp = NULL; + +static void reply_to(mgcp_trans_id_t trans_id, int code, const char *comment, + int conn_id, const char *params) +{ + static char compose[4096 - 128]; + int len; + + len = snprintf(compose, sizeof(compose), + "%d %u %s\r\nI: %d\n\n%s", + code, trans_id, comment, conn_id, params); + OSMO_ASSERT(len < sizeof(compose)); + OSMO_ASSERT(len > 0); + + printf("composed response:\n-----\n%s\n-----\n", + compose); + mgcpgw_client_rx(mgcp, from_str(compose)); +} + +void test_response_cb(struct mgcp_response *response, void *priv) +{ + OSMO_ASSERT(priv == mgcp); + mgcp_response_parse_params(response); + + printf("response cb received:\n" + " head.response_code = %d\n" + " head.trans_id = %u\n" + " head.comment = %s\n" + " audio_port = %u\n", + response->head.response_code, + response->head.trans_id, + response->head.comment, + response->audio_port + ); +} + +mgcp_trans_id_t dummy_mgcp_send(struct msgb *msg) +{ + mgcp_trans_id_t trans_id; + trans_id = msg->cb[MSGB_CB_MGCP_TRANS_ID]; + char *end; + + OSMO_ASSERT(mgcpgw_client_pending_add(mgcp, trans_id, test_response_cb, mgcp)); + + end = msgb_put(msg, 1); + *end = '\0'; + printf("composed:\n-----\n%s\n-----\n", + (char*)msgb_l2(msg)); + + talloc_free(msg); + return trans_id; +} + +void test_crcx(void) +{ + struct msgb *msg; + mgcp_trans_id_t trans_id; + + printf("\n===== %s =====\n", __func__); + + if (mgcp) + talloc_free(mgcp); + mgcp = mgcpgw_client_init(ctx, &conf); + + msg = mgcp_msg_crcx(mgcp, 23, 42, MGCP_CONN_LOOPBACK); + trans_id = dummy_mgcp_send(msg); + + reply_to(trans_id, 200, "OK", 1, + "v=0\r\n" + "o=- 1 23 IN IP4 10.9.1.120\r\n" + "s=-\r\n" + "c=IN IP4 10.9.1.120\r\n" + "t=0 0\r\n" + "m=audio 16002 RTP/AVP 98\r\n" + "a=rtpmap:98 AMR/8000\r\n" + "a=ptime:20\r\n"); +} + +int main(int argc, char **argv) +{ + ctx = talloc_named_const(NULL, 1, "mgcpgw_client_test"); + msgb_talloc_ctx_init(ctx, 0); + osmo_init_logging(&log_info); + + mgcpgw_client_conf_init(&conf); + + test_crcx(); + + printf("Done\n"); + fprintf(stderr, "Done\n"); + return EXIT_SUCCESS; +} diff --git a/openbsc/tests/mgcp/mgcpgw_client_test.err b/openbsc/tests/mgcp/mgcpgw_client_test.err new file mode 100644 index 000000000..a965a70ed --- /dev/null +++ b/openbsc/tests/mgcp/mgcpgw_client_test.err @@ -0,0 +1 @@ +Done diff --git a/openbsc/tests/mgcp/mgcpgw_client_test.ok b/openbsc/tests/mgcp/mgcpgw_client_test.ok new file mode 100644 index 000000000..d35f2d6b0 --- /dev/null +++ b/openbsc/tests/mgcp/mgcpgw_client_test.ok @@ -0,0 +1,31 @@ + +===== test_crcx ===== +composed: +----- +CRCX 1 17@mgw MGCP 1.0 +C: 2a +L: p:20, a:AMR, nt:IN +M: loopback + +----- +composed response: +----- +200 1 OK +I: 1 + +v=0 +o=- 1 23 IN IP4 10.9.1.120 +s=- +c=IN IP4 10.9.1.120 +t=0 0 +m=audio 16002 RTP/AVP 98 +a=rtpmap:98 AMR/8000 +a=ptime:20 + +----- +response cb received: + head.response_code = 200 + head.trans_id = 1 + head.comment = OK + audio_port = 16002 +Done diff --git a/openbsc/tests/testsuite.at b/openbsc/tests/testsuite.at index b44d5950f..5095bfc5d 100644 --- a/openbsc/tests/testsuite.at +++ b/openbsc/tests/testsuite.at @@ -40,6 +40,13 @@ cat $abs_srcdir/mgcp/mgcp_transcoding_test.ok > expout AT_CHECK([$abs_top_builddir/tests/mgcp/mgcp_transcoding_test], [], [expout], [ignore]) AT_CLEANUP +AT_SETUP([mgcpgw_client]) +AT_KEYWORDS([mgcpgw_client]) +cat $abs_srcdir/mgcp/mgcpgw_client_test.ok > expout +cat $abs_srcdir/mgcp/mgcpgw_client_test.err > experr +AT_CHECK([$abs_top_builddir/tests/mgcp/mgcpgw_client_test], [], [expout], [experr]) +AT_CLEANUP + AT_SETUP([gprs]) AT_KEYWORDS([gprs]) cat $abs_srcdir/gprs/gprs_test.ok > expout -- cgit v1.2.3