blob: c4e99e219c5737c322d28e394c6ae994602dc3a2 [file] [log] [blame]
Neels Hofmeyr7685a782017-01-30 23:30:26 +01001/* OsmoHLR VTY implementation */
2
3/* (C) 2016 sysmocom s.f.m.c. GmbH <info@sysmocom.de>
Neels Hofmeyr7685a782017-01-30 23:30:26 +01004 * Author: Neels Hofmeyr <nhofmeyr@sysmocom.de>
Harald Weltefa7ee332018-06-24 13:20:32 +02005 * (C) 2018 Harald Welte <laforge@gnumonks.org>
6 *
7 * All Rights Reserved
Neels Hofmeyr7685a782017-01-30 23:30:26 +01008 *
Harald Welte4956ae12018-06-15 22:04:28 +02009 * (C) 2018 Harald Welte <laforge@gnumonks.org>
10 *
11 * All Rights Reserved
12 *
Neels Hofmeyr7685a782017-01-30 23:30:26 +010013 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU Affero General Public License as published by
15 * the Free Software Foundation; either version 3 of the License, or
16 * (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Affero General Public License for more details.
22 *
23 * You should have received a copy of the GNU Affero General Public License
24 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25 *
26 */
27
Alexander Couzens268a33e2020-01-12 00:48:07 +010028#include <errno.h>
Alexander Couzens37f0b3a2023-04-11 19:28:36 +020029#include <string.h>
Alexander Couzens268a33e2020-01-12 00:48:07 +010030
Pau Espin Pedrolce9bc402017-05-31 13:19:22 +020031#include <osmocom/core/talloc.h>
Alexander Couzens268a33e2020-01-12 00:48:07 +010032#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
Alexander Couzens37f0b3a2023-04-11 19:28:36 +020033#include <osmocom/gsm/apn.h>
34
Neels Hofmeyr7685a782017-01-30 23:30:26 +010035#include <osmocom/vty/vty.h>
Max20ddfdb2019-02-18 13:12:27 +010036#include <osmocom/vty/stats.h>
Neels Hofmeyr7685a782017-01-30 23:30:26 +010037#include <osmocom/vty/command.h>
38#include <osmocom/vty/logging.h>
Harald Welte7ee6e552018-02-14 00:52:05 +010039#include <osmocom/vty/misc.h>
Harald Weltefa7ee332018-06-24 13:20:32 +020040#include <osmocom/abis/ipa.h>
Neels Hofmeyr7685a782017-01-30 23:30:26 +010041
Neels Hofmeyr2f758032019-11-20 00:37:07 +010042#include <osmocom/hlr/db.h>
43#include <osmocom/hlr/hlr.h>
44#include <osmocom/hlr/hlr_vty.h>
45#include <osmocom/hlr/hlr_vty_subscr.h>
46#include <osmocom/hlr/hlr_ussd.h>
Mychaela N. Falconiaff7c7ea2023-09-21 01:55:51 +000047#include <osmocom/hlr/hlr_sms.h>
Neels Hofmeyr2f758032019-11-20 00:37:07 +010048#include <osmocom/hlr/gsup_server.h>
Neels Hofmeyr7685a782017-01-30 23:30:26 +010049
Alexander Couzens268a33e2020-01-12 00:48:07 +010050static const struct value_string gsm48_gmm_cause_vty_names[] = {
51 { GMM_CAUSE_IMSI_UNKNOWN, "imsi-unknown" },
52 { GMM_CAUSE_ILLEGAL_MS, "illegal-ms" },
53 { GMM_CAUSE_PLMN_NOTALLOWED, "plmn-not-allowed" },
54 { GMM_CAUSE_LA_NOTALLOWED, "la-not-allowed" },
55 { GMM_CAUSE_ROAMING_NOTALLOWED, "roaming-not-allowed" },
56 { GMM_CAUSE_NO_SUIT_CELL_IN_LA, "no-suitable-cell-in-la" },
57 { GMM_CAUSE_NET_FAIL, "net-fail" },
58 { GMM_CAUSE_CONGESTION, "congestion" },
59 { GMM_CAUSE_GSM_AUTH_UNACCEPT, "auth-unacceptable" },
60 { GMM_CAUSE_PROTO_ERR_UNSPEC, "proto-error-unspec" },
61 { 0, NULL },
62};
63
64/* TS 24.008 4.4.4.7 */
65static const struct value_string gsm48_gmm_cause_vty_descs[] = {
66 { GMM_CAUSE_IMSI_UNKNOWN, " #02: (IMSI unknown in HLR)" },
67 { GMM_CAUSE_ILLEGAL_MS, " #03 (Illegal MS)" },
68 { GMM_CAUSE_PLMN_NOTALLOWED, " #11: (PLMN not allowed)" },
69 { GMM_CAUSE_LA_NOTALLOWED, " #12: (Location Area not allowed)" },
70 { GMM_CAUSE_ROAMING_NOTALLOWED, " #13: (Roaming not allowed in this location area)" },
71 { GMM_CAUSE_NO_SUIT_CELL_IN_LA, " #15: (No Suitable Cells In Location Area [continue search in PLMN])." },
72 { GMM_CAUSE_NET_FAIL, " #17: (Network Failure)" },
73 { GMM_CAUSE_CONGESTION, " #22: (Congestion)" },
74 { GMM_CAUSE_GSM_AUTH_UNACCEPT, " #23: (GSM authentication unacceptable [UMTS])" },
75 { GMM_CAUSE_PROTO_ERR_UNSPEC, "#111: (Protocol error, unspecified)" },
76 { 0, NULL },
77};
78
79
Pau Espin Pedrolce9bc402017-05-31 13:19:22 +020080struct cmd_node hlr_node = {
81 HLR_NODE,
82 "%s(config-hlr)# ",
83 1,
84};
85
86DEFUN(cfg_hlr,
87 cfg_hlr_cmd,
88 "hlr",
89 "Configure the HLR")
90{
91 vty->node = HLR_NODE;
92 return CMD_SUCCESS;
93}
94
95struct cmd_node gsup_node = {
96 GSUP_NODE,
97 "%s(config-hlr-gsup)# ",
98 1,
99};
100
101DEFUN(cfg_gsup,
102 cfg_gsup_cmd,
103 "gsup",
104 "Configure GSUP options")
105{
106 vty->node = GSUP_NODE;
107 return CMD_SUCCESS;
108}
109
Alexander Couzens37f0b3a2023-04-11 19:28:36 +0200110struct cmd_node ps_node = {
111 PS_NODE,
112 "%s(config-hlr-ps)# ",
113 1,
114};
115
116DEFUN(cfg_ps,
117 cfg_ps_cmd,
118 "ps",
119 "Configure the PS options")
120{
121 vty->node = PS_NODE;
122 return CMD_SUCCESS;
123}
124
125struct cmd_node ps_pdp_profiles_node = {
126 PS_PDP_PROFILES_NODE,
127 "%s(config-hlr-ps-pdp-profiles)# ",
128 1,
129};
130
131DEFUN(cfg_ps_pdp_profiles,
132 cfg_ps_pdp_profiles_cmd,
133 "pdp-profiles default",
134 "Define a PDP profile set.\n"
135 "Define the global default profile.\n")
136{
137 g_hlr->ps.pdp_profile.enabled = true;
138
139 vty->node = PS_PDP_PROFILES_NODE;
140 return CMD_SUCCESS;
141}
142
143DEFUN(cfg_no_ps_pdp_profiles,
144 cfg_no_ps_pdp_profiles_cmd,
145 "no pdp-profiles default",
146 NO_STR
147 "Delete PDP profile.\n"
148 "Unique identifier for this PDP profile set.\n")
149{
150 g_hlr->ps.pdp_profile.enabled = false;
151 return CMD_SUCCESS;
152}
153
154
155
156struct cmd_node ps_pdp_profiles_profile_node = {
157 PS_PDP_PROFILES_PROFILE_NODE,
158 "%s(config-hlr-ps-pdp-profile)# ",
159 1,
160};
161
162
163/* context_id == 0 means the slot is free */
164struct osmo_gsup_pdp_info *get_pdp_profile(uint8_t context_id)
165{
166 for (int i = 0; i < OSMO_GSUP_MAX_NUM_PDP_INFO; i++) {
167 struct osmo_gsup_pdp_info *info = &g_hlr->ps.pdp_profile.pdp_infos[i];
168 if (info->context_id == context_id)
169 return info;
170 }
171
172 return NULL;
173}
174
175struct osmo_gsup_pdp_info *create_pdp_profile(uint8_t context_id)
176{
177 struct osmo_gsup_pdp_info *info = get_pdp_profile(0);
178 if (!info)
179 return NULL;
180
181 memset(info, 0, sizeof(*info));
182 info->context_id = context_id;
183 info->have_info = 1;
184
185 g_hlr->ps.pdp_profile.num_pdp_infos++;
186 return info;
187}
188
189void destroy_pdp_profile(struct osmo_gsup_pdp_info *info)
190{
191 info->context_id = 0;
192 if (info->apn_enc)
193 talloc_free((void *) info->apn_enc);
194
195 g_hlr->ps.pdp_profile.num_pdp_infos--;
196 memset(info, 0, sizeof(*info));
197}
198
199DEFUN(cfg_ps_pdp_profiles_profile,
200 cfg_ps_pdp_profiles_profile_cmd,
201 "profile <1-10>",
202 "Configure a PDP profile\n"
203 "Unique PDP context identifier. The lowest profile will be used as default context.\n")
204{
205 struct osmo_gsup_pdp_info *info;
206 uint8_t context_id = atoi(argv[0]);
207
208 info = get_pdp_profile(context_id);
209 if (!info) {
210 info = create_pdp_profile(context_id);
211 if (!info) {
212 vty_out(vty, "Failed to create profile %d!%s", context_id, VTY_NEWLINE);
213 return CMD_ERR_INCOMPLETE;
214 }
215 }
216
217 vty->node = PS_PDP_PROFILES_PROFILE_NODE;
218 vty->index = info;
219 return CMD_SUCCESS;
220}
221
222DEFUN(cfg_no_ps_pdp_profiles_profile,
223 cfg_no_ps_pdp_profiles_profile_cmd,
224 "no profile <1-10>",
225 NO_STR
226 "Delete a PDP profile\n"
227 "Unique PDP context identifier. The lowest profile will be used as default context.\n")
228{
229 struct osmo_gsup_pdp_info *info;
230 uint8_t context_id = atoi(argv[0]);
231
232 info = get_pdp_profile(context_id);
233 if (info)
234 destroy_pdp_profile(info);
235
236 return CMD_SUCCESS;
237}
238
239DEFUN(cfg_ps_pdp_profile_apn, cfg_ps_pdp_profile_apn_cmd,
240 "apn ID",
241 "Configure the APN.\n"
242 "APN name or * for wildcard apn.\n")
243{
244 struct osmo_gsup_pdp_info *info = vty->index;
245 const char *apn_name = argv[0];
246
247 /* apn encoded takes one more byte than strlen() */
248 size_t apn_enc_len = strlen(apn_name) + 1;
249 uint8_t *apn_enc;
250 int ret;
251
252 if (apn_enc_len > APN_MAXLEN) {
253 vty_out(vty, "APN name is too long '%s'. Max is %d!%s", apn_name, APN_MAXLEN, VTY_NEWLINE);
254 return CMD_ERR_INCOMPLETE;
255 }
256
257 info->apn_enc = apn_enc = (uint8_t *) talloc_zero_size(g_hlr, apn_enc_len);
258 ret = info->apn_enc_len = osmo_apn_from_str(apn_enc, apn_enc_len, apn_name);
259 if (ret < 0) {
260 talloc_free(apn_enc);
261 info->apn_enc = NULL;
262 info->apn_enc_len = 0;
263 vty_out(vty, "Invalid APN name %s!", apn_name);
264 return CMD_WARNING;
265 }
266
267 return CMD_SUCCESS;
268}
269
270DEFUN(cfg_no_ps_pdp_profile_apn, cfg_no_ps_pdp_profile_apn_cmd,
271 "no apn",
272 NO_STR
273 "Delete the APN.\n")
274{
275 struct osmo_gsup_pdp_info *info = vty->index;
276 if (info->apn_enc) {
277 talloc_free((void *) info->apn_enc);
278 info->apn_enc = NULL;
279 info->apn_enc_len = 0;
280 }
281
282 return CMD_SUCCESS;
283}
284
285
Pau Espin Pedrolce9bc402017-05-31 13:19:22 +0200286static int config_write_hlr(struct vty *vty)
287{
288 vty_out(vty, "hlr%s", VTY_NEWLINE);
Alexander Couzens268a33e2020-01-12 00:48:07 +0100289
290 if (g_hlr->reject_cause != GMM_CAUSE_IMSI_UNKNOWN)
291 vty_out(vty, " reject-cause not-found %s%s",
292 get_value_string_or_null(gsm48_gmm_cause_vty_names,
293 (uint32_t) g_hlr->reject_cause), VTY_NEWLINE);
294 if (g_hlr->no_proxy_reject_cause != GMM_CAUSE_IMSI_UNKNOWN)
295 vty_out(vty, " reject-cause no-proxy %s%s",
296 get_value_string_or_null(gsm48_gmm_cause_vty_names,
297 (uint32_t) g_hlr->no_proxy_reject_cause), VTY_NEWLINE);
Oliver Smith851814a2019-01-11 15:30:21 +0100298 if (g_hlr->store_imei)
299 vty_out(vty, " store-imei%s", VTY_NEWLINE);
Neels Hofmeyr5857c592019-04-02 04:24:49 +0200300 if (g_hlr->db_file_path && strcmp(g_hlr->db_file_path, HLR_DEFAULT_DB_FILE_PATH))
301 vty_out(vty, " database %s%s", g_hlr->db_file_path, VTY_NEWLINE);
Oliver Smithc7f17872019-03-04 15:10:44 +0100302 if (g_hlr->subscr_create_on_demand) {
303 const char *flags_str = "none";
304 uint8_t flags = g_hlr->subscr_create_on_demand_flags;
305 unsigned int rand_msisdn_len = g_hlr->subscr_create_on_demand_rand_msisdn_len;
306
307 if ((flags & DB_SUBSCR_FLAG_NAM_CS) && (flags & DB_SUBSCR_FLAG_NAM_PS))
308 flags_str = "cs+ps";
309 else if (flags & DB_SUBSCR_FLAG_NAM_CS)
310 flags_str = "cs";
311 else if (flags & DB_SUBSCR_FLAG_NAM_PS)
312 flags_str = "ps";
313
314 if (rand_msisdn_len)
315 vty_out(vty, " subscriber-create-on-demand %i %s%s", rand_msisdn_len, flags_str, VTY_NEWLINE);
316 else
317 vty_out(vty, " subscriber-create-on-demand no-msisdn %s%s", flags_str, VTY_NEWLINE);
318 }
Pau Espin Pedrolce9bc402017-05-31 13:19:22 +0200319 return CMD_SUCCESS;
320}
321
322static int config_write_hlr_gsup(struct vty *vty)
323{
324 vty_out(vty, " gsup%s", VTY_NEWLINE);
325 if (g_hlr->gsup_bind_addr)
326 vty_out(vty, " bind ip %s%s", g_hlr->gsup_bind_addr, VTY_NEWLINE);
Keith649c3352021-02-26 01:05:31 +0100327 if (g_hlr->gsup_unit_name.serno)
328 vty_out(vty, " ipa-name %s%s", g_hlr->gsup_unit_name.serno, VTY_NEWLINE);
Pau Espin Pedrolce9bc402017-05-31 13:19:22 +0200329 return CMD_SUCCESS;
330}
331
Alexander Couzens37f0b3a2023-04-11 19:28:36 +0200332static int config_write_hlr_ps(struct vty *vty)
333{
334 vty_out(vty, " ps%s", VTY_NEWLINE);
335 return CMD_SUCCESS;
336}
337
338static int config_write_hlr_ps_pdp_profiles(struct vty *vty)
339{
340 char apn[APN_MAXLEN + 1] = {};
341
342 if (!g_hlr->ps.pdp_profile.enabled)
343 return CMD_SUCCESS;
344
345 vty_out(vty, " pdp-profiles default%s", VTY_NEWLINE);
346 for (int i = 0; i < g_hlr->ps.pdp_profile.num_pdp_infos; i++) {
347 struct osmo_gsup_pdp_info *pdp_info = &g_hlr->ps.pdp_profile.pdp_infos[i];
348 if (!pdp_info->context_id)
349 continue;
350
351 vty_out(vty, " profile %d%s", pdp_info->context_id, VTY_NEWLINE);
352 if (!pdp_info->have_info)
353 continue;
354
355 if (pdp_info->apn_enc && pdp_info->apn_enc_len) {
356 osmo_apn_to_str(apn, pdp_info->apn_enc, pdp_info->apn_enc_len);
357 vty_out(vty, " apn %s%s", apn, VTY_NEWLINE);
358 }
359 }
360 return CMD_SUCCESS;
361}
362
Harald Weltefa7ee332018-06-24 13:20:32 +0200363static void show_one_conn(struct vty *vty, const struct osmo_gsup_conn *conn)
364{
365 const struct ipa_server_conn *isc = conn->conn;
366 char *name;
367 int rc;
368
369 rc = osmo_gsup_conn_ccm_get(conn, (uint8_t **) &name, IPAC_IDTAG_SERNR);
370 OSMO_ASSERT(rc);
371
Neels Hofmeyr3f9d1972019-12-12 04:04:53 +0100372 vty_out(vty, " '%s' from %s:%5u, CS=%u, PS=%u%s",
373 name, isc->addr, isc->port, conn->supports_cs, conn->supports_ps,
Harald Weltefa7ee332018-06-24 13:20:32 +0200374 VTY_NEWLINE);
375}
376
377DEFUN(show_gsup_conn, show_gsup_conn_cmd,
378 "show gsup-connections",
379 SHOW_STR "GSUP Connections from VLRs, SGSNs, EUSEs\n")
380{
381 struct osmo_gsup_server *gs = g_hlr->gs;
382 struct osmo_gsup_conn *conn;
383
384 llist_for_each_entry(conn, &gs->clients, list)
385 show_one_conn(vty, conn);
386
387 return CMD_SUCCESS;
388}
389
Pau Espin Pedrolce9bc402017-05-31 13:19:22 +0200390DEFUN(cfg_hlr_gsup_bind_ip,
391 cfg_hlr_gsup_bind_ip_cmd,
392 "bind ip A.B.C.D",
393 "Listen/Bind related socket option\n"
394 IP_STR
395 "IPv4 Address to bind the GSUP interface to\n")
396{
397 if(g_hlr->gsup_bind_addr)
398 talloc_free(g_hlr->gsup_bind_addr);
399 g_hlr->gsup_bind_addr = talloc_strdup(g_hlr, argv[0]);
400
401 return CMD_SUCCESS;
402}
403
Neels Hofmeyr76328bd2019-11-20 03:35:37 +0100404DEFUN(cfg_hlr_gsup_ipa_name,
405 cfg_hlr_gsup_ipa_name_cmd,
406 "ipa-name NAME",
407 "Set the IPA name of this HLR, for proxying to remote HLRs\n"
408 "A globally unique name for this HLR. For example: PLMN + redundancy server number: HLR-901-70-0. "
409 "This name is used for GSUP routing and must be set if multiple HLRs interconnect (e.g. mslookup "
410 "for Distributed GSM).\n")
411{
412 if (vty->type != VTY_FILE) {
413 vty_out(vty, "gsup/ipa-name: The GSUP IPA name cannot be changed at run-time; "
Max53f60672022-09-18 20:51:23 +0700414 "It can only be set in the configuration file.%s", VTY_NEWLINE);
Neels Hofmeyr76328bd2019-11-20 03:35:37 +0100415 return CMD_WARNING;
416 }
417
418 g_hlr->gsup_unit_name.serno = talloc_strdup(g_hlr, argv[0]);
419 return CMD_SUCCESS;
420}
421
Harald Welte4956ae12018-06-15 22:04:28 +0200422/***********************************************************************
Harald Weltedab544e2018-07-29 16:14:48 +0200423 * USSD Entity
Harald Welte4956ae12018-06-15 22:04:28 +0200424 ***********************************************************************/
425
Harald Weltedab544e2018-07-29 16:14:48 +0200426#define USSD_STR "USSD Configuration\n"
427#define UROUTE_STR "Routing Configuration\n"
428#define PREFIX_STR "Prefix-Matching Route\n" "USSD Prefix\n"
Harald Welte4956ae12018-06-15 22:04:28 +0200429
Vadim Yanitskiydac855e2020-11-17 04:17:46 +0700430#define INT_CHOICE "(own-msisdn|own-imsi|test-idle)"
Harald Weltedab544e2018-07-29 16:14:48 +0200431#define INT_STR "Internal USSD Handler\n" \
432 "Respond with subscribers' own MSISDN\n" \
Vadim Yanitskiydac855e2020-11-17 04:17:46 +0700433 "Respond with subscribers' own IMSI\n" \
434 "Keep the session idle (useful for testing)\n"
Harald Weltedab544e2018-07-29 16:14:48 +0200435
436#define EXT_STR "External USSD Handler\n" \
437 "Name of External USSD Handler (IPA CCM ID)\n"
438
439DEFUN(cfg_ussd_route_pfx_int, cfg_ussd_route_pfx_int_cmd,
440 "ussd route prefix PREFIX internal " INT_CHOICE,
441 USSD_STR UROUTE_STR PREFIX_STR INT_STR)
442{
443 const struct hlr_iuse *iuse = iuse_find(argv[1]);
444 struct hlr_ussd_route *rt = ussd_route_find_prefix(g_hlr, argv[0]);
Harald Welte4956ae12018-06-15 22:04:28 +0200445 if (rt) {
446 vty_out(vty, "%% Cannot add [another?] route for prefix %s%s", argv[0], VTY_NEWLINE);
447 return CMD_WARNING;
448 }
Harald Weltedab544e2018-07-29 16:14:48 +0200449 ussd_route_prefix_alloc_int(g_hlr, argv[0], iuse);
Harald Welte4956ae12018-06-15 22:04:28 +0200450
451 return CMD_SUCCESS;
452}
453
Harald Weltedab544e2018-07-29 16:14:48 +0200454DEFUN(cfg_ussd_route_pfx_ext, cfg_ussd_route_pfx_ext_cmd,
455 "ussd route prefix PREFIX external EUSE",
456 USSD_STR UROUTE_STR PREFIX_STR EXT_STR)
Harald Welte4956ae12018-06-15 22:04:28 +0200457{
Harald Weltedab544e2018-07-29 16:14:48 +0200458 struct hlr_euse *euse = euse_find(g_hlr, argv[1]);
459 struct hlr_ussd_route *rt = ussd_route_find_prefix(g_hlr, argv[0]);
460 if (rt) {
461 vty_out(vty, "%% Cannot add [another?] route for prefix %s%s", argv[0], VTY_NEWLINE);
462 return CMD_WARNING;
463 }
464 if (!euse) {
465 vty_out(vty, "%% Cannot find euse '%s'%s", argv[1], VTY_NEWLINE);
466 return CMD_WARNING;
467 }
468 ussd_route_prefix_alloc_ext(g_hlr, argv[0], euse);
469
470 return CMD_SUCCESS;
471}
472
473DEFUN(cfg_ussd_no_route_pfx, cfg_ussd_no_route_pfx_cmd,
474 "no ussd route prefix PREFIX",
475 NO_STR USSD_STR UROUTE_STR PREFIX_STR)
476{
477 struct hlr_ussd_route *rt = ussd_route_find_prefix(g_hlr, argv[0]);
Harald Welte4956ae12018-06-15 22:04:28 +0200478 if (!rt) {
479 vty_out(vty, "%% Cannot find route for prefix %s%s", argv[0], VTY_NEWLINE);
480 return CMD_WARNING;
481 }
Harald Weltedab544e2018-07-29 16:14:48 +0200482 ussd_route_del(rt);
Harald Welte4956ae12018-06-15 22:04:28 +0200483
484 return CMD_SUCCESS;
485}
486
Harald Weltedab544e2018-07-29 16:14:48 +0200487DEFUN(cfg_ussd_defaultroute, cfg_ussd_defaultroute_cmd,
488 "ussd default-route external EUSE",
489 USSD_STR "Configure default-route for all USSD to unknown destinations\n"
490 EXT_STR)
Harald Welte4956ae12018-06-15 22:04:28 +0200491{
Vadim Yanitskiyb93c44f2018-08-02 23:37:51 +0700492 struct hlr_euse *euse;
493
494 euse = euse_find(g_hlr, argv[0]);
495 if (!euse) {
496 vty_out(vty, "%% Cannot find EUSE %s%s", argv[0], VTY_NEWLINE);
497 return CMD_WARNING;
498 }
Harald Welte4956ae12018-06-15 22:04:28 +0200499
500 if (g_hlr->euse_default != euse) {
501 vty_out(vty, "Switching default route from %s to %s%s",
Harald Welte55d32a12018-07-30 17:26:35 +0200502 g_hlr->euse_default ? g_hlr->euse_default->name : "<none>",
503 euse->name, VTY_NEWLINE);
Harald Welte4956ae12018-06-15 22:04:28 +0200504 g_hlr->euse_default = euse;
505 }
506
507 return CMD_SUCCESS;
508}
509
Harald Weltedab544e2018-07-29 16:14:48 +0200510DEFUN(cfg_ussd_no_defaultroute, cfg_ussd_no_defaultroute_cmd,
511 "no ussd default-route",
512 NO_STR USSD_STR "Remove the default-route for all USSD to unknown destinations\n")
Harald Welte4956ae12018-06-15 22:04:28 +0200513{
Harald Welte4956ae12018-06-15 22:04:28 +0200514 g_hlr->euse_default = NULL;
515
516 return CMD_SUCCESS;
517}
518
Neels Hofmeyr5857c592019-04-02 04:24:49 +0200519DEFUN(cfg_database, cfg_database_cmd,
520 "database PATH",
521 "Set the path to the HLR database file\n"
522 "Relative or absolute file system path to the database file (default is '" HLR_DEFAULT_DB_FILE_PATH "')\n")
523{
524 osmo_talloc_replace_string(g_hlr, &g_hlr->db_file_path, argv[0]);
525 return CMD_SUCCESS;
526}
527
Harald Welte4956ae12018-06-15 22:04:28 +0200528struct cmd_node euse_node = {
529 EUSE_NODE,
530 "%s(config-hlr-euse)# ",
531 1,
532};
533
534DEFUN(cfg_euse, cfg_euse_cmd,
535 "euse NAME",
536 "Configure a particular External USSD Entity\n"
537 "Alphanumeric name of the External USSD Entity\n")
538{
539 struct hlr_euse *euse;
540 const char *id = argv[0];
541
542 euse = euse_find(g_hlr, id);
543 if (!euse) {
544 euse = euse_alloc(g_hlr, id);
545 if (!euse)
546 return CMD_WARNING;
547 }
548 vty->index = euse;
549 vty->index_sub = &euse->description;
550 vty->node = EUSE_NODE;
551
552 return CMD_SUCCESS;
553}
554
555DEFUN(cfg_no_euse, cfg_no_euse_cmd,
556 "no euse NAME",
557 NO_STR "Remove a particular External USSD Entity\n"
558 "Alphanumeric name of the External USSD Entity\n")
559{
560 struct hlr_euse *euse = euse_find(g_hlr, argv[0]);
561 if (!euse) {
Vadim Yanitskiyc47d5c02020-10-29 18:05:22 +0700562 vty_out(vty, "%% Cannot remove non-existent EUSE %s%s", argv[0], VTY_NEWLINE);
Harald Welte4956ae12018-06-15 22:04:28 +0200563 return CMD_WARNING;
564 }
565 if (g_hlr->euse_default == euse) {
566 vty_out(vty, "%% Cannot remove EUSE %s, it is the default route%s", argv[0], VTY_NEWLINE);
567 return CMD_WARNING;
568 }
569 euse_del(euse);
570 return CMD_SUCCESS;
571}
572
573static void dump_one_euse(struct vty *vty, struct hlr_euse *euse)
574{
Harald Welte4956ae12018-06-15 22:04:28 +0200575 vty_out(vty, " euse %s%s", euse->name, VTY_NEWLINE);
Harald Welte4956ae12018-06-15 22:04:28 +0200576}
577
578static int config_write_euse(struct vty *vty)
579{
580 struct hlr_euse *euse;
Harald Weltedab544e2018-07-29 16:14:48 +0200581 struct hlr_ussd_route *rt;
Harald Welte4956ae12018-06-15 22:04:28 +0200582
583 llist_for_each_entry(euse, &g_hlr->euse_list, list)
584 dump_one_euse(vty, euse);
585
Harald Weltedab544e2018-07-29 16:14:48 +0200586 llist_for_each_entry(rt, &g_hlr->ussd_routes, list) {
587 vty_out(vty, " ussd route prefix %s %s %s%s", rt->prefix,
588 rt->is_external ? "external" : "internal",
589 rt->is_external ? rt->u.euse->name : rt->u.iuse->name,
590 VTY_NEWLINE);
591 }
592
593 if (g_hlr->euse_default)
594 vty_out(vty, " ussd default-route external %s%s", g_hlr->euse_default->name, VTY_NEWLINE);
595
Vadim Yanitskiyd157a562018-12-01 00:03:39 +0700596 if (g_hlr->ncss_guard_timeout != NCSS_GUARD_TIMEOUT_DEFAULT)
597 vty_out(vty, " ncss-guard-timeout %i%s",
598 g_hlr->ncss_guard_timeout, VTY_NEWLINE);
599
Harald Welte4956ae12018-06-15 22:04:28 +0200600 return 0;
601}
602
Vadim Yanitskiyd157a562018-12-01 00:03:39 +0700603DEFUN(cfg_ncss_guard_timeout, cfg_ncss_guard_timeout_cmd,
604 "ncss-guard-timeout <0-255>",
605 "Set guard timer for NCSS (call independent SS) session activity\n"
606 "Guard timer value (sec.), or 0 to disable")
607{
608 g_hlr->ncss_guard_timeout = atoi(argv[0]);
609 return CMD_SUCCESS;
610}
611
Mychaela N. Falconiaff7c7ea2023-09-21 01:55:51 +0000612/***********************************************************************
613 * Routing of SM-RL to GSUP-attached SMSCs
614 ***********************************************************************/
615
616#define SMSC_STR "Configuration of GSUP routing to SMSCs\n"
617
618struct cmd_node smsc_node = {
619 SMSC_NODE,
620 "%s(config-hlr-smsc)# ",
621 1,
622};
623
624DEFUN(cfg_smsc_entity, cfg_smsc_entity_cmd,
625 "smsc entity NAME",
626 SMSC_STR
627 "Configure a particular external SMSC\n"
628 "IPA name of the external SMSC\n")
629{
630 struct hlr_smsc *smsc;
631 const char *id = argv[0];
632
633 smsc = smsc_find(g_hlr, id);
634 if (!smsc) {
635 smsc = smsc_alloc(g_hlr, id);
636 if (!smsc)
637 return CMD_WARNING;
638 }
639 vty->index = smsc;
640 vty->index_sub = &smsc->description;
641 vty->node = SMSC_NODE;
642
643 return CMD_SUCCESS;
644}
645
646DEFUN(cfg_no_smsc_entity, cfg_no_smsc_entity_cmd,
647 "no smsc entity NAME",
648 NO_STR SMSC_STR "Remove a particular external SMSC\n"
649 "IPA name of the external SMSC\n")
650{
651 struct hlr_smsc *smsc = smsc_find(g_hlr, argv[0]);
652 if (!smsc) {
653 vty_out(vty, "%% Cannot remove non-existent SMSC %s%s",
654 argv[0], VTY_NEWLINE);
655 return CMD_WARNING;
656 }
657 if (g_hlr->smsc_default == smsc) {
658 vty_out(vty,
659 "%% Cannot remove SMSC %s, it is the default route%s",
660 argv[0], VTY_NEWLINE);
661 return CMD_WARNING;
662 }
663 smsc_free(smsc);
664 return CMD_SUCCESS;
665}
666
667DEFUN(cfg_smsc_route, cfg_smsc_route_cmd,
668 "smsc route NUMBER NAME",
669 SMSC_STR
670 "Configure GSUP route to a particular SMSC\n"
671 "Numeric address of this SMSC, must match EF.SMSP programming in SIMs\n"
672 "IPA name of the external SMSC\n")
673{
674 struct hlr_smsc *smsc = smsc_find(g_hlr, argv[1]);
675 struct hlr_smsc_route *rt = smsc_route_find(g_hlr, argv[0]);
676 if (rt) {
677 vty_out(vty,
678 "%% Cannot add [another?] route for SMSC address %s%s",
679 argv[0], VTY_NEWLINE);
680 return CMD_WARNING;
681 }
682 if (!smsc) {
683 vty_out(vty, "%% Cannot find SMSC '%s'%s", argv[1],
684 VTY_NEWLINE);
685 return CMD_WARNING;
686 }
687 smsc_route_alloc(g_hlr, argv[0], smsc);
688
689 return CMD_SUCCESS;
690}
691
692DEFUN(cfg_no_smsc_route, cfg_no_smsc_route_cmd,
693 "no smsc route NUMBER",
694 NO_STR SMSC_STR "Remove GSUP route to a particular SMSC\n"
695 "Numeric address of the SMSC\n")
696{
697 struct hlr_smsc_route *rt = smsc_route_find(g_hlr, argv[0]);
698 if (!rt) {
699 vty_out(vty, "%% Cannot find route for SMSC address %s%s",
700 argv[0], VTY_NEWLINE);
701 return CMD_WARNING;
702 }
703 smsc_route_free(rt);
704
705 return CMD_SUCCESS;
706}
707
708DEFUN(cfg_smsc_defroute, cfg_smsc_defroute_cmd,
709 "smsc default-route NAME",
710 SMSC_STR
711 "Configure default SMSC route for unknown SMSC numeric addresses\n"
712 "IPA name of the external SMSC\n")
713{
714 struct hlr_smsc *smsc;
715
716 smsc = smsc_find(g_hlr, argv[0]);
717 if (!smsc) {
718 vty_out(vty, "%% Cannot find SMSC %s%s", argv[0], VTY_NEWLINE);
719 return CMD_WARNING;
720 }
721
722 if (g_hlr->smsc_default != smsc) {
723 vty_out(vty, "Switching default route from %s to %s%s",
724 g_hlr->smsc_default ? g_hlr->smsc_default->name : "<none>",
725 smsc->name, VTY_NEWLINE);
726 g_hlr->smsc_default = smsc;
727 }
728
729 return CMD_SUCCESS;
730}
731
732DEFUN(cfg_no_smsc_defroute, cfg_no_smsc_defroute_cmd,
733 "no smsc default-route",
734 NO_STR SMSC_STR
735 "Remove default SMSC route for unknown SMSC numeric addresses\n")
736{
737 g_hlr->smsc_default = NULL;
738
739 return CMD_SUCCESS;
740}
741
742static void dump_one_smsc(struct vty *vty, struct hlr_smsc *smsc)
743{
744 vty_out(vty, " smsc entity %s%s", smsc->name, VTY_NEWLINE);
745}
746
747static int config_write_smsc(struct vty *vty)
748{
749 struct hlr_smsc *smsc;
750 struct hlr_smsc_route *rt;
751
752 llist_for_each_entry(smsc, &g_hlr->smsc_list, list)
753 dump_one_smsc(vty, smsc);
754
755 llist_for_each_entry(rt, &g_hlr->smsc_routes, list) {
756 vty_out(vty, " smsc route %s %s%s", rt->num_addr,
757 rt->smsc->name, VTY_NEWLINE);
758 }
759
760 if (g_hlr->smsc_default)
761 vty_out(vty, " smsc default-route %s%s",
762 g_hlr->smsc_default->name, VTY_NEWLINE);
763
764 return 0;
765}
Alexander Couzens268a33e2020-01-12 00:48:07 +0100766
767DEFUN(cfg_reject_cause, cfg_reject_cause_cmd,
768 "reject-cause TYPE CAUSE", "") /* Dynamically Generated */
769{
770 int cause_code = get_string_value(gsm48_gmm_cause_vty_names, argv[1]);
771 OSMO_ASSERT(cause_code >= 0);
772
773 if (strcmp(argv[0], "not-found") == 0)
774 g_hlr->reject_cause = (enum gsm48_gmm_cause) cause_code;
775 if (strcmp(argv[0], "no-proxy") == 0)
776 g_hlr->no_proxy_reject_cause = (enum gsm48_gmm_cause) cause_code;
777
778 return CMD_SUCCESS;
779}
780
Oliver Smith851814a2019-01-11 15:30:21 +0100781DEFUN(cfg_store_imei, cfg_store_imei_cmd,
782 "store-imei",
783 "Save the IMEI in the database when receiving Check IMEI requests. Note that an MSC does not necessarily send"
784 " Check IMEI requests (for OsmoMSC, you may want to set 'check-imei-rqd 1').")
785{
786 g_hlr->store_imei = true;
787 return CMD_SUCCESS;
788}
789
790DEFUN(cfg_no_store_imei, cfg_no_store_imei_cmd,
791 "no store-imei",
792 "Do not save the IMEI in the database, when receiving Check IMEI requests.")
793{
794 g_hlr->store_imei = false;
795 return CMD_SUCCESS;
796}
797
Oliver Smithc7f17872019-03-04 15:10:44 +0100798DEFUN(cfg_subscr_create_on_demand, cfg_subscr_create_on_demand_cmd,
799 "subscriber-create-on-demand (no-msisdn|<3-15>) (none|cs|ps|cs+ps)",
800 "Make a new record when a subscriber is first seen.\n"
801 "Do not automatically assign MSISDN.\n"
802 "Length of an automatically assigned MSISDN.\n"
803 "Do not allow any NAM (Network Access Mode) by default.\n"
804 "Allow access to circuit switched NAM by default.\n"
805 "Allow access to packet switched NAM by default.\n"
806 "Allow access to circuit and packet switched NAM by default.\n")
807{
808 unsigned int rand_msisdn_len = 0;
809 uint8_t flags = 0x00;
810
811 if (strcmp(argv[0], "no-msisdn") != 0)
812 rand_msisdn_len = atoi(argv[0]);
813
814 if (strstr(argv[1], "cs"))
815 flags |= DB_SUBSCR_FLAG_NAM_CS;
816 if (strstr(argv[1], "ps"))
817 flags |= DB_SUBSCR_FLAG_NAM_PS;
818
819 g_hlr->subscr_create_on_demand = true;
820 g_hlr->subscr_create_on_demand_rand_msisdn_len = rand_msisdn_len;
821 g_hlr->subscr_create_on_demand_flags = flags;
822
823 return CMD_SUCCESS;
824}
825
826DEFUN(cfg_no_subscr_create_on_demand, cfg_no_subscr_create_on_demand_cmd,
827 "no subscriber-create-on-demand",
828 "Do not make a new record when a subscriber is first seen.\n")
829{
830 g_hlr->subscr_create_on_demand = false;
831 return CMD_SUCCESS;
832}
833
Harald Welte4956ae12018-06-15 22:04:28 +0200834/***********************************************************************
835 * Common Code
836 ***********************************************************************/
837
Pau Espin Pedrolce9bc402017-05-31 13:19:22 +0200838int hlr_vty_go_parent(struct vty *vty)
839{
840 switch (vty->node) {
841 case GSUP_NODE:
Harald Welte4956ae12018-06-15 22:04:28 +0200842 case EUSE_NODE:
Pau Espin Pedrolce9bc402017-05-31 13:19:22 +0200843 vty->node = HLR_NODE;
844 vty->index = NULL;
Harald Welte4956ae12018-06-15 22:04:28 +0200845 vty->index_sub = NULL;
Pau Espin Pedrolce9bc402017-05-31 13:19:22 +0200846 break;
847 default:
848 case HLR_NODE:
849 vty->node = CONFIG_NODE;
850 vty->index = NULL;
851 break;
852 case CONFIG_NODE:
853 vty->node = ENABLE_NODE;
854 vty->index = NULL;
855 break;
856 }
857
858 return vty->node;
859}
860
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100861int hlr_vty_is_config_node(struct vty *vty, int node)
862{
863 switch (node) {
864 /* add items that are not config */
865 case CONFIG_NODE:
866 return 0;
867
868 default:
869 return 1;
870 }
871}
872
Alexander Couzens268a33e2020-01-12 00:48:07 +0100873void hlr_vty_init(void *hlr_ctx)
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100874{
Alexander Couzens268a33e2020-01-12 00:48:07 +0100875 cfg_reject_cause_cmd.string =
876 vty_cmd_string_from_valstr(hlr_ctx,
877 gsm48_gmm_cause_vty_names,
878 "reject-cause (not-found|no-proxy) (", "|", ")",
879 VTY_DO_LOWER);
880
881 cfg_reject_cause_cmd.doc =
882 vty_cmd_string_from_valstr(hlr_ctx,
883 gsm48_gmm_cause_vty_descs,
884 "GSUP/GMM cause to be sent\n"
885 "in the case the IMSI could not be found in the database\n"
886 "in the case no remote HLR reponded to mslookup GSUP request\n",
887 "\n", "", 0);
888
Pau Espin Pedrole49391b2019-08-05 15:57:10 +0200889 logging_vty_add_cmds();
Harald Welte7ee6e552018-02-14 00:52:05 +0100890 osmo_talloc_vty_add_cmds();
Max20ddfdb2019-02-18 13:12:27 +0100891 osmo_stats_vty_add_cmds();
Pau Espin Pedrolce9bc402017-05-31 13:19:22 +0200892
Harald Weltefa7ee332018-06-24 13:20:32 +0200893 install_element_ve(&show_gsup_conn_cmd);
894
Pau Espin Pedrolce9bc402017-05-31 13:19:22 +0200895 install_element(CONFIG_NODE, &cfg_hlr_cmd);
896 install_node(&hlr_node, config_write_hlr);
Pau Espin Pedrolce9bc402017-05-31 13:19:22 +0200897
898 install_element(HLR_NODE, &cfg_gsup_cmd);
899 install_node(&gsup_node, config_write_hlr_gsup);
Pau Espin Pedrolce9bc402017-05-31 13:19:22 +0200900
901 install_element(GSUP_NODE, &cfg_hlr_gsup_bind_ip_cmd);
Neels Hofmeyr76328bd2019-11-20 03:35:37 +0100902 install_element(GSUP_NODE, &cfg_hlr_gsup_ipa_name_cmd);
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200903
Alexander Couzens37f0b3a2023-04-11 19:28:36 +0200904 /* PS */
905 install_node(&ps_node, config_write_hlr_ps);
906 install_element(HLR_NODE, &cfg_ps_cmd);
907
908 install_node(&ps_pdp_profiles_node, config_write_hlr_ps_pdp_profiles);
909 install_element(PS_NODE, &cfg_ps_pdp_profiles_cmd);
910 install_element(PS_NODE, &cfg_no_ps_pdp_profiles_cmd);
911
912 install_node(&ps_pdp_profiles_profile_node, NULL);
913 install_element(PS_PDP_PROFILES_NODE, &cfg_ps_pdp_profiles_profile_cmd);
914 install_element(PS_PDP_PROFILES_NODE, &cfg_no_ps_pdp_profiles_profile_cmd);
915 install_element(PS_PDP_PROFILES_PROFILE_NODE, &cfg_ps_pdp_profile_apn_cmd);
916 install_element(PS_PDP_PROFILES_PROFILE_NODE, &cfg_no_ps_pdp_profile_apn_cmd);
917
Neels Hofmeyr5857c592019-04-02 04:24:49 +0200918 install_element(HLR_NODE, &cfg_database_cmd);
919
Harald Welte4956ae12018-06-15 22:04:28 +0200920 install_element(HLR_NODE, &cfg_euse_cmd);
921 install_element(HLR_NODE, &cfg_no_euse_cmd);
922 install_node(&euse_node, config_write_euse);
Harald Weltedab544e2018-07-29 16:14:48 +0200923 install_element(HLR_NODE, &cfg_ussd_route_pfx_int_cmd);
924 install_element(HLR_NODE, &cfg_ussd_route_pfx_ext_cmd);
925 install_element(HLR_NODE, &cfg_ussd_no_route_pfx_cmd);
926 install_element(HLR_NODE, &cfg_ussd_defaultroute_cmd);
927 install_element(HLR_NODE, &cfg_ussd_no_defaultroute_cmd);
Vadim Yanitskiyd157a562018-12-01 00:03:39 +0700928 install_element(HLR_NODE, &cfg_ncss_guard_timeout_cmd);
Mychaela N. Falconiaff7c7ea2023-09-21 01:55:51 +0000929
930 install_node(&smsc_node, config_write_smsc);
931 install_element(HLR_NODE, &cfg_smsc_entity_cmd);
932 install_element(HLR_NODE, &cfg_no_smsc_entity_cmd);
933 install_element(HLR_NODE, &cfg_smsc_route_cmd);
934 install_element(HLR_NODE, &cfg_no_smsc_route_cmd);
935 install_element(HLR_NODE, &cfg_smsc_defroute_cmd);
936 install_element(HLR_NODE, &cfg_no_smsc_defroute_cmd);
937
Alexander Couzens268a33e2020-01-12 00:48:07 +0100938 install_element(HLR_NODE, &cfg_reject_cause_cmd);
Oliver Smith851814a2019-01-11 15:30:21 +0100939 install_element(HLR_NODE, &cfg_store_imei_cmd);
940 install_element(HLR_NODE, &cfg_no_store_imei_cmd);
Oliver Smithc7f17872019-03-04 15:10:44 +0100941 install_element(HLR_NODE, &cfg_subscr_create_on_demand_cmd);
942 install_element(HLR_NODE, &cfg_no_subscr_create_on_demand_cmd);
Harald Welte4956ae12018-06-15 22:04:28 +0200943
Harald Welted5807b82018-07-29 12:27:41 +0200944 hlr_vty_subscriber_init();
Neels Hofmeyr7685a782017-01-30 23:30:26 +0100945}