blob: 9240d2d13204694fe2d9c20c29c5b8b9277f3314 [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 {
Harald Weltec6ecfad2020-12-12 14:17:51 +010081 vty_out(vty, "NSEI %5u, PTP-BVCI %5u, RAI %s [%s]%s", bvc->nse->nsei, bvc->bvci,
Philipp Maierda3af942021-02-04 21:54:09 +010082 osmo_rai_name(&bvc->cell->id.raid), osmo_fsm_inst_state_name(bvc->fi), VTY_NEWLINE);
Harald Weltec6ecfad2020-12-12 14:17:51 +010083 }
84}
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +020085
Harald Weltec6ecfad2020-12-12 14:17:51 +010086static void gbproxy_vty_print_nse(struct vty *vty, struct gbproxy_nse *nse, bool show_stats)
87{
88 struct gbproxy_bvc *bvc;
89 int j;
90
91 hash_for_each(nse->bvcs, j, bvc, list) {
92 gbprox_vty_print_bvc(vty, bvc);
93
94 if (show_stats)
95 vty_out_rate_ctr_group(vty, " ", bvc->ctrg);
96 }
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +020097}
98
Harald Welte9151b4a2020-12-12 15:16:43 +010099static void gbproxy_vty_print_cell(struct vty *vty, struct gbproxy_cell *cell, bool show_stats)
100{
Harald Welte9151b4a2020-12-12 15:16:43 +0100101 unsigned int num_sgsn_bvc = 0;
102 unsigned int i;
103
Philipp Maierda3af942021-02-04 21:54:09 +0100104 vty_out(vty, "BVCI %5u RAI %s CID %05u: ", cell->bvci, osmo_rai_name(&cell->id.raid), cell->id.cid);
Harald Welte9151b4a2020-12-12 15:16:43 +0100105 if (cell->bss_bvc)
106 vty_out(vty, "BSS NSEI %5u, SGSN NSEI ", cell->bss_bvc->nse->nsei);
107 else
108 vty_out(vty, "BSS NSEI <none>, SGSN NSEI ");
109
110 for (i = 0; i < ARRAY_SIZE(cell->sgsn_bvc); i++) {
111 struct gbproxy_bvc *sgsn_bvc = cell->sgsn_bvc[i];
112 if (sgsn_bvc) {
113 vty_out(vty, "%5u ", sgsn_bvc->nse->nsei);
114 num_sgsn_bvc++;
115 }
116 }
117 if (num_sgsn_bvc)
118 vty_out(vty, "%s", VTY_NEWLINE);
119 else
120 vty_out(vty, "<none>%s", VTY_NEWLINE);
121}
122
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200123static int config_write_gbproxy(struct vty *vty)
124{
Daniel Willmannee834af2020-12-14 16:22:39 +0100125 struct osmo_nri_range *r;
Harald Weltee5209642020-12-05 19:59:45 +0100126
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200127 vty_out(vty, "gbproxy%s", VTY_NEWLINE);
128
Harald Welte209dc9f2020-12-12 19:02:16 +0100129 if (g_cfg->pool.bvc_fc_ratio != 100)
130 vty_out(vty, " pool bvc-flow-control-ratio %u%s", g_cfg->pool.bvc_fc_ratio, VTY_NEWLINE);
131
Daniel Willmannee834af2020-12-14 16:22:39 +0100132 if (g_cfg->pool.nri_bitlen != OSMO_NRI_BITLEN_DEFAULT)
133 vty_out(vty, " nri bitlen %u%s", g_cfg->pool.nri_bitlen, VTY_NEWLINE);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200134
Daniel Willmannee834af2020-12-14 16:22:39 +0100135 llist_for_each_entry(r, &g_cfg->pool.null_nri_ranges->entries, entry) {
136 vty_out(vty, " nri null add %d", r->first);
137 if (r->first != r->last)
138 vty_out(vty, " %d", r->last);
139 vty_out(vty, "%s", VTY_NEWLINE);
140 }
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200141 return CMD_SUCCESS;
142}
143
144DEFUN(cfg_gbproxy,
145 cfg_gbproxy_cmd,
146 "gbproxy",
147 "Configure the Gb proxy")
148{
149 vty->node = GBPROXY_NODE;
150 return CMD_SUCCESS;
151}
152
Daniel Willmannee834af2020-12-14 16:22:39 +0100153/* VTY code for SGSN (pool) configuration */
Harald Weltee5209642020-12-05 19:59:45 +0100154extern const struct bssgp_bvc_fsm_ops sgsn_sig_bvc_fsm_ops;
155#include <osmocom/gprs/protocol/gsm_08_18.h>
156
Daniel Willmannee834af2020-12-14 16:22:39 +0100157static struct cmd_node sgsn_node = {
158 SGSN_NODE,
159 "%s(config-sgsn)# ",
160 1,
161};
162
163static void sgsn_write_nri(struct vty *vty, struct gbproxy_sgsn *sgsn, bool verbose)
164{
165 struct osmo_nri_range *r;
166
167 if (verbose) {
168 vty_out(vty, "sgsn nsei %d%s", sgsn->nse->nsei, VTY_NEWLINE);
169 if (llist_empty(&sgsn->pool.nri_ranges->entries)) {
170 vty_out(vty, " %% no NRI mappings%s", VTY_NEWLINE);
171 return;
172 }
173 }
174
175 llist_for_each_entry(r, &sgsn->pool.nri_ranges->entries, entry) {
176 if (osmo_nri_range_validate(r, 255))
177 vty_out(vty, " %% INVALID RANGE:");
178 vty_out(vty, " nri add %d", r->first);
179 if (r->first != r->last)
180 vty_out(vty, " %d", r->last);
181 vty_out(vty, "%s", VTY_NEWLINE);
182 }
183}
184
185static void write_sgsn(struct vty *vty, struct gbproxy_sgsn *sgsn)
186{
187 vty_out(vty, "sgsn nsei %u%s", sgsn->nse->nsei, VTY_NEWLINE);
Daniel Willmanna648f3c2020-12-28 18:07:27 +0100188 vty_out(vty, " name %s%s", sgsn->name, VTY_NEWLINE);
Daniel Willmannee834af2020-12-14 16:22:39 +0100189 vty_out(vty, " %sallow-attach%s", sgsn->pool.allow_attach ? "" : "no ", VTY_NEWLINE);
190 sgsn_write_nri(vty, sgsn, false);
191}
192
193static int config_write_sgsn(struct vty *vty)
194{
195 struct gbproxy_sgsn *sgsn;
196
197 llist_for_each_entry(sgsn, &g_cfg->sgsns, list)
198 write_sgsn(vty, sgsn);
199
200 return CMD_SUCCESS;
201}
202
203DEFUN(cfg_sgsn_nsei,
204 cfg_sgsn_nsei_cmd,
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200205 "sgsn nsei <0-65534>",
Daniel Willmannee834af2020-12-14 16:22:39 +0100206 "Configure the SGSN\n"
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200207 "NSEI to be used in the connection with the SGSN\n"
208 "The NSEI\n")
209{
Harald Weltee5209642020-12-05 19:59:45 +0100210 uint32_t features = 0; // FIXME: make configurable
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200211 unsigned int nsei = atoi(argv[0]);
Daniel Willmannee834af2020-12-14 16:22:39 +0100212 unsigned int num_sgsn = llist_count(&g_cfg->sgsns);
213 struct gbproxy_sgsn *sgsn;
Harald Weltee5209642020-12-05 19:59:45 +0100214 struct gbproxy_nse *nse;
215 struct gbproxy_bvc *bvc;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200216
Daniel Willmannee834af2020-12-14 16:22:39 +0100217 if (num_sgsn >= GBPROXY_MAX_NR_SGSN) {
218 vty_out(vty, "%% Too many SGSN NSE defined (%d), increase GBPROXY_MAX_NR_SGSN%s",
219 num_sgsn, VTY_NEWLINE);
220 return CMD_WARNING;
221 }
222
223 /* This will have created the gbproxy_nse as well */
224 sgsn = gbproxy_sgsn_by_nsei_or_new(g_cfg, nsei);
225 if (!sgsn)
Harald Weltee5209642020-12-05 19:59:45 +0100226 goto free_nothing;
Daniel Willmannee834af2020-12-14 16:22:39 +0100227 nse = sgsn->nse;
228 if (num_sgsn > 1 && g_cfg->pool.nri_bitlen == 0)
229 vty_out(vty, "%% Multiple SGSNs defined, but no pooling enabled%s", VTY_NEWLINE);
230
Harald Weltee5209642020-12-05 19:59:45 +0100231
232 if (!gbproxy_bvc_by_bvci(nse, 0)) {
233 uint8_t cause = BSSGP_CAUSE_OML_INTERV;
234 bvc = gbproxy_bvc_alloc(nse, 0);
235 if (!bvc)
Daniel Willmannee834af2020-12-14 16:22:39 +0100236 goto free_sgsn;
Harald Weltee5209642020-12-05 19:59:45 +0100237 bvc->fi = bssgp_bvc_fsm_alloc_sig_bss(bvc, nse->cfg->nsi, nsei, features);
238 if (!bvc->fi)
239 goto free_bvc;
240 bssgp_bvc_fsm_set_ops(bvc->fi, &sgsn_sig_bvc_fsm_ops, bvc);
241 osmo_fsm_inst_dispatch(bvc->fi, BSSGP_BVCFSM_E_REQ_RESET, &cause);
242 }
243
Daniel Willmannee834af2020-12-14 16:22:39 +0100244 vty->node = SGSN_NODE;
245 vty->index = sgsn;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200246 return CMD_SUCCESS;
Harald Weltee5209642020-12-05 19:59:45 +0100247
248free_bvc:
249 gbproxy_bvc_free(bvc);
Daniel Willmannee834af2020-12-14 16:22:39 +0100250free_sgsn:
251 gbproxy_sgsn_free(sgsn);
Harald Weltee5209642020-12-05 19:59:45 +0100252free_nothing:
253 vty_out(vty, "%% Unable to create NSE for NSEI=%05u%s", nsei, VTY_NEWLINE);
254 return CMD_WARNING;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200255}
256
Daniel Willmanna648f3c2020-12-28 18:07:27 +0100257DEFUN(cfg_sgsn_name,
258 cfg_sgsn_name_cmd,
259 "name NAME",
260 "Configure the SGSN\n"
261 "Name the SGSN\n"
262 "The name\n")
263{
264 struct gbproxy_sgsn *sgsn = vty->index;
265 const char *name = argv[0];
266
267
268 osmo_talloc_replace_string(sgsn, &sgsn->name, name);
269 if (!sgsn->name) {
270 vty_out(vty, "%% Unable to set name for SGSN with nsei %05u%s", sgsn->nse->nsei, VTY_NEWLINE);
271 return CMD_WARNING;
272 }
273
274 return CMD_SUCCESS;
275}
276
Daniel Willmannee834af2020-12-14 16:22:39 +0100277DEFUN_ATTR(cfg_sgsn_nri_add, cfg_sgsn_nri_add_cmd,
278 "nri add <0-32767> [<0-32767>]",
Daniel Willmann6cfedc42021-02-10 13:33:58 +0100279 NRI_STR "Add NRI value or range to the NRI mapping for this SGSN\n"
Daniel Willmannee834af2020-12-14 16:22:39 +0100280 NRI_FIRST_LAST_STR,
281 CMD_ATTR_IMMEDIATE)
282{
283 struct gbproxy_sgsn *sgsn = vty->index;
284 struct gbproxy_sgsn *other_sgsn;
285 bool before;
286 int rc;
287 const char *message;
288 struct osmo_nri_range add_range;
289
290 rc = osmo_nri_ranges_vty_add(&message, &add_range, sgsn->pool.nri_ranges, argc, argv, g_cfg->pool.nri_bitlen);
291 if (message) {
292 NRI_WARN(sgsn, "%s: " NRI_ARGS_TO_STR_FMT, message, NRI_ARGS_TO_STR_ARGS(argc, argv));
293 }
294 if (rc < 0)
295 return CMD_WARNING;
296
297 /* Issue a warning about NRI range overlaps (but still allow them).
298 * Overlapping ranges will map to whichever SGSN comes fist in the gbproxy_config->sgsns llist,
299 * which should be the first one defined in the config */
300 before = true;
301
302 llist_for_each_entry(other_sgsn, &g_cfg->sgsns, list) {
303 if (other_sgsn == sgsn) {
304 before = false;
305 continue;
306 }
307 if (osmo_nri_range_overlaps_ranges(&add_range, other_sgsn->pool.nri_ranges)) {
308 uint16_t nsei = sgsn->nse->nsei;
309 uint16_t other_nsei = other_sgsn->nse->nsei;
310 NRI_WARN(sgsn, "NRI range [%d..%d] overlaps between NSE %05d and NSE %05d."
311 " For overlaps, NSE %05d has higher priority than NSE %05d",
312 add_range.first, add_range.last, nsei, other_nsei,
313 before ? other_nsei : nsei, before ? nsei : other_nsei);
314 }
315 }
316 return CMD_SUCCESS;
317}
318
319DEFUN_ATTR(cfg_sgsn_nri_del, cfg_sgsn_nri_del_cmd,
320 "nri del <0-32767> [<0-32767>]",
Daniel Willmann6cfedc42021-02-10 13:33:58 +0100321 NRI_STR "Remove NRI value or range from the NRI mapping for this SGSN\n"
Daniel Willmannee834af2020-12-14 16:22:39 +0100322 NRI_FIRST_LAST_STR,
323 CMD_ATTR_IMMEDIATE)
324{
325 struct gbproxy_sgsn *sgsn = vty->index;
326 int rc;
327 const char *message;
328
329 rc = osmo_nri_ranges_vty_del(&message, NULL, sgsn->pool.nri_ranges, argc, argv);
330 if (message) {
331 NRI_WARN(sgsn, "%s: " NRI_ARGS_TO_STR_FMT, message, NRI_ARGS_TO_STR_ARGS(argc, argv));
332 }
333 if (rc < 0)
334 return CMD_WARNING;
335 return CMD_SUCCESS;
336}
337
338DEFUN_ATTR(cfg_sgsn_allow_attach, cfg_sgsn_allow_attach_cmd,
339 "allow-attach",
340 "Allow this SGSN to attach new subscribers (default).\n",
341 CMD_ATTR_IMMEDIATE)
342{
343 struct gbproxy_sgsn *sgsn = vty->index;
344 sgsn->pool.allow_attach = true;
345 return CMD_SUCCESS;
346}
347
348DEFUN_ATTR(cfg_sgsn_no_allow_attach, cfg_sgsn_no_allow_attach_cmd,
349 "no allow-attach",
350 NO_STR
Daniel Willmann6cfedc42021-02-10 13:33:58 +0100351 "Do not assign new subscribers to this SGSN."
352 " Useful if an SGSN in an SGSN pool is configured to off-load subscribers."
353 " The SGSN will still be operational for already IMSI-Attached subscribers,"
354 " but the NAS node selection function will skip this SGSN for new subscribers\n",
Daniel Willmannee834af2020-12-14 16:22:39 +0100355 CMD_ATTR_IMMEDIATE)
356{
357 struct gbproxy_sgsn *sgsn = vty->index;
358 sgsn->pool.allow_attach = false;
359 return CMD_SUCCESS;
360}
361
Daniel Willmann182a0b62021-01-18 13:37:40 +0100362DEFUN(sgsn_show_nri_all, show_nri_all_cmd,
Daniel Willmannee834af2020-12-14 16:22:39 +0100363 "show nri all",
364 SHOW_STR NRI_STR "Show all SGSNs\n")
365{
366 struct gbproxy_sgsn *sgsn;
367
368 llist_for_each_entry(sgsn, &g_cfg->sgsns, list)
369 sgsn_write_nri(vty, sgsn, true);
370
371 return CMD_SUCCESS;
372}
373
Daniel Willmann182a0b62021-01-18 13:37:40 +0100374DEFUN(show_nri_nsei, show_nri_nsei_cmd,
Daniel Willmannee834af2020-12-14 16:22:39 +0100375 "show nri nsei <0-65535>",
376 SHOW_STR NRI_STR "Identify SGSN by NSEI\n"
377 "NSEI of the SGSN\n")
378{
379 struct gbproxy_sgsn *sgsn;
380 int nsei = atoi(argv[0]);
381
382 sgsn = gbproxy_sgsn_by_nsei(g_cfg, nsei);
383 if (!sgsn) {
384 vty_out(vty, "%% No SGSN with found for NSEI %05d%s", nsei, VTY_NEWLINE);
385 return CMD_SUCCESS;
386 }
387 sgsn_write_nri(vty, sgsn, true);
388
389 return CMD_SUCCESS;
390}
391
Harald Welte209dc9f2020-12-12 19:02:16 +0100392DEFUN(cfg_pool_bvc_fc_ratio,
393 cfg_pool_bvc_fc_ratio_cmd,
394 "pool bvc-flow-control-ratio <1-100>",
395 "SGSN Pool related configuration\n"
396 "Ratio of BSS-advertised bucket size + leak rate advertised to each SGSN\n"
397 "Ratio of BSS-advertised bucket size + leak rate advertised to each SGSN (Percent)\n")
398{
399 g_cfg->pool.bvc_fc_ratio = atoi(argv[0]);
400 return CMD_SUCCESS;
401}
Daniel Willmannee834af2020-12-14 16:22:39 +0100402DEFUN_ATTR(cfg_gbproxy_nri_bitlen,
403 cfg_gbproxy_nri_bitlen_cmd,
404 "nri bitlen <0-15>",
405 NRI_STR
406 "Set number of bits that an NRI has, to extract from TMSI identities (always starting just after the TMSI's most significant octet).\n"
407 "bit count (0 disables) pooling)\n",
408 CMD_ATTR_IMMEDIATE)
409{
410 g_cfg->pool.nri_bitlen = atoi(argv[0]);
411
412 if (llist_count(&g_cfg->sgsns) > 1 && g_cfg->pool.nri_bitlen == 0)
413 vty_out(vty, "%% Pooling disabled, but multiple SGSNs defined%s", VTY_NEWLINE);
414
415 /* TODO: Verify all nri ranges and warn on mismatch */
416
417 return CMD_SUCCESS;
418}
419
420DEFUN_ATTR(cfg_gbproxy_nri_null_add,
421 cfg_gbproxy_nri_null_add_cmd,
422 "nri null add <0-32767> [<0-32767>]",
423 NRI_STR NULL_NRI_STR "Add NULL-NRI value (or range)\n"
424 NRI_FIRST_LAST_STR,
425 CMD_ATTR_IMMEDIATE)
426{
427 int rc;
428 const char *message;
429
430 rc = osmo_nri_ranges_vty_add(&message, NULL, g_cfg->pool.null_nri_ranges, argc, argv,
431 g_cfg->pool.nri_bitlen);
432 if (message) {
433 vty_out(vty, "%% nri null add: %s: " NRI_ARGS_TO_STR_FMT "%s", message, NRI_ARGS_TO_STR_ARGS(argc, argv),
434 VTY_NEWLINE);
435 vty_out(vty, "%s: \n" NRI_ARGS_TO_STR_FMT, message, NRI_ARGS_TO_STR_ARGS(argc, argv));
436 }
437 if (rc < 0)
438 return CMD_WARNING;
439 return CMD_SUCCESS;
440}
441
442DEFUN_ATTR(cfg_gbproxy_nri_null_del,
443 cfg_gbproxy_nri_null_del_cmd,
444 "nri null del <0-32767> [<0-32767>]",
Daniel Willmann6cfedc42021-02-10 13:33:58 +0100445 NRI_STR NULL_NRI_STR "Remove NULL-NRI value (or range)\n"
Daniel Willmannee834af2020-12-14 16:22:39 +0100446 NRI_FIRST_LAST_STR,
447 CMD_ATTR_IMMEDIATE)
448{
449 int rc;
450 const char *message;
451 rc = osmo_nri_ranges_vty_del(&message, NULL, g_cfg->pool.null_nri_ranges, argc, argv);
452 if (message) {
453 vty_out(vty, "%% %s: " NRI_ARGS_TO_STR_FMT "%s", message, NRI_ARGS_TO_STR_ARGS(argc, argv),
454 VTY_NEWLINE);
455 }
456 if (rc < 0)
457 return CMD_WARNING;
458 return CMD_SUCCESS;
459}
Harald Welte209dc9f2020-12-12 19:02:16 +0100460
Daniel Willmann8f407b12020-12-02 19:33:50 +0100461static void log_set_bvc_filter(struct log_target *target,
462 const uint16_t *bvci)
463{
464 if (bvci) {
465 uintptr_t bvci_filter = *bvci | BVC_LOG_CTX_FLAG;
466 target->filter_map |= (1 << LOG_FLT_GB_BVC);
467 target->filter_data[LOG_FLT_GB_BVC] = (void *)bvci_filter;
468 } else if (target->filter_data[LOG_FLT_GB_BVC]) {
469 target->filter_map = ~(1 << LOG_FLT_GB_BVC);
470 target->filter_data[LOG_FLT_GB_BVC] = NULL;
471 }
472}
473
474DEFUN(logging_fltr_bvc,
475 logging_fltr_bvc_cmd,
476 "logging filter bvc bvci <0-65535>",
477 LOGGING_STR FILTER_STR
478 "Filter based on BSSGP VC\n"
479 "Identify BVC by BVCI\n"
480 "Numeric identifier\n")
481{
482 struct log_target *tgt;
483 uint16_t id = atoi(argv[0]);
484
485 log_tgt_mutex_lock();
486 tgt = osmo_log_vty2tgt(vty);
487 if (!tgt) {
488 log_tgt_mutex_unlock();
489 return CMD_WARNING;
490 }
491
492 log_set_bvc_filter(tgt, &id);
493 log_tgt_mutex_unlock();
494 return CMD_SUCCESS;
495}
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200496
Harald Weltebefe1c32020-12-12 15:15:34 +0100497DEFUN(show_gbproxy_bvc, show_gbproxy_bvc_cmd, "show gbproxy bvc (bss|sgsn) [stats]",
Daniel Willmann2c758f22021-01-18 17:10:51 +0100498 SHOW_STR GBPROXY_STR
Harald Weltebefe1c32020-12-12 15:15:34 +0100499 "Show BSSGP Virtual Connections\n"
Harald Weltec6ecfad2020-12-12 14:17:51 +0100500 "Display BSS-side BVCs\n"
501 "Display SGSN-side BVCs\n"
502 "Show statistics\n")
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200503{
Daniel Willmanne50550e2020-11-26 18:19:21 +0100504 struct gbproxy_nse *nse;
Harald Weltec6ecfad2020-12-12 14:17:51 +0100505 bool show_stats = argc >= 2;
506 int i;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200507
508 if (show_stats)
509 vty_out_rate_ctr_group(vty, "", g_cfg->ctrg);
510
Harald Weltec6ecfad2020-12-12 14:17:51 +0100511 if (!strcmp(argv[0], "bss")) {
512 hash_for_each(g_cfg->bss_nses, i, nse, list)
513 gbproxy_vty_print_nse(vty, nse, show_stats);
514 } else {
515 hash_for_each(g_cfg->sgsn_nses, i, nse, list)
516 gbproxy_vty_print_nse(vty, nse, show_stats);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200517 }
518 return CMD_SUCCESS;
519}
520
Harald Welte9151b4a2020-12-12 15:16:43 +0100521DEFUN(show_gbproxy_cell, show_gbproxy_cell_cmd, "show gbproxy cell [stats]",
Daniel Willmann2c758f22021-01-18 17:10:51 +0100522 SHOW_STR GBPROXY_STR
Harald Welte9151b4a2020-12-12 15:16:43 +0100523 "Show GPRS Cell Information\n"
524 "Show statistics\n")
525{
526 struct gbproxy_cell *cell;
527 bool show_stats = argc >= 1;
528 int i;
529
530 hash_for_each(g_cfg->cells, i, cell, list)
531 gbproxy_vty_print_cell(vty, cell, show_stats);
532
533 return CMD_SUCCESS;
534}
535
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200536DEFUN(show_gbproxy_links, show_gbproxy_links_cmd, "show gbproxy links",
Daniel Willmann2c758f22021-01-18 17:10:51 +0100537 SHOW_STR GBPROXY_STR "Show logical links\n")
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200538{
Daniel Willmanne50550e2020-11-26 18:19:21 +0100539 struct gbproxy_nse *nse;
Harald Welte8b4c7942020-12-05 10:14:49 +0100540 int i, j;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200541
Harald Welted2fef952020-12-05 00:31:07 +0100542 hash_for_each(g_cfg->bss_nses, i, nse, list) {
Harald Welte560bdb32020-12-04 22:24:47 +0100543 struct gbproxy_bvc *bvc;
Harald Welte8b4c7942020-12-05 10:14:49 +0100544 hash_for_each(nse->bvcs, j, bvc, list) {
Harald Welte560bdb32020-12-04 22:24:47 +0100545 gbprox_vty_print_bvc(vty, bvc);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200546 }
547 }
548 return CMD_SUCCESS;
549}
550
Daniel Willmannf2812fb2021-01-18 16:57:01 +0100551DEFUN(show_gbproxy_tlli_cache, show_gbproxy_tlli_cache_cmd,
552 "show gbproxy tlli-cache",
553 SHOW_STR GBPROXY_STR "Show TLLI cache entries\n")
554{
555 struct gbproxy_tlli_cache_entry *entry;
556 struct timespec now;
557 time_t expiry;
558 int i, count = 0;
559
560 osmo_clock_gettime(CLOCK_MONOTONIC, &now);
561 expiry = now.tv_sec - g_cfg->tlli_cache.timeout;
562
563 vty_out(vty, "TLLI cache timeout %us%s", g_cfg->tlli_cache.timeout, VTY_NEWLINE);
564 hash_for_each(g_cfg->tlli_cache.entries, i, entry, list) {
565 time_t valid = entry->tstamp - expiry;
566 struct gbproxy_nse *nse = entry->nse;
567
568 vty_out(vty, " TLLI %08x -> NSE(%05u/%s) valid %lds%s", entry->tlli, nse->nsei,
569 nse->sgsn_facing ? "SGSN" : "BSS", valid, VTY_NEWLINE);
570 count++;
571 }
572 vty_out(vty, "TLLI cache contains %u entries%s", count, VTY_NEWLINE);
573 return CMD_SUCCESS;
574}
575
576DEFUN(show_gbproxy_imsi_cache, show_gbproxy_imsi_cache_cmd,
577 "show gbproxy imsi-cache",
578 SHOW_STR GBPROXY_STR "Show IMSI cache entries\n")
579{
580 struct gbproxy_imsi_cache_entry *entry;
581 struct timespec now;
582 time_t expiry;
583 int i, count = 0;
584
585 osmo_clock_gettime(CLOCK_MONOTONIC, &now);
586 expiry = now.tv_sec - g_cfg->imsi_cache.timeout;
587
588 vty_out(vty, "IMSI cache timeout %us%s", g_cfg->imsi_cache.timeout, VTY_NEWLINE);
589 hash_for_each(g_cfg->imsi_cache.entries, i, entry, list) {
590 time_t valid = entry->tstamp - expiry;
591 struct gbproxy_nse *nse = entry->nse;
592 vty_out(vty, " IMSI %s -> NSE(%05u/%s): valid %lds%s", entry->imsi, nse->nsei,
593 nse->sgsn_facing ? "SGSN" : "BSS", valid, VTY_NEWLINE);
594 count++;
595 }
596 vty_out(vty, "IMSI cache contains %u entries%s", count, VTY_NEWLINE);
597 return CMD_SUCCESS;
598}
599
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200600DEFUN(delete_gb_bvci, delete_gb_bvci_cmd,
601 "delete-gbproxy-peer <0-65534> bvci <2-65534>",
Harald Welte560bdb32020-12-04 22:24:47 +0100602 "Delete a GBProxy bvc by NSEI and optionally BVCI\n"
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200603 "NSEI number\n"
Harald Welte560bdb32020-12-04 22:24:47 +0100604 "Only delete bvc with a matching BVCI\n"
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200605 "BVCI number\n")
606{
607 const uint16_t nsei = atoi(argv[0]);
608 const uint16_t bvci = atoi(argv[1]);
Harald Weltee5209642020-12-05 19:59:45 +0100609 struct gbproxy_nse *nse = gbproxy_nse_by_nsei(g_cfg, nsei, NSE_F_BSS);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200610 int counter;
611
Harald Weltee5209642020-12-05 19:59:45 +0100612 if (!nse) {
613 vty_out(vty, "NSE not found%s", VTY_NEWLINE);
614 return CMD_WARNING;
615 }
616
617 counter = gbproxy_cleanup_bvcs(nse, bvci);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200618
619 if (counter == 0) {
620 vty_out(vty, "BVC not found%s", VTY_NEWLINE);
621 return CMD_WARNING;
622 }
623
624 return CMD_SUCCESS;
625}
626
627DEFUN(delete_gb_nsei, delete_gb_nsei_cmd,
628 "delete-gbproxy-peer <0-65534> (only-bvc|only-nsvc|all) [dry-run]",
Harald Welte560bdb32020-12-04 22:24:47 +0100629 "Delete a GBProxy bvc by NSEI and optionally BVCI\n"
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200630 "NSEI number\n"
631 "Only delete BSSGP connections (BVC)\n"
632 "Only delete dynamic NS connections (NS-VC)\n"
633 "Delete BVC and dynamic NS connections\n"
634 "Show what would be deleted instead of actually deleting\n"
635 )
636{
637 const uint16_t nsei = atoi(argv[0]);
638 const char *mode = argv[1];
639 int dry_run = argc > 2;
640 int delete_bvc = 0;
641 int delete_nsvc = 0;
642 int counter;
643
644 if (strcmp(mode, "only-bvc") == 0)
645 delete_bvc = 1;
646 else if (strcmp(mode, "only-nsvc") == 0)
647 delete_nsvc = 1;
648 else
649 delete_bvc = delete_nsvc = 1;
650
651 if (delete_bvc) {
Daniel Willmann5b897712020-12-04 17:43:27 +0100652 if (!dry_run) {
Harald Weltee5209642020-12-05 19:59:45 +0100653 struct gbproxy_nse *nse = gbproxy_nse_by_nsei(g_cfg, nsei, NSE_F_BSS);
654 counter = gbproxy_cleanup_bvcs(nse, 0);
Daniel Willmann5b897712020-12-04 17:43:27 +0100655 gbproxy_nse_free(nse);
656 } else {
Daniel Willmanne50550e2020-11-26 18:19:21 +0100657 struct gbproxy_nse *nse;
Harald Welte560bdb32020-12-04 22:24:47 +0100658 struct gbproxy_bvc *bvc;
Harald Welte8b4c7942020-12-05 10:14:49 +0100659 int i, j;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200660 counter = 0;
Harald Welted2fef952020-12-05 00:31:07 +0100661 hash_for_each(g_cfg->bss_nses, i, nse, list) {
Daniel Willmanne50550e2020-11-26 18:19:21 +0100662 if (nse->nsei != nsei)
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200663 continue;
Harald Welte8b4c7942020-12-05 10:14:49 +0100664 hash_for_each(nse->bvcs, j, bvc, list) {
Daniel Willmanne50550e2020-11-26 18:19:21 +0100665 vty_out(vty, "BVC: ");
Harald Welte560bdb32020-12-04 22:24:47 +0100666 gbprox_vty_print_bvc(vty, bvc);
Daniel Willmanne50550e2020-11-26 18:19:21 +0100667 counter += 1;
668 }
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200669 }
670 }
671 vty_out(vty, "%sDeleted %d BVC%s",
672 dry_run ? "Not " : "", counter, VTY_NEWLINE);
673 }
674
675 if (delete_nsvc) {
Alexander Couzens951e1332020-09-22 13:21:46 +0200676 struct gprs_ns2_inst *nsi = g_cfg->nsi;
677 struct gprs_ns2_nse *nse;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200678
Alexander Couzens951e1332020-09-22 13:21:46 +0200679 nse = gprs_ns2_nse_by_nsei(nsi, nsei);
680 if (!nse) {
681 vty_out(vty, "NSEI not found%s", VTY_NEWLINE);
682 return CMD_WARNING;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200683 }
Alexander Couzens951e1332020-09-22 13:21:46 +0200684
685 /* TODO: We should NOT delete a persistent NSEI/NSVC as soon as we can check for these */
686 if (!dry_run)
687 gprs_ns2_free_nse(nse);
688
689 vty_out(vty, "%sDeleted NS-VCs for NSEI %d%s",
690 dry_run ? "Not " : "", nsei, VTY_NEWLINE);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200691 }
692
693 return CMD_SUCCESS;
694}
695
Daniel Willmannb387c1e2020-12-27 18:14:39 +0100696/* Only for ttcn3 testing */
697DEFUN_HIDDEN(sgsn_pool_nsf_fixed, sgsn_pool_nsf_fixed_cmd,
698 "sgsn-pool nsf fixed NAME",
699 "SGSN pooling: load balancing across multiple SGSNs.\n"
700 "Customize the Network Selection Function.\n"
701 "Set a fixed SGSN to use (for testing).\n"
702 "The name of the SGSN to use.\n")
703{
704 const char *name = argv[0];
705 struct gbproxy_sgsn *sgsn = gbproxy_sgsn_by_name(g_cfg, name);
706
707 if (!sgsn) {
708 vty_out(vty, "%% Could not find SGSN with name %s%s", name, VTY_NEWLINE);
709 return CMD_WARNING;
710 }
711
712 g_cfg->pool.nsf_override = sgsn;
713 return CMD_SUCCESS;
714}
715
716DEFUN_HIDDEN(sgsn_pool_nsf_normal, sgsn_pool_nsf_normal_cmd,
717 "sgsn-pool nsf normal",
718 "SGSN pooling: load balancing across multiple SGSNs.\n"
719 "Customize the Network Selection Function.\n"
720 "Reset the NSF back to regular operation (for testing).\n")
721{
722 g_cfg->pool.nsf_override = NULL;
723 return CMD_SUCCESS;
724}
725
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200726int gbproxy_vty_init(void)
727{
Harald Weltebefe1c32020-12-12 15:15:34 +0100728 install_element_ve(&show_gbproxy_bvc_cmd);
Harald Welte9151b4a2020-12-12 15:16:43 +0100729 install_element_ve(&show_gbproxy_cell_cmd);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200730 install_element_ve(&show_gbproxy_links_cmd);
Daniel Willmannf2812fb2021-01-18 16:57:01 +0100731 install_element_ve(&show_gbproxy_tlli_cache_cmd);
732 install_element_ve(&show_gbproxy_imsi_cache_cmd);
Daniel Willmannee834af2020-12-14 16:22:39 +0100733 install_element_ve(&show_nri_all_cmd);
734 install_element_ve(&show_nri_nsei_cmd);
Daniel Willmann8f407b12020-12-02 19:33:50 +0100735 install_element_ve(&logging_fltr_bvc_cmd);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200736
737 install_element(ENABLE_NODE, &delete_gb_bvci_cmd);
738 install_element(ENABLE_NODE, &delete_gb_nsei_cmd);
Daniel Willmannb387c1e2020-12-27 18:14:39 +0100739 install_element(ENABLE_NODE, &sgsn_pool_nsf_fixed_cmd);
740 install_element(ENABLE_NODE, &sgsn_pool_nsf_normal_cmd);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200741
742 install_element(CONFIG_NODE, &cfg_gbproxy_cmd);
743 install_node(&gbproxy_node, config_write_gbproxy);
Harald Welte209dc9f2020-12-12 19:02:16 +0100744 install_element(GBPROXY_NODE, &cfg_pool_bvc_fc_ratio_cmd);
Daniel Willmannee834af2020-12-14 16:22:39 +0100745 install_element(GBPROXY_NODE, &cfg_gbproxy_nri_bitlen_cmd);
746 install_element(GBPROXY_NODE, &cfg_gbproxy_nri_null_add_cmd);
747 install_element(GBPROXY_NODE, &cfg_gbproxy_nri_null_del_cmd);
748
749 install_element(CONFIG_NODE, &cfg_sgsn_nsei_cmd);
750 install_node(&sgsn_node, config_write_sgsn);
Daniel Willmanna648f3c2020-12-28 18:07:27 +0100751 install_element(SGSN_NODE, &cfg_sgsn_name_cmd);
Daniel Willmannee834af2020-12-14 16:22:39 +0100752 install_element(SGSN_NODE, &cfg_sgsn_allow_attach_cmd);
753 install_element(SGSN_NODE, &cfg_sgsn_no_allow_attach_cmd);
754 install_element(SGSN_NODE, &cfg_sgsn_nri_add_cmd);
755 install_element(SGSN_NODE, &cfg_sgsn_nri_del_cmd);
756
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200757
758 return 0;
759}
760
761int gbproxy_parse_config(const char *config_file, struct gbproxy_config *cfg)
762{
763 int rc;
764
765 g_cfg = cfg;
766 rc = vty_read_config_file(config_file, NULL);
767 if (rc < 0) {
768 fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file);
769 return rc;
770 }
771
772 return 0;
773}