blob: caad52e650dedcfa2f883d58187111ab00042061 [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, "
Daniel Willmanne50550e2020-11-26 18:19:21 +010076 "RAI %s", peer->nse->nsei, peer->bvci, osmo_rai_name(&raid));
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +020077 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{
Daniel Willmanne50550e2020-11-26 18:19:21 +0100423 struct gbproxy_nse *nse;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200424 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 */
Daniel Willmanne50550e2020-11-26 18:19:21 +0100430 llist_for_each_entry(nse, &g_cfg->nse_peers, list) {
431 struct gbproxy_peer *peer;
432 llist_for_each_entry(peer, &nse->bts_peers, list)
433 osmo_timer_schedule(&peer->clean_stale_timer,
434 random() % 5, random() % 1000000);
435 }
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200436
437 return CMD_SUCCESS;
438}
439
440DEFUN(cfg_gbproxy_link_list_no_clean_stale_timer,
441 cfg_gbproxy_link_list_no_clean_stale_timer_cmd,
442 "no link-list clean-stale-timer",
443 NO_STR GBPROXY_LINK_LIST_STR GBPROXY_CLEAN_STALE_TIMER_STR)
444
445{
Daniel Willmanne50550e2020-11-26 18:19:21 +0100446 struct gbproxy_nse *nse;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200447 g_cfg->clean_stale_timer_freq = 0;
448
Daniel Willmanne50550e2020-11-26 18:19:21 +0100449 llist_for_each_entry(nse, &g_cfg->nse_peers, list) {
450 struct gbproxy_peer *peer;
451 llist_for_each_entry(peer, &nse->bts_peers, list)
452 osmo_timer_del(&peer->clean_stale_timer);
453 }
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200454
455 return CMD_SUCCESS;
456}
457
458#define GBPROXY_MAX_AGE_STR "Limit maximum age\n"
459
460DEFUN(cfg_gbproxy_link_list_max_age,
461 cfg_gbproxy_link_list_max_age_cmd,
462 "link-list max-age <1-999999>",
463 GBPROXY_LINK_LIST_STR GBPROXY_MAX_AGE_STR
464 "Maximum age in seconds\n")
465{
466 g_cfg->tlli_max_age = atoi(argv[0]);
467
468 return CMD_SUCCESS;
469}
470
471DEFUN(cfg_gbproxy_link_list_no_max_age,
472 cfg_gbproxy_link_list_no_max_age_cmd,
473 "no link-list max-age",
474 NO_STR GBPROXY_LINK_LIST_STR GBPROXY_MAX_AGE_STR)
475{
476 g_cfg->tlli_max_age = 0;
477
478 return CMD_SUCCESS;
479}
480
481#define GBPROXY_MAX_LEN_STR "Limit list length\n"
482
483DEFUN(cfg_gbproxy_link_list_max_len,
484 cfg_gbproxy_link_list_max_len_cmd,
485 "link-list max-length <1-99999>",
486 GBPROXY_LINK_LIST_STR GBPROXY_MAX_LEN_STR
487 "Maximum number of logical links in the list\n")
488{
489 g_cfg->tlli_max_len = atoi(argv[0]);
490
491 return CMD_SUCCESS;
492}
493
494DEFUN(cfg_gbproxy_link_list_no_max_len,
495 cfg_gbproxy_link_list_no_max_len_cmd,
496 "no link-list max-length",
497 NO_STR GBPROXY_LINK_LIST_STR GBPROXY_MAX_LEN_STR)
498{
499 g_cfg->tlli_max_len = 0;
500
501 return CMD_SUCCESS;
502}
503
504DEFUN(cfg_gbproxy_link_list_keep_mode,
505 cfg_gbproxy_link_list_keep_mode_cmd,
506 "link-list keep-mode (never|re-attach|identified|always)",
507 GBPROXY_LINK_LIST_STR "How to keep entries for detached logical links\n"
508 "Discard entry immediately after detachment\n"
509 "Keep entry if a re-attachment has be requested\n"
510 "Keep entry if it associated with an IMSI\n"
511 "Don't discard entries after detachment\n")
512{
513 int val = get_string_value(keep_modes, argv[0]);
514 OSMO_ASSERT(val >= GBPROX_KEEP_NEVER && val <= GBPROX_KEEP_ALWAYS);
515 g_cfg->keep_link_infos = val;
516
517 return CMD_SUCCESS;
518}
519
520DEFUN(cfg_gbproxy_link_stored_msgs_max_len,
521 cfg_gbproxy_link_stored_msgs_max_len_cmd,
522 "link stored-msgs-max-length <1-99999>",
523 GBPROXY_LINK_STR GBPROXY_MAX_LEN_STR
524 "Maximum number of msgb stored in the logical link waiting to acquire its IMSI\n")
525{
526 g_cfg->stored_msgs_max_len = (uint32_t) atoi(argv[0]);
527
528 return CMD_SUCCESS;
529}
530
531DEFUN(cfg_gbproxy_link_no_stored_msgs_max_len,
532 cfg_gbproxy_link_no_stored_msgs_max_len_cmd,
533 "no link stored-msgs-max-length",
534 NO_STR GBPROXY_LINK_STR GBPROXY_MAX_LEN_STR)
535{
536 g_cfg->stored_msgs_max_len = 0;
537
538 return CMD_SUCCESS;
539}
540
541
542DEFUN(show_gbproxy, show_gbproxy_cmd, "show gbproxy [stats]",
543 SHOW_STR "Display information about the Gb proxy\n" "Show statistics\n")
544{
Daniel Willmanne50550e2020-11-26 18:19:21 +0100545 struct gbproxy_nse *nse;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200546 int show_stats = argc >= 1;
547
548 if (show_stats)
549 vty_out_rate_ctr_group(vty, "", g_cfg->ctrg);
550
Daniel Willmanne50550e2020-11-26 18:19:21 +0100551 llist_for_each_entry(nse, &g_cfg->nse_peers, list) {
552 struct gbproxy_peer *peer;
553 llist_for_each_entry(peer, &nse->bts_peers, list) {
554 gbprox_vty_print_peer(vty, peer);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200555
Daniel Willmanne50550e2020-11-26 18:19:21 +0100556 if (show_stats)
557 vty_out_rate_ctr_group(vty, " ", peer->ctrg);
558 }
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200559 }
560 return CMD_SUCCESS;
561}
562
563DEFUN(show_gbproxy_links, show_gbproxy_links_cmd, "show gbproxy links",
564 SHOW_STR "Display information about the Gb proxy\n" "Show logical links\n")
565{
Daniel Willmanne50550e2020-11-26 18:19:21 +0100566 struct gbproxy_nse *nse;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200567 time_t now;
568 struct timespec ts = {0,};
569
570 osmo_clock_gettime(CLOCK_MONOTONIC, &ts);
571 now = ts.tv_sec;
572
Daniel Willmanne50550e2020-11-26 18:19:21 +0100573 llist_for_each_entry(nse, &g_cfg->nse_peers, list) {
574 struct gbproxy_peer *peer;
575 llist_for_each_entry(peer, &nse->bts_peers, list) {
576 struct gbproxy_link_info *link_info;
577 struct gbproxy_patch_state *state = &peer->patch_state;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200578
Daniel Willmanne50550e2020-11-26 18:19:21 +0100579 gbprox_vty_print_peer(vty, peer);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200580
Daniel Willmanne50550e2020-11-26 18:19:21 +0100581 llist_for_each_entry(link_info, &state->logical_links, list) {
582 time_t age = now - link_info->timestamp;
583 struct osmo_mobile_identity mi;
584 const char *imsi_str;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200585
Daniel Willmanne50550e2020-11-26 18:19:21 +0100586 if (link_info->imsi > 0) {
587 if (osmo_mobile_identity_decode(&mi, link_info->imsi, link_info->imsi_len, false)
588 || mi.type != GSM_MI_TYPE_IMSI)
589 imsi_str = "(invalid)";
590 else
591 imsi_str = mi.imsi;
592 } else {
593 imsi_str = "(none)";
594 }
595 vty_out(vty, " TLLI %08x, IMSI %s, AGE %d",
596 link_info->tlli.current, imsi_str, (int)age);
597
598 if (link_info->stored_msgs_len)
599 vty_out(vty, ", STORED %"PRIu32"/%"PRIu32,
600 link_info->stored_msgs_len,
601 g_cfg->stored_msgs_max_len);
602
603 if (g_cfg->route_to_sgsn2)
604 vty_out(vty, ", SGSN NSEI %d",
605 link_info->sgsn_nsei);
606
607 if (link_info->is_deregistered)
608 vty_out(vty, ", DE-REGISTERED");
609
610 vty_out(vty, "%s", VTY_NEWLINE);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200611 }
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200612 }
613 }
614 return CMD_SUCCESS;
615}
616
617DEFUN(delete_gb_bvci, delete_gb_bvci_cmd,
618 "delete-gbproxy-peer <0-65534> bvci <2-65534>",
619 "Delete a GBProxy peer by NSEI and optionally BVCI\n"
620 "NSEI number\n"
621 "Only delete peer with a matching BVCI\n"
622 "BVCI number\n")
623{
624 const uint16_t nsei = atoi(argv[0]);
625 const uint16_t bvci = atoi(argv[1]);
626 int counter;
627
628 counter = gbproxy_cleanup_peers(g_cfg, nsei, bvci);
629
630 if (counter == 0) {
631 vty_out(vty, "BVC not found%s", VTY_NEWLINE);
632 return CMD_WARNING;
633 }
634
635 return CMD_SUCCESS;
636}
637
638DEFUN(delete_gb_nsei, delete_gb_nsei_cmd,
639 "delete-gbproxy-peer <0-65534> (only-bvc|only-nsvc|all) [dry-run]",
640 "Delete a GBProxy peer by NSEI and optionally BVCI\n"
641 "NSEI number\n"
642 "Only delete BSSGP connections (BVC)\n"
643 "Only delete dynamic NS connections (NS-VC)\n"
644 "Delete BVC and dynamic NS connections\n"
645 "Show what would be deleted instead of actually deleting\n"
646 )
647{
648 const uint16_t nsei = atoi(argv[0]);
649 const char *mode = argv[1];
650 int dry_run = argc > 2;
651 int delete_bvc = 0;
652 int delete_nsvc = 0;
653 int counter;
654
655 if (strcmp(mode, "only-bvc") == 0)
656 delete_bvc = 1;
657 else if (strcmp(mode, "only-nsvc") == 0)
658 delete_nsvc = 1;
659 else
660 delete_bvc = delete_nsvc = 1;
661
662 if (delete_bvc) {
663 if (!dry_run)
664 counter = gbproxy_cleanup_peers(g_cfg, nsei, 0);
665 else {
Daniel Willmanne50550e2020-11-26 18:19:21 +0100666 struct gbproxy_nse *nse;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200667 struct gbproxy_peer *peer;
668 counter = 0;
Daniel Willmanne50550e2020-11-26 18:19:21 +0100669 llist_for_each_entry(nse, &g_cfg->nse_peers, list) {
670 if (nse->nsei != nsei)
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200671 continue;
Daniel Willmanne50550e2020-11-26 18:19:21 +0100672 llist_for_each_entry(peer, &nse->bts_peers, list) {
673 vty_out(vty, "BVC: ");
674 gbprox_vty_print_peer(vty, peer);
675 counter += 1;
676 }
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200677 }
678 }
679 vty_out(vty, "%sDeleted %d BVC%s",
680 dry_run ? "Not " : "", counter, VTY_NEWLINE);
681 }
682
683 if (delete_nsvc) {
Alexander Couzens951e1332020-09-22 13:21:46 +0200684 struct gprs_ns2_inst *nsi = g_cfg->nsi;
685 struct gprs_ns2_nse *nse;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200686
Alexander Couzens951e1332020-09-22 13:21:46 +0200687 nse = gprs_ns2_nse_by_nsei(nsi, nsei);
688 if (!nse) {
689 vty_out(vty, "NSEI not found%s", VTY_NEWLINE);
690 return CMD_WARNING;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200691 }
Alexander Couzens951e1332020-09-22 13:21:46 +0200692
693 /* TODO: We should NOT delete a persistent NSEI/NSVC as soon as we can check for these */
694 if (!dry_run)
695 gprs_ns2_free_nse(nse);
696
697 vty_out(vty, "%sDeleted NS-VCs for NSEI %d%s",
698 dry_run ? "Not " : "", nsei, VTY_NEWLINE);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200699 }
700
701 return CMD_SUCCESS;
702}
703
704#define GBPROXY_DELETE_LINK_STR \
705 "Delete a GBProxy logical link entry by NSEI and identification\nNSEI number\n"
706
707DEFUN(delete_gb_link_by_id, delete_gb_link_by_id_cmd,
708 "delete-gbproxy-link <0-65534> (tlli|imsi|sgsn-nsei) IDENT",
709 GBPROXY_DELETE_LINK_STR
710 "Delete entries with a matching TLLI (hex)\n"
711 "Delete entries with a matching IMSI\n"
712 "Delete entries with a matching SGSN NSEI\n"
713 "Identification to match\n")
714{
715 const uint16_t nsei = atoi(argv[0]);
716 enum {MATCH_TLLI = 't', MATCH_IMSI = 'i', MATCH_SGSN = 's'} match;
717 uint32_t ident = 0;
718 const char *imsi = NULL;
719 struct gbproxy_peer *peer = 0;
720 struct gbproxy_link_info *link_info, *nxt;
721 struct gbproxy_patch_state *state;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200722 int found = 0;
723
724 match = argv[1][0];
725
726 switch (match) {
727 case MATCH_TLLI: ident = strtoll(argv[2], NULL, 16); break;
728 case MATCH_IMSI: imsi = argv[2]; break;
729 case MATCH_SGSN: ident = strtoll(argv[2], NULL, 0); break;
730 };
731
732 peer = gbproxy_peer_by_nsei(g_cfg, nsei);
733 if (!peer) {
734 vty_out(vty, "Didn't find peer with NSEI %d%s",
735 nsei, VTY_NEWLINE);
736 return CMD_WARNING;
737 }
738
739 state = &peer->patch_state;
740
741 llist_for_each_entry_safe(link_info, nxt, &state->logical_links, list) {
Neels Hofmeyr7facc862020-05-29 16:53:23 +0200742 struct osmo_mobile_identity mi;
743
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200744 switch (match) {
745 case MATCH_TLLI:
746 if (link_info->tlli.current != ident)
747 continue;
748 break;
749 case MATCH_SGSN:
750 if (link_info->sgsn_nsei != ident)
751 continue;
752 break;
753 case MATCH_IMSI:
754 if (!link_info->imsi)
755 continue;
Neels Hofmeyr7facc862020-05-29 16:53:23 +0200756 if (osmo_mobile_identity_decode(&mi, link_info->imsi, link_info->imsi_len, false)
757 || mi.type != GSM_MI_TYPE_IMSI)
758 continue;
759 if (strcmp(mi.imsi, imsi) != 0)
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200760 continue;
761 break;
762 }
763
764 vty_out(vty, "Deleting link with TLLI %08x%s", link_info->tlli.current,
765 VTY_NEWLINE);
766 gbproxy_delete_link_info(peer, link_info);
767 found += 1;
768 }
769
770 if (!found && argc >= 2) {
771 vty_out(vty, "Didn't find link entry with %s %s%s",
772 argv[1], argv[2], VTY_NEWLINE);
773 }
774
775 return CMD_SUCCESS;
776}
777
778DEFUN(delete_gb_link, delete_gb_link_cmd,
779 "delete-gbproxy-link <0-65534> (stale|de-registered)",
780 GBPROXY_DELETE_LINK_STR
781 "Delete stale entries\n"
782 "Delete de-registered entries\n")
783{
784 const uint16_t nsei = atoi(argv[0]);
785 enum {MATCH_STALE = 's', MATCH_DEREGISTERED = 'd'} match;
786 struct gbproxy_peer *peer = 0;
787 struct gbproxy_link_info *link_info, *nxt;
788 struct gbproxy_patch_state *state;
789 time_t now;
790 struct timespec ts = {0,};
791
792 int found = 0;
793
794 match = argv[1][0];
795
796 peer = gbproxy_peer_by_nsei(g_cfg, nsei);
797 if (!peer) {
798 vty_out(vty, "Didn't find peer with NSEI %d%s",
799 nsei, VTY_NEWLINE);
800 return CMD_WARNING;
801 }
802
803 state = &peer->patch_state;
804
805 osmo_clock_gettime(CLOCK_MONOTONIC, &ts);
806 now = ts.tv_sec;
807
808 if (match == MATCH_STALE) {
809 found = gbproxy_remove_stale_link_infos(peer, now);
810 if (found)
811 vty_out(vty, "Deleted %d stale logical link%s%s",
812 found, found == 1 ? "" : "s", VTY_NEWLINE);
813 } else {
814 llist_for_each_entry_safe(link_info, nxt,
815 &state->logical_links, list) {
816 if (!link_info->is_deregistered)
817 continue;
818
819 gbproxy_delete_link_info(peer, link_info);
820 found += 1;
821 }
822 }
823
824 if (found)
825 vty_out(vty, "Deleted %d %s logical link%s%s",
826 found, argv[1], found == 1 ? "" : "s", VTY_NEWLINE);
827
828 return CMD_SUCCESS;
829}
830
831/*
832 * legacy commands to provide an upgrade path from "broken" releases
833 * or pre-releases
834 */
835DEFUN_DEPRECATED(cfg_gbproxy_broken_apn_match,
836 cfg_gbproxy_broken_apn_match_cmd,
837 "core-access-point-name none match-imsi .REGEXP",
838 GBPROXY_CORE_APN_STR GBPROXY_MATCH_IMSI_STR "Remove APN\n"
839 "Patch MS related information elements on match only\n"
840 "Route to the secondary SGSN on match only\n"
841 "Regular expression for the IMSI match\n")
842{
843 const char *filter = argv[0];
844 const char *err_msg = NULL;
845 struct gbproxy_match *match;
846 enum gbproxy_match_id match_id = get_string_value(match_ids, "patching");
847
848 /* apply APN none */
849 set_core_apn(vty, "");
850
851 /* do the matching... with copy and paste */
852 OSMO_ASSERT(match_id >= GBPROX_MATCH_PATCHING &&
853 match_id < GBPROX_MATCH_LAST);
854 match = &g_cfg->matches[match_id];
855
856 if (gbproxy_set_patch_filter(match, filter, &err_msg) != 0) {
857 vty_out(vty, "Match expression invalid: %s%s",
858 err_msg, VTY_NEWLINE);
859 return CMD_WARNING;
860 }
861
862 g_cfg->acquire_imsi = true;
863
864 return CMD_SUCCESS;
865}
866
867#define GBPROXY_TLLI_LIST_STR "Set TLLI list parameters\n"
868#define GBPROXY_MAX_LEN_STR "Limit list length\n"
869DEFUN_DEPRECATED(cfg_gbproxy_depr_tlli_list_max_len,
870 cfg_gbproxy_depr_tlli_list_max_len_cmd,
871 "tlli-list max-length <1-99999>",
872 GBPROXY_TLLI_LIST_STR GBPROXY_MAX_LEN_STR
873 "Maximum number of TLLIs in the list\n")
874{
875 g_cfg->tlli_max_len = atoi(argv[0]);
876
877 return CMD_SUCCESS;
878}
879
880int gbproxy_vty_init(void)
881{
882 install_element_ve(&show_gbproxy_cmd);
883 install_element_ve(&show_gbproxy_links_cmd);
884
885 install_element(ENABLE_NODE, &delete_gb_bvci_cmd);
886 install_element(ENABLE_NODE, &delete_gb_nsei_cmd);
887 install_element(ENABLE_NODE, &delete_gb_link_by_id_cmd);
888 install_element(ENABLE_NODE, &delete_gb_link_cmd);
889
890 install_element(CONFIG_NODE, &cfg_gbproxy_cmd);
891 install_node(&gbproxy_node, config_write_gbproxy);
892 install_element(GBPROXY_NODE, &cfg_nsip_sgsn_nsei_cmd);
893 install_element(GBPROXY_NODE, &cfg_gbproxy_core_mcc_cmd);
894 install_element(GBPROXY_NODE, &cfg_gbproxy_core_mnc_cmd);
895 install_element(GBPROXY_NODE, &cfg_gbproxy_match_imsi_cmd);
896 install_element(GBPROXY_NODE, &cfg_gbproxy_core_apn_cmd);
897 install_element(GBPROXY_NODE, &cfg_gbproxy_secondary_sgsn_cmd);
898 install_element(GBPROXY_NODE, &cfg_gbproxy_patch_ptmsi_cmd);
899 install_element(GBPROXY_NODE, &cfg_gbproxy_acquire_imsi_cmd);
900 install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_clean_stale_timer_cmd);
901 install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_max_age_cmd);
902 install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_max_len_cmd);
903 install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_keep_mode_cmd);
904 install_element(GBPROXY_NODE, &cfg_gbproxy_link_stored_msgs_max_len_cmd);
905 install_element(GBPROXY_NODE, &cfg_gbproxy_no_core_mcc_cmd);
906 install_element(GBPROXY_NODE, &cfg_gbproxy_no_core_mnc_cmd);
907 install_element(GBPROXY_NODE, &cfg_gbproxy_no_match_imsi_cmd);
908 install_element(GBPROXY_NODE, &cfg_gbproxy_no_core_apn_cmd);
909 install_element(GBPROXY_NODE, &cfg_gbproxy_no_secondary_sgsn_cmd);
910 install_element(GBPROXY_NODE, &cfg_gbproxy_no_patch_ptmsi_cmd);
911 install_element(GBPROXY_NODE, &cfg_gbproxy_no_acquire_imsi_cmd);
912 install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_no_clean_stale_timer_cmd);
913 install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_no_max_age_cmd);
914 install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_no_max_len_cmd);
915 install_element(GBPROXY_NODE, &cfg_gbproxy_link_no_stored_msgs_max_len_cmd);
916
917 /* broken or deprecated to allow an upgrade path */
918 install_element(GBPROXY_NODE, &cfg_gbproxy_broken_apn_match_cmd);
919 install_element(GBPROXY_NODE, &cfg_gbproxy_depr_tlli_list_max_len_cmd);
920
921 return 0;
922}
923
924int gbproxy_parse_config(const char *config_file, struct gbproxy_config *cfg)
925{
926 int rc;
927
928 g_cfg = cfg;
929 rc = vty_read_config_file(config_file, NULL);
930 if (rc < 0) {
931 fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file);
932 return rc;
933 }
934
935 return 0;
936}