[PATCH] osmo-msc[master]: NOT FOR MERGE combined changes from openbsc.git for gerrit c...

This is merely a historical archive of years 2008-2021, before the migration to mailman3.

A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.

Neels Hofmeyr gerrit-no-reply at lists.osmocom.org
Sat Aug 26 19:27:25 UTC 2017


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 <laforge at gnumonks.org>
+ *
+ * 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 <nhofmeyr at sysmocom.de>
Gerrit-Reviewer: Pau Espin Pedrol <pespin at sysmocom.de>



More information about the gerrit-log mailing list