blob: 9ac45e759f5bde915a7a602776eb385530108f3c [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
Harald Welte9151b4a2020-12-12 15:16:43 +0100532DEFUN(show_gbproxy_cell, show_gbproxy_cell_cmd, "show gbproxy cell [stats]",
Daniel Willmann2c758f22021-01-18 17:10:51 +0100533 SHOW_STR GBPROXY_STR
Harald Welte9151b4a2020-12-12 15:16:43 +0100534 "Show GPRS Cell Information\n"
535 "Show statistics\n")
536{
537 struct gbproxy_cell *cell;
538 bool show_stats = argc >= 1;
539 int i;
540
541 hash_for_each(g_cfg->cells, i, cell, list)
542 gbproxy_vty_print_cell(vty, cell, show_stats);
543
544 return CMD_SUCCESS;
545}
546
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200547DEFUN(show_gbproxy_links, show_gbproxy_links_cmd, "show gbproxy links",
Daniel Willmann2c758f22021-01-18 17:10:51 +0100548 SHOW_STR GBPROXY_STR "Show logical links\n")
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200549{
Daniel Willmanne50550e2020-11-26 18:19:21 +0100550 struct gbproxy_nse *nse;
Harald Welte8b4c7942020-12-05 10:14:49 +0100551 int i, j;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200552
Harald Welted2fef952020-12-05 00:31:07 +0100553 hash_for_each(g_cfg->bss_nses, i, nse, list) {
Harald Welte560bdb32020-12-04 22:24:47 +0100554 struct gbproxy_bvc *bvc;
Harald Welte8b4c7942020-12-05 10:14:49 +0100555 hash_for_each(nse->bvcs, j, bvc, list) {
Harald Welte560bdb32020-12-04 22:24:47 +0100556 gbprox_vty_print_bvc(vty, bvc);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200557 }
558 }
559 return CMD_SUCCESS;
560}
561
Daniel Willmannf2812fb2021-01-18 16:57:01 +0100562DEFUN(show_gbproxy_tlli_cache, show_gbproxy_tlli_cache_cmd,
563 "show gbproxy tlli-cache",
564 SHOW_STR GBPROXY_STR "Show TLLI cache entries\n")
565{
566 struct gbproxy_tlli_cache_entry *entry;
567 struct timespec now;
568 time_t expiry;
569 int i, count = 0;
570
571 osmo_clock_gettime(CLOCK_MONOTONIC, &now);
572 expiry = now.tv_sec - g_cfg->tlli_cache.timeout;
573
574 vty_out(vty, "TLLI cache timeout %us%s", g_cfg->tlli_cache.timeout, VTY_NEWLINE);
575 hash_for_each(g_cfg->tlli_cache.entries, i, entry, list) {
576 time_t valid = entry->tstamp - expiry;
577 struct gbproxy_nse *nse = entry->nse;
578
579 vty_out(vty, " TLLI %08x -> NSE(%05u/%s) valid %lds%s", entry->tlli, nse->nsei,
580 nse->sgsn_facing ? "SGSN" : "BSS", valid, VTY_NEWLINE);
581 count++;
582 }
583 vty_out(vty, "TLLI cache contains %u entries%s", count, VTY_NEWLINE);
584 return CMD_SUCCESS;
585}
586
587DEFUN(show_gbproxy_imsi_cache, show_gbproxy_imsi_cache_cmd,
588 "show gbproxy imsi-cache",
589 SHOW_STR GBPROXY_STR "Show IMSI cache entries\n")
590{
591 struct gbproxy_imsi_cache_entry *entry;
592 struct timespec now;
593 time_t expiry;
594 int i, count = 0;
595
596 osmo_clock_gettime(CLOCK_MONOTONIC, &now);
597 expiry = now.tv_sec - g_cfg->imsi_cache.timeout;
598
599 vty_out(vty, "IMSI cache timeout %us%s", g_cfg->imsi_cache.timeout, VTY_NEWLINE);
600 hash_for_each(g_cfg->imsi_cache.entries, i, entry, list) {
601 time_t valid = entry->tstamp - expiry;
602 struct gbproxy_nse *nse = entry->nse;
603 vty_out(vty, " IMSI %s -> NSE(%05u/%s): valid %lds%s", entry->imsi, nse->nsei,
604 nse->sgsn_facing ? "SGSN" : "BSS", valid, VTY_NEWLINE);
605 count++;
606 }
607 vty_out(vty, "IMSI cache contains %u entries%s", count, VTY_NEWLINE);
608 return CMD_SUCCESS;
609}
610
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200611DEFUN(delete_gb_bvci, delete_gb_bvci_cmd,
612 "delete-gbproxy-peer <0-65534> bvci <2-65534>",
Harald Welte560bdb32020-12-04 22:24:47 +0100613 "Delete a GBProxy bvc by NSEI and optionally BVCI\n"
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200614 "NSEI number\n"
Harald Welte560bdb32020-12-04 22:24:47 +0100615 "Only delete bvc with a matching BVCI\n"
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200616 "BVCI number\n")
617{
618 const uint16_t nsei = atoi(argv[0]);
619 const uint16_t bvci = atoi(argv[1]);
Harald Weltee5209642020-12-05 19:59:45 +0100620 struct gbproxy_nse *nse = gbproxy_nse_by_nsei(g_cfg, nsei, NSE_F_BSS);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200621 int counter;
622
Harald Weltee5209642020-12-05 19:59:45 +0100623 if (!nse) {
624 vty_out(vty, "NSE not found%s", VTY_NEWLINE);
625 return CMD_WARNING;
626 }
627
628 counter = gbproxy_cleanup_bvcs(nse, bvci);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200629
630 if (counter == 0) {
631 vty_out(vty, "BVC not found%s", VTY_NEWLINE);
632 return CMD_WARNING;
633 }
634
635 return CMD_SUCCESS;
636}
637
638DEFUN(delete_gb_nsei, delete_gb_nsei_cmd,
639 "delete-gbproxy-peer <0-65534> (only-bvc|only-nsvc|all) [dry-run]",
Harald Welte560bdb32020-12-04 22:24:47 +0100640 "Delete a GBProxy bvc by NSEI and optionally BVCI\n"
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200641 "NSEI number\n"
642 "Only delete BSSGP connections (BVC)\n"
643 "Only delete dynamic NS connections (NS-VC)\n"
644 "Delete BVC and dynamic NS connections\n"
645 "Show what would be deleted instead of actually deleting\n"
646 )
647{
648 const uint16_t nsei = atoi(argv[0]);
649 const char *mode = argv[1];
650 int dry_run = argc > 2;
651 int delete_bvc = 0;
652 int delete_nsvc = 0;
653 int counter;
654
655 if (strcmp(mode, "only-bvc") == 0)
656 delete_bvc = 1;
657 else if (strcmp(mode, "only-nsvc") == 0)
658 delete_nsvc = 1;
659 else
660 delete_bvc = delete_nsvc = 1;
661
662 if (delete_bvc) {
Daniel Willmann5b897712020-12-04 17:43:27 +0100663 if (!dry_run) {
Harald Weltee5209642020-12-05 19:59:45 +0100664 struct gbproxy_nse *nse = gbproxy_nse_by_nsei(g_cfg, nsei, NSE_F_BSS);
665 counter = gbproxy_cleanup_bvcs(nse, 0);
Daniel Willmann5b897712020-12-04 17:43:27 +0100666 gbproxy_nse_free(nse);
667 } else {
Daniel Willmanne50550e2020-11-26 18:19:21 +0100668 struct gbproxy_nse *nse;
Harald Welte560bdb32020-12-04 22:24:47 +0100669 struct gbproxy_bvc *bvc;
Harald Welte8b4c7942020-12-05 10:14:49 +0100670 int i, j;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200671 counter = 0;
Harald Welted2fef952020-12-05 00:31:07 +0100672 hash_for_each(g_cfg->bss_nses, i, nse, list) {
Daniel Willmanne50550e2020-11-26 18:19:21 +0100673 if (nse->nsei != nsei)
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200674 continue;
Harald Welte8b4c7942020-12-05 10:14:49 +0100675 hash_for_each(nse->bvcs, j, bvc, list) {
Daniel Willmanne50550e2020-11-26 18:19:21 +0100676 vty_out(vty, "BVC: ");
Harald Welte560bdb32020-12-04 22:24:47 +0100677 gbprox_vty_print_bvc(vty, bvc);
Daniel Willmanne50550e2020-11-26 18:19:21 +0100678 counter += 1;
679 }
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200680 }
681 }
682 vty_out(vty, "%sDeleted %d BVC%s",
683 dry_run ? "Not " : "", counter, VTY_NEWLINE);
684 }
685
686 if (delete_nsvc) {
Alexander Couzens951e1332020-09-22 13:21:46 +0200687 struct gprs_ns2_inst *nsi = g_cfg->nsi;
688 struct gprs_ns2_nse *nse;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200689
Alexander Couzens951e1332020-09-22 13:21:46 +0200690 nse = gprs_ns2_nse_by_nsei(nsi, nsei);
691 if (!nse) {
692 vty_out(vty, "NSEI not found%s", VTY_NEWLINE);
693 return CMD_WARNING;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200694 }
Alexander Couzens951e1332020-09-22 13:21:46 +0200695
696 /* TODO: We should NOT delete a persistent NSEI/NSVC as soon as we can check for these */
697 if (!dry_run)
698 gprs_ns2_free_nse(nse);
699
700 vty_out(vty, "%sDeleted NS-VCs for NSEI %d%s",
701 dry_run ? "Not " : "", nsei, VTY_NEWLINE);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200702 }
703
704 return CMD_SUCCESS;
705}
706
Daniel Willmannb387c1e2020-12-27 18:14:39 +0100707/* Only for ttcn3 testing */
708DEFUN_HIDDEN(sgsn_pool_nsf_fixed, sgsn_pool_nsf_fixed_cmd,
709 "sgsn-pool nsf fixed NAME",
710 "SGSN pooling: load balancing across multiple SGSNs.\n"
711 "Customize the Network Selection Function.\n"
712 "Set a fixed SGSN to use (for testing).\n"
713 "The name of the SGSN to use.\n")
714{
715 const char *name = argv[0];
716 struct gbproxy_sgsn *sgsn = gbproxy_sgsn_by_name(g_cfg, name);
717
718 if (!sgsn) {
719 vty_out(vty, "%% Could not find SGSN with name %s%s", name, VTY_NEWLINE);
720 return CMD_WARNING;
721 }
722
723 g_cfg->pool.nsf_override = sgsn;
724 return CMD_SUCCESS;
725}
726
727DEFUN_HIDDEN(sgsn_pool_nsf_normal, sgsn_pool_nsf_normal_cmd,
728 "sgsn-pool nsf normal",
729 "SGSN pooling: load balancing across multiple SGSNs.\n"
730 "Customize the Network Selection Function.\n"
731 "Reset the NSF back to regular operation (for testing).\n")
732{
733 g_cfg->pool.nsf_override = NULL;
734 return CMD_SUCCESS;
735}
736
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200737int gbproxy_vty_init(void)
738{
Harald Weltebefe1c32020-12-12 15:15:34 +0100739 install_element_ve(&show_gbproxy_bvc_cmd);
Harald Welte9151b4a2020-12-12 15:16:43 +0100740 install_element_ve(&show_gbproxy_cell_cmd);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200741 install_element_ve(&show_gbproxy_links_cmd);
Daniel Willmannf2812fb2021-01-18 16:57:01 +0100742 install_element_ve(&show_gbproxy_tlli_cache_cmd);
743 install_element_ve(&show_gbproxy_imsi_cache_cmd);
Daniel Willmannee834af2020-12-14 16:22:39 +0100744 install_element_ve(&show_nri_all_cmd);
745 install_element_ve(&show_nri_nsei_cmd);
Daniel Willmann8f407b12020-12-02 19:33:50 +0100746 install_element_ve(&logging_fltr_bvc_cmd);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200747
748 install_element(ENABLE_NODE, &delete_gb_bvci_cmd);
749 install_element(ENABLE_NODE, &delete_gb_nsei_cmd);
Daniel Willmannb387c1e2020-12-27 18:14:39 +0100750 install_element(ENABLE_NODE, &sgsn_pool_nsf_fixed_cmd);
751 install_element(ENABLE_NODE, &sgsn_pool_nsf_normal_cmd);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200752
753 install_element(CONFIG_NODE, &cfg_gbproxy_cmd);
754 install_node(&gbproxy_node, config_write_gbproxy);
Harald Welte209dc9f2020-12-12 19:02:16 +0100755 install_element(GBPROXY_NODE, &cfg_pool_bvc_fc_ratio_cmd);
Daniel Willmannee834af2020-12-14 16:22:39 +0100756 install_element(GBPROXY_NODE, &cfg_gbproxy_nri_bitlen_cmd);
757 install_element(GBPROXY_NODE, &cfg_gbproxy_nri_null_add_cmd);
758 install_element(GBPROXY_NODE, &cfg_gbproxy_nri_null_del_cmd);
759
760 install_element(CONFIG_NODE, &cfg_sgsn_nsei_cmd);
761 install_node(&sgsn_node, config_write_sgsn);
Daniel Willmanna648f3c2020-12-28 18:07:27 +0100762 install_element(SGSN_NODE, &cfg_sgsn_name_cmd);
Daniel Willmannee834af2020-12-14 16:22:39 +0100763 install_element(SGSN_NODE, &cfg_sgsn_allow_attach_cmd);
764 install_element(SGSN_NODE, &cfg_sgsn_no_allow_attach_cmd);
765 install_element(SGSN_NODE, &cfg_sgsn_nri_add_cmd);
766 install_element(SGSN_NODE, &cfg_sgsn_nri_del_cmd);
767
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200768
769 return 0;
770}
771
772int gbproxy_parse_config(const char *config_file, struct gbproxy_config *cfg)
773{
774 int rc;
775
776 g_cfg = cfg;
777 rc = vty_read_config_file(config_file, NULL);
778 if (rc < 0) {
779 fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file);
780 return rc;
781 }
782
783 return 0;
784}