Review at  https://gerrit.osmocom.org/3695

NOT FOR MERGE combined changes from openbsc.git for gerrit checking

Change-Id: Id072aa07793aceca66bd74950bc40dd504982a55
---
M configure.ac
M contrib/jenkins.sh
M doc/examples/osmo-bsc/osmo-bsc.cfg
M include/openbsc/Makefile.am
M include/openbsc/gprs_sgsn.h
M include/openbsc/gprs_utils.h
A include/openbsc/gsm_04_14.h
M include/openbsc/gsm_data.h
M include/openbsc/gsm_data_shared.h
M include/openbsc/osmux.h
M src/gprs/gb_proxy.c
M src/gprs/gb_proxy_main.c
M src/gprs/gb_proxy_patch.c
M src/gprs/gb_proxy_peer.c
M src/gprs/gb_proxy_vty.c
M src/gprs/gprs_sgsn.c
M src/gprs/gprs_subscriber.c
M src/gprs/gprs_utils.c
M src/gprs/gtphub.c
M src/gprs/gtphub_main.c
M src/gprs/sgsn_cdr.c
M src/gprs/sgsn_libgtp.c
M src/gprs/sgsn_main.c
M src/gprs/sgsn_vty.c
M src/libbsc/abis_nm.c
M src/libbsc/abis_rsl.c
M src/libbsc/bsc_api.c
M src/libbsc/bsc_init.c
M src/libbsc/bsc_vty.c
M src/libbsc/handover_logic.c
M src/libbsc/net_init.c
M src/libcommon-cs/common_cs.c
M src/libcommon/gsm_data.c
M src/libcommon/gsm_data_shared.c
M src/libmgcp/mgcp_osmux.c
M src/libmgcp/mgcp_protocol.c
M src/libmsc/Makefile.am
M src/libmsc/db.c
M src/libmsc/gsm_04_08.c
M src/libmsc/gsm_04_11.c
A src/libmsc/gsm_04_14.c
M src/libmsc/smpp_openbsc.c
M src/libmsc/smpp_smsc.c
M src/libmsc/smpp_smsc.h
M src/libmsc/transaction.c
M src/libmsc/vty_interface_layer3.c
M src/osmo-bsc/osmo_bsc_main.c
M src/osmo-msc/msc_main.c
M src/utils/smpp_mirror.c
M tests/channel/channel_test.c
M tests/channel/channel_test.ok
M tests/db/db_test.err
M tests/gprs/gprs_test.c
M tests/gsm0408/gsm0408_test.c
M tests/gtphub/Makefile.am
M tests/sgsn/sgsn_test.c
56 files changed, 1,087 insertions(+), 405 deletions(-)


  git pull ssh://gerrit.osmocom.org:29418/osmo-msc refs/changes/95/3695/1

diff --git a/configure.ac b/configure.ac
index f184e78..871f7e5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -66,7 +66,7 @@
 AC_ARG_ENABLE([smpp], [AS_HELP_STRING([--enable-smpp], [Build the SMPP 
interface])],
     [osmo_ac_build_smpp="$enableval"],[osmo_ac_build_smpp="no"])
 if test "$osmo_ac_build_smpp" = "yes" ; then
-    PKG_CHECK_MODULES(LIBSMPP34, libsmpp34 >= 1.10)
+    PKG_CHECK_MODULES(LIBSMPP34, libsmpp34 >= 1.12)
     AC_DEFINE(BUILD_SMPP, 1, [Define if we want to build SMPP])
 fi
 AM_CONDITIONAL(BUILD_SMPP, test "x$osmo_ac_build_smpp" = "xyes")
diff --git a/contrib/jenkins.sh b/contrib/jenkins.sh
index b4074e4..7734965 100755
--- a/contrib/jenkins.sh
+++ b/contrib/jenkins.sh
@@ -1,4 +1,11 @@
 #!/usr/bin/env bash
+# jenkins build helper script for openbsc.  This is how we build on 
jenkins.osmocom.org
+
+if ! [ -x "$(command -v osmo-build-dep.sh)" ]; then
+       echo "Error: We need to have scripts/osmo-deps.sh from 
http://git.osmocom.org/osmo-ci/ in PATH !"
+       exit 2
+fi
+
 
 set -ex
 
diff --git a/doc/examples/osmo-bsc/osmo-bsc.cfg 
b/doc/examples/osmo-bsc/osmo-bsc.cfg
index b974b76..534605a 100644
--- a/doc/examples/osmo-bsc/osmo-bsc.cfg
+++ b/doc/examples/osmo-bsc/osmo-bsc.cfg
@@ -28,18 +28,6 @@
  handover power budget interval 6
  handover power budget hysteresis 3
  handover maximum distance 9999
- timer t3101 10
- timer t3103 0
- timer t3105 0
- timer t3107 0
- timer t3109 0
- timer t3111 0
- timer t3113 60
- timer t3115 0
- timer t3117 0
- timer t3119 0
- timer t3122 0
- timer t3141 0
  bts 0
   type nanobts
   band DCS1800
diff --git a/include/openbsc/Makefile.am b/include/openbsc/Makefile.am
index 2558d7c..995f02d 100644
--- a/include/openbsc/Makefile.am
+++ b/include/openbsc/Makefile.am
@@ -37,6 +37,7 @@
        gprs_utils.h \
        gsm_04_08.h \
        gsm_04_11.h \
+       gsm_04_14.h \
        gsm_04_80.h \
        gsm_data.h \
        gsm_data_shared.h \
diff --git a/include/openbsc/gprs_sgsn.h b/include/openbsc/gprs_sgsn.h
index fd86174..4e49c08 100644
--- a/include/openbsc/gprs_sgsn.h
+++ b/include/openbsc/gprs_sgsn.h
@@ -393,6 +393,8 @@
        char                    apn_str[GSM_APN_LENGTH];
        uint8_t                 qos_subscribed[20];
        size_t                  qos_subscribed_len;
+       uint8_t                 pdp_charg[2];
+       bool                    has_pdp_charg;
 };
 
 struct sgsn_subscriber_data {
@@ -407,6 +409,9 @@
 
        uint8_t                 hlr[9];
        size_t                  hlr_len;
+
+       uint8_t                 pdp_charg[2];
+       bool                    has_pdp_charg;
 };
 
 #define SGSN_ERROR_CAUSE_NONE (-1)
diff --git a/include/openbsc/gprs_utils.h b/include/openbsc/gprs_utils.h
index 603605c..574f5c5 100644
--- a/include/openbsc/gprs_utils.h
+++ b/include/openbsc/gprs_utils.h
@@ -30,7 +30,6 @@
 struct msgb *gprs_msgb_copy(const struct msgb *msg, const char *name);
 int gprs_msgb_resize_area(struct msgb *msg, uint8_t *area,
                            size_t old_size, size_t new_size);
-char *gprs_apn_to_str(char *out_str, const uint8_t *apn_enc, size_t 
rest_chars);
 int gprs_str_to_apn(uint8_t *apn_enc, size_t max_len, const char *str);
 
 /* GSM 04.08, 10.5.7.3 GPRS Timer */
diff --git a/include/openbsc/gsm_04_14.h b/include/openbsc/gsm_04_14.h
new file mode 100644
index 0000000..3cdbe04
--- /dev/null
+++ b/include/openbsc/gsm_04_14.h
@@ -0,0 +1,15 @@
+#pragma once
+
+#include <osmocom/gsm/protocol/gsm_04_14.h>
+
+int gsm0414_tx_close_tch_loop_cmd(struct gsm_subscriber_connection *conn,
+                                 enum gsm414_tch_loop_mode loop_mode);
+int gsm0414_tx_open_loop_cmd(struct gsm_subscriber_connection *conn);
+int gsm0414_tx_act_emmi_cmd(struct gsm_subscriber_connection *conn);
+int gsm0414_tx_test_interface(struct gsm_subscriber_connection *conn,
+                             uint8_t tested_devs);
+int gsm0414_tx_reset_ms_pos_store(struct gsm_subscriber_connection *conn,
+                                 uint8_t technology);
+
+int gsm0414_rcv_test(struct gsm_subscriber_connection *conn,
+                    struct msgb *msg);
diff --git a/include/openbsc/gsm_data.h b/include/openbsc/gsm_data.h
index c307fee..f4de381 100644
--- a/include/openbsc/gsm_data.h
+++ b/include/openbsc/gsm_data.h
@@ -323,10 +323,18 @@
        GSM_AUTH_POLICY_REGEXP, /* accept IMSIs matching given regexp */
 };
 
-#define GSM_T3101_DEFAULT 10
-#define GSM_T3105_DEFAULT 40
+#define GSM_T3101_DEFAULT 10   /* s */
+#define GSM_T3103_DEFAULT 5    /* s */
+#define GSM_T3105_DEFAULT 100  /* ms */
+#define GSM_T3107_DEFAULT 5    /* s */
+#define GSM_T3109_DEFAULT 19   /* s, must be 2s + radio_link_timeout*0.48 */
+#define GSM_T3111_DEFAULT 2    /* s */
 #define GSM_T3113_DEFAULT 60
+#define GSM_T3115_DEFAULT 10
+#define GSM_T3117_DEFAULT 10
+#define GSM_T3119_DEFAULT 10
 #define GSM_T3122_DEFAULT 10
+#define GSM_T3141_DEFAULT 10
 
 struct gsm_tz {
        int override; /* if 0, use system's time zone instead. */
@@ -503,6 +511,8 @@
        } smpp;
 
        unsigned long validity_minutes;
+       time_t created;
+       bool is_report;
        uint8_t reply_path_req;
        uint8_t status_rep_req;
        uint8_t ud_hdr_ind;
diff --git a/include/openbsc/gsm_data_shared.h 
b/include/openbsc/gsm_data_shared.h
index 0790807..60da2e5 100644
--- a/include/openbsc/gsm_data_shared.h
+++ b/include/openbsc/gsm_data_shared.h
@@ -899,7 +899,7 @@
 };
 
 
-struct gsm_bts *gsm_bts_alloc(void *talloc_ctx);
+struct gsm_bts *gsm_bts_alloc(void *talloc_ctx, uint8_t bts_num);
 struct gsm_bts *gsm_bts_num(struct gsm_network *net, int num);
 
 struct gsm_bts_trx *gsm_bts_trx_alloc(struct gsm_bts *bts);
diff --git a/include/openbsc/osmux.h b/include/openbsc/osmux.h
index 0b64a7f..f3ea72a 100644
--- a/include/openbsc/osmux.h
+++ b/include/openbsc/osmux.h
@@ -11,8 +11,7 @@
 };
 
 int osmux_init(int role, struct mgcp_config *cfg);
-int osmux_enable_endpoint(struct mgcp_endpoint *endp, int role,
-                         struct in_addr *addr, uint16_t port);
+int osmux_enable_endpoint(struct mgcp_endpoint *endp, struct in_addr *addr, 
uint16_t port);
 void osmux_disable_endpoint(struct mgcp_endpoint *endp);
 void osmux_allocate_cid(struct mgcp_endpoint *endp);
 void osmux_release_cid(struct mgcp_endpoint *endp);
diff --git a/src/gprs/gb_proxy.c b/src/gprs/gb_proxy.c
index d95139f..cd38d23 100644
--- a/src/gprs/gb_proxy.c
+++ b/src/gprs/gb_proxy.c
@@ -1266,8 +1266,7 @@
                rc = bssgp_tx_status(BSSGP_CAUSE_PDU_INCOMP_FEAT, NULL, 
orig_msg);
                break;
        default:
-               LOGP(DGPRS, LOGL_NOTICE, "BSSGP PDU type 0x%02x unknown\n",
-                       pdu_type);
+               LOGP(DGPRS, LOGL_NOTICE, "BSSGP PDU type %s not supported\n", 
bssgp_pdu_str(pdu_type));
                rate_ctr_inc(&cfg->ctrg->
                             ctr[GBPROX_GLOB_CTR_PROTO_ERR_SGSN]);
                rc = bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, 
orig_msg);
@@ -1370,9 +1369,8 @@
                /* from BSS to SGSN */
                peer = gbproxy_peer_by_nsei(cfg, nsvc->nsei);
                if (!peer) {
-                       LOGP(DGPRS, LOGL_NOTICE, "signal %u for unknown peer "
-                            "NSEI=%u/NSVCI=%u\n", signal, nsvc->nsei,
-                            nsvc->nsvci);
+                       LOGP(DGPRS, LOGL_NOTICE, "signal '%s' for unknown peer 
NSEI=%u/NSVCI=%u\n",
+                            get_value_string(gprs_ns_signal_ns_names, signal), 
nsvc->nsei, nsvc->nsvci);
                        return 0;
                }
                switch (signal) {
@@ -1380,9 +1378,8 @@
                case S_NS_BLOCK:
                        if (!peer->blocked)
                                break;
-                       LOGP(DGPRS, LOGL_NOTICE, "Converting NS_RESET from "
-                            "NSEI=%u/NSVCI=%u into BSSGP_BVC_BLOCK to SGSN\n",
-                            nsvc->nsei, nsvc->nsvci);
+                       LOGP(DGPRS, LOGL_NOTICE, "Converting '%s' from 
NSEI=%u/NSVCI=%u into BSSGP_BVC_BLOCK to SGSN\n",
+                            get_value_string(gprs_ns_signal_ns_names, signal), 
nsvc->nsei, nsvc->nsvci);
                        bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_BLOCK, nsvc->nsei,
                                             peer->bvci, 0);
                        break;
@@ -1431,6 +1428,10 @@
 
        INIT_LLIST_HEAD(&cfg->bts_peers);
        cfg->ctrg = rate_ctr_group_alloc(tall_bsc_ctx, &global_ctrg_desc, 0);
