blob: 47ac9b9a2d97d863776a51c273f6f89f7cfe4ce2 [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;
Harald Welte8b4c7942020-12-05 10:14:49 +0100425 int i, j;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200426 g_cfg->clean_stale_timer_freq = (unsigned int) atoi(argv[0]);
427
428 /* Re-schedule running timers soon in case prev frequency was really big
429 and new frequency is desired to be lower. After initial run, periodic
Harald Welte560bdb32020-12-04 22:24:47 +0100430 time is used. Use random() to avoid firing timers for all bvcs at
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200431 the same time */
Harald Welted2fef952020-12-05 00:31:07 +0100432 hash_for_each(g_cfg->bss_nses, i, nse, list) {
Harald Welte560bdb32020-12-04 22:24:47 +0100433 struct gbproxy_bvc *bvc;
Harald Welte8b4c7942020-12-05 10:14:49 +0100434 hash_for_each(nse->bvcs, j, bvc, list)
Harald Welte560bdb32020-12-04 22:24:47 +0100435 osmo_timer_schedule(&bvc->clean_stale_timer,
Daniel Willmanne50550e2020-11-26 18:19:21 +0100436 random() % 5, random() % 1000000);
437 }
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200438
439 return CMD_SUCCESS;
440}
441
442DEFUN(cfg_gbproxy_link_list_no_clean_stale_timer,
443 cfg_gbproxy_link_list_no_clean_stale_timer_cmd,
444 "no link-list clean-stale-timer",
445 NO_STR GBPROXY_LINK_LIST_STR GBPROXY_CLEAN_STALE_TIMER_STR)
446
447{
Daniel Willmanne50550e2020-11-26 18:19:21 +0100448 struct gbproxy_nse *nse;
Harald Welte8b4c7942020-12-05 10:14:49 +0100449 int i, j;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200450 g_cfg->clean_stale_timer_freq = 0;
451
Harald Welted2fef952020-12-05 00:31:07 +0100452 hash_for_each(g_cfg->bss_nses, i, nse, list) {
Harald Welte560bdb32020-12-04 22:24:47 +0100453 struct gbproxy_bvc *bvc;
Harald Welte8b4c7942020-12-05 10:14:49 +0100454 hash_for_each(nse->bvcs, j, bvc, list)
Harald Welte560bdb32020-12-04 22:24:47 +0100455 osmo_timer_del(&bvc->clean_stale_timer);
Daniel Willmanne50550e2020-11-26 18:19:21 +0100456 }
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200457
458 return CMD_SUCCESS;
459}
460
461#define GBPROXY_MAX_AGE_STR "Limit maximum age\n"
462
463DEFUN(cfg_gbproxy_link_list_max_age,
464 cfg_gbproxy_link_list_max_age_cmd,
465 "link-list max-age <1-999999>",
466 GBPROXY_LINK_LIST_STR GBPROXY_MAX_AGE_STR
467 "Maximum age in seconds\n")
468{
469 g_cfg->tlli_max_age = atoi(argv[0]);
470
471 return CMD_SUCCESS;
472}
473
474DEFUN(cfg_gbproxy_link_list_no_max_age,
475 cfg_gbproxy_link_list_no_max_age_cmd,
476 "no link-list max-age",
477 NO_STR GBPROXY_LINK_LIST_STR GBPROXY_MAX_AGE_STR)
478{
479 g_cfg->tlli_max_age = 0;
480
481 return CMD_SUCCESS;
482}
483
484#define GBPROXY_MAX_LEN_STR "Limit list length\n"
485
486DEFUN(cfg_gbproxy_link_list_max_len,
487 cfg_gbproxy_link_list_max_len_cmd,
488 "link-list max-length <1-99999>",
489 GBPROXY_LINK_LIST_STR GBPROXY_MAX_LEN_STR
490 "Maximum number of logical links in the list\n")
491{
492 g_cfg->tlli_max_len = atoi(argv[0]);
493
494 return CMD_SUCCESS;
495}
496
497DEFUN(cfg_gbproxy_link_list_no_max_len,
498 cfg_gbproxy_link_list_no_max_len_cmd,
499 "no link-list max-length",
500 NO_STR GBPROXY_LINK_LIST_STR GBPROXY_MAX_LEN_STR)
501{
502 g_cfg->tlli_max_len = 0;
503
504 return CMD_SUCCESS;
505}
506
507DEFUN(cfg_gbproxy_link_list_keep_mode,
508 cfg_gbproxy_link_list_keep_mode_cmd,
509 "link-list keep-mode (never|re-attach|identified|always)",
510 GBPROXY_LINK_LIST_STR "How to keep entries for detached logical links\n"
511 "Discard entry immediately after detachment\n"
512 "Keep entry if a re-attachment has be requested\n"
513 "Keep entry if it associated with an IMSI\n"
514 "Don't discard entries after detachment\n")
515{
516 int val = get_string_value(keep_modes, argv[0]);
517 OSMO_ASSERT(val >= GBPROX_KEEP_NEVER && val <= GBPROX_KEEP_ALWAYS);
518 g_cfg->keep_link_infos = val;
519
520 return CMD_SUCCESS;
521}
522
523DEFUN(cfg_gbproxy_link_stored_msgs_max_len,
524 cfg_gbproxy_link_stored_msgs_max_len_cmd,
525 "link stored-msgs-max-length <1-99999>",
526 GBPROXY_LINK_STR GBPROXY_MAX_LEN_STR
527 "Maximum number of msgb stored in the logical link waiting to acquire its IMSI\n")
528{
529 g_cfg->stored_msgs_max_len = (uint32_t) atoi(argv[0]);
530
531 return CMD_SUCCESS;
532}
533
534DEFUN(cfg_gbproxy_link_no_stored_msgs_max_len,
535 cfg_gbproxy_link_no_stored_msgs_max_len_cmd,
536 "no link stored-msgs-max-length",
537 NO_STR GBPROXY_LINK_STR GBPROXY_MAX_LEN_STR)
538{
539 g_cfg->stored_msgs_max_len = 0;
540
541 return CMD_SUCCESS;
542}
543
Daniel Willmann8f407b12020-12-02 19:33:50 +0100544static void log_set_bvc_filter(struct log_target *target,
545 const uint16_t *bvci)
546{
547 if (bvci) {
548 uintptr_t bvci_filter = *bvci | BVC_LOG_CTX_FLAG;
549 target->filter_map |= (1 << LOG_FLT_GB_BVC);
550 target->filter_data[LOG_FLT_GB_BVC] = (void *)bvci_filter;
551 } else if (target->filter_data[LOG_FLT_GB_BVC]) {
552 target->filter_map = ~(1 << LOG_FLT_GB_BVC);
553 target->filter_data[LOG_FLT_GB_BVC] = NULL;
554 }
555}
556
557DEFUN(logging_fltr_bvc,
558 logging_fltr_bvc_cmd,
559 "logging filter bvc bvci <0-65535>",
560 LOGGING_STR FILTER_STR
561 "Filter based on BSSGP VC\n"
562 "Identify BVC by BVCI\n"
563 "Numeric identifier\n")
564{
565 struct log_target *tgt;
566 uint16_t id = atoi(argv[0]);
567
568 log_tgt_mutex_lock();
569 tgt = osmo_log_vty2tgt(vty);
570 if (!tgt) {
571 log_tgt_mutex_unlock();
572 return CMD_WARNING;
573 }
574
575 log_set_bvc_filter(tgt, &id);
576 log_tgt_mutex_unlock();
577 return CMD_SUCCESS;
578}
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200579
580DEFUN(show_gbproxy, show_gbproxy_cmd, "show gbproxy [stats]",
581 SHOW_STR "Display information about the Gb proxy\n" "Show statistics\n")
582{
Daniel Willmanne50550e2020-11-26 18:19:21 +0100583 struct gbproxy_nse *nse;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200584 int show_stats = argc >= 1;
Harald Welte8b4c7942020-12-05 10:14:49 +0100585 int i, j;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200586
587 if (show_stats)
588 vty_out_rate_ctr_group(vty, "", g_cfg->ctrg);
589
Harald Welted2fef952020-12-05 00:31:07 +0100590 hash_for_each(g_cfg->bss_nses, i, nse, list) {
Harald Welte560bdb32020-12-04 22:24:47 +0100591 struct gbproxy_bvc *bvc;
Harald Welte8b4c7942020-12-05 10:14:49 +0100592 hash_for_each(nse->bvcs, j, bvc, list) {
Harald Welte560bdb32020-12-04 22:24:47 +0100593 gbprox_vty_print_bvc(vty, bvc);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200594
Daniel Willmanne50550e2020-11-26 18:19:21 +0100595 if (show_stats)
Harald Welte560bdb32020-12-04 22:24:47 +0100596 vty_out_rate_ctr_group(vty, " ", bvc->ctrg);
Daniel Willmanne50550e2020-11-26 18:19:21 +0100597 }
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200598 }
599 return CMD_SUCCESS;
600}
601
602DEFUN(show_gbproxy_links, show_gbproxy_links_cmd, "show gbproxy links",
603 SHOW_STR "Display information about the Gb proxy\n" "Show logical links\n")
604{
Daniel Willmanne50550e2020-11-26 18:19:21 +0100605 struct gbproxy_nse *nse;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200606 time_t now;
607 struct timespec ts = {0,};
Harald Welte8b4c7942020-12-05 10:14:49 +0100608 int i, j;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200609
610 osmo_clock_gettime(CLOCK_MONOTONIC, &ts);
611 now = ts.tv_sec;
612
Harald Welted2fef952020-12-05 00:31:07 +0100613 hash_for_each(g_cfg->bss_nses, i, nse, list) {
Harald Welte560bdb32020-12-04 22:24:47 +0100614 struct gbproxy_bvc *bvc;
Harald Welte8b4c7942020-12-05 10:14:49 +0100615 hash_for_each(nse->bvcs, j, bvc, list) {
Daniel Willmanne50550e2020-11-26 18:19:21 +0100616 struct gbproxy_link_info *link_info;
Harald Welte560bdb32020-12-04 22:24:47 +0100617 struct gbproxy_patch_state *state = &bvc->patch_state;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200618
Harald Welte560bdb32020-12-04 22:24:47 +0100619 gbprox_vty_print_bvc(vty, bvc);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200620
Daniel Willmanne50550e2020-11-26 18:19:21 +0100621 llist_for_each_entry(link_info, &state->logical_links, list) {
622 time_t age = now - link_info->timestamp;
623 struct osmo_mobile_identity mi;
624 const char *imsi_str;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200625
Daniel Willmanne50550e2020-11-26 18:19:21 +0100626 if (link_info->imsi > 0) {
627 if (osmo_mobile_identity_decode(&mi, link_info->imsi, link_info->imsi_len, false)
628 || mi.type != GSM_MI_TYPE_IMSI)
629 imsi_str = "(invalid)";
630 else
631 imsi_str = mi.imsi;
632 } else {
633 imsi_str = "(none)";
634 }
635 vty_out(vty, " TLLI %08x, IMSI %s, AGE %d",
636 link_info->tlli.current, imsi_str, (int)age);
637
638 if (link_info->stored_msgs_len)
639 vty_out(vty, ", STORED %"PRIu32"/%"PRIu32,
640 link_info->stored_msgs_len,
641 g_cfg->stored_msgs_max_len);
642
643 if (g_cfg->route_to_sgsn2)
644 vty_out(vty, ", SGSN NSEI %d",
645 link_info->sgsn_nsei);
646
647 if (link_info->is_deregistered)
648 vty_out(vty, ", DE-REGISTERED");
649
650 vty_out(vty, "%s", VTY_NEWLINE);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200651 }
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200652 }
653 }
654 return CMD_SUCCESS;
655}
656
657DEFUN(delete_gb_bvci, delete_gb_bvci_cmd,
658 "delete-gbproxy-peer <0-65534> bvci <2-65534>",
Harald Welte560bdb32020-12-04 22:24:47 +0100659 "Delete a GBProxy bvc by NSEI and optionally BVCI\n"
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200660 "NSEI number\n"
Harald Welte560bdb32020-12-04 22:24:47 +0100661 "Only delete bvc with a matching BVCI\n"
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200662 "BVCI number\n")
663{
664 const uint16_t nsei = atoi(argv[0]);
665 const uint16_t bvci = atoi(argv[1]);
666 int counter;
667
Harald Welte560bdb32020-12-04 22:24:47 +0100668 counter = gbproxy_cleanup_bvcs(g_cfg, nsei, bvci);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200669
670 if (counter == 0) {
671 vty_out(vty, "BVC not found%s", VTY_NEWLINE);
672 return CMD_WARNING;
673 }
674
675 return CMD_SUCCESS;
676}
677
678DEFUN(delete_gb_nsei, delete_gb_nsei_cmd,
679 "delete-gbproxy-peer <0-65534> (only-bvc|only-nsvc|all) [dry-run]",
Harald Welte560bdb32020-12-04 22:24:47 +0100680 "Delete a GBProxy bvc by NSEI and optionally BVCI\n"
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200681 "NSEI number\n"
682 "Only delete BSSGP connections (BVC)\n"
683 "Only delete dynamic NS connections (NS-VC)\n"
684 "Delete BVC and dynamic NS connections\n"
685 "Show what would be deleted instead of actually deleting\n"
686 )
687{
688 const uint16_t nsei = atoi(argv[0]);
689 const char *mode = argv[1];
690 int dry_run = argc > 2;
691 int delete_bvc = 0;
692 int delete_nsvc = 0;
693 int counter;
694
695 if (strcmp(mode, "only-bvc") == 0)
696 delete_bvc = 1;
697 else if (strcmp(mode, "only-nsvc") == 0)
698 delete_nsvc = 1;
699 else
700 delete_bvc = delete_nsvc = 1;
701
702 if (delete_bvc) {
Daniel Willmann5b897712020-12-04 17:43:27 +0100703 if (!dry_run) {
704 struct gbproxy_nse *nse = gbproxy_nse_by_nsei(g_cfg, nsei);
Harald Welte560bdb32020-12-04 22:24:47 +0100705 counter = gbproxy_cleanup_bvcs(g_cfg, nsei, 0);
Daniel Willmann5b897712020-12-04 17:43:27 +0100706 gbproxy_nse_free(nse);
707 } else {
Daniel Willmanne50550e2020-11-26 18:19:21 +0100708 struct gbproxy_nse *nse;
Harald Welte560bdb32020-12-04 22:24:47 +0100709 struct gbproxy_bvc *bvc;
Harald Welte8b4c7942020-12-05 10:14:49 +0100710 int i, j;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200711 counter = 0;
Harald Welted2fef952020-12-05 00:31:07 +0100712 hash_for_each(g_cfg->bss_nses, i, nse, list) {
Daniel Willmanne50550e2020-11-26 18:19:21 +0100713 if (nse->nsei != nsei)
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200714 continue;
Harald Welte8b4c7942020-12-05 10:14:49 +0100715 hash_for_each(nse->bvcs, j, bvc, list) {
Daniel Willmanne50550e2020-11-26 18:19:21 +0100716 vty_out(vty, "BVC: ");
Harald Welte560bdb32020-12-04 22:24:47 +0100717 gbprox_vty_print_bvc(vty, bvc);
Daniel Willmanne50550e2020-11-26 18:19:21 +0100718 counter += 1;
719 }
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200720 }
721 }
722 vty_out(vty, "%sDeleted %d BVC%s",
723 dry_run ? "Not " : "", counter, VTY_NEWLINE);
724 }
725
726 if (delete_nsvc) {
Alexander Couzens951e1332020-09-22 13:21:46 +0200727 struct gprs_ns2_inst *nsi = g_cfg->nsi;
728 struct gprs_ns2_nse *nse;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200729
Alexander Couzens951e1332020-09-22 13:21:46 +0200730 nse = gprs_ns2_nse_by_nsei(nsi, nsei);
731 if (!nse) {
732 vty_out(vty, "NSEI not found%s", VTY_NEWLINE);
733 return CMD_WARNING;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200734 }
Alexander Couzens951e1332020-09-22 13:21:46 +0200735
736 /* TODO: We should NOT delete a persistent NSEI/NSVC as soon as we can check for these */
737 if (!dry_run)
738 gprs_ns2_free_nse(nse);
739
740 vty_out(vty, "%sDeleted NS-VCs for NSEI %d%s",
741 dry_run ? "Not " : "", nsei, VTY_NEWLINE);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200742 }
743
744 return CMD_SUCCESS;
745}
746
747#define GBPROXY_DELETE_LINK_STR \
748 "Delete a GBProxy logical link entry by NSEI and identification\nNSEI number\n"
749
750DEFUN(delete_gb_link_by_id, delete_gb_link_by_id_cmd,
751 "delete-gbproxy-link <0-65534> (tlli|imsi|sgsn-nsei) IDENT",
752 GBPROXY_DELETE_LINK_STR
753 "Delete entries with a matching TLLI (hex)\n"
754 "Delete entries with a matching IMSI\n"
755 "Delete entries with a matching SGSN NSEI\n"
756 "Identification to match\n")
757{
758 const uint16_t nsei = atoi(argv[0]);
759 enum {MATCH_TLLI = 't', MATCH_IMSI = 'i', MATCH_SGSN = 's'} match;
760 uint32_t ident = 0;
761 const char *imsi = NULL;
Harald Welte560bdb32020-12-04 22:24:47 +0100762 struct gbproxy_bvc *bvc = 0;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200763 struct gbproxy_link_info *link_info, *nxt;
764 struct gbproxy_patch_state *state;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200765 int found = 0;
766
767 match = argv[1][0];
768
769 switch (match) {
770 case MATCH_TLLI: ident = strtoll(argv[2], NULL, 16); break;
771 case MATCH_IMSI: imsi = argv[2]; break;
772 case MATCH_SGSN: ident = strtoll(argv[2], NULL, 0); break;
773 };
774
Harald Welte560bdb32020-12-04 22:24:47 +0100775 bvc = gbproxy_bvc_by_nsei(g_cfg, nsei);
776 if (!bvc) {
777 vty_out(vty, "Didn't find bvc with NSEI %d%s",
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200778 nsei, VTY_NEWLINE);
779 return CMD_WARNING;
780 }
781
Harald Welte560bdb32020-12-04 22:24:47 +0100782 state = &bvc->patch_state;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200783
784 llist_for_each_entry_safe(link_info, nxt, &state->logical_links, list) {
Neels Hofmeyr7facc862020-05-29 16:53:23 +0200785 struct osmo_mobile_identity mi;
786
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200787 switch (match) {
788 case MATCH_TLLI:
789 if (link_info->tlli.current != ident)
790 continue;
791 break;
792 case MATCH_SGSN:
793 if (link_info->sgsn_nsei != ident)
794 continue;
795 break;
796 case MATCH_IMSI:
797 if (!link_info->imsi)
798 continue;
Neels Hofmeyr7facc862020-05-29 16:53:23 +0200799 if (osmo_mobile_identity_decode(&mi, link_info->imsi, link_info->imsi_len, false)
800 || mi.type != GSM_MI_TYPE_IMSI)
801 continue;
802 if (strcmp(mi.imsi, imsi) != 0)
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200803 continue;
804 break;
805 }
806
807 vty_out(vty, "Deleting link with TLLI %08x%s", link_info->tlli.current,
808 VTY_NEWLINE);
Harald Welte560bdb32020-12-04 22:24:47 +0100809 gbproxy_delete_link_info(bvc, link_info);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200810 found += 1;
811 }
812
813 if (!found && argc >= 2) {
814 vty_out(vty, "Didn't find link entry with %s %s%s",
815 argv[1], argv[2], VTY_NEWLINE);
816 }
817
818 return CMD_SUCCESS;
819}
820
821DEFUN(delete_gb_link, delete_gb_link_cmd,
822 "delete-gbproxy-link <0-65534> (stale|de-registered)",
823 GBPROXY_DELETE_LINK_STR
824 "Delete stale entries\n"
825 "Delete de-registered entries\n")
826{
827 const uint16_t nsei = atoi(argv[0]);
828 enum {MATCH_STALE = 's', MATCH_DEREGISTERED = 'd'} match;
Harald Welte560bdb32020-12-04 22:24:47 +0100829 struct gbproxy_bvc *bvc = 0;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200830 struct gbproxy_link_info *link_info, *nxt;
831 struct gbproxy_patch_state *state;
832 time_t now;
833 struct timespec ts = {0,};
834
835 int found = 0;
836
837 match = argv[1][0];
838
Harald Welte560bdb32020-12-04 22:24:47 +0100839 bvc = gbproxy_bvc_by_nsei(g_cfg, nsei);
840 if (!bvc) {
841 vty_out(vty, "Didn't find bvc with NSEI %d%s",
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200842 nsei, VTY_NEWLINE);
843 return CMD_WARNING;
844 }
845
Harald Welte560bdb32020-12-04 22:24:47 +0100846 state = &bvc->patch_state;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200847
848 osmo_clock_gettime(CLOCK_MONOTONIC, &ts);
849 now = ts.tv_sec;
850
851 if (match == MATCH_STALE) {
Harald Welte560bdb32020-12-04 22:24:47 +0100852 found = gbproxy_remove_stale_link_infos(bvc, now);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200853 if (found)
854 vty_out(vty, "Deleted %d stale logical link%s%s",
855 found, found == 1 ? "" : "s", VTY_NEWLINE);
856 } else {
857 llist_for_each_entry_safe(link_info, nxt,
858 &state->logical_links, list) {
859 if (!link_info->is_deregistered)
860 continue;
861
Harald Welte560bdb32020-12-04 22:24:47 +0100862 gbproxy_delete_link_info(bvc, link_info);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200863 found += 1;
864 }
865 }
866
867 if (found)
868 vty_out(vty, "Deleted %d %s logical link%s%s",
869 found, argv[1], found == 1 ? "" : "s", VTY_NEWLINE);
870
871 return CMD_SUCCESS;
872}
873
874/*
875 * legacy commands to provide an upgrade path from "broken" releases
876 * or pre-releases
877 */
878DEFUN_DEPRECATED(cfg_gbproxy_broken_apn_match,
879 cfg_gbproxy_broken_apn_match_cmd,
880 "core-access-point-name none match-imsi .REGEXP",
881 GBPROXY_CORE_APN_STR GBPROXY_MATCH_IMSI_STR "Remove APN\n"
882 "Patch MS related information elements on match only\n"
883 "Route to the secondary SGSN on match only\n"
884 "Regular expression for the IMSI match\n")
885{
886 const char *filter = argv[0];
887 const char *err_msg = NULL;
888 struct gbproxy_match *match;
889 enum gbproxy_match_id match_id = get_string_value(match_ids, "patching");
890
891 /* apply APN none */
892 set_core_apn(vty, "");
893
894 /* do the matching... with copy and paste */
895 OSMO_ASSERT(match_id >= GBPROX_MATCH_PATCHING &&
896 match_id < GBPROX_MATCH_LAST);
897 match = &g_cfg->matches[match_id];
898
899 if (gbproxy_set_patch_filter(match, filter, &err_msg) != 0) {
900 vty_out(vty, "Match expression invalid: %s%s",
901 err_msg, VTY_NEWLINE);
902 return CMD_WARNING;
903 }
904
905 g_cfg->acquire_imsi = true;
906
907 return CMD_SUCCESS;
908}
909
910#define GBPROXY_TLLI_LIST_STR "Set TLLI list parameters\n"
911#define GBPROXY_MAX_LEN_STR "Limit list length\n"
912DEFUN_DEPRECATED(cfg_gbproxy_depr_tlli_list_max_len,
913 cfg_gbproxy_depr_tlli_list_max_len_cmd,
914 "tlli-list max-length <1-99999>",
915 GBPROXY_TLLI_LIST_STR GBPROXY_MAX_LEN_STR
916 "Maximum number of TLLIs in the list\n")
917{
918 g_cfg->tlli_max_len = atoi(argv[0]);
919
920 return CMD_SUCCESS;
921}
922
923int gbproxy_vty_init(void)
924{
925 install_element_ve(&show_gbproxy_cmd);
926 install_element_ve(&show_gbproxy_links_cmd);
Daniel Willmann8f407b12020-12-02 19:33:50 +0100927 install_element_ve(&logging_fltr_bvc_cmd);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200928
929 install_element(ENABLE_NODE, &delete_gb_bvci_cmd);
930 install_element(ENABLE_NODE, &delete_gb_nsei_cmd);
931 install_element(ENABLE_NODE, &delete_gb_link_by_id_cmd);
932 install_element(ENABLE_NODE, &delete_gb_link_cmd);
933
934 install_element(CONFIG_NODE, &cfg_gbproxy_cmd);
935 install_node(&gbproxy_node, config_write_gbproxy);
936 install_element(GBPROXY_NODE, &cfg_nsip_sgsn_nsei_cmd);
937 install_element(GBPROXY_NODE, &cfg_gbproxy_core_mcc_cmd);
938 install_element(GBPROXY_NODE, &cfg_gbproxy_core_mnc_cmd);
939 install_element(GBPROXY_NODE, &cfg_gbproxy_match_imsi_cmd);
940 install_element(GBPROXY_NODE, &cfg_gbproxy_core_apn_cmd);
941 install_element(GBPROXY_NODE, &cfg_gbproxy_secondary_sgsn_cmd);
942 install_element(GBPROXY_NODE, &cfg_gbproxy_patch_ptmsi_cmd);
943 install_element(GBPROXY_NODE, &cfg_gbproxy_acquire_imsi_cmd);
944 install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_clean_stale_timer_cmd);
945 install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_max_age_cmd);
946 install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_max_len_cmd);
947 install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_keep_mode_cmd);
948 install_element(GBPROXY_NODE, &cfg_gbproxy_link_stored_msgs_max_len_cmd);
949 install_element(GBPROXY_NODE, &cfg_gbproxy_no_core_mcc_cmd);
950 install_element(GBPROXY_NODE, &cfg_gbproxy_no_core_mnc_cmd);
951 install_element(GBPROXY_NODE, &cfg_gbproxy_no_match_imsi_cmd);
952 install_element(GBPROXY_NODE, &cfg_gbproxy_no_core_apn_cmd);
953 install_element(GBPROXY_NODE, &cfg_gbproxy_no_secondary_sgsn_cmd);
954 install_element(GBPROXY_NODE, &cfg_gbproxy_no_patch_ptmsi_cmd);
955 install_element(GBPROXY_NODE, &cfg_gbproxy_no_acquire_imsi_cmd);
956 install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_no_clean_stale_timer_cmd);
957 install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_no_max_age_cmd);
958 install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_no_max_len_cmd);
959 install_element(GBPROXY_NODE, &cfg_gbproxy_link_no_stored_msgs_max_len_cmd);
960
961 /* broken or deprecated to allow an upgrade path */
962 install_element(GBPROXY_NODE, &cfg_gbproxy_broken_apn_match_cmd);
963 install_element(GBPROXY_NODE, &cfg_gbproxy_depr_tlli_list_max_len_cmd);
964
965 return 0;
966}
967
968int gbproxy_parse_config(const char *config_file, struct gbproxy_config *cfg)
969{
970 int rc;
971
972 g_cfg = cfg;
973 rc = vty_read_config_file(config_file, NULL);
974 if (rc < 0) {
975 fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file);
976 return rc;
977 }
978
979 return 0;
980}