Hello Neels Hofmeyr, Jenkins Builder,

I'd like you to reexamine a change.  Please visit

    https://gerrit.osmocom.org/4980

to look at the new patch set (#10).

mgcp: use osmo-mgw to switch rtp streams

in the current implementation we still use osmo-bsc_mgcp, which
has many problems and is also obsoleted by osmo-mgw.

integrate osmo-mgw and re-implement the current switching using
an osmo fsm.

Depends: osmo-mgw Iab6a6038e7610c62f34e642cd49c93d11151252c
Closes: OS#2605
Change-Id: Ieea9630358b3963261fa1993cf1f3b563ff23538
---
M include/osmocom/msc/Makefile.am
M include/osmocom/msc/gsm_data.h
M include/osmocom/msc/iucs.h
M include/osmocom/msc/msc_ifaces.h
A include/osmocom/msc/msc_mgcp.h
M src/libmsc/Makefile.am
M src/libmsc/a_iface.c
M src/libmsc/a_iface_bssap.c
M src/libmsc/gsm_04_08.c
M src/libmsc/iucs.c
M src/libmsc/msc_ifaces.c
A src/libmsc/msc_mgcp.c
M tests/msc_vlr/Makefile.am
M tests/msc_vlr/msc_vlr_tests.c
14 files changed, 1,227 insertions(+), 321 deletions(-)


  git pull ssh://gerrit.osmocom.org:29418/osmo-msc refs/changes/80/4980/10

diff --git a/include/osmocom/msc/Makefile.am b/include/osmocom/msc/Makefile.am
index 1419e8e..052d830 100644
--- a/include/osmocom/msc/Makefile.am
+++ b/include/osmocom/msc/Makefile.am
@@ -39,6 +39,7 @@
        mncc.h \
        mncc_int.h \
        msc_ifaces.h \
+       msc_mgcp.h \
        network_listen.h \
        oap_client.h \
        openbscdefines.h \
diff --git a/include/osmocom/msc/gsm_data.h b/include/osmocom/msc/gsm_data.h
index 696cef1..9106421 100644
--- a/include/osmocom/msc/gsm_data.h
+++ b/include/osmocom/msc/gsm_data.h
@@ -196,9 +196,17 @@
        struct gsm_encr encr;
 
        struct {
+               struct mgcp_ctx *mgcp_ctx;
                unsigned int mgcp_rtp_endpoint;
-               uint16_t port_subscr;
-               uint16_t port_cn;
+
+               uint16_t local_port_ran;
+               char local_addr_ran[INET_ADDRSTRLEN];
+               uint16_t remote_port_ran;
+               char remote_addr_ran[INET_ADDRSTRLEN];
+               uint16_t local_port_cn;
+               char local_addr_cn[INET_ADDRSTRLEN];
+               uint16_t remote_port_cn;
+               char remote_addr_cn[INET_ADDRSTRLEN];
        } rtp;
 
        /* which Iu-CS connection, if any. */
diff --git a/include/osmocom/msc/iucs.h b/include/osmocom/msc/iucs.h
index b7d6064..a48a316 100644
--- a/include/osmocom/msc/iucs.h
+++ b/include/osmocom/msc/iucs.h
@@ -1,7 +1,10 @@
 #pragma once
 
+#include <osmocom/msc/transaction.h>
+
 int gsm0408_rcvmsg_iucs(struct gsm_network *network, struct msgb *msg,
                        uint16_t *lac);
 
 struct gsm_subscriber_connection *subscr_conn_lookup_iu(struct gsm_network 
*network,
                                                        struct 
ranap_ue_conn_ctx *ue);
+int iu_rab_act_cs(struct gsm_trans *trans);
diff --git a/include/osmocom/msc/msc_ifaces.h b/include/osmocom/msc/msc_ifaces.h
index 0592c07..ca25e9d 100644
--- a/include/osmocom/msc/msc_ifaces.h
+++ b/include/osmocom/msc/msc_ifaces.h
@@ -37,7 +37,3 @@
                             enum gsm48_reject_value value);
 
 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/osmocom/msc/msc_mgcp.h b/include/osmocom/msc/msc_mgcp.h
new file mode 100644
index 0000000..0ccd838
--- /dev/null
+++ b/include/osmocom/msc/msc_mgcp.h
@@ -0,0 +1,52 @@
+/* (C) 2017 by sysmocom - s.f.m.c. GmbH <i...@sysmocom.de>
+ * All Rights Reserved
+ *
+ * Author: Philipp Maier
+ *
+ * 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/>.
+ *
+ */
+
+#pragma once
+
+#include <osmocom/mgcp_client/mgcp_client.h>
+#include <osmocom/msc/gsm_data.h>
+
+/* MGCP state handler context (fsm etc..) */
+struct mgcp_ctx {
+       /* FSM instance, which handles the connection switching procedure */
+       struct osmo_fsm_inst *fsm;
+
+       /* RTP endpoint number */
+       uint16_t rtp_endpoint;
+
+       /* Set to true, when the context information is no longer
+        * needed */
+       bool free_ctx;
+
+       /* RTP connection identifiers */
+       char conn_id_ran[MGCP_CONN_ID_LENGTH];
+       char conn_id_cn[MGCP_CONN_ID_LENGTH];
+
+       /* Copy of the pointer and the data with context information
+        * needed to process the AoIP and MGCP requests (system data) */
+       struct mgcp_client *mgcp;
+       struct gsm_trans *trans;
+       mgcp_trans_id_t mgw_pending_trans;
+};
+
+int msc_mgcp_call_assignment(struct gsm_trans *trans);
+int msc_mgcp_ass_complete(struct gsm_subscriber_connection *conn, uint16_t 
port, char *addr);
+int msc_mgcp_call_complete(struct gsm_trans *trans, uint16_t port, char *addr);
+int msc_mgcp_call_release(struct gsm_trans *trans);
diff --git a/src/libmsc/Makefile.am b/src/libmsc/Makefile.am
index 998df26..2901df6 100644
--- a/src/libmsc/Makefile.am
+++ b/src/libmsc/Makefile.am
@@ -41,6 +41,7 @@
        mncc_builtin.c \
        mncc_sock.c \
        msc_ifaces.c \
+       msc_mgcp.c \
        rrlp.c \
        silent_call.c \
        sms_queue.c \
diff --git a/src/libmsc/a_iface.c b/src/libmsc/a_iface.c
index bdfef87..d117b31 100644
--- a/src/libmsc/a_iface.c
+++ b/src/libmsc/a_iface.c
@@ -408,8 +408,8 @@
        /* Package RTP-Address data */
        memset(&rtp_addr_in, 0, sizeof(rtp_addr_in));
        rtp_addr_in.sin_family = AF_INET;
-       rtp_addr_in.sin_port = osmo_htons(conn->rtp.port_subscr);
-       rtp_addr_in.sin_addr.s_addr = 
osmo_htonl(mgcp_client_remote_addr_n(gsm_network->mgw.client));
+       rtp_addr_in.sin_port = osmo_htons(conn->rtp.local_port_ran);
+       rtp_addr_in.sin_addr.s_addr = inet_addr(conn->rtp.local_addr_ran);
 
        memset(&rtp_addr, 0, sizeof(rtp_addr));
        memcpy(&rtp_addr, &rtp_addr_in, sizeof(rtp_addr_in));
diff --git a/src/libmsc/a_iface_bssap.c b/src/libmsc/a_iface_bssap.c
index 922dca9..d0b24eb 100644
--- a/src/libmsc/a_iface_bssap.c
+++ b/src/libmsc/a_iface_bssap.c
@@ -34,6 +34,7 @@
 #include <osmocom/core/byteswap.h>
 #include <osmocom/msc/a_reset.h>
 #include <osmocom/msc/transaction.h>
+#include <osmocom/msc/msc_mgcp.h>
 
 #include <errno.h>
 