+       if (!cfg->ctrg) {
+               LOGP(DGPRS, LOGL_ERROR, "Cannot allocate global counter 
group!\n");
+               return -1;
+       }
        clock_gettime(CLOCK_REALTIME, &tp);
 
        return 0;
diff --git a/src/gprs/gb_proxy_main.c b/src/gprs/gb_proxy_main.c
index 69a93b6..caff27f 100644
--- a/src/gprs/gb_proxy_main.c
+++ b/src/gprs/gb_proxy_main.c
@@ -98,6 +98,7 @@
 
        switch (signal) {
        case SIGINT:
+       case SIGTERM:
                osmo_signal_dispatch(SS_L_GLOBAL, S_L_GLOBAL_SHUTDOWN, NULL);
                sleep(1);
                exit(0);
@@ -232,6 +233,7 @@
        msgb_talloc_ctx_init(tall_bsc_ctx, 0);
 
        signal(SIGINT, &signal_handler);
+       signal(SIGTERM, &signal_handler);
        signal(SIGABRT, &signal_handler);
        signal(SIGUSR1, &signal_handler);
        signal(SIGUSR2, &signal_handler);
diff --git a/src/gprs/gb_proxy_patch.c b/src/gprs/gb_proxy_patch.c
index 7bddc44..210fb2b 100644
--- a/src/gprs/gb_proxy_patch.c
+++ b/src/gprs/gb_proxy_patch.c
@@ -28,6 +28,7 @@
 
 #include <osmocom/gprs/protocol/gsm_08_18.h>
 #include <osmocom/core/rate_ctr.h>
+#include <osmocom/gsm/apn.h>
 
 /* patch RA identifier in place */
 static void gbproxy_patch_raid(uint8_t *raid_enc, struct gbproxy_peer *peer,
@@ -101,7 +102,7 @@
                LOGP(DGPRS, LOGL_DEBUG,
                     "Patching %s to SGSN: Removing APN '%s'\n",
                     log_text,
-                    gprs_apn_to_str(str1, apn, apn_len));
+                    osmo_apn_to_str(str1, apn, apn_len));
 
                *new_apn_ie_len = 0;
                gprs_msgb_resize_area(msg, apn_ie, apn_ie_len, 0);
@@ -116,8 +117,8 @@
                     "Patching %s to SGSN: "
                     "Replacing APN '%s' -> '%s'\n",
                     log_text,
-                    gprs_apn_to_str(str1, apn, apn_len),
-                    gprs_apn_to_str(str2, peer->cfg->core_apn,
+                    osmo_apn_to_str(str1, apn, apn_len),
+                    osmo_apn_to_str(str2, peer->cfg->core_apn,
                                       peer->cfg->core_apn_size));
 
                *new_apn_ie_len = peer->cfg->core_apn_size + 2;
diff --git a/src/gprs/gb_proxy_peer.c b/src/gprs/gb_proxy_peer.c
index 5365ff0..8909687 100644
--- a/src/gprs/gb_proxy_peer.c
+++ b/src/gprs/gb_proxy_peer.c
@@ -177,6 +177,10 @@
 
        peer->bvci = bvci;
        peer->ctrg = rate_ctr_group_alloc(peer, &peer_ctrg_desc, bvci);
+       if (!peer->ctrg) {
+               talloc_free(peer);
+               return NULL;
+       }
        peer->cfg = cfg;
 
        llist_add(&peer->list, &cfg->bts_peers);
diff --git a/src/gprs/gb_proxy_vty.c b/src/gprs/gb_proxy_vty.c
index 933b6b0..86d65a8 100644
--- a/src/gprs/gb_proxy_vty.c
+++ b/src/gprs/gb_proxy_vty.c
@@ -29,6 +29,7 @@
 
 #include <openbsc/gsm_04_08.h>
 #include <osmocom/gprs/gprs_ns.h>
+#include <osmocom/gsm/apn.h>
 
 #include <openbsc/debug.h>
 #include <openbsc/gb_proxy.h>
@@ -107,7 +108,7 @@
               if (g_cfg->core_apn_size > 0) {
                       char str[500] = {0};
                       vty_out(vty, " core-access-point-name %s%s",
-                              gprs_apn_to_str(str, g_cfg->core_apn,
+                              osmo_apn_to_str(str, g_cfg->core_apn,
                                                 g_cfg->core_apn_size),
                               VTY_NEWLINE);
               } else {
diff --git a/src/gprs/gprs_sgsn.c b/src/gprs/gprs_sgsn.c
index 18625ae..43eeaaa 100644
--- a/src/gprs/gprs_sgsn.c
+++ b/src/gprs/gprs_sgsn.c
@@ -30,6 +30,7 @@
 #include <osmocom/gprs/gprs_ns.h>
 #include <osmocom/gprs/gprs_bssgp.h>
 #include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
+#include <osmocom/gsm/apn.h>
 
 #include <openbsc/gprs_subscriber.h>
 #include <openbsc/debug.h>
@@ -129,6 +130,7 @@
 
 void sgsn_rate_ctr_init() {
        sgsn->rate_ctrs = rate_ctr_group_alloc(tall_bsc_ctx, &sgsn_ctrg_desc, 
0);
+       OSMO_ASSERT(sgsn->rate_ctrs);
 }
 
 /* look-up an SGSN MM context based on Iu UE context (struct ue_conn_ctx)*/
@@ -229,6 +231,11 @@
        LOGMMCTXP(LOGL_DEBUG, ctx, "Allocated with %s cipher.\n",
                  get_value_string(gprs_cipher_names, ctx->ciph_algo));
        ctx->ctrg = rate_ctr_group_alloc(ctx, &mmctx_ctrg_desc, tlli);
+       if (!ctx->ctrg) {
+               LOGMMCTXP(LOGL_ERROR, ctx, "Cannot allocate counter group\n");
+               talloc_free(ctx);
+               return NULL;
+       }
        INIT_LLIST_HEAD(&ctx->pdp_list);
 
        llist_add(&ctx->list, &sgsn_mm_ctxts);
@@ -253,6 +260,11 @@
        ctx->pmm_state = PMM_DETACHED;
        ctx->auth_triplet.key_seq = GSM_KEY_SEQ_INVAL;
        ctx->ctrg = rate_ctr_group_alloc(ctx, &mmctx_ctrg_desc, 0);
+       if (!ctx->ctrg) {
+               LOGMMCTXP(LOGL_ERROR, ctx, "Cannot allocate counter group\n");
+               talloc_free(ctx);
+               return NULL;
+       }
 
        /* Need to get RAID from IU conn */
        ctx->ra = ctx->iu.ue_ctx->ra_id;
@@ -380,6 +392,11 @@
        pdp->mm = mm;
        pdp->nsapi = nsapi;
        pdp->ctrg = rate_ctr_group_alloc(pdp, &pdpctx_ctrg_desc, nsapi);
+       if (!pdp->ctrg) {
+               LOGPDPCTXP(LOGL_ERROR, pdp, "Error allocation counter group\n");
+               talloc_free(pdp);
+               return NULL;
+       }
        llist_add(&pdp->list, &mm->pdp_list);
        llist_add(&pdp->g_list, &sgsn_pdp_ctxts);
 
@@ -699,10 +716,21 @@
        sgsn_auth_update(mmctx);
 }
 
-static void insert_qos(struct tlv_parsed *tp, struct sgsn_subscriber_pdp_data 
*pdp)
+static void insert_extra(struct tlv_parsed *tp,
+                       struct sgsn_subscriber_data *data,
+                       struct sgsn_subscriber_pdp_data *pdp)
 {
        tp->lv[OSMO_IE_GSM_SUB_QOS].len = pdp->qos_subscribed_len;
        tp->lv[OSMO_IE_GSM_SUB_QOS].val = pdp->qos_subscribed;
+
+       /* Prefer PDP charging characteristics of per subscriber one */
+       if (pdp->has_pdp_charg) {
+               tp->lv[OSMO_IE_GSM_CHARG_CHAR].len = sizeof(pdp->pdp_charg);
+               tp->lv[OSMO_IE_GSM_CHARG_CHAR].val = &pdp->pdp_charg[0];
+       } else if (data->has_pdp_charg) {
+               tp->lv[OSMO_IE_GSM_CHARG_CHAR].len = sizeof(data->pdp_charg);
+               tp->lv[OSMO_IE_GSM_CHARG_CHAR].val = &data->pdp_charg[0];
+       }
 }
 
 /**
@@ -731,7 +759,7 @@
                        return NULL;
                }
 
-               gprs_apn_to_str(req_apn_str,
+               osmo_apn_to_str(req_apn_str,
                                TLVP_VAL(tp, GSM48_IE_GSM_APN),
                                TLVP_LEN(tp, GSM48_IE_GSM_APN));
 
@@ -751,7 +779,7 @@
                        {
                                allow_any_apn = 1;
                                selected_apn_str = "";
-                               insert_qos(tp, pdp);
+                               insert_extra(tp, mmctx->subscr->sgsn_data, pdp);
                                continue;
                        }
                        if (!llist_empty(&sgsn_apn_ctxts)) {
@@ -760,7 +788,7 @@
                                if (apn_ctx == NULL)
                                        continue;
                        }
-                       insert_qos(tp, pdp);
+                       insert_extra(tp, mmctx->subscr->sgsn_data, pdp);
                        selected_apn_str = pdp->apn_str;
                        break;
                }
@@ -768,13 +796,13 @@
                /* Check whether the given APN is granted */
                llist_for_each_entry(pdp, &mmctx->subscr->sgsn_data->pdp_list, 
list) {
                        if (strcmp(pdp->apn_str, "*") == 0) {
-                               insert_qos(tp, pdp);
+                               insert_extra(tp, mmctx->subscr->sgsn_data, pdp);
                                selected_apn_str = req_apn_str;
                                allow_any_apn = 1;
                                continue;
                        }
                        if (strcasecmp(pdp->apn_str, req_apn_str) == 0) {
-                               insert_qos(tp, pdp);
+                               insert_extra(tp, mmctx->subscr->sgsn_data, pdp);
                                selected_apn_str = req_apn_str;
                                break;
                        }
diff --git a/src/gprs/gprs_subscriber.c b/src/gprs/gprs_subscriber.c
index 176583b..94297d0 100644
--- a/src/gprs/gprs_subscriber.c
+++ b/src/gprs/gprs_subscriber.c
@@ -22,6 +22,7 @@
 
 #include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
 #include <osmocom/gsm/gsup.h>
+#include <osmocom/gsm/apn.h>
 #include <osmocom/core/utils.h>
 #include <osmocom/core/logging.h>
 #include <openbsc/gprs_subscriber.h>
@@ -325,6 +326,13 @@
                }
        }
 
+       if (gsup_msg->pdp_charg_enc && gsup_msg->pdp_charg_enc_len >= 
sizeof(sdata->pdp_charg)) {
+               memcpy(&sdata->pdp_charg, gsup_msg->pdp_charg_enc, 
sizeof(sdata->pdp_charg));
+               sdata->has_pdp_charg = 1;
+       } else {
+               sdata->has_pdp_charg = 0;
+       }
+
        if (gsup_msg->pdp_info_compl) {
                rc = gprs_subscr_pdp_data_clear(subscr);
                if (rc > 0)
@@ -364,10 +372,17 @@
 
                OSMO_ASSERT(pdp_data != NULL);
                pdp_data->pdp_type = pdp_info->pdp_type;
-               gprs_apn_to_str(pdp_data->apn_str,
+               osmo_apn_to_str(pdp_data->apn_str,
                                pdp_info->apn_enc, pdp_info->apn_enc_len);
                memcpy(pdp_data->qos_subscribed, pdp_info->qos_enc, 
pdp_info->qos_enc_len);
                pdp_data->qos_subscribed_len = pdp_info->qos_enc_len;
+
+               if (pdp_info->pdp_charg_enc && pdp_info->pdp_charg_enc_len >= 
sizeof(pdp_data->pdp_charg)) {
+                       memcpy(&pdp_data->pdp_charg, pdp_info->pdp_charg_enc, 
sizeof(pdp_data->pdp_charg));
+                       pdp_data->has_pdp_charg = 1;
+               } else {
+                       pdp_data->has_pdp_charg = 0;
+               }
        }
 }
 
diff --git a/src/gprs/gprs_utils.c b/src/gprs/gprs_utils.c
index 64ed978..91a09d2 100644
--- a/src/gprs/gprs_utils.c
+++ b/src/gprs/gprs_utils.c
@@ -114,34 +114,6 @@
        return 0;
 }
 
-/* TODO: Move these conversion functions to a utils file. */
-/* TODO: consolidate with gprs_apn2str(). */
-/** memmove apn_enc to out_str, replacing the length octets in apn_enc with '.'
- * (omitting the first one) and terminating with a '\0'.
- * out_str needs to have rest_chars amount of bytes or 1 whatever is bigger.
- */
-char * gprs_apn_to_str(char *out_str, const uint8_t *apn_enc, size_t 
rest_chars)
-{
-       char *str = out_str;
-
-       while (rest_chars > 0 && apn_enc[0]) {
-               size_t label_size = apn_enc[0];
-               if (label_size + 1 > rest_chars)
-                       return NULL;
-
-               memmove(str, apn_enc + 1, label_size);
-               str += label_size;
-               rest_chars -= label_size + 1;
-               apn_enc += label_size + 1;
-
-               if (rest_chars)
-                       *(str++) = '.';
-       }
-       str[0] = '\0';
-
-       return out_str;
-}
-
 int gprs_str_to_apn(uint8_t *apn_enc, size_t max_len, const char *str)
 {
        uint8_t *last_len_field;
diff --git a/src/gprs/gtphub.c b/src/gprs/gtphub.c
index 211018b..0a8e375 100644
--- a/src/gprs/gtphub.c
+++ b/src/gprs/gtphub.c
@@ -42,6 +42,8 @@
 #include <osmocom/core/rate_ctr.h>
 #include <osmocom/core/stats.h>
 
+#include <osmocom/gsm/apn.h>
+
 
 static const int GTPH_GC_TICK_SECONDS = 1;
 
@@ -498,7 +500,7 @@
                len = sizeof(apn_buf) - 1;
        apn_buf[len] = '\0';
 
-       *apn_str = gprs_apn_to_str(apn_buf, (uint8_t*)apn_buf, len);
+       *apn_str = osmo_apn_to_str(apn_buf, (uint8_t*)apn_buf, len);
        if (!(*apn_str)) {
                LOG(LOGL_ERROR, "APN IE: present but cannot be decoded: %s\n",
                    osmo_hexdump((uint8_t*)apn_buf, len));
@@ -2708,6 +2710,10 @@
 
        pp->counters_io = rate_ctr_group_alloc(osmo_gtphub_ctx,
                                               &gtphub_ctrg_io_desc, 0);
+       if (!pp->counters_io) {
+               talloc_free(pp);
+               return NULL;
+       }
 
        llist_add(&pp->entry, &a->ports);
 
diff --git a/src/gprs/gtphub_main.c b/src/gprs/gtphub_main.c
index 73a122c..2b87d19 100644
--- a/src/gprs/gtphub_main.c
+++ b/src/gprs/gtphub_main.c
@@ -96,6 +96,7 @@
 
        switch (signal) {
        case SIGINT:
+       case SIGTERM:
                osmo_signal_dispatch(SS_L_GLOBAL, S_L_GLOBAL_SHUTDOWN, NULL);
                sleep(1);
                exit(0);
@@ -302,6 +303,7 @@
        msgb_talloc_ctx_init(osmo_gtphub_ctx, 0);
 
        signal(SIGINT, &signal_handler);
+       signal(SIGTERM, &signal_handler);
        signal(SIGABRT, &signal_handler);
        signal(SIGUSR1, &signal_handler);
        signal(SIGUSR2, &signal_handler);
diff --git a/src/gprs/sgsn_cdr.c b/src/gprs/sgsn_cdr.c
index 0910896..16ea9d4 100644
--- a/src/gprs/sgsn_cdr.c
+++ b/src/gprs/sgsn_cdr.c
@@ -22,6 +22,7 @@
 #include <openbsc/signal.h>
 #include <openbsc/gprs_utils.h>
 #include <openbsc/debug.h>
+#include <osmocom/gsm/apn.h>
 
 #include <openbsc/vty.h>
 
@@ -145,7 +146,7 @@
 
 
        if (pdp->lib) {
-               gprs_apn_to_str(apni, pdp->lib->apn_use.v, pdp->lib->apn_use.l);
+               osmo_apn_to_str(apni, pdp->lib->apn_use.v, pdp->lib->apn_use.l);
                inet_ntop(AF_INET, &pdp->lib->hisaddr0.s_addr, ggsn_addr, 
sizeof(ggsn_addr));
                extract_eua(&pdp->lib->eua, eua_addr);
        }
diff --git a/src/gprs/sgsn_libgtp.c b/src/gprs/sgsn_libgtp.c
index 7595bf8..7ff8ece 100644
--- a/src/gprs/sgsn_libgtp.c
+++ b/src/gprs/sgsn_libgtp.c
@@ -234,6 +234,10 @@
                memcpy(pdp->qos_req.v, qos, pdp->qos_req.l);
        }
 
+       /* charging characteristics if present */
+       if (TLVP_LEN(tp, OSMO_IE_GSM_CHARG_CHAR) >= sizeof(pdp->cch_pdp))
+               pdp->cch_pdp = tlvp_val16be(tp, OSMO_IE_GSM_CHARG_CHAR);
+
        /* SGSN address for control plane */
        pdp->gsnlc.l = sizeof(sgsn->cfg.gtp_listenaddr.sin_addr);
        memcpy(pdp->gsnlc.v, &sgsn->cfg.gtp_listenaddr.sin_addr,
@@ -247,18 +251,28 @@
        memcpy(pdp->gsnlu.v, &sgsn->cfg.gtp_listenaddr.sin_addr,
                sizeof(sgsn->cfg.gtp_listenaddr.sin_addr));
 
-       /* Routing Area Identifier with LAC and RAC fixed values, as
-        * requested in 29.006 7.3.1 */
+       /* Encode RAT Type according to TS 29.060 7.7.50 */
+       pdp->rattype.l = 1;
+       if (mmctx->ran_type == MM_CTX_T_UTRAN_Iu)
+               pdp->rattype.v[0] = 1;
+       else
+               pdp->rattype.v[0] = 2;
+       pdp->rattype_given = 1;
+
+       /* Include RAI and ULI all the time */
        pdp->rai_given = 1;
        pdp->rai.l = 6;
+
+       /* Routing Area Identifier with LAC and RAC fixed values, as
+        * requested in 29.006 7.3.1 */
        raid = mmctx->ra;
        raid.lac = 0xFFFE;
        raid.rac = 0xFF;
        gsm48_construct_ra(pdp->rai.v, &raid);
 
-       pdp->rattype.l = 1;
-       pdp->rattype_given = 1;
-
+       /* Encode User Location Information accordint to TS 29.060 7.7.51 */
+       pdp->userloc_given = 1;
+       pdp->userloc.l = 8;
        switch (mmctx->ran_type) {
        case MM_CTX_T_GERAN_Gb:
        case MM_CTX_T_GERAN_Iu:
@@ -270,8 +284,9 @@
                bssgp_create_cell_id(&pdp->userloc.v[1], &mmctx->ra, 
mmctx->gb.cell_id);
                break;
        case MM_CTX_T_UTRAN_Iu:
-               pdp->rattype.v[0] = 1;
-               /* FIXME: Optional User Location Information with SAI */
+               pdp->userloc.v[0] = 1; /* SAI for UTRAN */
+               /* SAI is like CGI but with SAC instead of CID, so we can abuse 
this function */
+               bssgp_create_cell_id(&pdp->userloc.v[1], &mmctx->ra, 
mmctx->iu.sac);
                break;
        }
 
diff --git a/src/gprs/sgsn_main.c b/src/gprs/sgsn_main.c
index 71cb18c..d5d43ad 100644
--- a/src/gprs/sgsn_main.c
+++ b/src/gprs/sgsn_main.c
@@ -145,6 +145,7 @@
 
        switch (signal) {
        case SIGINT:
+       case SIGTERM:
                osmo_signal_dispatch(SS_L_GLOBAL, S_L_GLOBAL_SHUTDOWN, NULL);
                sleep(1);
                exit(0);
@@ -332,6 +333,7 @@
        msgb_talloc_ctx_init(tall_bsc_ctx, 0);
 
        signal(SIGINT, &signal_handler);
+       signal(SIGTERM, &signal_handler);
        signal(SIGABRT, &signal_handler);
        signal(SIGUSR1, &signal_handler);
        signal(SIGUSR2, &signal_handler);
diff --git a/src/gprs/sgsn_vty.c b/src/gprs/sgsn_vty.c
index 888f53a..cf44cc4 100644
--- a/src/gprs/sgsn_vty.c
+++ b/src/gprs/sgsn_vty.c
@@ -28,6 +28,7 @@
 #include <osmocom/core/utils.h>
 #include <osmocom/core/rate_ctr.h>
 #include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
+#include <osmocom/gsm/apn.h>
 
 #include <openbsc/debug.h>
 #include <openbsc/sgsn.h>
@@ -116,7 +117,6 @@
 
 
 #define GSM48_MAX_APN_LEN      102     /* 10.5.6.1 */
-/* TODO: consolidate with gprs_apn_to_str(). */
 /** Copy apn to a static buffer, replacing the length octets in apn_enc with 
'.'
  * and terminating with a '\0'. Return the static buffer.
  * len: the length of the encoded APN (which has no terminating zero).
@@ -124,23 +124,10 @@
 static char *gprs_apn2str(uint8_t *apn, unsigned int len)
 {
        static char apnbuf[GSM48_MAX_APN_LEN+1];
-       unsigned int i = 0;
 
        if (!apn)
                return "";
-
-       if (len > sizeof(apnbuf)-1)
-               len = sizeof(apnbuf)-1;
-
-       memcpy(apnbuf, apn, len);
-       apnbuf[len] = '\0';
-
-       /* replace the domain name step sizes with dots */
-       while (i < len) {
-               unsigned int step = apnbuf[i];
-               apnbuf[i] = '.';
-               i += step+1;
-       }
+       osmo_apn_to_str(apnbuf, apn, len);
 
        return apnbuf+1;
 }
@@ -468,20 +455,22 @@
        const char *imsi = pdp->mm ? pdp->mm->imsi : "(detaching)";
        vty_out(vty, "%sPDP Context IMSI: %s, SAPI: %u, NSAPI: %u, TI: %u%s",
                pfx, imsi, pdp->sapi, pdp->nsapi, pdp->ti, VTY_NEWLINE);
-       vty_out(vty, "%s  APN: %s%s", pfx,
-               gprs_apn2str(pdp->lib->apn_use.v, pdp->lib->apn_use.l),
-               VTY_NEWLINE);
-       vty_out(vty, "%s  PDP Address: %s%s", pfx,
-               gprs_pdpaddr2str(pdp->lib->eua.v, pdp->lib->eua.l),
-               VTY_NEWLINE);
-       vty_out(vty, "%s  GTP Local Control(%s / TEIC: 0x%08x) ", pfx,
-               gtp_ntoa(&pdp->lib->gsnlc), pdp->lib->teic_own);
-       vty_out(vty, "Data(%s / TEID: 0x%08x)%s",
-               gtp_ntoa(&pdp->lib->gsnlu), pdp->lib->teid_own, VTY_NEWLINE);
-       vty_out(vty, "%s  GTP Remote Control(%s / TEIC: 0x%08x) ", pfx,
-               gtp_ntoa(&pdp->lib->gsnrc), pdp->lib->teic_gn);
-       vty_out(vty, "Data(%s / TEID: 0x%08x)%s",
-               gtp_ntoa(&pdp->lib->gsnru), pdp->lib->teid_gn, VTY_NEWLINE);
+       if (pdp->lib) {
+               vty_out(vty, "%s  APN: %s%s", pfx,
+                       gprs_apn2str(pdp->lib->apn_use.v, pdp->lib->apn_use.l),
+                       VTY_NEWLINE);
+               vty_out(vty, "%s  PDP Address: %s%s", pfx,
+                       gprs_pdpaddr2str(pdp->lib->eua.v, pdp->lib->eua.l),
+                       VTY_NEWLINE);
+               vty_out(vty, "%s  GTP Local Control(%s / TEIC: 0x%08x) ", pfx,
+                       gtp_ntoa(&pdp->lib->gsnlc), pdp->lib->teic_own);
+               vty_out(vty, "Data(%s / TEID: 0x%08x)%s",
+                       gtp_ntoa(&pdp->lib->gsnlu), pdp->lib->teid_own, 
VTY_NEWLINE);
+               vty_out(vty, "%s  GTP Remote Control(%s / TEIC: 0x%08x) ", pfx,
+                       gtp_ntoa(&pdp->lib->gsnrc), pdp->lib->teic_gn);
+               vty_out(vty, "Data(%s / TEID: 0x%08x)%s",
+                       gtp_ntoa(&pdp->lib->gsnru), pdp->lib->teid_gn, 
VTY_NEWLINE);
+       }
 
        vty_out_rate_ctr_group(vty, " ", pdp->ctrg);
 }
diff --git a/src/libbsc/abis_nm.c b/src/libbsc/abis_nm.c
index 019d039..cf20d7c 100644
--- a/src/libbsc/abis_nm.c
+++ b/src/libbsc/abis_nm.c
@@ -1590,10 +1590,17 @@
                     const uint8_t *attr, uint8_t attr_len)
 {
        struct abis_om_hdr *oh;
-       struct msgb *msg = nm_msgb_alloc();
+       struct msgb *msg;
+
+       if (bts->type != GSM_BTS_TYPE_OSMOBTS) {
+               LOGPC(DNM, LOGL_NOTICE, "Getting attributes from BTS%d type %s 
is not supported.\n",
+                     bts->nr, btstype2str(bts->type));
+               return -EINVAL;
+       }
 
        DEBUGP(DNM, "Get Attr (bts=%d)\n", bts->nr);
 
+       msg = nm_msgb_alloc();
        oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
        fill_om_fom_hdr(oh, attr_len, NM_MT_GET_ATTR, obj_class,
                        bts_nr, trx_nr, ts_nr);
diff --git a/src/libbsc/abis_rsl.c b/src/libbsc/abis_rsl.c
index 441b386..4f687a0 100644
--- a/src/libbsc/abis_rsl.c
+++ b/src/libbsc/abis_rsl.c
@@ -245,12 +245,14 @@
            && type == RSL_SYSTEM_INFO_13) {
                /* Ericsson proprietary encoding of SI13 */
                msgb_tv_put(msg, RSL_IE_SYSINFO_TYPE, RSL_ERIC_SYSTEM_INFO_13);
-               msgb_tlv_put(msg, RSL_IE_FULL_BCCH_INFO, len, data);
+               if (data)
+                       msgb_tlv_put(msg, RSL_IE_FULL_BCCH_INFO, len, data);
                msgb_tv_put(msg, RSL_IE_ERIC_BCCH_MAPPING, 0x00);
        } else {
                /* Normal encoding */
                msgb_tv_put(msg, RSL_IE_SYSINFO_TYPE, type);
-               msgb_tlv_put(msg, RSL_IE_FULL_BCCH_INFO, len, data);
+               if (data)
+                       msgb_tlv_put(msg, RSL_IE_FULL_BCCH_INFO, len, data);
        }
 
        msg->dst = trx->rsl_link;
@@ -269,7 +271,8 @@
        ch->msg_type = RSL_MT_SACCH_FILL;
 
        msgb_tv_put(msg, RSL_IE_SYSINFO_TYPE, type);
-       msgb_tl16v_put(msg, RSL_IE_L3_INFO, len, data);
+       if (data)
+               msgb_tl16v_put(msg, RSL_IE_L3_INFO, len, data);
 
        msg->dst = trx->rsl_link;
 
@@ -288,7 +291,8 @@
        dh->chan_nr = chan_nr;
 
        msgb_tv_put(msg, RSL_IE_SYSINFO_TYPE, type);
-       msgb_tl16v_put(msg, RSL_IE_L3_INFO, len, data);
+       if (data)
+               msgb_tl16v_put(msg, RSL_IE_L3_INFO, len, data);
 
        msg->dst = lchan->ts->trx->rsl_link;
 
@@ -2446,7 +2450,7 @@
        dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
        init_dchan_hdr(dh, msg_type);
        dh->c.msg_discr = ABIS_RSL_MDISC_DED_CHAN;
-       dh->chan_nr = gsm_pchan2chan_nr(GSM_PCHAN_PDCH, ts->nr, 0);
+       dh->chan_nr = gsm_pchan2chan_nr(GSM_PCHAN_TCH_F, ts->nr, 0);
 
        DEBUGP(DRSL, "%s IPAC PDCH %sACT\n", gsm_ts_name(ts),
                act ? "" : "DE");
@@ -2905,10 +2909,6 @@
 int rsl_start_t3109(struct gsm_lchan *lchan)
 {
        struct gsm_bts *bts = lchan->ts->trx->bts;
-
-       /* Disabled, mostly legacy code */
-       if (bts->network->T3109 == 0)
-               return -1;
 
        osmo_timer_setup(&lchan->T3109, t3109_expired, lchan);
        osmo_timer_schedule(&lchan->T3109, bts->network->T3109, 0);
diff --git a/src/libbsc/bsc_api.c b/src/libbsc/bsc_api.c
index c2828e3..c60f818 100644
--- a/src/libbsc/bsc_api.c
+++ b/src/libbsc/bsc_api.c
@@ -607,7 +607,7 @@
                        break;
                case GSM48_MT_RR_STATUS:
                        LOGP(DRR, LOGL_NOTICE, "%s (cause: %s)\n",
-                            gsm48_rr_msg_name(GSM48_MT_RR_GPRS_SUSP_REQ),
+                            gsm48_rr_msg_name(GSM48_MT_RR_STATUS),
                             rr_cause_name(gh->data[0]));
                        break;
                case GSM48_MT_RR_MEAS_REP:
diff --git a/src/libbsc/bsc_init.c b/src/libbsc/bsc_init.c
index 64dcd15..78ca2ab 100644
--- a/src/libbsc/bsc_init.c
+++ b/src/libbsc/bsc_init.c
@@ -104,8 +104,11 @@
        struct gsm_bts *bts = trx->bts;
        int rc, j;
 
-       DEBUGP(DRR, "SI%s: %s\n", get_value_string(osmo_sitype_strs, i),
-               osmo_hexdump(GSM_BTS_SI(bts, i), GSM_MACBLOCK_LEN));
+       if (si_len) {
+               DEBUGP(DRR, "SI%s: %s\n", get_value_string(osmo_sitype_strs, i),
+                       osmo_hexdump(GSM_BTS_SI(bts, i), GSM_MACBLOCK_LEN));
+       } else
+               DEBUGP(DRR, "SI%s: OFF\n", get_value_string(osmo_sitype_strs, 
i));
 
        switch (i) {
        case SYSINFO_TYPE_5:
@@ -113,14 +116,18 @@
        case SYSINFO_TYPE_5ter:
        case SYSINFO_TYPE_6:
                rc = rsl_sacch_filling(trx, osmo_sitype2rsl(i),
-                                      GSM_BTS_SI(bts, i), si_len);
+                                      si_len ? GSM_BTS_SI(bts, i) : NULL, 
si_len);
                break;
        case SYSINFO_TYPE_2quater:
+               if (si_len == 0) {
+                       rc = rsl_bcch_info(trx, i, NULL, 0);
+                       break;
+               }
                for (j = 0; j <= bts->si2q_count; j++)
                        rc = rsl_bcch_info(trx, i, (const uint8_t 
*)GSM_BTS_SI2Q(bts, j), GSM_MACBLOCK_LEN);
                break;
        default:
-               rc = rsl_bcch_info(trx, i, GSM_BTS_SI(bts, i), si_len);
+               rc = rsl_bcch_info(trx, i, si_len ? GSM_BTS_SI(bts, i) : NULL, 
si_len);
                break;
        }
 
@@ -139,8 +146,8 @@
                        ms_pwr_ctl_lvl(bts->band, bts->ms_max_power);
        bts->si_common.cell_sel_par.neci = bts->network->neci;
 
-       /* Zero, forget the state of the SIs */
-       bts->si_valid = 0;
+       /* Zero/forget the state of the dynamically computed SIs, leeping the 
static ones */
+       bts->si_valid = bts->si_mode_static;
 
        /* First, we determine which of the SI messages we actually need */
 
@@ -193,9 +200,13 @@
 
        for (n = 0; n < n_si; n++) {
                i = gen_si[n];
+               /* if we don't currently have this SI, we send a zero-length
+                * RSL BCCH FILLING / SACCH FILLING * in order to deactivate
+                * the SI, in case it might have previously been active */
                if (!GSM_BTS_HAS_SI(bts, i))
-                       continue;
-               rc = rsl_si(trx, i, si_len[i]);
+                       rc = rsl_si(trx, i, 0);
+               else
+                       rc = rsl_si(trx, i, si_len[i]);
                if (rc < 0)
                        return rc;
        }
@@ -350,12 +361,12 @@
                        generate_cell_chan_list(ca, trx->bts);
 
                        /* Request generic BTS-level attributes */
-                       abis_nm_get_attr(trx->bts, NM_OC_BTS, trx->bts->nr, 
trx->nr, 0xFF, bts_attr, sizeof(bts_attr));
+                       abis_nm_get_attr(trx->bts, NM_OC_BTS, 0xFF, 0xFF, 0xFF, 
bts_attr, sizeof(bts_attr));
 
                        llist_for_each_entry(cur_trx, &trx->bts->trx_list, 
list) {
                                int i;
                                /* Request TRX-level attributes */
-                               abis_nm_get_attr(cur_trx->bts, 
NM_OC_BASEB_TRANSC, cur_trx->bts->nr, cur_trx->nr, 0xFF,
+                               abis_nm_get_attr(cur_trx->bts, 
NM_OC_BASEB_TRANSC, 0, cur_trx->nr, 0xFF,
                                                 trx_attr, sizeof(trx_attr));
                                for (i = 0; i < ARRAY_SIZE(cur_trx->ts); i++)
                                        generate_ma_for_ts(&cur_trx->ts[i]);
diff --git a/src/libbsc/bsc_vty.c b/src/libbsc/bsc_vty.c
index 722753a..bd363ae 100644
--- a/src/libbsc/bsc_vty.c
+++ b/src/libbsc/bsc_vty.c
@@ -30,6 +30,7 @@
 #include <osmocom/vty/misc.h>
 #include <osmocom/gsm/protocol/gsm_04_08.h>
 #include <osmocom/gsm/gsm0502.h>
+#include <osmocom/ctrl/control_if.h>
 
 #include <arpa/inet.h>
 
@@ -776,6 +777,11 @@
        return CMD_SUCCESS;
 }
 
+/* small helper macro for conditional dumping of timer */
+#define VTY_OUT_TIMER(number)  \
+       if (gsmnet->T##number != GSM_T##number##_DEFAULT)       \
+               vty_out(vty, " timer t"#number" %u%s", gsmnet->T##number, 
VTY_NEWLINE)
+
 static int config_write_net(struct vty *vty)
 {
        struct gsm_network *gsmnet = gsmnet_from_vty(vty);
@@ -812,18 +818,18 @@
                gsmnet->handover.pwr_hysteresis, VTY_NEWLINE);
        vty_out(vty, " handover maximum distance %u%s",
                gsmnet->handover.max_distance, VTY_NEWLINE);
-       vty_out(vty, " timer t3101 %u%s", gsmnet->T3101, VTY_NEWLINE);
-       vty_out(vty, " timer t3103 %u%s", gsmnet->T3103, VTY_NEWLINE);
-       vty_out(vty, " timer t3105 %u%s", gsmnet->T3105, VTY_NEWLINE);
-       vty_out(vty, " timer t3107 %u%s", gsmnet->T3107, VTY_NEWLINE);
-       vty_out(vty, " timer t3109 %u%s", gsmnet->T3109, VTY_NEWLINE);
-       vty_out(vty, " timer t3111 %u%s", gsmnet->T3111, VTY_NEWLINE);
-       vty_out(vty, " timer t3113 %u%s", gsmnet->T3113, VTY_NEWLINE);
-       vty_out(vty, " timer t3115 %u%s", gsmnet->T3115, VTY_NEWLINE);
-       vty_out(vty, " timer t3117 %u%s", gsmnet->T3117, VTY_NEWLINE);
-       vty_out(vty, " timer t3119 %u%s", gsmnet->T3119, VTY_NEWLINE);
-       vty_out(vty, " timer t3122 %u%s", gsmnet->T3122, VTY_NEWLINE);
-       vty_out(vty, " timer t3141 %u%s", gsmnet->T3141, VTY_NEWLINE);
+       VTY_OUT_TIMER(3101);
+       VTY_OUT_TIMER(3103);
+       VTY_OUT_TIMER(3105);
+       VTY_OUT_TIMER(3107);
+       VTY_OUT_TIMER(3109);
+       VTY_OUT_TIMER(3111);
+       VTY_OUT_TIMER(3113);
+       VTY_OUT_TIMER(3115);
+       VTY_OUT_TIMER(3117);
+       VTY_OUT_TIMER(3119);
+       VTY_OUT_TIMER(3122);
+       VTY_OUT_TIMER(3141);
        vty_out(vty, " dyn_ts_allow_tch_f %d%s",
                gsmnet->dyn_ts_allow_tch_f ? 1 : 0, VTY_NEWLINE);
        if (gsmnet->tz.override != 0) {
@@ -1534,38 +1540,43 @@
        return CMD_SUCCESS;
 }
 
+#define DEFAULT_TIMER(number) GSM_T##number##_DEFAULT
+/* Add another expansion so that DEFAULT_TIMER() becomes its value */
+#define EXPAND_AND_STRINGIFY(x) OSMO_STRINGIFY(x)
+
 #define DECLARE_TIMER(number, doc) \
     DEFUN(cfg_net_T##number,                                   \
       cfg_net_T##number##_cmd,                                 \
-      "timer t" #number  " <0-65535>",                         \
+      "timer t" #number  " (default|<1-65535>)",               \
       "Configure GSM Timers\n"                                 \
-      doc "Timer Value in seconds\n")                          \
+      doc " (default: " EXPAND_AND_STRINGIFY(DEFAULT_TIMER(number)) " 
seconds)\n" \
+      "Set to default timer value"                             \
+         " (" EXPAND_AND_STRINGIFY(DEFAULT_TIMER(number)) " seconds)\n" \
+      "Timer Value in seconds\n")                              \
 {                                                              \
        struct gsm_network *gsmnet = gsmnet_from_vty(vty);      \
-       int value = atoi(argv[0]);                              \
-                                                               \
-       if (value < 0 || value > 65535) {                       \
-               vty_out(vty, "Timer value %s out of range.%s",  \
-                       argv[0], VTY_NEWLINE);                  \
-               return CMD_WARNING;                             \
-       }                                                       \
+       int value;                                              \
+       if (strcmp(argv[0], "default") == 0)                    \
+               value = DEFAULT_TIMER(number);                  \
+       else                                                    \
+               value = atoi(argv[0]);                          \
                                                                \
        gsmnet->T##number = value;                              \
        return CMD_SUCCESS;                                     \
 }
 
-DECLARE_TIMER(3101, "Set the timeout value for IMMEDIATE ASSIGNMENT.\n")
-DECLARE_TIMER(3103, "Set the timeout value for HANDOVER.\n")
-DECLARE_TIMER(3105, "Set the timer for repetition of PHYSICAL INFORMATION.\n")
-DECLARE_TIMER(3107, "Currently not used.\n")
-DECLARE_TIMER(3109, "Set the RSL SACCH deactivation timeout.\n")
-DECLARE_TIMER(3111, "Set the RSL timeout to wait before releasing the RF 
Channel.\n")
-DECLARE_TIMER(3113, "Set the time to try paging a subscriber.\n")
-DECLARE_TIMER(3115, "Currently not used.\n")
-DECLARE_TIMER(3117, "Currently not used.\n")
-DECLARE_TIMER(3119, "Currently not used.\n")
-DECLARE_TIMER(3122, "Waiting time (seconds) after IMM ASS REJECT\n")
-DECLARE_TIMER(3141, "Currently not used.\n")
+DECLARE_TIMER(3101, "Set the timeout value for IMMEDIATE ASSIGNMENT")
+DECLARE_TIMER(3103, "Set the timeout value for HANDOVER")
+DECLARE_TIMER(3105, "Set the timer for repetition of PHYSICAL INFORMATION")
+DECLARE_TIMER(3107, "Currently not used")
+DECLARE_TIMER(3109, "Set the RSL SACCH deactivation timeout")
+DECLARE_TIMER(3111, "Set the RSL timeout to wait before releasing the RF 
Channel")
+DECLARE_TIMER(3113, "Set the time to try paging a subscriber")
+DECLARE_TIMER(3115, "Currently not used")
+DECLARE_TIMER(3117, "Currently not used")
+DECLARE_TIMER(3119, "Currently not used")
+DECLARE_TIMER(3122, "Waiting time (seconds) after IMM ASS REJECT")
+DECLARE_TIMER(3141, "Currently not used")
 
 DEFUN_DEPRECATED(cfg_net_dtx,
                 cfg_net_dtx_cmd,
@@ -3801,6 +3812,38 @@
        return CMD_SUCCESS;
 }
 
+DEFUN(bts_resend, bts_resend_cmd,
+      "bts <0-255> resend-system-information",
+      "BTS Specific Commands\n" "BTS Number\n"
+      "Re-generate + re-send BCCH SYSTEM INFORMATION\n")
+{
+       struct gsm_network *gsmnet;
+       struct gsm_bts_trx *trx;
+       struct gsm_bts *bts;
+       unsigned int bts_nr;
+
+       gsmnet = gsmnet_from_vty(vty);
+
+       bts_nr = atoi(argv[0]);
+       if (bts_nr >= gsmnet->num_bts) {
+               vty_out(vty, "BTS number must be between 0 and %d. It was 
%d.%s",
+                       gsmnet->num_bts, bts_nr, VTY_NEWLINE);
+               return CMD_WARNING;
+       }
+
+       bts = gsm_bts_num(gsmnet, bts_nr);
+       if (!bts) {
+               vty_out(vty, "BTS Nr. %d could not be found.%s", bts_nr, 
VTY_NEWLINE);
+               return CMD_WARNING;
+       }
+
+       llist_for_each_entry_reverse(trx, &bts->trx_list, list)
+               gsm_bts_trx_set_system_infos(trx);
+
+       return CMD_SUCCESS;
+}
+
+
 DEFUN(smscb_cmd, smscb_cmd_cmd,
        "bts <0-255> smscb-command <1-4> HEXSTRING",
        "BTS related commands\n" "BTS Number\n"
@@ -4061,6 +4104,20 @@
        rsl_ipacc_mdcx(lchan, ntohl(ia.s_addr), port, 0);
        return CMD_SUCCESS;
 }
+
+DEFUN(ctrl_trap, ctrl_trap_cmd,
+       "ctrl-interface generate-trap TRAP VALUE",
+       "Commands related to the CTRL Interface\n"
+       "Generate a TRAP for test purpose\n"
+       "Identity/Name of the TRAP variable\n"
+       "Value of the TRAP variable\n")
+{
+       struct gsm_network *net = gsmnet_from_vty(vty);
+
+       ctrl_cmd_send_trap(net->ctrl, argv[0], (char *) argv[1]);
+       return CMD_SUCCESS;
+}
+
 extern int bsc_vty_init_extra(void);
 
 int bsc_vty_init(struct gsm_network *network)
@@ -4256,10 +4313,12 @@
 
        install_element(ENABLE_NODE, &drop_bts_cmd);
        install_element(ENABLE_NODE, &restart_bts_cmd);
+       install_element(ENABLE_NODE, &bts_resend_cmd);
        install_element(ENABLE_NODE, &pdch_act_cmd);
        install_element(ENABLE_NODE, &lchan_act_cmd);
        install_element(ENABLE_NODE, &lchan_mdcx_cmd);
        install_element(ENABLE_NODE, &smscb_cmd_cmd);
+       install_element(ENABLE_NODE, &ctrl_trap_cmd);
 
        abis_nm_vty_init();
        abis_om2k_vty_init();
diff --git a/src/libbsc/handover_logic.c b/src/libbsc/handover_logic.c
index c03563f..57d1dcd 100644
--- a/src/libbsc/handover_logic.c
+++ b/src/libbsc/handover_logic.c
@@ -282,6 +282,7 @@
 
        new_lchan->conn->ho_lchan = NULL;
        new_lchan->conn->lchan = new_lchan;
+       new_lchan->conn->bts = new_lchan->ts->trx->bts;
        ho->old_lchan->conn = NULL;
 
        lchan_release(ho->old_lchan, 0, RSL_REL_LOCAL_END);
diff --git a/src/libbsc/net_init.c b/src/libbsc/net_init.c
index bc5ed35..9d54319 100644
--- a/src/libbsc/net_init.c
+++ b/src/libbsc/net_init.c
@@ -44,10 +44,17 @@
        net->num_bts = 0;
        net->reject_cause = GSM48_REJECT_ROAMING_NOT_ALLOWED;
        net->T3101 = GSM_T3101_DEFAULT;
+       net->T3103 = GSM_T3103_DEFAULT;
        net->T3105 = GSM_T3105_DEFAULT;
+       net->T3107 = GSM_T3107_DEFAULT;
+       net->T3109 = GSM_T3109_DEFAULT;
+       net->T3111 = GSM_T3111_DEFAULT;
        net->T3113 = GSM_T3113_DEFAULT;
+       net->T3115 = GSM_T3115_DEFAULT;
+       net->T3117 = GSM_T3117_DEFAULT;
+       net->T3119 = GSM_T3119_DEFAULT;
        net->T3122 = GSM_T3122_DEFAULT;
-       /* FIXME: initialize all other timers! */
+       net->T3141 = GSM_T3141_DEFAULT;
 
        /* default set of handover parameters */
        net->handover.win_rxlev_avg = 10;
@@ -61,6 +68,10 @@
 
        /* init statistics */
        net->bsc_ctrs = rate_ctr_group_alloc(net, &bsc_ctrg_desc, 0);
+       if (!net->bsc_ctrs) {
+               talloc_free(net);
+               return NULL;
+       }
 
        gsm_net_update_ctype(net);
 
diff --git a/src/libcommon-cs/common_cs.c b/src/libcommon-cs/common_cs.c
index 8e19bb2..99206c8 100644
--- a/src/libcommon-cs/common_cs.c
+++ b/src/libcommon-cs/common_cs.c
@@ -68,6 +68,10 @@
 
        /* init statistics */
        net->msc_ctrs = rate_ctr_group_alloc(net, &msc_ctrg_desc, 0);
+       if (!net->msc_ctrs) {
+               talloc_free(net);
+               return NULL;
+       }
        net->active_calls = osmo_counter_alloc("msc.active_calls");
 
        net->mncc_recv = mncc_recv;
diff --git a/src/libcommon/gsm_data.c b/src/libcommon/gsm_data.c
index b5bf059..7be2240 100644
--- a/src/libcommon/gsm_data.c
+++ b/src/libcommon/gsm_data.c
@@ -254,12 +254,13 @@
        if (!model && type != GSM_BTS_TYPE_UNKNOWN)
                return NULL;
 
-       bts = gsm_bts_alloc(net);
+       bts = gsm_bts_alloc(net, net->num_bts);
        if (!bts)
                return NULL;
 
+       net->num_bts++;
+
        bts->network = net;
-       bts->nr = net->num_bts++;
        bts->type = type;
        bts->model = model;
        bts->bsic = bsic;
diff --git a/src/libcommon/gsm_data_shared.c b/src/libcommon/gsm_data_shared.c
index 8992636..2696273 100644
--- a/src/libcommon/gsm_data_shared.c
+++ b/src/libcommon/gsm_data_shared.c
@@ -312,7 +312,7 @@
        .initial_mcs = 6,
 };
 
-struct gsm_bts *gsm_bts_alloc(void *ctx)
+struct gsm_bts *gsm_bts_alloc(void *ctx, uint8_t bts_num)
 {
        struct gsm_bts *bts = talloc_zero(ctx, struct gsm_bts);
        int i;
@@ -320,6 +320,7 @@
        if (!bts)
                return NULL;
 
+       bts->nr = bts_num;
        bts->num_trx = 0;
        INIT_LLIST_HEAD(&bts->trx_list);
        bts->ms_max_power = 15; /* dBm */
@@ -643,11 +644,14 @@
 
        switch (pchan) {
        case GSM_PCHAN_TCH_F:
-       case GSM_PCHAN_PDCH:
        case GSM_PCHAN_TCH_F_PDCH:
                OSMO_ASSERT(lchan_nr == 0);
                cbits = 0x01;
                break;
+       case GSM_PCHAN_PDCH:
+               OSMO_ASSERT(lchan_nr == 0);
+               cbits = RSL_CHAN_OSMO_PDCH >> 3;
+               break;
        case GSM_PCHAN_TCH_H:
                OSMO_ASSERT(lchan_nr < 2);
                cbits = 0x02;
diff --git a/src/libmgcp/mgcp_osmux.c b/src/libmgcp/mgcp_osmux.c
index b46a80e..c52984b 100644
--- a/src/libmgcp/mgcp_osmux.c
+++ b/src/libmgcp/mgcp_osmux.c
@@ -340,8 +340,7 @@
        if (endp->osmux.state == OSMUX_STATE_ENABLED)
                goto out;
 
-       if (osmux_enable_endpoint(endp, OSMUX_ROLE_BSC_NAT,
-                                 &addr->sin_addr, addr->sin_port) < 0 ){
+       if (osmux_enable_endpoint(endp, &addr->sin_addr, addr->sin_port) < 0 ) {
                LOGP(DMGCP, LOGL_ERROR,
                     "Could not enable osmux in endpoint %d\n",
                     ENDPOINT_NUMBER(endp));
@@ -433,8 +432,7 @@
        return 0;
 }
 
-int osmux_enable_endpoint(struct mgcp_endpoint *endp, int role,
-                         struct in_addr *addr, uint16_t port)
+int osmux_enable_endpoint(struct mgcp_endpoint *endp, struct in_addr *addr, 
uint16_t port)
 {
        /* If osmux is enabled, initialize the output handler. This handler is
         * used to reconstruct the RTP flow from osmux. The RTP SSRC is
@@ -522,8 +520,7 @@
                return 0;
 
        if (endp->osmux.state == OSMUX_STATE_ACTIVATING) {
-               if (osmux_enable_endpoint(endp, OSMUX_ROLE_BSC,
-                                         &endp->net_end.addr,
+               if (osmux_enable_endpoint(endp, &endp->net_end.addr,
                                          htons(endp->cfg->osmux_port)) < 0) {
                        LOGP(DMGCP, LOGL_ERROR,
                             "Could not activate osmux in endpoint %d\n",
diff --git a/src/libmgcp/mgcp_protocol.c b/src/libmgcp/mgcp_protocol.c
index 78e41f1..96542c5 100644
--- a/src/libmgcp/mgcp_protocol.c
+++ b/src/libmgcp/mgcp_protocol.c
@@ -277,10 +277,12 @@
        if (!addr)
                addr = mgcp_net_src_addr(endp);
 
-       if (endp->osmux.state == OSMUX_STATE_NEGOTIATING)
+       if (endp->osmux.state == OSMUX_STATE_NEGOTIATING) {
                sprintf(osmux_extension, "\nX-Osmux: %u", endp->osmux.cid);
-       else
+               endp->osmux.state = OSMUX_STATE_ACTIVATING;
+       } else {
                osmux_extension[0] = '\0';
+       }
 
        len = snprintf(sdp_record, sizeof(sdp_record),
                       "I: %u%s\n\n", endp->ci, osmux_extension);
diff --git a/src/libmsc/Makefile.am b/src/libmsc/Makefile.am
index 16154ff..4726bbe 100644
--- a/src/libmsc/Makefile.am
+++ b/src/libmsc/Makefile.am
@@ -30,6 +30,7 @@
        db.c \
        gsm_04_08.c \
        gsm_04_11.c \
+       gsm_04_14.c \
        gsm_04_80.c \
        gsm_subscriber.c \
        iucs.c \
diff --git a/src/libmsc/db.c b/src/libmsc/db.c
index 28e9782..ae7e287 100644
--- a/src/libmsc/db.c
+++ b/src/libmsc/db.c
@@ -48,7 +48,7 @@
 static char *db_dirname = NULL;
 static dbi_conn conn;
 
-#define SCHEMA_REVISION "4"
+#define SCHEMA_REVISION "5"
 
 enum {
        SCHEMA_META,
@@ -122,6 +122,8 @@
                "valid_until TIMESTAMP, "
                "reply_path_req INTEGER NOT NULL, "
                "status_rep_req INTEGER NOT NULL, "
+               "is_report INTEGER NOT NULL, "
+               "msg_ref INTEGER NOT NULL, "
                "protocol_id INTEGER NOT NULL, "
                "data_coding_scheme INTEGER NOT NULL, "
                "ud_hdr_ind INTEGER NOT NULL, "
@@ -378,6 +380,152 @@
        return -EINVAL;
 }
 
+/* Just like v3, but there is a new message reference field for status reports,
+ * that is set to zero for existing entries since there is no way we can infer
+ * this.
+ */
+static struct gsm_sms *sms_from_result_v4(dbi_result result)
+{
+       struct gsm_sms *sms = sms_alloc();
+       const unsigned char *user_data;
+       const char *text, *addr;
+
+       if (!sms)
+               return NULL;
+
+       sms->id = dbi_result_get_ulonglong(result, "id");
+
+       sms->reply_path_req = dbi_result_get_ulonglong(result, 
"reply_path_req");
+       sms->status_rep_req = dbi_result_get_ulonglong(result, 
"status_rep_req");
+       sms->ud_hdr_ind = dbi_result_get_ulonglong(result, "ud_hdr_ind");
+       sms->protocol_id = dbi_result_get_ulonglong(result, "protocol_id");
+       sms->data_coding_scheme = dbi_result_get_ulonglong(result,
+                                                 "data_coding_scheme");
+
+       addr = dbi_result_get_string(result, "src_addr");
+       osmo_strlcpy(sms->src.addr, addr, sizeof(sms->src.addr));
+       sms->src.ton = dbi_result_get_ulonglong(result, "src_ton");
+       sms->src.npi = dbi_result_get_ulonglong(result, "src_npi");
+
+       addr = dbi_result_get_string(result, "dest_addr");
+       osmo_strlcpy(sms->dst.addr, addr, sizeof(sms->dst.addr));
+       sms->dst.ton = dbi_result_get_ulonglong(result, "dest_ton");
+       sms->dst.npi = dbi_result_get_ulonglong(result, "dest_npi");
+
+       sms->user_data_len = dbi_result_get_field_length(result, "user_data");
+       user_data = dbi_result_get_binary(result, "user_data");
+       if (sms->user_data_len > sizeof(sms->user_data))
+               sms->user_data_len = (uint8_t) sizeof(sms->user_data);
+       memcpy(sms->user_data, user_data, sms->user_data_len);
+
+       text = dbi_result_get_string(result, "text");
+       if (text)
+               osmo_strlcpy(sms->text, text, sizeof(sms->text));
+       return sms;
+}
+
+static int update_db_revision_4(void)
+{
+       dbi_result result;
+       struct gsm_sms *sms;
+
+       LOGP(DDB, LOGL_NOTICE, "Going to migrate from revision 4\n");
+
+       result = dbi_conn_query(conn, "BEGIN EXCLUSIVE TRANSACTION");
+       if (!result) {
+               LOGP(DDB, LOGL_ERROR,
+                       "Failed to begin transaction (upgrade from rev 4)\n");
+               return -EINVAL;
+       }
+       dbi_result_free(result);
+
+       /* Rename old SMS table to be able create a new one */
+       result = dbi_conn_query(conn, "ALTER TABLE SMS RENAME TO SMS_4");
+       if (!result) {
+               LOGP(DDB, LOGL_ERROR,
+                    "Failed to rename the old SMS table (upgrade from rev 
4).\n");
+               goto rollback;
+       }
+       dbi_result_free(result);
+
+       /* Create new SMS table with all the bells and whistles! */
+       result = dbi_conn_query(conn, create_stmts[SCHEMA_SMS]);
+       if (!result) {
+               LOGP(DDB, LOGL_ERROR,
+                    "Failed to create a new SMS table (upgrade from rev 
4).\n");
+               goto rollback;
+       }
+       dbi_result_free(result);
+
+       /* Cycle through old messages and convert them to the new format */
+       result = dbi_conn_query(conn, "SELECT * FROM SMS_4");
+       if (!result) {
+               LOGP(DDB, LOGL_ERROR,
+                    "Failed fetch messages from the old SMS table (upgrade 
from rev 4).\n");
+               goto rollback;
+       }
+       while (dbi_result_next_row(result)) {
+               sms = sms_from_result_v4(result);
+               if (db_sms_store(sms) != 0) {
+                       LOGP(DDB, LOGL_ERROR, "Failed to store message to the 
new SMS table(upgrade from rev 4).\n");
+                       sms_free(sms);
+                       dbi_result_free(result);
+                       goto rollback;
+               }
+               sms_free(sms);
+       }
+       dbi_result_free(result);
+
+       /* Remove the temporary table */
+       result = dbi_conn_query(conn, "DROP TABLE SMS_4");
+       if (!result) {
+               LOGP(DDB, LOGL_ERROR,
+                    "Failed to drop the old SMS table (upgrade from rev 
4).\n");
+               goto rollback;
+       }
+       dbi_result_free(result);
+
+       /* We're done. Bump DB Meta revision to 4 */
+       result = dbi_conn_query(conn,
+                               "UPDATE Meta "
+                               "SET value = '5' "
+                               "WHERE key = 'revision'");
+       if (!result) {
+               LOGP(DDB, LOGL_ERROR,
+                    "Failed to update DB schema revision (upgrade from rev 
4).\n");
+               goto rollback;
+       }
+       dbi_result_free(result);
+
+       result = dbi_conn_query(conn, "COMMIT TRANSACTION");
+       if (!result) {
+               LOGP(DDB, LOGL_ERROR,
+                       "Failed to commit the transaction (upgrade from rev 
4)\n");
+               return -EINVAL;
+       } else {
+               dbi_result_free(result);
+       }
+
+       /* Shrink DB file size by actually wiping out SMS_4 table data */
+       result = dbi_conn_query(conn, "VACUUM");
+       if (!result)
+               LOGP(DDB, LOGL_ERROR,
+                       "VACUUM failed. Ignoring it (upgrade from rev 4).\n");
+       else
+               dbi_result_free(result);
+
+       return 0;
+
+rollback:
+       result = dbi_conn_query(conn, "ROLLBACK TRANSACTION");
+       if (!result)
+               LOGP(DDB, LOGL_ERROR,
+                       "Rollback failed (upgrade from rev 4).\n");
+       else
+               dbi_result_free(result);
+       return -EINVAL;
+}
+
 static int check_db_revision(void)
 {
        dbi_result result;
@@ -420,6 +568,9 @@
                        goto error;
        case 3:
                if (update_db_revision_3())
+                       goto error;
+       case 4:
+               if (update_db_revision_4())
                        goto error;
 
        /* The end of waterfall */
@@ -546,20 +697,23 @@
        result = dbi_conn_queryf(conn,
                "INSERT INTO SMS "
                "(created, valid_until, "
-                "reply_path_req, status_rep_req, protocol_id, "
-                "data_coding_scheme, ud_hdr_ind, "
+                "reply_path_req, status_rep_req, is_report, "
+                "msg_ref, protocol_id, data_coding_scheme, "
+                "ud_hdr_ind, "
                 "user_data, text, "
                 "dest_addr, dest_ton, dest_npi, "
                 "src_addr, src_ton, src_npi) VALUES "
                "(datetime('now'), %u, "
                "%u, %u, %u, "
-               "%u, %u, "
+               "%u, %u, %u, "
+               "%u, "
                "%s, %s, "
                "%s, %u, %u, "
                "%s, %u, %u)",
                validity_timestamp,
-               sms->reply_path_req, sms->status_rep_req, sms->protocol_id,
-               sms->data_coding_scheme, sms->ud_hdr_ind,
+               sms->reply_path_req, sms->status_rep_req, sms->is_report,
+               sms->msg_ref, sms->protocol_id, sms->data_coding_scheme,
+               sms->ud_hdr_ind,
                q_udata, q_text,
                q_daddr, sms->dst.ton, sms->dst.npi,
                q_saddr, sms->src.ton, sms->src.npi);
@@ -588,13 +742,15 @@
 
        /* FIXME: validity */
        /* FIXME: those should all be get_uchar, but sqlite3 is braindead */
+       sms->created = dbi_result_get_datetime(result, "created");
        sms->reply_path_req = dbi_result_get_ulonglong(result, 
"reply_path_req");
        sms->status_rep_req = dbi_result_get_ulonglong(result, 
"status_rep_req");
+       sms->is_report = dbi_result_get_ulonglong(result, "is_report");
+       sms->msg_ref = dbi_result_get_ulonglong(result, "msg_ref");
        sms->ud_hdr_ind = dbi_result_get_ulonglong(result, "ud_hdr_ind");
        sms->protocol_id = dbi_result_get_ulonglong(result, "protocol_id");
        sms->data_coding_scheme = dbi_result_get_ulonglong(result,
                                                  "data_coding_scheme");
-       /* sms->msg_ref is temporary and not stored in DB */
 
        sms->dst.npi = dbi_result_get_ulonglong(result, "dest_npi");
        sms->dst.ton = dbi_result_get_ulonglong(result, "dest_ton");
diff --git a/src/libmsc/gsm_04_08.c b/src/libmsc/gsm_04_08.c
index d97bde2..28cba5b 100644
--- a/src/libmsc/gsm_04_08.c
+++ b/src/libmsc/gsm_04_08.c
@@ -43,6 +43,7 @@
 #include <openbsc/gsm_04_11.h>
 #include <openbsc/gsm_04_08.h>
 #include <openbsc/gsm_04_80.h>
+#include <openbsc/gsm_04_14.h>
 #include <openbsc/abis_rsl.h>
 #include <openbsc/chan_alloc.h>
 #include <openbsc/paging.h>
@@ -496,12 +497,19 @@
        else {
                /* Need to get GSM offset and convert into 15 min units */
                /* This probably breaks if gmtoff returns a value not evenly 
divisible by 15? */
-               local_time = localtime(&cur_t);
 #ifdef HAVE_TM_GMTOFF_IN_TM
+               local_time = localtime(&cur_t);
                tzunits = (local_time->tm_gmtoff/60)/15;
 #else
-#warning find a portable way to obtain the timezone offset
-               tzunits = 0;
+               /* find timezone offset */
+               time_t utc;
+               double offsetFromUTC;
+               utc = mktime(gmt_time);
+               local_time = localtime(&cur_t);
+               offsetFromUTC = difftime(cur_t, utc);
+               if (local_time->tm_isdst)
+                       offsetFromUTC += 3600.0;
+               tzunits = ((int)offsetFromUTC) / 60 / 15;
 #endif
                if (tzunits < 0) {
                        tzunits = tzunits/-1;
@@ -3072,6 +3080,9 @@
        case GSM48_PDISC_NC_SS:
                rc = handle_rcv_ussd(conn, msg);
                break;
+       case GSM48_PDISC_TEST:
+               rc = gsm0414_rcv_test(conn, msg);
+               break;
        default:
                LOGP(DRLL, LOGL_NOTICE, "Unknown "
                        "GSM 04.08 discriminator 0x%02x\n", pdisc);
diff --git a/src/libmsc/gsm_04_11.c b/src/libmsc/gsm_04_11.c
index bdf2ad7..c5bcce7 100644
--- a/src/libmsc/gsm_04_11.c
+++ b/src/libmsc/gsm_04_11.c
@@ -189,7 +189,7 @@
        return gsm411_smc_send(&trans->sms.smc_inst, msg_type, msg);
 }
 
-static int gsm340_rx_sms_submit(struct msgb *msg, struct gsm_sms *gsms)
+static int gsm340_rx_sms_submit(struct gsm_sms *gsms)
 {
        if (db_sms_store(gsms) != 0) {
                LOGP(DLSMS, LOGL_ERROR, "Failed to store SMS in Database\n");
@@ -215,9 +215,9 @@
 {
        uint8_t *smsp;
        uint8_t oa[12]; /* max len per 03.40 */
-       uint8_t oa_len = 0;
        uint8_t octet_len;
        unsigned int old_msg_len = msg->len;
+       int oa_len;
 
        /* generate first octet with masked bits */
        smsp = msgb_put(msg, 1);
@@ -235,6 +235,9 @@
 
        /* generate originator address */
        oa_len = gsm340_gen_oa_sub(oa, sizeof(oa), &sms->src);
+       if (oa_len < 0)
+               return -ENOSPC;
+
        smsp = msgb_put(msg, oa_len);
        memcpy(smsp, oa, oa_len);
 
@@ -279,8 +282,55 @@
        return msg->len - old_msg_len;
 }
 
-int sms_route_mt_sms(struct gsm_subscriber_connection *conn, struct msgb *msg,
-                    struct gsm_sms *gsms, uint8_t sms_mti, bool *deferred)
+/* As defined by GSM 03.40, Section 9.2.2.3. */
+static int gsm340_gen_sms_status_report_tpdu(struct msgb *msg,
+                                            struct gsm_sms *sms)
+{
+       unsigned int old_msg_len = msg->len;
+       uint8_t oa[12]; /* max len per 03.40 */
+       uint8_t *smsp;
+       int oa_len;
+
+       /* generate first octet with masked bits */
+       smsp = msgb_put(msg, 1);
+       /* TP-MTI (message type indicator) */
+       *smsp = GSM340_SMS_STATUS_REP_SC2MS;
+       /* TP-MMS (more messages to send) */
+       if (0 /* FIXME */)
+               *smsp |= 0x04;
+       /* TP-MR (message reference) */
+       smsp = msgb_put(msg, 1);
+       *smsp = sms->msg_ref;
+
+       /* generate recipient address */
+       oa_len = gsm340_gen_oa_sub(oa, sizeof(oa), &sms->src);
+       if (oa_len < 0)
+               return -ENOSPC;
+
+       smsp = msgb_put(msg, oa_len);
+       memcpy(smsp, oa, oa_len);
+
+       /* generate TP-SCTS (Service centre timestamp) */
+       smsp = msgb_put(msg, 7);
+       gsm340_gen_scts(smsp, sms->created);
+
+       /* generate TP-DT (Discharge time, in TP-SCTS format). */
+       smsp = msgb_put(msg, 7);
+       gsm340_gen_scts(smsp, sms->created);
+
+       /* TP-ST (status) */
+       smsp = msgb_put(msg, 1);
+       /* From GSM 03.40, Section 9.2.3.15, 0x00 means OK. */
+       *smsp = 0x00;
+
+       LOGP(DLSMS, LOGL_INFO, "sending status report for SMS reference %x\n",
+            sms->msg_ref);
+
+       return msg->len - old_msg_len;
+}
+
+static int sms_route_mt_sms(struct gsm_subscriber_connection *conn,
+                           struct gsm_sms *gsms)
 {
        int rc;
 
@@ -294,8 +344,9 @@
         * delivery of the SMS.
         */
        if (smpp_first) {
-               rc = smpp_try_deliver(gsms, conn, deferred);
+               rc = smpp_try_deliver(gsms, conn);
                if (rc == GSM411_RP_CAUSE_MO_NUM_UNASSIGNED)
+                       /* unknown subscriber, try local */
                        goto try_local;
                if (rc < 0) {
                        LOGP(DLSMS, LOGL_ERROR, "%s: SMS delivery error: %d.",
@@ -322,7 +373,7 @@
                        return GSM411_RP_CAUSE_MO_NUM_UNASSIGNED;
                }
 
-               rc = smpp_try_deliver(gsms, conn, deferred);
+               rc = smpp_try_deliver(gsms, conn);
                if (rc == GSM411_RP_CAUSE_MO_NUM_UNASSIGNED) {
                        
rate_ctr_inc(&conn->network->msc_ctrs->ctr[MSC_CTR_SMS_NO_RECEIVER]);
                } else if (rc < 0) {
@@ -337,27 +388,7 @@
                rc = GSM411_RP_CAUSE_MO_NUM_UNASSIGNED;
                
rate_ctr_inc(&conn->network->msc_ctrs->ctr[MSC_CTR_SMS_NO_RECEIVER]);
 #endif
-               return rc;
        }
-
-       switch (sms_mti) {
-       case GSM340_SMS_SUBMIT_MS2SC:
-               /* MS is submitting a SMS */
-               rc = gsm340_rx_sms_submit(msg, gsms);
-               break;
-       case GSM340_SMS_COMMAND_MS2SC:
-       case GSM340_SMS_DELIVER_REP_MS2SC:
-               LOGP(DLSMS, LOGL_NOTICE, "Unimplemented MTI 0x%02x\n", sms_mti);
-               rc = GSM411_RP_CAUSE_IE_NOTEXIST;
-               break;
-       default:
-               LOGP(DLSMS, LOGL_NOTICE, "Undefined MTI 0x%02x\n", sms_mti);
-               rc = GSM411_RP_CAUSE_IE_NOTEXIST;
-               break;
-       }
-
-       if (!rc && !gsms->receiver)
-               rc = GSM411_RP_CAUSE_MO_NUM_UNASSIGNED;
 
        return rc;
 }
@@ -366,7 +397,7 @@
 /* process an incoming TPDU (called from RP-DATA)
  * return value > 0: RP CAUSE for ERROR; < 0: silent error; 0 = success */
 static int gsm340_rx_tpdu(struct gsm_trans *trans, struct msgb *msg,
-                         uint32_t gsm411_msg_ref, bool *deferred)
+                         uint32_t gsm411_msg_ref)
 {
        struct gsm_subscriber_connection *conn = trans->conn;
        uint8_t *smsp = msgb_sms(msg);
@@ -387,7 +418,7 @@
        /* invert those fields where 0 means active/present */
        sms_mti = *smsp & 0x03;
        sms_vpf = (*smsp & 0x18) >> 3;
-       gsms->status_rep_req = (*smsp & 0x20);
+       gsms->status_rep_req = (*smsp & 0x20) >> 5;
        gsms->ud_hdr_ind = (*smsp & 0x40);
        /*
         * Not evaluating MMS (More Messages to Send) because the
@@ -487,10 +518,29 @@
        /* FIXME: This looks very wrong */
        send_signal(0, NULL, gsms, 0);
 
-       rc = sms_route_mt_sms(conn, msg, gsms, sms_mti, deferred);
+       rc = sms_route_mt_sms(conn, gsms);
+
+       /* This SMS got routed through SMPP or no receiver exists. */
+       if (!gsms->receiver)
+               return rc;
+
+       switch (sms_mti) {
+       case GSM340_SMS_SUBMIT_MS2SC:
+               /* MS is submitting a SMS */
+               rc = gsm340_rx_sms_submit(gsms);
+               break;
+       case GSM340_SMS_COMMAND_MS2SC:
+       case GSM340_SMS_DELIVER_REP_MS2SC:
+               LOGP(DLSMS, LOGL_NOTICE, "Unimplemented MTI 0x%02x\n", sms_mti);
+               rc = GSM411_RP_CAUSE_IE_NOTEXIST;
+               break;
+       default:
+               LOGP(DLSMS, LOGL_NOTICE, "Undefined MTI 0x%02x\n", sms_mti);
+               rc = GSM411_RP_CAUSE_IE_NOTEXIST;
+               break;
+       }
 out:
-       if (!deferred)
-               sms_free(gsms);
+       sms_free(gsms);
 
        return rc;
 }
@@ -543,7 +593,6 @@
                          uint8_t dst_len, uint8_t *dst,
                          uint8_t tpdu_len, uint8_t *tpdu)
 {
-       bool deferred = false;
        int rc = 0;
 
        if (src_len && src)
@@ -560,8 +609,8 @@
 
        DEBUGP(DLSMS, "DST(%u,%s)\n", dst_len, osmo_hexdump(dst, dst_len));
 
-       rc = gsm340_rx_tpdu(trans, msg, rph->msg_ref, &deferred);
-       if (rc == 0 && !deferred)
+       rc = gsm340_rx_tpdu(trans, msg, rph->msg_ref);
+       if (rc == 0)
                return gsm411_send_rp_ack(trans, rph->msg_ref);
        else if (rc > 0)
                return gsm411_send_rp_error(trans, rph->msg_ref, rc);
@@ -595,6 +644,62 @@
                                rpud_len, rp_ud);
 }
 
+static struct gsm_sms *sms_report_alloc(struct gsm_sms *sms)
+{
+       struct gsm_sms *sms_report;
+       int len;
+
+       sms_report = sms_alloc();
+       OSMO_ASSERT(sms_report);
+
+       sms_report->msg_ref = sms->msg_ref;
+       sms_report->protocol_id = sms->protocol_id;
+       sms_report->data_coding_scheme = GSM338_DCS_1111_8BIT_DATA;
+
+       /* Invert address to send status report back to origin. */
+       sms_report->src = sms->dst;
+       sms_report->dst = sms->src;
+
+       /* As specified by Appendix B. Delivery Receipt Format.
+        * TODO: Many fields in this string are just set with dummy values,
+        *       revisit this.
+        */
+       len = snprintf((char *)sms_report->user_data,
+                      sizeof(sms_report->user_data),
+                      "id:%.08llu sub:000 dlvrd:000 submit date:YYMMDDhhmm 
done date:YYMMDDhhmm stat:DELIVRD err:000 text:%.20s",
+                      sms->id, sms->text);
+       sms_report->user_data_len = len;
+       LOGP(DLSMS, LOGL_NOTICE, "%s\n", sms_report->user_data);
+
+       /* This represents a sms report. */
+       sms_report->is_report = true;
+
+       return sms_report;
+}
+
+static void sms_status_report(struct gsm_sms *gsms,
+                             struct gsm_subscriber_connection *conn)
+{
+       struct gsm_sms *sms_report;
+       int rc;
+
+       sms_report = sms_report_alloc(gsms);
+
+       rc = sms_route_mt_sms(conn, sms_report);
+       if (rc < 0) {
+               LOGP(DLSMS, LOGL_ERROR,
+                    "Failed to send status report! err=%d\n", rc);
+       }
+
+       /* No route via SMPP, send the GSM 03.40 status-report now. */
+       if (gsms->receiver)
+               gsm340_rx_sms_submit(sms_report);
+
+       LOGP(DLSMS, LOGL_NOTICE, "Status report has been sent\n");
+
+       sms_free(sms_report);
+}
+
 /* Receive a 04.11 RP-ACK message (response to RP-DATA from us) */
 static int gsm411_rx_rp_ack(struct msgb *msg, struct gsm_trans *trans,
                            struct gsm411_rp_hdr *rph)
@@ -615,6 +720,9 @@
        db_sms_mark_delivered(sms);
 
        send_signal(S_SMS_DELIVERED, trans, sms, 0);
+
+       if (sms->status_rep_req)
+               sms_status_report(sms, trans->conn);
 
        sms_free(sms);
        trans->sms.sms = NULL;
@@ -940,8 +1048,13 @@
        /* obtain a pointer for the rp_ud_len, so we can fill it later */
        rp_ud_len = (uint8_t *)msgb_put(msg, 1);
 
-       /* generate the 03.40 SMS-DELIVER TPDU */
-       rc = gsm340_gen_sms_deliver_tpdu(msg, sms);
+       if (sms->is_report) {
+               /* generate the 03.40 SMS-STATUS-REPORT TPDU */
+               rc = gsm340_gen_sms_status_report_tpdu(msg, sms);
+       } else {
+               /* generate the 03.40 SMS-DELIVER TPDU */
+               rc = gsm340_gen_sms_deliver_tpdu(msg, sms);
+       }
        if (rc < 0) {
                send_signal(S_SMS_UNKNOWN_ERROR, trans, sms, 0);
                sms_free(sms);
diff --git a/src/libmsc/gsm_04_14.c b/src/libmsc/gsm_04_14.c
new file mode 100644
index 0000000..b529f4c
--- /dev/null
+++ b/src/libmsc/gsm_04_14.c
@@ -0,0 +1,133 @@
+/* GSM MS Testing  Layer 3 messages
+ * 3GPP TS 44.014 / GSM TS 04.14 */
+
+/* (C) 2017 by Harald Welte <[email protected]>
+ *
+ * All Rights Reserved
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "bscconfig.h"
+
+#include <openbsc/debug.h>
+#include <openbsc/gsm_data.h>
+#include <openbsc/gsm_subscriber.h>
+#include <openbsc/gsm_04_08.h>
+#include <openbsc/bsc_api.h>
+#include <openbsc/msc_ifaces.h>
+
+#include <osmocom/gsm/gsm48.h>
+#include <osmocom/gsm/gsm_utils.h>
+#include <osmocom/gsm/protocol/gsm_04_14.h>
+#include <osmocom/gsm/tlv.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/utils.h>
+
+static struct msgb *create_gsm0414_msg(uint8_t msg_type)
+{
+       struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.14");
+       struct gsm48_hdr *gh;
+
+       gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1);
+       gh->proto_discr = GSM48_PDISC_TEST;
+       gh->msg_type = msg_type;
+       return msg;
+}
+
+static int gsm0414_conn_sendmsg(struct gsm_subscriber_connection *conn, struct 
msgb *msg)
+{
+       return msc_tx_dtap(conn, msg);
+}
+
+static int gsm0414_tx_simple(struct gsm_subscriber_connection *conn, uint8_t 
msg_type)
+{
+       struct msgb *msg = create_gsm0414_msg(msg_type);
+
+       return gsm0414_conn_sendmsg(conn, msg);
+}
+
+
+/* Send a CLOSE_TCH_LOOOP_CMD according to Section 8.1 */
+int gsm0414_tx_close_tch_loop_cmd(struct gsm_subscriber_connection *conn,
+                                 enum gsm414_tch_loop_mode loop_mode)
+{
+       struct msgb *msg = create_gsm0414_msg(GSM414_MT_CLOSE_TCH_LOOP_CMD);
+       uint8_t subch;
+
+       subch = (loop_mode << 1);
+       msgb_put_u8(msg, subch);
+
+       msg->lchan = conn->lchan;
+       return gsm0414_conn_sendmsg(conn, msg);
+}
+
+/* Send a OPEN_LOOP_CMD according to Section 8.3 */
+int gsm0414_tx_open_loop_cmd(struct gsm_subscriber_connection *conn)
+{
+       return gsm0414_tx_simple(conn, GSM414_MT_OPEN_LOOP_CMD);
+}
+
+/* Send a ACT_EMMI_CMD according to Section 8.8 */
+int gsm0414_tx_act_emmi_cmd(struct gsm_subscriber_connection *conn)
+{
+       return gsm0414_tx_simple(conn, GSM414_MT_ACT_EMMI_CMD);
+}
+
+/* Send a DEACT_EMMI_CMD according to Section 8.10 */
+int gsm0414_tx_deact_emmi_cmd(struct gsm_subscriber_connection *conn)
+{
+       return gsm0414_tx_simple(conn, GSM414_MT_DEACT_EMMI_CMD);
+}
+
+/* Send a TEST_INTERFACE according to Section 8.11 */
+int gsm0414_tx_test_interface(struct gsm_subscriber_connection *conn,
+                             uint8_t tested_devs)
+{
+       struct msgb *msg = create_gsm0414_msg(GSM414_MT_TEST_INTERFACE);
+       msgb_put_u8(msg, tested_devs);
+       return gsm0414_conn_sendmsg(conn, msg);
+}
+
+/* Send a RESET_MS_POSITION_STORED according to Section 8.11 */
+int gsm0414_tx_reset_ms_pos_store(struct gsm_subscriber_connection *conn,
+                                 uint8_t technology)
+{
+       struct msgb *msg = create_gsm0414_msg(GSM414_MT_RESET_MS_POS_STORED);
+       msgb_put_u8(msg, technology);
+       return gsm0414_conn_sendmsg(conn, msg);
+}
+
+
+
+/* Entry point for incoming GSM48_PDISC_TEST received from MS */
+int gsm0414_rcv_test(struct gsm_subscriber_connection *conn,
+                    struct msgb *msg)
+{
+       struct gsm48_hdr *gh = msgb_l3(msg);
+
+       if (msgb_l3len(msg) < sizeof(*gh))
+               return -1;
+
+       LOGP(DMM, LOGL_NOTICE, "%s: Received TEST class message '%s'\n", 
"FIXME",
+               get_value_string(gsm414_msgt_names, gh->msg_type));
+
+       return 0;
+}
diff --git a/src/libmsc/smpp_openbsc.c b/src/libmsc/smpp_openbsc.c
index 24a4653..431cb4d 100644
--- a/src/libmsc/smpp_openbsc.c
+++ b/src/libmsc/smpp_openbsc.c
@@ -73,27 +73,32 @@
        return vsub;
 }
 
-/*! \brief find a TLV with given tag in list of libsmpp34 TLVs */
-static struct tlv_t *find_tlv(struct tlv_t *head, uint16_t tag)
+static int smpp34_submit_tlv_msg_payload(const struct tlv_t *t,
+                                        const struct submit_sm_t *submit,
+                                        const uint8_t **sms_msg,
+                                        unsigned int *sms_msg_len)
 {
-       struct tlv_t *t;
-
-       for (t = head; t != NULL; t = t->next) {
-               if (t->tag == tag)
-                       return t;
+       if (submit->sm_length) {
+               LOGP(DLSMS, LOGL_ERROR,
+                    "SMPP cannot have payload in TLV _and_ in the header\n");
+               return -1;
        }
-       return NULL;
+       *sms_msg = t->value.octet;
+       *sms_msg_len = t->length;
+
+       return 0;
 }
 
 /*! \brief convert from submit_sm_t to gsm_sms */
 static int submit_to_sms(struct gsm_sms **psms, struct gsm_network *net,
                         const struct submit_sm_t *submit)
 {
+       const uint8_t *sms_msg = NULL;
+       unsigned int sms_msg_len = 0;
        struct vlr_subscr *dest;
+       uint16_t msg_ref = 0;
        struct gsm_sms *sms;
        struct tlv_t *t;
-       const uint8_t *sms_msg;
-       unsigned int sms_msg_len;
        int mode;
 
        dest = subscr_by_dst(net, submit->dest_addr_npi,
@@ -106,30 +111,40 @@
                return ESME_RINVDSTADR;
        }
 
-       t = find_tlv(submit->tlv, TLVID_message_payload);
-       if (t) {
-               if (submit->sm_length) {
-                       /* ERROR: we cannot have both! */
-                       LOGP(DLSMS, LOGL_ERROR, "SMPP Cannot have payload in "
-                               "TLV _and_ in the header\n");
-                       vlr_subscr_put(dest);
-                       return ESME_ROPTPARNOTALLWD;
+       smpp34_tlv_for_each(t, submit->tlv) {
+               switch (t->tag) {
+               case TLVID_message_payload:
+                       if (smpp34_submit_tlv_msg_payload(t, submit, &sms_msg,
+                                                         &sms_msg_len) < 0) {
+                               vlr_subscr_put(dest);
+                               return ESME_ROPTPARNOTALLWD;
+                       }
+                       break;
+               case TLVID_user_message_reference:
+                       msg_ref = t->value.val16;
+                       break;
+               default:
+                       break;
                }
-               sms_msg = t->value.octet;
-               sms_msg_len = t->length;
-       } else if (submit->sm_length > 0 && submit->sm_length < 255) {
-               sms_msg = submit->short_message;
-               sms_msg_len = submit->sm_length;
-       } else {
-               LOGP(DLSMS, LOGL_ERROR,
-                       "SMPP neither message payload nor valid sm_length.\n");
-               vlr_subscr_put(dest);
-               return ESME_RINVPARLEN;
+       }
+
+       if (!sms_msg) {
+               if (submit->sm_length > 0 && submit->sm_length < 255) {
+                       sms_msg = submit->short_message;
+                       sms_msg_len = submit->sm_length;
+               } else {
+                       LOGP(DLSMS, LOGL_ERROR,
+                            "SMPP neither message payload nor valid 
sm_length.\n");
+                       vlr_subscr_put(dest);
+                       return ESME_RINVPARLEN;
+               }
        }
 
        sms = sms_alloc();
        sms->source = SMS_SOURCE_SMPP;
        sms->smpp.sequence_nr = submit->sequence_number;
+       sms->status_rep_req = submit->registered_delivery;
+       sms->msg_ref = msg_ref;
 
        /* fill in the destination address */
        sms->receiver = dest;
@@ -143,10 +158,13 @@
        osmo_strlcpy(sms->src.addr, (char *)submit->source_addr,
                     sizeof(sms->src.addr));
 
-       if (submit->esm_class & 0x40)
+       if (submit->esm_class == SMPP34_DELIVERY_ACK)
+               sms->is_report = true;
+
+       if (submit->esm_class & SMPP34_UDHI_IND)
                sms->ud_hdr_ind = 1;
 
-       if (submit->esm_class & 0x80) {
+       if (submit->esm_class & SMPP34_REPLY_PATH) {
                sms->reply_path_req = 1;
 #warning Implement reply path
        }
@@ -222,7 +240,7 @@
        sms->smpp.esme = esme;
        sms->protocol_id = submit->protocol_id;
 
-       switch (submit->esm_class & 3) {
+       switch (submit->esm_class & SMPP34_MSG_MODE_MASK) {
        case 0: /* default */
        case 1: /* datagram */
        case 3: /* store-and-forward */
@@ -419,7 +437,7 @@
        memset(&tlv, 0, sizeof(tlv));
        tlv.tag = tag;
        tlv.length = 2;
-       tlv.value.val16 = htons(val);
+       tlv.value.val16 = val;
        build_tlv(req_tlv, &tlv);
 }
 
@@ -501,7 +519,6 @@
        osmo_timer_del(&cmd->response_timer);
        llist_del(&cmd->list);
        vlr_subscr_put(cmd->vsub);
-       sms_free(cmd->sms);
        talloc_free(cmd);
 }
 
@@ -518,21 +535,24 @@
        struct gsm_subscriber_connection *conn;
        struct gsm_trans *trans;
 
+       if (cmd->is_report)
+               goto out;
+
        conn = connection_for_subscr(cmd->vsub);
        if (!conn) {
                LOGP(DSMPP, LOGL_ERROR, "No connection to subscriber 
anymore\n");
-               return;
+               goto out;
        }
 
-       trans = trans_find_by_id(conn, GSM48_PDISC_SMS,
-                                cmd->sms->gsm411.transaction_id);
+       trans = trans_find_by_id(conn, GSM48_PDISC_SMS, cmd->gsm411_trans_id);
        if (!trans) {
                LOGP(DSMPP, LOGL_ERROR, "GSM transaction %u is gone\n",
-                    cmd->sms->gsm411.transaction_id);
-               return;
+                    cmd->gsm411_trans_id);
+               goto out;
        }
 
-       gsm411_send_rp_ack(trans, cmd->sms->gsm411.msg_ref);
+       gsm411_send_rp_ack(trans, cmd->gsm411_msg_ref);
+out:
        smpp_cmd_free(cmd);
 }
 
@@ -542,25 +562,27 @@
        struct gsm_trans *trans;
        int gsm411_cause;
 
+       if (cmd->is_report)
+               goto out;
+
        conn = connection_for_subscr(cmd->vsub);
        if (!conn) {
                LOGP(DSMPP, LOGL_ERROR, "No connection to subscriber 
anymore\n");
-               return;
+               goto out;
        }
 
-       trans = trans_find_by_id(conn, GSM48_PDISC_SMS,
-                                cmd->sms->gsm411.transaction_id);
+       trans = trans_find_by_id(conn, GSM48_PDISC_SMS, cmd->gsm411_trans_id);
        if (!trans) {
                LOGP(DSMPP, LOGL_ERROR, "GSM transaction %u is gone\n",
-                    cmd->sms->gsm411.transaction_id);
-               return;
+                    cmd->gsm411_trans_id);
+               goto out;
        }
 
        if (smpp_to_gsm411_err(status, &gsm411_cause) < 0)
                gsm411_cause = GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER;
 
-       gsm411_send_rp_error(trans, cmd->sms->gsm411.msg_ref, gsm411_cause);
-
+       gsm411_send_rp_error(trans, cmd->gsm411_msg_ref, gsm411_cause);
+out:
        smpp_cmd_free(cmd);
 }
 
@@ -571,7 +593,7 @@
 
 static int smpp_cmd_enqueue(struct osmo_esme *esme,
                            struct vlr_subscr *vsub, struct gsm_sms *sms,
-                           uint32_t sequence_number, bool *deferred)
+                           uint32_t sequence_number)
 {
        struct osmo_smpp_cmd *cmd;
 
@@ -580,7 +602,9 @@
                return -1;
 
        cmd->sequence_nr        = sequence_number;
-       cmd->sms                = sms;
+       cmd->is_report          = sms->is_report;
+       cmd->gsm411_msg_ref     = sms->gsm411.msg_ref;
+       cmd->gsm411_trans_id    = sms->gsm411.transaction_id;
        cmd->vsub               = vlr_subscr_get(vsub);
 
        /* FIXME: No predefined value for this response_timer as specified by
@@ -591,7 +615,6 @@
        osmo_timer_setup(&cmd->response_timer, smpp_deliver_sm_cb, cmd);
        osmo_timer_schedule(&cmd->response_timer, 5, 0);
        llist_add_tail(&cmd->list, &esme->smpp_cmd_list);
-       *deferred = true;
 
        return 0;
 }
@@ -609,8 +632,7 @@
 }
 
 static int deliver_to_esme(struct osmo_esme *esme, struct gsm_sms *sms,
-                          struct gsm_subscriber_connection *conn,
-                          bool *deferred)
+                          struct gsm_subscriber_connection *conn)
 {
        struct deliver_sm_t deliver;
        int mode, ret;
@@ -641,15 +663,20 @@
        memcpy(deliver.destination_addr, sms->dst.addr,
                sizeof(deliver.destination_addr));
 
-       deliver.esm_class       = 1;    /* datagram mode */
+       if (sms->is_report)
+               deliver.esm_class = SMPP34_DELIVERY_RECEIPT;
+       else
+               deliver.esm_class = SMPP34_DATAGRAM_MODE;
+
        if (sms->ud_hdr_ind)
-               deliver.esm_class |= 0x40;
+               deliver.esm_class |= SMPP34_UDHI_IND;
        if (sms->reply_path_req)
-               deliver.esm_class |= 0x80;
+               deliver.esm_class |= SMPP34_REPLY_PATH;
 
        deliver.protocol_id     = sms->protocol_id;
        deliver.priority_flag   = 0;
-       deliver.registered_delivery = 0;
+       if (sms->status_rep_req)
+               deliver.registered_delivery = SMPP34_DELIVERY_RECEIPT_ON;
 
        /* Figure out SMPP DCS from TP-DCS */
        dcs = sms->data_coding_scheme;
@@ -679,8 +706,6 @@
        } else {
                deliver.sm_length = sms->user_data_len;
                memcpy(deliver.short_message, sms->user_data, 
deliver.sm_length);
-               deliver.sm_length = sms->user_data_len;
-               memcpy(deliver.short_message, sms->user_data, 
deliver.sm_length);
        }
 
 #if BEFORE_MSCSPLIT
@@ -689,12 +714,15 @@
                append_osmo_tlvs(&deliver.tlv, conn->lchan);
 #endif
 
+       append_tlv_u16(&deliver.tlv, TLVID_user_message_reference,
+                      sms->msg_ref);
+
        ret = smpp_tx_deliver(esme, &deliver);
        if (ret < 0)
                return ret;
 
        return smpp_cmd_enqueue(esme, conn->vsub, sms,
-                               deliver.sequence_number, deferred);
+                               deliver.sequence_number);
 }
 
 static struct smsc *g_smsc;
@@ -705,21 +733,22 @@
 }
 
 int smpp_try_deliver(struct gsm_sms *sms,
-                    struct gsm_subscriber_connection *conn, bool *deferred)
+                    struct gsm_subscriber_connection *conn)
 {
        struct osmo_esme *esme;
        struct osmo_smpp_addr dst;
+       int rc;
 
        memset(&dst, 0, sizeof(dst));
        dst.ton = sms->dst.ton;
        dst.npi = sms->dst.npi;
        memcpy(dst.addr, sms->dst.addr, sizeof(dst.addr));
 
-       esme = smpp_route(g_smsc, &dst);
-       if (!esme)
-               return GSM411_RP_CAUSE_MO_NUM_UNASSIGNED;
+       rc = smpp_route(g_smsc, &dst, &esme);
+       if (!rc)
+               rc = deliver_to_esme(esme, sms, conn);
 
-       return deliver_to_esme(esme, sms, conn, deferred);
+       return rc;
 }
 
 struct smsc *smsc_from_vty(struct vty *v)
diff --git a/src/libmsc/smpp_smsc.c b/src/libmsc/smpp_smsc.c
index 48a1192..04afc49 100644
--- a/src/libmsc/smpp_smsc.c
+++ b/src/libmsc/smpp_smsc.c
@@ -270,8 +270,7 @@
 }
 
 /*! \brief try to find a SMPP route (ESME) for given destination */
-struct osmo_esme *
-smpp_route(const struct smsc *smsc, const struct osmo_smpp_addr *dest)
+int smpp_route(const struct smsc *smsc, const struct osmo_smpp_addr *dest, 
struct osmo_esme **pesme)
 {
        struct osmo_smpp_route *r;
        struct osmo_smpp_acl *acl = NULL;
@@ -314,15 +313,20 @@
                struct osmo_esme *esme;
                DEBUGP(DSMPP, "ACL even has ESME, we can route to it!\n");
                esme = acl->esme;
-               if (esme->bind_flags & ESME_BIND_RX)
-                       return esme;
-               else
+               if (esme->bind_flags & ESME_BIND_RX) {
+                       *pesme = esme;
+                       return 0;
+               } else
                        LOGP(DSMPP, LOGL_NOTICE, "[%s] is matching route, "
                             "but not bound for Rx, discarding MO SMS\n",
                                     esme->system_id);
        }
 
-       return NULL;
+       *pesme = NULL;
+       if (acl)
+               return GSM48_CC_CAUSE_NETWORK_OOO;
+       else
+               return GSM48_CC_CAUSE_UNASSIGNED_NR;
 }
 
 
@@ -654,6 +658,9 @@
 {
        deliver->sequence_number = esme_inc_seq_nr(esme);
 
+       LOGP(DSMPP, LOGL_DEBUG, "[%s] Tx DELIVER-SM (from %s)\n",
+               esme->system_id, deliver->source_addr);
+
        return PACK_AND_SEND(esme, deliver);
 }
 
diff --git a/src/libmsc/smpp_smsc.h b/src/libmsc/smpp_smsc.h
index 4bee59b..755e685 100644
--- a/src/libmsc/smpp_smsc.h
+++ b/src/libmsc/smpp_smsc.h
@@ -89,8 +89,10 @@
 struct osmo_smpp_cmd {
        struct llist_head       list;
        struct vlr_subscr       *vsub;
-       struct gsm_sms          *sms;
        uint32_t                sequence_nr;
+       uint32_t                gsm411_msg_ref;
+       uint8_t                 gsm411_trans_id;
+       bool                    is_report;
        struct osmo_timer_list  response_timer;
 };
 
@@ -126,8 +128,7 @@
 void smpp_esme_get(struct osmo_esme *esme);
 void smpp_esme_put(struct osmo_esme *esme);
 
-struct osmo_esme *
-smpp_route(const struct smsc *smsc, const struct osmo_smpp_addr *dest);
+int smpp_route(const struct smsc *smsc, const struct osmo_smpp_addr *dest, 
struct osmo_esme **emse);
 
 struct osmo_smpp_acl *smpp_acl_alloc(struct smsc *smsc, const char *sys_id);
 struct osmo_smpp_acl *smpp_acl_by_system_id(struct smsc *smsc,
@@ -162,5 +163,5 @@
 int smpp_route_smpp_first(struct gsm_sms *sms,
                            struct gsm_subscriber_connection *conn);
 int smpp_try_deliver(struct gsm_sms *sms,
-                    struct gsm_subscriber_connection *conn, bool *deferred);
+                    struct gsm_subscriber_connection *conn);
 #endif
diff --git a/src/libmsc/transaction.c b/src/libmsc/transaction.c
index 7289a8f..28e0914 100644
--- a/src/libmsc/transaction.c
+++ b/src/libmsc/transaction.c
@@ -88,6 +88,13 @@
 
        DEBUGP(DCC, "subscr=%p, net=%p\n", vsub, net);
 
+       /* a valid subscriber is indispensable */
+       if (vsub == NULL) {
+               LOGP(DCC, LOGL_NOTICE,
+                    "unable to alloc transaction, invalid subscriber 
(NULL)\n");
+               return NULL;
+       }
+
        trans = talloc_zero(tall_trans_ctx, struct gsm_trans);
        if (!trans)
                return NULL;
diff --git a/src/libmsc/vty_interface_layer3.c 
b/src/libmsc/vty_interface_layer3.c
index d1bf6b3..864597d 100644
--- a/src/libmsc/vty_interface_layer3.c
+++ b/src/libmsc/vty_interface_layer3.c
@@ -46,6 +46,7 @@
 #include <openbsc/debug.h>
 #include <openbsc/vty.h>
 #include <openbsc/gsm_04_80.h>
+#include <openbsc/gsm_04_14.h>
 #include <openbsc/chan_alloc.h>
 #include <openbsc/sms_queue.h>
 #include <openbsc/mncc_int.h>
@@ -473,6 +474,97 @@
        return CMD_SUCCESS;
 }
 
+static int loop_by_char(uint8_t ch)
+{
+       switch (ch) {
+       case 'a':
+               return GSM414_LOOP_A;
+       case 'b':
+               return GSM414_LOOP_B;
+       case 'c':
+               return GSM414_LOOP_C;
+       case 'd':
+               return GSM414_LOOP_D;
+       case 'e':
+               return GSM414_LOOP_E;
+       case 'f':
+               return GSM414_LOOP_F;
+       case 'i':
+               return GSM414_LOOP_I;
+       }
+       return -1;
+}
+
+DEFUN(subscriber_mstest_close,
+      subscriber_mstest_close_cmd,
+      "subscriber " SUBSCR_TYPES " ID ms-test close-loop (a|b|c|d|e|f|i)",
+      SUBSCR_HELP "Send a TS 04.14 MS Test Command to subscriber\n"
+      "Close a TCH Loop inside the MS\n"
+      "Loop Type A\n"
+      "Loop Type B\n"
+      "Loop Type C\n"
+      "Loop Type D\n"
+      "Loop Type E\n"
+      "Loop Type F\n"
+      "Loop Type I\n")
+{
+       struct gsm_subscriber_connection *conn;
+       struct gsm_network *gsmnet = gsmnet_from_vty(vty);
+       struct vlr_subscr *vsub = get_vsub_by_argv(gsmnet, argv[0], argv[1]);
+       const char *loop_str;
+       int loop_mode;
+
+       if (!vsub) {
+               vty_out(vty, "%% No subscriber found for %s %s%s",
+                       argv[0], argv[1], VTY_NEWLINE);
+               return CMD_WARNING;
+       }
+
+       loop_str = argv[2];
+       loop_mode = loop_by_char(loop_str[0]);
+
+       conn = connection_for_subscr(vsub);
+       if (!conn) {
+               vty_out(vty, "%% An active connection is required for %s %s%s",
+                       argv[0], argv[1], VTY_NEWLINE);
+               vlr_subscr_put(vsub);
+               return CMD_WARNING;
+       }
+
+       gsm0414_tx_close_tch_loop_cmd(conn, loop_mode);
+
+       return CMD_SUCCESS;
+}
+
+DEFUN(subscriber_mstest_open,
+      subscriber_mstest_open_cmd,
+      "subscriber " SUBSCR_TYPES " ID ms-test open-loop",
+      SUBSCR_HELP "Send a TS 04.14 MS Test Command to subscriber\n"
+      "Open a TCH Loop inside the MS\n")
+{
+       struct gsm_subscriber_connection *conn;
+       struct gsm_network *gsmnet = gsmnet_from_vty(vty);
+       struct vlr_subscr *vsub = get_vsub_by_argv(gsmnet, argv[0], argv[1]);
+
+       if (!vsub) {
+               vty_out(vty, "%% No subscriber found for %s %s%s",
+                       argv[0], argv[1], VTY_NEWLINE);
+               return CMD_WARNING;
+       }
+
+       conn = connection_for_subscr(vsub);
+       if (!conn) {
+               vty_out(vty, "%% An active connection is required for %s %s%s",
+                       argv[0], argv[1], VTY_NEWLINE);
+               vlr_subscr_put(vsub);
+               return CMD_WARNING;
+       }
+
+       gsm0414_tx_open_loop_cmd(conn);
+
+       return CMD_SUCCESS;
+}
+
 DEFUN(ena_subscr_expire,
       ena_subscr_expire_cmd,
       "subscriber " SUBSCR_TYPES " ID expire",
@@ -570,11 +662,11 @@
                VTY_NEWLINE);
        vty_out(vty, "Handover                : %lu attempted, %lu no_channel, 
%lu timeout, "
                "%lu completed, %lu failed%s",
-               net->msc_ctrs->ctr[BSC_CTR_HANDOVER_ATTEMPTED].current,
-               net->msc_ctrs->ctr[BSC_CTR_HANDOVER_NO_CHANNEL].current,
-               net->msc_ctrs->ctr[BSC_CTR_HANDOVER_TIMEOUT].current,
-               net->msc_ctrs->ctr[BSC_CTR_HANDOVER_COMPLETED].current,
-               net->msc_ctrs->ctr[BSC_CTR_HANDOVER_FAILED].current,
+               net->bsc_ctrs->ctr[BSC_CTR_HANDOVER_ATTEMPTED].current,
+               net->bsc_ctrs->ctr[BSC_CTR_HANDOVER_NO_CHANNEL].current,
+               net->bsc_ctrs->ctr[BSC_CTR_HANDOVER_TIMEOUT].current,
+               net->bsc_ctrs->ctr[BSC_CTR_HANDOVER_COMPLETED].current,
+               net->bsc_ctrs->ctr[BSC_CTR_HANDOVER_FAILED].current,
                VTY_NEWLINE);
        vty_out(vty, "SMS MO                  : %lu submitted, %lu no 
receiver%s",
                net->msc_ctrs->ctr[MSC_CTR_SMS_SUBMITTED].current,
@@ -851,6 +943,8 @@
        install_element_ve(&subscriber_silent_call_start_cmd);
        install_element_ve(&subscriber_silent_call_stop_cmd);
        install_element_ve(&subscriber_ussd_notify_cmd);
+       install_element_ve(&subscriber_mstest_close_cmd);
+       install_element_ve(&subscriber_mstest_open_cmd);
        install_element_ve(&subscriber_update_cmd);
        install_element_ve(&show_stats_cmd);
        install_element_ve(&show_smsqueue_cmd);
diff --git a/src/osmo-bsc/osmo_bsc_main.c b/src/osmo-bsc/osmo_bsc_main.c
index ee094d6..90651b9 100644
--- a/src/osmo-bsc/osmo_bsc_main.c
+++ b/src/osmo-bsc/osmo_bsc_main.c
@@ -162,6 +162,7 @@
 
        switch (signal) {
        case SIGINT:
+       case SIGTERM:
                bsc_shutdown_net(bsc_gsmnet);
                osmo_signal_dispatch(SS_L_GLOBAL, S_L_GLOBAL_SHUTDOWN, NULL);
                sleep(3);
@@ -280,6 +281,7 @@
        }
 
        signal(SIGINT, &signal_handler);
+       signal(SIGTERM, &signal_handler);
        signal(SIGABRT, &signal_handler);
        signal(SIGUSR1, &signal_handler);
        signal(SIGUSR2, &signal_handler);
diff --git a/src/osmo-msc/msc_main.c b/src/osmo-msc/msc_main.c
index 4166159..723c0e6 100644
--- a/src/osmo-msc/msc_main.c
+++ b/src/osmo-msc/msc_main.c
@@ -140,7 +140,6 @@
        printf("  -c --config-file filename  The config file to use.\n");
        printf("  -s --disable-color\n");
        printf("  -l --database db-name      The database to use.\n");
-       printf("  -a --authorize-everyone    Authorize every new subscriber. 
Dangerous!\n");
        printf("  -T --timestamp             Prefix every log line with a 
timestamp.\n");
        printf("  -V --version               Print the version of OpenBSC.\n");
        printf("  -P --rtp-proxy             Enable the RTP Proxy code inside 
OpenBSC.\n");
@@ -163,7 +162,6 @@
                        {"config-file", 1, 0, 'c'},
                        {"disable-color", 0, 0, 's'},
                        {"database", 1, 0, 'l'},
-                       {"authorize-everyone", 0, 0, 'a'},
                        {"pcap", 1, 0, 'p'},
                        {"timestamp", 0, 0, 'T'},
                        {"version", 0, 0, 'V' },
@@ -268,6 +266,7 @@
 
        switch (signal) {
        case SIGINT:
+       case SIGTERM:
                msc_network_shutdown(msc_network);
                osmo_signal_dispatch(SS_L_GLOBAL, S_L_GLOBAL_SHUTDOWN, NULL);
                sleep(3);
@@ -474,6 +473,7 @@
                osmo_timer_schedule(&db_sync_timer, DB_SYNC_INTERVAL);
 
        signal(SIGINT, &signal_handler);
+       signal(SIGTERM, &signal_handler);
        signal(SIGABRT, &signal_handler);
        signal(SIGUSR1, &signal_handler);
        signal(SIGUSR2, &signal_handler);
diff --git a/src/utils/smpp_mirror.c b/src/utils/smpp_mirror.c
index 95df5f2..c570505 100644
--- a/src/utils/smpp_mirror.c
+++ b/src/utils/smpp_mirror.c
@@ -95,12 +95,23 @@
 }
 /* FIXME: merge with smpp_smsc.c */
 
+static struct tlv_t *find_tlv(struct tlv_t *head, uint16_t tag)
+{
+       struct tlv_t *t;
+
+       for (t = head; t != NULL; t = t->next) {
+               if (t->tag == tag)
+                       return t;
+       }
+       return NULL;
+}
 
 static int smpp_handle_deliver(struct esme *esme, struct msgb *msg)
 {
        struct deliver_sm_t deliver;
        struct deliver_sm_resp_t deliver_r;
        struct submit_sm_t submit;
+       tlv_t *t;
        int rc;
 
        memset(&deliver, 0, sizeof(deliver));
@@ -129,7 +140,15 @@
                OSMO_MIN(sizeof(submit.source_addr),
                         sizeof(deliver.destination_addr)));
 
-       submit.esm_class = deliver.esm_class;
+       /* Mirror delivery receipts as a delivery acknowledgements. */
+       if (deliver.esm_class == 0x04) {
+               LOGP(DSMPP, LOGL_DEBUG, "%s\n", deliver.short_message);
+               submit.esm_class = 0x08;
+       } else {
+               submit.esm_class = deliver.esm_class;
+       }
+
+       submit.registered_delivery = deliver.registered_delivery;
        submit.protocol_id = deliver.protocol_id;
        submit.priority_flag = deliver.priority_flag;
        memcpy(submit.schedule_delivery_time, deliver.schedule_delivery_time,
@@ -146,7 +165,18 @@
        memcpy(submit.short_message, deliver.short_message,
                OSMO_MIN(sizeof(submit.short_message),
                         sizeof(deliver.short_message)));
-       /* FIXME: TLV? */
+
+       /* FIXME: More TLV? */
+       t = find_tlv(deliver.tlv, TLVID_user_message_reference);
+       if (t) {
+               tlv_t tlv;
+
+               memset(&tlv, 0, sizeof(tlv));
+               tlv.tag = TLVID_user_message_reference;
+               tlv.length = 2;
+               tlv.value.val16 = t->value.val16;
+               build_tlv(&submit.tlv, &tlv);
+       }
 
        return PACK_AND_SEND(esme, &submit);
 }
diff --git a/tests/channel/channel_test.c b/tests/channel/channel_test.c
index f686969..beae658 100644
--- a/tests/channel/channel_test.c
+++ b/tests/channel/channel_test.c
@@ -31,6 +31,35 @@
 #include <openbsc/gsm_subscriber.h>
 #include <openbsc/vlr.h>
 
+void test_bts_debug_print(void)
+{
+       struct gsm_network *network;
+       struct gsm_bts *bts;
+       struct gsm_bts_trx *trx;
+
+       printf("Testing the lchan printing:");
+
+       /* Create a dummy network */
+       network = bsc_network_init(tall_bsc_ctx, 1, 1, NULL);
+       if (!network)
+               exit(1);
+       /* Add a BTS with some reasonanbly non-zero id */
+       bts = gsm_bts_alloc(network, 45);
+       /* Add a second TRX to test on multiple TRXs */
+       gsm_bts_trx_alloc(bts);
+
+       llist_for_each_entry(trx, &bts->trx_list, list) {
+               char *name = gsm_lchan_name(&trx->ts[3].lchan[4]);
+
+               if (name)
+                       printf(" %s", name);
+               else
+                       printf("NULL name");
+       }
+       printf("\n");
+}
+
+
 void test_dyn_ts_subslots(void)
 {
        struct gsm_bts_trx_ts ts;
@@ -66,6 +95,7 @@
        osmo_init_logging(&log_info);
 
        test_dyn_ts_subslots();
+       test_bts_debug_print();
 
        return EXIT_SUCCESS;
 }
diff --git a/tests/channel/channel_test.ok b/tests/channel/channel_test.ok
index e4d625a..81d6569 100644
--- a/tests/channel/channel_test.ok
+++ b/tests/channel/channel_test.ok
@@ -1 +1,2 @@
 Testing subslot numbers for pchan types
+Testing the lchan printing: (bts=45,trx=0,ts=3,ss=4) (bts=45,trx=1,ts=3,ss=4)
diff --git a/tests/db/db_test.err b/tests/db/db_test.err
index fa9a54c..27e5703 100644
--- a/tests/db/db_test.err
+++ b/tests/db/db_test.err
@@ -1,2 +1,3 @@
 Going to migrate from revision 3
+Going to migrate from revision 4
 
\ No newline at end of file
diff --git a/tests/gprs/gprs_test.c b/tests/gprs/gprs_test.c
index ff77404..aac9bb8 100644
--- a/tests/gprs/gprs_test.c
+++ b/tests/gprs/gprs_test.c
@@ -48,101 +48,6 @@
        ASSERT_FALSE(nu_is_retransmission(479, 511)); // wrapped
 }
 
-static void apn_round_trip(const uint8_t *input, size_t len, const char 
*wanted_output)
-{
-       char output[len ? len : 1];
-       uint8_t encoded[len + 50];
-       char *out_str;
-       int enc_len;
-
-       /* decode and verify we have what we want */
-       out_str = gprs_apn_to_str(output, input, len);
-       OSMO_ASSERT(out_str);
-       OSMO_ASSERT(out_str == &output[0]);
-       OSMO_ASSERT(strlen(out_str) == strlen(wanted_output));
-       OSMO_ASSERT(strcmp(out_str, wanted_output) == 0);
-
-       /* encode and verify it */
-       if (len != 0) {
-               enc_len = gprs_str_to_apn(encoded, ARRAY_SIZE(encoded), 
wanted_output);
-               OSMO_ASSERT(enc_len == len);
-               OSMO_ASSERT(memcmp(encoded, input, enc_len) == 0);
-       } else {
-               enc_len = gprs_str_to_apn(encoded, 0, wanted_output);
-               OSMO_ASSERT(enc_len == -1);
-       }
-}
-
-static void test_gsm_03_03_apn(void)
-{
-
-       {
-               /* test invalid writes */
-               const uint8_t ref[10] = { 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xAB, 
0xAC, 0xAD, 0xAE, 0xAF };
-               uint8_t output[10];
-               int enc_len;
-
-               memcpy(output, ref, ARRAY_SIZE(output));
-               enc_len = gprs_str_to_apn(output, 0, "");
-               OSMO_ASSERT(enc_len == -1);
-               OSMO_ASSERT(memcmp(ref, output, ARRAY_SIZE(ref)) == 0);
-
-               memcpy(output, ref, ARRAY_SIZE(output));
-               enc_len = gprs_str_to_apn(output, 0, "foo");
-               OSMO_ASSERT(enc_len == -1);
-               OSMO_ASSERT(memcmp(ref, output, ARRAY_SIZE(ref)) == 0);
-
-               memcpy(output, ref, ARRAY_SIZE(output));
-               enc_len = gprs_str_to_apn(output, 1, "foo");
-               OSMO_ASSERT(enc_len == -1);
-               OSMO_ASSERT(memcmp(ref + 1, output + 1, ARRAY_SIZE(ref) - 1) == 
0);
-
-               memcpy(output, ref, ARRAY_SIZE(output));
-               enc_len = gprs_str_to_apn(output, 2, "foo");
-               OSMO_ASSERT(enc_len == -1);
-               OSMO_ASSERT(memcmp(ref + 2, output + 2, ARRAY_SIZE(ref) - 2) == 
0);
-
-               memcpy(output, ref, ARRAY_SIZE(output));
-               enc_len = gprs_str_to_apn(output, 3, "foo");
-               OSMO_ASSERT(enc_len == -1);
-               OSMO_ASSERT(memcmp(ref + 3, output + 3, ARRAY_SIZE(ref) - 3) == 
0);
-       }
-
-       {
-               /* single empty label */
-               uint8_t input[] = { 0x0 };
-               const char *output = "";
-               apn_round_trip(input, ARRAY_SIZE(input), output);
-       }
-
-       {
-               /* no label */
-               uint8_t input[] = { };
-               const char *output = "";
-               apn_round_trip(input, ARRAY_SIZE(input), output);
-       }
-
-       {
-               /* single label with A */
-               uint8_t input[] = { 0x1, 65 };
-               const char *output = "A";
-               apn_round_trip(input, ARRAY_SIZE(input), output);
-               OSMO_ASSERT(gprs_apn_to_str(NULL, input, ARRAY_SIZE(input) - 1) 
== NULL);
-       }
-
-       {
-               uint8_t input[] = { 0x3, 65, 66, 67, 0x2, 90, 122 };
-               const char *output = "ABC.Zz";
-               char tmp[strlen(output) + 1];
-               apn_round_trip(input, ARRAY_SIZE(input), output);
-               OSMO_ASSERT(gprs_apn_to_str(tmp, input, ARRAY_SIZE(input) - 1) 
== NULL);
-               OSMO_ASSERT(gprs_apn_to_str(tmp, input, ARRAY_SIZE(input) - 2) 
== NULL);
-               OSMO_ASSERT(gprs_apn_to_str(tmp, input, ARRAY_SIZE(input) - 4) 
== NULL);
-               OSMO_ASSERT(gprs_apn_to_str(tmp, input, ARRAY_SIZE(input) - 5) 
== NULL);
-               OSMO_ASSERT(gprs_apn_to_str(tmp, input, ARRAY_SIZE(input) - 6) 
== NULL);
-       }
-}
-
 static void test_gprs_timer_enc_dec(void)
 {
        int i, u, secs, tmr;
@@ -228,7 +133,6 @@
        osmo_init_logging(&info);
 
        test_8_4_2();
-       test_gsm_03_03_apn();
        test_gprs_timer_enc_dec();
 
        printf("Done.\n");
diff --git a/tests/gsm0408/gsm0408_test.c b/tests/gsm0408/gsm0408_test.c
index 1b326ee..fcdc8f8 100644
--- a/tests/gsm0408/gsm0408_test.c
+++ b/tests/gsm0408/gsm0408_test.c
@@ -153,7 +153,7 @@
 
        if (!network)
                exit(1);
-       bts = gsm_bts_alloc(network);
+       bts = gsm_bts_alloc(network, 0);
 
        _bts_uarfcn_add(bts, 10564, 319, 0);
        _bts_uarfcn_add(bts, 10612, 319, 0);
@@ -168,7 +168,7 @@
 
        if (!network)
                exit(1);
-       bts = gsm_bts_alloc(network);
+       bts = gsm_bts_alloc(network, 0);
 
        _bts_uarfcn_add(bts, 10564, 318, 0);
        _bts_uarfcn_add(bts, 10612, 319, 0);
@@ -188,7 +188,7 @@
        if (!network)
                exit(1);
 
-       bts = gsm_bts_alloc(network);
+       bts = gsm_bts_alloc(network, 0);
 
        /* first generate invalid SI as no UARFCN added */
        gen(bts, __func__);
@@ -216,7 +216,7 @@
        if (!network)
                exit(1);
 
-       bts = gsm_bts_alloc(network);
+       bts = gsm_bts_alloc(network, 0);
 
        bts->si_common.si2quater_neigh_list.arfcn = 
bts->si_common.data.earfcn_list;
        bts->si_common.si2quater_neigh_list.meas_bw = 
bts->si_common.data.meas_bw_list;
@@ -249,7 +249,7 @@
        if (!network)
                exit(1);
 
-       bts = gsm_bts_alloc(network);
+       bts = gsm_bts_alloc(network, 0);
 
        bts->si_common.si2quater_neigh_list.arfcn = 
bts->si_common.data.earfcn_list;
        bts->si_common.si2quater_neigh_list.meas_bw = 
bts->si_common.data.meas_bw_list;
diff --git a/tests/gtphub/Makefile.am b/tests/gtphub/Makefile.am
index 5c834b7..f2a6b88 100644
--- a/tests/gtphub/Makefile.am
+++ b/tests/gtphub/Makefile.am
@@ -8,6 +8,7 @@
        -ggdb3 \
        $(LIBOSMOCORE_CFLAGS) \
        $(LIBOSMOABIS_CFLAGS) \
+       $(LIBOSMOGSM_CFLAGS) \
        $(LIBGTP_CFLAGS) \
        $(NULL)
 
@@ -37,6 +38,7 @@
        $(top_builddir)/src/gprs/gtphub.o \
        $(top_builddir)/src/gprs/gprs_utils.o \
        $(LIBOSMOCORE_LIBS) \
+       $(LIBOSMOGSM_LIBS) \
        $(LIBGTP_LIBS) \
        -lrt \
        $(NULL)
diff --git a/tests/sgsn/sgsn_test.c b/tests/sgsn/sgsn_test.c
index 2f1513a..d66c5dd 100644
--- a/tests/sgsn/sgsn_test.c
+++ b/tests/sgsn/sgsn_test.c
@@ -139,12 +139,12 @@
 };
 
 /* override, requires '-Wl,--wrap=gprs_subscr_request_auth_info' */
-int __real_gprs_subscr_request_auth_info(struct sgsn_mm_ctx *mmctx);
-int (*subscr_request_auth_info_cb)(struct sgsn_mm_ctx *mmctx) =
+int __real_gprs_subscr_request_auth_info(struct sgsn_mm_ctx *mmctx, const 
uint8_t *auts, const uint8_t *auts_rand);
+int (*subscr_request_auth_info_cb)(struct sgsn_mm_ctx *mmctx, const uint8_t 
*auts, const uint8_t *auts_rand) =
        &__real_gprs_subscr_request_auth_info;
 
-int __wrap_gprs_subscr_request_auth_info(struct sgsn_mm_ctx *mmctx) {
-       return (*subscr_request_auth_info_cb)(mmctx);
+int __wrap_gprs_subscr_request_auth_info(struct sgsn_mm_ctx *mmctx, const 
uint8_t *auts, const uint8_t *auts_rand) {
+       return (*subscr_request_auth_info_cb)(mmctx, auts, auts_rand);
 };
 
 /* override, requires '-Wl,--wrap=gsup_client_send' */
@@ -1160,7 +1160,7 @@
        cleanup_test();
 }
 
-int my_subscr_request_auth_info_real_auth(struct sgsn_mm_ctx *mmctx)
+int my_subscr_request_auth_info_real_auth(struct sgsn_mm_ctx *mmctx, const 
uint8_t *auts, const uint8_t *auts_rand)
 {
        struct gsm_auth_tuple at = {
                .vec.sres = {0x51, 0xe5, 0x51, 0xe5},

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

Gerrit-MessageType: newchange
Gerrit-Change-Id: Id072aa07793aceca66bd74950bc40dd504982a55
Gerrit-PatchSet: 1
Gerrit-Project: osmo-msc
Gerrit-Branch: master
Gerrit-Owner: Neels Hofmeyr <[email protected]>
Gerrit-Reviewer: Pau Espin Pedrol <[email protected]>

Reply via email to