aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNeels Hofmeyr <nhofmeyr@sysmocom.de>2016-10-18 18:38:59 +0200
committerNeels Hofmeyr <nhofmeyr@sysmocom.de>2017-03-09 17:12:24 +0100
commit2e5ab55617a82210de1a624f2bce213689d9d207 (patch)
treec6df09a0bca263d45d42c89504841382b02a78eb
parent3a30f461d4c1ece3157ed39081b59c6c1c98f425 (diff)
mgcp parsing, mgcp test
-rw-r--r--openbsc/include/openbsc/mgcpgw_client.h49
-rw-r--r--openbsc/include/openbsc/transaction.h13
-rw-r--r--openbsc/src/libmgcp/mgcpgw_client.c185
-rw-r--r--openbsc/src/libmsc/msc_ifaces.c144
-rw-r--r--openbsc/src/osmo-msc/msc_main.c11
-rw-r--r--openbsc/tests/db/db_test.c21
-rw-r--r--openbsc/tests/mgcp/Makefile.am20
-rw-r--r--openbsc/tests/mgcp/mgcpgw_client_test.c165
-rw-r--r--openbsc/tests/mgcp/mgcpgw_client_test.err1
-rw-r--r--openbsc/tests/mgcp/mgcpgw_client_test.ok31
-rw-r--r--openbsc/tests/testsuite.at7
11 files changed, 510 insertions, 137 deletions
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 07ab7a7da..a8df20baa 100644
--- a/openbsc/include/openbsc/transaction.h
+++ b/openbsc/include/openbsc/transaction.h
@@ -9,6 +9,14 @@
#include <osmocom/gsm/gsm0411_smc.h>
#include <osmocom/gsm/gsm0411_smr.h>
+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 7b50007d6..9f0c84de2 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 f75d425ca..d653e078a 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_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)
{
- /* TODO */
+ 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));
}
-int msc_call_bridge(struct gsm_trans *trans1, struct gsm_trans *trans2)
+static void mgcp_response_bridge_mdcx(struct mgcp_response *r, void *priv)
{
- struct gsm_subscriber_connection *conn1 = trans1->conn;
- struct gsm_subscriber_connection *conn2 = trans2->conn;
+ 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;
- struct mgcpgw_client *mgcp = conn1->network->mgcpgw.client;
- OSMO_ASSERT(mgcp);
+ default:
+ LOGP(DMGCP, LOGL_ERROR,
+ "Unexpected bridge state: %d for %s\n",
+ trans->bridge.state, subscr_name(trans->subscr));
+ break;
+ }
+}
- 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);
+int msc_call_bridge(struct gsm_trans *trans1, struct gsm_trans *trans2)
+{
+ /* 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-msc/msc_main.c b/openbsc/src/osmo-msc/msc_main.c
index f1128988e..9d3efd702 100644
--- a/openbsc/src/osmo-msc/msc_main.c
+++ b/openbsc/src/osmo-msc/msc_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. */
- msc_network->mgcpgw.client = mgcpgw_client_init(
- msc_network, &msc_network->mgcpgw.conf);
-
if (db_init(msc_cmdline_config.database_name)) {
printf("DB: Failed to init database: %s\n",
msc_cmdline_config.database_name);
@@ -506,6 +503,14 @@ TODO: we probably want some of the _net_ ctrl commands from bsc_base_ctrl_cmds_i
if (sms_queue_start(msc_network, 20) != 0)
return -1;
+ msc_network->mgcpgw.client = mgcpgw_client_init(
+ msc_network, &msc_network->mgcpgw.conf);
+
+ if (mgcpgw_client_connect(msc_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 f1eadc268..a0c1e79c3 100644
--- a/openbsc/tests/db/db_test.c
+++ b/openbsc/tests/db/db_test.c
@@ -264,16 +264,17 @@ void vlr_proc_acc_req() {}
void vlr_init() {}
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 <info@sysmocom.de>
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <string.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/application.h>
+#include <openbsc/mgcp.h>
+#include <openbsc/mgcpgw_client.h>
+
+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 375bd8228..5291185d4 100644
--- a/openbsc/tests/testsuite.at
+++ b/openbsc/tests/testsuite.at
@@ -33,6 +33,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