blob: 3ef8ccc9aabe08d47742aef4cac931f92ab55351 [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
Harald Welte560bdb32020-12-04 22:24:47 +010071static void gbprox_vty_print_bvc(struct vty *vty, struct gbproxy_bvc *bvc)
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +020072{
73 struct gprs_ra_id raid;
Harald Welte560bdb32020-12-04 22:24:47 +010074 gsm48_parse_ra(&raid, bvc->ra);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +020075
76 vty_out(vty, "NSEI %5u, PTP-BVCI %5u, "
Harald Welte560bdb32020-12-04 22:24:47 +010077 "RAI %s", bvc->nse->nsei, bvc->bvci, osmo_rai_name(&raid));
78 if (bvc->blocked)
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +020079 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
Harald Welte560bdb32020-12-04 22:24:47 +0100429 time is used. Use random() to avoid firing timers for all bvcs at
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200430 the same time */
Harald Welte560bdb32020-12-04 22:24:47 +0100431 llist_for_each_entry(nse, &g_cfg->nses, list) {
432 struct gbproxy_bvc *bvc;
433 llist_for_each_entry(bvc, &nse->bvcs, list)
434 osmo_timer_schedule(&bvc->clean_stale_timer,
Daniel Willmanne50550e2020-11-26 18:19:21 +0100435 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
Harald Welte560bdb32020-12-04 22:24:47 +0100450 llist_for_each_entry(nse, &g_cfg->nses, list) {
451 struct gbproxy_bvc *bvc;
452 llist_for_each_entry(bvc, &nse->bvcs, list)
453 osmo_timer_del(&bvc->clean_stale_timer);
Daniel Willmanne50550e2020-11-26 18:19:21 +0100454 }
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
Harald Welte560bdb32020-12-04 22:24:47 +0100587 llist_for_each_entry(nse, &g_cfg->nses, list) {
588 struct gbproxy_bvc *bvc;
589 llist_for_each_entry(bvc, &nse->bvcs, list) {
590 gbprox_vty_print_bvc(vty, bvc);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200591
Daniel Willmanne50550e2020-11-26 18:19:21 +0100592 if (show_stats)
Harald Welte560bdb32020-12-04 22:24:47 +0100593 vty_out_rate_ctr_group(vty, " ", bvc->ctrg);
Daniel Willmanne50550e2020-11-26 18:19:21 +0100594 }
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
Harald Welte560bdb32020-12-04 22:24:47 +0100609 llist_for_each_entry(nse, &g_cfg->nses, list) {
610 struct gbproxy_bvc *bvc;
611 llist_for_each_entry(bvc, &nse->bvcs, list) {
Daniel Willmanne50550e2020-11-26 18:19:21 +0100612 struct gbproxy_link_info *link_info;
Harald Welte560bdb32020-12-04 22:24:47 +0100613 struct gbproxy_patch_state *state = &bvc->patch_state;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200614
Harald Welte560bdb32020-12-04 22:24:47 +0100615 gbprox_vty_print_bvc(vty, bvc);
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>",
Harald Welte560bdb32020-12-04 22:24:47 +0100655 "Delete a GBProxy bvc by NSEI and optionally BVCI\n"
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200656 "NSEI number\n"
Harald Welte560bdb32020-12-04 22:24:47 +0100657 "Only delete bvc with a matching BVCI\n"
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200658 "BVCI number\n")
659{
660 const uint16_t nsei = atoi(argv[0]);
661 const uint16_t bvci = atoi(argv[1]);
662 int counter;
663
Harald Welte560bdb32020-12-04 22:24:47 +0100664 counter = gbproxy_cleanup_bvcs(g_cfg, nsei, bvci);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200665
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]",
Harald Welte560bdb32020-12-04 22:24:47 +0100676 "Delete a GBProxy bvc by NSEI and optionally BVCI\n"
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200677 "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);
Harald Welte560bdb32020-12-04 22:24:47 +0100701 counter = gbproxy_cleanup_bvcs(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;
Harald Welte560bdb32020-12-04 22:24:47 +0100705 struct gbproxy_bvc *bvc;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200706 counter = 0;
Harald Welte560bdb32020-12-04 22:24:47 +0100707 llist_for_each_entry(nse, &g_cfg->nses, list) {
Daniel Willmanne50550e2020-11-26 18:19:21 +0100708 if (nse->nsei != nsei)
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200709 continue;
Harald Welte560bdb32020-12-04 22:24:47 +0100710 llist_for_each_entry(bvc, &nse->bvcs, list) {
Daniel Willmanne50550e2020-11-26 18:19:21 +0100711 vty_out(vty, "BVC: ");
Harald Welte560bdb32020-12-04 22:24:47 +0100712 gbprox_vty_print_bvc(vty, bvc);
Daniel Willmanne50550e2020-11-26 18:19:21 +0100713 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;
Harald Welte560bdb32020-12-04 22:24:47 +0100757 struct gbproxy_bvc *bvc = 0;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200758 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
Harald Welte560bdb32020-12-04 22:24:47 +0100770 bvc = gbproxy_bvc_by_nsei(g_cfg, nsei);
771 if (!bvc) {
772 vty_out(vty, "Didn't find bvc with NSEI %d%s",
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200773 nsei, VTY_NEWLINE);
774 return CMD_WARNING;
775 }
776
Harald Welte560bdb32020-12-04 22:24:47 +0100777 state = &bvc->patch_state;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200778
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);
Harald Welte560bdb32020-12-04 22:24:47 +0100804 gbproxy_delete_link_info(bvc, link_info);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200805 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;
Harald Welte560bdb32020-12-04 22:24:47 +0100824 struct gbproxy_bvc *bvc = 0;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200825 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
Harald Welte560bdb32020-12-04 22:24:47 +0100834 bvc = gbproxy_bvc_by_nsei(g_cfg, nsei);
835 if (!bvc) {
836 vty_out(vty, "Didn't find bvc with NSEI %d%s",
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200837 nsei, VTY_NEWLINE);
838 return CMD_WARNING;
839 }
840
Harald Welte560bdb32020-12-04 22:24:47 +0100841 state = &bvc->patch_state;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200842
843 osmo_clock_gettime(CLOCK_MONOTONIC, &ts);
844 now = ts.tv_sec;
845
846 if (match == MATCH_STALE) {
Harald Welte560bdb32020-12-04 22:24:47 +0100847 found = gbproxy_remove_stale_link_infos(bvc, now);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200848 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
Harald Welte560bdb32020-12-04 22:24:47 +0100857 gbproxy_delete_link_info(bvc, link_info);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200858 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}