[MERGED] openbsc[master]: libmsc: send RP-ACK to MS after ESME sends SMPP DELIVER-SM-RESP

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/.

Pablo Neira Ayuso gerrit-no-reply at lists.osmocom.org
Mon May 8 18:20:34 UTC 2017


Pablo Neira Ayuso has submitted this change and it was merged.

Change subject: libmsc: send RP-ACK to MS after ESME sends SMPP DELIVER-SM-RESP
......................................................................


libmsc: send RP-ACK to MS after ESME sends SMPP DELIVER-SM-RESP

Hold on with the GSM 04.11 RP-ACK/RP-ERROR that we send to the MS until
we get a confirmation from the ESME, via SMPP DELIVER-SM-RESP, that we
can route this sms somewhere we can reach indeed.

After this change, the conversation looks like this:

    MS    GSM 03.40      SMSC   SMPP 3.4   ESME
     |                    |                 |
     |   SMS-SUBMIT       |                 |
     |------------------->|                 |
     |                    |    DELIVER-SM   |
     |                    |---------------->|
     |                    |                 |
     |                    | DELIVER-SM-RESP |
     |                    |<----------------|
     |  GSM 04.11 RP-ACK  |                 |
     |<-------------------|                 |
     |                    |                 |

Before this patch, the RP-ACK was sent back straight forward to the MS,
no matter if the sms can be route by the ESME or not. Thus, the user
ends up getting a misleading "message delivered" in their phone screen,
when the message may just be unroutable by the ESME hence silently
dropped.

If we get no reply from the ESME, there is a hardcoded timer that will
expire to send back an RP-ERROR to the MS indicating that network is
out-of-order. Currently this timer is arbitrarily set to 5 seconds. I
found no specific good default value on the SMPP 3.4 specs, section 7.2,
where the response_timer is described. There must be a place that
describes a better default value for this. We could also expose this
timer through VTY for configurability reasons, to be done later.

Given all this needs to happen asyncronously, ie. block the SMSC, this
patch extends the gsm_sms structure with two new fields to annotate
useful information to send the RP-ACK/RP-ERROR back to the MS of origin.
These new fields are:

* the GSM 04.07 transaction id, to look up for the gsm_trans object.

* the GSM 04.11 message reference so the MS of origin can correlate this
  response to its original request.

Tested here using python-libsmpp script that replies with
DELIVER_SM_RESP and status code 0x0b (Invalid Destination). I can see
here on my motorola C155 that message cannot be delivered. I have tested
with the success status code in the SMPP DELIVER_SM_RESP too.

Change-Id: I0d5bd5693fed6d4f4bd2951711c7888712507bfd
---
M openbsc/include/openbsc/gsm_04_11.h
M openbsc/include/openbsc/gsm_data.h
M openbsc/src/libmsc/gsm_04_11.c
M openbsc/src/libmsc/smpp_openbsc.c
M openbsc/src/libmsc/smpp_smsc.c
M openbsc/src/libmsc/smpp_smsc.h
6 files changed, 184 insertions(+), 17 deletions(-)

Approvals:
  Harald Welte: Looks good to me, approved
  Jenkins Builder: Verified



diff --git a/openbsc/include/openbsc/gsm_04_11.h b/openbsc/include/openbsc/gsm_04_11.h
index 149de90..017c887 100644
--- a/openbsc/include/openbsc/gsm_04_11.h
+++ b/openbsc/include/openbsc/gsm_04_11.h
@@ -39,4 +39,9 @@
 void gsm411_sapi_n_reject(struct gsm_subscriber_connection *conn);
 
 uint8_t sms_next_rp_msg_ref(uint8_t *next_rp_ref);
+
+int gsm411_send_rp_ack(struct gsm_trans *trans, uint8_t msg_ref);
+int gsm411_send_rp_error(struct gsm_trans *trans, uint8_t msg_ref,
+			 uint8_t cause);
+
 #endif
