blob: bc6f6a5f8b776cebd4b08e78884d542eeb033235 [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>
23
24#include <osmocom/gsm/gsm23003.h>
25#include <osmocom/vty/vty.h>
26#include <osmocom/vty/command.h>
27#include <osmocom/core/utils.h>
28
29#include "hlr.h"
30#include "db.h"
31
32struct vty;
33
34#define hexdump_buf(buf) osmo_hexdump_nospc((void*)buf, sizeof(buf))
35
Neels Hofmeyr183e7002017-10-06 02:59:54 +020036static void subscr_dump_full_vty(struct vty *vty, struct hlr_subscriber *subscr)
37{
38 int rc;
39 struct osmo_sub_auth_data aud2g;
40 struct osmo_sub_auth_data aud3g;
41
42 vty_out(vty, " ID: %"PRIu64"%s", subscr->id, VTY_NEWLINE);
43
Neels Hofmeyr36bec872017-10-23 18:44:23 +020044 vty_out(vty, " IMSI: %s%s", *subscr->imsi ? subscr->imsi : "none", VTY_NEWLINE);
Neels Hofmeyr183e7002017-10-06 02:59:54 +020045 vty_out(vty, " MSISDN: %s%s", *subscr->msisdn ? subscr->msisdn : "none", VTY_NEWLINE);
46 if (*subscr->vlr_number)
47 vty_out(vty, " VLR number: %s%s", subscr->vlr_number, VTY_NEWLINE);
48 if (*subscr->sgsn_number)
49 vty_out(vty, " SGSN number: %s%s", subscr->sgsn_number, VTY_NEWLINE);
50 if (*subscr->sgsn_address)
51 vty_out(vty, " SGSN address: %s%s", subscr->sgsn_address, VTY_NEWLINE);
52 if (subscr->periodic_lu_timer)
53 vty_out(vty, " Periodic LU timer: %u%s", subscr->periodic_lu_timer, VTY_NEWLINE);
54 if (subscr->periodic_rau_tau_timer)
55 vty_out(vty, " Periodic RAU/TAU timer: %u%s", subscr->periodic_rau_tau_timer, VTY_NEWLINE);
56 if (subscr->lmsi)
57 vty_out(vty, " LMSI: %x%s", subscr->lmsi, VTY_NEWLINE);
58 if (!subscr->nam_cs)
59 vty_out(vty, " CS disabled%s", VTY_NEWLINE);
60 if (subscr->ms_purged_cs)
61 vty_out(vty, " CS purged%s", VTY_NEWLINE);
62 if (!subscr->nam_ps)
63 vty_out(vty, " PS disabled%s", VTY_NEWLINE);
64 if (subscr->ms_purged_ps)
65 vty_out(vty, " PS purged%s", VTY_NEWLINE);
66
67 if (!*subscr->imsi)
68 return;
69
70 OSMO_ASSERT(g_hlr);
71 rc = db_get_auth_data(g_hlr->dbc, subscr->imsi, &aud2g, &aud3g, NULL);
72
Neels Hofmeyrbd1dca02017-11-23 15:25:30 +010073 switch (rc) {
74 case 0:
75 break;
76 case -ENOENT:
77 case -ENOKEY:
78 aud2g.algo = OSMO_AUTH_ALG_NONE;
79 aud3g.algo = OSMO_AUTH_ALG_NONE;
80 break;
81 default:
82 vty_out(vty, "%% Error retrieving data from database (%d)%s", rc, VTY_NEWLINE);
83 return;
Neels Hofmeyr183e7002017-10-06 02:59:54 +020084 }
85
86 if (aud2g.type != OSMO_AUTH_TYPE_NONE && aud2g.type != OSMO_AUTH_TYPE_GSM) {
87 vty_out(vty, "%% Error: 2G auth data is not of type 'GSM'%s", VTY_NEWLINE);
88 aud2g = (struct osmo_sub_auth_data){};
89 }
90
91 if (aud3g.type != OSMO_AUTH_TYPE_NONE && aud3g.type != OSMO_AUTH_TYPE_UMTS) {
92 vty_out(vty, "%% Error: 3G auth data is not of type 'UMTS'%s", VTY_NEWLINE);
93 aud3g = (struct osmo_sub_auth_data){};
94 }
95
96 if (aud2g.algo != OSMO_AUTH_ALG_NONE && aud2g.type != OSMO_AUTH_TYPE_NONE) {
97 vty_out(vty, " 2G auth: %s%s",
98 osmo_auth_alg_name(aud2g.algo), VTY_NEWLINE);
99 vty_out(vty, " KI=%s%s",
100 hexdump_buf(aud2g.u.gsm.ki), VTY_NEWLINE);
101 }
102
103 if (aud3g.algo != OSMO_AUTH_ALG_NONE && aud3g.type != OSMO_AUTH_TYPE_NONE) {
104 vty_out(vty, " 3G auth: %s%s", osmo_auth_alg_name(aud3g.algo), VTY_NEWLINE);
105 vty_out(vty, " K=%s%s", hexdump_buf(aud3g.u.umts.k), VTY_NEWLINE);
106 vty_out(vty, " %s=%s%s", aud3g.u.umts.opc_is_op? "OP" : "OPC",
107 hexdump_buf(aud3g.u.umts.opc), VTY_NEWLINE);
108 vty_out(vty, " IND-bitlen=%u", aud3g.u.umts.ind_bitlen);
109 if (aud3g.u.umts.sqn)
110 vty_out(vty, " last-SQN=%"PRIu64, aud3g.u.umts.sqn);
111 vty_out(vty, VTY_NEWLINE);
112 }
113}
114
115static int get_subscr_by_argv(struct vty *vty, const char *type, const char *id, struct hlr_subscriber *subscr)
116{
117 int rc = -1;
118 if (strcmp(type, "imsi") == 0)
119 rc = db_subscr_get_by_imsi(g_hlr->dbc, id, subscr);
120 else if (strcmp(type, "msisdn") == 0)
121 rc = db_subscr_get_by_msisdn(g_hlr->dbc, id, subscr);
122 else if (strcmp(type, "id") == 0)
123 rc = db_subscr_get_by_id(g_hlr->dbc, atoll(id), subscr);
124 if (rc)
125 vty_out(vty, "%% No subscriber for %s = '%s'%s",
126 type, id, VTY_NEWLINE);
127 return rc;
128}
129
130#define SUBSCR_CMD "subscriber "
131#define SUBSCR_CMD_HELP "Subscriber management commands\n"
132
Neels Hofmeyr8aa780b2018-12-02 18:52:49 +0100133#define SUBSCR_ID "(imsi|msisdn|id) IDENT"
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200134#define SUBSCR_ID_HELP \
135 "Identify subscriber by IMSI\n" \
136 "Identify subscriber by MSISDN (phone number)\n" \
137 "Identify subscriber by database ID\n" \
138 "IMSI/MSISDN/ID of the subscriber\n"
139
Neels Hofmeyr8aa780b2018-12-02 18:52:49 +0100140#define SUBSCR SUBSCR_CMD SUBSCR_ID " "
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200141#define SUBSCR_HELP SUBSCR_CMD_HELP SUBSCR_ID_HELP
142
143#define SUBSCR_UPDATE SUBSCR "update "
144#define SUBSCR_UPDATE_HELP SUBSCR_HELP "Set or update subscriber data\n"
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100145#define SUBSCR_MSISDN_HELP "Set MSISDN (phone number) of the subscriber\n"
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200146
147DEFUN(subscriber_show,
148 subscriber_show_cmd,
149 SUBSCR "show",
150 SUBSCR_HELP "Show subscriber information\n")
151{
152 struct hlr_subscriber subscr;
153 const char *id_type = argv[0];
154 const char *id = argv[1];
155
156 if (get_subscr_by_argv(vty, id_type, id, &subscr))
157 return CMD_WARNING;
158
159 subscr_dump_full_vty(vty, &subscr);
160 return CMD_SUCCESS;
161}
162
Neels Hofmeyr8aa780b2018-12-02 18:52:49 +0100163ALIAS(subscriber_show, show_subscriber_cmd,
164 "show " SUBSCR_CMD SUBSCR_ID,
165 SHOW_STR SUBSCR_CMD_HELP SUBSCR_ID_HELP);
166
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200167DEFUN(subscriber_create,
168 subscriber_create_cmd,
169 SUBSCR_CMD "imsi IDENT create",
170 SUBSCR_CMD_HELP
Vadim Yanitskiyf473c7b2018-07-30 14:29:39 +0700171 "Identify subscriber by IMSI\n"
172 "IMSI/MSISDN/ID of the subscriber\n"
173 "Create subscriber by IMSI\n")
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200174{
175 int rc;
176 struct hlr_subscriber subscr;
177 const char *imsi = argv[0];
178
179 if (!osmo_imsi_str_valid(imsi)) {
180 vty_out(vty, "%% Not a valid IMSI: %s%s", imsi, VTY_NEWLINE);
181 return CMD_WARNING;
182 }
183
184 rc = db_subscr_create(g_hlr->dbc, imsi);
185
186 if (rc) {
187 if (rc == -EEXIST)
188 vty_out(vty, "%% Subscriber already exists for IMSI = %s%s",
189 imsi, VTY_NEWLINE);
190 else
191 vty_out(vty, "%% Error (rc=%d): cannot create subscriber for IMSI = %s%s",
192 rc, imsi, VTY_NEWLINE);
193 return CMD_WARNING;
194 }
195
196 rc = db_subscr_get_by_imsi(g_hlr->dbc, imsi, &subscr);
197 vty_out(vty, "%% Created subscriber %s%s", imsi, VTY_NEWLINE);
198
199 subscr_dump_full_vty(vty, &subscr);
200
201 return CMD_SUCCESS;
202}
203
204DEFUN(subscriber_delete,
205 subscriber_delete_cmd,
206 SUBSCR "delete",
207 SUBSCR_HELP "Delete subscriber from database\n")
208{
209 struct hlr_subscriber subscr;
210 int rc;
211 const char *id_type = argv[0];
212 const char *id = argv[1];
213
214 /* Find out the IMSI regardless of which way the caller decided to
215 * identify the subscriber by. */
216 if (get_subscr_by_argv(vty, id_type, id, &subscr))
217 return CMD_WARNING;
218
219 rc = db_subscr_delete_by_id(g_hlr->dbc, subscr.id);
220 if (rc) {
221 vty_out(vty, "%% Error: Failed to remove subscriber for IMSI '%s'%s",
222 subscr.imsi, VTY_NEWLINE);
223 return CMD_WARNING;
224 }
225
226 vty_out(vty, "%% Deleted subscriber for IMSI '%s'%s", subscr.imsi, VTY_NEWLINE);
227 return CMD_SUCCESS;
228}
229
230DEFUN(subscriber_msisdn,
231 subscriber_msisdn_cmd,
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100232 SUBSCR_UPDATE "msisdn (none|MSISDN)",
233 SUBSCR_UPDATE_HELP SUBSCR_MSISDN_HELP
234 "Remove MSISDN (phone number)\n"
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200235 "New MSISDN (phone number)\n")
236{
237 struct hlr_subscriber subscr;
238 const char *id_type = argv[0];
239 const char *id = argv[1];
240 const char *msisdn = argv[2];
241
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100242 if (strcmp(msisdn, "none") == 0)
243 msisdn = NULL;
244 else {
245 if (strlen(msisdn) > sizeof(subscr.msisdn) - 1) {
246 vty_out(vty, "%% MSISDN is too long, max. %zu characters are allowed%s",
247 sizeof(subscr.msisdn)-1, VTY_NEWLINE);
248 return CMD_WARNING;
249 }
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200250
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100251 if (!osmo_msisdn_str_valid(msisdn)) {
252 vty_out(vty, "%% MSISDN invalid: '%s'%s", msisdn, VTY_NEWLINE);
253 return CMD_WARNING;
254 }
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200255 }
256
257 if (get_subscr_by_argv(vty, id_type, id, &subscr))
258 return CMD_WARNING;
259
260 if (db_subscr_update_msisdn_by_imsi(g_hlr->dbc, subscr.imsi, msisdn)) {
261 vty_out(vty, "%% Error: cannot update MSISDN for subscriber IMSI='%s'%s",
262 subscr.imsi, VTY_NEWLINE);
263 return CMD_WARNING;
264 }
265
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100266 if (msisdn) {
267 vty_out(vty, "%% Updated subscriber IMSI='%s' to MSISDN='%s'%s",
268 subscr.imsi, msisdn, VTY_NEWLINE);
Stefan Sperlingf1622522018-04-09 11:39:16 +0200269
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100270 if (db_subscr_get_by_msisdn(g_hlr->dbc, msisdn, &subscr) == 0)
271 osmo_hlr_subscriber_update_notify(&subscr);
272 } else {
273 vty_out(vty, "%% Updated subscriber IMSI='%s': removed MSISDN%s",
274 subscr.imsi, VTY_NEWLINE);
275
Stefan Sperlingf1622522018-04-09 11:39:16 +0200276 osmo_hlr_subscriber_update_notify(&subscr);
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100277 }
Stefan Sperlingf1622522018-04-09 11:39:16 +0200278
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200279 return CMD_SUCCESS;
280}
281
282static bool is_hexkey_valid(struct vty *vty, const char *label,
283 const char *hex_str, int minlen, int maxlen)
284{
285 if (osmo_is_hexstr(hex_str, minlen * 2, maxlen * 2, true))
286 return true;
287 vty_out(vty, "%% Invalid value for %s: '%s'%s", label, hex_str, VTY_NEWLINE);
288 return false;
289}
290
291#define AUTH_ALG_TYPES_2G "(comp128v1|comp128v2|comp128v3|xor)"
292#define AUTH_ALG_TYPES_2G_HELP \
293 "Use COMP128v1 algorithm\n" \
294 "Use COMP128v2 algorithm\n" \
295 "Use COMP128v3 algorithm\n" \
296 "Use XOR algorithm\n"
297
298#define AUTH_ALG_TYPES_3G "milenage"
299#define AUTH_ALG_TYPES_3G_HELP \
300 "Use Milenage algorithm\n"
301
302#define A38_XOR_MIN_KEY_LEN 12
303#define A38_XOR_MAX_KEY_LEN 16
304#define A38_COMP128_KEY_LEN 16
305
306#define MILENAGE_KEY_LEN 16
307
308static bool auth_algo_parse(const char *alg_str, enum osmo_auth_algo *algo,
309 int *minlen, int *maxlen)
310{
311 if (!strcasecmp(alg_str, "none")) {
312 *algo = OSMO_AUTH_ALG_NONE;
313 *minlen = *maxlen = 0;
314 } else if (!strcasecmp(alg_str, "comp128v1")) {
315 *algo = OSMO_AUTH_ALG_COMP128v1;
316 *minlen = *maxlen = A38_COMP128_KEY_LEN;
317 } else if (!strcasecmp(alg_str, "comp128v2")) {
318 *algo = OSMO_AUTH_ALG_COMP128v2;
319 *minlen = *maxlen = A38_COMP128_KEY_LEN;
320 } else if (!strcasecmp(alg_str, "comp128v3")) {
321 *algo = OSMO_AUTH_ALG_COMP128v3;
322 *minlen = *maxlen = A38_COMP128_KEY_LEN;
323 } else if (!strcasecmp(alg_str, "xor")) {
324 *algo = OSMO_AUTH_ALG_XOR;
325 *minlen = A38_XOR_MIN_KEY_LEN;
326 *maxlen = A38_XOR_MAX_KEY_LEN;
327 } else if (!strcasecmp(alg_str, "milenage")) {
328 *algo = OSMO_AUTH_ALG_MILENAGE;
329 *minlen = *maxlen = MILENAGE_KEY_LEN;
330 } else
331 return false;
332 return true;
333}
334
335DEFUN(subscriber_no_aud2g,
336 subscriber_no_aud2g_cmd,
337 SUBSCR_UPDATE "aud2g none",
338 SUBSCR_UPDATE_HELP
339 "Set 2G authentication data\n"
340 "Delete 2G authentication data\n")
341{
342 struct hlr_subscriber subscr;
343 int rc;
344 const char *id_type = argv[0];
345 const char *id = argv[1];
346 struct sub_auth_data_str aud = {
347 .type = OSMO_AUTH_TYPE_GSM,
348 .algo = OSMO_AUTH_ALG_NONE,
349 };
350
351 if (get_subscr_by_argv(vty, id_type, id, &subscr))
352 return CMD_WARNING;
353
354 rc = db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud);
355
Harald Welte880a34d2018-03-01 21:32:01 +0100356 if (rc && rc != -ENOENT) {
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200357 vty_out(vty, "%% Error: cannot disable 2G auth data for IMSI='%s'%s",
358 subscr.imsi, VTY_NEWLINE);
359 return CMD_WARNING;
360 }
361 return CMD_SUCCESS;
362}
363
364DEFUN(subscriber_aud2g,
365 subscriber_aud2g_cmd,
366 SUBSCR_UPDATE "aud2g " AUTH_ALG_TYPES_2G " ki KI",
367 SUBSCR_UPDATE_HELP
368 "Set 2G authentication data\n"
369 AUTH_ALG_TYPES_2G_HELP
370 "Set Ki Encryption Key\n" "Ki as 32 hexadecimal characters\n")
371{
372 struct hlr_subscriber subscr;
373 int rc;
374 int minlen = 0;
375 int maxlen = 0;
376 const char *id_type = argv[0];
377 const char *id = argv[1];
378 const char *alg_type = argv[2];
379 const char *ki = argv[3];
380 struct sub_auth_data_str aud2g = {
381 .type = OSMO_AUTH_TYPE_GSM,
382 .u.gsm.ki = ki,
383 };
384
385 if (!auth_algo_parse(alg_type, &aud2g.algo, &minlen, &maxlen)) {
386 vty_out(vty, "%% Unknown auth algorithm: '%s'%s", alg_type, VTY_NEWLINE);
387 return CMD_WARNING;
388 }
389
390 if (!is_hexkey_valid(vty, "KI", aud2g.u.gsm.ki, minlen, maxlen))
391 return CMD_WARNING;
392
393 if (get_subscr_by_argv(vty, id_type, id, &subscr))
394 return CMD_WARNING;
395
396 rc = db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud2g);
397
398 if (rc) {
399 vty_out(vty, "%% Error: cannot set 2G auth data for IMSI='%s'%s",
400 subscr.imsi, VTY_NEWLINE);
401 return CMD_WARNING;
402 }
403 return CMD_SUCCESS;
404}
405
406DEFUN(subscriber_no_aud3g,
407 subscriber_no_aud3g_cmd,
408 SUBSCR_UPDATE "aud3g none",
409 SUBSCR_UPDATE_HELP
410 "Set UMTS authentication data (3G, and 2G with UMTS AKA)\n"
411 "Delete 3G authentication data\n")
412{
413 struct hlr_subscriber subscr;
414 int rc;
415 const char *id_type = argv[0];
416 const char *id = argv[1];
417 struct sub_auth_data_str aud = {
418 .type = OSMO_AUTH_TYPE_UMTS,
419 .algo = OSMO_AUTH_ALG_NONE,
420 };
421
422 if (get_subscr_by_argv(vty, id_type, id, &subscr))
423 return CMD_WARNING;
424
425 rc = db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud);
426
Harald Welte880a34d2018-03-01 21:32:01 +0100427 if (rc && rc != -ENOENT) {
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200428 vty_out(vty, "%% Error: cannot disable 3G auth data for IMSI='%s'%s",
429 subscr.imsi, VTY_NEWLINE);
430 return CMD_WARNING;
431 }
432 return CMD_SUCCESS;
433}
434
435DEFUN(subscriber_aud3g,
436 subscriber_aud3g_cmd,
437 SUBSCR_UPDATE "aud3g " AUTH_ALG_TYPES_3G
438 " k K"
439 " (op|opc) OP_C"
440 " [ind-bitlen] [<0-28>]",
441 SUBSCR_UPDATE_HELP
442 "Set UMTS authentication data (3G, and 2G with UMTS AKA)\n"
443 AUTH_ALG_TYPES_3G_HELP
444 "Set Encryption Key K\n" "K as 32 hexadecimal characters\n"
445 "Set OP key\n" "Set OPC key\n" "OP or OPC as 32 hexadecimal characters\n"
446 "Set IND bit length\n" "IND bit length value (default: 5)\n")
447{
448 struct hlr_subscriber subscr;
449 int minlen = 0;
450 int maxlen = 0;
451 int rc;
452 const char *id_type = argv[0];
453 const char *id = argv[1];
454 const char *alg_type = AUTH_ALG_TYPES_3G;
455 const char *k = argv[2];
456 bool opc_is_op = (strcasecmp("op", argv[3]) == 0);
457 const char *op_opc = argv[4];
458 int ind_bitlen = argc > 6? atoi(argv[6]) : 5;
459 struct sub_auth_data_str aud3g = {
460 .type = OSMO_AUTH_TYPE_UMTS,
461 .u.umts = {
462 .k = k,
463 .opc_is_op = opc_is_op,
464 .opc = op_opc,
465 .ind_bitlen = ind_bitlen,
466 },
467 };
468
469 if (!auth_algo_parse(alg_type, &aud3g.algo, &minlen, &maxlen)) {
470 vty_out(vty, "%% Unknown auth algorithm: '%s'%s", alg_type, VTY_NEWLINE);
471 return CMD_WARNING;
472 }
473
474 if (!is_hexkey_valid(vty, "K", aud3g.u.umts.k, minlen, maxlen))
475 return CMD_WARNING;
476
477 if (!is_hexkey_valid(vty, opc_is_op ? "OP" : "OPC", aud3g.u.umts.opc,
478 MILENAGE_KEY_LEN, MILENAGE_KEY_LEN))
479 return CMD_WARNING;
480
481 if (get_subscr_by_argv(vty, id_type, id, &subscr))
482 return CMD_WARNING;
483
484 rc = db_subscr_update_aud_by_id(g_hlr->dbc, subscr.id, &aud3g);
485
486 if (rc) {
487 vty_out(vty, "%% Error: cannot set 3G auth data for IMSI='%s'%s",
488 subscr.imsi, VTY_NEWLINE);
489 return CMD_WARNING;
490 }
491 return CMD_SUCCESS;
492}
493
Harald Welted5807b82018-07-29 12:27:41 +0200494void hlr_vty_subscriber_init(void)
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200495{
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200496 install_element_ve(&subscriber_show_cmd);
Neels Hofmeyr8aa780b2018-12-02 18:52:49 +0100497 install_element_ve(&show_subscriber_cmd);
Neels Hofmeyr183e7002017-10-06 02:59:54 +0200498 install_element(ENABLE_NODE, &subscriber_create_cmd);
499 install_element(ENABLE_NODE, &subscriber_delete_cmd);
500 install_element(ENABLE_NODE, &subscriber_msisdn_cmd);
501 install_element(ENABLE_NODE, &subscriber_no_aud2g_cmd);
502 install_element(ENABLE_NODE, &subscriber_aud2g_cmd);
503 install_element(ENABLE_NODE, &subscriber_no_aud3g_cmd);
504 install_element(ENABLE_NODE, &subscriber_aud3g_cmd);
505}