Hello Neels Hofmeyr, Harald Welte, Jenkins Builder,

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

    https://gerrit.osmocom.org/4334

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

mgcp: use osmo-mgw to switch RTP streams

osmo-bsc currently negotiates the RTP stream directly with the
BTS and reports back the RTP IP/Port on the BTS. This works fine
for a single BTS, but for Handover the port/ip pointing to the
MSC side must not change, so an entity in between the BTSs and
the MSC is required.

Integrate the mgcp-client and use osmo-mgw to switch the RTP
streams.

TODO: Handover will not work yet, because there is no
functionality to update the connection with the port/ip of
the new BTS.

Depends: osmo-mgw Ib5fcc72775bf72b489ff79ade36fb345d8d20736
Depends: osmo-mgw I44b338b09de45e1675cedf9737fa72dde72e979a
Depends: osmo-mgw I29c5e2fb972896faeb771ba040f015592487fcbe

Change-Id: Ia2882b7ca31a3219c676986e85045fa08a425d7a
---
M configure.ac
M include/osmocom/bsc/Makefile.am
M include/osmocom/bsc/gsm_data.h
M include/osmocom/bsc/osmo_bsc.h
A include/osmocom/bsc/osmo_bsc_mgcp.h
M src/Makefile.am
M src/osmo-bsc/Makefile.am
M src/osmo-bsc/osmo_bsc_audio.c
M src/osmo-bsc/osmo_bsc_bssap.c
M src/osmo-bsc/osmo_bsc_main.c
A src/osmo-bsc/osmo_bsc_mgcp.c
M src/osmo-bsc/osmo_bsc_sigtran.c
M src/osmo-bsc/osmo_bsc_vty.c
13 files changed, 1,102 insertions(+), 65 deletions(-)


  git pull ssh://gerrit.osmocom.org:29418/osmo-bsc refs/changes/34/4334/10

diff --git a/configure.ac b/configure.ac
index 79523d1..862e5bc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -47,8 +47,9 @@
 PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 0.1.0)
 PKG_CHECK_MODULES(LIBOSMOSIGTRAN, libosmo-sigtran >= 0.8.0)
 PKG_CHECK_MODULES(LIBCRYPTO, libcrypto >= 0.9.5)
-PKG_CHECK_MODULES(LIBOSMOLEGACYMGCP, libosmo-legacy-mgcp >= 0.0.1)
 PKG_CHECK_MODULES(LIBOSMOSCCP, libosmo-sccp >= 0.0.2)
+PKG_CHECK_MODULES(LIBOSMOMGCPCLIENT, libosmo-mgcp-client >= 1.0.0)
+PKG_CHECK_MODULES(LIBOSMOLEGACYMGCP, libosmo-legacy-mgcp >= 1.0.0)
 
 dnl checks for header files
 AC_HEADER_STDC
@@ -135,7 +136,6 @@
     src/libfilter/Makefile
     src/libcommon-cs/Makefile
     src/osmo-bsc/Makefile
-    src/osmo-bsc_nat/Makefile
     src/ipaccess/Makefile
     src/utils/Makefile
     tests/Makefile
diff --git a/include/osmocom/bsc/Makefile.am b/include/osmocom/bsc/Makefile.am
index 8ad2b5d..1f7cd39 100644
--- a/include/osmocom/bsc/Makefile.am
+++ b/include/osmocom/bsc/Makefile.am
@@ -41,6 +41,7 @@
        openbscdefines.h \
        osmo_bsc.h \
        osmo_bsc_grace.h \
+       osmo_bsc_mgcp.h \
        osmo_bsc_rf.h \
        osmo_bsc_sigtran.h \
        bsc_msc_data.h \