@@ -596,11 +597,7 @@
         * transport address element */
        if (rtp_addr.ss_family == AF_INET) {
                rtp_addr_in = (struct sockaddr_in *)&rtp_addr;
-               conn->rtp.port_subscr = osmo_ntohs(rtp_addr_in->sin_port);
-               /* FIXME: We also get the IP-Address of the remote (e.g. BTS)
-                * end with the response. Currently we just ignore that address.
-                * Instead we expect that our local MGCP gateway and the code
-                * controlling it, magically knows the IP of the remote end. */
+               msc_mgcp_ass_complete(conn, osmo_ntohs(rtp_addr_in->sin_port), 
inet_ntoa(rtp_addr_in->sin_addr));
        } else {
                LOGP(DMSC, LOGL_ERROR, "Unsopported addressing scheme. 
(supports only IPV4)\n");
                goto fail;
diff --git a/src/libmsc/gsm_04_08.c b/src/libmsc/gsm_04_08.c
index d71b48b..57ed08c 100644
--- a/src/libmsc/gsm_04_08.c
+++ b/src/libmsc/gsm_04_08.c
@@ -70,6 +70,7 @@
 #include <osmocom/core/msgb.h>
 #include <osmocom/core/talloc.h>
 #include <osmocom/core/utils.h>
+#include <osmocom/core/byteswap.h>
 #include <osmocom/gsm/tlv.h>
 #include <osmocom/crypt/auth.h>
 #ifdef BUILD_IU
@@ -78,6 +79,7 @@
 
 #include <osmocom/msc/msc_ifaces.h>
 #include <osmocom/msc/a_iface.h>
+#include <osmocom/msc/msc_mgcp.h>
 
 #include <assert.h>
 
@@ -1328,8 +1330,8 @@
 {
        gsm48_stop_cc_timer(trans);
 
-       /* Make sure call also gets released on the mgcp side */
-       msc_call_release(trans);
+       /* Initiate the teadown of the related connections on the MGW */
+       msc_mgcp_call_release(trans);
 
        /* send release to L4, if callref still exists */
        if (trans->callref) {
@@ -1385,6 +1387,23 @@
        return 0;
 }
 
+/* helper function for tch_bridge() to bridge the RTP Voice streams also */
+static int rtp_bridge(struct gsm_trans *trans1, struct gsm_trans *trans2)
+{
+       int rc;
+       rc = msc_mgcp_call_complete(trans1, trans2->conn->rtp.local_port_cn,
+                                   trans2->conn->rtp.local_addr_cn);
+       if (rc)
+               return -EINVAL;
+
+       rc = msc_mgcp_call_complete(trans2, trans1->conn->rtp.local_port_cn,
+                                   trans1->conn->rtp.local_addr_cn);
+       if (rc)
+               return -EINVAL;
+
+       return 0;
+}
+
 /* bridge channels of two transactions */
 static int tch_bridge(struct gsm_network *net, struct gsm_mncc_bridge *bridge)
 {
@@ -1400,7 +1419,7 @@
        /* Which subscriber do we want to track trans1 or trans2? */
        log_set_context(LOG_CTX_VLR_SUBSCR, trans1->vsub);
 
-       return msc_call_bridge(trans1, trans2);
+       return rtp_bridge(trans1, trans2);
 }
 
 static int gsm48_cc_rx_status_enq(struct gsm_trans *trans, struct msgb *msg)
@@ -1743,7 +1762,7 @@
 
        /* Assign call (if not done yet) */
        if (trans->assignment_done == false) {
-               rc = msc_call_assignment(trans);
+               rc = msc_mgcp_call_assignment(trans);
                trans->assignment_done = true;
        }
        else
@@ -1785,7 +1804,7 @@
 
        /* Assign call (if not done yet) */
        if (trans->assignment_done == false) {
-               rc = msc_call_assignment(trans);
+               rc = msc_mgcp_call_assignment(trans);
                trans->assignment_done = true;
        }
        else
@@ -2669,8 +2688,8 @@
         * (0 if unknown) */
        msg_type = GSM_TCHF_FRAME;
 
-       uint32_t addr = mgcp_client_remote_addr_n(net->mgw.client);
-       uint16_t port = trans->conn->rtp.port_cn;
+       uint32_t addr = inet_addr(trans->conn->rtp.local_addr_cn);
+       uint16_t port = trans->conn->rtp.local_port_cn;
 
        /* FIXME: This has to be set to some meaningful value,
         * before the MSC-Split, this value was pulled from
@@ -2710,15 +2729,15 @@
 
        trans->conn->mncc_rtp_bridge = 1;
 
-       /* When we call msc_call_assignment() we will trigger, depending
+       /* When we call msc_mgcp_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
+        * msc_mgcp_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()
+        * MGCP-GW has arrived, we need will instruct msc_mgcp_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. */
@@ -2726,7 +2745,7 @@
 
        /* Assign call (if not done yet) */
        if (trans->assignment_done == false) {
-               rc = msc_call_assignment(trans);
+               rc = msc_mgcp_call_assignment(trans);
                trans->assignment_done = true;
        }
        else
@@ -2753,6 +2772,7 @@
 {
        struct gsm_trans *trans;
        struct gsm_mncc_rtp *rtp = arg;
+       struct in_addr addr;
 
        /* Find callref */
        trans = trans_find_by_callref(net, rtp->callref);
@@ -2768,8 +2788,8 @@
                return 0;
        }
 
-       msc_call_connect(trans, rtp->port, rtp->ip);
-       return 0;
+       addr.s_addr = osmo_htonl(rtp->ip);
+       return msc_mgcp_call_complete(trans, rtp->port, inet_ntoa(addr));
 }
 
 static struct downstate {
diff --git a/src/libmsc/iucs.c b/src/libmsc/iucs.c
index c89e412..736ed0b 100644
--- a/src/libmsc/iucs.c
+++ b/src/libmsc/iucs.c
@@ -30,8 +30,21 @@
 
 #include <osmocom/msc/gsm_data.h>
 #include <osmocom/msc/gsm_subscriber.h>
+#include <osmocom/msc/transaction.h>
 #include <osmocom/msc/osmo_msc.h>
 #include <osmocom/msc/vlr.h>
+
+#include "../../bscconfig.h"
+
+#ifdef BUILD_IU
+#include <osmocom/ranap/iu_client.h>
+extern struct msgb *ranap_new_msg_rab_assign_voice(uint8_t rab_id,
+                                                  uint32_t rtp_ip,
+                                                  uint16_t rtp_port,
+                                                  bool use_x213_nsap);
+#else
+#include <osmocom/msc/iu_dummy.h>
+#endif /* BUILD_IU */
 
 /* For A-interface see libbsc/bsc_api.c subscr_con_allocate() */
 static struct gsm_subscriber_connection *subscr_conn_allocate_iu(struct 
gsm_network *network,
@@ -187,3 +200,39 @@
 
        return rc;
 }
+
+int iu_rab_act_cs(struct gsm_trans *trans)
+{
+       struct gsm_subscriber_connection *conn;
+       struct msgb *msg;
+       bool use_x213_nsap;
+       uint32_t conn_id;
+       struct ranap_ue_conn_ctx *uectx;
+       uint8_t rab_id;
+       uint32_t rtp_ip;
+       uint16_t rtp_port;
+
+       conn = trans->conn;
+       uectx = conn->iu.ue_ctx;
+       rab_id = conn->iu.rab_id;
+       rtp_ip = inet_addr(conn->rtp.local_addr_ran);
+       rtp_port = conn->rtp.local_port_ran;
+       conn_id = uectx->conn_id;
+
+       use_x213_nsap = (uectx->rab_assign_addr_enc == 
RANAP_NSAP_ADDR_ENC_X213);
+
+       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;
+
+       if (ranap_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);
+       return 0;
+}
+
diff --git a/src/libmsc/msc_ifaces.c b/src/libmsc/msc_ifaces.c
index e29fe0e..437d75c 100644
--- a/src/libmsc/msc_ifaces.c
+++ b/src/libmsc/msc_ifaces.c
@@ -29,15 +29,12 @@
 #include <osmocom/msc/vlr.h>
 #include <osmocom/msc/a_iface.h>
 #include <osmocom/msc/gsm_04_08.h>
+#include <osmocom/msc/msc_mgcp.h>
 
 #include "../../bscconfig.h"
 
 #ifdef BUILD_IU
 #include <osmocom/ranap/iu_client.h>
-extern struct msgb *ranap_new_msg_rab_assign_voice(uint8_t rab_id,
-                                                  uint32_t rtp_ip,
-                                                  uint16_t rtp_port,
-                                                  bool use_x213_nsap);
 #else
 #include <osmocom/msc/iu_dummy.h>
 #endif /* BUILD_IU */
@@ -140,287 +137,4 @@
        DEBUGP(DIUCS, "%s: tx CommonID %s\n",
               vlr_subscr_name(conn->vsub), conn->vsub->imsi);
        return ranap_iu_tx_common_id(conn->iu.ue_ctx, conn->vsub->imsi);
-}
-
-static int iu_rab_act_cs(struct ranap_ue_conn_ctx *uectx, uint8_t rab_id,
-                        uint32_t rtp_ip, uint16_t rtp_port)
-{
-#ifdef BUILD_IU
-       struct msgb *msg;
-       bool use_x213_nsap;
-       uint32_t conn_id = uectx->conn_id;
-
-       use_x213_nsap = (uectx->rab_assign_addr_enc == 
RANAP_NSAP_ADDR_ENC_X213);
-
-       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;
-
-       if (ranap_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);
-       return 0;
-#else
-       LOGP(DMSC, LOGL_ERROR, "Cannot send Iu RAB Assignment: built without Iu 
support\n");
-       return -ENOTSUP;
-#endif
-}
-
-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;
-       uint32_t rtp_ip;
-       int rc;
-
-       if (r->head.response_code != 200) {
-               LOGP(DMGCP, LOGL_ERROR,
-                    "MGCPGW response yields error: %d %s\n",
-                    r->head.response_code, r->head.comment);
-               goto rab_act_cs_error;
-       }
-
-       rc = mgcp_response_parse_params(r);
-       if (rc) {
-               LOGP(DMGCP, LOGL_ERROR,
-                    "Cannot parse MGCP response, for %s\n",
-                    vlr_subscr_name(trans->vsub));
-               goto rab_act_cs_error;
-       }
-
-       conn->rtp.port_cn = r->audio_port;
-
-       rtp_ip = mgcp_client_remote_addr_n(conn->network->mgw.client);
-
-       if (trans->conn->via_ran == RAN_UTRAN_IU) {
-               /* Assign a voice channel via RANAP on 3G */
-               if (iu_rab_act_cs(conn->iu.ue_ctx, conn->iu.rab_id, rtp_ip, 
conn->rtp.port_subscr))
-                       goto rab_act_cs_error;
-       } else if (trans->conn->via_ran == RAN_GERAN_A) {
-               /* Assign a voice channel via A on 2G */
-               if (a_iface_tx_assignment(trans))
-                       goto rab_act_cs_error;
-       } 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;
-       }
-       return;
-
-rab_act_cs_error:
-       /* FIXME abort call, invalidate conn, ... */
-       LOGP(DMSC, LOGL_ERROR, "%s: failure during assignment\n",
-            vlr_subscr_name(trans->vsub));
-       return;
-}
-
-int msc_call_assignment(struct gsm_trans *trans)
-{
-       struct gsm_subscriber_connection *conn;
-       struct mgcp_client *mgcp;
-       struct msgb *msg;
-       uint16_t bts_base;
-
-       if (!trans)
-               return -EINVAL;
-       if (!trans->conn)
-               return -EINVAL;
-
-       conn = trans->conn;
-       mgcp = conn->network->mgw.client;
-
-#ifdef BUILD_IU
-       /* FIXME: HACK. where to scope the RAB Id? At the conn / subscriber / 
ranap_ue_conn_ctx? */
-       static uint8_t next_iu_rab_id = 1;
-       if (conn->via_ran == RAN_UTRAN_IU)
-               conn->iu.rab_id = next_iu_rab_id ++;
-#endif
-
-       conn->rtp.mgcp_rtp_endpoint =
-               mgcp_client_next_endpoint(conn->network->mgw.client);
-
-       /* This will calculate the port we assign to the BTS via AoIP
-        * assignment command (or rab-assignment on 3G) The BTS will send
-        * its RTP traffic to that port on the MGCPGW side. The MGCPGW only
-        * gets the endpoint ID via the CRCX. It will do the same calculation
-        * on his side too to get knowledge of the rtp port. */
-       bts_base = mgcp_client_conf_actual(mgcp)->bts_base;
-       conn->rtp.port_subscr = bts_base + 2 * conn->rtp.mgcp_rtp_endpoint;
-
-       /* 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. */
-       msg = mgcp_msg_crcx(mgcp, conn->rtp.mgcp_rtp_endpoint,
-                           conn->rtp.mgcp_rtp_endpoint, MGCP_CONN_LOOPBACK);
-       return mgcp_client_tx(mgcp, msg, mgcp_response_rab_act_cs_crcx, 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 mgcp_client *mgcp = conn1->network->mgw.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 = mgcp_client_remote_addr_str(mgcp);
-
-       msg = mgcp_msg_mdcx(mgcp,
-                           conn1->rtp.mgcp_rtp_endpoint,
-                           ip, conn2->rtp.port_cn,
-                           mode);
-       if (mgcp_client_tx(mgcp, msg, mgcp_response_bridge_mdcx, from))
-               LOGP(DMGCP, LOGL_ERROR,
-                    "Failed to send MDCX message for %s\n",
-                    vlr_subscr_name(from->vsub));
-}
-
-static void mgcp_response_bridge_mdcx(struct mgcp_response *r, void *priv)
-{
-       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, vlr_subscr_name(trans->vsub));
-                       break;
-               }
-               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, vlr_subscr_name(trans->vsub));
-               break;
-       }
-}
-
-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 mgcp_client *mgcp;
-       struct msgb *msg;
-
-       if (!trans)
-               return -EINVAL;
-       if (!trans->conn)
-               return -EINVAL;
-       if (!trans->conn->network)
-               return -EINVAL;
-       if (!trans->conn->network->mgw.client)
-               return -EINVAL;
-
-       mgcp = trans->conn->network->mgw.client;
-
-       struct in_addr ip_addr;
-       ip_addr.s_addr = ntohl(ip);
-
-       conn = trans->conn;
-
-       msg = mgcp_msg_mdcx(mgcp,
-                           conn->rtp.mgcp_rtp_endpoint,
-                           inet_ntoa(ip_addr), port, MGCP_CONN_RECV_SEND);
-       if (mgcp_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)
-               return -EINVAL;
-       if (!trans2)
-               return -EINVAL;
-
-       /* First setup as loopback and configure the counterparts' endpoints,
-        * so that when transmission starts the originating addresses are
-        * already known to be valid. The mgcp 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;
-}
-
-void msc_call_release(struct gsm_trans *trans)
-{
-       struct msgb *msg;
-       struct gsm_subscriber_connection *conn;
-       struct mgcp_client *mgcp;
-
-       if (!trans)
-               return;
-       if (!trans->conn)
-               return;
-       if (!trans->conn->network)
-               return;
-
-       conn = trans->conn;
-       mgcp = conn->network->mgw.client;
-
-       /* Send DLCX */
-       msg = mgcp_msg_dlcx(mgcp, conn->rtp.mgcp_rtp_endpoint,
-                           conn->rtp.mgcp_rtp_endpoint);
-       if (mgcp_client_tx(mgcp, msg, NULL, NULL))
-               LOGP(DMGCP, LOGL_ERROR,
-                    "Failed to send DLCX message for %s\n",
-                    vlr_subscr_name(trans->vsub));
-
-       /* Release endpoint id */
-       mgcp_client_release_endpoint(conn->rtp.mgcp_rtp_endpoint, mgcp);
 }
