aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPhilipp Maier <pmaier@sysmocom.de>2017-07-14 15:18:11 +0200
committerNeels Hofmeyr <neels@hofmeyr.de>2017-07-25 15:14:16 +0200
commit90835e0083b47f82e1f50b96e5d460f3ea9d1cb5 (patch)
tree65a36fb0d8903610a2df8de8a780412be83938a2
parent8643faad59baa48b4a8246abd7b5fbea1d2dbf27 (diff)
mncc: make external mncc work
The external mncc currently does not work properly since the MNCC_RTP_CREATE commands are removed due to the MSC-Split. It is possible to operate without these commands, but then it is not possible to route the RTP streams to an outside leg. Only internal bridging is currenlty possible. This method is used when the internal MNCC is enabled. Add the missing MNCC_RTP_CREATE implementation. Add logic to keep the old bridging mode working.
-rw-r--r--include/openbsc/gsm_04_08.h2
-rw-r--r--include/openbsc/msc_ifaces.h1
-rw-r--r--include/openbsc/transaction.h7
-rw-r--r--src/libmsc/gsm_04_08.c168
-rw-r--r--src/libmsc/msc_ifaces.c51
5 files changed, 225 insertions, 4 deletions
diff --git a/include/openbsc/gsm_04_08.h b/include/openbsc/gsm_04_08.h
index 6d6ead183..ca251b00b 100644
--- a/include/openbsc/gsm_04_08.h
+++ b/include/openbsc/gsm_04_08.h
@@ -80,4 +80,6 @@ void allocate_security_operation(struct gsm_subscriber_connection *conn);
int gsm48_multirate_config(uint8_t *lv, const struct amr_multirate_conf *mr, const struct amr_mode *modes);
+int gsm48_tch_rtp_create(struct gsm_trans *trans);
+
#endif
diff --git a/include/openbsc/msc_ifaces.h b/include/openbsc/msc_ifaces.h
index 620859b7c..a1071ae9b 100644
--- a/include/openbsc/msc_ifaces.h
+++ b/include/openbsc/msc_ifaces.h
@@ -39,3 +39,4 @@ int msc_tx_common_id(struct gsm_subscriber_connection *conn);
int msc_call_assignment(struct gsm_trans *trans);
int msc_call_bridge(struct gsm_trans *trans1, struct gsm_trans *trans2);
void msc_call_release(struct gsm_trans *trans);
+int msc_call_connect(struct gsm_trans *trans, uint16_t port, uint32_t ip);
diff --git a/include/openbsc/transaction.h b/include/openbsc/transaction.h
index 4ed1d3ad4..4930fbd32 100644
--- a/include/openbsc/transaction.h
+++ b/include/openbsc/transaction.h
@@ -49,6 +49,13 @@ struct gsm_trans {
/* bearer capabilities (rate and codec) */
struct gsm_mncc_bearer_cap bearer_cap;
+ /* status of the assignment, true when done */
+ bool assignment_done;
+
+ /* if true, TCH_RTP_CREATE is sent after the
+ * assignment is done */
+ bool tch_rtp_create;
+
union {
struct {
diff --git a/src/libmsc/gsm_04_08.c b/src/libmsc/gsm_04_08.c
index 0909a1b02..2070da9a8 100644
--- a/src/libmsc/gsm_04_08.c
+++ b/src/libmsc/gsm_04_08.c
@@ -1257,6 +1257,8 @@ static int mncc_recvmsg(struct gsm_network *net, struct gsm_trans *trans,
struct msgb *msg;
unsigned char *data;
+ DEBUGP(DMNCC, "transmit message %s\n", get_mncc_name(msg_type));
+
#if BEFORE_MSCSPLIT
/* Re-enable this log output once we can obtain this information via
* A-interface, see OS#2391. */
@@ -1681,6 +1683,7 @@ static int gsm48_cc_rx_call_conf(struct gsm_trans *trans, struct msgb *msg)
unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
struct tlv_parsed tp;
struct gsm_mncc call_conf;
+ int rc;
gsm48_stop_cc_timer(trans);
gsm48_start_cc_timer(trans, 0x310, GSM48_T310);
@@ -1726,7 +1729,18 @@ static int gsm48_cc_rx_call_conf(struct gsm_trans *trans, struct msgb *msg)
new_cc_state(trans, GSM_CSTATE_MO_TERM_CALL_CONF);
- msc_call_assignment(trans);
+ /* Assign call (if not done yet) */
+ if (trans->assignment_done == false) {
+ rc = msc_call_assignment(trans);
+ trans->assignment_done = true;
+ }
+ else
+ rc = 0;
+
+ /* don't continue, if there were problems with
+ * the call assignment. */
+ if (rc)
+ return rc;
return mncc_recvmsg(trans->net, trans, MNCC_CALL_CONF_IND,
&call_conf);
@@ -1757,7 +1771,15 @@ static int gsm48_cc_tx_call_proc_and_assign(struct gsm_trans *trans, void *arg)
if (rc)
return rc;
- return msc_call_assignment(trans);
+ /* Assign call (if not done yet) */
+ if (trans->assignment_done == false) {
+ rc = msc_call_assignment(trans);
+ trans->assignment_done = true;
+ }
+ else
+ rc = 0;
+
+ return rc;
}
static int gsm48_cc_rx_alerting(struct gsm_trans *trans, struct msgb *msg)
@@ -2605,6 +2627,139 @@ static int gsm48_cc_rx_userinfo(struct gsm_trans *trans, struct msgb *msg)
return mncc_recvmsg(trans->net, trans, MNCC_USERINFO_IND, &user);
}
+static void mncc_recv_rtp(struct gsm_network *net, uint32_t callref,
+ int cmd, uint32_t addr, uint16_t port, uint32_t payload_type,
+ uint32_t payload_msg_type)
+{
+ uint8_t data[sizeof(struct gsm_mncc)];
+ struct gsm_mncc_rtp *rtp;
+
+ memset(&data, 0, sizeof(data));
+ rtp = (struct gsm_mncc_rtp *) &data[0];
+
+ rtp->callref = callref;
+ rtp->msg_type = cmd;
+ rtp->ip = addr;
+ rtp->port = port;
+ rtp->payload_type = payload_type;
+ rtp->payload_msg_type = payload_msg_type;
+ mncc_recvmsg(net, NULL, cmd, (struct gsm_mncc *)data);
+}
+
+static void mncc_recv_rtp_sock(struct gsm_network *net, struct gsm_trans *trans, int cmd)
+{
+ int msg_type;
+
+ /* FIXME This has to be set to some meaningful value.
+ * Possible options are:
+ * GSM_TCHF_FRAME, GSM_TCHF_FRAME_EFR,
+ * GSM_TCHH_FRAME, GSM_TCH_FRAME_AMR
+ * (0 if unknown) */
+ msg_type = GSM_TCHF_FRAME;
+
+ uint32_t addr = mgcpgw_client_remote_addr_n(net->mgcpgw.client);
+ uint16_t port = trans->conn->rtp.port_cn;
+
+ /* FIXME: This has to be set to some meaningful value,
+ * before the MSC-Split, this value was pulled from
+ * lchan->abis_ip.rtp_payload */
+ uint32_t payload_type = 0;
+
+ return mncc_recv_rtp(net, trans->callref, cmd,
+ addr,
+ port,
+ payload_type,
+ msg_type);
+}
+
+static void mncc_recv_rtp_err(struct gsm_network *net, uint32_t callref, int cmd)
+{
+ return mncc_recv_rtp(net, callref, cmd, 0, 0, 0, 0);
+}
+
+static int tch_rtp_create(struct gsm_network *net, uint32_t callref)
+{
+ struct gsm_trans *trans;
+ int rc;
+
+ /* Find callref */
+ trans = trans_find_by_callref(net, callref);
+ if (!trans) {
+ LOGP(DMNCC, LOGL_ERROR, "RTP create for non-existing trans\n");
+ mncc_recv_rtp_err(net, callref, MNCC_RTP_CREATE);
+ return -EIO;
+ }
+ log_set_context(LOG_CTX_VLR_SUBSCR, trans->vsub);
+ if (!trans->conn) {
+ LOGP(DMNCC, LOGL_NOTICE, "RTP create for trans without conn\n");
+ mncc_recv_rtp_err(net, callref, MNCC_RTP_CREATE);
+ return 0;
+ }
+
+ trans->conn->mncc_rtp_bridge = 1;
+
+ /* When we call msc_call_assignment() we will trigger, depending
+ * on the RAN type the call assignment on the A or Iu interface.
+ * msc_call_assignment() also takes care about sending the CRCX
+ * command to the MGCP-GW. The CRCX will return the port number,
+ * where the PBX (e.g. Asterisk) will send its RTP stream to. We
+ * have to return this port number back to the MNCC by sending
+ * it back with the TCH_RTP_CREATE message. To make sure that
+ * this message is sent AFTER the response to CRCX from the
+ * MGCP-GW has arrived, we need will instruct msc_call_assignment()
+ * to take care of this by setting trans->tch_rtp_create to true.
+ * This will make sure that gsm48_tch_rtp_create() (below) is
+ * called as soon as the local port number has become known. */
+ trans->tch_rtp_create = true;
+
+ /* Assign call (if not done yet) */
+ if (trans->assignment_done == false) {
+ rc = msc_call_assignment(trans);
+ trans->assignment_done = true;
+ }
+ else
+ rc = 0;
+
+ return rc;
+}
+
+/* Trigger TCH_RTP_CREATE acknowledgement */
+int gsm48_tch_rtp_create(struct gsm_trans *trans)
+{
+ /* This function is called as soon as the port, on which the
+ * mgcp-gw expects the incoming RTP stream from the remote
+ * end (e.g. Asterisk) is known. */
+
+ struct gsm_subscriber_connection *conn = trans->conn;
+ struct gsm_network *network = conn->network;
+
+ mncc_recv_rtp_sock(network, trans, MNCC_RTP_CREATE);
+ return 0;
+}
+
+static int tch_rtp_connect(struct gsm_network *net, void *arg)
+{
+ struct gsm_trans *trans;
+ struct gsm_mncc_rtp *rtp = arg;
+
+ /* Find callref */
+ trans = trans_find_by_callref(net, rtp->callref);
+ if (!trans) {
+ LOGP(DMNCC, LOGL_ERROR, "RTP connect for non-existing trans\n");
+ mncc_recv_rtp_err(net, rtp->callref, MNCC_RTP_CONNECT);
+ return -EIO;
+ }
+ log_set_context(LOG_CTX_VLR_SUBSCR, trans->vsub);
+ if (!trans->conn) {
+ LOGP(DMNCC, LOGL_ERROR, "RTP connect for trans without conn\n");
+ mncc_recv_rtp_err(net, rtp->callref, MNCC_RTP_CONNECT);
+ return 0;
+ }
+
+ msc_call_connect(trans,rtp->port,rtp->ip);
+ return 0;
+}
+
static struct downstate {
uint32_t states;
int type;
@@ -2680,11 +2835,16 @@ int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg)
if (rc < 0)
disconnect_bridge(net, arg, -rc);
return rc;
- case MNCC_FRAME_DROP:
- case MNCC_FRAME_RECV:
case MNCC_RTP_CREATE:
+ return tch_rtp_create(net, data->callref);
case MNCC_RTP_CONNECT:
+ return tch_rtp_connect(net, arg);
case MNCC_RTP_FREE:
+ /* unused right now */
+ return -EIO;
+
+ case MNCC_FRAME_DROP:
+ case MNCC_FRAME_RECV:
case GSM_TCHF_FRAME:
case GSM_TCHF_FRAME_EFR:
case GSM_TCHH_FRAME:
diff --git a/src/libmsc/msc_ifaces.c b/src/libmsc/msc_ifaces.c
index 7059dd000..05435c7d1 100644
--- a/src/libmsc/msc_ifaces.c
+++ b/src/libmsc/msc_ifaces.c
@@ -203,6 +203,12 @@ static void mgcp_response_rab_act_cs_crcx(struct mgcp_response *r, void *priv)
} else
goto rab_act_cs_error;
+ /* Respond back to MNCC (if requested) */
+ if (trans->tch_rtp_create) {
+ if (gsm48_tch_rtp_create(trans))
+ goto rab_act_cs_error;
+ }
+
rab_act_cs_error:
/* FIXME abort call, invalidate conn, ... */
return;
@@ -360,6 +366,51 @@ static void mgcp_response_bridge_mdcx(struct mgcp_response *r, void *priv)
}
}
+int msc_call_connect(struct gsm_trans *trans, uint16_t port, uint32_t ip)
+{
+ /* With this function we inform the MGCP-GW where (ip/port) it
+ * has to send its outgoing voic traffic. The receiving end will
+ * usually be a PBX (e.g. Asterisk). The IP-Address we tell, will
+ * not only be used to direct the traffic, it will also be used
+ * as a filter to make sure only RTP packets from the right
+ * remote end will reach the BSS. This is also the reason why
+ * inbound audio will not work until this step is performed */
+
+ /* NOTE: This function is used when msc_call_bridge(), is not
+ * applicable. This is usually the case when an external MNCC
+ * is in use */
+
+ struct gsm_subscriber_connection *conn;
+ struct mgcpgw_client *mgcp;
+ struct msgb *msg;
+
+ if (!trans)
+ return -EINVAL;
+ if (!trans->conn)
+ return -EINVAL;
+ if (!trans->conn->network)
+ return -EINVAL;
+ if (!trans->conn->network->mgcpgw.client)
+ return -EINVAL;
+
+ mgcp = trans->conn->network->mgcpgw.client;
+
+ struct in_addr ip_addr;
+ ip_addr.s_addr = ntohl(ip);
+
+ conn = trans->conn;
+
+ msg = mgcp_msg_mdcx(mgcp,
+ conn->iu.mgcp_rtp_endpoint,
+ inet_ntoa(ip_addr), port, MGCP_CONN_RECV_SEND);
+ if (mgcpgw_client_tx(mgcp, msg, NULL, trans))
+ LOGP(DMGCP, LOGL_ERROR,
+ "Failed to send MDCX message for %s\n",
+ vlr_subscr_name(trans->vsub));
+
+ return 0;
+}
+
int msc_call_bridge(struct gsm_trans *trans1, struct gsm_trans *trans2)
{
if (!trans1)