diff --git a/include/osmocom/bsc/gsm_data.h b/include/osmocom/bsc/gsm_data.h
index 74298b8..b9427b8 100644
--- a/include/osmocom/bsc/gsm_data.h
+++ b/include/osmocom/bsc/gsm_data.h
@@ -479,6 +479,11 @@
        uint8_t t3212;
 
        struct {
+               struct mgcp_client_conf *conf;
+               struct mgcp_client *client;
+       } mgw;
+
+       struct {
                /* CS7 instance id number (set via VTY) */
                uint32_t cs7_instance;
                /* A list with the context information about
diff --git a/include/osmocom/bsc/osmo_bsc.h b/include/osmocom/bsc/osmo_bsc.h
index 5ebea50..8a5cd30 100644
--- a/include/osmocom/bsc/osmo_bsc.h
+++ b/include/osmocom/bsc/osmo_bsc.h
@@ -29,6 +29,20 @@
        uint32_t rtp_ip;
        int rtp_port;
 
+       /* RTP address of the remote end (assigned by MSC through assignment
+        * request) */
+       struct sockaddr_storage aoip_rtp_addr_remote;
+
+       /* Local RTP address (reported back to the MSC by us with the
+        * assignment complete message) */
+       struct sockaddr_storage aoip_rtp_addr_local;
+
+       /* storage to keep states of the MGCP connection handler, the
+        * handler is created when an assignment request is received
+        * and is terminated when the assignment complete message is
+        * sent */
+       struct mgcp_ctx *mgcp_ctx;
+
        /* for advanced ping/pong */
        int send_ping;
 
@@ -72,4 +86,6 @@
 
 struct llist_head *bsc_access_lists(void);
 
+int bssmap_send_aoip_ass_compl(struct gsm_lchan *lchan);
+
 #endif
diff --git a/include/osmocom/bsc/osmo_bsc_mgcp.h 
b/include/osmocom/bsc/osmo_bsc_mgcp.h
new file mode 100644
index 0000000..a3e13a4
--- /dev/null
+++ b/include/osmocom/bsc/osmo_bsc_mgcp.h
@@ -0,0 +1,46 @@
+/* (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
+
+/* 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;
+
+       /* 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 osmo_bsc_sccp_con *conn;
+       enum gsm48_chan_mode chan_mode;
+       bool full_rate;
+       struct gsm_lchan *lchan;
+       struct msgb *resp;
+};
+
+struct mgcp_ctx *mgcp_assignm_req(void *ctx, struct mgcp_client *mgcp, struct 
osmo_bsc_sccp_con *conn,
+                                 enum gsm48_chan_mode chan_mode, bool 
full_rate);
+void mgcp_clear_complete(struct mgcp_ctx *mgcp_ctx, struct msgb *resp);
+void mgcp_ass_complete(struct mgcp_ctx *mgcp_ctx, struct gsm_lchan *lchan);
+void mgcp_free_ctx(struct mgcp_ctx *mgcp_ctx);
diff --git a/src/Makefile.am b/src/Makefile.am
index d04f025..dd1ad3d 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -33,5 +33,4 @@
        utils \
        ipaccess \
        osmo-bsc \
-       osmo-bsc_nat \
        $(NULL)
diff --git a/src/osmo-bsc/Makefile.am b/src/osmo-bsc/Makefile.am
index dfc4def..7db698c 100644
--- a/src/osmo-bsc/Makefile.am
+++ b/src/osmo-bsc/Makefile.am
@@ -15,6 +15,7 @@
        $(COVERAGE_CFLAGS) \
        $(LIBOSMOABIS_CFLAGS) \
        $(LIBOSMOSIGTRAN_CFLAGS) \
+       $(LIBOSMOMGCPCLIENT_CFLAGS) \
        $(NULL)
 
 AM_LDFLAGS = \
@@ -30,6 +31,7 @@
        osmo_bsc_vty.c \
        osmo_bsc_api.c \
        osmo_bsc_grace.c \
+       osmo_bsc_mgcp.c \
        osmo_bsc_msc.c \
        osmo_bsc_sigtran.c \
        osmo_bsc_filter.c \
@@ -53,4 +55,5 @@
        $(COVERAGE_LDFLAGS) \
        $(LIBOSMOABIS_LIBS) \
        $(LIBOSMOSIGTRAN_LIBS) \
+       $(LIBOSMOMGCPCLIENT_LIBS) \
        $(NULL)
diff --git a/src/osmo-bsc/osmo_bsc_audio.c b/src/osmo-bsc/osmo_bsc_audio.c
index 94aa350..326703d 100644
--- a/src/osmo-bsc/osmo_bsc_audio.c
+++ b/src/osmo-bsc/osmo_bsc_audio.c
@@ -29,46 +29,9 @@
 #include <osmocom/gsm/gsm0808.h>
 #include <osmocom/gsm/gsm0808_utils.h>
 #include <osmocom/bsc/osmo_bsc_sigtran.h>
+#include <osmocom/bsc/osmo_bsc_mgcp.h>
 
 #include <arpa/inet.h>
-
-/* Generate and send assignment complete message */
-static int send_aoip_ass_compl(struct gsm_subscriber_connection *conn, struct 
gsm_lchan *lchan)
-{
-       struct msgb *resp;
-       struct sockaddr_storage rtp_addr;
-       struct sockaddr_in rtp_addr_in;
-       struct gsm0808_speech_codec sc;
-
-       OSMO_ASSERT(lchan->abis_ip.ass_compl.valid == true);
-
-       /* 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 = htons(lchan->abis_ip.bound_port);
-       rtp_addr_in.sin_addr.s_addr = htonl(lchan->abis_ip.bound_ip);
-       memset(&rtp_addr, 0, sizeof(rtp_addr));
-       memcpy(&rtp_addr, &rtp_addr_in, sizeof(rtp_addr_in));
-
-       /* Extrapolate speech codec from speech mode */
-       gsm0808_speech_codec_from_chan_type(&sc, 
lchan->abis_ip.ass_compl.speech_mode);
-
-       /* Generate message */
-       resp = gsm0808_create_ass_compl(lchan->abis_ip.ass_compl.rr_cause,
-                                       lchan->abis_ip.ass_compl.chosen_channel,
-                                       lchan->abis_ip.ass_compl.encr_alg_id,
-                                       lchan->abis_ip.ass_compl.speech_mode,
-                                       &rtp_addr,
-                                       &sc,
-                                       NULL);
-
-       if (!resp) {
-               LOGP(DMSC, LOGL_ERROR, "Failed to generate assignment completed 
message!\n"); \
-               return -EINVAL;
-       }
-
-       return osmo_bsc_sigtran_send(conn->sccp_con, resp);
-}
 
 static int handle_abisip_signal(unsigned int subsys, unsigned int signal,
                                 void *handler_data, void *signal_data)
@@ -124,11 +87,9 @@
                         * IPA based base stations. See also osmo_bsc_api.c,
                         * function bsc_assign_compl() */
                        LOGP(DMSC, LOGL_INFO, "Tx MSC ASSIGN COMPL 
(POSTPONED)\n");
-                       if (send_aoip_ass_compl(con, lchan) != 0)
-                               return -EINVAL;
+                       mgcp_ass_complete(con->sccp_con->mgcp_ctx, lchan);
                }
                break;
-       break;
        }
 
        return 0;
diff --git a/src/osmo-bsc/osmo_bsc_bssap.c b/src/osmo-bsc/osmo_bsc_bssap.c
index 4311250..d5aaab3 100644
--- a/src/osmo-bsc/osmo_bsc_bssap.c
+++ b/src/osmo-bsc/osmo_bsc_bssap.c
@@ -25,6 +25,7 @@
 #include <osmocom/bsc/debug.h>
 #include <osmocom/bsc/bsc_subscriber.h>
 #include <osmocom/legacy_mgcp/mgcp.h>
+#include <osmocom/bsc/osmo_bsc_mgcp.h>
 #include <osmocom/bsc/paging.h>
 #include <osmocom/bsc/gsm_04_08_utils.h>
 
@@ -321,14 +322,28 @@
                conn->conn = NULL;
        }
 
-       /* send the clear complete message */
+       /* generate the clear complete message */
        resp = gsm0808_create_clear_complete();
        if (!resp) {
                LOGP(DMSC, LOGL_ERROR, "Sending clear complete failed.\n");
                return -1;
        }
 
-       osmo_bsc_sigtran_send(conn, resp);
+       if (conn->mgcp_ctx) {
+               /* NOTE: This is the AoIP case, osmo-bsc has to negotiate with
+                * the MGCP-GW. For this an mgcp_ctx should be created that
+                * contains the FSM and some system data. When the connection
+                * is removed from the MGCP-GW, then osmo_bsc_sigtran_send()
+                * calls osmo_bsc_sigtran_send(). */
+               mgcp_clear_complete(conn->mgcp_ctx, resp);
+       } else {
+               /* NOTE: This is the SCCP-Lite case, since we do not handle
+                * the MGCP-GW switching ourselves, we may skip everything
+                * that is MGCP-GW related and sent the clear complete message
+                * directly */
+               osmo_bsc_sigtran_send(conn, resp);
+       }
+
        return 0;
 }
 
@@ -426,7 +441,6 @@
        int port, full_rate = -1;
        bool aoip = false;
        struct sockaddr_storage rtp_addr;
-       struct sockaddr_in *rtp_addr_in;
        struct gsm0808_channel_type ct;
        struct gsm0808_speech_codec_list scl;
        struct gsm0808_speech_codec_list *scl_ptr = NULL;
@@ -531,28 +545,39 @@
               get_value_string(gsm48_chan_mode_names, chan_mode),
               ct.ch_indctr, ct.ch_rate_type, osmo_hexdump(ct.perm_spch, 
ct.perm_spch_len));
 
