mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-hlr.git
synced 2025-11-02 13:13:29 +00:00
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
This commit is contained in:
@@ -6,6 +6,7 @@ noinst_HEADERS = \
|
||||
gsup_router.h \
|
||||
gsup_server.h \
|
||||
hlr.h \
|
||||
hlr_sms.h \
|
||||
hlr_ussd.h \
|
||||
hlr_vty.h \
|
||||
hlr_vty_subscr.h \
|
||||
|
||||
@@ -66,6 +66,10 @@ struct hlr {
|
||||
|
||||
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;
|
||||
|
||||
29
include/osmocom/hlr/hlr_sms.h
Normal file
29
include/osmocom/hlr/hlr_sms.h
Normal file
@@ -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);
|
||||
@@ -31,6 +31,7 @@ enum hlr_vty_node {
|
||||
HLR_NODE = _LAST_OSMOVTY_NODE + 1,
|
||||
GSUP_NODE,
|
||||
EUSE_NODE,
|
||||
SMSC_NODE,
|
||||
MSLOOKUP_NODE,
|
||||
MSLOOKUP_SERVER_NODE,
|
||||
MSLOOKUP_SERVER_MSC_NODE,
|
||||
|
||||
@@ -52,6 +52,7 @@ osmo_hlr_SOURCES = \
|
||||
hlr_vty.c \
|
||||
hlr_vty_subscr.c \
|
||||
gsup_send.c \
|
||||
hlr_sms.c \
|
||||
hlr_ussd.c \
|
||||
proxy.c \
|
||||
dgsm.c \
|
||||
|
||||
@@ -749,8 +749,10 @@ int main(int argc, char **argv)
|
||||
|
||||
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);
|
||||
|
||||
103
src/hlr_sms.c
Normal file
103
src/hlr_sms.c
Normal file
@@ -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);
|
||||
}
|
||||
164
src/hlr_vty.c
164
src/hlr_vty.c
@@ -41,6 +41,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[] = {
|
||||
@@ -398,6 +399,160 @@ DEFUN(cfg_ncss_guard_timeout, cfg_ncss_guard_timeout_cmd,
|
||||
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 */
|
||||
@@ -547,6 +702,15 @@ void hlr_vty_init(void *hlr_ctx)
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user