blob: 3a52e32cda59090c86f058a35184f15c50637c8d [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>
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 Weltec6ecfad2020-12-12 14:17:51 +010085 vty_out(vty, "NSEI %5u, PTP-BVCI %5u, RAI %s [%s]%s", bvc->nse->nsei, bvc->bvci,
Philipp Maierda3af942021-02-04 21:54:09 +010086 osmo_rai_name(&bvc->cell->id.raid), osmo_fsm_inst_state_name(bvc->fi), VTY_NEWLINE);
Harald Weltec6ecfad2020-12-12 14:17:51 +010087 }
88}
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +020089
Harald Weltec6ecfad2020-12-12 14:17:51 +010090static void gbproxy_vty_print_nse(struct vty *vty, struct gbproxy_nse *nse, bool show_stats)
91{
92 struct gbproxy_bvc *bvc;
93 int j;
94
95 hash_for_each(nse->bvcs, j, bvc, list) {
96 gbprox_vty_print_bvc(vty, bvc);
97
98 if (show_stats)
99 vty_out_rate_ctr_group(vty, " ", bvc->ctrg);
100 }
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200101}
102
Harald Welte9151b4a2020-12-12 15:16:43 +0100103static void gbproxy_vty_print_cell(struct vty *vty, struct gbproxy_cell *cell, bool show_stats)
104{
Harald Welte9151b4a2020-12-12 15:16:43 +0100105 unsigned int num_sgsn_bvc = 0;
106 unsigned int i;
107
Philipp Maierda3af942021-02-04 21:54:09 +0100108 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 +0100109 if (cell->bss_bvc)
110 vty_out(vty, "BSS NSEI %5u, SGSN NSEI ", cell->bss_bvc->nse->nsei);
111 else
112 vty_out(vty, "BSS NSEI <none>, SGSN NSEI ");
113
114 for (i = 0; i < ARRAY_SIZE(cell->sgsn_bvc); i++) {
115 struct gbproxy_bvc *sgsn_bvc = cell->sgsn_bvc[i];
116 if (sgsn_bvc) {
117 vty_out(vty, "%5u ", sgsn_bvc->nse->nsei);
118 num_sgsn_bvc++;
119 }
120 }
121 if (num_sgsn_bvc)
122 vty_out(vty, "%s", VTY_NEWLINE);
123 else
124 vty_out(vty, "<none>%s", VTY_NEWLINE);
125}
126
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200127static int config_write_gbproxy(struct vty *vty)
128{
Daniel Willmannee834af2020-12-14 16:22:39 +0100129 struct osmo_nri_range *r;
Harald Weltee5209642020-12-05 19:59:45 +0100130
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200131 vty_out(vty, "gbproxy%s", VTY_NEWLINE);
132
Harald Welte209dc9f2020-12-12 19:02:16 +0100133 if (g_cfg->pool.bvc_fc_ratio != 100)
134 vty_out(vty, " pool bvc-flow-control-ratio %u%s", g_cfg->pool.bvc_fc_ratio, VTY_NEWLINE);
135
Daniel Willmannee834af2020-12-14 16:22:39 +0100136 if (g_cfg->pool.nri_bitlen != OSMO_NRI_BITLEN_DEFAULT)
137 vty_out(vty, " nri bitlen %u%s", g_cfg->pool.nri_bitlen, VTY_NEWLINE);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200138
Daniel Willmannee834af2020-12-14 16:22:39 +0100139 llist_for_each_entry(r, &g_cfg->pool.null_nri_ranges->entries, entry) {
140 vty_out(vty, " nri null add %d", r->first);
141 if (r->first != r->last)
142 vty_out(vty, " %d", r->last);
143 vty_out(vty, "%s", VTY_NEWLINE);
144 }
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200145 return CMD_SUCCESS;
146}
147
148DEFUN(cfg_gbproxy,
149 cfg_gbproxy_cmd,
150 "gbproxy",
151 "Configure the Gb proxy")
152{
153 vty->node = GBPROXY_NODE;
154 return CMD_SUCCESS;
155}
156
Daniel Willmannee834af2020-12-14 16:22:39 +0100157/* VTY code for SGSN (pool) configuration */
Harald Weltee5209642020-12-05 19:59:45 +0100158extern const struct bssgp_bvc_fsm_ops sgsn_sig_bvc_fsm_ops;
159#include <osmocom/gprs/protocol/gsm_08_18.h>
160
Daniel Willmannee834af2020-12-14 16:22:39 +0100161static struct cmd_node sgsn_node = {
162 SGSN_NODE,
163 "%s(config-sgsn)# ",
164 1,
165};
166
167static void sgsn_write_nri(struct vty *vty, struct gbproxy_sgsn *sgsn, bool verbose)
168{
169 struct osmo_nri_range *r;
170
171 if (verbose) {
172 vty_out(vty, "sgsn nsei %d%s", sgsn->nse->nsei, VTY_NEWLINE);
173 if (llist_empty(&sgsn->pool.nri_ranges->entries)) {
174 vty_out(vty, " %% no NRI mappings%s", VTY_NEWLINE);
175 return;
176 }
177 }
178
179 llist_for_each_entry(r, &sgsn->pool.nri_ranges->entries, entry) {
180 if (osmo_nri_range_validate(r, 255))
181 vty_out(vty, " %% INVALID RANGE:");
182 vty_out(vty, " nri add %d", r->first);
183 if (r->first != r->last)
184 vty_out(vty, " %d", r->last);
185 vty_out(vty, "%s", VTY_NEWLINE);
186 }
187}
188
189static void write_sgsn(struct vty *vty, struct gbproxy_sgsn *sgsn)
190{
191 vty_out(vty, "sgsn nsei %u%s", sgsn->nse->nsei, VTY_NEWLINE);
Daniel Willmanna648f3c2020-12-28 18:07:27 +0100192 vty_out(vty, " name %s%s", sgsn->name, VTY_NEWLINE);
Daniel Willmannee834af2020-12-14 16:22:39 +0100193 vty_out(vty, " %sallow-attach%s", sgsn->pool.allow_attach ? "" : "no ", VTY_NEWLINE);
194 sgsn_write_nri(vty, sgsn, false);
195}
196
197static int config_write_sgsn(struct vty *vty)
198{
199 struct gbproxy_sgsn *sgsn;
200
201 llist_for_each_entry(sgsn, &g_cfg->sgsns, list)
202 write_sgsn(vty, sgsn);
203
204 return CMD_SUCCESS;
205}
206
207DEFUN(cfg_sgsn_nsei,
208 cfg_sgsn_nsei_cmd,
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200209 "sgsn nsei <0-65534>",
Daniel Willmannee834af2020-12-14 16:22:39 +0100210 "Configure the SGSN\n"
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200211 "NSEI to be used in the connection with the SGSN\n"
212 "The NSEI\n")
213{
Harald Weltee5209642020-12-05 19:59:45 +0100214 uint32_t features = 0; // FIXME: make configurable
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200215 unsigned int nsei = atoi(argv[0]);
Daniel Willmannee834af2020-12-14 16:22:39 +0100216 unsigned int num_sgsn = llist_count(&g_cfg->sgsns);
217 struct gbproxy_sgsn *sgsn;
Harald Weltee5209642020-12-05 19:59:45 +0100218 struct gbproxy_nse *nse;
219 struct gbproxy_bvc *bvc;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200220
Daniel Willmannee834af2020-12-14 16:22:39 +0100221 if (num_sgsn >= GBPROXY_MAX_NR_SGSN) {
222 vty_out(vty, "%% Too many SGSN NSE defined (%d), increase GBPROXY_MAX_NR_SGSN%s",
223 num_sgsn, VTY_NEWLINE);
224 return CMD_WARNING;
225 }
226
227 /* This will have created the gbproxy_nse as well */
228 sgsn = gbproxy_sgsn_by_nsei_or_new(g_cfg, nsei);
229 if (!sgsn)
Harald Weltee5209642020-12-05 19:59:45 +0100230 goto free_nothing;
Daniel Willmannee834af2020-12-14 16:22:39 +0100231 nse = sgsn->nse;
232 if (num_sgsn > 1 && g_cfg->pool.nri_bitlen == 0)
233 vty_out(vty, "%% Multiple SGSNs defined, but no pooling enabled%s", VTY_NEWLINE);
234
Harald Weltee5209642020-12-05 19:59:45 +0100235
236 if (!gbproxy_bvc_by_bvci(nse, 0)) {
237 uint8_t cause = BSSGP_CAUSE_OML_INTERV;
238 bvc = gbproxy_bvc_alloc(nse, 0);
239 if (!bvc)
Daniel Willmannee834af2020-12-14 16:22:39 +0100240 goto free_sgsn;
Harald Weltee5209642020-12-05 19:59:45 +0100241 bvc->fi = bssgp_bvc_fsm_alloc_sig_bss(bvc, nse->cfg->nsi, nsei, features);
242 if (!bvc->fi)
243 goto free_bvc;
Daniel Willmanna8b61652021-02-12 05:05:14 +0100244 bssgp_bvc_fsm_set_max_pdu_len(bvc->fi, nse->max_sdu_len);
Harald Weltee5209642020-12-05 19:59:45 +0100245 bssgp_bvc_fsm_set_ops(bvc->fi, &sgsn_sig_bvc_fsm_ops, bvc);
246 osmo_fsm_inst_dispatch(bvc->fi, BSSGP_BVCFSM_E_REQ_RESET, &cause);
247 }
248
Daniel Willmannee834af2020-12-14 16:22:39 +0100249 vty->node = SGSN_NODE;
250 vty->index = sgsn;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200251 return CMD_SUCCESS;
Harald Weltee5209642020-12-05 19:59:45 +0100252
253free_bvc:
254 gbproxy_bvc_free(bvc);
Daniel Willmannee834af2020-12-14 16:22:39 +0100255free_sgsn:
256 gbproxy_sgsn_free(sgsn);
Harald Weltee5209642020-12-05 19:59:45 +0100257free_nothing:
258 vty_out(vty, "%% Unable to create NSE for NSEI=%05u%s", nsei, VTY_NEWLINE);
259 return CMD_WARNING;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200260}
261
Daniel Willmanna648f3c2020-12-28 18:07:27 +0100262DEFUN(cfg_sgsn_name,
263 cfg_sgsn_name_cmd,
264 "name NAME",
265 "Configure the SGSN\n"
266 "Name the SGSN\n"
267 "The name\n")
268{
269 struct gbproxy_sgsn *sgsn = vty->index;
270 const char *name = argv[0];
271
272
273 osmo_talloc_replace_string(sgsn, &sgsn->name, name);
274 if (!sgsn->name) {
275 vty_out(vty, "%% Unable to set name for SGSN with nsei %05u%s", sgsn->nse->nsei, VTY_NEWLINE);
276 return CMD_WARNING;
277 }
278
279 return CMD_SUCCESS;
280}
281
Daniel Willmannee834af2020-12-14 16:22:39 +0100282DEFUN_ATTR(cfg_sgsn_nri_add, cfg_sgsn_nri_add_cmd,
283 "nri add <0-32767> [<0-32767>]",
Daniel Willmann6cfedc42021-02-10 13:33:58 +0100284 NRI_STR "Add NRI value or range to the NRI mapping for this SGSN\n"
Daniel Willmannee834af2020-12-14 16:22:39 +0100285 NRI_FIRST_LAST_STR,
286 CMD_ATTR_IMMEDIATE)
287{
288 struct gbproxy_sgsn *sgsn = vty->index;
289 struct gbproxy_sgsn *other_sgsn;
290 bool before;
Daniel Willmannfe5d3b72021-02-10 13:34:56 +0100291 bool overlaps = false;
Daniel Willmannee834af2020-12-14 16:22:39 +0100292 int rc;
293 const char *message;
294 struct osmo_nri_range add_range;
295
296 rc = osmo_nri_ranges_vty_add(&message, &add_range, sgsn->pool.nri_ranges, argc, argv, g_cfg->pool.nri_bitlen);
297 if (message) {
298 NRI_WARN(sgsn, "%s: " NRI_ARGS_TO_STR_FMT, message, NRI_ARGS_TO_STR_ARGS(argc, argv));
299 }
300 if (rc < 0)
301 return CMD_WARNING;
302
303 /* Issue a warning about NRI range overlaps (but still allow them).
304 * Overlapping ranges will map to whichever SGSN comes fist in the gbproxy_config->sgsns llist,
305 * which should be the first one defined in the config */
306 before = true;
307
308 llist_for_each_entry(other_sgsn, &g_cfg->sgsns, list) {
309 if (other_sgsn == sgsn) {
310 before = false;
311 continue;
312 }
313 if (osmo_nri_range_overlaps_ranges(&add_range, other_sgsn->pool.nri_ranges)) {
314 uint16_t nsei = sgsn->nse->nsei;
315 uint16_t other_nsei = other_sgsn->nse->nsei;
Daniel Willmannfe5d3b72021-02-10 13:34:56 +0100316 overlaps = true;
Daniel Willmannee834af2020-12-14 16:22:39 +0100317 NRI_WARN(sgsn, "NRI range [%d..%d] overlaps between NSE %05d and NSE %05d."
318 " For overlaps, NSE %05d has higher priority than NSE %05d",
319 add_range.first, add_range.last, nsei, other_nsei,
320 before ? other_nsei : nsei, before ? nsei : other_nsei);
321 }
322 }
Daniel Willmannfe5d3b72021-02-10 13:34:56 +0100323 if (overlaps)
324 return CMD_WARNING;
325 else
326 return CMD_SUCCESS;
Daniel Willmannee834af2020-12-14 16:22:39 +0100327}
328
329DEFUN_ATTR(cfg_sgsn_nri_del, cfg_sgsn_nri_del_cmd,
330 "nri del <0-32767> [<0-32767>]",
Daniel Willmann6cfedc42021-02-10 13:33:58 +0100331 NRI_STR "Remove NRI value or range from the NRI mapping for this SGSN\n"
Daniel Willmannee834af2020-12-14 16:22:39 +0100332 NRI_FIRST_LAST_STR,
333 CMD_ATTR_IMMEDIATE)
334{
335 struct gbproxy_sgsn *sgsn = vty->index;
336 int rc;
337 const char *message;
338
339 rc = osmo_nri_ranges_vty_del(&message, NULL, sgsn->pool.nri_ranges, argc, argv);
340 if (message) {
341 NRI_WARN(sgsn, "%s: " NRI_ARGS_TO_STR_FMT, message, NRI_ARGS_TO_STR_ARGS(argc, argv));
342 }
343 if (rc < 0)
344 return CMD_WARNING;
345 return CMD_SUCCESS;
346}
347
348DEFUN_ATTR(cfg_sgsn_allow_attach, cfg_sgsn_allow_attach_cmd,
349 "allow-attach",
350 "Allow this SGSN to attach new subscribers (default).\n",
351 CMD_ATTR_IMMEDIATE)
352{
353 struct gbproxy_sgsn *sgsn = vty->index;
354 sgsn->pool.allow_attach = true;
355 return CMD_SUCCESS;
356}
357
358DEFUN_ATTR(cfg_sgsn_no_allow_attach, cfg_sgsn_no_allow_attach_cmd,
359 "no allow-attach",
360 NO_STR
Daniel Willmann6cfedc42021-02-10 13:33:58 +0100361 "Do not assign new subscribers to this SGSN."
362 " Useful if an SGSN in an SGSN pool is configured to off-load subscribers."
363 " The SGSN will still be operational for already IMSI-Attached subscribers,"
364 " but the NAS node selection function will skip this SGSN for new subscribers\n",
Daniel Willmannee834af2020-12-14 16:22:39 +0100365 CMD_ATTR_IMMEDIATE)
366{
367 struct gbproxy_sgsn *sgsn = vty->index;
368 sgsn->pool.allow_attach = false;
369 return CMD_SUCCESS;
370}
371
Daniel Willmann182a0b62021-01-18 13:37:40 +0100372DEFUN(sgsn_show_nri_all, show_nri_all_cmd,
Daniel Willmannee834af2020-12-14 16:22:39 +0100373 "show nri all",
374 SHOW_STR NRI_STR "Show all SGSNs\n")
375{
376 struct gbproxy_sgsn *sgsn;
377
378 llist_for_each_entry(sgsn, &g_cfg->sgsns, list)
379 sgsn_write_nri(vty, sgsn, true);
380
381 return CMD_SUCCESS;
382}
383
Daniel Willmann182a0b62021-01-18 13:37:40 +0100384DEFUN(show_nri_nsei, show_nri_nsei_cmd,
Daniel Willmannee834af2020-12-14 16:22:39 +0100385 "show nri nsei <0-65535>",
386 SHOW_STR NRI_STR "Identify SGSN by NSEI\n"
387 "NSEI of the SGSN\n")
388{
389 struct gbproxy_sgsn *sgsn;
390 int nsei = atoi(argv[0]);
391
392 sgsn = gbproxy_sgsn_by_nsei(g_cfg, nsei);
393 if (!sgsn) {
394 vty_out(vty, "%% No SGSN with found for NSEI %05d%s", nsei, VTY_NEWLINE);
395 return CMD_SUCCESS;
396 }
397 sgsn_write_nri(vty, sgsn, true);
398
399 return CMD_SUCCESS;
400}
401
Harald Welte209dc9f2020-12-12 19:02:16 +0100402DEFUN(cfg_pool_bvc_fc_ratio,
403 cfg_pool_bvc_fc_ratio_cmd,
404 "pool bvc-flow-control-ratio <1-100>",
405 "SGSN Pool related configuration\n"
406 "Ratio of BSS-advertised bucket size + leak rate advertised to each SGSN\n"
407 "Ratio of BSS-advertised bucket size + leak rate advertised to each SGSN (Percent)\n")
408{
409 g_cfg->pool.bvc_fc_ratio = atoi(argv[0]);
410 return CMD_SUCCESS;
411}
Daniel Willmannee834af2020-12-14 16:22:39 +0100412DEFUN_ATTR(cfg_gbproxy_nri_bitlen,
413 cfg_gbproxy_nri_bitlen_cmd,
414 "nri bitlen <0-15>",
415 NRI_STR
416 "Set number of bits that an NRI has, to extract from TMSI identities (always starting just after the TMSI's most significant octet).\n"
417 "bit count (0 disables) pooling)\n",
418 CMD_ATTR_IMMEDIATE)
419{
420 g_cfg->pool.nri_bitlen = atoi(argv[0]);
421
422 if (llist_count(&g_cfg->sgsns) > 1 && g_cfg->pool.nri_bitlen == 0)
423 vty_out(vty, "%% Pooling disabled, but multiple SGSNs defined%s", VTY_NEWLINE);
424
425 /* TODO: Verify all nri ranges and warn on mismatch */
426
427 return CMD_SUCCESS;
428}
429
430DEFUN_ATTR(cfg_gbproxy_nri_null_add,
431 cfg_gbproxy_nri_null_add_cmd,
432 "nri null add <0-32767> [<0-32767>]",
433 NRI_STR NULL_NRI_STR "Add NULL-NRI value (or range)\n"
434 NRI_FIRST_LAST_STR,
435 CMD_ATTR_IMMEDIATE)
436{
437 int rc;
438 const char *message;
439
440 rc = osmo_nri_ranges_vty_add(&message, NULL, g_cfg->pool.null_nri_ranges, argc, argv,
441 g_cfg->pool.nri_bitlen);
442 if (message) {
443 vty_out(vty, "%% nri null add: %s: " NRI_ARGS_TO_STR_FMT "%s", message, NRI_ARGS_TO_STR_ARGS(argc, argv),
444 VTY_NEWLINE);
445 vty_out(vty, "%s: \n" NRI_ARGS_TO_STR_FMT, message, NRI_ARGS_TO_STR_ARGS(argc, argv));
446 }
447 if (rc < 0)
448 return CMD_WARNING;
449 return CMD_SUCCESS;
450}
451
452DEFUN_ATTR(cfg_gbproxy_nri_null_del,
453 cfg_gbproxy_nri_null_del_cmd,
454 "nri null del <0-32767> [<0-32767>]",
Daniel Willmann6cfedc42021-02-10 13:33:58 +0100455 NRI_STR NULL_NRI_STR "Remove NULL-NRI value (or range)\n"
Daniel Willmannee834af2020-12-14 16:22:39 +0100456 NRI_FIRST_LAST_STR,
457 CMD_ATTR_IMMEDIATE)
458{
459 int rc;
460 const char *message;
461 rc = osmo_nri_ranges_vty_del(&message, NULL, g_cfg->pool.null_nri_ranges, argc, argv);
462 if (message) {
463 vty_out(vty, "%% %s: " NRI_ARGS_TO_STR_FMT "%s", message, NRI_ARGS_TO_STR_ARGS(argc, argv),
464 VTY_NEWLINE);
465 }
466 if (rc < 0)
467 return CMD_WARNING;
468 return CMD_SUCCESS;
469}
Harald Welte209dc9f2020-12-12 19:02:16 +0100470
Daniel Willmann8f407b12020-12-02 19:33:50 +0100471static void log_set_bvc_filter(struct log_target *target,
472 const uint16_t *bvci)
473{
474 if (bvci) {
475 uintptr_t bvci_filter = *bvci | BVC_LOG_CTX_FLAG;
476 target->filter_map |= (1 << LOG_FLT_GB_BVC);
477 target->filter_data[LOG_FLT_GB_BVC] = (void *)bvci_filter;
478 } else if (target->filter_data[LOG_FLT_GB_BVC]) {
479 target->filter_map = ~(1 << LOG_FLT_GB_BVC);
480 target->filter_data[LOG_FLT_GB_BVC] = NULL;
481 }
482}
483
484DEFUN(logging_fltr_bvc,
485 logging_fltr_bvc_cmd,
486 "logging filter bvc bvci <0-65535>",
487 LOGGING_STR FILTER_STR
488 "Filter based on BSSGP VC\n"
489 "Identify BVC by BVCI\n"
490 "Numeric identifier\n")
491{
492 struct log_target *tgt;
493 uint16_t id = atoi(argv[0]);
494
495 log_tgt_mutex_lock();
496 tgt = osmo_log_vty2tgt(vty);
497 if (!tgt) {
498 log_tgt_mutex_unlock();
499 return CMD_WARNING;
500 }
501
502 log_set_bvc_filter(tgt, &id);
503 log_tgt_mutex_unlock();
504 return CMD_SUCCESS;
505}
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200506
Harald Weltebefe1c32020-12-12 15:15:34 +0100507DEFUN(show_gbproxy_bvc, show_gbproxy_bvc_cmd, "show gbproxy bvc (bss|sgsn) [stats]",
Daniel Willmann2c758f22021-01-18 17:10:51 +0100508 SHOW_STR GBPROXY_STR
Harald Weltebefe1c32020-12-12 15:15:34 +0100509 "Show BSSGP Virtual Connections\n"
Harald Weltec6ecfad2020-12-12 14:17:51 +0100510 "Display BSS-side BVCs\n"
511 "Display SGSN-side BVCs\n"
512 "Show statistics\n")
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200513{
Daniel Willmanne50550e2020-11-26 18:19:21 +0100514 struct gbproxy_nse *nse;
Harald Weltec6ecfad2020-12-12 14:17:51 +0100515 bool show_stats = argc >= 2;
516 int i;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200517
518 if (show_stats)
519 vty_out_rate_ctr_group(vty, "", g_cfg->ctrg);
520
Harald Weltec6ecfad2020-12-12 14:17:51 +0100521 if (!strcmp(argv[0], "bss")) {
522 hash_for_each(g_cfg->bss_nses, i, nse, list)
523 gbproxy_vty_print_nse(vty, nse, show_stats);
524 } else {
525 hash_for_each(g_cfg->sgsn_nses, i, nse, list)
526 gbproxy_vty_print_nse(vty, nse, show_stats);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200527 }
528 return CMD_SUCCESS;
529}
530
Harald Welte9151b4a2020-12-12 15:16:43 +0100531DEFUN(show_gbproxy_cell, show_gbproxy_cell_cmd, "show gbproxy cell [stats]",
Daniel Willmann2c758f22021-01-18 17:10:51 +0100532 SHOW_STR GBPROXY_STR
Harald Welte9151b4a2020-12-12 15:16:43 +0100533 "Show GPRS Cell Information\n"
534 "Show statistics\n")
535{
536 struct gbproxy_cell *cell;
537 bool show_stats = argc >= 1;
538 int i;
539
540 hash_for_each(g_cfg->cells, i, cell, list)
541 gbproxy_vty_print_cell(vty, cell, show_stats);
542
543 return CMD_SUCCESS;
544}
545
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200546DEFUN(show_gbproxy_links, show_gbproxy_links_cmd, "show gbproxy links",
Daniel Willmann2c758f22021-01-18 17:10:51 +0100547 SHOW_STR GBPROXY_STR "Show logical links\n")
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200548{
Daniel Willmanne50550e2020-11-26 18:19:21 +0100549 struct gbproxy_nse *nse;
Harald Welte8b4c7942020-12-05 10:14:49 +0100550 int i, j;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200551
Harald Welted2fef952020-12-05 00:31:07 +0100552 hash_for_each(g_cfg->bss_nses, i, nse, list) {
Harald Welte560bdb32020-12-04 22:24:47 +0100553 struct gbproxy_bvc *bvc;
Harald Welte8b4c7942020-12-05 10:14:49 +0100554 hash_for_each(nse->bvcs, j, bvc, list) {
Harald Welte560bdb32020-12-04 22:24:47 +0100555 gbprox_vty_print_bvc(vty, bvc);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200556 }
557 }
558 return CMD_SUCCESS;
559}
560
Daniel Willmannf2812fb2021-01-18 16:57:01 +0100561DEFUN(show_gbproxy_tlli_cache, show_gbproxy_tlli_cache_cmd,
562 "show gbproxy tlli-cache",
563 SHOW_STR GBPROXY_STR "Show TLLI cache entries\n")
564{
565 struct gbproxy_tlli_cache_entry *entry;
566 struct timespec now;
567 time_t expiry;
568 int i, count = 0;
569
570 osmo_clock_gettime(CLOCK_MONOTONIC, &now);
571 expiry = now.tv_sec - g_cfg->tlli_cache.timeout;
572
573 vty_out(vty, "TLLI cache timeout %us%s", g_cfg->tlli_cache.timeout, VTY_NEWLINE);
574 hash_for_each(g_cfg->tlli_cache.entries, i, entry, list) {
575 time_t valid = entry->tstamp - expiry;
576 struct gbproxy_nse *nse = entry->nse;
577
578 vty_out(vty, " TLLI %08x -> NSE(%05u/%s) valid %lds%s", entry->tlli, nse->nsei,
579 nse->sgsn_facing ? "SGSN" : "BSS", valid, VTY_NEWLINE);
580 count++;
581 }
582 vty_out(vty, "TLLI cache contains %u entries%s", count, VTY_NEWLINE);
583 return CMD_SUCCESS;
584}
585
586DEFUN(show_gbproxy_imsi_cache, show_gbproxy_imsi_cache_cmd,
587 "show gbproxy imsi-cache",
588 SHOW_STR GBPROXY_STR "Show IMSI cache entries\n")
589{
590 struct gbproxy_imsi_cache_entry *entry;
591 struct timespec now;
592 time_t expiry;
593 int i, count = 0;
594
595 osmo_clock_gettime(CLOCK_MONOTONIC, &now);
596 expiry = now.tv_sec - g_cfg->imsi_cache.timeout;
597
598 vty_out(vty, "IMSI cache timeout %us%s", g_cfg->imsi_cache.timeout, VTY_NEWLINE);
599 hash_for_each(g_cfg->imsi_cache.entries, i, entry, list) {
600 time_t valid = entry->tstamp - expiry;
601 struct gbproxy_nse *nse = entry->nse;
602 vty_out(vty, " IMSI %s -> NSE(%05u/%s): valid %lds%s", entry->imsi, nse->nsei,
603 nse->sgsn_facing ? "SGSN" : "BSS", valid, VTY_NEWLINE);
604 count++;
605 }
606 vty_out(vty, "IMSI cache contains %u entries%s", count, VTY_NEWLINE);
607 return CMD_SUCCESS;
608}
609
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200610DEFUN(delete_gb_bvci, delete_gb_bvci_cmd,
611 "delete-gbproxy-peer <0-65534> bvci <2-65534>",
Harald Welte560bdb32020-12-04 22:24:47 +0100612 "Delete a GBProxy bvc by NSEI and optionally BVCI\n"
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200613 "NSEI number\n"
Harald Welte560bdb32020-12-04 22:24:47 +0100614 "Only delete bvc with a matching BVCI\n"
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200615 "BVCI number\n")
616{
617 const uint16_t nsei = atoi(argv[0]);
618 const uint16_t bvci = atoi(argv[1]);
Harald Weltee5209642020-12-05 19:59:45 +0100619 struct gbproxy_nse *nse = gbproxy_nse_by_nsei(g_cfg, nsei, NSE_F_BSS);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200620 int counter;
621
Harald Weltee5209642020-12-05 19:59:45 +0100622 if (!nse) {
623 vty_out(vty, "NSE not found%s", VTY_NEWLINE);
624 return CMD_WARNING;
625 }
626
627 counter = gbproxy_cleanup_bvcs(nse, bvci);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200628
629 if (counter == 0) {
630 vty_out(vty, "BVC not found%s", VTY_NEWLINE);
631 return CMD_WARNING;
632 }
633
634 return CMD_SUCCESS;
635}
636
637DEFUN(delete_gb_nsei, delete_gb_nsei_cmd,
638 "delete-gbproxy-peer <0-65534> (only-bvc|only-nsvc|all) [dry-run]",
Harald Welte560bdb32020-12-04 22:24:47 +0100639 "Delete a GBProxy bvc by NSEI and optionally BVCI\n"
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200640 "NSEI number\n"
641 "Only delete BSSGP connections (BVC)\n"
642 "Only delete dynamic NS connections (NS-VC)\n"
643 "Delete BVC and dynamic NS connections\n"
644 "Show what would be deleted instead of actually deleting\n"
645 )
646{
647 const uint16_t nsei = atoi(argv[0]);
648 const char *mode = argv[1];
649 int dry_run = argc > 2;
650 int delete_bvc = 0;
651 int delete_nsvc = 0;
652 int counter;
653
654 if (strcmp(mode, "only-bvc") == 0)
655 delete_bvc = 1;
656 else if (strcmp(mode, "only-nsvc") == 0)
657 delete_nsvc = 1;
658 else
659 delete_bvc = delete_nsvc = 1;
660
661 if (delete_bvc) {
Daniel Willmann5b897712020-12-04 17:43:27 +0100662 if (!dry_run) {
Harald Weltee5209642020-12-05 19:59:45 +0100663 struct gbproxy_nse *nse = gbproxy_nse_by_nsei(g_cfg, nsei, NSE_F_BSS);
664 counter = gbproxy_cleanup_bvcs(nse, 0);
Daniel Willmann5b897712020-12-04 17:43:27 +0100665 gbproxy_nse_free(nse);
666 } else {
Daniel Willmanne50550e2020-11-26 18:19:21 +0100667 struct gbproxy_nse *nse;
Harald Welte560bdb32020-12-04 22:24:47 +0100668 struct gbproxy_bvc *bvc;
Harald Welte8b4c7942020-12-05 10:14:49 +0100669 int i, j;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200670 counter = 0;
Harald Welted2fef952020-12-05 00:31:07 +0100671 hash_for_each(g_cfg->bss_nses, i, nse, list) {
Daniel Willmanne50550e2020-11-26 18:19:21 +0100672 if (nse->nsei != nsei)
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200673 continue;
Harald Welte8b4c7942020-12-05 10:14:49 +0100674 hash_for_each(nse->bvcs, j, bvc, list) {
Daniel Willmanne50550e2020-11-26 18:19:21 +0100675 vty_out(vty, "BVC: ");
Harald Welte560bdb32020-12-04 22:24:47 +0100676 gbprox_vty_print_bvc(vty, bvc);
Daniel Willmanne50550e2020-11-26 18:19:21 +0100677 counter += 1;
678 }
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200679 }
680 }
681 vty_out(vty, "%sDeleted %d BVC%s",
682 dry_run ? "Not " : "", counter, VTY_NEWLINE);
683 }
684
685 if (delete_nsvc) {
Alexander Couzens951e1332020-09-22 13:21:46 +0200686 struct gprs_ns2_inst *nsi = g_cfg->nsi;
687 struct gprs_ns2_nse *nse;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200688
Alexander Couzens951e1332020-09-22 13:21:46 +0200689 nse = gprs_ns2_nse_by_nsei(nsi, nsei);
690 if (!nse) {
691 vty_out(vty, "NSEI not found%s", VTY_NEWLINE);
692 return CMD_WARNING;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200693 }
Alexander Couzens951e1332020-09-22 13:21:46 +0200694
695 /* TODO: We should NOT delete a persistent NSEI/NSVC as soon as we can check for these */
696 if (!dry_run)
697 gprs_ns2_free_nse(nse);
698
699 vty_out(vty, "%sDeleted NS-VCs for NSEI %d%s",
700 dry_run ? "Not " : "", nsei, VTY_NEWLINE);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200701 }
702
703 return CMD_SUCCESS;
704}
705
Daniel Willmannb387c1e2020-12-27 18:14:39 +0100706/* Only for ttcn3 testing */
707DEFUN_HIDDEN(sgsn_pool_nsf_fixed, sgsn_pool_nsf_fixed_cmd,
708 "sgsn-pool nsf fixed NAME",
709 "SGSN pooling: load balancing across multiple SGSNs.\n"
710 "Customize the Network Selection Function.\n"
711 "Set a fixed SGSN to use (for testing).\n"
712 "The name of the SGSN to use.\n")
713{
714 const char *name = argv[0];
715 struct gbproxy_sgsn *sgsn = gbproxy_sgsn_by_name(g_cfg, name);
716
717 if (!sgsn) {
718 vty_out(vty, "%% Could not find SGSN with name %s%s", name, VTY_NEWLINE);
719 return CMD_WARNING;
720 }
721
722 g_cfg->pool.nsf_override = sgsn;
723 return CMD_SUCCESS;
724}
725
726DEFUN_HIDDEN(sgsn_pool_nsf_normal, sgsn_pool_nsf_normal_cmd,
727 "sgsn-pool nsf normal",
728 "SGSN pooling: load balancing across multiple SGSNs.\n"
729 "Customize the Network Selection Function.\n"
730 "Reset the NSF back to regular operation (for testing).\n")
731{
732 g_cfg->pool.nsf_override = NULL;
733 return CMD_SUCCESS;
734}
735
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200736int gbproxy_vty_init(void)
737{
Harald Weltebefe1c32020-12-12 15:15:34 +0100738 install_element_ve(&show_gbproxy_bvc_cmd);
Harald Welte9151b4a2020-12-12 15:16:43 +0100739 install_element_ve(&show_gbproxy_cell_cmd);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200740 install_element_ve(&show_gbproxy_links_cmd);
Daniel Willmannf2812fb2021-01-18 16:57:01 +0100741 install_element_ve(&show_gbproxy_tlli_cache_cmd);
742 install_element_ve(&show_gbproxy_imsi_cache_cmd);
Daniel Willmannee834af2020-12-14 16:22:39 +0100743 install_element_ve(&show_nri_all_cmd);
744 install_element_ve(&show_nri_nsei_cmd);
Daniel Willmann8f407b12020-12-02 19:33:50 +0100745 install_element_ve(&logging_fltr_bvc_cmd);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200746
747 install_element(ENABLE_NODE, &delete_gb_bvci_cmd);
748 install_element(ENABLE_NODE, &delete_gb_nsei_cmd);
Daniel Willmannb387c1e2020-12-27 18:14:39 +0100749 install_element(ENABLE_NODE, &sgsn_pool_nsf_fixed_cmd);
750 install_element(ENABLE_NODE, &sgsn_pool_nsf_normal_cmd);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200751
752 install_element(CONFIG_NODE, &cfg_gbproxy_cmd);
753 install_node(&gbproxy_node, config_write_gbproxy);
Harald Welte209dc9f2020-12-12 19:02:16 +0100754 install_element(GBPROXY_NODE, &cfg_pool_bvc_fc_ratio_cmd);
Daniel Willmannee834af2020-12-14 16:22:39 +0100755 install_element(GBPROXY_NODE, &cfg_gbproxy_nri_bitlen_cmd);
756 install_element(GBPROXY_NODE, &cfg_gbproxy_nri_null_add_cmd);
757 install_element(GBPROXY_NODE, &cfg_gbproxy_nri_null_del_cmd);
758
759 install_element(CONFIG_NODE, &cfg_sgsn_nsei_cmd);
760 install_node(&sgsn_node, config_write_sgsn);
Daniel Willmanna648f3c2020-12-28 18:07:27 +0100761 install_element(SGSN_NODE, &cfg_sgsn_name_cmd);
Daniel Willmannee834af2020-12-14 16:22:39 +0100762 install_element(SGSN_NODE, &cfg_sgsn_allow_attach_cmd);
763 install_element(SGSN_NODE, &cfg_sgsn_no_allow_attach_cmd);
764 install_element(SGSN_NODE, &cfg_sgsn_nri_add_cmd);
765 install_element(SGSN_NODE, &cfg_sgsn_nri_del_cmd);
766
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200767
768 return 0;
769}
770
771int gbproxy_parse_config(const char *config_file, struct gbproxy_config *cfg)
772{
773 int rc;
774
775 g_cfg = cfg;
776 rc = vty_read_config_file(config_file, NULL);
777 if (rc < 0) {
778 fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file);
779 return rc;
780 }
781
782 return 0;
783}