-       if (aoip == false) {
-               /* map it to a MGCP Endpoint and a RTP port */
+       /* Forward the assingment request to lower layers */
+       if (aoip) {
+               /* Store network side RTP connection information, we will
+                * process this address later after we have established an RTP
+                * connection to the BTS. This is just for organizational
+                * reasons, functional wise it would not matter when exactly
+                * the network side RTP connection is made, as long it is made
+                * before we return with the assignment complete message. */
+               memcpy(&conn->aoip_rtp_addr_remote, &rtp_addr, 
sizeof(rtp_addr));
+
+               /* Create an assignment request using the MGCP fsm. This FSM
+                * is directly started when its created (now) and will also
+                * take care about the further processing (creating RTP
+                * endpoints, calling gsm0808_assign_req(), rsponding to
+                * the assignment request etc... */
+               conn->mgcp_ctx = mgcp_assignm_req(msc->network, 
msc->network->mgw.client, conn, chan_mode, full_rate);
+               if (!conn->mgcp_ctx) {
+                       LOGP(DMSC, LOGL_ERROR, "MGCP GW failure, rejecting 
assignment... (id=%i)\n", conn->conn_id);
+                       goto reject;
+               }
+
+               /* We now may return here, the FSM will do all further work */
+               return 0;
+       } else {
+               /* Note: In the sccp-lite case we to not perform any mgcp 
operation,
+                * (the MSC does that for us). We set conn->rtp_ip to 0 and 
check
+                * on this later. By this we know that we have to behave 
accordingly
+                * to sccp-lite. */
                port = mgcp_timeslot_to_endpoint(multiplex, timeslot);
                conn->rtp_port = rtp_calculate_port(port, msc->rtp_base);
                conn->rtp_ip = 0;
-       } else {
-               /* use address / port supplied with the AoIP
-                * transport address element */
-               if (rtp_addr.ss_family == AF_INET) {
-                       rtp_addr_in = (struct sockaddr_in *)&rtp_addr;
-                       conn->rtp_port = osmo_ntohs(rtp_addr_in->sin_port);
-                       memcpy(&conn->rtp_ip, &rtp_addr_in->sin_addr.s_addr,
-                              IP_V4_ADDR_LEN);
-                       conn->rtp_ip = osmo_ntohl(conn->rtp_ip);
-               } else {
-                       LOGP(DMSC, LOGL_ERROR,
-                            "Unsopported addressing scheme. (supports only 
IPV4)\n");
-                       goto reject;
-               }
+               return gsm0808_assign_req(conn->conn, chan_mode, full_rate);
        }
-
-       return gsm0808_assign_req(conn->conn, chan_mode, full_rate);
 
 reject:
        resp =
@@ -729,3 +754,39 @@
 
        return -1;
 }
+
+/* Generate and send assignment complete message */
+int bssmap_send_aoip_ass_compl(struct gsm_lchan *lchan)
+{
+       struct msgb *resp;
+       struct gsm0808_speech_codec sc;
+       struct gsm_subscriber_connection *conn;
+
+       conn = lchan->conn;
+
+       OSMO_ASSERT(lchan->abis_ip.ass_compl.valid);
+       OSMO_ASSERT(conn);
+       OSMO_ASSERT(conn->sccp_con);
+
+       LOGP(DMSC, LOGL_DEBUG, "Sending assignment complete message... 
(id=%i)\n", conn->sccp_con->conn_id);
+
+       /* Extrapolate speech codec from speech mode */
+       gsm0808_speech_codec_from_chan_type(&sc, 
lchan->abis_ip.ass_compl.speech_mode);
+
+       /* Generate message */
+       resp = gsm0808_create_ass_compl(lchan->abis_ip.ass_compl.rr_cause,
+                                       lchan->abis_ip.ass_compl.chosen_channel,
+                                       lchan->abis_ip.ass_compl.encr_alg_id,
+                                       lchan->abis_ip.ass_compl.speech_mode,
+                                       &conn->sccp_con->aoip_rtp_addr_local,
+                                       &sc,
+                                       NULL);
+
+       if (!resp) {
+               LOGP(DMSC, LOGL_ERROR, "Failed to generate assignment completed 
message! (id=%i)\n",
+                    conn->sccp_con->conn_id);
+               return -EINVAL;
+       }
+
+       return osmo_bsc_sigtran_send(conn->sccp_con, resp);
+}
diff --git a/src/osmo-bsc/osmo_bsc_main.c b/src/osmo-bsc/osmo_bsc_main.c
index 730e1db..5d25701 100644
--- a/src/osmo-bsc/osmo_bsc_main.c
+++ b/src/osmo-bsc/osmo_bsc_main.c
@@ -44,6 +44,7 @@
 #include <osmocom/abis/abis.h>
 
 #include <osmocom/sccp/sccp.h>
+#include <osmocom/mgcp_client/mgcp_client.h>
 
 #define _GNU_SOURCE
 #include <getopt.h>
@@ -206,6 +207,9 @@
                exit(1);
        }
 
+       bsc_gsmnet->mgw.conf = talloc_zero(bsc_gsmnet, struct mgcp_client_conf);
+       mgcp_client_conf_init(bsc_gsmnet->mgw.conf);
+
        bts_init();
        libosmo_abis_init(tall_bsc_ctx);
 
@@ -274,6 +278,15 @@
                }
        }
 
