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