blob: 355b23fb1218431688d95e35dce793f2cdc002b8 [file] [log] [blame]
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +02001/*
2 * (C) 2010 by Harald Welte <laforge@gnumonks.org>
3 * (C) 2010 by On-Waves
4 * All Rights Reserved
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Affero General Public License for more details.
15 *
16 * You should have received a copy of the GNU Affero General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 *
19 */
20
21#include <sys/socket.h>
22#include <netinet/in.h>
23#include <arpa/inet.h>
24#include <string.h>
25#include <time.h>
26#include <inttypes.h>
27
28#include <osmocom/core/talloc.h>
29#include <osmocom/core/rate_ctr.h>
30#include <osmocom/gsm/gsm48.h>
31
32#include <osmocom/gprs/gprs_ns.h>
33#include <osmocom/gsm/apn.h>
34
35#include <osmocom/sgsn/debug.h>
36#include <osmocom/sgsn/gb_proxy.h>
37#include <osmocom/sgsn/gprs_utils.h>
38#include <osmocom/sgsn/vty.h>
39
40#include <osmocom/vty/command.h>
41#include <osmocom/vty/vty.h>
42#include <osmocom/vty/misc.h>
43
44static struct gbproxy_config *g_cfg = NULL;
45
46/*
47 * vty code for gbproxy below
48 */
49static struct cmd_node gbproxy_node = {
50 GBPROXY_NODE,
51 "%s(config-gbproxy)# ",
52 1,
53};
54
55static const struct value_string keep_modes[] = {
56 {GBPROX_KEEP_NEVER, "never"},
57 {GBPROX_KEEP_REATTACH, "re-attach"},
58 {GBPROX_KEEP_IDENTIFIED, "identified"},
59 {GBPROX_KEEP_ALWAYS, "always"},
60 {0, NULL}
61};
62
63static const struct value_string match_ids[] = {
64 {GBPROX_MATCH_PATCHING, "patching"},
65 {GBPROX_MATCH_ROUTING, "routing"},
66 {0, NULL}
67};
68
69static void gbprox_vty_print_peer(struct vty *vty, struct gbproxy_peer *peer)
70{
71 struct gprs_ra_id raid;
72 gsm48_parse_ra(&raid, peer->ra);
73
74 vty_out(vty, "NSEI %5u, PTP-BVCI %5u, "
75 "RAI %s", peer->nsei, peer->bvci, osmo_rai_name(&raid));
76 if (peer->blocked)
77 vty_out(vty, " [BVC-BLOCKED]");
78
79 vty_out(vty, "%s", VTY_NEWLINE);
80}
81
82static int config_write_gbproxy(struct vty *vty)
83{
84 enum gbproxy_match_id match_id;
85
86 vty_out(vty, "gbproxy%s", VTY_NEWLINE);
87
88 vty_out(vty, " sgsn nsei %u%s", g_cfg->nsip_sgsn_nsei,
89 VTY_NEWLINE);
90
91 if (g_cfg->core_plmn.mcc > 0)
92 vty_out(vty, " core-mobile-country-code %s%s",
93 osmo_mcc_name(g_cfg->core_plmn.mcc), VTY_NEWLINE);
94 if (g_cfg->core_plmn.mnc > 0)
95 vty_out(vty, " core-mobile-network-code %s%s",
96 osmo_mnc_name(g_cfg->core_plmn.mnc, g_cfg->core_plmn.mnc_3_digits), VTY_NEWLINE);
97
98 for (match_id = 0; match_id < ARRAY_SIZE(g_cfg->matches); ++match_id) {
99 struct gbproxy_match *match = &g_cfg->matches[match_id];
100 if (match->re_str)
101 vty_out(vty, " match-imsi %s %s%s",
102 get_value_string(match_ids, match_id),
103 match->re_str, VTY_NEWLINE);
104 }
105
106 if (g_cfg->core_apn != NULL) {
107 if (g_cfg->core_apn_size > 0) {
108 char str[500] = {0};
109 vty_out(vty, " core-access-point-name %s%s",
110 osmo_apn_to_str(str, g_cfg->core_apn,
111 g_cfg->core_apn_size),
112 VTY_NEWLINE);
113 } else {
114 vty_out(vty, " core-access-point-name none%s",
115 VTY_NEWLINE);
116 }
117 }
118
119 if (g_cfg->route_to_sgsn2)
120 vty_out(vty, " secondary-sgsn nsei %u%s", g_cfg->nsip_sgsn2_nsei,
121 VTY_NEWLINE);
122
123 if (g_cfg->clean_stale_timer_freq > 0)
124 vty_out(vty, " link-list clean-stale-timer %u%s",
125 g_cfg->clean_stale_timer_freq, VTY_NEWLINE);
126 if (g_cfg->tlli_max_age > 0)
127 vty_out(vty, " link-list max-age %d%s",
128 g_cfg->tlli_max_age, VTY_NEWLINE);
129 if (g_cfg->tlli_max_len > 0)
130 vty_out(vty, " link-list max-length %d%s",
131 g_cfg->tlli_max_len, VTY_NEWLINE);
132 vty_out(vty, " link-list keep-mode %s%s",
133 get_value_string(keep_modes, g_cfg->keep_link_infos),
134 VTY_NEWLINE);
135 if (g_cfg->stored_msgs_max_len > 0)
136 vty_out(vty, " link stored-msgs-max-length %"PRIu32"%s",
137 g_cfg->stored_msgs_max_len, VTY_NEWLINE);
138
139
140 return CMD_SUCCESS;
141}
142
143DEFUN(cfg_gbproxy,
144 cfg_gbproxy_cmd,
145 "gbproxy",
146 "Configure the Gb proxy")
147{
148 vty->node = GBPROXY_NODE;
149 return CMD_SUCCESS;
150}
151
152DEFUN(cfg_nsip_sgsn_nsei,
153 cfg_nsip_sgsn_nsei_cmd,
154 "sgsn nsei <0-65534>",
155 "SGSN information\n"
156 "NSEI to be used in the connection with the SGSN\n"
157 "The NSEI\n")
158{
159 unsigned int nsei = atoi(argv[0]);
160
161 if (g_cfg->route_to_sgsn2 && g_cfg->nsip_sgsn2_nsei == nsei) {
162 vty_out(vty, "SGSN NSEI %d conflicts with secondary SGSN NSEI%s",
163 nsei, VTY_NEWLINE);
164 return CMD_WARNING;
165 }
166
167 g_cfg->nsip_sgsn_nsei = nsei;
168 return CMD_SUCCESS;
169}
170
171#define GBPROXY_CORE_MNC_STR "Use this network code for the core network\n"
172
173DEFUN(cfg_gbproxy_core_mnc,
174 cfg_gbproxy_core_mnc_cmd,
175 "core-mobile-network-code <1-999>",
176 GBPROXY_CORE_MNC_STR "NCC value\n")
177{
178 uint16_t mnc;
179 bool mnc_3_digits;
180 if (osmo_mnc_from_str(argv[0], &mnc, &mnc_3_digits)) {
181 vty_out(vty, "%% Invalid MNC: %s%s", argv[0], VTY_NEWLINE);
182 return CMD_WARNING;
183 }
184 g_cfg->core_plmn.mnc = mnc;
185 g_cfg->core_plmn.mnc_3_digits = mnc_3_digits;
186 return CMD_SUCCESS;
187}
188
189DEFUN(cfg_gbproxy_no_core_mnc,
190 cfg_gbproxy_no_core_mnc_cmd,
191 "no core-mobile-network-code",
192 NO_STR GBPROXY_CORE_MNC_STR)
193{
194 g_cfg->core_plmn.mnc = 0;
195 g_cfg->core_plmn.mnc_3_digits = false;
196 return CMD_SUCCESS;
197}
198
199#define GBPROXY_CORE_MCC_STR "Use this country code for the core network\n"
200
201DEFUN(cfg_gbproxy_core_mcc,
202 cfg_gbproxy_core_mcc_cmd,
203 "core-mobile-country-code <1-999>",
204 GBPROXY_CORE_MCC_STR "MCC value\n")
205{
206 g_cfg->core_plmn.mcc = atoi(argv[0]);
207 return CMD_SUCCESS;
208}
209
210DEFUN(cfg_gbproxy_no_core_mcc,
211 cfg_gbproxy_no_core_mcc_cmd,
212 "no core-mobile-country-code",
213 NO_STR GBPROXY_CORE_MCC_STR)
214{
215 g_cfg->core_plmn.mcc = 0;
216 return CMD_SUCCESS;
217}
218
219#define GBPROXY_MATCH_IMSI_STR "Restrict actions to certain IMSIs\n"
220
221DEFUN(cfg_gbproxy_match_imsi,
222 cfg_gbproxy_match_imsi_cmd,
223 "match-imsi (patching|routing) .REGEXP",
224 GBPROXY_MATCH_IMSI_STR
225 "Patch MS related information elements on match only\n"
226 "Route to the secondary SGSN on match only\n"
227 "Regular expression for the IMSI match\n")
228{
229 const char *filter = argv[1];
230 const char *err_msg = NULL;
231 struct gbproxy_match *match;
232 enum gbproxy_match_id match_id = get_string_value(match_ids, argv[0]);
233
234 OSMO_ASSERT(match_id >= GBPROX_MATCH_PATCHING &&
235 match_id < GBPROX_MATCH_LAST);
236 match = &g_cfg->matches[match_id];
237
238 if (gbproxy_set_patch_filter(match, filter, &err_msg) != 0) {
239 vty_out(vty, "Match expression invalid: %s%s",
240 err_msg, VTY_NEWLINE);
241 return CMD_WARNING;
242 }
243
244 g_cfg->acquire_imsi = true;
245
246 return CMD_SUCCESS;
247}
248
249DEFUN(cfg_gbproxy_no_match_imsi,
250 cfg_gbproxy_no_match_imsi_cmd,
251 "no match-imsi",
252 NO_STR GBPROXY_MATCH_IMSI_STR)
253{
254 enum gbproxy_match_id match_id;
255
256 for (match_id = 0; match_id < ARRAY_SIZE(g_cfg->matches); ++match_id)
257 gbproxy_clear_patch_filter(&g_cfg->matches[match_id]);
258
259 g_cfg->acquire_imsi = false;
260
261 return CMD_SUCCESS;
262}
263
264#define GBPROXY_CORE_APN_STR "Use this access point name (APN) for the backbone\n"
265#define GBPROXY_CORE_APN_ARG_STR "Replace APN by this string\n" "Remove APN\n"
266
267static int set_core_apn(struct vty *vty, const char *apn)
268{
269 int apn_len;
270
271 if (!apn) {
272 talloc_free(g_cfg->core_apn);
273 g_cfg->core_apn = NULL;
274 g_cfg->core_apn_size = 0;
275 return CMD_SUCCESS;
276 }
277
278 apn_len = strlen(apn);
279
280 if (apn_len >= 100) {
281 vty_out(vty, "APN string too long (max 99 chars)%s",
282 VTY_NEWLINE);
283 return CMD_WARNING;
284 }
285
286 if (apn_len == 0) {
287 talloc_free(g_cfg->core_apn);
288 /* TODO: replace NULL */
289 g_cfg->core_apn = talloc_zero_size(NULL, 2);
290 g_cfg->core_apn_size = 0;
291 } else {
292 /* TODO: replace NULL */
293 g_cfg->core_apn =
294 talloc_realloc_size(NULL, g_cfg->core_apn, apn_len + 1);
295 g_cfg->core_apn_size =
296 gprs_str_to_apn(g_cfg->core_apn, apn_len + 1, apn);
297 }
298
299 return CMD_SUCCESS;
300}
301
302DEFUN(cfg_gbproxy_core_apn,
303 cfg_gbproxy_core_apn_cmd,
304 "core-access-point-name (APN|none)",
305 GBPROXY_CORE_APN_STR GBPROXY_CORE_APN_ARG_STR)
306{
307 if (strcmp(argv[0], "none") == 0)
308 return set_core_apn(vty, "");
309 else
310 return set_core_apn(vty, argv[0]);
311}
312
313DEFUN(cfg_gbproxy_no_core_apn,
314 cfg_gbproxy_no_core_apn_cmd,
315 "no core-access-point-name",
316 NO_STR GBPROXY_CORE_APN_STR)
317{
318 return set_core_apn(vty, NULL);
319}
320
321/* TODO: Remove the patch-ptmsi command, since P-TMSI patching is enabled
322 * automatically when needed. This command is only left for manual testing
323 * (e.g. doing P-TMSI patching without using a secondary SGSN)
324 */
325#define GBPROXY_PATCH_PTMSI_STR "Patch P-TMSI/TLLI\n"
326
327DEFUN(cfg_gbproxy_patch_ptmsi,
328 cfg_gbproxy_patch_ptmsi_cmd,
329 "patch-ptmsi",
330 GBPROXY_PATCH_PTMSI_STR)
331{
332 g_cfg->patch_ptmsi = true;
333
334 return CMD_SUCCESS;
335}
336
337DEFUN(cfg_gbproxy_no_patch_ptmsi,
338 cfg_gbproxy_no_patch_ptmsi_cmd,
339 "no patch-ptmsi",
340 NO_STR GBPROXY_PATCH_PTMSI_STR)
341{
342 g_cfg->patch_ptmsi = false;
343
344 return CMD_SUCCESS;
345}
346
347/* TODO: Remove the acquire-imsi command, since that feature is enabled
348 * automatically when IMSI matching is enabled. This command is only left for
349 * manual testing (e.g. doing IMSI acquisition without IMSI based patching)
350 */
351#define GBPROXY_ACQUIRE_IMSI_STR "Acquire the IMSI before establishing a LLC connection (Experimental)\n"
352
353DEFUN(cfg_gbproxy_acquire_imsi,
354 cfg_gbproxy_acquire_imsi_cmd,
355 "acquire-imsi",
356 GBPROXY_ACQUIRE_IMSI_STR)
357{
358 g_cfg->acquire_imsi = true;
359
360 return CMD_SUCCESS;
361}
362
363DEFUN(cfg_gbproxy_no_acquire_imsi,
364 cfg_gbproxy_no_acquire_imsi_cmd,
365 "no acquire-imsi",
366 NO_STR GBPROXY_ACQUIRE_IMSI_STR)
367{
368 g_cfg->acquire_imsi = false;
369
370 return CMD_SUCCESS;
371}
372
373#define GBPROXY_SECOND_SGSN_STR "Route matching LLC connections to a second SGSN (Experimental)\n"
374
375DEFUN(cfg_gbproxy_secondary_sgsn,
376 cfg_gbproxy_secondary_sgsn_cmd,
377 "secondary-sgsn nsei <0-65534>",
378 GBPROXY_SECOND_SGSN_STR
379 "NSEI to be used in the connection with the SGSN\n"
380 "The NSEI\n")
381{
382 unsigned int nsei = atoi(argv[0]);
383
384 if (g_cfg->nsip_sgsn_nsei == nsei) {
385 vty_out(vty, "Secondary SGSN NSEI %d conflicts with primary SGSN NSEI%s",
386 nsei, VTY_NEWLINE);
387 return CMD_WARNING;
388 }
389
390 g_cfg->route_to_sgsn2 = true;
391 g_cfg->nsip_sgsn2_nsei = nsei;
392
393 g_cfg->patch_ptmsi = true;
394
395 return CMD_SUCCESS;
396}
397
398DEFUN(cfg_gbproxy_no_secondary_sgsn,
399 cfg_gbproxy_no_secondary_sgsn_cmd,
400 "no secondary-sgsn",
401 NO_STR GBPROXY_SECOND_SGSN_STR)
402{
403 g_cfg->route_to_sgsn2 = false;
404 g_cfg->nsip_sgsn2_nsei = 0xFFFF;
405
406 g_cfg->patch_ptmsi = false;
407
408 return CMD_SUCCESS;
409}
410
411#define GBPROXY_LINK_LIST_STR "Set TLLI list parameters\n"
412#define GBPROXY_LINK_STR "Set TLLI parameters\n"
413
414#define GBPROXY_CLEAN_STALE_TIMER_STR "Periodic timer to clean stale links\n"
415
416DEFUN(cfg_gbproxy_link_list_clean_stale_timer,
417 cfg_gbproxy_link_list_clean_stale_timer_cmd,
418 "link-list clean-stale-timer <1-999999>",
419 GBPROXY_LINK_LIST_STR GBPROXY_CLEAN_STALE_TIMER_STR
420 "Frequency at which the periodic timer is fired (in seconds)\n")
421{
422 struct gbproxy_peer *peer;
423 g_cfg->clean_stale_timer_freq = (unsigned int) atoi(argv[0]);
424
425 /* Re-schedule running timers soon in case prev frequency was really big
426 and new frequency is desired to be lower. After initial run, periodic
427 time is used. Use random() to avoid firing timers for all peers at
428 the same time */
429 llist_for_each_entry(peer, &g_cfg->bts_peers, list)
430 osmo_timer_schedule(&peer->clean_stale_timer,
431 random() % 5, random() % 1000000);
432
433 return CMD_SUCCESS;
434}
435
436DEFUN(cfg_gbproxy_link_list_no_clean_stale_timer,
437 cfg_gbproxy_link_list_no_clean_stale_timer_cmd,
438 "no link-list clean-stale-timer",
439 NO_STR GBPROXY_LINK_LIST_STR GBPROXY_CLEAN_STALE_TIMER_STR)
440
441{
442 struct gbproxy_peer *peer;
443 g_cfg->clean_stale_timer_freq = 0;
444
445 llist_for_each_entry(peer, &g_cfg->bts_peers, list)
446 osmo_timer_del(&peer->clean_stale_timer);
447
448 return CMD_SUCCESS;
449}
450
451#define GBPROXY_MAX_AGE_STR "Limit maximum age\n"
452
453DEFUN(cfg_gbproxy_link_list_max_age,
454 cfg_gbproxy_link_list_max_age_cmd,
455 "link-list max-age <1-999999>",
456 GBPROXY_LINK_LIST_STR GBPROXY_MAX_AGE_STR
457 "Maximum age in seconds\n")
458{
459 g_cfg->tlli_max_age = atoi(argv[0]);
460
461 return CMD_SUCCESS;
462}
463
464DEFUN(cfg_gbproxy_link_list_no_max_age,
465 cfg_gbproxy_link_list_no_max_age_cmd,
466 "no link-list max-age",
467 NO_STR GBPROXY_LINK_LIST_STR GBPROXY_MAX_AGE_STR)
468{
469 g_cfg->tlli_max_age = 0;
470
471 return CMD_SUCCESS;
472}
473
474#define GBPROXY_MAX_LEN_STR "Limit list length\n"
475
476DEFUN(cfg_gbproxy_link_list_max_len,
477 cfg_gbproxy_link_list_max_len_cmd,
478 "link-list max-length <1-99999>",
479 GBPROXY_LINK_LIST_STR GBPROXY_MAX_LEN_STR
480 "Maximum number of logical links in the list\n")
481{
482 g_cfg->tlli_max_len = atoi(argv[0]);
483
484 return CMD_SUCCESS;
485}
486
487DEFUN(cfg_gbproxy_link_list_no_max_len,
488 cfg_gbproxy_link_list_no_max_len_cmd,
489 "no link-list max-length",
490 NO_STR GBPROXY_LINK_LIST_STR GBPROXY_MAX_LEN_STR)
491{
492 g_cfg->tlli_max_len = 0;
493
494 return CMD_SUCCESS;
495}
496
497DEFUN(cfg_gbproxy_link_list_keep_mode,
498 cfg_gbproxy_link_list_keep_mode_cmd,
499 "link-list keep-mode (never|re-attach|identified|always)",
500 GBPROXY_LINK_LIST_STR "How to keep entries for detached logical links\n"
501 "Discard entry immediately after detachment\n"
502 "Keep entry if a re-attachment has be requested\n"
503 "Keep entry if it associated with an IMSI\n"
504 "Don't discard entries after detachment\n")
505{
506 int val = get_string_value(keep_modes, argv[0]);
507 OSMO_ASSERT(val >= GBPROX_KEEP_NEVER && val <= GBPROX_KEEP_ALWAYS);
508 g_cfg->keep_link_infos = val;
509
510 return CMD_SUCCESS;
511}
512
513DEFUN(cfg_gbproxy_link_stored_msgs_max_len,
514 cfg_gbproxy_link_stored_msgs_max_len_cmd,
515 "link stored-msgs-max-length <1-99999>",
516 GBPROXY_LINK_STR GBPROXY_MAX_LEN_STR
517 "Maximum number of msgb stored in the logical link waiting to acquire its IMSI\n")
518{
519 g_cfg->stored_msgs_max_len = (uint32_t) atoi(argv[0]);
520
521 return CMD_SUCCESS;
522}
523
524DEFUN(cfg_gbproxy_link_no_stored_msgs_max_len,
525 cfg_gbproxy_link_no_stored_msgs_max_len_cmd,
526 "no link stored-msgs-max-length",
527 NO_STR GBPROXY_LINK_STR GBPROXY_MAX_LEN_STR)
528{
529 g_cfg->stored_msgs_max_len = 0;
530
531 return CMD_SUCCESS;
532}
533
534
535DEFUN(show_gbproxy, show_gbproxy_cmd, "show gbproxy [stats]",
536 SHOW_STR "Display information about the Gb proxy\n" "Show statistics\n")
537{
538 struct gbproxy_peer *peer;
539 int show_stats = argc >= 1;
540
541 if (show_stats)
542 vty_out_rate_ctr_group(vty, "", g_cfg->ctrg);
543
544 llist_for_each_entry(peer, &g_cfg->bts_peers, list) {
545 gbprox_vty_print_peer(vty, peer);
546
547 if (show_stats)
548 vty_out_rate_ctr_group(vty, " ", peer->ctrg);
549 }
550 return CMD_SUCCESS;
551}
552
553DEFUN(show_gbproxy_links, show_gbproxy_links_cmd, "show gbproxy links",
554 SHOW_STR "Display information about the Gb proxy\n" "Show logical links\n")
555{
556 struct gbproxy_peer *peer;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200557 time_t now;
558 struct timespec ts = {0,};
559
560 osmo_clock_gettime(CLOCK_MONOTONIC, &ts);
561 now = ts.tv_sec;
562
563 llist_for_each_entry(peer, &g_cfg->bts_peers, list) {
564 struct gbproxy_link_info *link_info;
565 struct gbproxy_patch_state *state = &peer->patch_state;
566
567 gbprox_vty_print_peer(vty, peer);
568
569 llist_for_each_entry(link_info, &state->logical_links, list) {
570 time_t age = now - link_info->timestamp;
Neels Hofmeyr7facc862020-05-29 16:53:23 +0200571 struct osmo_mobile_identity mi;
572 const char *imsi_str;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200573
574 if (link_info->imsi > 0) {
Neels Hofmeyr7facc862020-05-29 16:53:23 +0200575 if (osmo_mobile_identity_decode(&mi, link_info->imsi, link_info->imsi_len, false)
576 || mi.type != GSM_MI_TYPE_IMSI)
577 imsi_str = "(invalid)";
578 else
579 imsi_str = mi.imsi;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200580 } else {
Neels Hofmeyr7facc862020-05-29 16:53:23 +0200581 imsi_str = "(none)";
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200582 }
583 vty_out(vty, " TLLI %08x, IMSI %s, AGE %d",
Neels Hofmeyr7facc862020-05-29 16:53:23 +0200584 link_info->tlli.current, imsi_str, (int)age);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200585
586 if (link_info->stored_msgs_len)
587 vty_out(vty, ", STORED %"PRIu32"/%"PRIu32,
588 link_info->stored_msgs_len,
589 g_cfg->stored_msgs_max_len);
590
591 if (g_cfg->route_to_sgsn2)
592 vty_out(vty, ", SGSN NSEI %d",
593 link_info->sgsn_nsei);
594
595 if (link_info->is_deregistered)
596 vty_out(vty, ", DE-REGISTERED");
597
598 vty_out(vty, "%s", VTY_NEWLINE);
599 }
600 }
601 return CMD_SUCCESS;
602}
603
604DEFUN(delete_gb_bvci, delete_gb_bvci_cmd,
605 "delete-gbproxy-peer <0-65534> bvci <2-65534>",
606 "Delete a GBProxy peer by NSEI and optionally BVCI\n"
607 "NSEI number\n"
608 "Only delete peer with a matching BVCI\n"
609 "BVCI number\n")
610{
611 const uint16_t nsei = atoi(argv[0]);
612 const uint16_t bvci = atoi(argv[1]);
613 int counter;
614
615 counter = gbproxy_cleanup_peers(g_cfg, nsei, bvci);
616
617 if (counter == 0) {
618 vty_out(vty, "BVC not found%s", VTY_NEWLINE);
619 return CMD_WARNING;
620 }
621
622 return CMD_SUCCESS;
623}
624
625DEFUN(delete_gb_nsei, delete_gb_nsei_cmd,
626 "delete-gbproxy-peer <0-65534> (only-bvc|only-nsvc|all) [dry-run]",
627 "Delete a GBProxy peer by NSEI and optionally BVCI\n"
628 "NSEI number\n"
629 "Only delete BSSGP connections (BVC)\n"
630 "Only delete dynamic NS connections (NS-VC)\n"
631 "Delete BVC and dynamic NS connections\n"
632 "Show what would be deleted instead of actually deleting\n"
633 )
634{
635 const uint16_t nsei = atoi(argv[0]);
636 const char *mode = argv[1];
637 int dry_run = argc > 2;
638 int delete_bvc = 0;
639 int delete_nsvc = 0;
640 int counter;
641
642 if (strcmp(mode, "only-bvc") == 0)
643 delete_bvc = 1;
644 else if (strcmp(mode, "only-nsvc") == 0)
645 delete_nsvc = 1;
646 else
647 delete_bvc = delete_nsvc = 1;
648
649 if (delete_bvc) {
650 if (!dry_run)
651 counter = gbproxy_cleanup_peers(g_cfg, nsei, 0);
652 else {
653 struct gbproxy_peer *peer;
654 counter = 0;
655 llist_for_each_entry(peer, &g_cfg->bts_peers, list) {
656 if (peer->nsei != nsei)
657 continue;
658
659 vty_out(vty, "BVC: ");
660 gbprox_vty_print_peer(vty, peer);
661 counter += 1;
662 }
663 }
664 vty_out(vty, "%sDeleted %d BVC%s",
665 dry_run ? "Not " : "", counter, VTY_NEWLINE);
666 }
667
668 if (delete_nsvc) {
669 struct gprs_ns_inst *nsi = g_cfg->nsi;
670 struct gprs_nsvc *nsvc, *nsvc2;
671
672 counter = 0;
673 llist_for_each_entry_safe(nsvc, nsvc2, &nsi->gprs_nsvcs, list) {
674 if (nsvc->nsei != nsei)
675 continue;
676 if (nsvc->persistent)
677 continue;
678
679 if (!dry_run)
680 gprs_nsvc_delete(nsvc);
681 else
682 vty_out(vty, "NS-VC: NSEI %5u, NS-VCI %5u, "
683 "remote %s%s",
684 nsvc->nsei, nsvc->nsvci,
685 gprs_ns_ll_str(nsvc), VTY_NEWLINE);
686 counter += 1;
687 }
688 vty_out(vty, "%sDeleted %d NS-VC%s",
689 dry_run ? "Not " : "", counter, VTY_NEWLINE);
690 }
691
692 return CMD_SUCCESS;
693}
694
695#define GBPROXY_DELETE_LINK_STR \
696 "Delete a GBProxy logical link entry by NSEI and identification\nNSEI number\n"
697
698DEFUN(delete_gb_link_by_id, delete_gb_link_by_id_cmd,
699 "delete-gbproxy-link <0-65534> (tlli|imsi|sgsn-nsei) IDENT",
700 GBPROXY_DELETE_LINK_STR
701 "Delete entries with a matching TLLI (hex)\n"
702 "Delete entries with a matching IMSI\n"
703 "Delete entries with a matching SGSN NSEI\n"
704 "Identification to match\n")
705{
706 const uint16_t nsei = atoi(argv[0]);
707 enum {MATCH_TLLI = 't', MATCH_IMSI = 'i', MATCH_SGSN = 's'} match;
708 uint32_t ident = 0;
709 const char *imsi = NULL;
710 struct gbproxy_peer *peer = 0;
711 struct gbproxy_link_info *link_info, *nxt;
712 struct gbproxy_patch_state *state;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200713 int found = 0;
714
715 match = argv[1][0];
716
717 switch (match) {
718 case MATCH_TLLI: ident = strtoll(argv[2], NULL, 16); break;
719 case MATCH_IMSI: imsi = argv[2]; break;
720 case MATCH_SGSN: ident = strtoll(argv[2], NULL, 0); break;
721 };
722
723 peer = gbproxy_peer_by_nsei(g_cfg, nsei);
724 if (!peer) {
725 vty_out(vty, "Didn't find peer with NSEI %d%s",
726 nsei, VTY_NEWLINE);
727 return CMD_WARNING;
728 }
729
730 state = &peer->patch_state;
731
732 llist_for_each_entry_safe(link_info, nxt, &state->logical_links, list) {
Neels Hofmeyr7facc862020-05-29 16:53:23 +0200733 struct osmo_mobile_identity mi;
734
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200735 switch (match) {
736 case MATCH_TLLI:
737 if (link_info->tlli.current != ident)
738 continue;
739 break;
740 case MATCH_SGSN:
741 if (link_info->sgsn_nsei != ident)
742 continue;
743 break;
744 case MATCH_IMSI:
745 if (!link_info->imsi)
746 continue;
Neels Hofmeyr7facc862020-05-29 16:53:23 +0200747 if (osmo_mobile_identity_decode(&mi, link_info->imsi, link_info->imsi_len, false)
748 || mi.type != GSM_MI_TYPE_IMSI)
749 continue;
750 if (strcmp(mi.imsi, imsi) != 0)
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200751 continue;
752 break;
753 }
754
755 vty_out(vty, "Deleting link with TLLI %08x%s", link_info->tlli.current,
756 VTY_NEWLINE);
757 gbproxy_delete_link_info(peer, link_info);
758 found += 1;
759 }
760
761 if (!found && argc >= 2) {
762 vty_out(vty, "Didn't find link entry with %s %s%s",
763 argv[1], argv[2], VTY_NEWLINE);
764 }
765
766 return CMD_SUCCESS;
767}
768
769DEFUN(delete_gb_link, delete_gb_link_cmd,
770 "delete-gbproxy-link <0-65534> (stale|de-registered)",
771 GBPROXY_DELETE_LINK_STR
772 "Delete stale entries\n"
773 "Delete de-registered entries\n")
774{
775 const uint16_t nsei = atoi(argv[0]);
776 enum {MATCH_STALE = 's', MATCH_DEREGISTERED = 'd'} match;
777 struct gbproxy_peer *peer = 0;
778 struct gbproxy_link_info *link_info, *nxt;
779 struct gbproxy_patch_state *state;
780 time_t now;
781 struct timespec ts = {0,};
782
783 int found = 0;
784
785 match = argv[1][0];
786
787 peer = gbproxy_peer_by_nsei(g_cfg, nsei);
788 if (!peer) {
789 vty_out(vty, "Didn't find peer with NSEI %d%s",
790 nsei, VTY_NEWLINE);
791 return CMD_WARNING;
792 }
793
794 state = &peer->patch_state;
795
796 osmo_clock_gettime(CLOCK_MONOTONIC, &ts);
797 now = ts.tv_sec;
798
799 if (match == MATCH_STALE) {
800 found = gbproxy_remove_stale_link_infos(peer, now);
801 if (found)
802 vty_out(vty, "Deleted %d stale logical link%s%s",
803 found, found == 1 ? "" : "s", VTY_NEWLINE);
804 } else {
805 llist_for_each_entry_safe(link_info, nxt,
806 &state->logical_links, list) {
807 if (!link_info->is_deregistered)
808 continue;
809
810 gbproxy_delete_link_info(peer, link_info);
811 found += 1;
812 }
813 }
814
815 if (found)
816 vty_out(vty, "Deleted %d %s logical link%s%s",
817 found, argv[1], found == 1 ? "" : "s", VTY_NEWLINE);
818
819 return CMD_SUCCESS;
820}
821
822/*
823 * legacy commands to provide an upgrade path from "broken" releases
824 * or pre-releases
825 */
826DEFUN_DEPRECATED(cfg_gbproxy_broken_apn_match,
827 cfg_gbproxy_broken_apn_match_cmd,
828 "core-access-point-name none match-imsi .REGEXP",
829 GBPROXY_CORE_APN_STR GBPROXY_MATCH_IMSI_STR "Remove APN\n"
830 "Patch MS related information elements on match only\n"
831 "Route to the secondary SGSN on match only\n"
832 "Regular expression for the IMSI match\n")
833{
834 const char *filter = argv[0];
835 const char *err_msg = NULL;
836 struct gbproxy_match *match;
837 enum gbproxy_match_id match_id = get_string_value(match_ids, "patching");
838
839 /* apply APN none */
840 set_core_apn(vty, "");
841
842 /* do the matching... with copy and paste */
843 OSMO_ASSERT(match_id >= GBPROX_MATCH_PATCHING &&
844 match_id < GBPROX_MATCH_LAST);
845 match = &g_cfg->matches[match_id];
846
847 if (gbproxy_set_patch_filter(match, filter, &err_msg) != 0) {
848 vty_out(vty, "Match expression invalid: %s%s",
849 err_msg, VTY_NEWLINE);
850 return CMD_WARNING;
851 }
852
853 g_cfg->acquire_imsi = true;
854
855 return CMD_SUCCESS;
856}
857
858#define GBPROXY_TLLI_LIST_STR "Set TLLI list parameters\n"
859#define GBPROXY_MAX_LEN_STR "Limit list length\n"
860DEFUN_DEPRECATED(cfg_gbproxy_depr_tlli_list_max_len,
861 cfg_gbproxy_depr_tlli_list_max_len_cmd,
862 "tlli-list max-length <1-99999>",
863 GBPROXY_TLLI_LIST_STR GBPROXY_MAX_LEN_STR
864 "Maximum number of TLLIs in the list\n")
865{
866 g_cfg->tlli_max_len = atoi(argv[0]);
867
868 return CMD_SUCCESS;
869}
870
871int gbproxy_vty_init(void)
872{
873 install_element_ve(&show_gbproxy_cmd);
874 install_element_ve(&show_gbproxy_links_cmd);
875
876 install_element(ENABLE_NODE, &delete_gb_bvci_cmd);
877 install_element(ENABLE_NODE, &delete_gb_nsei_cmd);
878 install_element(ENABLE_NODE, &delete_gb_link_by_id_cmd);
879 install_element(ENABLE_NODE, &delete_gb_link_cmd);
880
881 install_element(CONFIG_NODE, &cfg_gbproxy_cmd);
882 install_node(&gbproxy_node, config_write_gbproxy);
883 install_element(GBPROXY_NODE, &cfg_nsip_sgsn_nsei_cmd);
884 install_element(GBPROXY_NODE, &cfg_gbproxy_core_mcc_cmd);
885 install_element(GBPROXY_NODE, &cfg_gbproxy_core_mnc_cmd);
886 install_element(GBPROXY_NODE, &cfg_gbproxy_match_imsi_cmd);
887 install_element(GBPROXY_NODE, &cfg_gbproxy_core_apn_cmd);
888 install_element(GBPROXY_NODE, &cfg_gbproxy_secondary_sgsn_cmd);
889 install_element(GBPROXY_NODE, &cfg_gbproxy_patch_ptmsi_cmd);
890 install_element(GBPROXY_NODE, &cfg_gbproxy_acquire_imsi_cmd);
891 install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_clean_stale_timer_cmd);
892 install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_max_age_cmd);
893 install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_max_len_cmd);
894 install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_keep_mode_cmd);
895 install_element(GBPROXY_NODE, &cfg_gbproxy_link_stored_msgs_max_len_cmd);
896 install_element(GBPROXY_NODE, &cfg_gbproxy_no_core_mcc_cmd);
897 install_element(GBPROXY_NODE, &cfg_gbproxy_no_core_mnc_cmd);
898 install_element(GBPROXY_NODE, &cfg_gbproxy_no_match_imsi_cmd);
899 install_element(GBPROXY_NODE, &cfg_gbproxy_no_core_apn_cmd);
900 install_element(GBPROXY_NODE, &cfg_gbproxy_no_secondary_sgsn_cmd);
901 install_element(GBPROXY_NODE, &cfg_gbproxy_no_patch_ptmsi_cmd);
902 install_element(GBPROXY_NODE, &cfg_gbproxy_no_acquire_imsi_cmd);
903 install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_no_clean_stale_timer_cmd);
904 install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_no_max_age_cmd);
905 install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_no_max_len_cmd);
906 install_element(GBPROXY_NODE, &cfg_gbproxy_link_no_stored_msgs_max_len_cmd);
907
908 /* broken or deprecated to allow an upgrade path */
909 install_element(GBPROXY_NODE, &cfg_gbproxy_broken_apn_match_cmd);
910 install_element(GBPROXY_NODE, &cfg_gbproxy_depr_tlli_list_max_len_cmd);
911
912 return 0;
913}
914
915int gbproxy_parse_config(const char *config_file, struct gbproxy_config *cfg)
916{
917 int rc;
918
919 g_cfg = cfg;
920 rc = vty_read_config_file(config_file, NULL);
921 if (rc < 0) {
922 fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file);
923 return rc;
924 }
925
926 return 0;
927}