+       bsc_gsmnet->mgw.client = mgcp_client_init(bsc_gsmnet, 
bsc_gsmnet->mgw.conf);
+
+       if (mgcp_client_connect(bsc_gsmnet->mgw.client)) {
+               LOGP(DNM, LOGL_ERROR, "MGW connect failed at (%s:%u)\n",
+                    bsc_gsmnet->mgw.conf->remote_addr,
+                    bsc_gsmnet->mgw.conf->remote_port);
+               exit(1);
+       }
+
        if (osmo_bsc_sigtran_init(&bsc_gsmnet->bsc_data->mscs) != 0) {
                LOGP(DNM, LOGL_ERROR, "Failed to initalize sigtran 
backhaul.\n");
                exit(1);
diff --git a/src/osmo-bsc/osmo_bsc_mgcp.c b/src/osmo-bsc/osmo_bsc_mgcp.c
new file mode 100644
index 0000000..dbdd3c1
--- /dev/null
+++ b/src/osmo-bsc/osmo_bsc_mgcp.c
@@ -0,0 +1,921 @@
+/* (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 <osmocom/mgcp_client/mgcp_client.h>
+#include <osmocom/bsc/gsm_data.h>
+#include <osmocom/bsc/osmo_bsc_mgcp.h>
+#include <osmocom/bsc/debug.h>
+#include <osmocom/bsc/osmo_bsc.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/timer.h>
+#include <osmocom/core/fsm.h>
+#include <osmocom/bsc/osmo_bsc_sigtran.h>
+#include <osmocom/core/byteswap.h>
+#include <arpa/inet.h>
+
+#define CONN_ID_BTS 1
+#define CONN_ID_NET 2
+
+#define MGCP_MGW_TIMEOUT 4     /* in seconds */
+#define MGCP_MGW_TIMEOUT_TIMER_NR 1
+#define MGCP_BSS_TIMEOUT 4     /* in seconds */
+#define MGCP_BSS_TIMEOUT_TIMER_NR 2
+
+#define MGCP_ENDPOINT_FORMAT "%i@mgw"
+
+/* Some internal cause codes to indicate fault
+ * condition inside the FSM */
+enum int_cause_code {
+       MGCP_ERR_MGW_FAIL,
+       MGCP_ERR_MGW_INVAL_RESP,
+       MGCP_ERR_MGW_TX_FAIL,
+       MGCP_ERR_UNEXP_TEARDOWN,
+       MGCP_ERR_ASSGMNT_FAIL,
+       MGCP_ERR_UNSUPP_ADDR_FMT,
+       MGCP_ERR_BSS_TIMEOUT,
+       MGCP_ERR_NOMEM
+};
+
+/* Human readable respresentation of the faul codes,
+ * will be displayed by handle_error() */
+static const struct value_string int_cause_codes_str[] = {
+       {MGCP_ERR_MGW_FAIL, "operation failed on MGW"},
+       {MGCP_ERR_MGW_INVAL_RESP, "invalid / unparseable response from MGW"},
+       {MGCP_ERR_MGW_INVAL_RESP, "failed to transmit MGCP message to MGW"},
+       {MGCP_ERR_UNEXP_TEARDOWN, "unexpected connection teardown (BSS)"},
+       {MGCP_ERR_ASSGMNT_FAIL, "assignment failure (BSS)"},
+       {MGCP_ERR_UNSUPP_ADDR_FMT, "unsupported network address format used 
(BSS)"},
+       {MGCP_ERR_BSS_TIMEOUT, "assignment could not be completed in time 
(BSS)"},
+       {MGCP_ERR_NOMEM, "out of memory"},
+       {0, NULL}
+};
+
+enum fsm_bsc_mgcp_states {
+       ST_CRCX_BTS,
+       ST_ASSIGN_PROC,
+       ST_MDCX_BTS,
+       ST_CRCX_NET,
+       ST_ASSIGN_COMPL,
+       ST_DLCX,
+       ST_HALT
+};
+
+static const struct value_string fsm_bsc_mgcp_state_names[] = {
+       {ST_CRCX_BTS, "ST_CRCX_BTS (send CRCX for BTS)"},
+       {ST_ASSIGN_PROC, "ST_ASSIGN_PROC (continue assignment)"},
+       {ST_MDCX_BTS, "ST_MDCX_BTS (send MDCX for BTS)"},
+       {ST_CRCX_NET, "ST_CRCX_NET (send CRCX for NET)"},
+       {ST_ASSIGN_COMPL, "ST_ASSIGN_COMPL (complete assignment)"},
+       {ST_DLCX, "ST_DLCX (delete all rtp connections)"},
+       {ST_HALT, "ST_HALT (destroy state machine)"},
+       {0, NULL}
+};
+
+enum fsm_evt {
+       /* Initial event: start off the state machine */
+       EV_INIT,
+
+       /* External event: Assignment complete, event is issued shortly before
+        * the assignment complete message is sent via the A-Interface */
+       EV_ASS_COMPLETE,
+
+       /* External event: Teardown event, this event is used to notify the end
+        * of a. It is also issued in case of errors to teardown a half open
+        * connection. */
+       EV_TEARDOWN,
+
+       /* Internal event: The mgcp_gw has sent its CRCX response for
+        * the BTS side */
+       EV_CRCX_BTS_RESP,
+
+       /* Internal event: The mgcp_gw has sent its MDCX response for
+        * the BTS side */
+       EV_MDCX_BTS_RESP,
+
+       /* Internal event: The mgcp_gw has sent its CRCX response for
+        * the NET side */
+       EV_CRCX_NET_RESP,
+
+       /* Internal event: The mgcp_gw has sent its DLCX response for
+        * the NET and BTS side */
+       EV_DLCX_ALL_RESP,
+};
+
+static const struct value_string fsm_evt_names[] = {
+       {EV_INIT, "EV_INIT (start state machine (send CRCX for BTS)"},
+       {EV_ASS_COMPLETE, "EV_ASS_COMPLETE (assignment complete)"},
+       {EV_TEARDOWN, "EV_TEARDOWN (teardown all connections)"},
+       {EV_CRCX_BTS_RESP, "EV_CRCX_BTS_RESP (got CRCX reponse for BTS)"},
+       {EV_MDCX_BTS_RESP, "EV_MDCX_BTS_RESP (got MDCX reponse for BTS)"},
+       {EV_CRCX_NET_RESP, "EV_CRCX_NET_RESP (got CRCX reponse for NET)"},
+       {EV_DLCX_ALL_RESP, "EV_DLCX_ALL_RESP (got DLCX reponse for BTS/NET)"},
+       {0, NULL}
+};
+
+/* 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 int_cause_code cause)
+{
+       struct osmo_fsm_inst *fi;
+       struct osmo_bsc_sccp_con *conn;
+
+       OSMO_ASSERT(mgcp_ctx);
+       conn = mgcp_ctx->conn;
+       OSMO_ASSERT(conn);
+
+       fi = mgcp_ctx->fsm;
+       OSMO_ASSERT(fi);
+
+       LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "fsm-state: %s\n", 
get_value_string(fsm_bsc_mgcp_state_names, fi->state));
+
+       LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR, "%s -- graceful shutdown...\n",
+                get_value_string(int_cause_codes_str, cause));
+
+       /* Set the VM into the state where it waits for the call end */
+       osmo_fsm_inst_state_chg(fi, ST_DLCX, 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);
+}
+
+static void crcx_for_bts_resp_cb(struct mgcp_response *r, void *priv);
+
+/* Callback for ST_CRCX_BTS: startup state machine send out CRCX for BTS side 
*/
+static void fsm_crcx_bts_cb(struct osmo_fsm_inst *fi, uint32_t event, void 
*data)
+{
+       struct mgcp_ctx *mgcp_ctx = (struct mgcp_ctx *)data;
+       struct osmo_bsc_sccp_con *conn;
+       struct msgb *msg;
+       struct mgcp_msg mgcp_msg;
+       struct mgcp_client *mgcp;
+       uint16_t rtp_endpoint;
+       int rc;
+
+       OSMO_ASSERT(mgcp_ctx);
+       conn = mgcp_ctx->conn;
+       OSMO_ASSERT(conn);
+       mgcp = mgcp_ctx->mgcp;
+       OSMO_ASSERT(mgcp);
+
+       LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG,
+                "fsm-state: %s, fsm-event: %s\n",
+                get_value_string(fsm_bsc_mgcp_state_names, fi->state), 
get_value_string(fsm_evt_names, event));
+
+       rtp_endpoint = mgcp_client_next_endpoint(mgcp);
+       mgcp_ctx->rtp_endpoint = rtp_endpoint;
+
+       LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG,
+                "creating connection for the BTS side on " "MGW 
endpoint:%x...\n", 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_ID |
+                            MGCP_MSG_PRESENCE_CONN_MODE),
+               .call_id = conn->conn_id,
+               .conn_id = CONN_ID_BTS,
+               .conn_mode = MGCP_CONN_LOOPBACK
+       };
+       if (snprintf(mgcp_msg.endpoint, MGCP_ENDPOINT_MAXLEN, 
MGCP_ENDPOINT_FORMAT, 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 */
+       LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "CRCX/BTS: transmitting MGCP 
message to MGW...\n");
+       rc = mgcp_client_tx(mgcp, msg, crcx_for_bts_resp_cb, mgcp_ctx);
+       if (rc < 0) {
+               handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL);
+               return;
+       }
+
+       osmo_fsm_inst_state_chg(mgcp_ctx->fsm, ST_ASSIGN_PROC, 
MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
+}
+
+/* Callback for MGCP-Client: handle response for BTS associated CRCX */
+static void crcx_for_bts_resp_cb(struct mgcp_response *r, void *priv)
+{
+       struct mgcp_ctx *mgcp_ctx = priv;
+       int rc;
+       struct osmo_bsc_sccp_con *conn;
+
+       OSMO_ASSERT(mgcp_ctx);
+       conn = mgcp_ctx->conn;
+       OSMO_ASSERT(conn);
+
+       if (r->head.response_code != 200) {
+               LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR,
+                        "CRCX/BTS: response yields error: %d %s\n", 
r->head.response_code, r->head.comment);
+               handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL);
+               return;
+       }
+
+       rc = mgcp_response_parse_params(r);
+       if (rc) {
+               LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR, "CRCX/BTS: 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);
+
+       /* Set the connection details in the conn struct. The code that
+        * controls the BTS via RSL will take these values and signal them
+        * to the BTS via RSL/IPACC */
+       conn->rtp_port = r->audio_port;
+       conn->rtp_ip = osmo_ntohl(inet_addr(r->audio_ip));
+
+       /* Notify the FSM that we got the response. */
+       osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_CRCX_BTS_RESP, mgcp_ctx);
+}
+
+/* Callback for ST_ASSIGN_PROC: An mgcp response has been received, proceed
+ * with the assignment request */
+static void fsm_proc_assignmnent_req_cb(struct osmo_fsm_inst *fi, uint32_t 
event, void *data)
+{
+       struct mgcp_ctx *mgcp_ctx = (struct mgcp_ctx *)data;
+       struct osmo_bsc_sccp_con *conn;
+       enum gsm48_chan_mode chan_mode;
+       bool full_rate;
+       int rc;
+
+       OSMO_ASSERT(mgcp_ctx);
+       conn = mgcp_ctx->conn;
+       OSMO_ASSERT(conn);
+
+       LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG,
+                "fsm-state: %s, fsm-event: %s\n",
+                get_value_string(fsm_bsc_mgcp_state_names, fi->state), 
get_value_string(fsm_evt_names, event));
+
+       switch (event) {
+       default:
+               handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN);
+               return;
+       case EV_CRCX_BTS_RESP:
+               break;
+       }
+
+       OSMO_ASSERT(conn->conn);
+       chan_mode = mgcp_ctx->chan_mode;
+       full_rate = mgcp_ctx->full_rate;
+
+       LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "MGW proceeding assignment 
request...\n");
+       rc = gsm0808_assign_req(conn->conn, chan_mode, full_rate);
+
+       if (rc < 0) {
+               handle_error(mgcp_ctx, MGCP_ERR_ASSGMNT_FAIL);
+               return;
+       }
+
+       osmo_fsm_inst_state_chg(fi, ST_MDCX_BTS, MGCP_BSS_TIMEOUT, 
MGCP_BSS_TIMEOUT_TIMER_NR);
+}
+
+static void mdcx_for_bts_resp_cb(struct mgcp_response *r, void *priv);
+
+/* Callback for ST_MDCX_BTS: When the BSS has completed the assignment,
+ * proceed with updating the connection for the BTS side */
+static void fsm_mdcx_bts_cb(struct osmo_fsm_inst *fi, uint32_t event, void 
*data)
+{
+       struct mgcp_ctx *mgcp_ctx = (struct mgcp_ctx *)data;
+       struct osmo_bsc_sccp_con *conn;
+       struct gsm_lchan *lchan;
+       struct msgb *msg;
+       struct mgcp_msg mgcp_msg;
+       struct mgcp_client *mgcp;
+       uint16_t rtp_endpoint;
+       struct in_addr addr;
+       int rc;
+
+       OSMO_ASSERT(mgcp_ctx);
+       conn = mgcp_ctx->conn;
+       OSMO_ASSERT(conn);
+
+       LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG,
+                "fsm-state: %s, fsm-event: %s\n",
+                get_value_string(fsm_bsc_mgcp_state_names, fi->state), 
get_value_string(fsm_evt_names, event));
+
+       switch (event) {
+       default:
+               handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN);
+               return;
+       case EV_ASS_COMPLETE:
+               break;
+       }
+
+       mgcp = mgcp_ctx->mgcp;
+       OSMO_ASSERT(mgcp);
+       lchan = mgcp_ctx->lchan;
+       OSMO_ASSERT(lchan);
+
+       LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "BSS has completed the assignment, 
now prceed with MDCX towards BTS...\n");
+
+       rtp_endpoint = mgcp_ctx->rtp_endpoint;
+
+       LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG,
+                "completing connection for the BTS side on " "MGW 
endpoint:%x...\n", rtp_endpoint);
+
+       addr.s_addr = osmo_ntohl(lchan->abis_ip.bound_ip);
+       LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG,
+                "BTS expects RTP input on address %s:%u\n", inet_ntoa(addr), 
lchan->abis_ip.bound_port);
+
+       /* 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 = conn->conn_id,
+               .conn_id = CONN_ID_BTS,
+               .conn_mode = MGCP_CONN_RECV_SEND,
+               .audio_ip = inet_ntoa(addr),
+               .audio_port = lchan->abis_ip.bound_port
+       };
+       if (snprintf(mgcp_msg.endpoint, sizeof(mgcp_msg.endpoint), 
MGCP_ENDPOINT_FORMAT, rtp_endpoint) >=
+           sizeof(mgcp_msg.endpoint)) {
+               handle_error(mgcp_ctx, MGCP_ERR_NOMEM);
+               return;
+       }
+       msg = mgcp_msg_gen(mgcp, &mgcp_msg);
+       OSMO_ASSERT(msg);
+
+       /* Transmit MGCP message to MGW */
+       LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "MDCX/BTS: transmitting MGCP 
message to MGW...\n");
+       rc = mgcp_client_tx(mgcp, msg, mdcx_for_bts_resp_cb, mgcp_ctx);
+       if (rc < 0) {
+               handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL);
+               return;
+       }
+
+       osmo_fsm_inst_state_chg(mgcp_ctx->fsm, ST_CRCX_NET, MGCP_MGW_TIMEOUT, 
MGCP_MGW_TIMEOUT_TIMER_NR);
+}
+
+/* Callback for MGCP-Client: handle response for BTS associated MDCX */
+static void mdcx_for_bts_resp_cb(struct mgcp_response *r, void *priv)
+{
+       struct mgcp_ctx *mgcp_ctx = priv;
+       int rc;
+       struct in_addr addr;
+       struct gsm_lchan *lchan;
+
+       OSMO_ASSERT(mgcp_ctx);
+       lchan = mgcp_ctx->lchan;
+       OSMO_ASSERT(lchan);
+
+       if (r->head.response_code != 200) {
+               LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR,
+                        "MDCX/BTS: response yields error: %d %s\n", 
r->head.response_code, r->head.comment);
+               handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL);
+               return;
+       }
+
+       rc = mgcp_response_parse_params(r);
+       if (rc) {
+               LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR, "MDCX/BTS: Cannot parse 
MDCX response\n");
+               handle_error(mgcp_ctx, MGCP_ERR_MGW_INVAL_RESP);
+               return;
+       }
+
+       LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "MDCX/BTS: MGW responded with 
address %s:%u\n", r->audio_ip, r->audio_port);
+
+       addr.s_addr = lchan->abis_ip.bound_ip;
+       LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG,
+                "MDCX/BTS: corresponding lchan has been bound to address 
%s:%u\n",
+                inet_ntoa(addr), lchan->abis_ip.bound_port);
+
+       /* Notify the FSM that we got the response. */
+       osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_MDCX_BTS_RESP, mgcp_ctx);
+}
+
+static void crcx_for_net_resp_cb(struct mgcp_response *r, void *priv);
+
+/* Callback for ST_CRCX_NET: An mgcp response has been received, proceed... */
+static void fsm_crcx_net_cb(struct osmo_fsm_inst *fi, uint32_t event, void 
*data)
+{
+       struct mgcp_ctx *mgcp_ctx = (struct mgcp_ctx *)data;
+       struct osmo_bsc_sccp_con *conn;
+       struct msgb *msg;
+       struct mgcp_msg mgcp_msg;
+       struct mgcp_client *mgcp;
+       uint16_t rtp_endpoint;
+       struct sockaddr_in *sin;
+       char *addr;
+       uint16_t port;
+       int rc;
+
+       OSMO_ASSERT(mgcp_ctx);
+       conn = mgcp_ctx->conn;
+       OSMO_ASSERT(conn);
+       mgcp = mgcp_ctx->mgcp;
+       OSMO_ASSERT(mgcp);
+
+       LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG,
+                "fsm-state: %s, fsm-event: %s\n",
+                get_value_string(fsm_bsc_mgcp_state_names, fi->state), 
get_value_string(fsm_evt_names, event));
+
+       rtp_endpoint = mgcp_ctx->rtp_endpoint;
+
+       LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG,
+                "creating connection for the NET side on " "MGW 
endpoint:%x...\n", rtp_endpoint);
+
+       /* Currently we only have support for IPv4 in our MGCP software, the
+        * AoIP part is ready to support IPv6 in theory, because the IE
+        * parser/generator uses sockaddr_storage for the AoIP transport
+        * identifier. However, the MGCP-GW does not support IPv6 yet. This is
+        * why we stop here in case some MSC tries to signal IPv6 AoIP
+        * transport identifiers */
+       if (conn->aoip_rtp_addr_remote.ss_family != AF_INET) {
+               LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR,
+                        "endpoint:%x MSC uses unsupported address format in 
AoIP transport identifier -- aborting...\n",
+                        rtp_endpoint);
+               handle_error(mgcp_ctx, MGCP_ERR_UNSUPP_ADDR_FMT);
+               return;
+       }
+
+       sin = (struct sockaddr_in *)&conn->aoip_rtp_addr_remote;
+       addr = inet_ntoa(sin->sin_addr);
+       port = osmo_ntohs(sin->sin_port);
+       LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "MSC expects RTP input on address 
%s:%u\n", addr, port);
+
+       /* 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_ID |
+                            MGCP_MSG_PRESENCE_CONN_MODE | 
MGCP_MSG_PRESENCE_AUDIO_IP | MGCP_MSG_PRESENCE_AUDIO_PORT),
+               .call_id = conn->conn_id,
+               .conn_id = CONN_ID_NET,
+               .conn_mode = MGCP_CONN_RECV_SEND,
+               .audio_ip = addr,
+               .audio_port = port
+       };
+       if (snprintf(mgcp_msg.endpoint, sizeof(mgcp_msg.endpoint), 
MGCP_ENDPOINT_FORMAT, rtp_endpoint) >=
+           sizeof(mgcp_msg.endpoint)) {
+               handle_error(mgcp_ctx, MGCP_ERR_NOMEM);
+               return;
+       }
+       msg = mgcp_msg_gen(mgcp, &mgcp_msg);
+       OSMO_ASSERT(msg);
+
+       /* Transmit MGCP message to MGW */
+       LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "CRCX/NET: transmitting MGCP 
message to MGW...\n");
+       rc = mgcp_client_tx(mgcp, msg, crcx_for_net_resp_cb, mgcp_ctx);
+       if (rc < 0) {
+               handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL);
+               return;
+       }
+
+       osmo_fsm_inst_state_chg(mgcp_ctx->fsm, ST_ASSIGN_COMPL, 
MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
+}
+
+/* Callback for MGCP-Client: handle response for NET associated CRCX */
+static void crcx_for_net_resp_cb(struct mgcp_response *r, void *priv)
+{
+       struct mgcp_ctx *mgcp_ctx = priv;
+       int rc;
+       struct osmo_bsc_sccp_con *conn;
+       struct gsm_lchan *lchan;
+       struct sockaddr_in *sin;
+
+       OSMO_ASSERT(mgcp_ctx);
+       conn = mgcp_ctx->conn;
+       OSMO_ASSERT(conn);
+       lchan = mgcp_ctx->lchan;
+       OSMO_ASSERT(lchan);
+
+       if (r->head.response_code != 200) {
+               LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR,
+                        "CRCX/NET: response yields error: %d %s\n", 
r->head.response_code, r->head.comment);
+               handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL);
+               return;
+       }
+
+       rc = mgcp_response_parse_params(r);
+       if (rc) {
+               LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR, "CRCX/NET: Cannot parse 
CRCX response\n");
+               handle_error(mgcp_ctx, MGCP_ERR_MGW_INVAL_RESP);
+               return;
+       }
+
+       LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "CRCX/NET: MGW responded with 
address %s:%u\n", r->audio_ip, r->audio_port);
+
+       /* Store address */
+       sin = (struct sockaddr_in *)&conn->aoip_rtp_addr_local;
+       sin->sin_family = AF_INET;
+       sin->sin_addr.s_addr = inet_addr(r->audio_ip);
+       sin->sin_port = osmo_ntohs(r->audio_port);
+
+       /* Notify the FSM that we got the response. */
+       osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_CRCX_NET_RESP, mgcp_ctx);
+}
+
+/* Callback for ST_ASSIGN_COMPL: Send back assignment complete and wait until 
the call ends */
+static void fsm_send_assignment_complete(struct osmo_fsm_inst *fi, uint32_t 
event, void *data)
+{
+       struct mgcp_ctx *mgcp_ctx = (struct mgcp_ctx *)data;
+       struct gsm_lchan *lchan;
+
+       OSMO_ASSERT(mgcp_ctx);
+
+       LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG,
+                "fsm-state: %s, fsm-event: %s\n",
+                get_value_string(fsm_bsc_mgcp_state_names, fi->state), 
get_value_string(fsm_evt_names, event));
+
+       switch (event) {
+       default:
+               handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN);
+               return;
+       case EV_CRCX_NET_RESP:
+               break;
+       }
+
+       lchan = mgcp_ctx->lchan;
+       OSMO_ASSERT(lchan);
+
+       /* Send assignment completion message via AoIP, this will complete
+        * the circuit. The message will also contain the port and IP-Address
+        * where the MGW expects the RTP input from the MSC side */
+       bssmap_send_aoip_ass_compl(lchan);
+
+       LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "call in progress, waiting for call 
end...\n");
+
+       osmo_fsm_inst_state_chg(mgcp_ctx->fsm, ST_DLCX, 0, 0);
+}
+
+static void dlcx_for_all_resp_cb(struct mgcp_response *r, void *priv);
+
+/* Callback for ST_DLCX: Remove connection for the BTS and NET side. */
+static void fsm_dlcx_all_cb(struct osmo_fsm_inst *fi, uint32_t event, void 
*data)
+{
+       struct mgcp_ctx *mgcp_ctx = data;
+       struct osmo_bsc_sccp_con *conn;
+       struct msgb *msg;
+       struct mgcp_msg mgcp_msg;
+       struct mgcp_client *mgcp;
+       uint16_t rtp_endpoint;
+       int rc;
+
+       OSMO_ASSERT(mgcp_ctx);
+       conn = mgcp_ctx->conn;
+       OSMO_ASSERT(conn);
+       mgcp = mgcp_ctx->mgcp;
+       OSMO_ASSERT(mgcp);
+
+       LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG,
+                "fsm-state: %s, fsm-event: %s\n",
+                get_value_string(fsm_bsc_mgcp_state_names, fi->state), 
get_value_string(fsm_evt_names, event));
+
+       rtp_endpoint = mgcp_ctx->rtp_endpoint;
+
+       LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG,
+                "removing connection for the BTS and NET side on " "MGW 
endpoint:%x...\n", 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(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 = conn->conn_id
+       };
+       if (snprintf(mgcp_msg.endpoint, sizeof(mgcp_msg.endpoint), 
MGCP_ENDPOINT_FORMAT, rtp_endpoint) >=
+           sizeof(mgcp_msg.endpoint)) {
+               handle_error(mgcp_ctx, MGCP_ERR_NOMEM);
+               return;
+       }
+       msg = mgcp_msg_gen(mgcp, &mgcp_msg);
+       OSMO_ASSERT(msg);
+
+       /* Transmit MGCP message to MGW */
+       LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "DLCX: transmitting MGCP message to 
MGW...\n");
+       rc = mgcp_client_tx(mgcp, msg, dlcx_for_all_resp_cb, mgcp_ctx);
+       if (rc < 0) {
+               handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL);
+               return;
+       }
+
+       osmo_fsm_inst_state_chg(mgcp_ctx->fsm, ST_HALT, MGCP_MGW_TIMEOUT, 
MGCP_MGW_TIMEOUT_TIMER_NR);
+}
+
+/* Callback for MGCP-Client: handle response for NET associated CRCX */
+static void dlcx_for_all_resp_cb(struct mgcp_response *r, void *priv)
+{
+       struct mgcp_ctx *mgcp_ctx = priv;
+       struct osmo_bsc_sccp_con *conn;
+       struct mgcp_client *mgcp;
+
+       OSMO_ASSERT(mgcp_ctx);
+       conn = mgcp_ctx->conn;
+       OSMO_ASSERT(conn);
+       mgcp = mgcp_ctx->mgcp;
+       OSMO_ASSERT(mgcp);
+
+       /* Note: We check the return code, but in case of an error there is
+        * not much that can be done to recover. However, at least we tryed
+        * to remove the connection (if there was even any) */
+       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);
+       }
+
+       LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR, "DLCX: MGW has acknowledged the 
removal of the connections\n");
+
+       /* 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 = (struct mgcp_ctx *)data;
+       struct osmo_bsc_sccp_con *conn;
+
+       OSMO_ASSERT(mgcp_ctx);
+       conn = mgcp_ctx->conn;
+       OSMO_ASSERT(conn);
+
+       LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG,
+                "fsm-state: %s, fsm-event: %s\n",
+                get_value_string(fsm_bsc_mgcp_state_names, fi->state), 
get_value_string(fsm_evt_names, event));
+
+       /* Send pending sigtran message */
+       if (mgcp_ctx->resp) {
+               LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "sending pending sigtran 
response message...\n");
+               osmo_bsc_sigtran_send(conn, mgcp_ctx->resp);
+               mgcp_ctx->resp = NULL;
+       }
+
+       LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "state machine halted\n");
+
+       /* Destroy the state machine and all context information */
+       osmo_fsm_inst_free(mgcp_ctx->fsm);
+       mgcp_ctx->fsm = NULL;
+}
+
+/* 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);
+
+       LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR,
+                "timeout (T%i) in state %s, attempting graceful teardown...\n",
+                fi->T, get_value_string(fsm_bsc_mgcp_state_names, fi->state));
+
+       /* Ensure that no sigtran response, is present. Otherwiese we might try
+        * to send a sigtran response when the sccp connection is already 
freed. */
+       mgcp_ctx->resp = NULL;
+
+       if (fi->T == MGCP_MGW_TIMEOUT_TIMER_NR) {
+               /* Note: We were unable to communicate with the MGCP-GW,
+                * unfortunately there is no meaningful action we can take
+                * now other than giving up. */
+               LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR, "graceful teardown not 
possible, terminating...\n");
+
+               /* At least release the occupied endpoint ID */
+               mgcp_client_release_endpoint(mgcp_ctx->rtp_endpoint, mgcp);
+
+               /* Initiate self destruction of the FSM */
+               osmo_fsm_inst_state_chg(fi, ST_HALT, 0, 0);
+               osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_TEARDOWN, mgcp_ctx);
+       } else if (fi->T == MGCP_BSS_TIMEOUT_TIMER_NR)
+               /* Note: If the logic that controls the BSS is unable to
+                * negotiate a connection, we presumably still have a
+                * working connection to the MGCP-GW, we will try to
+                * shut down gracefully. */
+               handle_error(mgcp_ctx, MGCP_ERR_BSS_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 struct osmo_fsm_state fsm_bsc_mgcp_states[] = {
+
+       /* Startup state machine, send CRCX to BTS. */
+       [ST_CRCX_BTS] = {
+                        .in_event_mask = (1 << EV_INIT),
+                        .out_state_mask = (1 << ST_HALT) | (1 << 
ST_ASSIGN_PROC),
+                        .name = "ST_CRCX_BTS",
+                        .action = fsm_crcx_bts_cb,
+                        },
+
+       /* When the CRCX response for the BTS side is received, then
+        * proceed the assignment on the BSS side. */
+       [ST_ASSIGN_PROC] = {
+                           .in_event_mask = (1 << EV_TEARDOWN) | (1 << 
EV_CRCX_BTS_RESP),
+                           .out_state_mask = (1 << ST_HALT) | (1 << ST_DLCX) | 
(1 << ST_MDCX_BTS),
+                           .name = "ST_ASSIGN_PROC",
+                           .action = fsm_proc_assignmnent_req_cb,
+                           },
+
+       /* When the BSS has processed the assignment request,
+        * then send the MDCX command for the BTS side in order to
+        * update the connections with the actual PORT/IP where the
+        * BTS expects the RTP input. */
+       [ST_MDCX_BTS] = {
+                        .in_event_mask = (1 << EV_TEARDOWN) | (1 << 
EV_ASS_COMPLETE),
+                        .out_state_mask = (1 << ST_HALT) | (1 << ST_DLCX) | (1 
<< ST_CRCX_NET),
+                        .name = "ST_MDCX_BTS",
+                        .action = fsm_mdcx_bts_cb,
+                        },
+
+       /* When the MDCX response for the BTS siede is received, then
+        * directly proceed with sending the CRCX command to connect the
+        * network side. This is done in one phase (no MDCX needed). */
+       [ST_CRCX_NET] = {
+                        .in_event_mask = (1 << EV_TEARDOWN) | (1 << 
EV_MDCX_BTS_RESP),
+                        .out_state_mask = (1 << ST_HALT) | (1 << ST_DLCX) | (1 
<< ST_ASSIGN_COMPL),
+                        .name = "ST_CRCX_NET",
+                        .action = fsm_crcx_net_cb,
+                        },
+
+       /* When the CRCX response for the NET side is received. Then
+        * send the assignment complete message via the A-Interface and
+        * enter wait state in order to wait for the end of the call. */
+       [ST_ASSIGN_COMPL] = {
+                            .in_event_mask = (1 << EV_TEARDOWN) | (1 << 
EV_CRCX_NET_RESP),
+                            .out_state_mask = (1 << ST_HALT) | (1 << ST_DLCX),
+                            .name = "ST_ASSIGN_COMPL",
+                            .action = fsm_send_assignment_complete,
+                            },
+
+       /* When the call ends, remove all RTP connections from the
+        * MGCP-GW by sending a wildcarded DLCX. */
+       [ST_DLCX] = {
+                    .in_event_mask = (1 << EV_TEARDOWN),
+                    .out_state_mask = (1 << ST_HALT),
+                    .name = "ST_DLCX",
+                    .action = fsm_dlcx_all_cb,
+                    },
+
+       /* When the MGCP_GW confirms that the connections are terminated,
+        * then halt the state machine. */
+       [ST_HALT] = {
+                    .in_event_mask = (1 << EV_TEARDOWN) | (1 << 
EV_DLCX_ALL_RESP),
+                    .out_state_mask = 0,
+                    .name = "ST_HALT",
+                    .action = fsm_halt_cb,
+                    },
+};
+
+/* State machine definition */
+static struct osmo_fsm fsm_bsc_mgcp = {
+       .name = "MGW",
+       .states = fsm_bsc_mgcp_states,
+       .num_states = ARRAY_SIZE(fsm_bsc_mgcp_states),
+       .log_subsys = DMGCP,
+       .timer_cb = fsm_timeout_cb,
+};
+
+/* Notify that the a new call begins. This will create a connection for the
+ * BTS on the MGCP-GW and set up the port numbers in struct osmo_bsc_sccp_con.
+ * After that gsm0808_assign_req() to proceed.
+ * Parameter:
+ * ctx: talloc context
+ * network: associated gsm network
+ * conn: associated sccp connection
+ * chan_mode: channel mode (system data, passed through)
+ * full_rate: full rate flag (system data, passed through)
+ * Returns an mgcp_context that contains system data and the OSMO-FSM */
+struct mgcp_ctx *mgcp_assignm_req(void *ctx, struct mgcp_client *mgcp, struct 
osmo_bsc_sccp_con *conn,
+                                 enum gsm48_chan_mode chan_mode, bool 
full_rate)
+{
+       struct mgcp_ctx *mgcp_ctx;
+       char name[32];
+       static bool fsm_registered = false;
+
+       OSMO_ASSERT(mgcp);
+       OSMO_ASSERT(conn);
+
+       if(snprintf(name, sizeof(name), "MGW_%i", conn->conn_id) >= 
sizeof(name))
+               return NULL;
+
+       /* Register the fsm description (if not already done) */
+       if (fsm_registered == false) {
+               osmo_fsm_register(&fsm_bsc_mgcp);
+               fsm_registered = true;
+       }
+
+       /* Allocate and configure a new fsm instance */
+       mgcp_ctx = talloc_zero(ctx, struct mgcp_ctx);
+       OSMO_ASSERT(mgcp_ctx);
+
+       mgcp_ctx->fsm = osmo_fsm_inst_alloc(&fsm_bsc_mgcp, NULL, ctx, 
LOGL_DEBUG, name);
+       OSMO_ASSERT(mgcp_ctx->fsm);
+       mgcp_ctx->fsm->priv = mgcp_ctx;
+       LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "MGW handler fsm created\n");
+       mgcp_ctx->mgcp = mgcp;
+       mgcp_ctx->conn = conn;
+       mgcp_ctx->chan_mode = chan_mode;
+       mgcp_ctx->full_rate = full_rate;
+
+       /* start state machine */
+       OSMO_ASSERT(mgcp_ctx->fsm->state == ST_CRCX_BTS);
+       osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_INIT, mgcp_ctx);
+
+       return mgcp_ctx;
+}
+
+/* Notify that the call has ended, remove all connections from the MGCP-GW,
+ * then send the clear complete message and destroy the FSM instance
+ * Parameter:
+ * mgcp_ctx: context information (FSM, and pointer to external system data)
+ * respmgcp_ctx: pending clear complete message to send via A-Interface */
+void mgcp_clear_complete(struct mgcp_ctx *mgcp_ctx, struct msgb *resp)
+{
+       struct osmo_bsc_sccp_con *conn;
+
+       OSMO_ASSERT(mgcp_ctx);
+       OSMO_ASSERT(resp);
+       conn = mgcp_ctx->conn;
+       OSMO_ASSERT(conn);
+
+       if (mgcp_ctx->fsm == NULL) {
+               LOGP(DMGCP, LOGL_ERROR,
+                    "clear completion attemted on already terminated FSM -- 
forwarding directly...\n");
+               osmo_bsc_sigtran_send(conn, resp);
+               mgcp_ctx->resp = NULL;
+               return;
+       }
+
+       LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "bss is indicating call end...\n");
+
+       mgcp_ctx->resp = resp;
+
+       osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_TEARDOWN, mgcp_ctx);
+}
+
+/* Notify that the BSS ready, send the assingnment complete message when the
+ * mgcp connection is completed
+ * Parameter:
+ * mgcp_ctx: context information (FSM, and pointer to external system data)
+ * lchan: needed for sending the assignment complete message via A-Interface */
+void mgcp_ass_complete(struct mgcp_ctx *mgcp_ctx, struct gsm_lchan *lchan)
+{
+       OSMO_ASSERT(mgcp_ctx);
+       OSMO_ASSERT(lchan);
+
+       if (mgcp_ctx->fsm == NULL) {
+               LOGP(DMGCP, LOGL_ERROR, "assignment completion attemted on 
already terminated FSM -- ignored\n");
+               mgcp_ctx->lchan = NULL;
+               return;
+       }
+
+       LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "bss is indicating assignment 
completion...\n");
+
+       mgcp_ctx->lchan = lchan;
+
+       osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_ASS_COMPLETE, mgcp_ctx);
+
+       return;
+}
+
+/* Free an existing mgcp context gracefully
+ * Parameter:
+ * mgcp_ctx: context information (FSM, and pointer to external system data) */
+void mgcp_free_ctx(struct mgcp_ctx *mgcp_ctx)
+{
+       OSMO_ASSERT(mgcp_ctx);
+
+       if (mgcp_ctx->fsm == NULL) {
+               LOGP(DMGCP, LOGL_DEBUG, "fsm already terminated, freeing only 
related context information...\n");
+               talloc_free(mgcp_ctx);
+               return;
+       }
+
+       LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "terminating fsm and freeing 
related context information...\n");
+
+       osmo_fsm_inst_free(mgcp_ctx->fsm);
+       talloc_free(mgcp_ctx);
+}
diff --git a/src/osmo-bsc/osmo_bsc_sigtran.c b/src/osmo-bsc/osmo_bsc_sigtran.c
index 951061a..2ba777e 100644
--- a/src/osmo-bsc/osmo_bsc_sigtran.c
+++ b/src/osmo-bsc/osmo_bsc_sigtran.c
@@ -33,6 +33,7 @@
 #include <osmocom/bsc/osmo_bsc_sigtran.h>
 #include <osmocom/bsc/a_reset.h>
 #include <osmocom/bsc/gsm_04_80.h>
+#include <osmocom/bsc/osmo_bsc_mgcp.h>
 
 /* A pointer to a list with all involved MSCs
  * (a copy of the pointer location submitted with osmo_bsc_sigtran_init() */
@@ -384,6 +385,10 @@
                a_reset_conn_fail(conn->msc->a.reset);
        }
 
