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