diff --git a/openbsc/include/openbsc/gsm_data.h b/openbsc/include/openbsc/gsm_data.h
index 1d90eee..6d814c8 100644
--- a/openbsc/include/openbsc/gsm_data.h
+++ b/openbsc/include/openbsc/gsm_data.h
@@ -429,6 +429,11 @@
 	enum gsm_sms_source_id source;
 
 	struct {
+		uint8_t transaction_id;
+		uint32_t msg_ref;
+	} gsm411;
+
+	struct {
 		struct osmo_esme *esme;
 		uint32_t sequence_nr;
 		int transaction_mode;
diff --git a/openbsc/src/libmsc/gsm_04_11.c b/openbsc/src/libmsc/gsm_04_11.c
index a94b971..aa2030f 100644
--- a/openbsc/src/libmsc/gsm_04_11.c
+++ b/openbsc/src/libmsc/gsm_04_11.c
@@ -278,7 +278,7 @@
 }
 
 int sms_route_mt_sms(struct gsm_subscriber_connection *conn, struct msgb *msg,
-			struct gsm_sms *gsms, uint8_t sms_mti)
+		     struct gsm_sms *gsms, uint8_t sms_mti, bool *deferred)
 {
 	int rc;
 
@@ -292,7 +292,7 @@
 	 * delivery of the SMS.
 	 */
 	if (smpp_first) {
-		rc = smpp_try_deliver(gsms, conn);
+		rc = smpp_try_deliver(gsms, conn, deferred);
 		if (rc == GSM411_RP_CAUSE_MO_NUM_UNASSIGNED)
 			goto try_local;
 		if (rc < 0) {
@@ -320,7 +320,7 @@
 			return GSM411_RP_CAUSE_MO_NUM_UNASSIGNED;
 		}
 
-		rc = smpp_try_deliver(gsms, conn);
+		rc = smpp_try_deliver(gsms, conn, deferred);
 		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) {
@@ -363,8 +363,10 @@
 
 /* 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)
+static int gsm340_rx_tpdu(struct gsm_trans *trans, struct msgb *msg,
+			  uint32_t gsm411_msg_ref, bool *deferred)
 {
+	struct gsm_subscriber_connection *conn = trans->conn;
 	uint8_t *smsp = msgb_sms(msg);
 	struct gsm_sms *gsms;
 	unsigned int sms_alphabet;
@@ -394,6 +396,9 @@
 
 	smsp++;
 	gsms->msg_ref = *smsp++;
+
+	gsms->gsm411.transaction_id = trans->transaction_id;
+	gsms->gsm411.msg_ref = gsm411_msg_ref;
 
 	/* length in bytes of the destination address */
 	da_len_bytes = 2 + *smsp/2 + *smsp%2;
@@ -481,9 +486,10 @@
 	/* FIXME: This looks very wrong */
 	send_signal(0, NULL, gsms, 0);
 
-	rc = sms_route_mt_sms(conn, msg, gsms, sms_mti);
+	rc = sms_route_mt_sms(conn, msg, gsms, sms_mti, deferred);
 out:
-	sms_free(gsms);
+	if (!deferred)
+		sms_free(gsms);
 
 	return rc;
 }
@@ -505,7 +511,7 @@
 	return gsm411_smr_send(inst, rl_msg_type, msg);
 }
 
-static int gsm411_send_rp_ack(struct gsm_trans *trans, uint8_t msg_ref)
+int gsm411_send_rp_ack(struct gsm_trans *trans, uint8_t msg_ref)
 {
 	struct msgb *msg = gsm411_msgb_alloc();
 
@@ -515,8 +521,8 @@
 		msg_ref, GSM411_SM_RL_REPORT_REQ);
 }
 
