blob: ad16045c7dde274d631ba471ce61b3f738765571 [file] [log] [blame]
Neels Hofmeyr183e7002017-10-06 02:59:54 +02001/* OsmoHLR subscriber management VTY implementation */
2/* (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
3 * All Rights Reserved
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU Affero General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include <stdlib.h>
20#include <inttypes.h>
21#include <string.h>
22#include <errno.h>
Stefan Sperling5c14c9c2018-12-07 12:30:21 +010023#include <sys/types.h>
24#include <time.h>
Neels Hofmeyr183e7002017-10-06 02:59:54 +020025
26#include <osmocom/gsm/gsm23003.h>
27#include <osmocom/vty/vty.h>
28#include <osmocom/vty/command.h>
29#include <osmocom/core/utils.h>
30
Neels Hofmeyr2f758032019-11-20 00:37:07 +010031#include <osmocom/hlr/hlr.h>
32#include <osmocom/hlr/db.h>
Oliver Smithedc27ef2019-11-25 13:28:23 +010033#include <osmocom/hlr/timestamp.h>
Neels Hofmeyr183e7002017-10-06 02:59:54 +020034
35struct vty;
36
37#define hexdump_buf(buf) osmo_hexdump_nospc((void*)buf, sizeof(buf))
38
Neels Hofmeyrd9b36062018-12-25 17:23:41 +010039static char *get_datestr(const time_t *t, char *buf, size_t bufsize)
Stefan Sperling5c14c9c2018-12-07 12:30:21 +010040{
Neels Hofmeyrd9b36062018-12-25 17:23:41 +010041 struct tm tm;
42 gmtime_r(t, &tm);
43 strftime(buf, bufsize, "%FT%T+00:00", &tm);
44 return buf;
Stefan Sperling5c14c9c2018-12-07 12:30:21 +010045}
46
Keith89fda302021-01-19 07:01:33 +010047static void dump_last_lu_seen(struct vty *vty, const char *domain_label, time_t last_lu_seen, bool only_age)
Neels Hofmeyr07e16022019-11-20 02:36:35 +010048{
Oliver Smithedc27ef2019-11-25 13:28:23 +010049 uint32_t age;
Neels Hofmeyrd9b36062018-12-25 17:23:41 +010050 char datebuf[32];
Neels Hofmeyr07e16022019-11-20 02:36:35 +010051 if (!last_lu_seen)
52 return;
Keith89fda302021-01-19 07:01:33 +010053 if (!only_age)
54 vty_out(vty, " last LU seen on %s: %s", domain_label, get_datestr(&last_lu_seen, datebuf, sizeof(datebuf)));
Oliver Smithedc27ef2019-11-25 13:28:23 +010055 if (!timestamp_age(&last_lu_seen, &age))
56 vty_out(vty, " (invalid timestamp)%s", VTY_NEWLINE);
Neels Hofmeyr26b49052019-11-27 16:47:50 +010057 else {
58 vty_out(vty, " (");
59#define UNIT_AGO(UNITNAME, UNITVAL) \
60 if (age >= (UNITVAL)) { \
61 vty_out(vty, "%u%s", age / (UNITVAL), UNITNAME); \
62 age = age % (UNITVAL); \
63 }
64 UNIT_AGO("d", 60*60*24);
65 UNIT_AGO("h", 60*60);
66 UNIT_AGO("m", 60);
67 UNIT_AGO("s", 1);
Keith89fda302021-01-19 07:01:33 +010068 if (!only_age)
69 vty_out(vty, " ago)%s", VTY_NEWLINE);
70 else
71 vty_out(vty, " ago)");
Neels Hofmeyr26b49052019-11-27 16:47:50 +010072#undef UNIT_AGO
73 }
Neels Hofmeyr07e16022019-11-20 02:36:35 +010074}
75
Neels Hofmeyr183e7002017-10-06 02:59:54 +020076static void subscr_dump_full_vty(struct vty *vty, struct hlr_subscriber *subscr)
77{
78 int rc;
79 struct osmo_sub_auth_data aud2g;
80 struct osmo_sub_auth_data aud3g;
81
82 vty_out(vty, " ID: %"PRIu64"%s", subscr->id, VTY_NEWLINE);
83
Neels Hofmeyr36bec872017-10-23 18:44:23 +020084 vty_out(vty, " IMSI: %s%s", *subscr->imsi ? subscr->imsi : "none", VTY_NEWLINE);
Neels Hofmeyr183e7002017-10-06 02:59:54 +020085 vty_out(vty, " MSISDN: %s%s", *subscr->msisdn ? subscr->msisdn : "none", VTY_NEWLINE);
Oliver Smith02078b72019-01-11 15:41:29 +010086
87 if (*subscr->imei) {
88 char checksum = osmo_luhn(subscr->imei, 14);
89 if (checksum == -EINVAL)
90 vty_out(vty, " IMEI: %s (INVALID LENGTH!)%s", subscr->imei, VTY_NEWLINE);
91 else
92 vty_out(vty, " IMEI: %s%c%s", subscr->imei, checksum, VTY_NEWLINE);
93 }
94
Neels Hofmeyr183e7002017-10-06 02:59:54 +020095 if (*subscr->vlr_number)
96 vty_out(vty, " VLR number: %s%s", subscr->vlr_number, VTY_NEWLINE);
97 if (*subscr->sgsn_number)
98 vty_out(vty, " SGSN number: %s%s", subscr->sgsn_number, VTY_NEWLINE);
99 if (*subscr->sgsn_address)
100 vty_out(vty, " SGSN address: %s%s", subscr->sgsn_address, VTY_NEWLINE);
101 if (subscr->periodic_lu_timer)
102 vty_out(vty, " Periodic LU timer: %u%s", subscr->periodic_lu_timer, VTY_NEWLINE);
103 if (subscr->periodic_rau_tau_timer)
104 vty_out(vty, " Periodic RAU/TAU timer: %u%s", subscr->periodic_rau_tau_timer, VTY_NEWLINE);
105 if (subscr->lmsi)
106 vty_out(vty, " LMSI: %x%s", subscr->lmsi, VTY_NEWLINE);
107 if (!subscr->nam_cs)
108 vty_out(vty, " CS disabled%s", VTY_NEWLINE);
109 if (subscr->ms_purged_cs)
110 vty_out(vty, " CS purged%s", VTY_NEWLINE);
111 if (!subscr->nam_ps)
112 vty_out(vty, " PS disabled%s", VTY_NEWLINE);
113 if (subscr->ms_purged_ps)
114 vty_out(vty, " PS purged%s", VTY_NEWLINE);
Keith89fda302021-01-19 07:01:33 +0100115 dump_last_lu_seen(vty, "CS", subscr->last_lu_seen, false);
116 dump_last_lu_seen(vty, "PS", subscr->last_lu_seen_ps, false);
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200117
118 if (!*subscr->imsi)
119 return;
120
121 OSMO_ASSERT(g_hlr);
122 rc = db_get_auth_data(g_hlr->dbc, subscr->imsi, &aud2g, &aud3g, NULL);
123
Neels Hofmeyrbd1dca02017-11-23 15:25:30 +0100124 switch (rc) {
125 case 0:
126 break;
127 case -ENOENT:
128 case -ENOKEY:
129 aud2g.algo = OSMO_AUTH_ALG_NONE;
130 aud3g.algo = OSMO_AUTH_ALG_NONE;
131 break;
132 default:
133 vty_out(vty, "%% Error retrieving data from database (%d)%s", rc, VTY_NEWLINE);
134 return;
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200135 }
136
137 if (aud2g.type != OSMO_AUTH_TYPE_NONE && aud2g.type != OSMO_AUTH_TYPE_GSM) {
138 vty_out(vty, "%% Error: 2G auth data is not of type 'GSM'%s", VTY_NEWLINE);
139 aud2g = (struct osmo_sub_auth_data){};
140 }
141
142 if (aud3g.type != OSMO_AUTH_TYPE_NONE && aud3g.type != OSMO_AUTH_TYPE_UMTS) {
143 vty_out(vty, "%% Error: 3G auth data is not of type 'UMTS'%s", VTY_NEWLINE);
144 aud3g = (struct osmo_sub_auth_data){};
145 }
146
147 if (aud2g.algo != OSMO_AUTH_ALG_NONE && aud2g.type != OSMO_AUTH_TYPE_NONE) {
148 vty_out(vty, " 2G auth: %s%s",
149 osmo_auth_alg_name(aud2g.algo), VTY_NEWLINE);
150 vty_out(vty, " KI=%s%s",
151 hexdump_buf(aud2g.u.gsm.ki), VTY_NEWLINE);
152 }
153
154 if (aud3g.algo != OSMO_AUTH_ALG_NONE && aud3g.type != OSMO_AUTH_TYPE_NONE) {
155 vty_out(vty, " 3G auth: %s%s", osmo_auth_alg_name(aud3g.algo), VTY_NEWLINE);
156 vty_out(vty, " K=%s%s", hexdump_buf(aud3g.u.umts.k), VTY_NEWLINE);
157 vty_out(vty, " %s=%s%s", aud3g.u.umts.opc_is_op? "OP" : "OPC",
158 hexdump_buf(aud3g.u.umts.opc), VTY_NEWLINE);
159 vty_out(vty, " IND-bitlen=%u", aud3g.u.umts.ind_bitlen);
160 if (aud3g.u.umts.sqn)
161 vty_out(vty, " last-SQN=%"PRIu64, aud3g.u.umts.sqn);
162 vty_out(vty, VTY_NEWLINE);
163 }
164}
165
Keith89fda302021-01-19 07:01:33 +0100166static void subscr_dump_summary_vty(struct hlr_subscriber *subscr, void *data)
167{
168 struct vty *vty = data;
169 vty_out(vty, "%-5"PRIu64" %-12s %-16s", subscr->id,
170 *subscr->msisdn ? subscr->msisdn : "none",
171 *subscr->imsi ? subscr->imsi : "none");
172
173 if (*subscr->imei) {
174 char checksum = osmo_luhn(subscr->imei, 14);
175 if (checksum == -EINVAL)
176 vty_out(vty, " %-14s (INVALID LENGTH!)", subscr->imei);
177 else
178 vty_out(vty, " %-14s%c", subscr->imei, checksum);
179 } else {
180 vty_out(vty," ------------- ");
181 }
182 vty_out(vty, " %-2s%-2s ", subscr->nam_cs ? "CS" : "", subscr->nam_ps ? "PS" : "");
183 if (subscr->last_lu_seen)
184 dump_last_lu_seen(vty, "CS", subscr->last_lu_seen, true);
185 vty_out_newline(vty);
186}
187
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200188static int get_subscr_by_argv(struct vty *vty, const char *type, const char *id, struct hlr_subscriber *subscr)
189{
Oliver Smith02078b72019-01-11 15:41:29 +0100190 char imei_buf[GSM23003_IMEI_NUM_DIGITS_NO_CHK+1];
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200191 int rc = -1;
192 if (strcmp(type, "imsi") == 0)
193 rc = db_subscr_get_by_imsi(g_hlr->dbc, id, subscr);
194 else if (strcmp(type, "msisdn") == 0)
195 rc = db_subscr_get_by_msisdn(g_hlr->dbc, id, subscr);
196 else if (strcmp(type, "id") == 0)
197 rc = db_subscr_get_by_id(g_hlr->dbc, atoll(id), subscr);
Oliver Smith02078b72019-01-11 15:41:29 +0100198 else if (strcmp(type, "imei") == 0) {
199 /* Verify IMEI with checksum digit */
200 if (osmo_imei_str_valid(id, true)) {
201 /* Cut the checksum off */
202 osmo_strlcpy(imei_buf, id, sizeof(imei_buf));
203 id = imei_buf;
204 vty_out(vty, "%% Checksum validated and stripped for search: imei = '%s'%s", id,
205 VTY_NEWLINE);
206 }
207 rc = db_subscr_get_by_imei(g_hlr->dbc, id, subscr);
208 }
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200209 if (rc)
210 vty_out(vty, "%% No subscriber for %s = '%s'%s",
211 type, id, VTY_NEWLINE);
212 return rc;
213}
214
Keith89fda302021-01-19 07:01:33 +0100215static void dump_summary_table_vty(struct vty *vty, bool header, bool show_ls)
216{
217 const char *texts = "ID MSISDN IMSI IMEI NAM";
218 const char *lines = "----- ------------ ---------------- ---------------- -----";
219 const char *ls_text = " LAST SEEN";
220 const char *ls_line = " ------------";
221 if (header) {
222 if (!show_ls)
223 vty_out(vty, "%s%s%s%s", texts, VTY_NEWLINE, lines, VTY_NEWLINE);
224 else
225 vty_out(vty, "%s%s%s%s%s%s", texts, ls_text, VTY_NEWLINE, lines, ls_line, VTY_NEWLINE);
226 } else {
227 if (!show_ls)
228 vty_out(vty, "%s%s%s%s", lines, VTY_NEWLINE, texts, VTY_NEWLINE);
229 else
230 vty_out(vty, "%s%s%s%s%s%s", lines, ls_line, VTY_NEWLINE, texts, ls_text, VTY_NEWLINE);
231 }
232}
233
234static int get_subscrs(struct vty *vty, const char *filter_type, const char *filter)
235{
236 int rc = -1;
237 int count = 0;
238 const char *err;
239 bool show_ls = (filter_type && strcmp(filter_type, "last_lu_seen") == 0);
240 dump_summary_table_vty(vty, true, show_ls);
241 rc = db_subscrs_get(g_hlr->dbc, filter_type, filter, subscr_dump_summary_vty, vty, &count, &err);
242 if (count > 40) {
243 dump_summary_table_vty(vty, false, show_ls);
244 }
245 if (count > 0)
246 vty_out(vty, " Subscribers Shown: %d%s", count, VTY_NEWLINE);
247 if (rc)
248 vty_out(vty, "%% %s%s", err, VTY_NEWLINE);
249 return rc;
250}
251
252
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200253#define SUBSCR_CMD "subscriber "
254#define SUBSCR_CMD_HELP "Subscriber management commands\n"
Keithcc90bfd2021-01-19 06:55:22 +0100255#define SUBSCR_SHOW_HELP "Show subscriber information\n"
Keith89fda302021-01-19 07:01:33 +0100256#define SUBSCRS_SHOW_HELP "Show all subscribers (with filter possibility)\n"
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200257
Oliver Smith02078b72019-01-11 15:41:29 +0100258#define SUBSCR_ID "(imsi|msisdn|id|imei) IDENT"
Keith89fda302021-01-19 07:01:33 +0100259#define SUBSCR_FILTER "(imsi|msisdn) FILTER"
260
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200261#define SUBSCR_ID_HELP \
262 "Identify subscriber by IMSI\n" \
263 "Identify subscriber by MSISDN (phone number)\n" \
264 "Identify subscriber by database ID\n" \
Oliver Smith02078b72019-01-11 15:41:29 +0100265 "Identify subscriber by IMEI\n" \
266 "IMSI/MSISDN/ID/IMEI of the subscriber\n"
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200267
Neels Hofmeyr8aa780b2018-12-02 18:52:49 +0100268#define SUBSCR SUBSCR_CMD SUBSCR_ID " "
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200269#define SUBSCR_HELP SUBSCR_CMD_HELP SUBSCR_ID_HELP
270
271#define SUBSCR_UPDATE SUBSCR "update "
272#define SUBSCR_UPDATE_HELP SUBSCR_HELP "Set or update subscriber data\n"
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100273#define SUBSCR_MSISDN_HELP "Set MSISDN (phone number) of the subscriber\n"
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200274
275DEFUN(subscriber_show,
276 subscriber_show_cmd,
277 SUBSCR "show",
Keithcc90bfd2021-01-19 06:55:22 +0100278 SUBSCR_HELP SUBSCR_SHOW_HELP)
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200279{
280 struct hlr_subscriber subscr;
281 const char *id_type = argv[0];
282 const char *id = argv[1];
283
284 if (get_subscr_by_argv(vty, id_type, id, &subscr))
285 return CMD_WARNING;
286
287 subscr_dump_full_vty(vty, &subscr);
288 return CMD_SUCCESS;
289}
290
Neels Hofmeyr8aa780b2018-12-02 18:52:49 +0100291ALIAS(subscriber_show, show_subscriber_cmd,
292 "show " SUBSCR_CMD SUBSCR_ID,
Keithcc90bfd2021-01-19 06:55:22 +0100293 SHOW_STR SUBSCR_SHOW_HELP SUBSCR_ID_HELP);
Neels Hofmeyr8aa780b2018-12-02 18:52:49 +0100294
Keith89fda302021-01-19 07:01:33 +0100295DEFUN(show_subscriber_all,
296 show_subscriber_all_cmd,
297 "show subscribers all",
298 SHOW_STR SUBSCRS_SHOW_HELP "Show summary of all subscribers\n")
299{
300 if (get_subscrs(vty, NULL, NULL))
301 return CMD_WARNING;
302
303 return CMD_SUCCESS;
304}
305
306DEFUN(show_subscriber_filtered,
307 show_subscriber_filtered_cmd,
308 "show subscribers " SUBSCR_FILTER,
309 SHOW_STR SUBSCRS_SHOW_HELP
310 "Filter Subscribers by IMSI\n" "Filter Subscribers by MSISDN\n" "String to match in msisdn or imsi\n")
311{
312 const char *filter_type = argv[0];
313 const char *filter = argv[1];
314
315 if (get_subscrs(vty, filter_type, filter))
316 return CMD_WARNING;
317
318 return CMD_SUCCESS;
319}
320
321ALIAS(show_subscriber_filtered, show_subscriber_filtered_cmd2,
322 "show subscribers (cs|ps) (on|off)",
323 SHOW_STR SUBSCR_SHOW_HELP
324 "Filter Subscribers by CS Network Access Mode\n" "Filter Subscribers by PS Network Access Mode\n"
325 "Authorised\n" "Not Authorised\n");
326
327DEFUN(show_subscriber_order_last_seen, show_subscriber_order_last_seen_cmd,
328 "show subscribers last-seen",
329 SHOW_STR SUBSCR_SHOW_HELP "Show Subscribers Ordered by Last Seen Time\n")
330{
331 if (get_subscrs(vty, "last_lu_seen", NULL))
332 return CMD_WARNING;
333
334 return CMD_SUCCESS;
335}
336
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200337DEFUN(subscriber_create,
338 subscriber_create_cmd,
339 SUBSCR_CMD "imsi IDENT create",
340 SUBSCR_CMD_HELP
Vadim Yanitskiyf473c7b2018-07-30 14:29:39 +0700341 "Identify subscriber by IMSI\n"
342 "IMSI/MSISDN/ID of the subscriber\n"
343 "Create subscriber by IMSI\n")
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200344{
345 int rc;
346 struct hlr_subscriber subscr;
347 const char *imsi = argv[0];
348
349 if (!osmo_imsi_str_valid(imsi)) {
350 vty_out(vty, "%% Not a valid IMSI: %s%s", imsi, VTY_NEWLINE);
351 return CMD_WARNING;
352 }
353
Oliver Smithcd2af5e2019-03-06 13:17:39 +0100354 rc = db_subscr_create(g_hlr->dbc, imsi, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS);
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200355
356 if (rc) {
357 if (rc == -EEXIST)
358 vty_out(vty, "%% Subscriber already exists for IMSI = %s%s",
359 imsi, VTY_NEWLINE);
360 else
361 vty_out(vty, "%% Error (rc=%d): cannot create subscriber for IMSI = %s%s",
362 rc, imsi, VTY_NEWLINE);
363 return CMD_WARNING;
364 }
365
366 rc = db_subscr_get_by_imsi(g_hlr->dbc, imsi, &subscr);
367 vty_out(vty, "%% Created subscriber %s%s", imsi, VTY_NEWLINE);
368
369 subscr_dump_full_vty(vty, &subscr);
370
371 return CMD_SUCCESS;
372}
373
374DEFUN(subscriber_delete,
375 subscriber_delete_cmd,
376 SUBSCR "delete",
377 SUBSCR_HELP "Delete subscriber from database\n")
378{
379 struct hlr_subscriber subscr;
380 int rc;
381 const char *id_type = argv[0];
382 const char *id = argv[1];
383
384 /* Find out the IMSI regardless of which way the caller decided to
385 * identify the subscriber by. */
386 if (get_subscr_by_argv(vty, id_type, id, &subscr))
387 return CMD_WARNING;
388
389 rc = db_subscr_delete_by_id(g_hlr->dbc, subscr.id);
390 if (rc) {
391 vty_out(vty, "%% Error: Failed to remove subscriber for IMSI '%s'%s",
392 subscr.imsi, VTY_NEWLINE);
393 return CMD_WARNING;
394 }
395
396 vty_out(vty, "%% Deleted subscriber for IMSI '%s'%s", subscr.imsi, VTY_NEWLINE);
397 return CMD_SUCCESS;
398}
399
400DEFUN(subscriber_msisdn,
401 subscriber_msisdn_cmd,
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100402 SUBSCR_UPDATE "msisdn (none|MSISDN)",
403 SUBSCR_UPDATE_HELP SUBSCR_MSISDN_HELP
404 "Remove MSISDN (phone number)\n"
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200405 "New MSISDN (phone number)\n")
406{
407 struct hlr_subscriber subscr;
408 const char *id_type = argv[0];
409 const char *id = argv[1];
410 const char *msisdn = argv[2];
411
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100412 if (strcmp(msisdn, "none") == 0)
413 msisdn = NULL;
414 else {
415 if (strlen(msisdn) > sizeof(subscr.msisdn) - 1) {
416 vty_out(vty, "%% MSISDN is too long, max. %zu characters are allowed%s",
417 sizeof(subscr.msisdn)-1, VTY_NEWLINE);
418 return CMD_WARNING;
419 }
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200420
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100421 if (!osmo_msisdn_str_valid(msisdn)) {
422 vty_out(vty, "%% MSISDN invalid: '%s'%s", msisdn, VTY_NEWLINE);
423 return CMD_WARNING;
424 }
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200425 }
426
427 if (get_subscr_by_argv(vty, id_type, id, &subscr))
428 return CMD_WARNING;
429
430 if (db_subscr_update_msisdn_by_imsi(g_hlr->dbc, subscr.imsi, msisdn)) {
431 vty_out(vty, "%% Error: cannot update MSISDN for subscriber IMSI='%s'%s",
432 subscr.imsi, VTY_NEWLINE);
433 return CMD_WARNING;
434 }
435
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100436 if (msisdn) {
437 vty_out(vty, "%% Updated subscriber IMSI='%s' to MSISDN='%s'%s",
438 subscr.imsi, msisdn, VTY_NEWLINE);
Stefan Sperlingf1622522018-04-09 11:39:16 +0200439
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100440 if (db_subscr_get_by_msisdn(g_hlr->dbc, msisdn, &subscr) == 0)
441 osmo_hlr_subscriber_update_notify(&subscr);
442 } else {
443 vty_out(vty, "%% Updated subscriber IMSI='%s': removed MSISDN%s",
444 subscr.imsi, VTY_NEWLINE);
445
Stefan Sperlingf1622522018-04-09 11:39:16 +0200446 osmo_hlr_subscriber_update_notify(&subscr);
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100447 }
Stefan Sperlingf1622522018-04-09 11:39:16 +0200448
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200449 return CMD_SUCCESS;
450}
451
452static bool is_hexkey_valid(struct vty *vty, const char *label,
453 const char *hex_str, int minlen, int maxlen)
454{
455 if (osmo_is_hexstr(hex_str, minlen * 2, maxlen * 2, true))
456 return true;
457 vty_out(vty, "%% Invalid value for %s: '%s'%s", label, hex_str, VTY_NEWLINE);
458 return false;
459}
460
461#define AUTH_ALG_TYPES_2G "(comp128v1|comp128v2|comp128v3|xor)"
462#define AUTH_ALG_TYPES_2G_HELP \
463 "Use COMP128v1 algorithm\n" \
464 "Use COMP128v2 algorithm\n" \
465 "Use COMP128v3 algorithm\n" \
466 "Use XOR algorithm\n"
467
468#define AUTH_ALG_TYPES_3G "milenage"
469#define AUTH_ALG_TYPES_3G_HELP \
470 "Use Milenage algorithm\n"
471
472#define A38_XOR_MIN_KEY_LEN 12
473#define A38_XOR_MAX_KEY_LEN 16
474#define A38_COMP128_KEY_LEN 16
475
476#define MILENAGE_KEY_LEN 16
477
478static bool auth_algo_parse(const char *alg_str, enum osmo_auth_algo *algo,
479 int *minlen, int *maxlen)
480{
481 if (!strcasecmp(alg_str, "none")) {
482 *algo = OSMO_AUTH_ALG_NONE;
483 *minlen = *maxlen = 0;
484 } else if (!strcasecmp(alg_str, "comp128v1")) {
485 *algo = OSMO_AUTH_ALG_COMP128v1;
486 *minlen = *maxlen = A38_COMP128_KEY_LEN;
487 } else if (!strcasecmp(alg_str, "comp128v2")) {
488 *algo = OSMO_AUTH_ALG_COMP128v2;
489 *minlen = *maxlen = A38_COMP128_KEY_LEN;
490 } else if (!strcasecmp(alg_str, "comp128v3")) {
491 *algo = OSMO_AUTH_ALG_COMP128v3;
492 *minlen = *maxlen = A38_COMP128_KEY_LEN;
493 } else if (!strcasecmp(alg_str, "xor")) {
494 *algo = OSMO_AUTH_ALG_XOR;
495 *minlen = A38_XOR_MIN_KEY_LEN;
496 *maxlen = A38_XOR_MAX_KEY_LEN;
497 } else if (!strcasecmp(alg_str, "milenage")) {
498 *algo = OSMO_AUTH_ALG_MILENAGE;
499 *minlen = *maxlen = MILENAGE_KEY_LEN;
500 } else
501 return false;
502 return true;
503}
504
505DEFUN(subscriber_no_aud2g,
506 subscriber_no_aud2g_cmd,
507 SUBSCR_UPDATE "aud2g none",
508 SUBSCR_UPDATE_HELP
509 "Set 2G authentication data\n"
510 "Delete 2G authentication data\n")
511{
512 struct hlr_subscriber subscr;
513 int rc;
514 const char *id_type = argv[0];
515 const char *id = argv[1];
516 struct sub_auth_data_str aud = {
517 .type = OSMO_AUTH_TYPE_GSM,
518 .algo = OSMO_AUTH_ALG_NONE,
519 };
520
521 if (get_subscr_by_argv(vty, id_type, id, &subscr))
522 return CMD_WARNING;
523
524 rc = db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud);
525
Harald Welte880a34d2018-03-01 21:32:01 +0100526 if (rc && rc != -ENOENT) {
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200527 vty_out(vty, "%% Error: cannot disable 2G auth data for IMSI='%s'%s",
528 subscr.imsi, VTY_NEWLINE);
529 return CMD_WARNING;
530 }
531 return CMD_SUCCESS;
532}
533
534DEFUN(subscriber_aud2g,
535 subscriber_aud2g_cmd,
536 SUBSCR_UPDATE "aud2g " AUTH_ALG_TYPES_2G " ki KI",
537 SUBSCR_UPDATE_HELP
538 "Set 2G authentication data\n"
539 AUTH_ALG_TYPES_2G_HELP
540 "Set Ki Encryption Key\n" "Ki as 32 hexadecimal characters\n")
541{
542 struct hlr_subscriber subscr;
543 int rc;
544 int minlen = 0;
545 int maxlen = 0;
546 const char *id_type = argv[0];
547 const char *id = argv[1];
548 const char *alg_type = argv[2];
549 const char *ki = argv[3];
550 struct sub_auth_data_str aud2g = {
551 .type = OSMO_AUTH_TYPE_GSM,
552 .u.gsm.ki = ki,
553 };
554
555 if (!auth_algo_parse(alg_type, &aud2g.algo, &minlen, &maxlen)) {
556 vty_out(vty, "%% Unknown auth algorithm: '%s'%s", alg_type, VTY_NEWLINE);
557 return CMD_WARNING;
558 }
559
560 if (!is_hexkey_valid(vty, "KI", aud2g.u.gsm.ki, minlen, maxlen))
561 return CMD_WARNING;
562
563 if (get_subscr_by_argv(vty, id_type, id, &subscr))
564 return CMD_WARNING;
565
566 rc = db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud2g);
567
568 if (rc) {
569 vty_out(vty, "%% Error: cannot set 2G auth data for IMSI='%s'%s",
570 subscr.imsi, VTY_NEWLINE);
571 return CMD_WARNING;
572 }
573 return CMD_SUCCESS;
574}
575
576DEFUN(subscriber_no_aud3g,
577 subscriber_no_aud3g_cmd,
578 SUBSCR_UPDATE "aud3g none",
579 SUBSCR_UPDATE_HELP
580 "Set UMTS authentication data (3G, and 2G with UMTS AKA)\n"
581 "Delete 3G authentication data\n")
582{
583 struct hlr_subscriber subscr;
584 int rc;
585 const char *id_type = argv[0];
586 const char *id = argv[1];
587 struct sub_auth_data_str aud = {
588 .type = OSMO_AUTH_TYPE_UMTS,
589 .algo = OSMO_AUTH_ALG_NONE,
590 };
591
592 if (get_subscr_by_argv(vty, id_type, id, &subscr))
593 return CMD_WARNING;
594
595 rc = db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud);
596
Harald Welte880a34d2018-03-01 21:32:01 +0100597 if (rc && rc != -ENOENT) {
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200598 vty_out(vty, "%% Error: cannot disable 3G auth data for IMSI='%s'%s",
599 subscr.imsi, VTY_NEWLINE);
600 return CMD_WARNING;
601 }
602 return CMD_SUCCESS;
603}
604
605DEFUN(subscriber_aud3g,
606 subscriber_aud3g_cmd,
607 SUBSCR_UPDATE "aud3g " AUTH_ALG_TYPES_3G
608 " k K"
609 " (op|opc) OP_C"
610 " [ind-bitlen] [<0-28>]",
611 SUBSCR_UPDATE_HELP
612 "Set UMTS authentication data (3G, and 2G with UMTS AKA)\n"
613 AUTH_ALG_TYPES_3G_HELP
614 "Set Encryption Key K\n" "K as 32 hexadecimal characters\n"
615 "Set OP key\n" "Set OPC key\n" "OP or OPC as 32 hexadecimal characters\n"
616 "Set IND bit length\n" "IND bit length value (default: 5)\n")
617{
618 struct hlr_subscriber subscr;
619 int minlen = 0;
620 int maxlen = 0;
621 int rc;
622 const char *id_type = argv[0];
623 const char *id = argv[1];
624 const char *alg_type = AUTH_ALG_TYPES_3G;
625 const char *k = argv[2];
626 bool opc_is_op = (strcasecmp("op", argv[3]) == 0);
627 const char *op_opc = argv[4];
628 int ind_bitlen = argc > 6? atoi(argv[6]) : 5;
629 struct sub_auth_data_str aud3g = {
630 .type = OSMO_AUTH_TYPE_UMTS,
631 .u.umts = {
632 .k = k,
633 .opc_is_op = opc_is_op,
634 .opc = op_opc,
635 .ind_bitlen = ind_bitlen,
636 },
637 };
638
639 if (!auth_algo_parse(alg_type, &aud3g.algo, &minlen, &maxlen)) {
640 vty_out(vty, "%% Unknown auth algorithm: '%s'%s", alg_type, VTY_NEWLINE);
641 return CMD_WARNING;
642 }
643
644 if (!is_hexkey_valid(vty, "K", aud3g.u.umts.k, minlen, maxlen))
645 return CMD_WARNING;
646
647 if (!is_hexkey_valid(vty, opc_is_op ? "OP" : "OPC", aud3g.u.umts.opc,
648 MILENAGE_KEY_LEN, MILENAGE_KEY_LEN))
649 return CMD_WARNING;
650
651 if (get_subscr_by_argv(vty, id_type, id, &subscr))
652 return CMD_WARNING;
653
654 rc = db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud3g);
655
656 if (rc) {
657 vty_out(vty, "%% Error: cannot set 3G auth data for IMSI='%s'%s",
658 subscr.imsi, VTY_NEWLINE);
659 return CMD_WARNING;
660 }
661 return CMD_SUCCESS;
662}
663
Harald Welte6e237d32020-12-28 01:01:31 +0100664DEFUN(subscriber_aud3g_xor,
665 subscriber_aud3g_xor_cmd,
666 SUBSCR_UPDATE "aud3g xor k K"
667 " [ind-bitlen] [<0-28>]",
668 SUBSCR_UPDATE_HELP
669 "Set UMTS authentication data (3G, and 2G with UMTS AKA)\n"
670 "Use XOR algorithm\n"
671 "Set Encryption Key K\n" "K as 32 hexadecimal characters\n"
672 "Set IND bit length\n" "IND bit length value (default: 5)\n")
673{
674 struct hlr_subscriber subscr;
675 int minlen = 0;
676 int maxlen = 0;
677 int rc;
678 const char *id_type = argv[0];
679 const char *id = argv[1];
680 const char *k = argv[2];
681 int ind_bitlen = argc > 4? atoi(argv[4]) : 5;
682 struct sub_auth_data_str aud3g = {
683 .type = OSMO_AUTH_TYPE_UMTS,
684 .u.umts = {
685 .k = k,
686 .opc_is_op = 0,
687 .opc = "00000000000000000000000000000000",
688 .ind_bitlen = ind_bitlen,
689 },
690 };
691
692 if (!auth_algo_parse("xor", &aud3g.algo, &minlen, &maxlen)) {
693 vty_out(vty, "%% Unknown auth algorithm: '%s'%s", "xor", VTY_NEWLINE);
694 return CMD_WARNING;
695 }
696
697 if (!is_hexkey_valid(vty, "K", aud3g.u.umts.k, minlen, maxlen))
698 return CMD_WARNING;
699
700 if (get_subscr_by_argv(vty, id_type, id, &subscr))
701 return CMD_WARNING;
702
703 rc = db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud3g);
704
705 if (rc) {
706 vty_out(vty, "%% Error: cannot set 3G auth data for IMSI='%s'%s",
707 subscr.imsi, VTY_NEWLINE);
708 return CMD_WARNING;
709 }
710 return CMD_SUCCESS;
711}
712
Oliver Smith02078b72019-01-11 15:41:29 +0100713DEFUN(subscriber_imei,
714 subscriber_imei_cmd,
715 SUBSCR_UPDATE "imei (none|IMEI)",
716 SUBSCR_UPDATE_HELP
717 "Set IMEI of the subscriber (normally populated from MSC, no need to set this manually)\n"
718 "Forget IMEI\n"
719 "Set IMEI (use for debug only!)\n")
720{
721 struct hlr_subscriber subscr;
722 const char *id_type = argv[0];
723 const char *id = argv[1];
724 const char *imei = argv[2];
725 char imei_buf[GSM23003_IMEI_NUM_DIGITS_NO_CHK+1];
726
727 if (strcmp(imei, "none") == 0)
728 imei = NULL;
729 else {
730 /* Verify IMEI with checksum digit */
731 if (osmo_imei_str_valid(imei, true)) {
732 /* Cut the checksum off */
733 osmo_strlcpy(imei_buf, imei, sizeof(imei_buf));
734 imei = imei_buf;
735 } else if (!osmo_imei_str_valid(imei, false)) {
736 vty_out(vty, "%% IMEI invalid: '%s'%s", imei, VTY_NEWLINE);
737 return CMD_WARNING;
738 }
739 }
740
741 if (get_subscr_by_argv(vty, id_type, id, &subscr))
742 return CMD_WARNING;
743
744 if (db_subscr_update_imei_by_imsi(g_hlr->dbc, subscr.imsi, imei)) {
745 vty_out(vty, "%% Error: cannot update IMEI for subscriber IMSI='%s'%s",
746 subscr.imsi, VTY_NEWLINE);
747 return CMD_WARNING;
748 }
749
750 if (imei)
751 vty_out(vty, "%% Updated subscriber IMSI='%s' to IMEI='%s'%s",
752 subscr.imsi, imei, VTY_NEWLINE);
753 else
754 vty_out(vty, "%% Updated subscriber IMSI='%s': removed IMEI%s",
755 subscr.imsi, VTY_NEWLINE);
756
757 return CMD_SUCCESS;
758}
759
Oliver Smith3b33b012019-07-15 10:35:22 +0200760DEFUN(subscriber_nam,
761 subscriber_nam_cmd,
762 SUBSCR_UPDATE "network-access-mode (none|cs|ps|cs+ps)",
763 SUBSCR_UPDATE_HELP
764 "Set Network Access Mode (NAM) of the subscriber\n"
765 "Do not allow access to circuit switched or packet switched services\n"
766 "Allow access to circuit switched services only\n"
767 "Allow access to packet switched services only\n"
768 "Allow access to both circuit and packet switched services\n")
769{
770 struct hlr_subscriber subscr;
771 const char *id_type = argv[0];
772 const char *id = argv[1];
773 bool nam_cs = strstr(argv[2], "cs");
774 bool nam_ps = strstr(argv[2], "ps");
775
776 if (get_subscr_by_argv(vty, id_type, id, &subscr))
777 return CMD_WARNING;
778
779 if (nam_cs != subscr.nam_cs)
780 hlr_subscr_nam(g_hlr, &subscr, nam_cs, 0);
781 if (nam_ps != subscr.nam_ps)
782 hlr_subscr_nam(g_hlr, &subscr, nam_ps, 1);
783
784 return CMD_SUCCESS;
785}
786
Oliver Smith02078b72019-01-11 15:41:29 +0100787
Harald Welted5807b82018-07-29 12:27:41 +0200788void hlr_vty_subscriber_init(void)
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200789{
Keith89fda302021-01-19 07:01:33 +0100790 install_element_ve(&show_subscriber_all_cmd);
791 install_element_ve(&show_subscriber_filtered_cmd);
792 install_element_ve(&show_subscriber_filtered_cmd2);
793 install_element_ve(&show_subscriber_order_last_seen_cmd);
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200794 install_element_ve(&subscriber_show_cmd);
Neels Hofmeyr8aa780b2018-12-02 18:52:49 +0100795 install_element_ve(&show_subscriber_cmd);
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200796 install_element(ENABLE_NODE, &subscriber_create_cmd);
797 install_element(ENABLE_NODE, &subscriber_delete_cmd);
798 install_element(ENABLE_NODE, &subscriber_msisdn_cmd);
799 install_element(ENABLE_NODE, &subscriber_no_aud2g_cmd);
800 install_element(ENABLE_NODE, &subscriber_aud2g_cmd);
801 install_element(ENABLE_NODE, &subscriber_no_aud3g_cmd);
802 install_element(ENABLE_NODE, &subscriber_aud3g_cmd);
Harald Welte6e237d32020-12-28 01:01:31 +0100803 install_element(ENABLE_NODE, &subscriber_aud3g_xor_cmd);
Oliver Smith02078b72019-01-11 15:41:29 +0100804 install_element(ENABLE_NODE, &subscriber_imei_cmd);
Oliver Smith3b33b012019-07-15 10:35:22 +0200805 install_element(ENABLE_NODE, &subscriber_nam_cmd);
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200806}