mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw.git
synced 2025-10-23 08:12:01 +00:00
mgcp parsing, mgcp test
Change-Id: Ibe2ab17b3fa3a506a2e841ba979ea4175e3a21e8
This commit is contained in:
@@ -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(struct mgcpgw_client *mgcp, struct msgb *msg,
|
||||
mgcp_response_cb_t response_cb, void *priv);
|
||||
|
||||
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);
|
||||
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);
|
||||
|
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
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)
|
||||
static mgcp_trans_id_t mgcpgw_client_next_trans_id(struct mgcpgw_client *mgcp)
|
||||
{
|
||||
unsigned int trans_id = mgcp->next_trans_id ++;
|
||||
return mgcpgw_client_tx_str(mgcp,
|
||||
response_cb, priv, trans_id,
|
||||
/* avoid zero trans_id to distinguish from unset trans_id */
|
||||
if (!mgcp->next_trans_id)
|
||||
mgcp->next_trans_id ++;
|
||||
return mgcp->next_trans_id ++;
|
||||
}
|
||||
|
||||
struct msgb *mgcp_msg_crcx(struct mgcpgw_client *mgcp,
|
||||
uint16_t rtp_endpoint, unsigned int call_id,
|
||||
enum mgcp_connection_mode mode)
|
||||
{
|
||||
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"
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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. */
|
||||
|
||||
|
@@ -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)
|
||||
|
@@ -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)
|
||||
|
165
openbsc/tests/mgcp/mgcpgw_client_test.c
Normal file
165
openbsc/tests/mgcp/mgcpgw_client_test.c
Normal file
@@ -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;
|
||||
}
|
1
openbsc/tests/mgcp/mgcpgw_client_test.err
Normal file
1
openbsc/tests/mgcp/mgcpgw_client_test.err
Normal file
@@ -0,0 +1 @@
|
||||
Done
|
31
openbsc/tests/mgcp/mgcpgw_client_test.ok
Normal file
31
openbsc/tests/mgcp/mgcpgw_client_test.ok
Normal file
@@ -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
|
@@ -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
|
||||
|
Reference in New Issue
Block a user