-static int gsm411_send_rp_error(struct gsm_trans *trans,
-				uint8_t msg_ref, uint8_t cause)
+int gsm411_send_rp_error(struct gsm_trans *trans, uint8_t msg_ref,
+			 uint8_t cause)
 {
 	struct msgb *msg = gsm411_msgb_alloc();
 
@@ -536,6 +542,7 @@
 			  uint8_t dst_len, uint8_t *dst,
 			  uint8_t tpdu_len, uint8_t *tpdu)
 {
+	bool deferred = false;
 	int rc = 0;
 
 	if (src_len && src)
@@ -552,8 +559,8 @@
 
 	DEBUGP(DLSMS, "DST(%u,%s)\n", dst_len, osmo_hexdump(dst, dst_len));
 
-	rc = gsm340_rx_tpdu(trans->conn, msg);
-	if (rc == 0)
+	rc = gsm340_rx_tpdu(trans, msg, rph->msg_ref, &deferred);
+	if (rc == 0 && !deferred)
 		return gsm411_send_rp_ack(trans, rph->msg_ref);
 	else if (rc > 0)
 		return gsm411_send_rp_error(trans, rph->msg_ref, rc);
diff --git a/openbsc/src/libmsc/smpp_openbsc.c b/openbsc/src/libmsc/smpp_openbsc.c
index ec9dda3..8f1a96c 100644
--- a/openbsc/src/libmsc/smpp_openbsc.c
+++ b/openbsc/src/libmsc/smpp_openbsc.c
@@ -44,6 +44,7 @@
 #include <openbsc/signal.h>
 #include <openbsc/transaction.h>
 #include <openbsc/gsm_subscriber.h>
+#include <openbsc/chan_alloc.h>
 
 #include "smpp_smsc.h"
 
@@ -460,12 +461,122 @@
 	}
 }
 