diff --git a/src/libmsc/msc_mgcp.c b/src/libmsc/msc_mgcp.c
new file mode 100644
index 0000000..e6bd7bd
--- /dev/null
+++ b/src/libmsc/msc_mgcp.c
@@ -0,0 +1,1065 @@
+/* (C) 2017 by sysmocom - s.f.m.c. GmbH <i...@sysmocom.de>
+ * All Rights Reserved
+ *
+ * Author: Philipp Maier
+ *
+ * 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 <arpa/inet.h>
+
+#include <osmocom/mgcp_client/mgcp_client.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/timer.h>
+#include <osmocom/core/fsm.h>
+#include <osmocom/core/byteswap.h>
+#include <osmocom/msc/msc_mgcp.h>
+#include <osmocom/msc/debug.h>
+#include <osmocom/msc/transaction.h>
+#include <osmocom/msc/a_iface.h>
+#include <osmocom/msc/msc_ifaces.h>
+#include <osmocom/msc/gsm_04_08.h>
+#include <osmocom/msc/iucs.h>
+#include <osmocom/msc/vlr.h>
+
+#include "../../bscconfig.h"
+
+#define S(x)   (1 << (x))
+
+#define CONN_ID_RAN 1
+#define CONN_ID_CN 2
+
+#define MGCP_MGW_TIMEOUT 4     /* in seconds */
+#define MGCP_MGW_TIMEOUT_TIMER_NR 1
+#define MGCP_RAN_TIMEOUT 10    /* in seconds */
+#define MGCP_RAN_TIMEOUT_TIMER_NR 2
+#define MGCP_REL_TIMEOUT 60    /* in seconds */
+#define MGCP_REL_TIMEOUT_TIMER_NR 3
+#define MGCP_ASS_TIMEOUT 10    /* in seconds */
+#define MGCP_ASS_TIMEOUT_TIMER_NR 4
+
+#define MGCP_ENDPOINT_FORMAT "%x@mgw"
+
+/* Some internal cause codes to indicate fault
+ * condition inside the FSM */
+enum msc_mgcp_cause_code {
+       MGCP_ERR_MGW_FAIL,
+       MGCP_ERR_MGW_INVAL_RESP,
+       MGCP_ERR_MGW_TX_FAIL,
+       MGCP_ERR_UNEXP_TEARDOWN,
+       MGCP_ERR_UNSUPP_ADDR_FMT,
+       MGCP_ERR_RAN_TIMEOUT,
+       MGCP_ERR_ASS_TIMEOUT,
+       MGCP_ERR_NOMEM,
+       MGCP_ERR_ASSGMNT_FAIL
+};
+
+/* Human readable respresentation of the faul codes,
+ * will be displayed by handle_error() */
+static const struct value_string msc_mgcp_cause_codes_names[] = {
+       {MGCP_ERR_MGW_FAIL, "operation failed on MGW"},
+       {MGCP_ERR_MGW_INVAL_RESP, "invalid / unparseable response from MGW"},
+       {MGCP_ERR_MGW_TX_FAIL, "failed to transmit MGCP message to MGW"},
+       {MGCP_ERR_UNEXP_TEARDOWN, "unexpected connection teardown"},
+       {MGCP_ERR_UNSUPP_ADDR_FMT, "unsupported network address format used 
(RAN)"},
+       {MGCP_ERR_RAN_TIMEOUT, "call could not be completed in time (RAN)"},
+       {MGCP_ERR_ASS_TIMEOUT, "assignment could not be completed in time 
(RAN)"},
+       {MGCP_ERR_NOMEM, "out of memory"},
+       {MGCP_ERR_ASSGMNT_FAIL, "assignment failure (RAN)"},
+       {0, NULL}
+};
+
+enum fsm_msc_mgcp_states {
+       ST_CRCX_RAN,
+       ST_CRCX_CN,
+       ST_CRCX_COMPL,
+       ST_MDCX_CN,
+       ST_ASS_COMPL,
+       ST_MDCX_RAN,
+       ST_MDCX_COMPL,
+       ST_CALL,
+       ST_HALT,
+};
+
+enum msc_mgcp_fsm_evt {
+       /* Initial event: start off the state machine */
+       EV_INIT,
+
+       /* External event: Notify that the Assignment is complete and we
+        * may now forward IP/Port of the remote call leg to the MGW*/
+       EV_ASSIGN,
+
+       /* External event: Notify that the Call is complete and that the
+        * two half open connections on the MGW should now be connected */
+       EV_CONNECT,
+
+       /* External event: Notify that the call is over and the connections
+        * on the mgw shall be removed */
+       EV_TEARDOWN,
+
+       /* Internal event: The mgcp_gw has sent its CRCX response for
+        * the RAN side */
+       EV_CRCX_RAN_RESP,
+
+       /* Internal event: The mgcp_gw has sent its CRCX response for
+        * the CN side */
+       EV_CRCX_CN_RESP,
+
+       /* Internal event: The mgcp_gw has sent its MDCX response for
+        * the RAN side */
+       EV_MDCX_RAN_RESP,
+
+       /* Internal event: The mgcp_gw has sent its MDCX response for
+        * the CN side */
+       EV_MDCX_CN_RESP,
+
+       /* Internal event: The mgcp_gw has sent its DLCX response for
+        * the RAN and CN side */
+       EV_DLCX_ALL_RESP,
+};
+
+/* A general error handler function. On error we still have an interest to
+ * remove a half open connection (if possible). This function will execute
+ * a controlled jump to the DLCX phase. From there, the FSM will then just
+ * continue like the call were ended normally */
+static void handle_error(struct mgcp_ctx *mgcp_ctx, enum msc_mgcp_cause_code 
cause)
+{
+       struct osmo_fsm_inst *fi;
+
+       OSMO_ASSERT(mgcp_ctx);
+       fi = mgcp_ctx->fsm;
+       OSMO_ASSERT(fi);
+
+       LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR, "%s -- graceful shutdown...\n",
+                get_value_string(msc_mgcp_cause_codes_names, cause));
+
+       /* Set the VM into the state where it waits for the call end */
+       osmo_fsm_inst_state_chg(fi, ST_CALL, 0, 0);
+
+       /* Simulate the call end by sending a teardown event, so that
+        * the FSM proceeds directly with the DLCX */
+       osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_TEARDOWN, mgcp_ctx);
+}
+
+/* Timer callback to shut down in case of connectivity problems */
+static int fsm_timeout_cb(struct osmo_fsm_inst *fi)
+{
+       struct mgcp_ctx *mgcp_ctx = fi->priv;
+       struct mgcp_client *mgcp;
+
+       OSMO_ASSERT(mgcp_ctx);
+       mgcp = mgcp_ctx->mgcp;
+       OSMO_ASSERT(mgcp);
+
+       if (fi->T == MGCP_MGW_TIMEOUT_TIMER_NR) {
+               /* Note: We were unable to communicate with the MGW,
+                * unfortunately there is no meaningful action we can take
+                * now other than giving up. */
+
+               /* At least release the occupied endpoint ID */
+               mgcp_client_release_endpoint(mgcp_ctx->rtp_endpoint, mgcp);
+
+               /* Cancel the transaction that timed out */
+               mgcp_client_cancel(mgcp, mgcp_ctx->mgw_pending_trans);
+
+               /* Initiate self destruction of the FSM */
+               osmo_fsm_inst_state_chg(fi, ST_HALT, 0, 0);
+               osmo_fsm_inst_dispatch(fi, EV_TEARDOWN, mgcp_ctx);
+       } else if (fi->T == MGCP_RAN_TIMEOUT_TIMER_NR) {
+               /* Note: If the logic that controls the RAN is unable to
+                * negotiate a connection, we presumably still have a
+                * working connection to the MGW, we will try to
+                * shut down gracefully. */
+               handle_error(mgcp_ctx, MGCP_ERR_RAN_TIMEOUT);
+       } else if (fi->T == MGCP_REL_TIMEOUT_TIMER_NR) {
+               /* Note: Under normal conditions, the MSC logic should
+                * always command to release the call at some point.
+                * However, the release may be missing due to errors
+                * in the MSC logic and we may have reached ST_HALT
+                * because of cascading errors and timeouts. In this
+                * and only in this case we will allow ST_HALT to
+                * free all context information on its own authority. */
+               mgcp_ctx->free_ctx = true;
+
+               /* Initiate self destruction of the FSM */
+               osmo_fsm_inst_state_chg(fi, ST_HALT, 0, 0);
+               osmo_fsm_inst_dispatch(fi, EV_TEARDOWN, mgcp_ctx);
+       } else if (fi->T == MGCP_ASS_TIMEOUT_TIMER_NR) {
+               /* Note: There may be rare cases in which the MSC
+               * is unable to complete the call assignment */
+               handle_error(mgcp_ctx, MGCP_ERR_ASS_TIMEOUT);
+       } else {
+               /* Note: Ther must not be any unsolicited timers
+                * in this FSM. If so, we have serious problem. */
+               OSMO_ASSERT(false);
+       }
+
+       return 0;
+}
+
+static void mgw_crcx_ran_resp_cb(struct mgcp_response *r, void *priv);
+
+/* Callback for ST_CRCX_RAN: Send CRCX for RAN side to MGW */
+static void fsm_crcx_ran_cb(struct osmo_fsm_inst *fi, uint32_t event, void 
*data)
+{
+       struct mgcp_ctx *mgcp_ctx = data;
+       struct mgcp_client *mgcp;
+       struct mgcp_msg mgcp_msg;
+       struct msgb *msg;
+       int rc;
+
+       OSMO_ASSERT(mgcp_ctx);
+       mgcp = mgcp_ctx->mgcp;
+       OSMO_ASSERT(mgcp);
+
+       mgcp_ctx->rtp_endpoint = mgcp_client_next_endpoint(mgcp);
+
+       LOGPFSML(fi, LOGL_DEBUG,
+                "CRCX/RAN: creating connection for the RAN side on " "MGW 
endpoint:0x%x...\n", mgcp_ctx->rtp_endpoint);
+
+       /* Generate MGCP message string */
+       mgcp_msg = (struct mgcp_msg) {
+               .verb = MGCP_VERB_CRCX,
+               .presence = (MGCP_MSG_PRESENCE_ENDPOINT | 
MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_MODE),
+               .call_id = mgcp_ctx->rtp_endpoint,
+               .conn_mode = MGCP_CONN_LOOPBACK
+       };
+       if (snprintf(mgcp_msg.endpoint, MGCP_ENDPOINT_MAXLEN, 
MGCP_ENDPOINT_FORMAT, mgcp_ctx->rtp_endpoint) >=
+           MGCP_ENDPOINT_MAXLEN) {
+               handle_error(mgcp_ctx, MGCP_ERR_NOMEM);
+               return;
+       }
+       msg = mgcp_msg_gen(mgcp, &mgcp_msg);
+       OSMO_ASSERT(msg);
+
+       /* Transmit MGCP message to MGW */
+       mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
+       rc = mgcp_client_tx(mgcp, msg, mgw_crcx_ran_resp_cb, mgcp_ctx);
+       if (rc < 0) {
+               handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL);
+               return;
+       }
+
+       osmo_fsm_inst_state_chg(fi, ST_CRCX_CN, MGCP_MGW_TIMEOUT, 
MGCP_MGW_TIMEOUT_TIMER_NR);
+}
+
+/* Callback for MGCP-Client: handle response for RAN associated CRCX */
+static void mgw_crcx_ran_resp_cb(struct mgcp_response *r, void *priv)
+{
+       struct mgcp_ctx *mgcp_ctx = priv;
+       int rc;
+       struct gsm_trans *trans;
+       struct gsm_subscriber_connection *conn;
+
+       OSMO_ASSERT(mgcp_ctx);
+       trans = mgcp_ctx->trans;
+       OSMO_ASSERT(trans);
+       conn = trans->conn;
+       OSMO_ASSERT(conn);
+
+       if (r->head.response_code != 200) {
+               LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR,
+                        "CRCX/RAN: response yields error: %d %s\n", 
r->head.response_code, r->head.comment);
+               handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL);
+               return;
+       }
+
+       /* memorize connection identifier */
+       osmo_strlcpy(mgcp_ctx->conn_id_ran, r->head.conn_id, 
sizeof(mgcp_ctx->conn_id_ran));
+       LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "CRCX/RAN: MGW responded with CI: 
%s\n", mgcp_ctx->conn_id_ran);
+
+       rc = mgcp_response_parse_params(r);
+       if (rc) {
+               LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR, "CRCX/RAN: Cannot parse 
response\n");
+               handle_error(mgcp_ctx, MGCP_ERR_MGW_INVAL_RESP);
+               return;
+       }
+
+       LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "CRCX/BTS: MGW responded with 
address %s:%u\n", r->audio_ip, r->audio_port);
+
+       conn->rtp.local_port_ran = r->audio_port;
+       osmo_strlcpy(conn->rtp.local_addr_ran, r->audio_ip, 
sizeof(conn->rtp.local_addr_ran));
+
+       /* Notify the FSM that we got the response. */
+       osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_CRCX_RAN_RESP, mgcp_ctx);
+}
+
+static void mgw_crcx_cn_resp_cb(struct mgcp_response *r, void *priv);
+
+/* Callback for ST_CRCX_CN: check MGW response and send CRCX for CN side to 
MGW */
+static void fsm_crcx_cn_cb(struct osmo_fsm_inst *fi, uint32_t event, void 
*data)
+{
+       struct mgcp_ctx *mgcp_ctx = data;
+       struct mgcp_client *mgcp;
+       struct mgcp_msg mgcp_msg;
+       struct msgb *msg;
+       int rc;
+
+       OSMO_ASSERT(mgcp_ctx);
+       mgcp = mgcp_ctx->mgcp;
+       OSMO_ASSERT(mgcp);
+
+       switch (event) {
+       case EV_CRCX_RAN_RESP:
+               break;
+       default:
+               handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN);
+               return;
+       }
+
+       LOGPFSML(fi, LOGL_DEBUG,
+                "CRCX/CN creating connection for the CN side on MGW 
endpoint:0x%x...\n", mgcp_ctx->rtp_endpoint);
+
+       /* Generate MGCP message string */
+       mgcp_msg = (struct mgcp_msg) {
+               .verb = MGCP_VERB_CRCX,
+               .presence = (MGCP_MSG_PRESENCE_ENDPOINT | 
MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_MODE),
+               .call_id = mgcp_ctx->rtp_endpoint,
+               .conn_mode = MGCP_CONN_LOOPBACK
+       };
+       if (snprintf(mgcp_msg.endpoint, MGCP_ENDPOINT_MAXLEN, 
MGCP_ENDPOINT_FORMAT, mgcp_ctx->rtp_endpoint) >=
+           MGCP_ENDPOINT_MAXLEN) {
+               handle_error(mgcp_ctx, MGCP_ERR_NOMEM);
+               return;
+       }
+       msg = mgcp_msg_gen(mgcp, &mgcp_msg);
+       OSMO_ASSERT(msg);
+
+       /* Transmit MGCP message to MGW */
+       mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
+       rc = mgcp_client_tx(mgcp, msg, mgw_crcx_cn_resp_cb, mgcp_ctx);
+       if (rc < 0) {
+               handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL);
+               return;
+       }
+
+       osmo_fsm_inst_state_chg(fi, ST_CRCX_COMPL, MGCP_MGW_TIMEOUT, 
MGCP_MGW_TIMEOUT_TIMER_NR);
+}
+
+/* Callback for MGCP-Client: handle response for CN associated CRCX */
+static void mgw_crcx_cn_resp_cb(struct mgcp_response *r, void *priv)
+{
+       struct mgcp_ctx *mgcp_ctx = priv;
+       int rc;
+       struct gsm_trans *trans;
+       struct gsm_subscriber_connection *conn;
+
+       OSMO_ASSERT(mgcp_ctx);
+       trans = mgcp_ctx->trans;
+       OSMO_ASSERT(trans);
+       conn = trans->conn;
+       OSMO_ASSERT(conn);
+
+       if (r->head.response_code != 200) {
+               LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR,
+                        "CRCX/CN: response yields error: %d %s\n", 
r->head.response_code, r->head.comment);
+               handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL);
+               return;
+       }
+
+       /* memorize connection identifier */
+       osmo_strlcpy(mgcp_ctx->conn_id_cn, r->head.conn_id, 
sizeof(mgcp_ctx->conn_id_cn));
+       LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "CRCX/CN: MGW responded with CI: 
%s\n", mgcp_ctx->conn_id_cn);
+
+       rc = mgcp_response_parse_params(r);
+       if (rc) {
+               LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR, "CRCX/CN: Cannot parse 
response\n");
+               handle_error(mgcp_ctx, MGCP_ERR_MGW_INVAL_RESP);
+               return;
+       }
+
+       LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "CRCX/CN: MGW responded with 
address %s:%u\n", r->audio_ip, r->audio_port);
+
+       conn->rtp.local_port_cn = r->audio_port;
+       osmo_strlcpy(conn->rtp.local_addr_cn, r->audio_ip, 
sizeof(conn->rtp.local_addr_cn));
+
+       /* Notify the FSM that we got the response. */
+       osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_CRCX_CN_RESP, mgcp_ctx);
+}
+
+/* Callback for ST_CRCX_COMPL: check MGW response, start assignment */
+static void fsm_crcx_compl(struct osmo_fsm_inst *fi, uint32_t event, void 
*data)
+{
+       struct mgcp_ctx *mgcp_ctx = data;
+       struct gsm_trans *trans;
+       struct gsm_subscriber_connection *conn;
+
+       OSMO_ASSERT(mgcp_ctx);
+       trans = mgcp_ctx->trans;
+       OSMO_ASSERT(trans);
+       conn = trans->conn;
+       OSMO_ASSERT(conn);
+
+       switch (event) {
+       case EV_CRCX_CN_RESP:
+               break;
+       default:
+               handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN);
+               return;
+       }
+
+       /* Forward assignment request to A/RANAP */
+       if (conn->via_ran == RAN_UTRAN_IU) {
+#ifdef BUILD_IU
+               /* Assign a voice channel via RANAP on 3G */
+               if (iu_rab_act_cs(trans))
+                       goto error;
+#else
+               LOGPFSML(fi, LOGL_ERROR, "Cannot send Iu RAB Assignment: built 
without Iu support\n");
+               goto error;
+#endif
+       } else if (conn->via_ran == RAN_GERAN_A) {
+               /* Assign a voice channel via A on 2G */
+               if (a_iface_tx_assignment(trans))
+                       goto error;
+       } else {
+               /* Unset or unimplemented new RAN type */
+               LOGPFSML(fi, LOGL_ERROR, "Unknown RAN type: %d\n", 
conn->via_ran);
+               return;
+       }
+
+       /* Respond back to MNCC (if requested) */
+       if (trans->tch_rtp_create) {
+               if (gsm48_tch_rtp_create(trans))
+                       goto error;
+       }
+
+       /* Note: When we reach this point then the situation is basically that
+        * we have two sides connected, both are in loopback. The local ports
+        * of the side pointing towards the BSS should be already communicated
+        * and we are waiting now for the BSS to return with the assignment
+        * complete command. */
+       osmo_fsm_inst_state_chg(fi, ST_MDCX_CN, MGCP_RAN_TIMEOUT, 
MGCP_RAN_TIMEOUT_TIMER_NR);
+       return;
+
+error:
+       handle_error(mgcp_ctx, MGCP_ERR_ASSGMNT_FAIL);
+}
+
+static void mgw_mdcx_cn_resp_cb(struct mgcp_response *r, void *priv);
+
+/* Callback for ST_MDCX_CN: send MDCX for RAN side to MGW */
+static void fsm_mdcx_cn_cb(struct osmo_fsm_inst *fi, uint32_t event, void 
*data)
+{
+       struct mgcp_ctx *mgcp_ctx = data;
+       struct mgcp_client *mgcp;
+       struct gsm_trans *trans;
+       struct gsm_subscriber_connection *conn;
+       struct mgcp_msg mgcp_msg;
+       struct msgb *msg;
+       int rc;
+
+       OSMO_ASSERT(mgcp_ctx);
+       mgcp = mgcp_ctx->mgcp;
+       OSMO_ASSERT(mgcp);
+       trans = mgcp_ctx->trans;
+       OSMO_ASSERT(trans);
+       conn = trans->conn;
+       OSMO_ASSERT(conn);
+
+       switch (event) {
+       case EV_CONNECT:
+               break;
+       default:
+               handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN);
+               return;
+       }
+
+       LOGPFSML(fi, LOGL_DEBUG,
+                "MDCX/CN: completing connection for the CN side on MGW 
endpoint:0x%x, remote leg expects RTP input on address %s:%u\n",
+                mgcp_ctx->rtp_endpoint, conn->rtp.remote_addr_cn, 
conn->rtp.remote_port_cn);
+
+       /* Generate MGCP message string */
+       mgcp_msg = (struct mgcp_msg) {
+               .verb = MGCP_VERB_MDCX,
+               .presence = (MGCP_MSG_PRESENCE_ENDPOINT | 
MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_ID |
+                            MGCP_MSG_PRESENCE_CONN_MODE | 
MGCP_MSG_PRESENCE_AUDIO_IP |
+                            MGCP_MSG_PRESENCE_AUDIO_PORT),
+               .call_id = mgcp_ctx->rtp_endpoint,
+               .conn_id = mgcp_ctx->conn_id_cn,
+               .conn_mode = MGCP_CONN_RECV_SEND,
+               .audio_ip = conn->rtp.remote_addr_cn,
+               .audio_port = conn->rtp.remote_port_cn
+       };
+       if (snprintf(mgcp_msg.endpoint, MGCP_ENDPOINT_MAXLEN, 
MGCP_ENDPOINT_FORMAT, mgcp_ctx->rtp_endpoint) >=
+           MGCP_ENDPOINT_MAXLEN) {
+               handle_error(mgcp_ctx, MGCP_ERR_NOMEM);
+               return;
+       }
+       msg = mgcp_msg_gen(mgcp, &mgcp_msg);
+       OSMO_ASSERT(msg);
+
+       /* Transmit MGCP message to MGW */
+       mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
+       rc = mgcp_client_tx(mgcp, msg, mgw_mdcx_cn_resp_cb, mgcp_ctx);
+       if (rc < 0) {
+               handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL);
+               return;
+       }
+
+       osmo_fsm_inst_state_chg(fi, ST_ASS_COMPL, MGCP_MGW_TIMEOUT, 
MGCP_MGW_TIMEOUT_TIMER_NR);
+}
+
+/* Callback for MGCP-Client: handle response for CN associated CRCX */
+static void mgw_mdcx_cn_resp_cb(struct mgcp_response *r, void *priv)
+{
+       struct mgcp_ctx *mgcp_ctx = priv;
+
+       OSMO_ASSERT(mgcp_ctx);
+
+       if (r->head.response_code != 200) {
+               LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR,
+                        "MDCX/CN: response yields error: %d %s\n", 
r->head.response_code, r->head.comment);
+               handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL);
+               return;
+       }
+
+       /* Notify the FSM that we got the response. */
+       osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_MDCX_CN_RESP, mgcp_ctx);
+}
+
+/* Callback for ST_ASS_COMPL: wait for mgw response, check if assignment is 
complete */
+static void fsm_ass_compl_cb(struct osmo_fsm_inst *fi, uint32_t event, void 
*data)
+{
+       struct mgcp_ctx *mgcp_ctx = data;
+       struct gsm_subscriber_connection *conn;
+       struct gsm_trans *trans;
+
+       OSMO_ASSERT(mgcp_ctx);
+       trans = mgcp_ctx->trans;
+       OSMO_ASSERT(trans);
+       conn = trans->conn;
+       OSMO_ASSERT(conn);
+
+       switch (event) {
+       case EV_MDCX_CN_RESP:
+               break;
+       default:
+               handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN);
+               return;
+       }
+
+       /* Enter MDCX phase */
+       osmo_fsm_inst_state_chg(fi, ST_MDCX_RAN, MGCP_MGW_TIMEOUT, 
MGCP_MGW_TIMEOUT_TIMER_NR);
+
+       /* If we already have a valid remote port and IP-Address from the RAN 
side
+        * call leg, the assignment has been completed before we got here, so we
+        * may move on immediately */
+       if (conn->rtp.remote_port_ran != 0 || strlen(conn->rtp.remote_addr_ran) 
> 0)
+               osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_ASSIGN, mgcp_ctx);
+}
+
+static void mgw_mdcx_ran_resp_cb(struct mgcp_response *r, void *priv);
+
+/* Callback for ST_MDCX_RAN: wait for assignment completion, send MDCX for CN 
side to MGW */
+static void fsm_mdcx_ran_cb(struct osmo_fsm_inst *fi, uint32_t event, void 
*data)
+{
+       struct mgcp_ctx *mgcp_ctx = data;
+       struct mgcp_client *mgcp;
+       struct gsm_trans *trans;
+       struct gsm_subscriber_connection *conn;
+       struct mgcp_msg mgcp_msg;
+       struct msgb *msg;
+       int rc;
+
+       OSMO_ASSERT(mgcp_ctx);
+       mgcp = mgcp_ctx->mgcp;
+       OSMO_ASSERT(mgcp);
+       trans = mgcp_ctx->trans;
+       OSMO_ASSERT(trans);
+       conn = trans->conn;
+       OSMO_ASSERT(conn);
+
+       switch (event) {
+       case EV_ASSIGN:
+               break;
+       default:
+               handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN);
+               return;
+       }
+
+       LOGPFSML(fi, LOGL_DEBUG,
+                "MDCX/RAN: completing connection for the CN side on MGW 
endpoint:0x%x, RAN expects RTP input on address %s:%u\n",
+                mgcp_ctx->rtp_endpoint, conn->rtp.remote_addr_ran, 
conn->rtp.remote_port_ran);
+
+       /* Generate MGCP message string */
+       mgcp_msg = (struct mgcp_msg) {
+               .verb = MGCP_VERB_MDCX,
+               .presence = (MGCP_MSG_PRESENCE_ENDPOINT | 
MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_ID |
+                            MGCP_MSG_PRESENCE_CONN_MODE | 
MGCP_MSG_PRESENCE_AUDIO_IP |
+                            MGCP_MSG_PRESENCE_AUDIO_PORT),
+               .call_id = mgcp_ctx->rtp_endpoint,
+               .conn_id = mgcp_ctx->conn_id_ran,
+               .conn_mode = MGCP_CONN_RECV_SEND,
+               .audio_ip = conn->rtp.remote_addr_ran,
+               .audio_port = conn->rtp.remote_port_ran
+       };
+       if (snprintf(mgcp_msg.endpoint, MGCP_ENDPOINT_MAXLEN, 
MGCP_ENDPOINT_FORMAT, mgcp_ctx->rtp_endpoint) >=
+           MGCP_ENDPOINT_MAXLEN) {
+               handle_error(mgcp_ctx, MGCP_ERR_NOMEM);
+               return;
+       }
+       msg = mgcp_msg_gen(mgcp, &mgcp_msg);
+       OSMO_ASSERT(msg);
+
+       /* Transmit MGCP message to MGW */
+       mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
+       rc = mgcp_client_tx(mgcp, msg, mgw_mdcx_ran_resp_cb, mgcp_ctx);
+       if (rc < 0) {
+               handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL);
+               return;
+       }
+
+       osmo_fsm_inst_state_chg(fi, ST_MDCX_COMPL, MGCP_MGW_TIMEOUT, 
MGCP_MGW_TIMEOUT_TIMER_NR);
+}
+
+/* Callback for MGCP-Client: handle response for CN associated CRCX */
+static void mgw_mdcx_ran_resp_cb(struct mgcp_response *r, void *priv)
+{
+       struct mgcp_ctx *mgcp_ctx = priv;
+
+       OSMO_ASSERT(mgcp_ctx);
+
+       if (r->head.response_code != 200) {
+               LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR,
+                        "MDCX/RAN: response yields error: %d %s\n", 
r->head.response_code, r->head.comment);
+               handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL);
+               return;
+       }
+
+       /* Notify the FSM that we got the response. */
+       osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_MDCX_RAN_RESP, mgcp_ctx);
+}
+
+/* Callback for ST_MDCX_COMPL: check MGW response */
+static void fsm_mdcx_compl_cb(struct osmo_fsm_inst *fi, uint32_t event, void 
*data)
+{
+       struct mgcp_ctx *mgcp_ctx = data;
+       OSMO_ASSERT(mgcp_ctx);
+
+       switch (event) {
+       case EV_MDCX_RAN_RESP:
+               break;
+       default:
+               handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN);
+               return;
+       }
+
+       LOGPFSML(fi, LOGL_DEBUG, "call active, waiting for teardown...\n");
+       osmo_fsm_inst_state_chg(fi, ST_CALL, 0, 0);
+}
+
+static void mgw_dlcx_all_resp_cb(struct mgcp_response *r, void *priv);
+
+/* Callback for ST_CALL: call is active, send DLCX for both sides on teardown 
*/
+static void fsm_call_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+
+       struct mgcp_ctx *mgcp_ctx = (struct mgcp_ctx *)data;
+       struct mgcp_client *mgcp;
+       struct mgcp_msg mgcp_msg;
+       struct msgb *msg;
+       int rc;
+
+       OSMO_ASSERT(mgcp_ctx);
+       mgcp = mgcp_ctx->mgcp;
+       OSMO_ASSERT(mgcp);
+
+       LOGPFSML(fi, LOGL_DEBUG,
+                "DLCX: removing connection for the RAN and CN side on MGW 
endpoint:0x%x...\n", mgcp_ctx->rtp_endpoint);
+
+       /* We now relase the endpoint back to the pool in order to allow
+        * other connections to use this endpoint */
+       mgcp_client_release_endpoint(mgcp_ctx->rtp_endpoint, mgcp);
+
+       /* Generate MGCP message string */
+       mgcp_msg = (struct mgcp_msg) {
+               .verb = MGCP_VERB_DLCX,
+               .presence = (MGCP_MSG_PRESENCE_ENDPOINT | 
MGCP_MSG_PRESENCE_CALL_ID),
+               .call_id = mgcp_ctx->rtp_endpoint
+       };
+       if (snprintf(mgcp_msg.endpoint, MGCP_ENDPOINT_MAXLEN, 
MGCP_ENDPOINT_FORMAT, mgcp_ctx->rtp_endpoint) >=
+           MGCP_ENDPOINT_MAXLEN) {
+               handle_error(mgcp_ctx, MGCP_ERR_NOMEM);
+               return;
+       }
+       msg = mgcp_msg_gen(mgcp, &mgcp_msg);
+       OSMO_ASSERT(msg);
+
+       /* Transmit MGCP message to MGW */
+       mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
+       rc = mgcp_client_tx(mgcp, msg, mgw_dlcx_all_resp_cb, mgcp_ctx);
+       if (rc < 0) {
+               handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL);
+               return;
+       }
+
+       osmo_fsm_inst_state_chg(fi, ST_HALT, MGCP_MGW_TIMEOUT, 
MGCP_MGW_TIMEOUT_TIMER_NR);
+}
+
+/* Callback for MGCP-Client: handle response for CN associated CRCX */
+static void mgw_dlcx_all_resp_cb(struct mgcp_response *r, void *priv)
+{
+       struct mgcp_ctx *mgcp_ctx = priv;
+
+       OSMO_ASSERT(mgcp_ctx);
+
+       if (r->head.response_code != 200) {
+               LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR,
+                        "DLCX: response yields error: %d %s\n", 
r->head.response_code, r->head.comment);
+               handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL);
+               return;
+       }
+
+       /* Notify the FSM that we got the response. */
+       osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_DLCX_ALL_RESP, mgcp_ctx);
+}
+
+/* Callback for ST_HALT: Terminate the state machine */
+static void fsm_halt_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+       struct mgcp_ctx *mgcp_ctx = data;
+
+       OSMO_ASSERT(mgcp_ctx);
+
+       LOGPFSML(fi, LOGL_DEBUG, "state machine halted\n");
+
+       /* NOTE: We must not free the context information now, we have to
+        * wait until msc_mgcp_call_release() is called. Then we are sure
+        * that the logic controlling us is fully aware that the context
+        * information is freed. If we would free early now the controlling
+        * logic might mistakenly think that the context info is still alive,
+        * so lets keep the context info until we are explicitly asked for
+        * throwing it away. */
+       if (mgcp_ctx->free_ctx) {
+               osmo_fsm_inst_free(mgcp_ctx->fsm);
+               talloc_free(mgcp_ctx);
+               return;
+       }
+
+       osmo_fsm_inst_state_chg(fi, ST_HALT, MGCP_REL_TIMEOUT, 
MGCP_REL_TIMEOUT_TIMER_NR);
+}
+
+static struct osmo_fsm_state fsm_msc_mgcp_states[] = {
+
+       /* Startup state machine, send CRCX for RAN side. */
+       [ST_CRCX_RAN] = {
+                        .in_event_mask = S(EV_INIT),
+                        .out_state_mask = S(ST_HALT) | S(ST_CALL) | 
S(ST_CRCX_CN),
+                        .name = OSMO_STRINGIFY(ST_CRCX_RAN),
+                        .action = fsm_crcx_ran_cb,
+                        },
+       /* When the response to the CRCX is received, then proceed with sending
+          the CRCX for CN side */
+       [ST_CRCX_CN] = {
+                       .in_event_mask = S(EV_TEARDOWN) | S(EV_CRCX_RAN_RESP),
+                       .out_state_mask = S(ST_HALT) | S(ST_CALL) | 
S(ST_CRCX_COMPL),
+                       .name = OSMO_STRINGIFY(ST_CRCX_CN),
+                       .action = fsm_crcx_cn_cb,
+                       },
+       /* Complete the CRCX phase by starting the assignment. Depending on
+        * the RAT, this will either trigger an Assignment Request on the
+        * A-Interface or an RAB-Assignment on the IU-interface */
+       [ST_CRCX_COMPL] = {
+                          .in_event_mask = S(EV_TEARDOWN) | S(EV_CRCX_CN_RESP),
+                          .out_state_mask = S(ST_HALT) | S(ST_CALL) | 
S(ST_MDCX_CN),
+                          .name = OSMO_STRINGIFY(ST_CRCX_COMPL),
+                          .action = fsm_crcx_compl,
+                          },
+       /* Wait for MSC to complete the assignment request, when complete,
+        * we will enter the MDCX phaseby sending an MDCX for the CN side
+        * to the MGW */
+       [ST_MDCX_CN] = {
+                       .in_event_mask = S(EV_TEARDOWN) | S(EV_CONNECT),
+                       .out_state_mask = S(ST_HALT) | S(ST_CALL) | 
S(ST_ASS_COMPL),
+                       .name = OSMO_STRINGIFY(ST_MDCX_CN),
+                       .action = fsm_mdcx_cn_cb,
+                       },
+       /* Check if the assignment has been completed and the remote leg
+        * address/port combination has become valid. If not, wait until
+        * the assignment is completed */
+       [ST_ASS_COMPL] = {
+                        .in_event_mask = S(EV_TEARDOWN) | S(EV_MDCX_CN_RESP),
+                        .out_state_mask = S(ST_HALT) | S(ST_CALL) | 
S(ST_MDCX_RAN),
+                        .name = OSMO_STRINGIFY(ST_ASS_COMPL),
+                        .action = fsm_ass_compl_cb,
+                        },
+       /* When the response for the MDCX is received, send the MDCX for the
+        * RAN side to the MGW */
+       [ST_MDCX_RAN] = {
+                        .in_event_mask = S(EV_TEARDOWN) | S(EV_ASSIGN),
+                        .out_state_mask = S(ST_HALT) | S(ST_CALL) | 
S(ST_MDCX_COMPL),
+                        .name = OSMO_STRINGIFY(ST_MDCX_RAN),
+                        .action = fsm_mdcx_ran_cb,
+                        },
+       /* The MDCX phase is complete when the response is received from the
+        * MGW. The call is now active */
+       [ST_MDCX_COMPL] = {
+                          .in_event_mask = S(EV_TEARDOWN) | 
S(EV_MDCX_RAN_RESP),
+                          .out_state_mask = S(ST_HALT) | S(ST_CALL),
+                          .name = OSMO_STRINGIFY(ST_MDCX_COMPL),
+                          .action = fsm_mdcx_compl_cb,
+                          },
+       /* We are now in the active call phase, wait until the call is done
+        * and send a DLCX then to remove all connections from the MGW */
+       [ST_CALL] = {
+                    .in_event_mask = S(EV_TEARDOWN),
+                    .out_state_mask = S(ST_HALT),
+                    .name = OSMO_STRINGIFY(ST_CALL),
+                    .action = fsm_call_cb,
+                    },
+       /* When the MGW confirms that the connections are terminated, then halt
+        * the state machine. */
+       [ST_HALT] = {
+                    .in_event_mask = S(EV_TEARDOWN) | S(EV_DLCX_ALL_RESP),
+                    .out_state_mask = S(ST_HALT),
+                    .name = OSMO_STRINGIFY(ST_HALT),
+                    .action = fsm_halt_cb,
+                    },
+};
+
+/* State machine definition */
+static struct osmo_fsm fsm_msc_mgcp = {
+       .name = "MGW",
+       .states = fsm_msc_mgcp_states,
+       .num_states = ARRAY_SIZE(fsm_msc_mgcp_states),
+       .log_subsys = DMGCP,
+       .timer_cb = fsm_timeout_cb,
+};
+
+/* Notify that that a new call begins. This will create a connection for the
+ * RAN and the CN on the MGW.
+ * Parameter:
+ * trans: transaction context
+ * Returns -EINVAL on error, 0 on success */
+int msc_mgcp_call_assignment(struct gsm_trans *trans)
+{
+       struct mgcp_ctx *mgcp_ctx;
+       char name[32];
+       static bool fsm_registered = false;
+       struct gsm_subscriber_connection *conn;
+       struct mgcp_client *mgcp;
+
+       OSMO_ASSERT(trans);
+
+       if (!trans->conn) {
+               LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid conn, call 
assignment failed\n",
+                    vlr_subscr_name(trans->vsub));
+               return -EINVAL;
+       }
+
+       conn = trans->conn;
+       mgcp = conn->network->mgw.client;
+       OSMO_ASSERT(mgcp);
+
+       if (conn->rtp.mgcp_ctx) {
+               LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) double assignment 
detected, dropping...\n",
+                    vlr_subscr_name(trans->vsub));
+               return -EINVAL;
+       }
+
+#ifdef BUILD_IU
+       /* FIXME: HACK. where to scope the RAB Id? At the conn / subscriber / 
ranap_ue_conn_ctx? */
+       static uint8_t next_iu_rab_id = 1;
+       if (conn->via_ran == RAN_UTRAN_IU)
+               conn->iu.rab_id = next_iu_rab_id++;
+#endif
+
+       if (snprintf(name, sizeof(name), "MGW_%i", trans->transaction_id) >= 
sizeof(name))
+               return -EINVAL;
+
+       /* Register the fsm description (if not already done) */
+       if (fsm_registered == false) {
+               osmo_fsm_register(&fsm_msc_mgcp);
+               fsm_registered = true;
+       }
+
+       /* Allocate and configure a new fsm instance */
+       mgcp_ctx = talloc_zero(NULL, struct mgcp_ctx);
+       OSMO_ASSERT(mgcp_ctx);
+
+       mgcp_ctx->fsm = osmo_fsm_inst_alloc(&fsm_msc_mgcp, NULL, NULL, 
LOGL_DEBUG, name);
+       OSMO_ASSERT(mgcp_ctx->fsm);
+       mgcp_ctx->fsm->priv = mgcp_ctx;
+       mgcp_ctx->mgcp = mgcp;
+       mgcp_ctx->trans = trans;
+
+       /* start state machine */
+       OSMO_ASSERT(mgcp_ctx->fsm->state == ST_CRCX_RAN);
+       osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_INIT, mgcp_ctx);
+
+       conn->rtp.mgcp_ctx = mgcp_ctx;
+
+       LOGP(DMGCP, LOGL_DEBUG, "(subscriber:%s) call assignment initiated\n",
+            vlr_subscr_name(conn->vsub));
+
+       return 0;
+}
+
+/* Inform the FSM that the assignment (RAN connection) is now complete
+ * Parameter:
+ * conn: subscriber connection context
+ * port: port number of the remote leg
+ * addr: IP-address of the remote leg
+ * Returns -EINVAL on error, 0 on success */
+int msc_mgcp_ass_complete(struct gsm_subscriber_connection *conn, uint16_t 
port, char *addr)
+{
+       struct mgcp_ctx *mgcp_ctx;
+
+       if (port == 0) {
+               LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid remote call 
leg port, call completion failed\n",
+                    vlr_subscr_name(conn->vsub));
+               return -EINVAL;
+       }
+       if (!addr || strlen(addr) <= 0) {
+               LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) missing remote call 
leg address, call completion failed\n",
+                    vlr_subscr_name(conn->vsub));
+               return -EINVAL;
+       }
+       if (!conn) {
+               LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid conn, 
assignment completion failed\n",
+                    vlr_subscr_name(conn->vsub));
+               return -EINVAL;
+       }
+
+       mgcp_ctx = conn->rtp.mgcp_ctx;
+       if (!mgcp_ctx) {
+               LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid mgcp context, 
assignmnet completion failed.\n",
+                    vlr_subscr_name(conn->vsub));
+               return -EINVAL;
+       }
+
+       /* Memorize port and IP-Address of the remote RAN call leg. We need this
+        * information at latest when we enter the MDCX phase for the RAN side. 
*/
+       conn->rtp.remote_port_ran = port;
+       osmo_strlcpy(conn->rtp.remote_addr_ran, addr, 
sizeof(conn->rtp.remote_addr_ran));
+
+       /* Note: We only dispatch the event if we are really waiting for the
+        * assignment, if we are not yet waiting, there is no need to loudly
+        * broadcast an event that the all other states do not understand 
anyway */
+       if (mgcp_ctx->fsm->state == ST_MDCX_RAN)
+               osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_ASSIGN, mgcp_ctx);
+
+       return 0;
+}
+
+/* Make the connection of a previously assigned call complete
+ * Parameter:
+ * trans: transaction context
+ * port: port number of the remote leg
+ * addr: IP-address of the remote leg
+ * Returns -EINVAL on error, 0 on success */
+int msc_mgcp_call_complete(struct gsm_trans *trans, uint16_t port, char *addr)
+{
+       struct mgcp_ctx *mgcp_ctx;
+       struct gsm_subscriber_connection *conn;
+
+       OSMO_ASSERT(trans);
+       OSMO_ASSERT(addr);
+
+       if (port == 0) {
+               LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid remote call 
leg port, call completion failed\n",
+                    vlr_subscr_name(trans->vsub));
+               return -EINVAL;
+       }
+       if (!addr || strlen(addr) <= 0) {
+               LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) missing remote call 
leg address, call completion failed\n",
+                    vlr_subscr_name(trans->vsub));
+               return -EINVAL;
+       }
+       if (!trans->conn) {
+               LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid conn, call 
completion failed\n",
+                    vlr_subscr_name(trans->vsub));
+               return -EINVAL;
+       }
+       if (!trans->conn->rtp.mgcp_ctx) {
+               LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid mgcp context, 
call completion failed.\n",
+                    vlr_subscr_name(trans->vsub));
+               return -EINVAL;
+       }
+       if (!trans->conn->rtp.mgcp_ctx->fsm) {
+               LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) no FSM, call 
completion failed\n",
+                    vlr_subscr_name(trans->vsub));
+               return -EINVAL;
+       }
+
+       mgcp_ctx = trans->conn->rtp.mgcp_ctx;
+
+       /* The FSM should already have passed all CRCX phases and be ready to 
move
+        * on with the MDCX phases. */
+       if (mgcp_ctx->fsm->state != ST_MDCX_CN) {
+               LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid call state, 
call completion failed\n",
+                    vlr_subscr_name(trans->vsub));
+               return -EINVAL;
+       }
+
+       conn = trans->conn;
+       osmo_strlcpy(conn->rtp.remote_addr_cn, addr, 
sizeof(conn->rtp.remote_addr_cn));
+       conn->rtp.remote_port_cn = port;
+
+       osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_CONNECT, mgcp_ctx);
+
+       LOGP(DMGCP, LOGL_DEBUG, "(subscriber:%s) call completion initiated\n",
+            vlr_subscr_name(conn->vsub));
+
+       return 0;
+}
+
+/* Release ongoing call
+ * Parameter:
+ * trans: connection context
+ * Returns -EINVAL on error, 0 on success */
+int msc_mgcp_call_release(struct gsm_trans *trans)
+{
+       struct mgcp_ctx *mgcp_ctx;
+
+       if (!trans->conn) {
+               LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid conn, call 
release failed\n",
+                    vlr_subscr_name(trans->vsub));
+               return -EINVAL;
+       }
+       if (!trans->conn->rtp.mgcp_ctx) {
+               LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid mgcp context, 
call release failed.\n",
+                    vlr_subscr_name(trans->vsub));
+               return -EINVAL;
+       }
+       if (!trans->conn->rtp.mgcp_ctx->fsm) {
+               LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) no FSM, call release 
failed\n",
+                    vlr_subscr_name(trans->vsub));
+               return -EINVAL;
+       }
+
+       mgcp_ctx = trans->conn->rtp.mgcp_ctx;
+
+       /* Inform the FSM that as soon as it reaches ST_HALT it may free
+        * all context information immediately */
+       mgcp_ctx->free_ctx = true;
+
+       /* Initaite teardown, regardless of which state we are currently
+        * in */
+       osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_TEARDOWN, mgcp_ctx);
+
+       /* Prevent any further operation that is triggered from outside by
+        * overwriting the context pointer with NULL. The FSM will now
+        * take care for a graceful shutdown and when done it will free
+        * all related context information */
+       trans->conn->rtp.mgcp_ctx = NULL;
+
+       LOGP(DMGCP, LOGL_DEBUG, "(subscriber:%s) call release initiated\n",
+            vlr_subscr_name(trans->vsub));
+
+       return 0;
+}
diff --git a/tests/msc_vlr/Makefile.am b/tests/msc_vlr/Makefile.am
index 8bbe6cb..ac45b23 100644
--- a/tests/msc_vlr/Makefile.am
+++ b/tests/msc_vlr/Makefile.am
@@ -31,8 +31,8 @@
        -Wl,--wrap=msc_stop_paging \
        -Wl,--wrap=gsm340_gen_scts \
        -Wl,--wrap=RAND_bytes \
