From d90586f93a9dbfd9c0a5ec8396f5a3485d40fedf Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Fri, 26 Jul 2019 01:51:17 +0700 Subject: [PATCH] WIP: basic VTY configuration for routing SMS over GSUP Change-Id: I4dc6ae1dd59928c5ae20205f4d893df106e6b38d --- include/osmocom/hlr/Makefile.am | 1 + include/osmocom/hlr/hlr.h | 5 +- include/osmocom/hlr/hlr_sms.h | 28 ++++ src/Makefile.am | 1 + src/hlr.c | 1 + src/hlr_sms.c | 79 +++++++++++ src/hlr_ussd.c | 4 +- src/hlr_vty.c | 226 +++++++++++++++++++++++++++++--- tests/test_nodes.vty | 2 + 9 files changed, 329 insertions(+), 18 deletions(-) create mode 100644 include/osmocom/hlr/hlr_sms.h create mode 100644 src/hlr_sms.c diff --git a/include/osmocom/hlr/Makefile.am b/include/osmocom/hlr/Makefile.am index 77a87643..abdfa4ef 100644 --- a/include/osmocom/hlr/Makefile.am +++ b/include/osmocom/hlr/Makefile.am @@ -6,6 +6,7 @@ noinst_HEADERS = \ gsup_server.h \ hlr.h \ hlr_ussd.h \ + hlr_sms.h \ hlr_vty.h \ hlr_vty_subscr.h \ logging.h \ diff --git a/include/osmocom/hlr/hlr.h b/include/osmocom/hlr/hlr.h index 18c4a1d7..cdcdd988 100644 --- a/include/osmocom/hlr/hlr.h +++ b/include/osmocom/hlr/hlr.h @@ -45,13 +45,16 @@ struct hlr { char *gsup_bind_addr; struct llist_head euse_list; - struct hlr_euse *euse_default; struct llist_head iuse_list; /* NCSS (call independent) session guard timeout value */ int ncss_guard_timeout; struct llist_head ussd_routes; + struct llist_head sms_routes; + + struct hlr_euse *ussd_euse_default; + struct hlr_euse *sms_euse_default; struct llist_head ss_sessions; diff --git a/include/osmocom/hlr/hlr_sms.h b/include/osmocom/hlr/hlr_sms.h new file mode 100644 index 00000000..8e06b9ed --- /dev/null +++ b/include/osmocom/hlr/hlr_sms.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include + +#include "gsup_server.h" + +enum hlr_sms_route_type { + HLR_SMS_RT_SMSC_ADDR, + HLR_SMS_RT_SENDER_MSISDN, + HLR_SMS_RT_SENDER_IMSI, +}; + +struct hlr_sms_route { + struct llist_head list; + enum hlr_sms_route_type type; + char *match_pattern; + const struct hlr_euse *euse; +}; + +struct hlr_sms_route *sms_route_find(struct hlr *hlr, + enum hlr_sms_route_type type, + const char *pattern); +struct hlr_sms_route *sms_route_alloc(struct hlr *hlr, + enum hlr_sms_route_type type, + const char *pattern, + const struct hlr_euse *euse); +void sms_route_del(struct hlr_sms_route *rt); diff --git a/src/Makefile.am b/src/Makefile.am index a5b71cf2..aea56ce8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -50,6 +50,7 @@ osmo_hlr_SOURCES = \ hlr_vty_subscr.c \ gsup_send.c \ hlr_ussd.c \ + hlr_sms.c \ $(NULL) osmo_hlr_LDADD = \ diff --git a/src/hlr.c b/src/hlr.c index c3737d5c..770590c1 100644 --- a/src/hlr.c +++ b/src/hlr.c @@ -844,6 +844,7 @@ int main(int argc, char **argv) INIT_LLIST_HEAD(&g_hlr->iuse_list); INIT_LLIST_HEAD(&g_hlr->ss_sessions); INIT_LLIST_HEAD(&g_hlr->ussd_routes); + INIT_LLIST_HEAD(&g_hlr->sms_routes); g_hlr->db_file_path = talloc_strdup(g_hlr, HLR_DEFAULT_DB_FILE_PATH); /* Init default (call independent) SS session guard timeout value */ diff --git a/src/hlr_sms.c b/src/hlr_sms.c new file mode 100644 index 00000000..7e2c1456 --- /dev/null +++ b/src/hlr_sms.c @@ -0,0 +1,79 @@ +/* OsmoHLR SMS routing implementation */ + +/* (C) 2019 by Vadim Yanitskiy + * + * All Rights Reserved + * + * 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 . + * + */ + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +struct hlr_sms_route *sms_route_find(struct hlr *hlr, + enum hlr_sms_route_type type, + const char *pattern) +{ + struct hlr_sms_route *rt; + + llist_for_each_entry(rt, &hlr->sms_routes, list) { + if (rt->type != type) + continue; + if (!strcmp(rt->match_pattern, pattern)) + return rt; + } + + return NULL; +} + +struct hlr_sms_route *sms_route_alloc(struct hlr *hlr, + enum hlr_sms_route_type type, + const char *pattern, + const struct hlr_euse *euse) +{ + struct hlr_sms_route *rt; + + if (sms_route_find(hlr, type, pattern)) + return NULL; + + rt = talloc(hlr, struct hlr_sms_route); + OSMO_ASSERT(rt != NULL); + + rt->match_pattern = talloc_strdup(rt, pattern); + rt->type = type; + rt->euse = euse; + + llist_add_tail(&rt->list, &hlr->sms_routes); + + return rt; +} + +void sms_route_del(struct hlr_sms_route *rt) +{ + llist_del(&rt->list); + talloc_free(rt); +} diff --git a/src/hlr_ussd.c b/src/hlr_ussd.c index 8cdc15c4..2c28f7b2 100644 --- a/src/hlr_ussd.c +++ b/src/hlr_ussd.c @@ -569,9 +569,9 @@ int rx_proc_ss_req(struct osmo_gsup_conn *conn, const struct osmo_gsup_message * ss->u.iuse = rt->u.iuse; } } else { - if (hlr->euse_default) { + if (hlr->ussd_euse_default) { ss->is_external = true; - ss->u.euse = hlr->euse_default; + ss->u.euse = hlr->ussd_euse_default; } } } diff --git a/src/hlr_vty.c b/src/hlr_vty.c index 848a1039..f3c1bd7c 100644 --- a/src/hlr_vty.c +++ b/src/hlr_vty.c @@ -38,6 +38,7 @@ #include #include #include +#include #include struct cmd_node hlr_node = { @@ -159,8 +160,8 @@ DEFUN(cfg_hlr_gsup_bind_ip, "Respond with subscribers' own MSISDN\n" \ "Respond with subscribers' own IMSI\n" -#define EXT_STR "External USSD Handler\n" \ - "Name of External USSD Handler (IPA CCM ID)\n" +#define EXT_STR "External USSD/SMS Handler\n" \ + "Name of External USSD/SMS Handler (IPA CCM ID)\n" DEFUN(cfg_ussd_route_pfx_int, cfg_ussd_route_pfx_int_cmd, "ussd route prefix PREFIX internal " INT_CHOICE, @@ -223,11 +224,11 @@ DEFUN(cfg_ussd_defaultroute, cfg_ussd_defaultroute_cmd, return CMD_WARNING; } - if (g_hlr->euse_default != euse) { - vty_out(vty, "Switching default route from %s to %s%s", - g_hlr->euse_default ? g_hlr->euse_default->name : "", + if (g_hlr->ussd_euse_default != euse) { + vty_out(vty, "Switching default USSD route from '%s' to '%s'%s", + g_hlr->ussd_euse_default ? g_hlr->ussd_euse_default->name : "", euse->name, VTY_NEWLINE); - g_hlr->euse_default = euse; + g_hlr->ussd_euse_default = euse; } return CMD_SUCCESS; @@ -237,7 +238,164 @@ DEFUN(cfg_ussd_no_defaultroute, cfg_ussd_no_defaultroute_cmd, "no ussd default-route", NO_STR USSD_STR "Remove the default-route for all USSD to unknown destinations\n") { - g_hlr->euse_default = NULL; + g_hlr->ussd_euse_default = NULL; + + return CMD_SUCCESS; +} + +/*********************************************************************** + * SMS forwarding entity + ***********************************************************************/ + +#define SMS_STR "SMS Routing Configuration\n" + +DEFUN(cfg_sms_route_smsc_addr, cfg_sms_route_smsc_addr_cmd, + "sms route smsc-address ADDRESS external EUSE", + SMS_STR "Add a new route\n" "Match by address of SMS Center\n" + "Address of SMS Center\n" EXT_STR) +{ + const struct hlr_sms_route *rt; + const struct hlr_euse *euse; + + rt = sms_route_find(g_hlr, HLR_SMS_RT_SMSC_ADDR, argv[0]); + if (rt) { + vty_out(vty, "%% Cannot add duplicate route for smsc-address '%s'%s", + argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + euse = euse_find(g_hlr, argv[1]); + if (!euse) { + vty_out(vty, "%% Cannot find EUSE '%s'%s", argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + + rt = sms_route_alloc(g_hlr, HLR_SMS_RT_SMSC_ADDR, argv[0], euse); + if (!rt) { + vty_out(vty, "%% Failed to add a new route for smsc-address '%s'%s", + argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_sms_no_route_smsc_addr, cfg_sms_no_route_smsc_addr_cmd, + "no sms route smsc-address ADDRESS", + SMS_STR "Delete a route\n" "Match by address of SMS Center\n" + "Address of SMS Center\n") +{ + struct hlr_sms_route *rt; + + rt = sms_route_find(g_hlr, HLR_SMS_RT_SMSC_ADDR, argv[0]); + if (!rt) { + vty_out(vty, "%% Cannot find route for smsc-address '%s'%s", + argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + sms_route_del(rt); + + return CMD_SUCCESS; +} + +DEFUN(cfg_sms_route_sender, cfg_sms_route_sender_cmd, + "sms route sender (msisdn|imsi) IDENT external EUSE", + SMS_STR "Add a new route\n" "Match by sender of SMS message\n" + "Identify subscriber by IMSI\n" + "Identify subscriber by MSISDN (phone number)\n" EXT_STR) +{ + const struct hlr_sms_route *rt; + enum hlr_sms_route_type type; + const struct hlr_euse *euse; + + if (argv[0][0] == 'm') + type = HLR_SMS_RT_SENDER_MSISDN; + else if (argv[0][0] == 'i') + type = HLR_SMS_RT_SENDER_IMSI; + else /* Shall not happen */ + OSMO_ASSERT(0); + + rt = sms_route_find(g_hlr, type, argv[1]); + if (rt) { + vty_out(vty, "%% Cannot add duplicate route for %s '%s'%s", + argv[0], argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + + euse = euse_find(g_hlr, argv[2]); + if (!euse) { + vty_out(vty, "%% Cannot find EUSE '%s'%s", argv[2], VTY_NEWLINE); + return CMD_WARNING; + } + + rt = sms_route_alloc(g_hlr, type, argv[1], euse); + if (!rt) { + vty_out(vty, "%% Failed to add a new route for %s '%s'%s", + argv[0], argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_sms_no_route_sender, cfg_sms_no_route_sender_cmd, + "no sms route sender (msisdn|imsi) IDENT", + SMS_STR "Delete a route\n" "Match by sender of SMS message\n" + "Identify subscriber by IMSI\n" + "Identify subscriber by MSISDN (phone number)\n") +{ + struct hlr_sms_route *rt; + enum hlr_sms_route_type type; + + if (argv[0][0] == 'm') + type = HLR_SMS_RT_SENDER_MSISDN; + else if (argv[0][0] == 'i') + type = HLR_SMS_RT_SENDER_IMSI; + else /* Shall not happen */ + OSMO_ASSERT(0); + + rt = sms_route_find(g_hlr, type, argv[1]); + if (!rt) { + vty_out(vty, "%% Cannot find route for %s '%s'%s", + argv[0], argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + + sms_route_del(rt); + + return CMD_SUCCESS; +} + +DEFUN(cfg_sms_defaultroute, cfg_sms_defaultroute_cmd, + "sms default-route external EUSE", + SMS_STR "Configure default-route for all " + "SMS to unknown destinations\n" EXT_STR) +{ + struct hlr_euse *euse; + + euse = euse_find(g_hlr, argv[0]); + if (!euse) { + vty_out(vty, "%% Cannot find EUSE %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + if (g_hlr->sms_euse_default != euse) { + vty_out(vty, "Switching default SMS route from '%s' to '%s'%s", + g_hlr->sms_euse_default ? g_hlr->sms_euse_default->name : "", + euse->name, VTY_NEWLINE); + g_hlr->sms_euse_default = euse; + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_sms_no_defaultroute, cfg_sms_no_defaultroute_cmd, + "no sms default-route", + NO_STR SMS_STR "Remove the default-route " + "for all SMS to unknown destinations\n") +{ + g_hlr->sms_euse_default = NULL; return CMD_SUCCESS; } @@ -288,7 +446,7 @@ DEFUN(cfg_no_euse, cfg_no_euse_cmd, vty_out(vty, "%% Cannot remove non-existant EUSE %s%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } - if (g_hlr->euse_default == euse) { + if (g_hlr->ussd_euse_default == euse || g_hlr->sms_euse_default == euse) { vty_out(vty, "%% Cannot remove EUSE %s, it is the default route%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } @@ -301,23 +459,53 @@ static void dump_one_euse(struct vty *vty, struct hlr_euse *euse) vty_out(vty, " euse %s%s", euse->name, VTY_NEWLINE); } +static const char *sms_route_type_to_str(enum hlr_sms_route_type type) +{ + switch (type) { + case HLR_SMS_RT_SMSC_ADDR: + return "smsc-address"; + case HLR_SMS_RT_SENDER_MSISDN: + return "sender msisdn"; + case HLR_SMS_RT_SENDER_IMSI: + return "sender imsi"; + } + + return NULL; +} + static int config_write_euse(struct vty *vty) { + struct hlr_ussd_route *ussd_rt; + struct hlr_sms_route *sms_rt; struct hlr_euse *euse; - struct hlr_ussd_route *rt; llist_for_each_entry(euse, &g_hlr->euse_list, list) dump_one_euse(vty, euse); - llist_for_each_entry(rt, &g_hlr->ussd_routes, list) { - vty_out(vty, " ussd route prefix %s %s %s%s", rt->prefix, - rt->is_external ? "external" : "internal", - rt->is_external ? rt->u.euse->name : rt->u.iuse->name, + llist_for_each_entry(ussd_rt, &g_hlr->ussd_routes, list) { + vty_out(vty, " ussd route prefix %s %s %s%s", ussd_rt->prefix, + ussd_rt->is_external ? "external" : "internal", + ussd_rt->is_external ? ussd_rt->u.euse->name : ussd_rt->u.iuse->name, VTY_NEWLINE); } - if (g_hlr->euse_default) - vty_out(vty, " ussd default-route external %s%s", g_hlr->euse_default->name, VTY_NEWLINE); + llist_for_each_entry(sms_rt, &g_hlr->sms_routes, list) { + vty_out(vty, " sms route %s %s external %s%s", + sms_route_type_to_str(sms_rt->type), + sms_rt->match_pattern, + sms_rt->euse->name, + VTY_NEWLINE); + } + + if (g_hlr->ussd_euse_default) { + vty_out(vty, " ussd default-route external %s%s", + g_hlr->ussd_euse_default->name, VTY_NEWLINE); + } + + if (g_hlr->sms_euse_default) { + vty_out(vty, " sms default-route external %s%s", + g_hlr->sms_euse_default->name, VTY_NEWLINE); + } if (g_hlr->ncss_guard_timeout != NCSS_GUARD_TIMEOUT_DEFAULT) vty_out(vty, " ncss-guard-timeout %i%s", @@ -453,6 +641,14 @@ void hlr_vty_init(void) install_element(HLR_NODE, &cfg_ussd_no_route_pfx_cmd); install_element(HLR_NODE, &cfg_ussd_defaultroute_cmd); install_element(HLR_NODE, &cfg_ussd_no_defaultroute_cmd); + + install_element(HLR_NODE, &cfg_sms_route_smsc_addr_cmd); + install_element(HLR_NODE, &cfg_sms_no_route_smsc_addr_cmd); + install_element(HLR_NODE, &cfg_sms_route_sender_cmd); + install_element(HLR_NODE, &cfg_sms_no_route_sender_cmd); + install_element(HLR_NODE, &cfg_sms_defaultroute_cmd); + install_element(HLR_NODE, &cfg_sms_no_defaultroute_cmd); + install_element(HLR_NODE, &cfg_ncss_guard_timeout_cmd); install_element(HLR_NODE, &cfg_store_imei_cmd); install_element(HLR_NODE, &cfg_no_store_imei_cmd); diff --git a/tests/test_nodes.vty b/tests/test_nodes.vty index a752c931..5205cb33 100644 --- a/tests/test_nodes.vty +++ b/tests/test_nodes.vty @@ -107,6 +107,8 @@ OsmoHLR(config-hlr)# list database PATH euse NAME no euse NAME + sms route smsc-address ADDRESS external EUSE + sms route sender (msisdn|imsi) IDENT external EUSE ussd route prefix PREFIX internal (own-msisdn|own-imsi) ussd route prefix PREFIX external EUSE no ussd route prefix PREFIX