blob: e5f53e21fc00fc9a53964df7b8fb46cfc2618ff5 [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"
Daniel Willmanna16ecc32021-03-10 09:57:12 +010042#include <osmocom/gbproxy/gb_proxy.h>
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +020043
44#include <osmocom/vty/command.h>
Daniel Willmann8f407b12020-12-02 19:33:50 +010045#include <osmocom/vty/logging.h>
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +020046#include <osmocom/vty/vty.h>
47#include <osmocom/vty/misc.h>
48
Daniel Willmann2c758f22021-01-18 17:10:51 +010049#define GBPROXY_STR "Display information about the Gb proxy\n"
Daniel Willmannee834af2020-12-14 16:22:39 +010050#define NRI_STR "Mapping of Network Resource Indicators to this SGSN, for SGSN pooling\n"
51#define NULL_NRI_STR "Define NULL-NRI values that cause re-assignment of an MS to a different SGSN, for SGSN pooling.\n"
52#define NRI_FIRST_LAST_STR "First value of the NRI value range, should not surpass the configured 'nri bitlen'.\n" \
53 "Last value of the NRI value range, should not surpass the configured 'nri bitlen' and be larger than the" \
54 " first value; if omitted, apply only the first value.\n"
55#define NRI_ARGS_TO_STR_FMT "%s%s%s"
56#define NRI_ARGS_TO_STR_ARGS(ARGC, ARGV) ARGV[0], (ARGC>1)? ".." : "", (ARGC>1)? ARGV[1] : ""
57#define NRI_WARN(SGSN, FORMAT, args...) do { \
58 vty_out(vty, "%% Warning: NSE(%05d/SGSN): " FORMAT "%s", (SGSN)->nse->nsei, ##args, VTY_NEWLINE); \
59 LOGP(DLBSSGP, LOGL_ERROR, "NSE(%05d/SGSN): " FORMAT "\n", (SGSN)->nse->nsei, ##args); \
60 } while (0)
Harald Weltee5209642020-12-05 19:59:45 +010061
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +020062static struct gbproxy_config *g_cfg = NULL;
63
64/*
65 * vty code for gbproxy below
66 */
Daniel Willmann2689c6e2021-02-11 23:47:10 +010067enum gbproxy_vty_node {
68 GBPROXY_NODE = _LAST_OSMOVTY_NODE + 1,
69 SGSN_NODE,
70};
71
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +020072static struct cmd_node gbproxy_node = {
73 GBPROXY_NODE,
74 "%s(config-gbproxy)# ",
75 1,
76};
77
Harald Welte560bdb32020-12-04 22:24:47 +010078static void gbprox_vty_print_bvc(struct vty *vty, struct gbproxy_bvc *bvc)
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +020079{
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +020080
Harald Weltec6ecfad2020-12-12 14:17:51 +010081 if (bvc->bvci == 0) {
82 vty_out(vty, "NSEI %5u, SIG-BVCI %5u [%s]%s", bvc->nse->nsei, bvc->bvci,
83 osmo_fsm_inst_state_name(bvc->fi), VTY_NEWLINE);
84 } else {
Harald Welte838ebb32021-04-07 14:05:16 +020085 vty_out(vty, "NSEI %5u, PTP-BVCI %5u, RAI %s, CellID %u [%s]%s", bvc->nse->nsei,
86 bvc->bvci, osmo_rai_name(&bvc->cell->id.raid), bvc->cell->id.cid,
87 osmo_fsm_inst_state_name(bvc->fi), VTY_NEWLINE);
Harald Weltec6ecfad2020-12-12 14:17:51 +010088 }
89}
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +020090
Harald Weltec6ecfad2020-12-12 14:17:51 +010091static void gbproxy_vty_print_nse(struct vty *vty, struct gbproxy_nse *nse, bool show_stats)
92{
93 struct gbproxy_bvc *bvc;
94 int j;
95
96 hash_for_each(nse->bvcs, j, bvc, list) {
97 gbprox_vty_print_bvc(vty, bvc);
98
99 if (show_stats)
100 vty_out_rate_ctr_group(vty, " ", bvc->ctrg);
101 }
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200102}
103
Harald Welte9151b4a2020-12-12 15:16:43 +0100104static void gbproxy_vty_print_cell(struct vty *vty, struct gbproxy_cell *cell, bool show_stats)
105{
Harald Welte9151b4a2020-12-12 15:16:43 +0100106 unsigned int num_sgsn_bvc = 0;
107 unsigned int i;
108
Philipp Maierda3af942021-02-04 21:54:09 +0100109 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 +0100110 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;
Daniel Willmanna8b61652021-02-12 05:05:14 +0100245 bssgp_bvc_fsm_set_max_pdu_len(bvc->fi, nse->max_sdu_len);
Harald Weltee5209642020-12-05 19:59:45 +0100246 bssgp_bvc_fsm_set_ops(bvc->fi, &sgsn_sig_bvc_fsm_ops, bvc);
247 osmo_fsm_inst_dispatch(bvc->fi, BSSGP_BVCFSM_E_REQ_RESET, &cause);
248 }
249
Daniel Willmannee834af2020-12-14 16:22:39 +0100250 vty->node = SGSN_NODE;
251 vty->index = sgsn;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200252 return CMD_SUCCESS;
Harald Weltee5209642020-12-05 19:59:45 +0100253
254free_bvc:
255 gbproxy_bvc_free(bvc);
Daniel Willmannee834af2020-12-14 16:22:39 +0100256free_sgsn:
257 gbproxy_sgsn_free(sgsn);
Harald Weltee5209642020-12-05 19:59:45 +0100258free_nothing:
259 vty_out(vty, "%% Unable to create NSE for NSEI=%05u%s", nsei, VTY_NEWLINE);
260 return CMD_WARNING;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200261}
262
Daniel Willmanna648f3c2020-12-28 18:07:27 +0100263DEFUN(cfg_sgsn_name,
264 cfg_sgsn_name_cmd,
265 "name NAME",
266 "Configure the SGSN\n"
267 "Name the SGSN\n"
268 "The name\n")
269{
270 struct gbproxy_sgsn *sgsn = vty->index;
271 const char *name = argv[0];
272
273
274 osmo_talloc_replace_string(sgsn, &sgsn->name, name);
275 if (!sgsn->name) {
276 vty_out(vty, "%% Unable to set name for SGSN with nsei %05u%s", sgsn->nse->nsei, VTY_NEWLINE);
277 return CMD_WARNING;
278 }
279
280 return CMD_SUCCESS;
281}
282
Daniel Willmannee834af2020-12-14 16:22:39 +0100283DEFUN_ATTR(cfg_sgsn_nri_add, cfg_sgsn_nri_add_cmd,
284 "nri add <0-32767> [<0-32767>]",
Daniel Willmann6cfedc42021-02-10 13:33:58 +0100285 NRI_STR "Add NRI value or range to the NRI mapping for this SGSN\n"
Daniel Willmannee834af2020-12-14 16:22:39 +0100286 NRI_FIRST_LAST_STR,
287 CMD_ATTR_IMMEDIATE)
288{
289 struct gbproxy_sgsn *sgsn = vty->index;
290 struct gbproxy_sgsn *other_sgsn;
291 bool before;
Daniel Willmannfe5d3b72021-02-10 13:34:56 +0100292 bool overlaps = false;
Daniel Willmannee834af2020-12-14 16:22:39 +0100293 int rc;
294 const char *message;
295 struct osmo_nri_range add_range;
296
297 rc = osmo_nri_ranges_vty_add(&message, &add_range, sgsn->pool.nri_ranges, argc, argv, g_cfg->pool.nri_bitlen);
298 if (message) {
299 NRI_WARN(sgsn, "%s: " NRI_ARGS_TO_STR_FMT, message, NRI_ARGS_TO_STR_ARGS(argc, argv));
300 }
301 if (rc < 0)
302 return CMD_WARNING;
303
304 /* Issue a warning about NRI range overlaps (but still allow them).
305 * Overlapping ranges will map to whichever SGSN comes fist in the gbproxy_config->sgsns llist,
306 * which should be the first one defined in the config */
307 before = true;
308
309 llist_for_each_entry(other_sgsn, &g_cfg->sgsns, list) {
310 if (other_sgsn == sgsn) {
311 before = false;
312 continue;
313 }
314 if (osmo_nri_range_overlaps_ranges(&add_range, other_sgsn->pool.nri_ranges)) {
315 uint16_t nsei = sgsn->nse->nsei;
316 uint16_t other_nsei = other_sgsn->nse->nsei;
Daniel Willmannfe5d3b72021-02-10 13:34:56 +0100317 overlaps = true;
Daniel Willmannee834af2020-12-14 16:22:39 +0100318 NRI_WARN(sgsn, "NRI range [%d..%d] overlaps between NSE %05d and NSE %05d."
319 " For overlaps, NSE %05d has higher priority than NSE %05d",
320 add_range.first, add_range.last, nsei, other_nsei,
321 before ? other_nsei : nsei, before ? nsei : other_nsei);
322 }
323 }
Daniel Willmannfe5d3b72021-02-10 13:34:56 +0100324 if (overlaps)
325 return CMD_WARNING;
326 else
327 return CMD_SUCCESS;
Daniel Willmannee834af2020-12-14 16:22:39 +0100328}
329
330DEFUN_ATTR(cfg_sgsn_nri_del, cfg_sgsn_nri_del_cmd,
331 "nri del <0-32767> [<0-32767>]",
Daniel Willmann6cfedc42021-02-10 13:33:58 +0100332 NRI_STR "Remove NRI value or range from the NRI mapping for this SGSN\n"
Daniel Willmannee834af2020-12-14 16:22:39 +0100333 NRI_FIRST_LAST_STR,
334 CMD_ATTR_IMMEDIATE)
335{
336 struct gbproxy_sgsn *sgsn = vty->index;
337 int rc;
338 const char *message;
339
340 rc = osmo_nri_ranges_vty_del(&message, NULL, sgsn->pool.nri_ranges, argc, argv);
341 if (message) {
342 NRI_WARN(sgsn, "%s: " NRI_ARGS_TO_STR_FMT, message, NRI_ARGS_TO_STR_ARGS(argc, argv));
343 }
344 if (rc < 0)
345 return CMD_WARNING;
346 return CMD_SUCCESS;
347}
348
349DEFUN_ATTR(cfg_sgsn_allow_attach, cfg_sgsn_allow_attach_cmd,
350 "allow-attach",
351 "Allow this SGSN to attach new subscribers (default).\n",
352 CMD_ATTR_IMMEDIATE)
353{
354 struct gbproxy_sgsn *sgsn = vty->index;
355 sgsn->pool.allow_attach = true;
356 return CMD_SUCCESS;
357}
358
359DEFUN_ATTR(cfg_sgsn_no_allow_attach, cfg_sgsn_no_allow_attach_cmd,
360 "no allow-attach",
361 NO_STR
Daniel Willmann6cfedc42021-02-10 13:33:58 +0100362 "Do not assign new subscribers to this SGSN."
363 " Useful if an SGSN in an SGSN pool is configured to off-load subscribers."
364 " The SGSN will still be operational for already IMSI-Attached subscribers,"
365 " but the NAS node selection function will skip this SGSN for new subscribers\n",
Daniel Willmannee834af2020-12-14 16:22:39 +0100366 CMD_ATTR_IMMEDIATE)
367{
368 struct gbproxy_sgsn *sgsn = vty->index;
369 sgsn->pool.allow_attach = false;
370 return CMD_SUCCESS;
371}
372
Daniel Willmann182a0b62021-01-18 13:37:40 +0100373DEFUN(sgsn_show_nri_all, show_nri_all_cmd,
Daniel Willmannee834af2020-12-14 16:22:39 +0100374 "show nri all",
375 SHOW_STR NRI_STR "Show all SGSNs\n")
376{
377 struct gbproxy_sgsn *sgsn;
378
379 llist_for_each_entry(sgsn, &g_cfg->sgsns, list)
380 sgsn_write_nri(vty, sgsn, true);
381
382 return CMD_SUCCESS;
383}
384
Daniel Willmann182a0b62021-01-18 13:37:40 +0100385DEFUN(show_nri_nsei, show_nri_nsei_cmd,
Daniel Willmannee834af2020-12-14 16:22:39 +0100386 "show nri nsei <0-65535>",
387 SHOW_STR NRI_STR "Identify SGSN by NSEI\n"
388 "NSEI of the SGSN\n")
389{
390 struct gbproxy_sgsn *sgsn;
391 int nsei = atoi(argv[0]);
392
393 sgsn = gbproxy_sgsn_by_nsei(g_cfg, nsei);
394 if (!sgsn) {
395 vty_out(vty, "%% No SGSN with found for NSEI %05d%s", nsei, VTY_NEWLINE);
396 return CMD_SUCCESS;
397 }
398 sgsn_write_nri(vty, sgsn, true);
399
400 return CMD_SUCCESS;
401}
402
Harald Welte209dc9f2020-12-12 19:02:16 +0100403DEFUN(cfg_pool_bvc_fc_ratio,
404 cfg_pool_bvc_fc_ratio_cmd,
405 "pool bvc-flow-control-ratio <1-100>",
406 "SGSN Pool related configuration\n"
407 "Ratio of BSS-advertised bucket size + leak rate advertised to each SGSN\n"
408 "Ratio of BSS-advertised bucket size + leak rate advertised to each SGSN (Percent)\n")
409{
410 g_cfg->pool.bvc_fc_ratio = atoi(argv[0]);
411 return CMD_SUCCESS;
412}
Daniel Willmannee834af2020-12-14 16:22:39 +0100413DEFUN_ATTR(cfg_gbproxy_nri_bitlen,
414 cfg_gbproxy_nri_bitlen_cmd,
415 "nri bitlen <0-15>",
416 NRI_STR
417 "Set number of bits that an NRI has, to extract from TMSI identities (always starting just after the TMSI's most significant octet).\n"
418 "bit count (0 disables) pooling)\n",
419 CMD_ATTR_IMMEDIATE)
420{
421 g_cfg->pool.nri_bitlen = atoi(argv[0]);
422
423 if (llist_count(&g_cfg->sgsns) > 1 && g_cfg->pool.nri_bitlen == 0)
424 vty_out(vty, "%% Pooling disabled, but multiple SGSNs defined%s", VTY_NEWLINE);
425
426 /* TODO: Verify all nri ranges and warn on mismatch */
427
428 return CMD_SUCCESS;
429}
430
431DEFUN_ATTR(cfg_gbproxy_nri_null_add,
432 cfg_gbproxy_nri_null_add_cmd,
433 "nri null add <0-32767> [<0-32767>]",
434 NRI_STR NULL_NRI_STR "Add NULL-NRI value (or range)\n"
435 NRI_FIRST_LAST_STR,
436 CMD_ATTR_IMMEDIATE)
437{
438 int rc;
439 const char *message;
440
441 rc = osmo_nri_ranges_vty_add(&message, NULL, g_cfg->pool.null_nri_ranges, argc, argv,
442 g_cfg->pool.nri_bitlen);
443 if (message) {
444 vty_out(vty, "%% nri null add: %s: " NRI_ARGS_TO_STR_FMT "%s", message, NRI_ARGS_TO_STR_ARGS(argc, argv),
445 VTY_NEWLINE);
446 vty_out(vty, "%s: \n" NRI_ARGS_TO_STR_FMT, message, NRI_ARGS_TO_STR_ARGS(argc, argv));
447 }
448 if (rc < 0)
449 return CMD_WARNING;
450 return CMD_SUCCESS;
451}
452
453DEFUN_ATTR(cfg_gbproxy_nri_null_del,
454 cfg_gbproxy_nri_null_del_cmd,
455 "nri null del <0-32767> [<0-32767>]",
Daniel Willmann6cfedc42021-02-10 13:33:58 +0100456 NRI_STR NULL_NRI_STR "Remove NULL-NRI value (or range)\n"
Daniel Willmannee834af2020-12-14 16:22:39 +0100457 NRI_FIRST_LAST_STR,
458 CMD_ATTR_IMMEDIATE)
459{
460 int rc;
461 const char *message;
462 rc = osmo_nri_ranges_vty_del(&message, NULL, g_cfg->pool.null_nri_ranges, argc, argv);
463 if (message) {
464 vty_out(vty, "%% %s: " NRI_ARGS_TO_STR_FMT "%s", message, NRI_ARGS_TO_STR_ARGS(argc, argv),
465 VTY_NEWLINE);
466 }
467 if (rc < 0)
468 return CMD_WARNING;
469 return CMD_SUCCESS;
470}
Harald Welte209dc9f2020-12-12 19:02:16 +0100471
Daniel Willmann8f407b12020-12-02 19:33:50 +0100472static void log_set_bvc_filter(struct log_target *target,
473 const uint16_t *bvci)
474{
475 if (bvci) {
476 uintptr_t bvci_filter = *bvci | BVC_LOG_CTX_FLAG;
477 target->filter_map |= (1 << LOG_FLT_GB_BVC);
478 target->filter_data[LOG_FLT_GB_BVC] = (void *)bvci_filter;
479 } else if (target->filter_data[LOG_FLT_GB_BVC]) {
480 target->filter_map = ~(1 << LOG_FLT_GB_BVC);
481 target->filter_data[LOG_FLT_GB_BVC] = NULL;
482 }
483}
484
485DEFUN(logging_fltr_bvc,
486 logging_fltr_bvc_cmd,
487 "logging filter bvc bvci <0-65535>",
488 LOGGING_STR FILTER_STR
489 "Filter based on BSSGP VC\n"
490 "Identify BVC by BVCI\n"
491 "Numeric identifier\n")
492{
493 struct log_target *tgt;
494 uint16_t id = atoi(argv[0]);
495
496 log_tgt_mutex_lock();
497 tgt = osmo_log_vty2tgt(vty);
498 if (!tgt) {
499 log_tgt_mutex_unlock();
500 return CMD_WARNING;
501 }
502
503 log_set_bvc_filter(tgt, &id);
504 log_tgt_mutex_unlock();
505 return CMD_SUCCESS;
506}
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200507
Harald Weltebefe1c32020-12-12 15:15:34 +0100508DEFUN(show_gbproxy_bvc, show_gbproxy_bvc_cmd, "show gbproxy bvc (bss|sgsn) [stats]",
Daniel Willmann2c758f22021-01-18 17:10:51 +0100509 SHOW_STR GBPROXY_STR
Harald Weltebefe1c32020-12-12 15:15:34 +0100510 "Show BSSGP Virtual Connections\n"
Harald Weltec6ecfad2020-12-12 14:17:51 +0100511 "Display BSS-side BVCs\n"
512 "Display SGSN-side BVCs\n"
513 "Show statistics\n")
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200514{
Daniel Willmanne50550e2020-11-26 18:19:21 +0100515 struct gbproxy_nse *nse;
Harald Weltec6ecfad2020-12-12 14:17:51 +0100516 bool show_stats = argc >= 2;
517 int i;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200518
519 if (show_stats)
520 vty_out_rate_ctr_group(vty, "", g_cfg->ctrg);
521
Harald Weltec6ecfad2020-12-12 14:17:51 +0100522 if (!strcmp(argv[0], "bss")) {
523 hash_for_each(g_cfg->bss_nses, i, nse, list)
524 gbproxy_vty_print_nse(vty, nse, show_stats);
525 } else {
526 hash_for_each(g_cfg->sgsn_nses, i, nse, list)
527 gbproxy_vty_print_nse(vty, nse, show_stats);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200528 }
529 return CMD_SUCCESS;
530}
531
Daniel Willmann39a9d0c2022-10-10 16:28:40 +0200532DEFUN(show_gbproxy_bvc_nsei, show_gbproxy_bvc_nsei_cmd, "show gbproxy bvc (bss|sgsn) nsei <0-65535> [stats]",
533 SHOW_STR GBPROXY_STR
534 "Show BSSGP Virtual Connections\n"
535 "Display BSS-side BVCs\n"
536 "Display SGSN-side BVCs\n"
537 "Limit display to cells from an NSE\n"
538 "NSEI to use\n"
539 "Show statistics\n")
540{
541 struct gbproxy_nse *nse;
542 bool show_stats = argc >= 3;
543 int i;
544 uint16_t nsei = atoi(argv[1]);
545
546 if (!strcmp(argv[0], "bss")) {
547 hash_for_each(g_cfg->bss_nses, i, nse, list) {
548 if (nse->nsei == nsei)
549 gbproxy_vty_print_nse(vty, nse, show_stats);
550 }
551 } else {
552 hash_for_each(g_cfg->sgsn_nses, i, nse, list) {
553 if (nse->nsei == nsei)
554 gbproxy_vty_print_nse(vty, nse, show_stats);
555 }
556 }
557 return CMD_SUCCESS;
558}
559
Harald Welte9151b4a2020-12-12 15:16:43 +0100560DEFUN(show_gbproxy_cell, show_gbproxy_cell_cmd, "show gbproxy cell [stats]",
Daniel Willmann2c758f22021-01-18 17:10:51 +0100561 SHOW_STR GBPROXY_STR
Harald Welte9151b4a2020-12-12 15:16:43 +0100562 "Show GPRS Cell Information\n"
563 "Show statistics\n")
564{
565 struct gbproxy_cell *cell;
566 bool show_stats = argc >= 1;
567 int i;
568
569 hash_for_each(g_cfg->cells, i, cell, list)
570 gbproxy_vty_print_cell(vty, cell, show_stats);
571
572 return CMD_SUCCESS;
573}
574
Daniel Willmann39a9d0c2022-10-10 16:28:40 +0200575DEFUN(show_gbproxy_cell_nsei, show_gbproxy_cell_nsei_cmd, "show gbproxy cell nsei <0-65535> [stats]",
576 SHOW_STR GBPROXY_STR
577 "Show GPRS Cell Information\n"
578 "Limit display to cells from an NSE\n"
579 "NSEI to use\n"
580 "Show statistics\n")
581{
582 struct gbproxy_cell *cell;
583 bool show_stats = argc >= 2;
584 int i;
585 uint16_t nsei = atoi(argv[0]);
586
587 hash_for_each(g_cfg->cells, i, cell, list) {
588 if (cell->bss_bvc->nse->nsei == nsei)
589 gbproxy_vty_print_cell(vty, cell, show_stats);
590 }
591
592 return CMD_SUCCESS;
593}
594
Daniel Willmannf2812fb2021-01-18 16:57:01 +0100595DEFUN(show_gbproxy_tlli_cache, show_gbproxy_tlli_cache_cmd,
596 "show gbproxy tlli-cache",
597 SHOW_STR GBPROXY_STR "Show TLLI cache entries\n")
598{
599 struct gbproxy_tlli_cache_entry *entry;
600 struct timespec now;
601 time_t expiry;
602 int i, count = 0;
603
604 osmo_clock_gettime(CLOCK_MONOTONIC, &now);
605 expiry = now.tv_sec - g_cfg->tlli_cache.timeout;
606
607 vty_out(vty, "TLLI cache timeout %us%s", g_cfg->tlli_cache.timeout, VTY_NEWLINE);
608 hash_for_each(g_cfg->tlli_cache.entries, i, entry, list) {
609 time_t valid = entry->tstamp - expiry;
610 struct gbproxy_nse *nse = entry->nse;
611
612 vty_out(vty, " TLLI %08x -> NSE(%05u/%s) valid %lds%s", entry->tlli, nse->nsei,
613 nse->sgsn_facing ? "SGSN" : "BSS", valid, VTY_NEWLINE);
614 count++;
615 }
616 vty_out(vty, "TLLI cache contains %u entries%s", count, VTY_NEWLINE);
617 return CMD_SUCCESS;
618}
619
620DEFUN(show_gbproxy_imsi_cache, show_gbproxy_imsi_cache_cmd,
621 "show gbproxy imsi-cache",
622 SHOW_STR GBPROXY_STR "Show IMSI cache entries\n")
623{
624 struct gbproxy_imsi_cache_entry *entry;
625 struct timespec now;
626 time_t expiry;
627 int i, count = 0;
628
629 osmo_clock_gettime(CLOCK_MONOTONIC, &now);
630 expiry = now.tv_sec - g_cfg->imsi_cache.timeout;
631
632 vty_out(vty, "IMSI cache timeout %us%s", g_cfg->imsi_cache.timeout, VTY_NEWLINE);
633 hash_for_each(g_cfg->imsi_cache.entries, i, entry, list) {
634 time_t valid = entry->tstamp - expiry;
635 struct gbproxy_nse *nse = entry->nse;
636 vty_out(vty, " IMSI %s -> NSE(%05u/%s): valid %lds%s", entry->imsi, nse->nsei,
637 nse->sgsn_facing ? "SGSN" : "BSS", valid, VTY_NEWLINE);
638 count++;
639 }
640 vty_out(vty, "IMSI cache contains %u entries%s", count, VTY_NEWLINE);
641 return CMD_SUCCESS;
642}
643
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200644DEFUN(delete_gb_bvci, delete_gb_bvci_cmd,
645 "delete-gbproxy-peer <0-65534> bvci <2-65534>",
Harald Welte560bdb32020-12-04 22:24:47 +0100646 "Delete a GBProxy bvc by NSEI and optionally BVCI\n"
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200647 "NSEI number\n"
Harald Welte560bdb32020-12-04 22:24:47 +0100648 "Only delete bvc with a matching BVCI\n"
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200649 "BVCI number\n")
650{
651 const uint16_t nsei = atoi(argv[0]);
652 const uint16_t bvci = atoi(argv[1]);
Harald Weltee5209642020-12-05 19:59:45 +0100653 struct gbproxy_nse *nse = gbproxy_nse_by_nsei(g_cfg, nsei, NSE_F_BSS);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200654 int counter;
655
Harald Weltee5209642020-12-05 19:59:45 +0100656 if (!nse) {
657 vty_out(vty, "NSE not found%s", VTY_NEWLINE);
658 return CMD_WARNING;
659 }
660
661 counter = gbproxy_cleanup_bvcs(nse, bvci);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200662
663 if (counter == 0) {
664 vty_out(vty, "BVC not found%s", VTY_NEWLINE);
665 return CMD_WARNING;
666 }
667
668 return CMD_SUCCESS;
669}
670
671DEFUN(delete_gb_nsei, delete_gb_nsei_cmd,
672 "delete-gbproxy-peer <0-65534> (only-bvc|only-nsvc|all) [dry-run]",
Harald Welte560bdb32020-12-04 22:24:47 +0100673 "Delete a GBProxy bvc by NSEI and optionally BVCI\n"
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200674 "NSEI number\n"
675 "Only delete BSSGP connections (BVC)\n"
676 "Only delete dynamic NS connections (NS-VC)\n"
677 "Delete BVC and dynamic NS connections\n"
678 "Show what would be deleted instead of actually deleting\n"
679 )
680{
681 const uint16_t nsei = atoi(argv[0]);
682 const char *mode = argv[1];
683 int dry_run = argc > 2;
684 int delete_bvc = 0;
685 int delete_nsvc = 0;
686 int counter;
687
688 if (strcmp(mode, "only-bvc") == 0)
689 delete_bvc = 1;
690 else if (strcmp(mode, "only-nsvc") == 0)
691 delete_nsvc = 1;
692 else
693 delete_bvc = delete_nsvc = 1;
694
695 if (delete_bvc) {
Daniel Willmann5b897712020-12-04 17:43:27 +0100696 if (!dry_run) {
Harald Weltee5209642020-12-05 19:59:45 +0100697 struct gbproxy_nse *nse = gbproxy_nse_by_nsei(g_cfg, nsei, NSE_F_BSS);
698 counter = gbproxy_cleanup_bvcs(nse, 0);
Daniel Willmann5b897712020-12-04 17:43:27 +0100699 gbproxy_nse_free(nse);
700 } else {
Daniel Willmanne50550e2020-11-26 18:19:21 +0100701 struct gbproxy_nse *nse;
Harald Welte560bdb32020-12-04 22:24:47 +0100702 struct gbproxy_bvc *bvc;
Harald Welte8b4c7942020-12-05 10:14:49 +0100703 int i, j;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200704 counter = 0;
Harald Welted2fef952020-12-05 00:31:07 +0100705 hash_for_each(g_cfg->bss_nses, i, nse, list) {
Daniel Willmanne50550e2020-11-26 18:19:21 +0100706 if (nse->nsei != nsei)
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200707 continue;
Harald Welte8b4c7942020-12-05 10:14:49 +0100708 hash_for_each(nse->bvcs, j, bvc, list) {
Daniel Willmanne50550e2020-11-26 18:19:21 +0100709 vty_out(vty, "BVC: ");
Harald Welte560bdb32020-12-04 22:24:47 +0100710 gbprox_vty_print_bvc(vty, bvc);
Daniel Willmanne50550e2020-11-26 18:19:21 +0100711 counter += 1;
712 }
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200713 }
714 }
715 vty_out(vty, "%sDeleted %d BVC%s",
716 dry_run ? "Not " : "", counter, VTY_NEWLINE);
717 }
718
719 if (delete_nsvc) {
Alexander Couzens951e1332020-09-22 13:21:46 +0200720 struct gprs_ns2_inst *nsi = g_cfg->nsi;
721 struct gprs_ns2_nse *nse;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200722
Alexander Couzens951e1332020-09-22 13:21:46 +0200723 nse = gprs_ns2_nse_by_nsei(nsi, nsei);
724 if (!nse) {
725 vty_out(vty, "NSEI not found%s", VTY_NEWLINE);
726 return CMD_WARNING;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200727 }
Alexander Couzens951e1332020-09-22 13:21:46 +0200728
729 /* TODO: We should NOT delete a persistent NSEI/NSVC as soon as we can check for these */
730 if (!dry_run)
731 gprs_ns2_free_nse(nse);
732
733 vty_out(vty, "%sDeleted NS-VCs for NSEI %d%s",
734 dry_run ? "Not " : "", nsei, VTY_NEWLINE);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200735 }
736
737 return CMD_SUCCESS;
738}
739
Daniel Willmannb387c1e2020-12-27 18:14:39 +0100740/* Only for ttcn3 testing */
741DEFUN_HIDDEN(sgsn_pool_nsf_fixed, sgsn_pool_nsf_fixed_cmd,
742 "sgsn-pool nsf fixed NAME",
743 "SGSN pooling: load balancing across multiple SGSNs.\n"
744 "Customize the Network Selection Function.\n"
745 "Set a fixed SGSN to use (for testing).\n"
746 "The name of the SGSN to use.\n")
747{
748 const char *name = argv[0];
749 struct gbproxy_sgsn *sgsn = gbproxy_sgsn_by_name(g_cfg, name);
750
751 if (!sgsn) {
752 vty_out(vty, "%% Could not find SGSN with name %s%s", name, VTY_NEWLINE);
753 return CMD_WARNING;
754 }
755
756 g_cfg->pool.nsf_override = sgsn;
757 return CMD_SUCCESS;
758}
759
760DEFUN_HIDDEN(sgsn_pool_nsf_normal, sgsn_pool_nsf_normal_cmd,
761 "sgsn-pool nsf normal",
762 "SGSN pooling: load balancing across multiple SGSNs.\n"
763 "Customize the Network Selection Function.\n"
764 "Reset the NSF back to regular operation (for testing).\n")
765{
766 g_cfg->pool.nsf_override = NULL;
767 return CMD_SUCCESS;
768}
769
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200770int gbproxy_vty_init(void)
771{
Harald Weltebefe1c32020-12-12 15:15:34 +0100772 install_element_ve(&show_gbproxy_bvc_cmd);
Daniel Willmann39a9d0c2022-10-10 16:28:40 +0200773 install_element_ve(&show_gbproxy_bvc_nsei_cmd);
Harald Welte9151b4a2020-12-12 15:16:43 +0100774 install_element_ve(&show_gbproxy_cell_cmd);
Daniel Willmann39a9d0c2022-10-10 16:28:40 +0200775 install_element_ve(&show_gbproxy_cell_nsei_cmd);
Daniel Willmannf2812fb2021-01-18 16:57:01 +0100776 install_element_ve(&show_gbproxy_tlli_cache_cmd);
777 install_element_ve(&show_gbproxy_imsi_cache_cmd);
Daniel Willmannee834af2020-12-14 16:22:39 +0100778 install_element_ve(&show_nri_all_cmd);
779 install_element_ve(&show_nri_nsei_cmd);
Daniel Willmann8f407b12020-12-02 19:33:50 +0100780 install_element_ve(&logging_fltr_bvc_cmd);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200781
782 install_element(ENABLE_NODE, &delete_gb_bvci_cmd);
783 install_element(ENABLE_NODE, &delete_gb_nsei_cmd);
Daniel Willmannb387c1e2020-12-27 18:14:39 +0100784 install_element(ENABLE_NODE, &sgsn_pool_nsf_fixed_cmd);
785 install_element(ENABLE_NODE, &sgsn_pool_nsf_normal_cmd);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200786
787 install_element(CONFIG_NODE, &cfg_gbproxy_cmd);
788 install_node(&gbproxy_node, config_write_gbproxy);
Harald Welte209dc9f2020-12-12 19:02:16 +0100789 install_element(GBPROXY_NODE, &cfg_pool_bvc_fc_ratio_cmd);
Daniel Willmannee834af2020-12-14 16:22:39 +0100790 install_element(GBPROXY_NODE, &cfg_gbproxy_nri_bitlen_cmd);
791 install_element(GBPROXY_NODE, &cfg_gbproxy_nri_null_add_cmd);
792 install_element(GBPROXY_NODE, &cfg_gbproxy_nri_null_del_cmd);
793
794 install_element(CONFIG_NODE, &cfg_sgsn_nsei_cmd);
795 install_node(&sgsn_node, config_write_sgsn);
Daniel Willmanna648f3c2020-12-28 18:07:27 +0100796 install_element(SGSN_NODE, &cfg_sgsn_name_cmd);
Daniel Willmannee834af2020-12-14 16:22:39 +0100797 install_element(SGSN_NODE, &cfg_sgsn_allow_attach_cmd);
798 install_element(SGSN_NODE, &cfg_sgsn_no_allow_attach_cmd);
799 install_element(SGSN_NODE, &cfg_sgsn_nri_add_cmd);
800 install_element(SGSN_NODE, &cfg_sgsn_nri_del_cmd);
801
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200802
803 return 0;
804}
805
806int gbproxy_parse_config(const char *config_file, struct gbproxy_config *cfg)
807{
808 int rc;
809
810 g_cfg = cfg;
811 rc = vty_read_config_file(config_file, NULL);
812 if (rc < 0) {
813 fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file);
814 return rc;
815 }
816
817 return 0;
818}