Merge branch 'zecke/features/smpp-route'
diff --git a/openbsc/src/libmsc/gsm_04_11.c b/openbsc/src/libmsc/gsm_04_11.c
index 1b2a42c..c2cec26 100644
--- a/openbsc/src/libmsc/gsm_04_11.c
+++ b/openbsc/src/libmsc/gsm_04_11.c
@@ -57,8 +57,6 @@
 
 #ifdef BUILD_SMPP
 #include "smpp_smsc.h"
-extern int smpp_try_deliver(struct gsm_sms *sms,
-			    struct gsm_subscriber_connection *conn);
 #endif
 
 void *tall_gsms_ctx;
@@ -277,6 +275,81 @@
 	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)
+{
+	int rc;
+
+#ifdef BUILD_SMPP
+	int smpp_first = smpp_route_smpp_first(gsms, conn);
+
+	/*
+	 * Route through SMPP first before going to the local database. In case
+	 * of a unroutable message and no local subscriber, SMPP will be tried
+	 * twice. In case of an unknown subscriber continue with the normal
+	 * delivery of the SMS.
+	 */
+	if (smpp_first) {
+		rc = smpp_try_deliver(gsms, conn);
+		if (rc == 1)
+			goto try_local;
+		if (rc < 0) {
+			rc = 21; /* cause 21: short message transfer rejected */
+			/* FIXME: handle the error somehow? */
+		}
+		return rc;
+	}
+
+try_local:
+#endif
+
+	/* determine gsms->receiver based on dialled number */
+	gsms->receiver = subscr_get_by_extension(conn->bts->network->subscr_group,
+						 gsms->dst.addr);
+	if (!gsms->receiver) {
+#ifdef BUILD_SMPP
+		/* Avoid a second look-up */
+		if (smpp_first)
+			return 1; /* cause 1: unknown subscriber */
+
+		rc = smpp_try_deliver(gsms, conn);
+		if (rc == 1) {
+			rc = 1; /* cause 1: unknown subscriber */
+			osmo_counter_inc(conn->bts->network->stats.sms.no_receiver);
+		} else if (rc < 0) {
+			rc = 21; /* cause 21: short message transfer rejected */
+			/* FIXME: handle the error somehow? */
+		}
+#else
+		rc = 1; /* cause 1: unknown subscriber */
+		osmo_counter_inc(conn->bts->network->stats.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;
+}
+
+
 /* 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_subscriber_connection *conn, struct msgb *msg)
@@ -392,45 +465,7 @@
 	/* FIXME: This looks very wrong */
 	send_signal(0, NULL, gsms, 0);
 
-	/* determine gsms->receiver based on dialled number */
-	gsms->receiver = subscr_get_by_extension(conn->bts->network->subscr_group,
-						 gsms->dst.addr);
-	if (!gsms->receiver) {
-#ifdef BUILD_SMPP
-		rc = smpp_try_deliver(gsms, conn);
-		if (rc == 1) {
-			rc = 1; /* cause 1: unknown subscriber */
-			osmo_counter_inc(conn->bts->network->stats.sms.no_receiver);
-		} else if (rc < 0) {
-			rc = 21; /* cause 21: short message transfer rejected */
-			/* FIXME: handle the error somehow? */
-		}
-#else
-		rc = 1; /* cause 1: unknown subscriber */
-		osmo_counter_inc(conn->bts->network->stats.sms.no_receiver);
-#endif
-		goto out;
-	}
-
-	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;
-
+	rc = sms_route_mt_sms(conn, msg, gsms, sms_mti);
 out:
 	sms_free(gsms);
 
diff --git a/openbsc/src/libmsc/smpp_openbsc.c b/openbsc/src/libmsc/smpp_openbsc.c
index b17222f..057a9d0 100644
--- a/openbsc/src/libmsc/smpp_openbsc.c
+++ b/openbsc/src/libmsc/smpp_openbsc.c
@@ -539,6 +539,11 @@
 
 static struct smsc *g_smsc;
 
+int smpp_route_smpp_first(struct gsm_sms *sms, struct gsm_subscriber_connection *conn)
+{
+	return g_smsc->smpp_first;
+}
+
 int smpp_try_deliver(struct gsm_sms *sms, struct gsm_subscriber_connection *conn)
 {
 	struct osmo_esme *esme;
diff --git a/openbsc/src/libmsc/smpp_smsc.h b/openbsc/src/libmsc/smpp_smsc.h
index 21d28dd..3dd6562 100644
--- a/openbsc/src/libmsc/smpp_smsc.h
+++ b/openbsc/src/libmsc/smpp_smsc.h
@@ -92,6 +92,7 @@
 	uint16_t listen_port;
 	char system_id[SMPP_SYS_ID_LEN+1];
 	int accept_all;
+	int smpp_first;
 	struct osmo_smpp_acl *def_route;
 	void *priv;
 };
@@ -131,4 +132,14 @@
 int smpp_vty_init(void);
 
 int smpp_determine_scheme(uint8_t dcs, uint8_t *data_coding, int *mode);
+
+
+
+struct gsm_sms;
+struct gsm_subscriber_connection;
+
+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);
 #endif
diff --git a/openbsc/src/libmsc/smpp_vty.c b/openbsc/src/libmsc/smpp_vty.c
index 75427a9..c0695fe 100644
--- a/openbsc/src/libmsc/smpp_vty.c
+++ b/openbsc/src/libmsc/smpp_vty.c
@@ -58,6 +58,24 @@
 	return CMD_SUCCESS;
 }
 