+static void smpp_cmd_free(struct osmo_smpp_cmd *cmd)
+{
+	osmo_timer_del(&cmd->response_timer);
+	llist_del(&cmd->list);
+	subscr_put(cmd->subscr);
+	sms_free(cmd->sms);
+	talloc_free(cmd);
+}
+
+void smpp_cmd_flush_pending(struct osmo_esme *esme)
+{
+	struct osmo_smpp_cmd *cmd, *next;
+
+	llist_for_each_entry_safe(cmd, next, &esme->smpp_cmd_list, list)
+		smpp_cmd_free(cmd);
+}
+
+void smpp_cmd_ack(struct osmo_smpp_cmd *cmd)
+{
+	struct gsm_subscriber_connection *conn;
+	struct gsm_trans *trans;
+
+	conn = connection_for_subscr(cmd->subscr);
+	if (!conn) {
+		LOGP(DSMPP, LOGL_ERROR, "No connection to subscriber anymore\n");
+		return;
+	}
+
+	trans = trans_find_by_id(conn, GSM48_PDISC_SMS,
+				 cmd->sms->gsm411.transaction_id);
+	if (!trans) {
+		LOGP(DSMPP, LOGL_ERROR, "GSM transaction %u is gone\n",
+		     cmd->sms->gsm411.transaction_id);
+		return;
+	}
+
+	gsm411_send_rp_ack(trans, cmd->sms->gsm411.msg_ref);
+	smpp_cmd_free(cmd);
+}
+
+void smpp_cmd_err(struct osmo_smpp_cmd *cmd)
+{
+	struct gsm_subscriber_connection *conn;
+	struct gsm_trans *trans;
+
+	conn = connection_for_subscr(cmd->subscr);
+	if (!conn) {
+		LOGP(DSMPP, LOGL_ERROR, "No connection to subscriber anymore\n");
+		return;
+	}
+
+	trans = trans_find_by_id(conn, GSM48_PDISC_SMS,
+				 cmd->sms->gsm411.transaction_id);
+	if (!trans) {
+		LOGP(DSMPP, LOGL_ERROR, "GSM transaction %u is gone\n",
+		     cmd->sms->gsm411.transaction_id);
+		return;
+	}
+
+	gsm411_send_rp_error(trans, cmd->sms->gsm411.msg_ref,
+			     GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER);
+	smpp_cmd_free(cmd);
+}
+
+static void smpp_deliver_sm_cb(void *data)
+{
+	smpp_cmd_err(data);
+}
+
+static int smpp_cmd_enqueue(struct osmo_esme *esme,
+			    struct gsm_subscriber *subscr, struct gsm_sms *sms,
+			    uint32_t sequence_number, bool *deferred)
+{
+	struct osmo_smpp_cmd *cmd;
+
+	cmd = talloc_zero(esme, struct osmo_smpp_cmd);
+	if (!cmd)
+		return -1;
+
+	cmd->sequence_nr	= sequence_number;
+	cmd->sms		= sms;
+	cmd->subscr		= subscr_get(subscr);
+
+	/* FIXME: No predefined value for this response_timer as specified by
+	 * SMPP 3.4 specs, section 7.2. Make this configurable? Don't forget
+	 * lchan keeps busy until we get a reply to this SMPP command. Too high
+	 * value may exhaust resources.
+	 */
+	cmd->response_timer.cb	= smpp_deliver_sm_cb;
+	cmd->response_timer.data = cmd;
+	osmo_timer_schedule(&cmd->response_timer, 5, 0);
+	llist_add_tail(&cmd->list, &esme->smpp_cmd_list);
+	*deferred = true;
+
+	return 0;
+}
+
+struct osmo_smpp_cmd *smpp_cmd_find_by_seqnum(struct osmo_esme *esme,
+					      uint32_t sequence_nr)
+{
+	struct osmo_smpp_cmd *cmd;
+
+	llist_for_each_entry(cmd, &esme->smpp_cmd_list, list) {
+		if (cmd->sequence_nr == sequence_nr)
+			return cmd;
+	}
+	return NULL;
+}
+
 static int deliver_to_esme(struct osmo_esme *esme, struct gsm_sms *sms,
-			   struct gsm_subscriber_connection *conn)
+			   struct gsm_subscriber_connection *conn,
+			   bool *deferred)
 {
 	struct deliver_sm_t deliver;
+	int mode, ret;
 	uint8_t dcs;
-	int mode;
 
 	memset(&deliver, 0, sizeof(deliver));
 	deliver.command_length	= 0;
@@ -537,7 +648,12 @@
 	if (esme->acl && esme->acl->osmocom_ext && conn->lchan)
 		append_osmo_tlvs(&deliver.tlv, conn->lchan);
 
-	return smpp_tx_deliver(esme, &deliver);
+	ret = smpp_tx_deliver(esme, &deliver);
+	if (ret < 0)
+		return ret;
+
+	return smpp_cmd_enqueue(esme, conn->subscr, sms,
+				deliver.sequence_number, deferred);
 }
 
 static struct smsc *g_smsc;
@@ -547,7 +663,8 @@
 	return g_smsc->smpp_first;
 }
 
-int smpp_try_deliver(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 osmo_esme *esme;
 	struct osmo_smpp_addr dst;
@@ -561,7 +678,7 @@
 	if (!esme)
 		return GSM411_RP_CAUSE_MO_NUM_UNASSIGNED;
 
-	return deliver_to_esme(esme, sms, conn);
+	return deliver_to_esme(esme, sms, conn, deferred);
 }
 
 struct smsc *smsc_from_vty(struct vty *v)
diff --git a/openbsc/src/libmsc/smpp_smsc.c b/openbsc/src/libmsc/smpp_smsc.c
index e537490..bd25918 100644
--- a/openbsc/src/libmsc/smpp_smsc.c
+++ b/openbsc/src/libmsc/smpp_smsc.c
@@ -247,6 +247,7 @@
 		osmo_fd_unregister(&esme->wqueue.bfd);
 		close(esme->wqueue.bfd.fd);
 	}
+	smpp_cmd_flush_pending(esme);
 	llist_del(&esme->list);
 	talloc_free(esme);
 }
