blob: fd4692d99edfff693c71f4cbbc7fe00085d11501 [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
Daniel Willmannee834af2020-12-14 16:22:39 +010028#include <osmocom/core/hashtable.h>
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +020029#include <osmocom/core/talloc.h>
Alexander Couzens951e1332020-09-22 13:21:46 +020030#include <osmocom/core/timer.h>
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +020031#include <osmocom/core/rate_ctr.h>
Daniel Willmanna648f3c2020-12-28 18:07:27 +010032#include <osmocom/core/utils.h>
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +020033
Alexander Couzens951e1332020-09-22 13:21:46 +020034#include <osmocom/gprs/gprs_ns2.h>
Harald Weltee5209642020-12-05 19:59:45 +010035#include <osmocom/gprs/bssgp_bvc_fsm.h>
Daniel Willmannee834af2020-12-14 16:22:39 +010036
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +020037#include <osmocom/gsm/apn.h>
Daniel Willmannee834af2020-12-14 16:22:39 +010038#include <osmocom/gsm/gsm23236.h>
39#include <osmocom/gsm/gsm48.h>
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +020040
Oliver Smith29532c22021-01-29 11:13:00 +010041#include "debug.h"
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +020042#include <osmocom/sgsn/gb_proxy.h>
Oliver Smith29532c22021-01-29 11:13:00 +010043#include "vty.h"
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +020044
45#include <osmocom/vty/command.h>
Daniel Willmann8f407b12020-12-02 19:33:50 +010046#include <osmocom/vty/logging.h>
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +020047#include <osmocom/vty/vty.h>
48#include <osmocom/vty/misc.h>
49
Daniel Willmann2c758f22021-01-18 17:10:51 +010050#define GBPROXY_STR "Display information about the Gb proxy\n"
Daniel Willmannee834af2020-12-14 16:22:39 +010051#define NRI_STR "Mapping of Network Resource Indicators to this SGSN, for SGSN pooling\n"
52#define NULL_NRI_STR "Define NULL-NRI values that cause re-assignment of an MS to a different SGSN, for SGSN pooling.\n"
53#define NRI_FIRST_LAST_STR "First value of the NRI value range, should not surpass the configured 'nri bitlen'.\n" \
54 "Last value of the NRI value range, should not surpass the configured 'nri bitlen' and be larger than the" \
55 " first value; if omitted, apply only the first value.\n"
56#define NRI_ARGS_TO_STR_FMT "%s%s%s"
57#define NRI_ARGS_TO_STR_ARGS(ARGC, ARGV) ARGV[0], (ARGC>1)? ".." : "", (ARGC>1)? ARGV[1] : ""
58#define NRI_WARN(SGSN, FORMAT, args...) do { \
59 vty_out(vty, "%% Warning: NSE(%05d/SGSN): " FORMAT "%s", (SGSN)->nse->nsei, ##args, VTY_NEWLINE); \
60 LOGP(DLBSSGP, LOGL_ERROR, "NSE(%05d/SGSN): " FORMAT "\n", (SGSN)->nse->nsei, ##args); \
61 } while (0)
Harald Weltee5209642020-12-05 19:59:45 +010062
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +020063static struct gbproxy_config *g_cfg = NULL;
64
65/*
66 * vty code for gbproxy below
67 */
68static struct cmd_node gbproxy_node = {
69 GBPROXY_NODE,
70 "%s(config-gbproxy)# ",
71 1,
72};
73
Harald Welte560bdb32020-12-04 22:24:47 +010074static void gbprox_vty_print_bvc(struct vty *vty, struct gbproxy_bvc *bvc)
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +020075{
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +020076
Harald Weltec6ecfad2020-12-12 14:17:51 +010077 if (bvc->bvci == 0) {
78 vty_out(vty, "NSEI %5u, SIG-BVCI %5u [%s]%s", bvc->nse->nsei, bvc->bvci,
79 osmo_fsm_inst_state_name(bvc->fi), VTY_NEWLINE);
80 } else {
81 struct gprs_ra_id raid;
82 gsm48_parse_ra(&raid, bvc->ra);
83 vty_out(vty, "NSEI %5u, PTP-BVCI %5u, RAI %s [%s]%s", bvc->nse->nsei, bvc->bvci,
84 osmo_rai_name(&raid), osmo_fsm_inst_state_name(bvc->fi), VTY_NEWLINE);
85 }
86}
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +020087
Harald Weltec6ecfad2020-12-12 14:17:51 +010088static void gbproxy_vty_print_nse(struct vty *vty, struct gbproxy_nse *nse, bool show_stats)
89{
90 struct gbproxy_bvc *bvc;
91 int j;
92
93 hash_for_each(nse->bvcs, j, bvc, list) {
94 gbprox_vty_print_bvc(vty, bvc);
95
96 if (show_stats)
97 vty_out_rate_ctr_group(vty, " ", bvc->ctrg);
98 }
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +020099}
100
Harald Welte9151b4a2020-12-12 15:16:43 +0100101static void gbproxy_vty_print_cell(struct vty *vty, struct gbproxy_cell *cell, bool show_stats)
102{
103 struct gprs_ra_id raid;
104 gsm48_parse_ra(&raid, cell->ra);
105 unsigned int num_sgsn_bvc = 0;
106 unsigned int i;
107
108 vty_out(vty, "BVCI %5u RAI %s: ", cell->bvci, osmo_rai_name(&raid));
109 if (cell->bss_bvc)
110 vty_out(vty, "BSS NSEI %5u, SGSN NSEI ", cell->bss_bvc->nse->nsei);
111 else
112 vty_out(vty, "BSS NSEI <none>, SGSN NSEI ");
113
114 for (i = 0; i < ARRAY_SIZE(cell->sgsn_bvc); i++) {
115 struct gbproxy_bvc *sgsn_bvc = cell->sgsn_bvc[i];
116 if (sgsn_bvc) {
117 vty_out(vty, "%5u ", sgsn_bvc->nse->nsei);
118 num_sgsn_bvc++;
119 }
120 }
121 if (num_sgsn_bvc)
122 vty_out(vty, "%s", VTY_NEWLINE);
123 else
124 vty_out(vty, "<none>%s", VTY_NEWLINE);
125}
126
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200127static int config_write_gbproxy(struct vty *vty)
128{
Daniel Willmannee834af2020-12-14 16:22:39 +0100129 struct osmo_nri_range *r;
Harald Weltee5209642020-12-05 19:59:45 +0100130
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200131 vty_out(vty, "gbproxy%s", VTY_NEWLINE);
132
Harald Welte209dc9f2020-12-12 19:02:16 +0100133 if (g_cfg->pool.bvc_fc_ratio != 100)
134 vty_out(vty, " pool bvc-flow-control-ratio %u%s", g_cfg->pool.bvc_fc_ratio, VTY_NEWLINE);
135
Daniel Willmannee834af2020-12-14 16:22:39 +0100136 if (g_cfg->pool.nri_bitlen != OSMO_NRI_BITLEN_DEFAULT)
137 vty_out(vty, " nri bitlen %u%s", g_cfg->pool.nri_bitlen, VTY_NEWLINE);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200138
Daniel Willmannee834af2020-12-14 16:22:39 +0100139 llist_for_each_entry(r, &g_cfg->pool.null_nri_ranges->entries, entry) {
140 vty_out(vty, " nri null add %d", r->first);
141 if (r->first != r->last)
142 vty_out(vty, " %d", r->last);
143 vty_out(vty, "%s", VTY_NEWLINE);
144 }
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200145 return CMD_SUCCESS;
146}
147
148DEFUN(cfg_gbproxy,
149 cfg_gbproxy_cmd,
150 "gbproxy",
151 "Configure the Gb proxy")
152{
153 vty->node = GBPROXY_NODE;
154 return CMD_SUCCESS;
155}
156
Daniel Willmannee834af2020-12-14 16:22:39 +0100157/* VTY code for SGSN (pool) configuration */
Harald Weltee5209642020-12-05 19:59:45 +0100158extern const struct bssgp_bvc_fsm_ops sgsn_sig_bvc_fsm_ops;
159#include <osmocom/gprs/protocol/gsm_08_18.h>
160
Daniel Willmannee834af2020-12-14 16:22:39 +0100161static struct cmd_node sgsn_node = {
162 SGSN_NODE,
163 "%s(config-sgsn)# ",
164 1,
165};
166
167static void sgsn_write_nri(struct vty *vty, struct gbproxy_sgsn *sgsn, bool verbose)
168{
169 struct osmo_nri_range *r;
170
171 if (verbose) {
172 vty_out(vty, "sgsn nsei %d%s", sgsn->nse->nsei, VTY_NEWLINE);
173 if (llist_empty(&sgsn->pool.nri_ranges->entries)) {
174 vty_out(vty, " %% no NRI mappings%s", VTY_NEWLINE);
175 return;
176 }
177 }
178
179 llist_for_each_entry(r, &sgsn->pool.nri_ranges->entries, entry) {
180 if (osmo_nri_range_validate(r, 255))
181 vty_out(vty, " %% INVALID RANGE:");
182 vty_out(vty, " nri add %d", r->first);
183 if (r->first != r->last)
184 vty_out(vty, " %d", r->last);
185 vty_out(vty, "%s", VTY_NEWLINE);
186 }
187}
188
189static void write_sgsn(struct vty *vty, struct gbproxy_sgsn *sgsn)
190{
191 vty_out(vty, "sgsn nsei %u%s", sgsn->nse->nsei, VTY_NEWLINE);
Daniel Willmanna648f3c2020-12-28 18:07:27 +0100192 vty_out(vty, " name %s%s", sgsn->name, VTY_NEWLINE);
Daniel Willmannee834af2020-12-14 16:22:39 +0100193 vty_out(vty, " %sallow-attach%s", sgsn->pool.allow_attach ? "" : "no ", VTY_NEWLINE);
194 sgsn_write_nri(vty, sgsn, false);
195}
196
197static int config_write_sgsn(struct vty *vty)
198{
199 struct gbproxy_sgsn *sgsn;
200
201 llist_for_each_entry(sgsn, &g_cfg->sgsns, list)
202 write_sgsn(vty, sgsn);
203
204 return CMD_SUCCESS;
205}
206
207DEFUN(cfg_sgsn_nsei,
208 cfg_sgsn_nsei_cmd,
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200209 "sgsn nsei <0-65534>",
Daniel Willmannee834af2020-12-14 16:22:39 +0100210 "Configure the SGSN\n"
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200211 "NSEI to be used in the connection with the SGSN\n"
212 "The NSEI\n")
213{
Harald Weltee5209642020-12-05 19:59:45 +0100214 uint32_t features = 0; // FIXME: make configurable
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200215 unsigned int nsei = atoi(argv[0]);
Daniel Willmannee834af2020-12-14 16:22:39 +0100216 unsigned int num_sgsn = llist_count(&g_cfg->sgsns);
217 struct gbproxy_sgsn *sgsn;
Harald Weltee5209642020-12-05 19:59:45 +0100218 struct gbproxy_nse *nse;
219 struct gbproxy_bvc *bvc;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200220
Daniel Willmannee834af2020-12-14 16:22:39 +0100221 if (num_sgsn >= GBPROXY_MAX_NR_SGSN) {
222 vty_out(vty, "%% Too many SGSN NSE defined (%d), increase GBPROXY_MAX_NR_SGSN%s",
223 num_sgsn, VTY_NEWLINE);
224 return CMD_WARNING;
225 }
226
227 /* This will have created the gbproxy_nse as well */
228 sgsn = gbproxy_sgsn_by_nsei_or_new(g_cfg, nsei);
229 if (!sgsn)
Harald Weltee5209642020-12-05 19:59:45 +0100230 goto free_nothing;
Daniel Willmannee834af2020-12-14 16:22:39 +0100231 nse = sgsn->nse;
232 if (num_sgsn > 1 && g_cfg->pool.nri_bitlen == 0)
233 vty_out(vty, "%% Multiple SGSNs defined, but no pooling enabled%s", VTY_NEWLINE);
234
Harald Weltee5209642020-12-05 19:59:45 +0100235
236 if (!gbproxy_bvc_by_bvci(nse, 0)) {
237 uint8_t cause = BSSGP_CAUSE_OML_INTERV;
238 bvc = gbproxy_bvc_alloc(nse, 0);
239 if (!bvc)
Daniel Willmannee834af2020-12-14 16:22:39 +0100240 goto free_sgsn;
Harald Weltee5209642020-12-05 19:59:45 +0100241 bvc->fi = bssgp_bvc_fsm_alloc_sig_bss(bvc, nse->cfg->nsi, nsei, features);
242 if (!bvc->fi)
243 goto free_bvc;
244 bssgp_bvc_fsm_set_ops(bvc->fi, &sgsn_sig_bvc_fsm_ops, bvc);
245 osmo_fsm_inst_dispatch(bvc->fi, BSSGP_BVCFSM_E_REQ_RESET, &cause);
246 }
247
Daniel Willmannee834af2020-12-14 16:22:39 +0100248 vty->node = SGSN_NODE;
249 vty->index = sgsn;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200250 return CMD_SUCCESS;
Harald Weltee5209642020-12-05 19:59:45 +0100251
252free_bvc:
253 gbproxy_bvc_free(bvc);
Daniel Willmannee834af2020-12-14 16:22:39 +0100254free_sgsn:
255 gbproxy_sgsn_free(sgsn);
Harald Weltee5209642020-12-05 19:59:45 +0100256free_nothing:
257 vty_out(vty, "%% Unable to create NSE for NSEI=%05u%s", nsei, VTY_NEWLINE);
258 return CMD_WARNING;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200259}
260
Daniel Willmanna648f3c2020-12-28 18:07:27 +0100261DEFUN(cfg_sgsn_name,
262 cfg_sgsn_name_cmd,
263 "name NAME",
264 "Configure the SGSN\n"
265 "Name the SGSN\n"
266 "The name\n")
267{
268 struct gbproxy_sgsn *sgsn = vty->index;
269 const char *name = argv[0];
270
271
272 osmo_talloc_replace_string(sgsn, &sgsn->name, name);
273 if (!sgsn->name) {
274 vty_out(vty, "%% Unable to set name for SGSN with nsei %05u%s", sgsn->nse->nsei, VTY_NEWLINE);
275 return CMD_WARNING;
276 }
277
278 return CMD_SUCCESS;
279}
280
Daniel Willmannee834af2020-12-14 16:22:39 +0100281DEFUN_ATTR(cfg_sgsn_nri_add, cfg_sgsn_nri_add_cmd,
282 "nri add <0-32767> [<0-32767>]",
283 NRI_STR "Add NRI value or range to the NRI mapping for this MSC\n"
284 NRI_FIRST_LAST_STR,
285 CMD_ATTR_IMMEDIATE)
286{
287 struct gbproxy_sgsn *sgsn = vty->index;
288 struct gbproxy_sgsn *other_sgsn;
289 bool before;
290 int rc;
291 const char *message;
292 struct osmo_nri_range add_range;
293
294 rc = osmo_nri_ranges_vty_add(&message, &add_range, sgsn->pool.nri_ranges, argc, argv, g_cfg->pool.nri_bitlen);
295 if (message) {
296 NRI_WARN(sgsn, "%s: " NRI_ARGS_TO_STR_FMT, message, NRI_ARGS_TO_STR_ARGS(argc, argv));
297 }
298 if (rc < 0)
299 return CMD_WARNING;
300
301 /* Issue a warning about NRI range overlaps (but still allow them).
302 * Overlapping ranges will map to whichever SGSN comes fist in the gbproxy_config->sgsns llist,
303 * which should be the first one defined in the config */
304 before = true;
305
306 llist_for_each_entry(other_sgsn, &g_cfg->sgsns, list) {
307 if (other_sgsn == sgsn) {
308 before = false;
309 continue;
310 }
311 if (osmo_nri_range_overlaps_ranges(&add_range, other_sgsn->pool.nri_ranges)) {
312 uint16_t nsei = sgsn->nse->nsei;
313 uint16_t other_nsei = other_sgsn->nse->nsei;
314 NRI_WARN(sgsn, "NRI range [%d..%d] overlaps between NSE %05d and NSE %05d."
315 " For overlaps, NSE %05d has higher priority than NSE %05d",
316 add_range.first, add_range.last, nsei, other_nsei,
317 before ? other_nsei : nsei, before ? nsei : other_nsei);
318 }
319 }
320 return CMD_SUCCESS;
321}
322
323DEFUN_ATTR(cfg_sgsn_nri_del, cfg_sgsn_nri_del_cmd,
324 "nri del <0-32767> [<0-32767>]",
325 NRI_STR "Remove NRI value or range from the NRI mapping for this MSC\n"
326 NRI_FIRST_LAST_STR,
327 CMD_ATTR_IMMEDIATE)
328{
329 struct gbproxy_sgsn *sgsn = vty->index;
330 int rc;
331 const char *message;
332
333 rc = osmo_nri_ranges_vty_del(&message, NULL, sgsn->pool.nri_ranges, argc, argv);
334 if (message) {
335 NRI_WARN(sgsn, "%s: " NRI_ARGS_TO_STR_FMT, message, NRI_ARGS_TO_STR_ARGS(argc, argv));
336 }
337 if (rc < 0)
338 return CMD_WARNING;
339 return CMD_SUCCESS;
340}
341
342DEFUN_ATTR(cfg_sgsn_allow_attach, cfg_sgsn_allow_attach_cmd,
343 "allow-attach",
344 "Allow this SGSN to attach new subscribers (default).\n",
345 CMD_ATTR_IMMEDIATE)
346{
347 struct gbproxy_sgsn *sgsn = vty->index;
348 sgsn->pool.allow_attach = true;
349 return CMD_SUCCESS;
350}
351
352DEFUN_ATTR(cfg_sgsn_no_allow_attach, cfg_sgsn_no_allow_attach_cmd,
353 "no allow-attach",
354 NO_STR
355 "Do not assign new subscribers to this MSC."
356 " Useful if an MSC in an MSC pool is configured to off-load subscribers."
357 " The MSC will still be operational for already IMSI-Attached subscribers,"
358 " but the NAS node selection function will skip this MSC for new subscribers\n",
359 CMD_ATTR_IMMEDIATE)
360{
361 struct gbproxy_sgsn *sgsn = vty->index;
362 sgsn->pool.allow_attach = false;
363 return CMD_SUCCESS;
364}
365
Daniel Willmann182a0b62021-01-18 13:37:40 +0100366DEFUN(sgsn_show_nri_all, show_nri_all_cmd,
Daniel Willmannee834af2020-12-14 16:22:39 +0100367 "show nri all",
368 SHOW_STR NRI_STR "Show all SGSNs\n")
369{
370 struct gbproxy_sgsn *sgsn;
371
372 llist_for_each_entry(sgsn, &g_cfg->sgsns, list)
373 sgsn_write_nri(vty, sgsn, true);
374
375 return CMD_SUCCESS;
376}
377
Daniel Willmann182a0b62021-01-18 13:37:40 +0100378DEFUN(show_nri_nsei, show_nri_nsei_cmd,
Daniel Willmannee834af2020-12-14 16:22:39 +0100379 "show nri nsei <0-65535>",
380 SHOW_STR NRI_STR "Identify SGSN by NSEI\n"
381 "NSEI of the SGSN\n")
382{
383 struct gbproxy_sgsn *sgsn;
384 int nsei = atoi(argv[0]);
385
386 sgsn = gbproxy_sgsn_by_nsei(g_cfg, nsei);
387 if (!sgsn) {
388 vty_out(vty, "%% No SGSN with found for NSEI %05d%s", nsei, VTY_NEWLINE);
389 return CMD_SUCCESS;
390 }
391 sgsn_write_nri(vty, sgsn, true);
392
393 return CMD_SUCCESS;
394}
395
Harald Welte209dc9f2020-12-12 19:02:16 +0100396DEFUN(cfg_pool_bvc_fc_ratio,
397 cfg_pool_bvc_fc_ratio_cmd,
398 "pool bvc-flow-control-ratio <1-100>",
399 "SGSN Pool related configuration\n"
400 "Ratio of BSS-advertised bucket size + leak rate advertised to each SGSN\n"
401 "Ratio of BSS-advertised bucket size + leak rate advertised to each SGSN (Percent)\n")
402{
403 g_cfg->pool.bvc_fc_ratio = atoi(argv[0]);
404 return CMD_SUCCESS;
405}
Daniel Willmannee834af2020-12-14 16:22:39 +0100406DEFUN_ATTR(cfg_gbproxy_nri_bitlen,
407 cfg_gbproxy_nri_bitlen_cmd,
408 "nri bitlen <0-15>",
409 NRI_STR
410 "Set number of bits that an NRI has, to extract from TMSI identities (always starting just after the TMSI's most significant octet).\n"
411 "bit count (0 disables) pooling)\n",
412 CMD_ATTR_IMMEDIATE)
413{
414 g_cfg->pool.nri_bitlen = atoi(argv[0]);
415
416 if (llist_count(&g_cfg->sgsns) > 1 && g_cfg->pool.nri_bitlen == 0)
417 vty_out(vty, "%% Pooling disabled, but multiple SGSNs defined%s", VTY_NEWLINE);
418
419 /* TODO: Verify all nri ranges and warn on mismatch */
420
421 return CMD_SUCCESS;
422}
423
424DEFUN_ATTR(cfg_gbproxy_nri_null_add,
425 cfg_gbproxy_nri_null_add_cmd,
426 "nri null add <0-32767> [<0-32767>]",
427 NRI_STR NULL_NRI_STR "Add NULL-NRI value (or range)\n"
428 NRI_FIRST_LAST_STR,
429 CMD_ATTR_IMMEDIATE)
430{
431 int rc;
432 const char *message;
433
434 rc = osmo_nri_ranges_vty_add(&message, NULL, g_cfg->pool.null_nri_ranges, argc, argv,
435 g_cfg->pool.nri_bitlen);
436 if (message) {
437 vty_out(vty, "%% nri null add: %s: " NRI_ARGS_TO_STR_FMT "%s", message, NRI_ARGS_TO_STR_ARGS(argc, argv),
438 VTY_NEWLINE);
439 vty_out(vty, "%s: \n" NRI_ARGS_TO_STR_FMT, message, NRI_ARGS_TO_STR_ARGS(argc, argv));
440 }
441 if (rc < 0)
442 return CMD_WARNING;
443 return CMD_SUCCESS;
444}
445
446DEFUN_ATTR(cfg_gbproxy_nri_null_del,
447 cfg_gbproxy_nri_null_del_cmd,
448 "nri null del <0-32767> [<0-32767>]",
449 NRI_STR NULL_NRI_STR "Remove NRI value or range from the NRI mapping for this MSC\n"
450 NRI_FIRST_LAST_STR,
451 CMD_ATTR_IMMEDIATE)
452{
453 int rc;
454 const char *message;
455 rc = osmo_nri_ranges_vty_del(&message, NULL, g_cfg->pool.null_nri_ranges, argc, argv);
456 if (message) {
457 vty_out(vty, "%% %s: " NRI_ARGS_TO_STR_FMT "%s", message, NRI_ARGS_TO_STR_ARGS(argc, argv),
458 VTY_NEWLINE);
459 }
460 if (rc < 0)
461 return CMD_WARNING;
462 return CMD_SUCCESS;
463}
Harald Welte209dc9f2020-12-12 19:02:16 +0100464
Daniel Willmann8f407b12020-12-02 19:33:50 +0100465static void log_set_bvc_filter(struct log_target *target,
466 const uint16_t *bvci)
467{
468 if (bvci) {
469 uintptr_t bvci_filter = *bvci | BVC_LOG_CTX_FLAG;
470 target->filter_map |= (1 << LOG_FLT_GB_BVC);
471 target->filter_data[LOG_FLT_GB_BVC] = (void *)bvci_filter;
472 } else if (target->filter_data[LOG_FLT_GB_BVC]) {
473 target->filter_map = ~(1 << LOG_FLT_GB_BVC);
474 target->filter_data[LOG_FLT_GB_BVC] = NULL;
475 }
476}
477
478DEFUN(logging_fltr_bvc,
479 logging_fltr_bvc_cmd,
480 "logging filter bvc bvci <0-65535>",
481 LOGGING_STR FILTER_STR
482 "Filter based on BSSGP VC\n"
483 "Identify BVC by BVCI\n"
484 "Numeric identifier\n")
485{
486 struct log_target *tgt;
487 uint16_t id = atoi(argv[0]);
488
489 log_tgt_mutex_lock();
490 tgt = osmo_log_vty2tgt(vty);
491 if (!tgt) {
492 log_tgt_mutex_unlock();
493 return CMD_WARNING;
494 }
495
496 log_set_bvc_filter(tgt, &id);
497 log_tgt_mutex_unlock();
498 return CMD_SUCCESS;
499}
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200500
Harald Weltebefe1c32020-12-12 15:15:34 +0100501DEFUN(show_gbproxy_bvc, show_gbproxy_bvc_cmd, "show gbproxy bvc (bss|sgsn) [stats]",
Daniel Willmann2c758f22021-01-18 17:10:51 +0100502 SHOW_STR GBPROXY_STR
Harald Weltebefe1c32020-12-12 15:15:34 +0100503 "Show BSSGP Virtual Connections\n"
Harald Weltec6ecfad2020-12-12 14:17:51 +0100504 "Display BSS-side BVCs\n"
505 "Display SGSN-side BVCs\n"
506 "Show statistics\n")
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200507{
Daniel Willmanne50550e2020-11-26 18:19:21 +0100508 struct gbproxy_nse *nse;
Harald Weltec6ecfad2020-12-12 14:17:51 +0100509 bool show_stats = argc >= 2;
510 int i;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200511
512 if (show_stats)
513 vty_out_rate_ctr_group(vty, "", g_cfg->ctrg);
514
Harald Weltec6ecfad2020-12-12 14:17:51 +0100515 if (!strcmp(argv[0], "bss")) {
516 hash_for_each(g_cfg->bss_nses, i, nse, list)
517 gbproxy_vty_print_nse(vty, nse, show_stats);
518 } else {
519 hash_for_each(g_cfg->sgsn_nses, i, nse, list)
520 gbproxy_vty_print_nse(vty, nse, show_stats);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200521 }
522 return CMD_SUCCESS;
523}
524
Harald Welte9151b4a2020-12-12 15:16:43 +0100525DEFUN(show_gbproxy_cell, show_gbproxy_cell_cmd, "show gbproxy cell [stats]",
Daniel Willmann2c758f22021-01-18 17:10:51 +0100526 SHOW_STR GBPROXY_STR
Harald Welte9151b4a2020-12-12 15:16:43 +0100527 "Show GPRS Cell Information\n"
528 "Show statistics\n")
529{
530 struct gbproxy_cell *cell;
531 bool show_stats = argc >= 1;
532 int i;
533
534 hash_for_each(g_cfg->cells, i, cell, list)
535 gbproxy_vty_print_cell(vty, cell, show_stats);
536
537 return CMD_SUCCESS;
538}
539
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200540DEFUN(show_gbproxy_links, show_gbproxy_links_cmd, "show gbproxy links",
Daniel Willmann2c758f22021-01-18 17:10:51 +0100541 SHOW_STR GBPROXY_STR "Show logical links\n")
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200542{
Daniel Willmanne50550e2020-11-26 18:19:21 +0100543 struct gbproxy_nse *nse;
Harald Welte8b4c7942020-12-05 10:14:49 +0100544 int i, j;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200545
Harald Welted2fef952020-12-05 00:31:07 +0100546 hash_for_each(g_cfg->bss_nses, i, nse, list) {
Harald Welte560bdb32020-12-04 22:24:47 +0100547 struct gbproxy_bvc *bvc;
Harald Welte8b4c7942020-12-05 10:14:49 +0100548 hash_for_each(nse->bvcs, j, bvc, list) {
Harald Welte560bdb32020-12-04 22:24:47 +0100549 gbprox_vty_print_bvc(vty, bvc);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200550 }
551 }
552 return CMD_SUCCESS;
553}
554
Daniel Willmannf2812fb2021-01-18 16:57:01 +0100555DEFUN(show_gbproxy_tlli_cache, show_gbproxy_tlli_cache_cmd,
556 "show gbproxy tlli-cache",
557 SHOW_STR GBPROXY_STR "Show TLLI cache entries\n")
558{
559 struct gbproxy_tlli_cache_entry *entry;
560 struct timespec now;
561 time_t expiry;
562 int i, count = 0;
563
564 osmo_clock_gettime(CLOCK_MONOTONIC, &now);
565 expiry = now.tv_sec - g_cfg->tlli_cache.timeout;
566
567 vty_out(vty, "TLLI cache timeout %us%s", g_cfg->tlli_cache.timeout, VTY_NEWLINE);
568 hash_for_each(g_cfg->tlli_cache.entries, i, entry, list) {
569 time_t valid = entry->tstamp - expiry;
570 struct gbproxy_nse *nse = entry->nse;
571
572 vty_out(vty, " TLLI %08x -> NSE(%05u/%s) valid %lds%s", entry->tlli, nse->nsei,
573 nse->sgsn_facing ? "SGSN" : "BSS", valid, VTY_NEWLINE);
574 count++;
575 }
576 vty_out(vty, "TLLI cache contains %u entries%s", count, VTY_NEWLINE);
577 return CMD_SUCCESS;
578}
579
580DEFUN(show_gbproxy_imsi_cache, show_gbproxy_imsi_cache_cmd,
581 "show gbproxy imsi-cache",
582 SHOW_STR GBPROXY_STR "Show IMSI cache entries\n")
583{
584 struct gbproxy_imsi_cache_entry *entry;
585 struct timespec now;
586 time_t expiry;
587 int i, count = 0;
588
589 osmo_clock_gettime(CLOCK_MONOTONIC, &now);
590 expiry = now.tv_sec - g_cfg->imsi_cache.timeout;
591
592 vty_out(vty, "IMSI cache timeout %us%s", g_cfg->imsi_cache.timeout, VTY_NEWLINE);
593 hash_for_each(g_cfg->imsi_cache.entries, i, entry, list) {
594 time_t valid = entry->tstamp - expiry;
595 struct gbproxy_nse *nse = entry->nse;
596 vty_out(vty, " IMSI %s -> NSE(%05u/%s): valid %lds%s", entry->imsi, nse->nsei,
597 nse->sgsn_facing ? "SGSN" : "BSS", valid, VTY_NEWLINE);
598 count++;
599 }
600 vty_out(vty, "IMSI cache contains %u entries%s", count, VTY_NEWLINE);
601 return CMD_SUCCESS;
602}
603
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200604DEFUN(delete_gb_bvci, delete_gb_bvci_cmd,
605 "delete-gbproxy-peer <0-65534> bvci <2-65534>",
Harald Welte560bdb32020-12-04 22:24:47 +0100606 "Delete a GBProxy bvc by NSEI and optionally BVCI\n"
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200607 "NSEI number\n"
Harald Welte560bdb32020-12-04 22:24:47 +0100608 "Only delete bvc with a matching BVCI\n"
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200609 "BVCI number\n")
610{
611 const uint16_t nsei = atoi(argv[0]);
612 const uint16_t bvci = atoi(argv[1]);
Harald Weltee5209642020-12-05 19:59:45 +0100613 struct gbproxy_nse *nse = gbproxy_nse_by_nsei(g_cfg, nsei, NSE_F_BSS);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200614 int counter;
615
Harald Weltee5209642020-12-05 19:59:45 +0100616 if (!nse) {
617 vty_out(vty, "NSE not found%s", VTY_NEWLINE);
618 return CMD_WARNING;
619 }
620
621 counter = gbproxy_cleanup_bvcs(nse, bvci);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200622
623 if (counter == 0) {
624 vty_out(vty, "BVC not found%s", VTY_NEWLINE);
625 return CMD_WARNING;
626 }
627
628 return CMD_SUCCESS;
629}
630
631DEFUN(delete_gb_nsei, delete_gb_nsei_cmd,
632 "delete-gbproxy-peer <0-65534> (only-bvc|only-nsvc|all) [dry-run]",
Harald Welte560bdb32020-12-04 22:24:47 +0100633 "Delete a GBProxy bvc by NSEI and optionally BVCI\n"
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200634 "NSEI number\n"
635 "Only delete BSSGP connections (BVC)\n"
636 "Only delete dynamic NS connections (NS-VC)\n"
637 "Delete BVC and dynamic NS connections\n"
638 "Show what would be deleted instead of actually deleting\n"
639 )
640{
641 const uint16_t nsei = atoi(argv[0]);
642 const char *mode = argv[1];
643 int dry_run = argc > 2;
644 int delete_bvc = 0;
645 int delete_nsvc = 0;
646 int counter;
647
648 if (strcmp(mode, "only-bvc") == 0)
649 delete_bvc = 1;
650 else if (strcmp(mode, "only-nsvc") == 0)
651 delete_nsvc = 1;
652 else
653 delete_bvc = delete_nsvc = 1;
654
655 if (delete_bvc) {
Daniel Willmann5b897712020-12-04 17:43:27 +0100656 if (!dry_run) {
Harald Weltee5209642020-12-05 19:59:45 +0100657 struct gbproxy_nse *nse = gbproxy_nse_by_nsei(g_cfg, nsei, NSE_F_BSS);
658 counter = gbproxy_cleanup_bvcs(nse, 0);
Daniel Willmann5b897712020-12-04 17:43:27 +0100659 gbproxy_nse_free(nse);
660 } else {
Daniel Willmanne50550e2020-11-26 18:19:21 +0100661 struct gbproxy_nse *nse;
Harald Welte560bdb32020-12-04 22:24:47 +0100662 struct gbproxy_bvc *bvc;
Harald Welte8b4c7942020-12-05 10:14:49 +0100663 int i, j;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200664 counter = 0;
Harald Welted2fef952020-12-05 00:31:07 +0100665 hash_for_each(g_cfg->bss_nses, i, nse, list) {
Daniel Willmanne50550e2020-11-26 18:19:21 +0100666 if (nse->nsei != nsei)
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200667 continue;
Harald Welte8b4c7942020-12-05 10:14:49 +0100668 hash_for_each(nse->bvcs, j, bvc, list) {
Daniel Willmanne50550e2020-11-26 18:19:21 +0100669 vty_out(vty, "BVC: ");
Harald Welte560bdb32020-12-04 22:24:47 +0100670 gbprox_vty_print_bvc(vty, bvc);
Daniel Willmanne50550e2020-11-26 18:19:21 +0100671 counter += 1;
672 }
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200673 }
674 }
675 vty_out(vty, "%sDeleted %d BVC%s",
676 dry_run ? "Not " : "", counter, VTY_NEWLINE);
677 }
678
679 if (delete_nsvc) {
Alexander Couzens951e1332020-09-22 13:21:46 +0200680 struct gprs_ns2_inst *nsi = g_cfg->nsi;
681 struct gprs_ns2_nse *nse;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200682
Alexander Couzens951e1332020-09-22 13:21:46 +0200683 nse = gprs_ns2_nse_by_nsei(nsi, nsei);
684 if (!nse) {
685 vty_out(vty, "NSEI not found%s", VTY_NEWLINE);
686 return CMD_WARNING;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200687 }
Alexander Couzens951e1332020-09-22 13:21:46 +0200688
689 /* TODO: We should NOT delete a persistent NSEI/NSVC as soon as we can check for these */
690 if (!dry_run)
691 gprs_ns2_free_nse(nse);
692
693 vty_out(vty, "%sDeleted NS-VCs for NSEI %d%s",
694 dry_run ? "Not " : "", nsei, VTY_NEWLINE);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200695 }
696
697 return CMD_SUCCESS;
698}
699
Daniel Willmannb387c1e2020-12-27 18:14:39 +0100700/* Only for ttcn3 testing */
701DEFUN_HIDDEN(sgsn_pool_nsf_fixed, sgsn_pool_nsf_fixed_cmd,
702 "sgsn-pool nsf fixed NAME",
703 "SGSN pooling: load balancing across multiple SGSNs.\n"
704 "Customize the Network Selection Function.\n"
705 "Set a fixed SGSN to use (for testing).\n"
706 "The name of the SGSN to use.\n")
707{
708 const char *name = argv[0];
709 struct gbproxy_sgsn *sgsn = gbproxy_sgsn_by_name(g_cfg, name);
710
711 if (!sgsn) {
712 vty_out(vty, "%% Could not find SGSN with name %s%s", name, VTY_NEWLINE);
713 return CMD_WARNING;
714 }
715
716 g_cfg->pool.nsf_override = sgsn;
717 return CMD_SUCCESS;
718}
719
720DEFUN_HIDDEN(sgsn_pool_nsf_normal, sgsn_pool_nsf_normal_cmd,
721 "sgsn-pool nsf normal",
722 "SGSN pooling: load balancing across multiple SGSNs.\n"
723 "Customize the Network Selection Function.\n"
724 "Reset the NSF back to regular operation (for testing).\n")
725{
726 g_cfg->pool.nsf_override = NULL;
727 return CMD_SUCCESS;
728}
729
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200730int gbproxy_vty_init(void)
731{
Harald Weltebefe1c32020-12-12 15:15:34 +0100732 install_element_ve(&show_gbproxy_bvc_cmd);
Harald Welte9151b4a2020-12-12 15:16:43 +0100733 install_element_ve(&show_gbproxy_cell_cmd);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200734 install_element_ve(&show_gbproxy_links_cmd);
Daniel Willmannf2812fb2021-01-18 16:57:01 +0100735 install_element_ve(&show_gbproxy_tlli_cache_cmd);
736 install_element_ve(&show_gbproxy_imsi_cache_cmd);
Daniel Willmannee834af2020-12-14 16:22:39 +0100737 install_element_ve(&show_nri_all_cmd);
738 install_element_ve(&show_nri_nsei_cmd);
Daniel Willmann8f407b12020-12-02 19:33:50 +0100739 install_element_ve(&logging_fltr_bvc_cmd);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200740
741 install_element(ENABLE_NODE, &delete_gb_bvci_cmd);
742 install_element(ENABLE_NODE, &delete_gb_nsei_cmd);
Daniel Willmannb387c1e2020-12-27 18:14:39 +0100743 install_element(ENABLE_NODE, &sgsn_pool_nsf_fixed_cmd);
744 install_element(ENABLE_NODE, &sgsn_pool_nsf_normal_cmd);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200745
746 install_element(CONFIG_NODE, &cfg_gbproxy_cmd);
747 install_node(&gbproxy_node, config_write_gbproxy);
Harald Welte209dc9f2020-12-12 19:02:16 +0100748 install_element(GBPROXY_NODE, &cfg_pool_bvc_fc_ratio_cmd);
Daniel Willmannee834af2020-12-14 16:22:39 +0100749 install_element(GBPROXY_NODE, &cfg_gbproxy_nri_bitlen_cmd);
750 install_element(GBPROXY_NODE, &cfg_gbproxy_nri_null_add_cmd);
751 install_element(GBPROXY_NODE, &cfg_gbproxy_nri_null_del_cmd);
752
753 install_element(CONFIG_NODE, &cfg_sgsn_nsei_cmd);
754 install_node(&sgsn_node, config_write_sgsn);
Daniel Willmanna648f3c2020-12-28 18:07:27 +0100755 install_element(SGSN_NODE, &cfg_sgsn_name_cmd);
Daniel Willmannee834af2020-12-14 16:22:39 +0100756 install_element(SGSN_NODE, &cfg_sgsn_allow_attach_cmd);
757 install_element(SGSN_NODE, &cfg_sgsn_no_allow_attach_cmd);
758 install_element(SGSN_NODE, &cfg_sgsn_nri_add_cmd);
759 install_element(SGSN_NODE, &cfg_sgsn_nri_del_cmd);
760
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200761
762 return 0;
763}
764
765int gbproxy_parse_config(const char *config_file, struct gbproxy_config *cfg)
766{
767 int rc;
768
769 g_cfg = cfg;
770 rc = vty_read_config_file(config_file, NULL);
771 if (rc < 0) {
772 fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file);
773 return rc;
774 }
775
776 return 0;
777}