SMS over GSUP: implement forwarding of MO SMS
MO-forwardSM.req messages are now forwarded to a connected SMSC
based on the SMSC address (SM-RP-DA) in the MO SM and the vty-defined
mapping from SMSC numeric addresses to IPA names.
Related: OS#6135
Change-Id: Iaad4531922c41583d261c79f42561a1bdbe03521
diff --git a/include/osmocom/hlr/hlr_sms.h b/include/osmocom/hlr/hlr_sms.h
index e6e0d8a..6e68a85 100644
--- a/include/osmocom/hlr/hlr_sms.h
+++ b/include/osmocom/hlr/hlr_sms.h
@@ -27,3 +27,5 @@
struct hlr_smsc_route *smsc_route_alloc(struct hlr *hlr, const char *num_addr,
struct hlr_smsc *smsc);
void smsc_route_del(struct hlr_smsc_route *rt);
+
+void forward_mo_sms(struct osmo_gsup_req *req);
diff --git a/src/hlr.c b/src/hlr.c
index a7bebf8..9aabada 100644
--- a/src/hlr.c
+++ b/src/hlr.c
@@ -49,6 +49,7 @@
#include <osmocom/hlr/rand.h>
#include <osmocom/hlr/hlr_vty.h>
#include <osmocom/hlr/hlr_ussd.h>
+#include <osmocom/hlr/hlr_sms.h>
#include <osmocom/hlr/dgsm.h>
#include <osmocom/hlr/proxy.h>
#include <osmocom/hlr/lu_fsm.h>
@@ -555,6 +556,9 @@
case OSMO_GSUP_MSGT_CHECK_IMEI_REQUEST:
rx_check_imei_req(req);
break;
+ case OSMO_GSUP_MSGT_MO_FORWARD_SM_REQUEST:
+ forward_mo_sms(req);
+ break;
default:
LOGP(DMAIN, LOGL_DEBUG, "Unhandled GSUP message type %s\n",
osmo_gsup_message_type_name(req->gsup.message_type));
diff --git a/src/hlr_sms.c b/src/hlr_sms.c
index bd5c85b..ebf4099 100644
--- a/src/hlr_sms.c
+++ b/src/hlr_sms.c
@@ -26,6 +26,8 @@
#include <osmocom/core/talloc.h>
#include <osmocom/gsm/gsup.h>
+#include <osmocom/gsm/gsm48_ie.h>
+#include <osmocom/gsm/protocol/gsm_04_11.h>
#include <osmocom/hlr/hlr.h>
#include <osmocom/hlr/hlr_sms.h>
@@ -101,3 +103,91 @@
llist_del(&rt->list);
talloc_free(rt);
}
+
+/***********************************************************************
+ * forwarding of MO SMS to SMSCs based on SM-RP-DA
+ ***********************************************************************/
+
+static const struct hlr_smsc *find_smsc_route(const char *smsc_addr)
+{
+ const struct hlr_smsc_route *rt;
+
+ rt = smsc_route_find(g_hlr, smsc_addr);
+ if (rt)
+ return rt->smsc;
+ return g_hlr->smsc_default;
+}
+
+static void respond_with_sm_rp_cause(struct osmo_gsup_req *req,
+ uint8_t sm_rp_cause)
+{
+ struct osmo_gsup_message rsp_msg = { };
+
+ rsp_msg.sm_rp_cause = &sm_rp_cause;
+ osmo_gsup_req_respond(req, &rsp_msg, true, true);
+}
+
+/* Short Message from MSC/VLR towards SMSC */
+void forward_mo_sms(struct osmo_gsup_req *req)
+{
+ /* The length limit on the SMSC address is 20 digits, stated
+ * indirectly in GSM 04.11 section 8.2.5.2. */
+ uint8_t gsm48_decode_buffer[11];
+ char smsc_addr[21];
+ const struct hlr_smsc *smsc;
+ struct osmo_cni_peer_id dest_peer;
+
+ /* Make sure SM-RP-DA (SMSC address) is present */
+ if (req->gsup.sm_rp_da == NULL || !req->gsup.sm_rp_da_len) {
+ osmo_gsup_req_respond_err(req, GMM_CAUSE_INV_MAND_INFO,
+ "missing SM-RP-DA");
+ return;
+ }
+
+ if (req->gsup.sm_rp_da_type != OSMO_GSUP_SMS_SM_RP_ODA_SMSC_ADDR) {
+ osmo_gsup_req_respond_err(req, GMM_CAUSE_INV_MAND_INFO,
+ "SM-RP-DA type is not SMSC");
+ return;
+ }
+
+ /* Enforce the length constrainst on SM-RP-DA, as specified in
+ * GSM 04.11 section 8.2.5.2. Also enforce absence of ToN/NPI
+ * extension octets at the same time. */
+ if (req->gsup.sm_rp_da_len < 2 || req->gsup.sm_rp_da_len > 11 ||
+ !(req->gsup.sm_rp_da[0] & 0x80)) {
+ /* This form of bogosity originates from the MS,
+ * not from OsmoMSC or any other Osmocom network elements! */
+ LOGP(DLSMS, LOGL_NOTICE,
+ "Rx '%s' (IMSI-%s) contains invalid SM-RP-DA from MS\n",
+ osmo_gsup_message_type_name(req->gsup.message_type),
+ req->gsup.imsi);
+ respond_with_sm_rp_cause(req, GSM411_RP_CAUSE_SEMANT_INC_MSG);
+ return;
+ }
+
+ /* Decode SMSC address from SM-RP-DA */
+ gsm48_decode_buffer[0] = req->gsup.sm_rp_da_len - 1;
+ memcpy(gsm48_decode_buffer + 1, req->gsup.sm_rp_da + 1,
+ req->gsup.sm_rp_da_len - 1);
+ gsm48_decode_bcd_number2(smsc_addr, sizeof(smsc_addr),
+ gsm48_decode_buffer,
+ req->gsup.sm_rp_da_len, 0);
+
+ /* Look for a route to this SMSC */
+ smsc = find_smsc_route(smsc_addr);
+ if (smsc == NULL) {
+ LOGP(DLSMS, LOGL_NOTICE,
+ "Failed to find a route for '%s' (IMSI-%s, SMSC-Addr-%s)\n",
+ osmo_gsup_message_type_name(req->gsup.message_type),
+ req->gsup.imsi, smsc_addr);
+ respond_with_sm_rp_cause(req,
+ GSM411_RP_CAUSE_MO_NUM_UNASSIGNED);
+ return;
+ }
+
+ /* We got the IPA name of our SMSC - forward the message */
+ osmo_cni_peer_id_set(&dest_peer, OSMO_CNI_PEER_ID_IPA_NAME,
+ (const uint8_t *) smsc->name,
+ strlen(smsc->name) + 1);
+ osmo_gsup_forward_to_local_peer(req->cb_data, &dest_peer, req, NULL);
+}