blob: 5c4f4542045f36bb23e20a60ec8bb8d683bad8b3 [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;
557 char mi_buf[200];
558 time_t now;
559 struct timespec ts = {0,};
560
561 osmo_clock_gettime(CLOCK_MONOTONIC, &ts);
562 now = ts.tv_sec;
563
564 llist_for_each_entry(peer, &g_cfg->bts_peers, list) {
565 struct gbproxy_link_info *link_info;
566 struct gbproxy_patch_state *state = &peer->patch_state;
567
568 gbprox_vty_print_peer(vty, peer);
569
570 llist_for_each_entry(link_info, &state->logical_links, list) {
571 time_t age = now - link_info->timestamp;
572
573 if (link_info->imsi > 0) {
574 snprintf(mi_buf, sizeof(mi_buf), "(invalid)");
575 gsm48_mi_to_string(mi_buf, sizeof(mi_buf),
576 link_info->imsi,
577 link_info->imsi_len);
578 } else {
579 snprintf(mi_buf, sizeof(mi_buf), "(none)");
580 }
581 vty_out(vty, " TLLI %08x, IMSI %s, AGE %d",
582 link_info->tlli.current, mi_buf, (int)age);
583
584 if (link_info->stored_msgs_len)
585 vty_out(vty, ", STORED %"PRIu32"/%"PRIu32,
586 link_info->stored_msgs_len,
587 g_cfg->stored_msgs_max_len);
588
589 if (g_cfg->route_to_sgsn2)
590 vty_out(vty, ", SGSN NSEI %d",
591 link_info->sgsn_nsei);
592
593 if (link_info->is_deregistered)
594 vty_out(vty, ", DE-REGISTERED");
595
596 vty_out(vty, "%s", VTY_NEWLINE);
597 }
598 }
599 return CMD_SUCCESS;
600}
601
602DEFUN(delete_gb_bvci, delete_gb_bvci_cmd,
603 "delete-gbproxy-peer <0-65534> bvci <2-65534>",
604 "Delete a GBProxy peer by NSEI and optionally BVCI\n"
605 "NSEI number\n"
606 "Only delete peer with a matching BVCI\n"
607 "BVCI number\n")
608{
609 const uint16_t nsei = atoi(argv[0]);
610 const uint16_t bvci = atoi(argv[1]);
611 int counter;
612
613 counter = gbproxy_cleanup_peers(g_cfg, nsei, bvci);
614
615 if (counter == 0) {
616 vty_out(vty, "BVC not found%s", VTY_NEWLINE);
617 return CMD_WARNING;
618 }
619
620 return CMD_SUCCESS;
621}
622
623DEFUN(delete_gb_nsei, delete_gb_nsei_cmd,
624 "delete-gbproxy-peer <0-65534> (only-bvc|only-nsvc|all) [dry-run]",
625 "Delete a GBProxy peer by NSEI and optionally BVCI\n"
626 "NSEI number\n"
627 "Only delete BSSGP connections (BVC)\n"
628 "Only delete dynamic NS connections (NS-VC)\n"
629 "Delete BVC and dynamic NS connections\n"
630 "Show what would be deleted instead of actually deleting\n"
631 )
632{
633 const uint16_t nsei = atoi(argv[0]);
634 const char *mode = argv[1];
635 int dry_run = argc > 2;
636 int delete_bvc = 0;
637 int delete_nsvc = 0;
638 int counter;
639
640 if (strcmp(mode, "only-bvc") == 0)
641 delete_bvc = 1;
642 else if (strcmp(mode, "only-nsvc") == 0)
643 delete_nsvc = 1;
644 else
645 delete_bvc = delete_nsvc = 1;
646
647 if (delete_bvc) {
648 if (!dry_run)
649 counter = gbproxy_cleanup_peers(g_cfg, nsei, 0);
650 else {
651 struct gbproxy_peer *peer;
652 counter = 0;
653 llist_for_each_entry(peer, &g_cfg->bts_peers, list) {
654 if (peer->nsei != nsei)
655 continue;
656
657 vty_out(vty, "BVC: ");
658 gbprox_vty_print_peer(vty, peer);
659 counter += 1;
660 }
661 }
662 vty_out(vty, "%sDeleted %d BVC%s",
663 dry_run ? "Not " : "", counter, VTY_NEWLINE);
664 }
665
666 if (delete_nsvc) {
667 struct gprs_ns_inst *nsi = g_cfg->nsi;
668 struct gprs_nsvc *nsvc, *nsvc2;
669
670 counter = 0;
671 llist_for_each_entry_safe(nsvc, nsvc2, &nsi->gprs_nsvcs, list) {
672 if (nsvc->nsei != nsei)
673 continue;
674 if (nsvc->persistent)
675 continue;
676
677 if (!dry_run)
678 gprs_nsvc_delete(nsvc);
679 else
680 vty_out(vty, "NS-VC: NSEI %5u, NS-VCI %5u, "
681 "remote %s%s",
682 nsvc->nsei, nsvc->nsvci,
683 gprs_ns_ll_str(nsvc), VTY_NEWLINE);
684 counter += 1;
685 }
686 vty_out(vty, "%sDeleted %d NS-VC%s",
687 dry_run ? "Not " : "", counter, VTY_NEWLINE);
688 }
689
690 return CMD_SUCCESS;
691}
692
693#define GBPROXY_DELETE_LINK_STR \
694 "Delete a GBProxy logical link entry by NSEI and identification\nNSEI number\n"
695
696DEFUN(delete_gb_link_by_id, delete_gb_link_by_id_cmd,
697 "delete-gbproxy-link <0-65534> (tlli|imsi|sgsn-nsei) IDENT",
698 GBPROXY_DELETE_LINK_STR
699 "Delete entries with a matching TLLI (hex)\n"
700 "Delete entries with a matching IMSI\n"
701 "Delete entries with a matching SGSN NSEI\n"
702 "Identification to match\n")
703{
704 const uint16_t nsei = atoi(argv[0]);
705 enum {MATCH_TLLI = 't', MATCH_IMSI = 'i', MATCH_SGSN = 's'} match;
706 uint32_t ident = 0;
707 const char *imsi = NULL;
708 struct gbproxy_peer *peer = 0;
709 struct gbproxy_link_info *link_info, *nxt;
710 struct gbproxy_patch_state *state;
711 char mi_buf[200];
712 int found = 0;
713
714 match = argv[1][0];
715
716 switch (match) {
717 case MATCH_TLLI: ident = strtoll(argv[2], NULL, 16); break;
718 case MATCH_IMSI: imsi = argv[2]; break;
719 case MATCH_SGSN: ident = strtoll(argv[2], NULL, 0); break;
720 };
721
722 peer = gbproxy_peer_by_nsei(g_cfg, nsei);
723 if (!peer) {
724 vty_out(vty, "Didn't find peer with NSEI %d%s",
725 nsei, VTY_NEWLINE);
726 return CMD_WARNING;
727 }
728
729 state = &peer->patch_state;
730
731 llist_for_each_entry_safe(link_info, nxt, &state->logical_links, list) {
732 switch (match) {
733 case MATCH_TLLI:
734 if (link_info->tlli.current != ident)
735 continue;
736 break;
737 case MATCH_SGSN:
738 if (link_info->sgsn_nsei != ident)
739 continue;
740 break;
741 case MATCH_IMSI:
742 if (!link_info->imsi)
743 continue;
744 mi_buf[0] = '\0';
745 gsm48_mi_to_string(mi_buf, sizeof(mi_buf),
746 link_info->imsi,
747 link_info->imsi_len);
748
749 if (strcmp(mi_buf, imsi) != 0)
750 continue;
751 break;
752 }
753
754 vty_out(vty, "Deleting link with TLLI %08x%s", link_info->tlli.current,
755 VTY_NEWLINE);
756 gbproxy_delete_link_info(peer, link_info);
757 found += 1;
758 }
759
760 if (!found && argc >= 2) {
761 vty_out(vty, "Didn't find link entry with %s %s%s",
762 argv[1], argv[2], VTY_NEWLINE);
763 }
764
765 return CMD_SUCCESS;
766}
767
768DEFUN(delete_gb_link, delete_gb_link_cmd,
769 "delete-gbproxy-link <0-65534> (stale|de-registered)",
770 GBPROXY_DELETE_LINK_STR
771 "Delete stale entries\n"
772 "Delete de-registered entries\n")
773{
774 const uint16_t nsei = atoi(argv[0]);
775 enum {MATCH_STALE = 's', MATCH_DEREGISTERED = 'd'} match;
776 struct gbproxy_peer *peer = 0;
777 struct gbproxy_link_info *link_info, *nxt;
778 struct gbproxy_patch_state *state;
779 time_t now;
780 struct timespec ts = {0,};
781
782 int found = 0;
783
784 match = argv[1][0];
785
786 peer = gbproxy_peer_by_nsei(g_cfg, nsei);
787 if (!peer) {
788 vty_out(vty, "Didn't find peer with NSEI %d%s",
789 nsei, VTY_NEWLINE);
790 return CMD_WARNING;
791 }
792
793 state = &peer->patch_state;
794
795 osmo_clock_gettime(CLOCK_MONOTONIC, &ts);
796 now = ts.tv_sec;
797
798 if (match == MATCH_STALE) {
799 found = gbproxy_remove_stale_link_infos(peer, now);
800 if (found)
801 vty_out(vty, "Deleted %d stale logical link%s%s",
802 found, found == 1 ? "" : "s", VTY_NEWLINE);
803 } else {
804 llist_for_each_entry_safe(link_info, nxt,
805 &state->logical_links, list) {
806 if (!link_info->is_deregistered)
807 continue;
808
809 gbproxy_delete_link_info(peer, link_info);
810 found += 1;
811 }
812 }
813
814 if (found)
815 vty_out(vty, "Deleted %d %s logical link%s%s",
816 found, argv[1], found == 1 ? "" : "s", VTY_NEWLINE);
817
818 return CMD_SUCCESS;
819}
820
821/*
822 * legacy commands to provide an upgrade path from "broken" releases
823 * or pre-releases
824 */
825DEFUN_DEPRECATED(cfg_gbproxy_broken_apn_match,
826 cfg_gbproxy_broken_apn_match_cmd,
827 "core-access-point-name none match-imsi .REGEXP",
828 GBPROXY_CORE_APN_STR GBPROXY_MATCH_IMSI_STR "Remove APN\n"
829 "Patch MS related information elements on match only\n"
830 "Route to the secondary SGSN on match only\n"
831 "Regular expression for the IMSI match\n")
832{
833 const char *filter = argv[0];
834 const char *err_msg = NULL;
835 struct gbproxy_match *match;
836 enum gbproxy_match_id match_id = get_string_value(match_ids, "patching");
837
838 /* apply APN none */
839 set_core_apn(vty, "");
840
841 /* do the matching... with copy and paste */
842 OSMO_ASSERT(match_id >= GBPROX_MATCH_PATCHING &&
843 match_id < GBPROX_MATCH_LAST);
844 match = &g_cfg->matches[match_id];
845
846 if (gbproxy_set_patch_filter(match, filter, &err_msg) != 0) {
847 vty_out(vty, "Match expression invalid: %s%s",
848 err_msg, VTY_NEWLINE);
849 return CMD_WARNING;
850 }
851
852 g_cfg->acquire_imsi = true;
853
854 return CMD_SUCCESS;
855}
856
857#define GBPROXY_TLLI_LIST_STR "Set TLLI list parameters\n"
858#define GBPROXY_MAX_LEN_STR "Limit list length\n"
859DEFUN_DEPRECATED(cfg_gbproxy_depr_tlli_list_max_len,
860 cfg_gbproxy_depr_tlli_list_max_len_cmd,
861 "tlli-list max-length <1-99999>",
862 GBPROXY_TLLI_LIST_STR GBPROXY_MAX_LEN_STR
863 "Maximum number of TLLIs in the list\n")
864{
865 g_cfg->tlli_max_len = atoi(argv[0]);
866
867 return CMD_SUCCESS;
868}
869
870int gbproxy_vty_init(void)
871{
872 install_element_ve(&show_gbproxy_cmd);
873 install_element_ve(&show_gbproxy_links_cmd);
874
875 install_element(ENABLE_NODE, &delete_gb_bvci_cmd);
876 install_element(ENABLE_NODE, &delete_gb_nsei_cmd);
877 install_element(ENABLE_NODE, &delete_gb_link_by_id_cmd);
878 install_element(ENABLE_NODE, &delete_gb_link_cmd);
879
880 install_element(CONFIG_NODE, &cfg_gbproxy_cmd);
881 install_node(&gbproxy_node, config_write_gbproxy);
882 install_element(GBPROXY_NODE, &cfg_nsip_sgsn_nsei_cmd);
883 install_element(GBPROXY_NODE, &cfg_gbproxy_core_mcc_cmd);
884 install_element(GBPROXY_NODE, &cfg_gbproxy_core_mnc_cmd);
885 install_element(GBPROXY_NODE, &cfg_gbproxy_match_imsi_cmd);
886 install_element(GBPROXY_NODE, &cfg_gbproxy_core_apn_cmd);
887 install_element(GBPROXY_NODE, &cfg_gbproxy_secondary_sgsn_cmd);
888 install_element(GBPROXY_NODE, &cfg_gbproxy_patch_ptmsi_cmd);
889 install_element(GBPROXY_NODE, &cfg_gbproxy_acquire_imsi_cmd);
890 install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_clean_stale_timer_cmd);
891 install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_max_age_cmd);
892 install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_max_len_cmd);
893 install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_keep_mode_cmd);
894 install_element(GBPROXY_NODE, &cfg_gbproxy_link_stored_msgs_max_len_cmd);
895 install_element(GBPROXY_NODE, &cfg_gbproxy_no_core_mcc_cmd);
896 install_element(GBPROXY_NODE, &cfg_gbproxy_no_core_mnc_cmd);
897 install_element(GBPROXY_NODE, &cfg_gbproxy_no_match_imsi_cmd);
898 install_element(GBPROXY_NODE, &cfg_gbproxy_no_core_apn_cmd);
899 install_element(GBPROXY_NODE, &cfg_gbproxy_no_secondary_sgsn_cmd);
900 install_element(GBPROXY_NODE, &cfg_gbproxy_no_patch_ptmsi_cmd);
901 install_element(GBPROXY_NODE, &cfg_gbproxy_no_acquire_imsi_cmd);
902 install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_no_clean_stale_timer_cmd);
903 install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_no_max_age_cmd);
904 install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_no_max_len_cmd);
905 install_element(GBPROXY_NODE, &cfg_gbproxy_link_no_stored_msgs_max_len_cmd);
906
907 /* broken or deprecated to allow an upgrade path */
908 install_element(GBPROXY_NODE, &cfg_gbproxy_broken_apn_match_cmd);
909 install_element(GBPROXY_NODE, &cfg_gbproxy_depr_tlli_list_max_len_cmd);
910
911 return 0;
912}
913
914int gbproxy_parse_config(const char *config_file, struct gbproxy_config *cfg)
915{
916 int rc;
917
918 g_cfg = cfg;
919 rc = vty_read_config_file(config_file, NULL);
920 if (rc < 0) {
921 fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file);
922 return rc;
923 }
924
925 return 0;
926}