blob: 7ae65d20e5b11f425fe440ff62f357128b39ad0f [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
41#include <osmocom/sgsn/debug.h>
42#include <osmocom/sgsn/gb_proxy.h>
43#include <osmocom/sgsn/gprs_utils.h>
44#include <osmocom/sgsn/vty.h>
45
46#include <osmocom/vty/command.h>
Daniel Willmann8f407b12020-12-02 19:33:50 +010047#include <osmocom/vty/logging.h>
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +020048#include <osmocom/vty/vty.h>
49#include <osmocom/vty/misc.h>
50
Daniel Willmann2c758f22021-01-18 17:10:51 +010051#define GBPROXY_STR "Display information about the Gb proxy\n"
Daniel Willmannee834af2020-12-14 16:22:39 +010052#define NRI_STR "Mapping of Network Resource Indicators to this SGSN, for SGSN pooling\n"
53#define NULL_NRI_STR "Define NULL-NRI values that cause re-assignment of an MS to a different SGSN, for SGSN pooling.\n"
54#define NRI_FIRST_LAST_STR "First value of the NRI value range, should not surpass the configured 'nri bitlen'.\n" \
55 "Last value of the NRI value range, should not surpass the configured 'nri bitlen' and be larger than the" \
56 " first value; if omitted, apply only the first value.\n"
57#define NRI_ARGS_TO_STR_FMT "%s%s%s"
58#define NRI_ARGS_TO_STR_ARGS(ARGC, ARGV) ARGV[0], (ARGC>1)? ".." : "", (ARGC>1)? ARGV[1] : ""
59#define NRI_WARN(SGSN, FORMAT, args...) do { \
60 vty_out(vty, "%% Warning: NSE(%05d/SGSN): " FORMAT "%s", (SGSN)->nse->nsei, ##args, VTY_NEWLINE); \
61 LOGP(DLBSSGP, LOGL_ERROR, "NSE(%05d/SGSN): " FORMAT "\n", (SGSN)->nse->nsei, ##args); \
62 } while (0)
Harald Weltee5209642020-12-05 19:59:45 +010063
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +020064static struct gbproxy_config *g_cfg = NULL;
65
66/*
67 * vty code for gbproxy below
68 */
69static struct cmd_node gbproxy_node = {
70 GBPROXY_NODE,
71 "%s(config-gbproxy)# ",
72 1,
73};
74
Harald Welte560bdb32020-12-04 22:24:47 +010075static void gbprox_vty_print_bvc(struct vty *vty, struct gbproxy_bvc *bvc)
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +020076{
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +020077
Harald Weltec6ecfad2020-12-12 14:17:51 +010078 if (bvc->bvci == 0) {
79 vty_out(vty, "NSEI %5u, SIG-BVCI %5u [%s]%s", bvc->nse->nsei, bvc->bvci,
80 osmo_fsm_inst_state_name(bvc->fi), VTY_NEWLINE);
81 } else {
82 struct gprs_ra_id raid;
83 gsm48_parse_ra(&raid, bvc->ra);
84 vty_out(vty, "NSEI %5u, PTP-BVCI %5u, RAI %s [%s]%s", bvc->nse->nsei, bvc->bvci,
85 osmo_rai_name(&raid), osmo_fsm_inst_state_name(bvc->fi), VTY_NEWLINE);
86 }
87}
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +020088
Harald Weltec6ecfad2020-12-12 14:17:51 +010089static void gbproxy_vty_print_nse(struct vty *vty, struct gbproxy_nse *nse, bool show_stats)
90{
91 struct gbproxy_bvc *bvc;
92 int j;
93
94 hash_for_each(nse->bvcs, j, bvc, list) {
95 gbprox_vty_print_bvc(vty, bvc);
96
97 if (show_stats)
98 vty_out_rate_ctr_group(vty, " ", bvc->ctrg);
99 }
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200100}
101
Harald Welte9151b4a2020-12-12 15:16:43 +0100102static void gbproxy_vty_print_cell(struct vty *vty, struct gbproxy_cell *cell, bool show_stats)
103{
104 struct gprs_ra_id raid;
105 gsm48_parse_ra(&raid, cell->ra);
106 unsigned int num_sgsn_bvc = 0;
107 unsigned int i;
108
109 vty_out(vty, "BVCI %5u RAI %s: ", cell->bvci, osmo_rai_name(&raid));
110 if (cell->bss_bvc)
111 vty_out(vty, "BSS NSEI %5u, SGSN NSEI ", cell->bss_bvc->nse->nsei);
112 else
113 vty_out(vty, "BSS NSEI <none>, SGSN NSEI ");
114
115 for (i = 0; i < ARRAY_SIZE(cell->sgsn_bvc); i++) {
116 struct gbproxy_bvc *sgsn_bvc = cell->sgsn_bvc[i];
117 if (sgsn_bvc) {
118 vty_out(vty, "%5u ", sgsn_bvc->nse->nsei);
119 num_sgsn_bvc++;
120 }
121 }
122 if (num_sgsn_bvc)
123 vty_out(vty, "%s", VTY_NEWLINE);
124 else
125 vty_out(vty, "<none>%s", VTY_NEWLINE);
126}
127
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200128static int config_write_gbproxy(struct vty *vty)
129{
Daniel Willmannee834af2020-12-14 16:22:39 +0100130 struct osmo_nri_range *r;
Harald Weltee5209642020-12-05 19:59:45 +0100131
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200132 vty_out(vty, "gbproxy%s", VTY_NEWLINE);
133
Harald Welte209dc9f2020-12-12 19:02:16 +0100134 if (g_cfg->pool.bvc_fc_ratio != 100)
135 vty_out(vty, " pool bvc-flow-control-ratio %u%s", g_cfg->pool.bvc_fc_ratio, VTY_NEWLINE);
136
Daniel Willmannee834af2020-12-14 16:22:39 +0100137 if (g_cfg->pool.nri_bitlen != OSMO_NRI_BITLEN_DEFAULT)
138 vty_out(vty, " nri bitlen %u%s", g_cfg->pool.nri_bitlen, VTY_NEWLINE);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200139
Daniel Willmannee834af2020-12-14 16:22:39 +0100140 llist_for_each_entry(r, &g_cfg->pool.null_nri_ranges->entries, entry) {
141 vty_out(vty, " nri null add %d", r->first);
142 if (r->first != r->last)
143 vty_out(vty, " %d", r->last);
144 vty_out(vty, "%s", VTY_NEWLINE);
145 }
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200146 return CMD_SUCCESS;
147}
148
149DEFUN(cfg_gbproxy,
150 cfg_gbproxy_cmd,
151 "gbproxy",
152 "Configure the Gb proxy")
153{
154 vty->node = GBPROXY_NODE;
155 return CMD_SUCCESS;
156}
157
Daniel Willmannee834af2020-12-14 16:22:39 +0100158/* VTY code for SGSN (pool) configuration */
Harald Weltee5209642020-12-05 19:59:45 +0100159extern const struct bssgp_bvc_fsm_ops sgsn_sig_bvc_fsm_ops;
160#include <osmocom/gprs/protocol/gsm_08_18.h>
161
Daniel Willmannee834af2020-12-14 16:22:39 +0100162static struct cmd_node sgsn_node = {
163 SGSN_NODE,
164 "%s(config-sgsn)# ",
165 1,
166};
167
168static void sgsn_write_nri(struct vty *vty, struct gbproxy_sgsn *sgsn, bool verbose)
169{
170 struct osmo_nri_range *r;
171
172 if (verbose) {
173 vty_out(vty, "sgsn nsei %d%s", sgsn->nse->nsei, VTY_NEWLINE);
174 if (llist_empty(&sgsn->pool.nri_ranges->entries)) {
175 vty_out(vty, " %% no NRI mappings%s", VTY_NEWLINE);
176 return;
177 }
178 }
179
180 llist_for_each_entry(r, &sgsn->pool.nri_ranges->entries, entry) {
181 if (osmo_nri_range_validate(r, 255))
182 vty_out(vty, " %% INVALID RANGE:");
183 vty_out(vty, " nri add %d", r->first);
184 if (r->first != r->last)
185 vty_out(vty, " %d", r->last);
186 vty_out(vty, "%s", VTY_NEWLINE);
187 }
188}
189
190static void write_sgsn(struct vty *vty, struct gbproxy_sgsn *sgsn)
191{
192 vty_out(vty, "sgsn nsei %u%s", sgsn->nse->nsei, VTY_NEWLINE);
Daniel Willmanna648f3c2020-12-28 18:07:27 +0100193 vty_out(vty, " name %s%s", sgsn->name, VTY_NEWLINE);
Daniel Willmannee834af2020-12-14 16:22:39 +0100194 vty_out(vty, " %sallow-attach%s", sgsn->pool.allow_attach ? "" : "no ", VTY_NEWLINE);
195 sgsn_write_nri(vty, sgsn, false);
196}
197
198static int config_write_sgsn(struct vty *vty)
199{
200 struct gbproxy_sgsn *sgsn;
201
202 llist_for_each_entry(sgsn, &g_cfg->sgsns, list)
203 write_sgsn(vty, sgsn);
204
205 return CMD_SUCCESS;
206}
207
208DEFUN(cfg_sgsn_nsei,
209 cfg_sgsn_nsei_cmd,
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200210 "sgsn nsei <0-65534>",
Daniel Willmannee834af2020-12-14 16:22:39 +0100211 "Configure the SGSN\n"
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200212 "NSEI to be used in the connection with the SGSN\n"
213 "The NSEI\n")
214{
Harald Weltee5209642020-12-05 19:59:45 +0100215 uint32_t features = 0; // FIXME: make configurable
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200216 unsigned int nsei = atoi(argv[0]);
Daniel Willmannee834af2020-12-14 16:22:39 +0100217 unsigned int num_sgsn = llist_count(&g_cfg->sgsns);
218 struct gbproxy_sgsn *sgsn;
Harald Weltee5209642020-12-05 19:59:45 +0100219 struct gbproxy_nse *nse;
220 struct gbproxy_bvc *bvc;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200221
Daniel Willmannee834af2020-12-14 16:22:39 +0100222 if (num_sgsn >= GBPROXY_MAX_NR_SGSN) {
223 vty_out(vty, "%% Too many SGSN NSE defined (%d), increase GBPROXY_MAX_NR_SGSN%s",
224 num_sgsn, VTY_NEWLINE);
225 return CMD_WARNING;
226 }
227
228 /* This will have created the gbproxy_nse as well */
229 sgsn = gbproxy_sgsn_by_nsei_or_new(g_cfg, nsei);
230 if (!sgsn)
Harald Weltee5209642020-12-05 19:59:45 +0100231 goto free_nothing;
Daniel Willmannee834af2020-12-14 16:22:39 +0100232 nse = sgsn->nse;
233 if (num_sgsn > 1 && g_cfg->pool.nri_bitlen == 0)
234 vty_out(vty, "%% Multiple SGSNs defined, but no pooling enabled%s", VTY_NEWLINE);
235
Harald Weltee5209642020-12-05 19:59:45 +0100236
237 if (!gbproxy_bvc_by_bvci(nse, 0)) {
238 uint8_t cause = BSSGP_CAUSE_OML_INTERV;
239 bvc = gbproxy_bvc_alloc(nse, 0);
240 if (!bvc)
Daniel Willmannee834af2020-12-14 16:22:39 +0100241 goto free_sgsn;
Harald Weltee5209642020-12-05 19:59:45 +0100242 bvc->fi = bssgp_bvc_fsm_alloc_sig_bss(bvc, nse->cfg->nsi, nsei, features);
243 if (!bvc->fi)
244 goto free_bvc;
245 bssgp_bvc_fsm_set_ops(bvc->fi, &sgsn_sig_bvc_fsm_ops, bvc);
246 osmo_fsm_inst_dispatch(bvc->fi, BSSGP_BVCFSM_E_REQ_RESET, &cause);
247 }
248
Daniel Willmannee834af2020-12-14 16:22:39 +0100249 vty->node = SGSN_NODE;
250 vty->index = sgsn;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200251 return CMD_SUCCESS;
Harald Weltee5209642020-12-05 19:59:45 +0100252
253free_bvc:
254 gbproxy_bvc_free(bvc);
Daniel Willmannee834af2020-12-14 16:22:39 +0100255free_sgsn:
256 gbproxy_sgsn_free(sgsn);
Harald Weltee5209642020-12-05 19:59:45 +0100257free_nothing:
258 vty_out(vty, "%% Unable to create NSE for NSEI=%05u%s", nsei, VTY_NEWLINE);
259 return CMD_WARNING;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200260}
261
Daniel Willmanna648f3c2020-12-28 18:07:27 +0100262DEFUN(cfg_sgsn_name,
263 cfg_sgsn_name_cmd,
264 "name NAME",
265 "Configure the SGSN\n"
266 "Name the SGSN\n"
267 "The name\n")
268{
269 struct gbproxy_sgsn *sgsn = vty->index;
270 const char *name = argv[0];
271
272
273 osmo_talloc_replace_string(sgsn, &sgsn->name, name);
274 if (!sgsn->name) {
275 vty_out(vty, "%% Unable to set name for SGSN with nsei %05u%s", sgsn->nse->nsei, VTY_NEWLINE);
276 return CMD_WARNING;
277 }
278
279 return CMD_SUCCESS;
280}
281
Daniel Willmannee834af2020-12-14 16:22:39 +0100282DEFUN_ATTR(cfg_sgsn_nri_add, cfg_sgsn_nri_add_cmd,
283 "nri add <0-32767> [<0-32767>]",
284 NRI_STR "Add NRI value or range to the NRI mapping for this MSC\n"
285 NRI_FIRST_LAST_STR,
286 CMD_ATTR_IMMEDIATE)
287{
288 struct gbproxy_sgsn *sgsn = vty->index;
289 struct gbproxy_sgsn *other_sgsn;
290 bool before;
291 int rc;
292 const char *message;
293 struct osmo_nri_range add_range;
294
295 rc = osmo_nri_ranges_vty_add(&message, &add_range, sgsn->pool.nri_ranges, argc, argv, g_cfg->pool.nri_bitlen);
296 if (message) {
297 NRI_WARN(sgsn, "%s: " NRI_ARGS_TO_STR_FMT, message, NRI_ARGS_TO_STR_ARGS(argc, argv));
298 }
299 if (rc < 0)
300 return CMD_WARNING;
301
302 /* Issue a warning about NRI range overlaps (but still allow them).
303 * Overlapping ranges will map to whichever SGSN comes fist in the gbproxy_config->sgsns llist,
304 * which should be the first one defined in the config */
305 before = true;
306
307 llist_for_each_entry(other_sgsn, &g_cfg->sgsns, list) {
308 if (other_sgsn == sgsn) {
309 before = false;
310 continue;
311 }
312 if (osmo_nri_range_overlaps_ranges(&add_range, other_sgsn->pool.nri_ranges)) {
313 uint16_t nsei = sgsn->nse->nsei;
314 uint16_t other_nsei = other_sgsn->nse->nsei;
315 NRI_WARN(sgsn, "NRI range [%d..%d] overlaps between NSE %05d and NSE %05d."
316 " For overlaps, NSE %05d has higher priority than NSE %05d",
317 add_range.first, add_range.last, nsei, other_nsei,
318 before ? other_nsei : nsei, before ? nsei : other_nsei);
319 }
320 }
321 return CMD_SUCCESS;
322}
323
324DEFUN_ATTR(cfg_sgsn_nri_del, cfg_sgsn_nri_del_cmd,
325 "nri del <0-32767> [<0-32767>]",
326 NRI_STR "Remove NRI value or range from the NRI mapping for this MSC\n"
327 NRI_FIRST_LAST_STR,
328 CMD_ATTR_IMMEDIATE)
329{
330 struct gbproxy_sgsn *sgsn = vty->index;
331 int rc;
332 const char *message;
333
334 rc = osmo_nri_ranges_vty_del(&message, NULL, sgsn->pool.nri_ranges, argc, argv);
335 if (message) {
336 NRI_WARN(sgsn, "%s: " NRI_ARGS_TO_STR_FMT, message, NRI_ARGS_TO_STR_ARGS(argc, argv));
337 }
338 if (rc < 0)
339 return CMD_WARNING;
340 return CMD_SUCCESS;
341}
342
343DEFUN_ATTR(cfg_sgsn_allow_attach, cfg_sgsn_allow_attach_cmd,
344 "allow-attach",
345 "Allow this SGSN to attach new subscribers (default).\n",
346 CMD_ATTR_IMMEDIATE)
347{
348 struct gbproxy_sgsn *sgsn = vty->index;
349 sgsn->pool.allow_attach = true;
350 return CMD_SUCCESS;
351}
352
353DEFUN_ATTR(cfg_sgsn_no_allow_attach, cfg_sgsn_no_allow_attach_cmd,
354 "no allow-attach",
355 NO_STR
356 "Do not assign new subscribers to this MSC."
357 " Useful if an MSC in an MSC pool is configured to off-load subscribers."
358 " The MSC will still be operational for already IMSI-Attached subscribers,"
359 " but the NAS node selection function will skip this MSC for new subscribers\n",
360 CMD_ATTR_IMMEDIATE)
361{
362 struct gbproxy_sgsn *sgsn = vty->index;
363 sgsn->pool.allow_attach = false;
364 return CMD_SUCCESS;
365}
366
Daniel Willmann182a0b62021-01-18 13:37:40 +0100367DEFUN(sgsn_show_nri_all, show_nri_all_cmd,
Daniel Willmannee834af2020-12-14 16:22:39 +0100368 "show nri all",
369 SHOW_STR NRI_STR "Show all SGSNs\n")
370{
371 struct gbproxy_sgsn *sgsn;
372
373 llist_for_each_entry(sgsn, &g_cfg->sgsns, list)
374 sgsn_write_nri(vty, sgsn, true);
375
376 return CMD_SUCCESS;
377}
378
Daniel Willmann182a0b62021-01-18 13:37:40 +0100379DEFUN(show_nri_nsei, show_nri_nsei_cmd,
Daniel Willmannee834af2020-12-14 16:22:39 +0100380 "show nri nsei <0-65535>",
381 SHOW_STR NRI_STR "Identify SGSN by NSEI\n"
382 "NSEI of the SGSN\n")
383{
384 struct gbproxy_sgsn *sgsn;
385 int nsei = atoi(argv[0]);
386
387 sgsn = gbproxy_sgsn_by_nsei(g_cfg, nsei);
388 if (!sgsn) {
389 vty_out(vty, "%% No SGSN with found for NSEI %05d%s", nsei, VTY_NEWLINE);
390 return CMD_SUCCESS;
391 }
392 sgsn_write_nri(vty, sgsn, true);
393
394 return CMD_SUCCESS;
395}
396
Harald Welte209dc9f2020-12-12 19:02:16 +0100397DEFUN(cfg_pool_bvc_fc_ratio,
398 cfg_pool_bvc_fc_ratio_cmd,
399 "pool bvc-flow-control-ratio <1-100>",
400 "SGSN Pool related configuration\n"
401 "Ratio of BSS-advertised bucket size + leak rate advertised to each SGSN\n"
402 "Ratio of BSS-advertised bucket size + leak rate advertised to each SGSN (Percent)\n")
403{
404 g_cfg->pool.bvc_fc_ratio = atoi(argv[0]);
405 return CMD_SUCCESS;
406}
Daniel Willmannee834af2020-12-14 16:22:39 +0100407DEFUN_ATTR(cfg_gbproxy_nri_bitlen,
408 cfg_gbproxy_nri_bitlen_cmd,
409 "nri bitlen <0-15>",
410 NRI_STR
411 "Set number of bits that an NRI has, to extract from TMSI identities (always starting just after the TMSI's most significant octet).\n"
412 "bit count (0 disables) pooling)\n",
413 CMD_ATTR_IMMEDIATE)
414{
415 g_cfg->pool.nri_bitlen = atoi(argv[0]);
416
417 if (llist_count(&g_cfg->sgsns) > 1 && g_cfg->pool.nri_bitlen == 0)
418 vty_out(vty, "%% Pooling disabled, but multiple SGSNs defined%s", VTY_NEWLINE);
419
420 /* TODO: Verify all nri ranges and warn on mismatch */
421
422 return CMD_SUCCESS;
423}
424
425DEFUN_ATTR(cfg_gbproxy_nri_null_add,
426 cfg_gbproxy_nri_null_add_cmd,
427 "nri null add <0-32767> [<0-32767>]",
428 NRI_STR NULL_NRI_STR "Add NULL-NRI value (or range)\n"
429 NRI_FIRST_LAST_STR,
430 CMD_ATTR_IMMEDIATE)
431{
432 int rc;
433 const char *message;
434
435 rc = osmo_nri_ranges_vty_add(&message, NULL, g_cfg->pool.null_nri_ranges, argc, argv,
436 g_cfg->pool.nri_bitlen);
437 if (message) {
438 vty_out(vty, "%% nri null add: %s: " NRI_ARGS_TO_STR_FMT "%s", message, NRI_ARGS_TO_STR_ARGS(argc, argv),
439 VTY_NEWLINE);
440 vty_out(vty, "%s: \n" NRI_ARGS_TO_STR_FMT, message, NRI_ARGS_TO_STR_ARGS(argc, argv));
441 }
442 if (rc < 0)
443 return CMD_WARNING;
444 return CMD_SUCCESS;
445}
446
447DEFUN_ATTR(cfg_gbproxy_nri_null_del,
448 cfg_gbproxy_nri_null_del_cmd,
449 "nri null del <0-32767> [<0-32767>]",
450 NRI_STR NULL_NRI_STR "Remove NRI value or range from the NRI mapping for this MSC\n"
451 NRI_FIRST_LAST_STR,
452 CMD_ATTR_IMMEDIATE)
453{
454 int rc;
455 const char *message;
456 rc = osmo_nri_ranges_vty_del(&message, NULL, g_cfg->pool.null_nri_ranges, argc, argv);
457 if (message) {
458 vty_out(vty, "%% %s: " NRI_ARGS_TO_STR_FMT "%s", message, NRI_ARGS_TO_STR_ARGS(argc, argv),
459 VTY_NEWLINE);
460 }
461 if (rc < 0)
462 return CMD_WARNING;
463 return CMD_SUCCESS;
464}
Harald Welte209dc9f2020-12-12 19:02:16 +0100465
Daniel Willmann8f407b12020-12-02 19:33:50 +0100466static void log_set_bvc_filter(struct log_target *target,
467 const uint16_t *bvci)
468{
469 if (bvci) {
470 uintptr_t bvci_filter = *bvci | BVC_LOG_CTX_FLAG;
471 target->filter_map |= (1 << LOG_FLT_GB_BVC);
472 target->filter_data[LOG_FLT_GB_BVC] = (void *)bvci_filter;
473 } else if (target->filter_data[LOG_FLT_GB_BVC]) {
474 target->filter_map = ~(1 << LOG_FLT_GB_BVC);
475 target->filter_data[LOG_FLT_GB_BVC] = NULL;
476 }
477}
478
479DEFUN(logging_fltr_bvc,
480 logging_fltr_bvc_cmd,
481 "logging filter bvc bvci <0-65535>",
482 LOGGING_STR FILTER_STR
483 "Filter based on BSSGP VC\n"
484 "Identify BVC by BVCI\n"
485 "Numeric identifier\n")
486{
487 struct log_target *tgt;
488 uint16_t id = atoi(argv[0]);
489
490 log_tgt_mutex_lock();
491 tgt = osmo_log_vty2tgt(vty);
492 if (!tgt) {
493 log_tgt_mutex_unlock();
494 return CMD_WARNING;
495 }
496
497 log_set_bvc_filter(tgt, &id);
498 log_tgt_mutex_unlock();
499 return CMD_SUCCESS;
500}
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200501
Harald Weltebefe1c32020-12-12 15:15:34 +0100502DEFUN(show_gbproxy_bvc, show_gbproxy_bvc_cmd, "show gbproxy bvc (bss|sgsn) [stats]",
Daniel Willmann2c758f22021-01-18 17:10:51 +0100503 SHOW_STR GBPROXY_STR
Harald Weltebefe1c32020-12-12 15:15:34 +0100504 "Show BSSGP Virtual Connections\n"
Harald Weltec6ecfad2020-12-12 14:17:51 +0100505 "Display BSS-side BVCs\n"
506 "Display SGSN-side BVCs\n"
507 "Show statistics\n")
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200508{
Daniel Willmanne50550e2020-11-26 18:19:21 +0100509 struct gbproxy_nse *nse;
Harald Weltec6ecfad2020-12-12 14:17:51 +0100510 bool show_stats = argc >= 2;
511 int i;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200512
513 if (show_stats)
514 vty_out_rate_ctr_group(vty, "", g_cfg->ctrg);
515
Harald Weltec6ecfad2020-12-12 14:17:51 +0100516 if (!strcmp(argv[0], "bss")) {
517 hash_for_each(g_cfg->bss_nses, i, nse, list)
518 gbproxy_vty_print_nse(vty, nse, show_stats);
519 } else {
520 hash_for_each(g_cfg->sgsn_nses, i, nse, list)
521 gbproxy_vty_print_nse(vty, nse, show_stats);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200522 }
523 return CMD_SUCCESS;
524}
525
Harald Welte9151b4a2020-12-12 15:16:43 +0100526DEFUN(show_gbproxy_cell, show_gbproxy_cell_cmd, "show gbproxy cell [stats]",
Daniel Willmann2c758f22021-01-18 17:10:51 +0100527 SHOW_STR GBPROXY_STR
Harald Welte9151b4a2020-12-12 15:16:43 +0100528 "Show GPRS Cell Information\n"
529 "Show statistics\n")
530{
531 struct gbproxy_cell *cell;
532 bool show_stats = argc >= 1;
533 int i;
534
535 hash_for_each(g_cfg->cells, i, cell, list)
536 gbproxy_vty_print_cell(vty, cell, show_stats);
537
538 return CMD_SUCCESS;
539}
540
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200541DEFUN(show_gbproxy_links, show_gbproxy_links_cmd, "show gbproxy links",
Daniel Willmann2c758f22021-01-18 17:10:51 +0100542 SHOW_STR GBPROXY_STR "Show logical links\n")
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200543{
Daniel Willmanne50550e2020-11-26 18:19:21 +0100544 struct gbproxy_nse *nse;
Harald Welte8b4c7942020-12-05 10:14:49 +0100545 int i, j;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200546
Harald Welted2fef952020-12-05 00:31:07 +0100547 hash_for_each(g_cfg->bss_nses, i, nse, list) {
Harald Welte560bdb32020-12-04 22:24:47 +0100548 struct gbproxy_bvc *bvc;
Harald Welte8b4c7942020-12-05 10:14:49 +0100549 hash_for_each(nse->bvcs, j, bvc, list) {
Harald Welte560bdb32020-12-04 22:24:47 +0100550 gbprox_vty_print_bvc(vty, bvc);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200551 }
552 }
553 return CMD_SUCCESS;
554}
555
Daniel Willmannf2812fb2021-01-18 16:57:01 +0100556DEFUN(show_gbproxy_tlli_cache, show_gbproxy_tlli_cache_cmd,
557 "show gbproxy tlli-cache",
558 SHOW_STR GBPROXY_STR "Show TLLI cache entries\n")
559{
560 struct gbproxy_tlli_cache_entry *entry;
561 struct timespec now;
562 time_t expiry;
563 int i, count = 0;
564
565 osmo_clock_gettime(CLOCK_MONOTONIC, &now);
566 expiry = now.tv_sec - g_cfg->tlli_cache.timeout;
567
568 vty_out(vty, "TLLI cache timeout %us%s", g_cfg->tlli_cache.timeout, VTY_NEWLINE);
569 hash_for_each(g_cfg->tlli_cache.entries, i, entry, list) {
570 time_t valid = entry->tstamp - expiry;
571 struct gbproxy_nse *nse = entry->nse;
572
573 vty_out(vty, " TLLI %08x -> NSE(%05u/%s) valid %lds%s", entry->tlli, nse->nsei,
574 nse->sgsn_facing ? "SGSN" : "BSS", valid, VTY_NEWLINE);
575 count++;
576 }
577 vty_out(vty, "TLLI cache contains %u entries%s", count, VTY_NEWLINE);
578 return CMD_SUCCESS;
579}
580
581DEFUN(show_gbproxy_imsi_cache, show_gbproxy_imsi_cache_cmd,
582 "show gbproxy imsi-cache",
583 SHOW_STR GBPROXY_STR "Show IMSI cache entries\n")
584{
585 struct gbproxy_imsi_cache_entry *entry;
586 struct timespec now;
587 time_t expiry;
588 int i, count = 0;
589
590 osmo_clock_gettime(CLOCK_MONOTONIC, &now);
591 expiry = now.tv_sec - g_cfg->imsi_cache.timeout;
592
593 vty_out(vty, "IMSI cache timeout %us%s", g_cfg->imsi_cache.timeout, VTY_NEWLINE);
594 hash_for_each(g_cfg->imsi_cache.entries, i, entry, list) {
595 time_t valid = entry->tstamp - expiry;
596 struct gbproxy_nse *nse = entry->nse;
597 vty_out(vty, " IMSI %s -> NSE(%05u/%s): valid %lds%s", entry->imsi, nse->nsei,
598 nse->sgsn_facing ? "SGSN" : "BSS", valid, VTY_NEWLINE);
599 count++;
600 }
601 vty_out(vty, "IMSI cache contains %u entries%s", count, VTY_NEWLINE);
602 return CMD_SUCCESS;
603}
604
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200605DEFUN(delete_gb_bvci, delete_gb_bvci_cmd,
606 "delete-gbproxy-peer <0-65534> bvci <2-65534>",
Harald Welte560bdb32020-12-04 22:24:47 +0100607 "Delete a GBProxy bvc by NSEI and optionally BVCI\n"
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200608 "NSEI number\n"
Harald Welte560bdb32020-12-04 22:24:47 +0100609 "Only delete bvc with a matching BVCI\n"
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200610 "BVCI number\n")
611{
612 const uint16_t nsei = atoi(argv[0]);
613 const uint16_t bvci = atoi(argv[1]);
Harald Weltee5209642020-12-05 19:59:45 +0100614 struct gbproxy_nse *nse = gbproxy_nse_by_nsei(g_cfg, nsei, NSE_F_BSS);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200615 int counter;
616
Harald Weltee5209642020-12-05 19:59:45 +0100617 if (!nse) {
618 vty_out(vty, "NSE not found%s", VTY_NEWLINE);
619 return CMD_WARNING;
620 }
621
622 counter = gbproxy_cleanup_bvcs(nse, bvci);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200623
624 if (counter == 0) {
625 vty_out(vty, "BVC not found%s", VTY_NEWLINE);
626 return CMD_WARNING;
627 }
628
629 return CMD_SUCCESS;
630}
631
632DEFUN(delete_gb_nsei, delete_gb_nsei_cmd,
633 "delete-gbproxy-peer <0-65534> (only-bvc|only-nsvc|all) [dry-run]",
Harald Welte560bdb32020-12-04 22:24:47 +0100634 "Delete a GBProxy bvc by NSEI and optionally BVCI\n"
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200635 "NSEI number\n"
636 "Only delete BSSGP connections (BVC)\n"
637 "Only delete dynamic NS connections (NS-VC)\n"
638 "Delete BVC and dynamic NS connections\n"
639 "Show what would be deleted instead of actually deleting\n"
640 )
641{
642 const uint16_t nsei = atoi(argv[0]);
643 const char *mode = argv[1];
644 int dry_run = argc > 2;
645 int delete_bvc = 0;
646 int delete_nsvc = 0;
647 int counter;
648
649 if (strcmp(mode, "only-bvc") == 0)
650 delete_bvc = 1;
651 else if (strcmp(mode, "only-nsvc") == 0)
652 delete_nsvc = 1;
653 else
654 delete_bvc = delete_nsvc = 1;
655
656 if (delete_bvc) {
Daniel Willmann5b897712020-12-04 17:43:27 +0100657 if (!dry_run) {
Harald Weltee5209642020-12-05 19:59:45 +0100658 struct gbproxy_nse *nse = gbproxy_nse_by_nsei(g_cfg, nsei, NSE_F_BSS);
659 counter = gbproxy_cleanup_bvcs(nse, 0);
Daniel Willmann5b897712020-12-04 17:43:27 +0100660 gbproxy_nse_free(nse);
661 } else {
Daniel Willmanne50550e2020-11-26 18:19:21 +0100662 struct gbproxy_nse *nse;
Harald Welte560bdb32020-12-04 22:24:47 +0100663 struct gbproxy_bvc *bvc;
Harald Welte8b4c7942020-12-05 10:14:49 +0100664 int i, j;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200665 counter = 0;
Harald Welted2fef952020-12-05 00:31:07 +0100666 hash_for_each(g_cfg->bss_nses, i, nse, list) {
Daniel Willmanne50550e2020-11-26 18:19:21 +0100667 if (nse->nsei != nsei)
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200668 continue;
Harald Welte8b4c7942020-12-05 10:14:49 +0100669 hash_for_each(nse->bvcs, j, bvc, list) {
Daniel Willmanne50550e2020-11-26 18:19:21 +0100670 vty_out(vty, "BVC: ");
Harald Welte560bdb32020-12-04 22:24:47 +0100671 gbprox_vty_print_bvc(vty, bvc);
Daniel Willmanne50550e2020-11-26 18:19:21 +0100672 counter += 1;
673 }
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200674 }
675 }
676 vty_out(vty, "%sDeleted %d BVC%s",
677 dry_run ? "Not " : "", counter, VTY_NEWLINE);
678 }
679
680 if (delete_nsvc) {
Alexander Couzens951e1332020-09-22 13:21:46 +0200681 struct gprs_ns2_inst *nsi = g_cfg->nsi;
682 struct gprs_ns2_nse *nse;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200683
Alexander Couzens951e1332020-09-22 13:21:46 +0200684 nse = gprs_ns2_nse_by_nsei(nsi, nsei);
685 if (!nse) {
686 vty_out(vty, "NSEI not found%s", VTY_NEWLINE);
687 return CMD_WARNING;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200688 }
Alexander Couzens951e1332020-09-22 13:21:46 +0200689
690 /* TODO: We should NOT delete a persistent NSEI/NSVC as soon as we can check for these */
691 if (!dry_run)
692 gprs_ns2_free_nse(nse);
693
694 vty_out(vty, "%sDeleted NS-VCs for NSEI %d%s",
695 dry_run ? "Not " : "", nsei, VTY_NEWLINE);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200696 }
697
698 return CMD_SUCCESS;
699}
700
Daniel Willmannb387c1e2020-12-27 18:14:39 +0100701/* Only for ttcn3 testing */
702DEFUN_HIDDEN(sgsn_pool_nsf_fixed, sgsn_pool_nsf_fixed_cmd,
703 "sgsn-pool nsf fixed NAME",
704 "SGSN pooling: load balancing across multiple SGSNs.\n"
705 "Customize the Network Selection Function.\n"
706 "Set a fixed SGSN to use (for testing).\n"
707 "The name of the SGSN to use.\n")
708{
709 const char *name = argv[0];
710 struct gbproxy_sgsn *sgsn = gbproxy_sgsn_by_name(g_cfg, name);
711
712 if (!sgsn) {
713 vty_out(vty, "%% Could not find SGSN with name %s%s", name, VTY_NEWLINE);
714 return CMD_WARNING;
715 }
716
717 g_cfg->pool.nsf_override = sgsn;
718 return CMD_SUCCESS;
719}
720
721DEFUN_HIDDEN(sgsn_pool_nsf_normal, sgsn_pool_nsf_normal_cmd,
722 "sgsn-pool nsf normal",
723 "SGSN pooling: load balancing across multiple SGSNs.\n"
724 "Customize the Network Selection Function.\n"
725 "Reset the NSF back to regular operation (for testing).\n")
726{
727 g_cfg->pool.nsf_override = NULL;
728 return CMD_SUCCESS;
729}
730
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200731int gbproxy_vty_init(void)
732{
Harald Weltebefe1c32020-12-12 15:15:34 +0100733 install_element_ve(&show_gbproxy_bvc_cmd);
Harald Welte9151b4a2020-12-12 15:16:43 +0100734 install_element_ve(&show_gbproxy_cell_cmd);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200735 install_element_ve(&show_gbproxy_links_cmd);
Daniel Willmannf2812fb2021-01-18 16:57:01 +0100736 install_element_ve(&show_gbproxy_tlli_cache_cmd);
737 install_element_ve(&show_gbproxy_imsi_cache_cmd);
Daniel Willmannee834af2020-12-14 16:22:39 +0100738 install_element_ve(&show_nri_all_cmd);
739 install_element_ve(&show_nri_nsei_cmd);
Daniel Willmann8f407b12020-12-02 19:33:50 +0100740 install_element_ve(&logging_fltr_bvc_cmd);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200741
742 install_element(ENABLE_NODE, &delete_gb_bvci_cmd);
743 install_element(ENABLE_NODE, &delete_gb_nsei_cmd);
Daniel Willmannb387c1e2020-12-27 18:14:39 +0100744 install_element(ENABLE_NODE, &sgsn_pool_nsf_fixed_cmd);
745 install_element(ENABLE_NODE, &sgsn_pool_nsf_normal_cmd);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200746
747 install_element(CONFIG_NODE, &cfg_gbproxy_cmd);
748 install_node(&gbproxy_node, config_write_gbproxy);
Harald Welte209dc9f2020-12-12 19:02:16 +0100749 install_element(GBPROXY_NODE, &cfg_pool_bvc_fc_ratio_cmd);
Daniel Willmannee834af2020-12-14 16:22:39 +0100750 install_element(GBPROXY_NODE, &cfg_gbproxy_nri_bitlen_cmd);
751 install_element(GBPROXY_NODE, &cfg_gbproxy_nri_null_add_cmd);
752 install_element(GBPROXY_NODE, &cfg_gbproxy_nri_null_del_cmd);
753
754 install_element(CONFIG_NODE, &cfg_sgsn_nsei_cmd);
755 install_node(&sgsn_node, config_write_sgsn);
Daniel Willmanna648f3c2020-12-28 18:07:27 +0100756 install_element(SGSN_NODE, &cfg_sgsn_name_cmd);
Daniel Willmannee834af2020-12-14 16:22:39 +0100757 install_element(SGSN_NODE, &cfg_sgsn_allow_attach_cmd);
758 install_element(SGSN_NODE, &cfg_sgsn_no_allow_attach_cmd);
759 install_element(SGSN_NODE, &cfg_sgsn_nri_add_cmd);
760 install_element(SGSN_NODE, &cfg_sgsn_nri_del_cmd);
761
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200762
763 return 0;
764}
765
766int gbproxy_parse_config(const char *config_file, struct gbproxy_config *cfg)
767{
768 int rc;
769
770 g_cfg = cfg;
771 rc = vty_read_config_file(config_file, NULL);
772 if (rc < 0) {
773 fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file);
774 return rc;
775 }
776
777 return 0;
778}