@@ -660,6 +661,7 @@
 static int smpp_handle_deliver_resp(struct osmo_esme *esme, struct msgb *msg)
 {
 	struct deliver_sm_resp_t deliver_r;
+	struct osmo_smpp_cmd *cmd;
 	int rc;
 
 	memset(&deliver_r, 0, sizeof(deliver_r));
@@ -670,6 +672,20 @@
 			esme->system_id, smpp34_strerror);
 		return rc;
 	}
+
+	cmd = smpp_cmd_find_by_seqnum(esme, deliver_r.sequence_number);
+	if (!cmd) {
+		LOGP(DSMPP, LOGL_ERROR, "[%s] Rx DELIVER-SM RESP !? (%s)\n",
+			esme->system_id, get_value_string(smpp_status_strs,
+						  deliver_r.command_status));
+		return -1;
+	}
+
+	/* Map SMPP command status to GSM 04.11 cause? */
+	if (deliver_r.command_status == ESME_ROK)
+		smpp_cmd_ack(cmd);
+	else
+		smpp_cmd_err(cmd);
 
 	LOGP(DSMPP, LOGL_INFO, "[%s] Rx DELIVER-SM RESP (%s)\n",
 		esme->system_id, get_value_string(smpp_status_strs,
@@ -889,6 +905,7 @@
 		return -ENOMEM;
 	}
 
+	INIT_LLIST_HEAD(&esme->smpp_cmd_list);
 	smpp_esme_get(esme);
 	esme->own_seq_nr = rand();
 	esme_inc_seq_nr(esme);
diff --git a/openbsc/src/libmsc/smpp_smsc.h b/openbsc/src/libmsc/smpp_smsc.h
index bd20137..b95a1f5 100644
--- a/openbsc/src/libmsc/smpp_smsc.h
+++ b/openbsc/src/libmsc/smpp_smsc.h
@@ -7,6 +7,7 @@
 #include <osmocom/core/utils.h>
 #include <osmocom/core/msgb.h>
 #include <osmocom/core/write_queue.h>
+#include <osmocom/core/timer.h>
 
 #include <smpp34.h>
 #include <smpp34_structs.h>
@@ -36,6 +37,8 @@
 	struct smsc *smsc;
 	struct osmo_smpp_acl *acl;
 	int use;
+
+	struct llist_head smpp_cmd_list;
 
 	uint32_t own_seq_nr;
 
@@ -83,6 +86,19 @@
 	} u;
 };
 
+struct osmo_smpp_cmd {
+	struct llist_head	list;
+	struct gsm_subscriber	*subscr;
+	struct gsm_sms		*sms;
+	uint32_t		sequence_nr;
+	struct osmo_timer_list	response_timer;
+};
+
+struct osmo_smpp_cmd *smpp_cmd_find_by_seqnum(struct osmo_esme *esme,
+					      uint32_t sequence_number);
+void smpp_cmd_ack(struct osmo_smpp_cmd *cmd);
+void smpp_cmd_err(struct osmo_smpp_cmd *cmd);
+void smpp_cmd_flush_pending(struct osmo_esme *esme);
 
 struct smsc {
 	struct osmo_fd listen_ofd;
@@ -146,5 +162,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);
+		     struct gsm_subscriber_connection *conn, bool *deferred);
 #endif

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

Gerrit-MessageType: merged
Gerrit-Change-Id: I0d5bd5693fed6d4f4bd2951711c7888712507bfd
Gerrit-PatchSet: 3
Gerrit-Project: openbsc
Gerrit-Branch: master
Gerrit-Owner: Pablo Neira Ayuso <pablo at gnumonks.org>
Gerrit-Reviewer: Harald Welte <laforge at gnumonks.org>
Gerrit-Reviewer: Holger Freyther <holger at freyther.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: Keith Whyte <keith at rhizomatica.org>
Gerrit-Reviewer: Pablo Neira Ayuso <pablo at gnumonks.org>



More information about the gerrit-log mailing list