aboutsummaryrefslogtreecommitdiffstats
path: root/openbsc/src/libmsc/smpp_openbsc.c
diff options
context:
space:
mode:
authorPablo Neira Ayuso <pablo@gnumonks.org>2017-05-04 18:44:22 +0200
committerPablo Neira Ayuso <pablo@gnumonks.org>2017-05-08 17:15:12 +0200
commit93ffbd0029d1200dfa51a0e68b4c11743f1cc0e8 (patch)
tree9c121bcee447955c3c46d881b7d9584fa105d7d5 /openbsc/src/libmsc/smpp_openbsc.c
parent638ad068e8597af81dc985d3d256ccc26e82373b (diff)
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
Diffstat (limited to 'openbsc/src/libmsc/smpp_openbsc.c')
-rw-r--r--openbsc/src/libmsc/smpp_openbsc.c127
1 files changed, 122 insertions, 5 deletions
diff --git a/openbsc/src/libmsc/smpp_openbsc.c b/openbsc/src/libmsc/smpp_openbsc.c
index ec9dda3c1..8f1a96c5c 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 append_osmo_tlvs(tlv_t **req_tlv, const struct gsm_lchan *lchan)
}
}
+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 @@ static int deliver_to_esme(struct osmo_esme *esme, struct gsm_sms *sms,
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 @@ int smpp_route_smpp_first(struct gsm_sms *sms, struct gsm_subscriber_connection
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 @@ int smpp_try_deliver(struct gsm_sms *sms, struct gsm_subscriber_connection *conn
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)