+       /* Remove mgcp context if existant */
+       if (conn->mgcp_ctx)
+               mgcp_free_ctx(conn->mgcp_ctx);
+
        llist_del(&conn->entry);
        talloc_free(conn);
 
diff --git a/src/osmo-bsc/osmo_bsc_vty.c b/src/osmo-bsc/osmo_bsc_vty.c
index f816ae4..ca47097 100644
--- a/src/osmo-bsc/osmo_bsc_vty.c
+++ b/src/osmo-bsc/osmo_bsc_vty.c
@@ -29,6 +29,8 @@
 #include <osmocom/core/talloc.h>
 #include <osmocom/vty/logging.h>
 #include <osmocom/sccp/sccp_types.h>
+#include <osmocom/mgcp_client/mgcp_client.h>
+
 
 #include <time.h>
 
@@ -972,6 +974,8 @@
 
 int bsc_vty_init_extra(void)
 {
+       struct gsm_network *net = bsc_gsmnet;
+
        install_element(CONFIG_NODE, &cfg_net_msc_cmd);
        install_element(CONFIG_NODE, &cfg_net_bsc_cmd);
 
@@ -1034,5 +1038,7 @@
 
        install_element(CFG_LOG_NODE, &logging_fltr_imsi_cmd);
 
+       mgcp_client_vty_init(net, MSC_NODE, net->mgw.conf);
+
        return 0;
 }

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

Gerrit-MessageType: newpatchset
Gerrit-Change-Id: Ia2882b7ca31a3219c676986e85045fa08a425d7a
Gerrit-PatchSet: 10
Gerrit-Project: osmo-bsc
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>
Gerrit-Reviewer: dexter <pma...@sysmocom.de>

Reply via email to