blob: c5435590065281d9fc86cdb445560d5e36a8c399 [file] [log] [blame]
Max372868b2017-03-02 12:12:00 +01001/* OsmoHLR Control Interface implementation */
2
3/* (C) 2017 sysmocom s.f.m.c. GmbH <info@sysmocom.de>
4 * All Rights Reserved
5 *
6 * Author: Max Suraev <msuraev@sysmocom.de>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU Affero General Public License as published by
10 * the Free Software Foundation; either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Affero General Public License for more details.
17 *
18 * You should have received a copy of the GNU Affero General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 *
21 */
22
23#include <stdbool.h>
Neels Hofmeyr446eb0f2017-10-17 01:58:24 +020024#include <errno.h>
25#include <inttypes.h>
26#include <string.h>
Max372868b2017-03-02 12:12:00 +010027
Neels Hofmeyr446eb0f2017-10-17 01:58:24 +020028#include <osmocom/gsm/gsm23003.h>
Max372868b2017-03-02 12:12:00 +010029#include <osmocom/ctrl/ports.h>
30
Neels Hofmeyr2f758032019-11-20 00:37:07 +010031#include <osmocom/hlr/hlr.h>
32#include <osmocom/hlr/ctrl.h>
33#include <osmocom/hlr/db.h>
Max372868b2017-03-02 12:12:00 +010034
Neels Hofmeyr446eb0f2017-10-17 01:58:24 +020035#define SEL_BY "by-"
36#define SEL_BY_IMSI SEL_BY "imsi-"
37#define SEL_BY_MSISDN SEL_BY "msisdn-"
38#define SEL_BY_ID SEL_BY "id-"
39
Pau Espin Pedrol1d0a0302022-06-20 16:48:45 +020040extern bool auth_algo_parse(const char *alg_str, enum osmo_auth_algo *algo,
41 int *minlen, int *maxlen);
42
Neels Hofmeyr446eb0f2017-10-17 01:58:24 +020043#define hexdump_buf(buf) osmo_hexdump_nospc((void*)buf, sizeof(buf))
44
45static bool startswith(const char *str, const char *start)
46{
47 return strncmp(str, start, strlen(start)) == 0;
48}
49
50static int _get_subscriber(struct db_context *dbc,
51 const char *by_selector,
52 struct hlr_subscriber *subscr)
53{
54 const char *val;
55 if (startswith(by_selector, SEL_BY_IMSI)) {
56 val = by_selector + strlen(SEL_BY_IMSI);
57 if (!osmo_imsi_str_valid(val))
58 return -EINVAL;
59 return db_subscr_get_by_imsi(dbc, val, subscr);
60 }
61 if (startswith(by_selector, SEL_BY_MSISDN)) {
62 val = by_selector + strlen(SEL_BY_MSISDN);
63 if (!osmo_msisdn_str_valid(val))
64 return -EINVAL;
65 return db_subscr_get_by_msisdn(dbc, val, subscr);
66 }
67 if (startswith(by_selector, SEL_BY_ID)) {
68 int64_t id;
69 char *endptr;
70 val = by_selector + strlen(SEL_BY_ID);
71 if (*val == '+')
72 return -EINVAL;
73 errno = 0;
74 id = strtoll(val, &endptr, 10);
75 if (errno || *endptr)
76 return -EINVAL;
77 return db_subscr_get_by_id(dbc, id, subscr);
78 }
79 return -ENOTSUP;
80}
81
82static bool get_subscriber(struct db_context *dbc,
83 const char *by_selector,
84 struct hlr_subscriber *subscr,
85 struct ctrl_cmd *cmd)
86{
87 int rc = _get_subscriber(dbc, by_selector, subscr);
88 switch (rc) {
89 case 0:
90 return true;
91 case -ENOTSUP:
92 cmd->reply = "Not a known subscriber 'by-xxx-' selector.";
93 return false;
94 case -EINVAL:
95 cmd->reply = "Invalid value part of 'by-xxx-value' selector.";
96 return false;
97 case -ENOENT:
98 cmd->reply = "No such subscriber.";
99 return false;
100 default:
Thorsten Alteholzb07f33d2019-07-16 21:21:36 +0200101 cmd->reply = "An unknown error has occurred during get_subscriber().";
Neels Hofmeyr446eb0f2017-10-17 01:58:24 +0200102 return false;
103 }
104}
105
106/* Optimization: if a subscriber operation is requested by-imsi, just return
107 * the IMSI right back. */
108static const char *get_subscriber_imsi(struct db_context *dbc,
109 const char *by_selector,
110 struct ctrl_cmd *cmd)
111{
112 static struct hlr_subscriber subscr;
113
114 if (startswith(by_selector, SEL_BY_IMSI))
115 return by_selector + strlen(SEL_BY_IMSI);
116 if (!get_subscriber(dbc, by_selector, &subscr, cmd))
117 return NULL;
118 return subscr.imsi;
119}
120
121/* printf fmt and arg to completely omit a string if it is empty. */
122#define FMT_S "%s%s%s%s"
123#define ARG_S(name, val) \
124 (val) && *(val) ? "\n" : "", \
125 (val) && *(val) ? name : "", \
126 (val) && *(val) ? "\t" : "", \
127 (val) && *(val) ? (val) : "" \
128
129/* printf fmt and arg to completely omit bool of given value. */
130#define FMT_BOOL "%s"
131#define ARG_BOOL(name, val) \
132 val ? "\n" name "\t1" : "\n" name "\t0"
133
134static void print_subscr_info(struct ctrl_cmd *cmd,
135 struct hlr_subscriber *subscr)
136{
137 ctrl_cmd_reply_printf(cmd,
Stefan Sperling705b61b2018-12-07 12:44:50 +0100138 "\nid\t%" PRIu64
Neels Hofmeyr446eb0f2017-10-17 01:58:24 +0200139 FMT_S
140 FMT_S
141 FMT_BOOL
142 FMT_BOOL
143 FMT_S
144 FMT_S
145 FMT_S
146 FMT_BOOL
147 FMT_BOOL
148 "\nperiodic_lu_timer\t%u"
149 "\nperiodic_rau_tau_timer\t%u"
150 "\nlmsi\t%08x"
151 ,
152 subscr->id,
153 ARG_S("imsi", subscr->imsi),
154 ARG_S("msisdn", subscr->msisdn),
155 ARG_BOOL("nam_cs", subscr->nam_cs),
156 ARG_BOOL("nam_ps", subscr->nam_ps),
157 ARG_S("vlr_number", subscr->vlr_number),
158 ARG_S("sgsn_number", subscr->sgsn_number),
159 ARG_S("sgsn_address", subscr->sgsn_address),
160 ARG_BOOL("ms_purged_cs", subscr->ms_purged_cs),
161 ARG_BOOL("ms_purged_ps", subscr->ms_purged_ps),
162 subscr->periodic_lu_timer,
163 subscr->periodic_rau_tau_timer,
164 subscr->lmsi
165 );
166}
167
168static void print_subscr_info_aud2g(struct ctrl_cmd *cmd, struct osmo_sub_auth_data *aud)
169{
170 if (aud->algo == OSMO_AUTH_ALG_NONE)
171 return;
172 ctrl_cmd_reply_printf(cmd,
173 "\naud2g.algo\t%s"
174 "\naud2g.ki\t%s"
175 ,
176 osmo_auth_alg_name(aud->algo),
177 hexdump_buf(aud->u.gsm.ki));
178}
179
180static void print_subscr_info_aud3g(struct ctrl_cmd *cmd, struct osmo_sub_auth_data *aud)
181{
182 if (aud->algo == OSMO_AUTH_ALG_NONE)
183 return;
184 ctrl_cmd_reply_printf(cmd,
185 "\naud3g.algo\t%s"
186 "\naud3g.k\t%s"
187 ,
188 osmo_auth_alg_name(aud->algo),
189 hexdump_buf(aud->u.umts.k));
190 /* hexdump uses a static string buffer, hence only one hexdump per
191 * printf(). */
192 ctrl_cmd_reply_printf(cmd,
193 "\naud3g.%s\t%s"
194 "\naud3g.ind_bitlen\t%u"
Stefan Sperling705b61b2018-12-07 12:44:50 +0100195 "\naud3g.sqn\t%" PRIu64
Neels Hofmeyr446eb0f2017-10-17 01:58:24 +0200196 ,
197 aud->u.umts.opc_is_op? "op" : "opc",
198 hexdump_buf(aud->u.umts.opc),
199 aud->u.umts.ind_bitlen,
200 aud->u.umts.sqn);
201}
202
Pau Espin Pedrold63ec882022-06-17 17:54:51 +0200203CTRL_CMD_DEFINE_WO_NOVRF(subscr_create, "create");
204static int set_subscr_create(struct ctrl_cmd *cmd, void *data)
205{
206 struct hlr_subscriber subscr;
207 struct hlr *hlr = data;
208 const char *imsi = cmd->value;
209 int rc;
210
211 if (!osmo_imsi_str_valid(imsi)) {
212 cmd->reply = "Invalid IMSI value.";
213 return CTRL_CMD_ERROR;
214 }
215
216 /* Create the subscriber in the DB */
217 rc = db_subscr_create(g_hlr->dbc, imsi, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS);
218 if (rc) {
219 if (rc == -EEXIST)
220 cmd->reply = "Subscriber already exists.";
221 else
222 cmd->reply = "Cannot create subscriber.";
223 return CTRL_CMD_ERROR;
224 }
225
226 LOGP(DCTRL, LOGL_INFO, "Created subscriber IMSI='%s'\n",
227 imsi);
228
229 /* Retrieve data of newly created subscriber: */
230 rc = db_subscr_get_by_imsi(hlr->dbc, imsi, &subscr);
231 if (rc < 0) {
232 cmd->reply = "Failed retrieving ID of newly created subscriber.";
233 return CTRL_CMD_ERROR;
234 }
235
236 cmd->reply = talloc_asprintf(cmd, "%" PRIu64, subscr.id);
237 return CTRL_CMD_REPLY;
238}
239
Pau Espin Pedrol3ca9a1f2022-06-20 15:38:21 +0200240CTRL_CMD_DEFINE_WO_NOVRF(subscr_delete, "delete");
241static int set_subscr_delete(struct ctrl_cmd *cmd, void *data)
242{
243 struct hlr_subscriber subscr;
244 struct hlr *hlr = data;
245 const char *imsi = cmd->value;
246 int rc;
247
248 if (!osmo_imsi_str_valid(imsi)) {
249 cmd->reply = "Invalid IMSI value.";
250 return CTRL_CMD_ERROR;
251 }
252
253 /* Retrieve data of newly created subscriber: */
254 rc = db_subscr_get_by_imsi(hlr->dbc, imsi, &subscr);
255 if (rc < 0) {
256 cmd->reply = "Subscriber doesn't exist.";
257 return CTRL_CMD_ERROR;
258 }
259
260 /* Create the subscriber in the DB */
261 rc = db_subscr_delete_by_id(g_hlr->dbc, subscr.id);
262 if (rc) {
263 cmd->reply = "Cannot delete subscriber.";
264 return CTRL_CMD_ERROR;
265 }
266
267 LOGP(DCTRL, LOGL_INFO, "Deleted subscriber IMSI='%s'\n",
268 imsi);
269
270 cmd->reply = talloc_asprintf(cmd, "%" PRIu64, subscr.id);
271 return CTRL_CMD_REPLY;
272}
273
Neels Hofmeyr446eb0f2017-10-17 01:58:24 +0200274CTRL_CMD_DEFINE_RO(subscr_info, "info");
275static int get_subscr_info(struct ctrl_cmd *cmd, void *data)
Max9cacb6f2017-02-20 17:22:56 +0100276{
Neels Hofmeyr00b1d432017-10-17 01:43:48 +0200277 struct hlr_subscriber subscr;
Neels Hofmeyr446eb0f2017-10-17 01:58:24 +0200278 struct hlr *hlr = data;
279 const char *by_selector = cmd->node;
Max9cacb6f2017-02-20 17:22:56 +0100280
Neels Hofmeyr446eb0f2017-10-17 01:58:24 +0200281 if (!get_subscriber(hlr->dbc, by_selector, &subscr, cmd))
282 return CTRL_CMD_ERROR;
283
284 print_subscr_info(cmd, &subscr);
285
286 return CTRL_CMD_REPLY;
287}
288
289CTRL_CMD_DEFINE_RO(subscr_info_aud, "info-aud");
290static int get_subscr_info_aud(struct ctrl_cmd *cmd, void *data)
291{
292 const char *imsi;
293 struct osmo_sub_auth_data aud2g;
294 struct osmo_sub_auth_data aud3g;
295 struct hlr *hlr = data;
296 const char *by_selector = cmd->node;
297 int rc;
298
299 imsi = get_subscriber_imsi(hlr->dbc, by_selector, cmd);
300 if (!imsi)
301 return CTRL_CMD_ERROR;
302
303 rc = db_get_auth_data(hlr->dbc, imsi, &aud2g, &aud3g, NULL);
304
Neels Hofmeyrbd1dca02017-11-23 15:25:30 +0100305 switch (rc) {
306 case 0:
307 break;
308 case -ENOENT:
309 case -ENOKEY:
Neels Hofmeyr446eb0f2017-10-17 01:58:24 +0200310 /* No auth data found, tell the print*() functions about it. */
311 aud2g.algo = OSMO_AUTH_ALG_NONE;
312 aud3g.algo = OSMO_AUTH_ALG_NONE;
Neels Hofmeyrbd1dca02017-11-23 15:25:30 +0100313 break;
314 default:
Neels Hofmeyr446eb0f2017-10-17 01:58:24 +0200315 cmd->reply = "Error retrieving authentication data.";
Max9cacb6f2017-02-20 17:22:56 +0100316 return CTRL_CMD_ERROR;
317 }
318
Neels Hofmeyr446eb0f2017-10-17 01:58:24 +0200319 print_subscr_info_aud2g(cmd, &aud2g);
320 print_subscr_info_aud3g(cmd, &aud3g);
321
322 return CTRL_CMD_REPLY;
323}
324
325CTRL_CMD_DEFINE_RO(subscr_info_all, "info-all");
326static int get_subscr_info_all(struct ctrl_cmd *cmd, void *data)
327{
328 struct hlr_subscriber subscr;
329 struct osmo_sub_auth_data aud2g;
330 struct osmo_sub_auth_data aud3g;
331 struct hlr *hlr = data;
332 const char *by_selector = cmd->node;
333 int rc;
334
335 if (!get_subscriber(hlr->dbc, by_selector, &subscr, cmd))
336 return CTRL_CMD_ERROR;
337
338 rc = db_get_auth_data(hlr->dbc, subscr.imsi, &aud2g, &aud3g, NULL);
339
Neels Hofmeyrbd1dca02017-11-23 15:25:30 +0100340 switch (rc) {
341 case 0:
342 break;
343 case -ENOENT:
344 case -ENOKEY:
Neels Hofmeyr446eb0f2017-10-17 01:58:24 +0200345 /* No auth data found, tell the print*() functions about it. */
346 aud2g.algo = OSMO_AUTH_ALG_NONE;
347 aud3g.algo = OSMO_AUTH_ALG_NONE;
Neels Hofmeyrbd1dca02017-11-23 15:25:30 +0100348 break;
349 default:
Neels Hofmeyr446eb0f2017-10-17 01:58:24 +0200350 cmd->reply = "Error retrieving authentication data.";
Max9cacb6f2017-02-20 17:22:56 +0100351 return CTRL_CMD_ERROR;
352 }
353
Neels Hofmeyr446eb0f2017-10-17 01:58:24 +0200354 print_subscr_info(cmd, &subscr);
355 print_subscr_info_aud2g(cmd, &aud2g);
356 print_subscr_info_aud3g(cmd, &aud3g);
357
358 return CTRL_CMD_REPLY;
359}
360
361static int verify_subscr_cs_ps_enabled(struct ctrl_cmd *cmd, const char *value, void *data)
362{
363 if (!value || !*value
364 || (strcmp(value, "0") && strcmp(value, "1")))
365 return 1;
366 return 0;
367}
368
369static int get_subscr_cs_ps_enabled(struct ctrl_cmd *cmd, void *data,
370 bool is_ps)
371{
372 struct hlr_subscriber subscr;
373 struct hlr *hlr = data;
374 const char *by_selector = cmd->node;
375
376 if (!get_subscriber(hlr->dbc, by_selector, &subscr, cmd))
377 return CTRL_CMD_ERROR;
378
379 cmd->reply = (is_ps ? subscr.nam_ps : subscr.nam_cs)
380 ? "1" : "0";
381 return CTRL_CMD_REPLY;
382}
383
384static int set_subscr_cs_ps_enabled(struct ctrl_cmd *cmd, void *data,
385 bool is_ps)
386{
387 const char *imsi;
388 struct hlr *hlr = data;
389 const char *by_selector = cmd->node;
390
391 imsi = get_subscriber_imsi(hlr->dbc, by_selector, cmd);
392 if (!imsi)
393 return CTRL_CMD_ERROR;
394 if (db_subscr_nam(hlr->dbc, imsi, strcmp(cmd->value, "1") == 0, is_ps))
395 return CTRL_CMD_ERROR;
Max9cacb6f2017-02-20 17:22:56 +0100396 cmd->reply = "OK";
Max9cacb6f2017-02-20 17:22:56 +0100397 return CTRL_CMD_REPLY;
398}
399
Neels Hofmeyr446eb0f2017-10-17 01:58:24 +0200400CTRL_CMD_DEFINE(subscr_ps_enabled, "ps-enabled");
401static int verify_subscr_ps_enabled(struct ctrl_cmd *cmd, const char *value, void *data)
Max9cacb6f2017-02-20 17:22:56 +0100402{
Neels Hofmeyr446eb0f2017-10-17 01:58:24 +0200403 return verify_subscr_cs_ps_enabled(cmd, value, data);
404}
405static int get_subscr_ps_enabled(struct ctrl_cmd *cmd, void *data)
406{
407 return get_subscr_cs_ps_enabled(cmd, data, true);
408}
409static int set_subscr_ps_enabled(struct ctrl_cmd *cmd, void *data)
410{
411 return set_subscr_cs_ps_enabled(cmd, data, true);
Max9cacb6f2017-02-20 17:22:56 +0100412}
413
Neels Hofmeyr446eb0f2017-10-17 01:58:24 +0200414CTRL_CMD_DEFINE(subscr_cs_enabled, "cs-enabled");
415static int verify_subscr_cs_enabled(struct ctrl_cmd *cmd, const char *value, void *data)
Max9cacb6f2017-02-20 17:22:56 +0100416{
Neels Hofmeyr446eb0f2017-10-17 01:58:24 +0200417 return verify_subscr_cs_ps_enabled(cmd, value, data);
Max9cacb6f2017-02-20 17:22:56 +0100418}
Neels Hofmeyr446eb0f2017-10-17 01:58:24 +0200419static int get_subscr_cs_enabled(struct ctrl_cmd *cmd, void *data)
Max372868b2017-03-02 12:12:00 +0100420{
Neels Hofmeyr446eb0f2017-10-17 01:58:24 +0200421 return get_subscr_cs_ps_enabled(cmd, data, false);
422}
423static int set_subscr_cs_enabled(struct ctrl_cmd *cmd, void *data)
424{
425 return set_subscr_cs_ps_enabled(cmd, data, false);
Max372868b2017-03-02 12:12:00 +0100426}
427
Pau Espin Pedrol140dffd2022-06-17 19:04:23 +0200428CTRL_CMD_DEFINE(subscr_msisdn, "msisdn");
429static int verify_subscr_msisdn(struct ctrl_cmd *cmd, const char *value, void *data)
430{
431 struct hlr_subscriber subscr;
432 if (!value)
433 return 1;
434 if (strlen(value) > sizeof(subscr.msisdn) - 1)
435 return 1;
436 if (strcmp(value, "none") != 0 && !osmo_msisdn_str_valid(value))
437 return 1;
438 return 0;
439}
440static int get_subscr_msisdn(struct ctrl_cmd *cmd, void *data)
441{
442 struct hlr_subscriber subscr;
443 struct hlr *hlr = data;
444 const char *by_selector = cmd->node;
445
446 if (!get_subscriber(hlr->dbc, by_selector, &subscr, cmd))
447 return CTRL_CMD_ERROR;
448
449 if (strlen(subscr.msisdn) == 0)
450 snprintf(subscr.msisdn, sizeof(subscr.msisdn), "none");
451
452 cmd->reply = talloc_asprintf(cmd, "%s", subscr.msisdn);
453 return CTRL_CMD_REPLY;
454}
455static int set_subscr_msisdn(struct ctrl_cmd *cmd, void *data)
456{
457 struct hlr_subscriber subscr;
458 struct hlr *hlr = data;
459 const char *by_selector = cmd->node;
460 const char *msisdn;
461
462 if (!get_subscriber(hlr->dbc, by_selector, &subscr, cmd))
463 return CTRL_CMD_ERROR;
464
465 if (strcmp(cmd->value, "none") == 0)
466 msisdn = NULL;
467 else
468 msisdn = cmd->value;
469
470 if (db_subscr_update_msisdn_by_imsi(g_hlr->dbc, subscr.imsi, msisdn)) {
471 cmd->reply = "Update MSISDN failed";
472 return CTRL_CMD_ERROR;
473 }
474
475 cmd->reply = "OK";
476 return CTRL_CMD_REPLY;
477}
478
Pau Espin Pedrol1d0a0302022-06-20 16:48:45 +0200479/* value format: <algo[,KI]> */
480CTRL_CMD_DEFINE(subscr_aud2g, "aud2g");
481static int verify_subscr_aud2g(struct ctrl_cmd *cmd, const char *value, void *data)
482{
483 if (!value)
484 return 1;
485 if (strcasecmp(value, "none") != 0 && !strchr(value, ','))
486 return 1;
487 return 0;
488}
489static int get_subscr_aud2g(struct ctrl_cmd *cmd, void *data)
490{
491 struct hlr_subscriber subscr;
492 struct hlr *hlr = data;
493 const char *by_selector = cmd->node;
494 struct osmo_sub_auth_data aud2g;
495 struct osmo_sub_auth_data aud3g_unused;
496 int rc;
497
498 if (!get_subscriber(hlr->dbc, by_selector, &subscr, cmd))
499 return CTRL_CMD_ERROR;
500
501 rc = db_get_auth_data(hlr->dbc, subscr.imsi, &aud2g, &aud3g_unused, NULL);
502 switch (rc) {
503 case 0:
504 break;
505 case -ENOENT:
506 case -ENOKEY:
507 aud2g.algo = OSMO_AUTH_ALG_NONE;
508 break;
509 default:
510 cmd->reply = "Error retrieving data from database.";
511 return CTRL_CMD_ERROR;
512 }
513
514 if (aud2g.algo == OSMO_AUTH_ALG_NONE) {
515 cmd->reply = "none";
516 return CTRL_CMD_REPLY;
517 }
518
519 cmd->reply = talloc_asprintf(cmd, "%s,%s", osmo_auth_alg_name(aud2g.algo),
520 hexdump_buf(aud2g.u.gsm.ki));
521 return CTRL_CMD_REPLY;
522}
523static int set_subscr_aud2g(struct ctrl_cmd *cmd, void *data)
524{
525 struct hlr_subscriber subscr;
526 struct hlr *hlr = data;
527 const char *by_selector = cmd->node;
528 char *tmp = NULL, *tok, *saveptr;
529 int minlen = 0;
530 int maxlen = 0;
531 struct sub_auth_data_str aud2g = {
532 .type = OSMO_AUTH_TYPE_GSM
533 };
534
535 if (!get_subscriber(hlr->dbc, by_selector, &subscr, cmd))
536 return CTRL_CMD_ERROR;
537
538 tmp = talloc_strdup(cmd, cmd->value);
539 if (!tmp) {
540 cmd->reply = "OOM";
541 return CTRL_CMD_ERROR;
542 }
543
544 /* Parse alg_type: */
545 tok = strtok_r(tmp, ",", &saveptr);
546 if (!tok) {
547 cmd->reply = "Invalid format";
548 return CTRL_CMD_ERROR;
549 }
550 if (strcmp(tok, "none") == 0) {
551 aud2g.algo = OSMO_AUTH_ALG_NONE;
552 } else if (!auth_algo_parse(tok, &aud2g.algo, &minlen, &maxlen)) {
553 cmd->reply = "Unknown auth algorithm.";
554 return CTRL_CMD_ERROR;
555 }
556
557 if (aud2g.algo != OSMO_AUTH_ALG_NONE) {
558 tok = strtok_r(NULL, "\0", &saveptr);
559 if (!tok) {
560 cmd->reply = "Invalid format.";
561 return CTRL_CMD_ERROR;
562 }
563 aud2g.u.gsm.ki = tok;
564 if (!osmo_is_hexstr(aud2g.u.gsm.ki, minlen * 2, maxlen * 2, true)) {
565 cmd->reply = "Invalid KI.";
566 return CTRL_CMD_ERROR;
567 }
568 }
569
570 if (db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud2g)) {
571 cmd->reply = "Update aud2g failed.";
572 return CTRL_CMD_ERROR;
573 }
574
575 cmd->reply = "OK";
576 return CTRL_CMD_REPLY;
577}
578
Neels Hofmeyr446eb0f2017-10-17 01:58:24 +0200579static int hlr_ctrl_node_lookup(void *data, vector vline, int *node_type,
580 void **node_data, int *i)
581{
582 const char *token = vector_slot(vline, *i);
583
584 switch (*node_type) {
585 case CTRL_NODE_ROOT:
586 if (strcmp(token, "subscriber") != 0)
587 return 0;
588 *node_data = NULL;
589 *node_type = CTRL_NODE_SUBSCR;
590 break;
591 case CTRL_NODE_SUBSCR:
592 if (!startswith(token, "by-"))
593 return 0;
594 *node_data = (void*)token;
595 *node_type = CTRL_NODE_SUBSCR_BY;
596 break;
597 default:
598 return 0;
599 }
600
601 return 1;
602}
603
Pau Espin Pedrolb4f25a02022-06-17 15:53:28 +0200604static int hlr_ctrl_cmds_install()
605{
606 int rc = 0;
607
Pau Espin Pedrold63ec882022-06-17 17:54:51 +0200608 rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR, &cmd_subscr_create);
Pau Espin Pedrol3ca9a1f2022-06-20 15:38:21 +0200609 rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR, &cmd_subscr_delete);
Pau Espin Pedrold63ec882022-06-17 17:54:51 +0200610
Pau Espin Pedrolb4f25a02022-06-17 15:53:28 +0200611 rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR_BY, &cmd_subscr_info);
612 rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR_BY, &cmd_subscr_info_aud);
613 rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR_BY, &cmd_subscr_info_all);
614 rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR_BY, &cmd_subscr_ps_enabled);
615 rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR_BY, &cmd_subscr_cs_enabled);
Pau Espin Pedrol140dffd2022-06-17 19:04:23 +0200616 rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR_BY, &cmd_subscr_msisdn);
Pau Espin Pedrol1d0a0302022-06-20 16:48:45 +0200617 rc |= ctrl_cmd_install(CTRL_NODE_SUBSCR_BY, &cmd_subscr_aud2g);
Pau Espin Pedrolb4f25a02022-06-17 15:53:28 +0200618
619 return rc;
620}
621
Neels Hofmeyr234f9cb2017-10-24 17:23:04 +0200622struct ctrl_handle *hlr_controlif_setup(struct hlr *hlr)
Max372868b2017-03-02 12:12:00 +0100623{
624 int rc;
Neels Hofmeyr234f9cb2017-10-24 17:23:04 +0200625 struct ctrl_handle *hdl = ctrl_interface_setup_dynip2(hlr,
626 hlr->ctrl_bind_addr,
627 OSMO_CTRL_PORT_HLR,
Neels Hofmeyr446eb0f2017-10-17 01:58:24 +0200628 hlr_ctrl_node_lookup,
629 _LAST_CTRL_NODE_HLR);
Max372868b2017-03-02 12:12:00 +0100630 if (!hdl)
631 return NULL;
632
633 rc = hlr_ctrl_cmds_install();
634 if (rc) /* FIXME: close control interface? */
635 return NULL;
636
637 return hdl;
638}