falconia has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmo-hlr/+/34448?usp=email )
Change subject: SMS over GSUP: implement vty config of SMSC routing ......................................................................
SMS over GSUP: implement vty config of SMSC routing
At the user-visible level (advanced settings menus on phones, GSM 07.05 AT commands, SIM programming) each SMSC is identified by a numeric address that looks like a phone number, originally meant to be a Global Title. OsmoMSC passes these SMSC addresses through as-is to MO-forwardSM.req GSUP message - however, SMSCs that connect to OsmoHLR via GSUP identify themselves by their IPA names instead. Hence we need a mapping mechanism in OsmoHLR config.
To accommodate different styles of network design ranging from strict recreation of classic GSM architecture to guest roaming arrangements, a two-level configuration is implemented, modeled after EUSE/USSD configuration: first one defines which SMSCs exist as entities, identified only by their IPA names, and then one defines which numeric SMSC address (in SM-RP-DA) should go to which configured SMSC, with the additional possibility of a default route.
Related: OS#6135 Change-Id: I1624dcd9d22b4efca965ccdd1c74f0063a94a33c --- M include/osmocom/hlr/Makefile.am M include/osmocom/hlr/hlr.h A include/osmocom/hlr/hlr_sms.h M include/osmocom/hlr/hlr_vty.h M src/Makefile.am M src/hlr.c A src/hlr_sms.c M src/hlr_vty.c 8 files changed, 332 insertions(+), 0 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/osmo-hlr refs/changes/48/34448/1
diff --git a/include/osmocom/hlr/Makefile.am b/include/osmocom/hlr/Makefile.am index aceda4a..545ef6f 100644 --- a/include/osmocom/hlr/Makefile.am +++ b/include/osmocom/hlr/Makefile.am @@ -6,6 +6,7 @@ gsup_router.h \ gsup_server.h \ hlr.h \ + hlr_sms.h \ hlr_ussd.h \ hlr_vty.h \ hlr_vty_subscr.h \ diff --git a/include/osmocom/hlr/hlr.h b/include/osmocom/hlr/hlr.h index dc0c77e..278a85a 100644 --- a/include/osmocom/hlr/hlr.h +++ b/include/osmocom/hlr/hlr.h @@ -74,6 +74,10 @@
struct llist_head ss_sessions;
+ struct llist_head smsc_list; + struct llist_head smsc_routes; + struct hlr_smsc *smsc_default; + bool store_imei;
bool subscr_create_on_demand; diff --git a/include/osmocom/hlr/hlr_sms.h b/include/osmocom/hlr/hlr_sms.h new file mode 100644 index 0000000..e6e0d8a --- /dev/null +++ b/include/osmocom/hlr/hlr_sms.h @@ -0,0 +1,29 @@ +#pragma once + +#include <osmocom/core/linuxlist.h> + +struct hlr_smsc { + /* g_hlr->smsc_list */ + struct llist_head list; + struct hlr *hlr; + /* name (must match the IPA ID tag) */ + const char *name; + /* human-readable description */ + const char *description; +}; + +struct hlr_smsc *smsc_find(struct hlr *hlr, const char *name); +struct hlr_smsc *smsc_alloc(struct hlr *hlr, const char *name); +void smsc_del(struct hlr_smsc *smsc); + +struct hlr_smsc_route { + /* g_hlr->smsc_routes */ + struct llist_head list; + const char *num_addr; + struct hlr_smsc *smsc; +}; + +struct hlr_smsc_route *smsc_route_find(struct hlr *hlr, const char *num_addr); +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); diff --git a/include/osmocom/hlr/hlr_vty.h b/include/osmocom/hlr/hlr_vty.h index 43b6566..8601590 100644 --- a/include/osmocom/hlr/hlr_vty.h +++ b/include/osmocom/hlr/hlr_vty.h @@ -31,6 +31,7 @@ HLR_NODE = _LAST_OSMOVTY_NODE + 1, GSUP_NODE, EUSE_NODE, + SMSC_NODE, MSLOOKUP_NODE, MSLOOKUP_SERVER_NODE, MSLOOKUP_SERVER_MSC_NODE, diff --git a/src/Makefile.am b/src/Makefile.am index 380e34a..6a3bb3f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -52,6 +52,7 @@ hlr_vty.c \ hlr_vty_subscr.c \ gsup_send.c \ + hlr_sms.c \ hlr_ussd.c \ proxy.c \ dgsm.c \ diff --git a/src/hlr.c b/src/hlr.c index 457850e..17acdab 100644 --- a/src/hlr.c +++ b/src/hlr.c @@ -750,8 +750,10 @@
g_hlr = talloc_zero(hlr_ctx, struct hlr); INIT_LLIST_HEAD(&g_hlr->euse_list); + INIT_LLIST_HEAD(&g_hlr->smsc_list); INIT_LLIST_HEAD(&g_hlr->ss_sessions); INIT_LLIST_HEAD(&g_hlr->ussd_routes); + INIT_LLIST_HEAD(&g_hlr->smsc_routes); INIT_LLIST_HEAD(&g_hlr->mslookup.server.local_site_services); g_hlr->db_file_path = talloc_strdup(g_hlr, HLR_DEFAULT_DB_FILE_PATH); g_hlr->mslookup.server.mdns.domain_suffix = talloc_strdup(g_hlr, OSMO_MDNS_DOMAIN_SUFFIX_DEFAULT); diff --git a/src/hlr_sms.c b/src/hlr_sms.c new file mode 100644 index 0000000..bd5c85b --- /dev/null +++ b/src/hlr_sms.c @@ -0,0 +1,103 @@ +/* OsmoHLR SMS-over-GSUP routing implementation */ + +/* Author: Mychaela N. Falconia falcon@freecalypso.org, 2023 - however, + * Mother Mychaela's contributions are NOT subject to copyright. + * No rights reserved, all rights relinquished. + * + * Based on earlier unmerged work by Vadim Yanitskiy, 2019. + * + * 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 <stdint.h> +#include <string.h> +#include <errno.h> + +#include <osmocom/core/talloc.h> +#include <osmocom/gsm/gsup.h> + +#include <osmocom/hlr/hlr.h> +#include <osmocom/hlr/hlr_sms.h> +#include <osmocom/hlr/gsup_server.h> +#include <osmocom/hlr/gsup_router.h> +#include <osmocom/hlr/logging.h> +#include <osmocom/hlr/db.h> + +/*********************************************************************** + * core data structures expressing config from VTY + ***********************************************************************/ + +struct hlr_smsc *smsc_find(struct hlr *hlr, const char *name) +{ + struct hlr_smsc *smsc; + + llist_for_each_entry(smsc, &hlr->smsc_list, list) { + if (!strcmp(smsc->name, name)) + return smsc; + } + return NULL; +} + +struct hlr_smsc *smsc_alloc(struct hlr *hlr, const char *name) +{ + struct hlr_smsc *smsc = smsc_find(hlr, name); + if (smsc) + return NULL; + + smsc = talloc_zero(hlr, struct hlr_smsc); + smsc->name = talloc_strdup(smsc, name); + smsc->hlr = hlr; + llist_add_tail(&smsc->list, &hlr->smsc_list); + + return smsc; +} + +void smsc_del(struct hlr_smsc *smsc) +{ + llist_del(&smsc->list); + talloc_free(smsc); +} + +struct hlr_smsc_route *smsc_route_find(struct hlr *hlr, const char *num_addr) +{ + struct hlr_smsc_route *rt; + + llist_for_each_entry(rt, &hlr->smsc_routes, list) { + if (!strcmp(rt->num_addr, num_addr)) + return rt; + } + return NULL; +} + +struct hlr_smsc_route *smsc_route_alloc(struct hlr *hlr, const char *num_addr, + struct hlr_smsc *smsc) +{ + struct hlr_smsc_route *rt; + + if (smsc_route_find(hlr, num_addr)) + return NULL; + + rt = talloc_zero(hlr, struct hlr_smsc_route); + rt->num_addr = talloc_strdup(rt, num_addr); + rt->smsc = smsc; + llist_add_tail(&rt->list, &hlr->smsc_routes); + + return rt; +} + +void smsc_route_del(struct hlr_smsc_route *rt) +{ + llist_del(&rt->list); + talloc_free(rt); +} diff --git a/src/hlr_vty.c b/src/hlr_vty.c index f8cf852..6a66f75 100644 --- a/src/hlr_vty.c +++ b/src/hlr_vty.c @@ -44,6 +44,7 @@ #include <osmocom/hlr/hlr_vty.h> #include <osmocom/hlr/hlr_vty_subscr.h> #include <osmocom/hlr/hlr_ussd.h> +#include <osmocom/hlr/hlr_sms.h> #include <osmocom/hlr/gsup_server.h>
static const struct value_string gsm48_gmm_cause_vty_names[] = { @@ -608,6 +609,160 @@ return CMD_SUCCESS; }
+/*********************************************************************** + * Routing of SM-RL to GSUP-attached SMSCs + ***********************************************************************/ + +#define SMSC_STR "Configuration of GSUP routing to SMSCs\n" + +struct cmd_node smsc_node = { + SMSC_NODE, + "%s(config-hlr-smsc)# ", + 1, +}; + +DEFUN(cfg_smsc_entity, cfg_smsc_entity_cmd, + "smsc entity NAME", + SMSC_STR + "Configure a particular external SMSC\n" + "IPA name of the external SMSC\n") +{ + struct hlr_smsc *smsc; + const char *id = argv[0]; + + smsc = smsc_find(g_hlr, id); + if (!smsc) { + smsc = smsc_alloc(g_hlr, id); + if (!smsc) + return CMD_WARNING; + } + vty->index = smsc; + vty->index_sub = &smsc->description; + vty->node = SMSC_NODE; + + return CMD_SUCCESS; +} + +DEFUN(cfg_no_smsc_entity, cfg_no_smsc_entity_cmd, + "no smsc entity NAME", + NO_STR SMSC_STR "Remove a particular external SMSC\n" + "IPA name of the external SMSC\n") +{ + struct hlr_smsc *smsc = smsc_find(g_hlr, argv[0]); + if (!smsc) { + vty_out(vty, "%% Cannot remove non-existent SMSC %s%s", + argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + if (g_hlr->smsc_default == smsc) { + vty_out(vty, + "%% Cannot remove SMSC %s, it is the default route%s", + argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + smsc_del(smsc); + return CMD_SUCCESS; +} + +DEFUN(cfg_smsc_route, cfg_smsc_route_cmd, + "smsc route NUMBER NAME", + SMSC_STR + "Configure GSUP route to a particular SMSC\n" + "Numeric address of this SMSC, must match EF.SMSP programming in SIMs\n" + "IPA name of the external SMSC\n") +{ + struct hlr_smsc *smsc = smsc_find(g_hlr, argv[1]); + struct hlr_smsc_route *rt = smsc_route_find(g_hlr, argv[0]); + if (rt) { + vty_out(vty, + "%% Cannot add [another?] route for SMSC address %s%s", + argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + if (!smsc) { + vty_out(vty, "%% Cannot find SMSC '%s'%s", argv[1], + VTY_NEWLINE); + return CMD_WARNING; + } + smsc_route_alloc(g_hlr, argv[0], smsc); + + return CMD_SUCCESS; +} + +DEFUN(cfg_no_smsc_route, cfg_no_smsc_route_cmd, + "no smsc route NUMBER", + NO_STR SMSC_STR "Remove GSUP route to a particular SMSC\n" + "Numeric address of the SMSC\n") +{ + struct hlr_smsc_route *rt = smsc_route_find(g_hlr, argv[0]); + if (!rt) { + vty_out(vty, "%% Cannot find route for SMSC address %s%s", + argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + smsc_route_del(rt); + + return CMD_SUCCESS; +} + +DEFUN(cfg_smsc_defroute, cfg_smsc_defroute_cmd, + "smsc default-route NAME", + SMSC_STR + "Configure default SMSC route for unknown SMSC numeric addresses\n" + "IPA name of the external SMSC\n") +{ + struct hlr_smsc *smsc; + + smsc = smsc_find(g_hlr, argv[0]); + if (!smsc) { + vty_out(vty, "%% Cannot find SMSC %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + if (g_hlr->smsc_default != smsc) { + vty_out(vty, "Switching default route from %s to %s%s", + g_hlr->smsc_default ? g_hlr->smsc_default->name : "<none>", + smsc->name, VTY_NEWLINE); + g_hlr->smsc_default = smsc; + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_no_smsc_defroute, cfg_no_smsc_defroute_cmd, + "no smsc default-route", + NO_STR SMSC_STR + "Remove default SMSC route for unknown SMSC numeric addresses\n") +{ + g_hlr->smsc_default = NULL; + + return CMD_SUCCESS; +} + +static void dump_one_smsc(struct vty *vty, struct hlr_smsc *smsc) +{ + vty_out(vty, " smsc entity %s%s", smsc->name, VTY_NEWLINE); +} + +static int config_write_smsc(struct vty *vty) +{ + struct hlr_smsc *smsc; + struct hlr_smsc_route *rt; + + llist_for_each_entry(smsc, &g_hlr->smsc_list, list) + dump_one_smsc(vty, smsc); + + llist_for_each_entry(rt, &g_hlr->smsc_routes, list) { + vty_out(vty, " smsc route %s %s%s", rt->num_addr, + rt->smsc->name, VTY_NEWLINE); + } + + if (g_hlr->smsc_default) + vty_out(vty, " smsc default-route %s%s", + g_hlr->smsc_default->name, VTY_NEWLINE); + + return 0; +}
DEFUN(cfg_reject_cause, cfg_reject_cause_cmd, "reject-cause TYPE CAUSE", "") /* Dynamically Generated */ @@ -771,6 +926,15 @@ install_element(HLR_NODE, &cfg_ussd_defaultroute_cmd); install_element(HLR_NODE, &cfg_ussd_no_defaultroute_cmd); install_element(HLR_NODE, &cfg_ncss_guard_timeout_cmd); + + install_node(&smsc_node, config_write_smsc); + install_element(HLR_NODE, &cfg_smsc_entity_cmd); + install_element(HLR_NODE, &cfg_no_smsc_entity_cmd); + install_element(HLR_NODE, &cfg_smsc_route_cmd); + install_element(HLR_NODE, &cfg_no_smsc_route_cmd); + install_element(HLR_NODE, &cfg_smsc_defroute_cmd); + install_element(HLR_NODE, &cfg_no_smsc_defroute_cmd); + install_element(HLR_NODE, &cfg_reject_cause_cmd); install_element(HLR_NODE, &cfg_store_imei_cmd); install_element(HLR_NODE, &cfg_no_store_imei_cmd);