-       -Wl,--wrap=msc_call_assignment \
-       -Wl,--wrap=msc_call_release \
+       -Wl,--wrap=msc_mgcp_call_release \
+       -Wl,--wrap=msc_mgcp_call_assignment \
        $(NULL)
 
 LDADD = \
diff --git a/tests/msc_vlr/msc_vlr_tests.c b/tests/msc_vlr/msc_vlr_tests.c
index dd7196c..a6f6c69 100644
--- a/tests/msc_vlr/msc_vlr_tests.c
+++ b/tests/msc_vlr/msc_vlr_tests.c
@@ -556,18 +556,18 @@
        return 0;
 }
 
-/* override, requires '-Wl,--wrap=msc_call_assignment' */
-int __real_msc_call_assignment(struct gsm_trans *trans);
-int __wrap_msc_call_assignment(struct gsm_trans *trans)
+/* override, requires '-Wl,--wrap=msc_mgcp_call_assignment' */
+int __real_msc_mgcp_call_assignment(struct gsm_trans *trans);
+int __wrap_msc_mgcp_call_assignment(struct gsm_trans *trans)
 {
        log("MS <--Call Assignment-- MSC: subscr=%s callref=0x%x",
            vlr_subscr_name(trans->vsub), trans->callref);
        return 0;
 }
 
-/* override, requires '-Wl,--wrap=msc_call_release' */
-void __real_msc_call_release(struct gsm_trans *trans);
-void __wrap_msc_call_release(struct gsm_trans *trans)
+/* override, requires '-Wl,--wrap=msc_mgcp_call_release' */
+void __real_msc_mgcp_call_release(struct gsm_trans *trans);
+void __wrap_msc_mgcp_call_release(struct gsm_trans *trans)
 {
        log("MS <--Call Release-- MSC: subscr=%s callref=0x%x",
            vlr_subscr_name(trans->vsub), trans->callref);

-- 
To view, visit https://gerrit.osmocom.org/4980
To unsubscribe, visit https://gerrit.osmocom.org/settings

Gerrit-MessageType: newpatchset
Gerrit-Change-Id: Ieea9630358b3963261fa1993cf1f3b563ff23538
Gerrit-PatchSet: 10
Gerrit-Project: osmo-msc
Gerrit-Branch: master
Gerrit-Owner: dexter <pma...@sysmocom.de>
Gerrit-Reviewer: Harald Welte <lafo...@gnumonks.org>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: Neels Hofmeyr <nhofm...@sysmocom.de>

Reply via email to