+DEFUN(cfg_smpp_first, cfg_smpp_first_cmd,
+	"smpp-first",
+	"Try SMPP routes before the subscriber DB\n")
+{
+	struct smsc *smsc = smsc_from_vty(vty);
+	smsc->smpp_first = 1;
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_no_smpp_first, cfg_no_smpp_first_cmd,
+	"no smpp-first",
+	NO_STR "Try SMPP before routes before the subscriber DB\n")
+{
+	struct smsc *smsc = smsc_from_vty(vty);
+	smsc->smpp_first = 0;
+	return CMD_SUCCESS;
+}
+
 DEFUN(cfg_smpp_port, cfg_smpp_port_cmd,
 	"local-tcp-port <1-65535>",
 	"Set the local TCP port on which we listen for SMPP\n"
@@ -125,6 +143,8 @@
 		vty_out(vty, " system-id %s%s", smsc->system_id, VTY_NEWLINE);
 	vty_out(vty, " policy %s%s",
 		smsc->accept_all ? "accept-all" : "closed", VTY_NEWLINE);
+	vty_out(vty, " %ssmpp-first%s",
+		smsc->smpp_first ? "" : "no ", VTY_NEWLINE);
 
 	return CMD_SUCCESS;
 }
@@ -512,6 +532,8 @@
 	vty_install_default(SMPP_NODE);
 	install_element(CONFIG_NODE, &cfg_smpp_cmd);
 
+	install_element(SMPP_NODE, &cfg_smpp_first_cmd);
+	install_element(SMPP_NODE, &cfg_no_smpp_first_cmd);
 	install_element(SMPP_NODE, &cfg_smpp_port_cmd);
 	install_element(SMPP_NODE, &cfg_smpp_sys_id_cmd);
 	install_element(SMPP_NODE, &cfg_smpp_policy_cmd);
diff --git a/openbsc/tests/vty_test_runner.py b/openbsc/tests/vty_test_runner.py
index 4cd4665..1aedcf2 100644
--- a/openbsc/tests/vty_test_runner.py
+++ b/openbsc/tests/vty_test_runner.py
@@ -154,6 +154,28 @@
         res = self.vty.command("list")
         return "smpp" in res
 
+    def testSmppFirst(self):
+        if not self.checkForSmpp():
+            return
+
+        # enable the configuration
+        self.vty.enable()
+        self.vty.command("configure terminal")
+        self.vty.command("smpp")
+
+        # check the default
+        res = self.vty.command("write terminal")
+        self.assert_(res.find(' no smpp-first') > 0)
+
+        self.vty.verify("smpp-first", [''])
+        res = self.vty.command("write terminal")
+        self.assert_(res.find(' smpp-first') > 0)
+        self.assertEquals(res.find('no smpp-first'), -1)
+
+        self.vty.verify("no smpp-first", [''])
+        res = self.vty.command("write terminal")
+        self.assert_(res.find('no smpp-first') > 0)
+
     def testVtyTree(self):
         self.vty.enable()
         self.assertTrue(self.vty.verify("configure terminal", ['']))