blob: f8036551b51c62c3077d3f092ca7409b3de84f0a [file] [log] [blame]
Alexander Couzens6a161492020-07-12 13:45:50 +02001/*! \file gprs_ns2_vty.c
2 * VTY interface for our GPRS Networks Service (NS) implementation. */
3
Pau Espin Pedrolcaf53172021-01-28 13:37:50 +01004/* (C) 2009-2014 by Harald Welte <laforge@gnumonks.org>
5 * (C) 2016-2017 by sysmocom - s.f.m.c. GmbH
6 * (C) 2020 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
Alexander Couzens6a161492020-07-12 13:45:50 +02007 * Author: Alexander Couzens <lynxis@fe80.eu>
8 *
9 * All Rights Reserved
10 *
11 * SPDX-License-Identifier: GPL-2.0+
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25 *
26 */
27
28#include <stdlib.h>
29#include <unistd.h>
30#include <errno.h>
31#include <stdint.h>
32
33#include <arpa/inet.h>
Alexander Couzens841817e2020-11-19 00:41:29 +010034#include <net/if.h>
Alexander Couzens6a161492020-07-12 13:45:50 +020035
Pau Espin Pedrolcaf53172021-01-28 13:37:50 +010036#include <osmocom/core/msgb.h>
Alexander Couzens6a161492020-07-12 13:45:50 +020037#include <osmocom/core/byteswap.h>
Daniel Willmanndbab7142020-11-18 14:19:56 +010038#include <osmocom/core/fsm.h>
Alexander Couzensb3060942021-01-25 16:27:33 +010039#include <osmocom/core/talloc.h>
Pau Espin Pedrolcaf53172021-01-28 13:37:50 +010040#include <osmocom/core/select.h>
41#include <osmocom/core/rate_ctr.h>
42#include <osmocom/core/socket.h>
Alexander Couzensb3060942021-01-25 16:27:33 +010043#include <osmocom/core/sockaddr_str.h>
Pau Espin Pedrolcaf53172021-01-28 13:37:50 +010044#include <osmocom/core/linuxlist.h>
Alexander Couzens6a161492020-07-12 13:45:50 +020045#include <osmocom/core/socket.h>
Alexander Couzens841817e2020-11-19 00:41:29 +010046#include <osmocom/gprs/frame_relay.h>
Alexander Couzens6a161492020-07-12 13:45:50 +020047#include <osmocom/gprs/gprs_ns2.h>
48#include <osmocom/gsm/tlv.h>
Pau Espin Pedrolcaf53172021-01-28 13:37:50 +010049#include <osmocom/vty/vty.h>
Alexander Couzens6a161492020-07-12 13:45:50 +020050#include <osmocom/vty/command.h>
51#include <osmocom/vty/logging.h>
Alexander Couzensb3060942021-01-25 16:27:33 +010052#include <osmocom/vty/telnet_interface.h>
Pau Espin Pedrolcaf53172021-01-28 13:37:50 +010053#include <osmocom/vty/misc.h>
Alexander Couzens6a161492020-07-12 13:45:50 +020054
55#include "gprs_ns2_internal.h"
56
Daniel Willmanncb3e9b52020-12-02 15:50:22 +010057#define SHOW_NS_STR "Display information about the NS protocol\n"
Alexander Couzens6a161492020-07-12 13:45:50 +020058
Pau Espin Pedrolcaf53172021-01-28 13:37:50 +010059struct ns2_vty_priv {
60 /* global listen */
61 struct osmo_sockaddr_str udp;
62 struct osmo_sockaddr_str frgreaddr;
Alexander Couzensb3060942021-01-25 16:27:33 +010063 int dscp;
Pau Espin Pedrolcaf53172021-01-28 13:37:50 +010064 enum gprs_ns2_vc_mode vc_mode;
65 bool frgre;
66
67 struct llist_head vtyvc;
Alexander Couzensb3060942021-01-25 16:27:33 +010068};
69
Pau Espin Pedrolcaf53172021-01-28 13:37:50 +010070struct ns2_vty_vc {
71 struct llist_head list;
72
73 struct osmo_sockaddr_str remote;
74 enum gprs_ns2_ll ll;
75
76 /* old vty code doesnt support multiple NSVCI per NSEI */
77 uint16_t nsei;
78 uint16_t nsvci;
79 uint16_t frdlci;
80
81 struct {
82 enum osmo_fr_role role;
83 } fr;
84
85 char netif[IF_NAMESIZE];
86
87 bool remote_end_is_sgsn;
88 bool configured;
89};
90
91static struct gprs_ns2_inst *vty_nsi = NULL;
92static struct ns2_vty_priv priv;
93static struct osmo_fr_network *vty_fr_network = NULL;
94
95/* FIXME: this should go to some common file as it is copied
96 * in vty_interface.c of the BSC */
Alexander Couzens6a161492020-07-12 13:45:50 +020097static const struct value_string gprs_ns_timer_strs[] = {
98 { 0, "tns-block" },
99 { 1, "tns-block-retries" },
100 { 2, "tns-reset" },
101 { 3, "tns-reset-retries" },
102 { 4, "tns-test" },
103 { 5, "tns-alive" },
104 { 6, "tns-alive-retries" },
105 { 7, "tsns-prov" },
Harald Welte33c3c062020-12-16 11:59:19 +0100106 { 8, "tsns-size-retries" },
107 { 9, "tsns-config-retries" },
Alexander Couzens6a161492020-07-12 13:45:50 +0200108 { 0, NULL }
109};
110
Pau Espin Pedrolcaf53172021-01-28 13:37:50 +0100111static void log_set_nse_filter(struct log_target *target,
112 struct gprs_ns2_nse *nse)
Daniel Willmann751977b2020-12-02 18:59:44 +0100113{
Pau Espin Pedrolcaf53172021-01-28 13:37:50 +0100114 if (nse) {
115 target->filter_map |= (1 << LOG_FLT_GB_NSE);
116 target->filter_data[LOG_FLT_GB_NSE] = nse;
117 } else if (target->filter_data[LOG_FLT_GB_NSE]) {
118 target->filter_map = ~(1 << LOG_FLT_GB_NSE);
119 target->filter_data[LOG_FLT_GB_NSE] = NULL;
Daniel Willmann751977b2020-12-02 18:59:44 +0100120 }
121}
122
Pau Espin Pedrolcaf53172021-01-28 13:37:50 +0100123static void log_set_nsvc_filter(struct log_target *target,
124 struct gprs_ns2_vc *nsvc)
Alexander Couzens6a161492020-07-12 13:45:50 +0200125{
Pau Espin Pedrolcaf53172021-01-28 13:37:50 +0100126 if (nsvc) {
127 target->filter_map |= (1 << LOG_FLT_GB_NSVC);
128 target->filter_data[LOG_FLT_GB_NSVC] = nsvc;
129 } else if (target->filter_data[LOG_FLT_GB_NSVC]) {
130 target->filter_map = ~(1 << LOG_FLT_GB_NSVC);
131 target->filter_data[LOG_FLT_GB_NSVC] = NULL;
Alexander Couzens6a161492020-07-12 13:45:50 +0200132 }
133}
134
135static struct cmd_node ns_node = {
136 L_NS_NODE,
137 "%s(config-ns)# ",
138 1,
139};
140
Pau Espin Pedrolcaf53172021-01-28 13:37:50 +0100141static struct ns2_vty_vc *vtyvc_alloc(uint16_t nsei) {
142 struct ns2_vty_vc *vtyvc = talloc_zero(vty_nsi, struct ns2_vty_vc);
143 if (!vtyvc)
144 return vtyvc;
145
146 vtyvc->nsei = nsei;
147
148 llist_add(&vtyvc->list, &priv.vtyvc);
149
150 return vtyvc;
151}
152
153static void ns2_vc_free(struct ns2_vty_vc *vtyvc) {
154 if (!vtyvc)
155 return;
156
157 llist_del(&vtyvc->list);
158 talloc_free(vtyvc);
159}
160
161static struct ns2_vty_vc *vtyvc_by_nsei(uint16_t nsei, bool alloc_missing) {
162 struct ns2_vty_vc *vtyvc;
163
164 llist_for_each_entry(vtyvc, &priv.vtyvc, list) {
165 if (vtyvc->nsei == nsei)
166 return vtyvc;
167 }
168
169 if (!alloc_missing)
170 return NULL;
171
172 vtyvc = vtyvc_alloc(nsei);
173 if (!vtyvc)
174 return vtyvc;
175
176 vtyvc->nsei = nsei;
177 return vtyvc;
178}
179
180static int config_write_ns(struct vty *vty)
181{
182 struct ns2_vty_vc *vtyvc;
183 unsigned int i;
184 struct osmo_sockaddr_str sockstr;
185
186 vty_out(vty, "ns%s", VTY_NEWLINE);
187
188 /* global configuration must be written first, as some of it may be
189 * relevant when creating the NSE/NSVC later below */
190
191 vty_out(vty, " encapsulation framerelay-gre enabled %u%s",
192 priv.frgre ? 1 : 0, VTY_NEWLINE);
193
194 if (priv.frgre) {
195 if (strlen(priv.frgreaddr.ip)) {
196 vty_out(vty, " encapsulation framerelay-gre local-ip %s%s",
197 sockstr.ip, VTY_NEWLINE);
198 }
199 } else {
200 if (strlen(priv.udp.ip)) {
201 vty_out(vty, " encapsulation udp local-ip %s%s",
202 priv.udp.ip, VTY_NEWLINE);
203 }
204
205 if (priv.udp.port)
206 vty_out(vty, " encapsulation udp local-port %u%s",
207 priv.udp.port, VTY_NEWLINE);
208 }
209
210 if (priv.dscp)
211 vty_out(vty, " encapsulation udp dscp %d%s",
212 priv.dscp, VTY_NEWLINE);
213
214 vty_out(vty, " encapsulation udp use-reset-block-unblock %s%s",
215 priv.vc_mode == GPRS_NS2_VC_MODE_BLOCKRESET ? "enabled" : "disabled", VTY_NEWLINE);
216
217 llist_for_each_entry(vtyvc, &priv.vtyvc, list) {
218 vty_out(vty, " nse %u nsvci %u%s",
219 vtyvc->nsei, vtyvc->nsvci, VTY_NEWLINE);
220
221 vty_out(vty, " nse %u remote-role %s%s",
222 vtyvc->nsei, vtyvc->remote_end_is_sgsn ? "sgsn" : "bss",
223 VTY_NEWLINE);
224
225 switch (vtyvc->ll) {
226 case GPRS_NS2_LL_UDP:
227 vty_out(vty, " nse %u encapsulation udp%s", vtyvc->nsei, VTY_NEWLINE);
228 vty_out(vty, " nse %u remote-ip %s%s",
229 vtyvc->nsei,
230 vtyvc->remote.ip,
231 VTY_NEWLINE);
232 vty_out(vty, " nse %u remote-port %u%s",
233 vtyvc->nsei, vtyvc->remote.port,
234 VTY_NEWLINE);
235 break;
236 case GPRS_NS2_LL_FR_GRE:
237 vty_out(vty, " nse %u encapsulation framerelay-gre%s",
238 vtyvc->nsei, VTY_NEWLINE);
239 vty_out(vty, " nse %u remote-ip %s%s",
240 vtyvc->nsei,
241 vtyvc->remote.ip,
242 VTY_NEWLINE);
243 vty_out(vty, " nse %u fr-dlci %u%s",
244 vtyvc->nsei, vtyvc->frdlci,
245 VTY_NEWLINE);
246 break;
247 case GPRS_NS2_LL_FR:
248 vty_out(vty, " nse %u fr %s dlci %u%s",
249 vtyvc->nsei, vtyvc->netif, vtyvc->frdlci,
250 VTY_NEWLINE);
251 break;
252 default:
253 break;
254 }
255 }
256
257 for (i = 0; i < ARRAY_SIZE(vty_nsi->timeout); i++)
258 vty_out(vty, " timer %s %u%s",
259 get_value_string(gprs_ns_timer_strs, i),
260 vty_nsi->timeout[i], VTY_NEWLINE);
261
262 return CMD_SUCCESS;
263}
264
Alexander Couzens6a161492020-07-12 13:45:50 +0200265DEFUN(cfg_ns, cfg_ns_cmd,
266 "ns",
267 "Configure the GPRS Network Service")
268{
269 vty->node = L_NS_NODE;
270 return CMD_SUCCESS;
271}
272
273static void dump_nsvc(struct vty *vty, struct gprs_ns2_vc *nsvc, bool stats)
274{
Harald Weltedc2d0802020-12-01 18:17:28 +0100275 char nsvci_str[32];
276
277 if (nsvc->nsvci_is_valid)
278 snprintf(nsvci_str, sizeof(nsvci_str), "%05u", nsvc->nsvci);
279 else
280 snprintf(nsvci_str, sizeof(nsvci_str), "none");
281
282 vty_out(vty, " NSVCI %s: %s %s data_weight=%u sig_weight=%u %s%s", nsvci_str,
283 osmo_fsm_inst_state_name(nsvc->fi),
284 nsvc->persistent ? "PERSIST" : "DYNAMIC",
285 nsvc->data_weight, nsvc->sig_weight,
286 gprs_ns2_ll_str(nsvc), VTY_NEWLINE);
Alexander Couzens6a161492020-07-12 13:45:50 +0200287
288 if (stats) {
Harald Welte7aa60992020-12-01 17:53:17 +0100289 vty_out_rate_ctr_group(vty, " ", nsvc->ctrg);
290 vty_out_stat_item_group(vty, " ", nsvc->statg);
Alexander Couzens6a161492020-07-12 13:45:50 +0200291 }
292}
293
294static void dump_nse(struct vty *vty, const struct gprs_ns2_nse *nse, bool stats, bool persistent_only)
295{
296 struct gprs_ns2_vc *nsvc;
297
Harald Welte0ff12ad2020-12-01 17:51:07 +0100298 vty_out(vty, "NSEI %05u: %s, %s%s", nse->nsei, gprs_ns2_lltype_str(nse->ll),
299 nse->alive ? "ALIVE" : "DEAD", VTY_NEWLINE);
Alexander Couzens6a161492020-07-12 13:45:50 +0200300
Alexander Couzens8dfc24c2021-01-25 16:09:23 +0100301 ns2_sns_dump_vty(vty, " ", nse, stats);
Alexander Couzens6a161492020-07-12 13:45:50 +0200302 llist_for_each_entry(nsvc, &nse->nsvc, list) {
303 if (persistent_only) {
304 if (nsvc->persistent)
305 dump_nsvc(vty, nsvc, stats);
306 } else {
307 dump_nsvc(vty, nsvc, stats);
308 }
309 }
310}
311
Alexander Couzens22f34712020-10-02 02:34:39 +0200312static void dump_bind(struct vty *vty, const struct gprs_ns2_vc_bind *bind, bool stats)
313{
314 if (bind->dump_vty)
315 bind->dump_vty(bind, vty, stats);
316}
317
Harald Welte2fce19a2020-12-01 17:52:55 +0100318static void dump_ns_bind(struct vty *vty, const struct gprs_ns2_inst *nsi, bool stats)
Alexander Couzens6a161492020-07-12 13:45:50 +0200319{
Alexander Couzens22f34712020-10-02 02:34:39 +0200320 struct gprs_ns2_vc_bind *bind;
Alexander Couzens6a161492020-07-12 13:45:50 +0200321
Alexander Couzens22f34712020-10-02 02:34:39 +0200322 llist_for_each_entry(bind, &nsi->binding, list) {
323 dump_bind(vty, bind, stats);
324 }
Harald Welte2fce19a2020-12-01 17:52:55 +0100325}
326
327
328static void dump_ns_entities(struct vty *vty, const struct gprs_ns2_inst *nsi, bool stats, bool persistent_only)
329{
330 struct gprs_ns2_nse *nse;
Alexander Couzens22f34712020-10-02 02:34:39 +0200331
Alexander Couzens6a161492020-07-12 13:45:50 +0200332 llist_for_each_entry(nse, &nsi->nse, list) {
333 dump_nse(vty, nse, stats, persistent_only);
Alexander Couzens6a161492020-07-12 13:45:50 +0200334 }
Alexander Couzens6a161492020-07-12 13:45:50 +0200335}
336
Harald Welte25ee7552020-12-02 22:14:00 +0100337/* Backwards compatibility, among other things for the TestVTYGbproxy which expects
338 * 'show ns' to output something about binds */
339DEFUN_HIDDEN(show_ns, show_ns_cmd, "show ns",
340 SHOW_STR SHOW_NS_STR)
341{
342 dump_ns_entities(vty, vty_nsi, false, false);
343 dump_ns_bind(vty, vty_nsi, false);
344 return CMD_SUCCESS;
345}
346
347
Harald Welte2fce19a2020-12-01 17:52:55 +0100348DEFUN(show_ns_binds, show_ns_binds_cmd, "show ns binds [stats]",
Daniel Willmanncb3e9b52020-12-02 15:50:22 +0100349 SHOW_STR SHOW_NS_STR
Harald Welte2fce19a2020-12-01 17:52:55 +0100350 "Display information about the NS protocol binds\n"
351 "Include statistic\n")
Alexander Couzens6a161492020-07-12 13:45:50 +0200352{
Harald Welte2fce19a2020-12-01 17:52:55 +0100353 bool stats = false;
354 if (argc > 0)
355 stats = true;
356
357 dump_ns_bind(vty, vty_nsi, stats);
Alexander Couzens6a161492020-07-12 13:45:50 +0200358 return CMD_SUCCESS;
359}
360
Harald Welte2fce19a2020-12-01 17:52:55 +0100361DEFUN(show_ns_entities, show_ns_entities_cmd, "show ns entities [stats]",
Daniel Willmanncb3e9b52020-12-02 15:50:22 +0100362 SHOW_STR SHOW_NS_STR
Harald Welte2fce19a2020-12-01 17:52:55 +0100363 "Display information about the NS protocol entities (NSEs)\n"
Alexander Couzens6a161492020-07-12 13:45:50 +0200364 "Include statistics\n")
365{
Harald Welte2fce19a2020-12-01 17:52:55 +0100366 bool stats = false;
367 if (argc > 0)
368 stats = true;
369
370 dump_ns_entities(vty, vty_nsi, stats, false);
Alexander Couzens6a161492020-07-12 13:45:50 +0200371 return CMD_SUCCESS;
372}
373
374DEFUN(show_ns_pers, show_ns_pers_cmd, "show ns persistent",
Daniel Willmanncb3e9b52020-12-02 15:50:22 +0100375 SHOW_STR SHOW_NS_STR
Alexander Couzens6a161492020-07-12 13:45:50 +0200376 "Show only persistent NS\n")
377{
Harald Welte2fce19a2020-12-01 17:52:55 +0100378 dump_ns_entities(vty, vty_nsi, true, true);
Alexander Couzens6a161492020-07-12 13:45:50 +0200379 return CMD_SUCCESS;
380}
381
382DEFUN(show_nse, show_nse_cmd, "show ns (nsei|nsvc) <0-65535> [stats]",
Daniel Willmanncb3e9b52020-12-02 15:50:22 +0100383 SHOW_STR SHOW_NS_STR
Alexander Couzens6a161492020-07-12 13:45:50 +0200384 "Select one NSE by its NSE Identifier\n"
385 "Select one NSE by its NS-VC Identifier\n"
386 "The Identifier of selected type\n"
387 "Include Statistics\n")
388{
389 struct gprs_ns2_inst *nsi = vty_nsi;
390 struct gprs_ns2_nse *nse;
391 struct gprs_ns2_vc *nsvc;
392 uint16_t id = atoi(argv[1]);
393 bool show_stats = false;
394
395 if (argc >= 3)
396 show_stats = true;
397
398 if (!strcmp(argv[0], "nsei")) {
399 nse = gprs_ns2_nse_by_nsei(nsi, id);
400 if (!nse) {
401 return CMD_WARNING;
402 }
403
404 dump_nse(vty, nse, show_stats, false);
405 } else {
406 nsvc = gprs_ns2_nsvc_by_nsvci(nsi, id);
407
408 if (!nsvc) {
409 vty_out(vty, "No such NS Entity%s", VTY_NEWLINE);
410 return CMD_WARNING;
411 }
412
413 dump_nsvc(vty, nsvc, show_stats);
414 }
415
416 return CMD_SUCCESS;
417}
418
Daniel Willmanndbab7142020-11-18 14:19:56 +0100419static int nsvc_force_unconf_cb(struct gprs_ns2_vc *nsvc, void *ctx)
420{
Alexander Couzens8dfc24c2021-01-25 16:09:23 +0100421 ns2_vc_force_unconfigured(nsvc);
Daniel Willmanndbab7142020-11-18 14:19:56 +0100422 return 0;
423}
424
425DEFUN_HIDDEN(nsvc_force_unconf, nsvc_force_unconf_cmd,
426 "nsvc nsei <0-65535> force-unconfigured",
427 "NS Virtual Connection\n"
428 "The NSEI\n"
429 "Reset the NSVCs back to initial state\n"
430 )
431{
432 struct gprs_ns2_inst *nsi = vty_nsi;
433 struct gprs_ns2_nse *nse;
434
435 uint16_t id = atoi(argv[0]);
436
437 nse = gprs_ns2_nse_by_nsei(nsi, id);
438 if (!nse) {
439 vty_out(vty, "Could not find NSE for NSEI %u%s", id, VTY_NEWLINE);
440 return CMD_WARNING;
441 }
442
Pau Espin Pedrolcaf53172021-01-28 13:37:50 +0100443 if (nse->dialect == GPRS_NS2_DIALECT_SNS) {
Alexander Couzens280ed782020-12-21 18:25:41 +0100444 gprs_ns2_free_nsvcs(nse);
445 } else {
446 /* Perform the operation for all nsvc */
447 gprs_ns2_nse_foreach_nsvc(nse, nsvc_force_unconf_cb, NULL);
448 }
Daniel Willmanndbab7142020-11-18 14:19:56 +0100449
450 return CMD_SUCCESS;
451}
452
Pau Espin Pedrolcaf53172021-01-28 13:37:50 +0100453#define NSE_CMD_STR "Persistent NS Entity\n" "NS Entity ID (NSEI)\n"
454
455DEFUN(cfg_nse_fr, cfg_nse_fr_cmd,
456 "nse <0-65535> nsvci <0-65535> (fr|frnet) NETIF dlci <0-1023>",
457 NSE_CMD_STR
458 "NS Virtual Connection\n"
459 "NS Virtual Connection ID (NSVCI)\n"
460 "Frame Relay User-Side\n"
461 "Frame Relay Network-Side\n"
462 IFNAME_STR
463 "Data Link connection identifier\n"
464 "Data Link connection identifier\n"
465 )
Alexander Couzens841817e2020-11-19 00:41:29 +0100466{
Pau Espin Pedrolcaf53172021-01-28 13:37:50 +0100467 struct ns2_vty_vc *vtyvc;
Alexander Couzens841817e2020-11-19 00:41:29 +0100468
Pau Espin Pedrolcaf53172021-01-28 13:37:50 +0100469 uint16_t nsei = atoi(argv[0]);
470 uint16_t nsvci = atoi(argv[1]);
471 const char *role = argv[2];
472 const char *name = argv[3];
473 uint16_t dlci = atoi(argv[4]);
Alexander Couzens841817e2020-11-19 00:41:29 +0100474
Pau Espin Pedrolcaf53172021-01-28 13:37:50 +0100475 vtyvc = vtyvc_by_nsei(nsei, true);
476 if (!vtyvc) {
477 vty_out(vty, "Can not allocate space %s", VTY_NEWLINE);
Alexander Couzens841817e2020-11-19 00:41:29 +0100478 return CMD_WARNING;
479 }
480
Pau Espin Pedrolcaf53172021-01-28 13:37:50 +0100481 if (!strcmp(role, "fr"))
482 vtyvc->fr.role = FR_ROLE_USER_EQUIPMENT;
483 else if (!strcmp(role, "frnet"))
484 vtyvc->fr.role = FR_ROLE_NETWORK_EQUIPMENT;
485
486 osmo_strlcpy(vtyvc->netif, name, sizeof(vtyvc->netif));
487 vtyvc->frdlci = dlci;
488 vtyvc->nsvci = nsvci;
489 vtyvc->ll = GPRS_NS2_LL_FR;
490
491 return CMD_SUCCESS;
492}
493
494DEFUN(cfg_nse_nsvc, cfg_nse_nsvci_cmd,
495 "nse <0-65535> nsvci <0-65535>",
496 NSE_CMD_STR
497 "NS Virtual Connection\n"
498 "NS Virtual Connection ID (NSVCI)\n"
499 )
500{
501 struct ns2_vty_vc *vtyvc;
502
503 uint16_t nsei = atoi(argv[0]);
504 uint16_t nsvci = atoi(argv[1]);
505
506 vtyvc = vtyvc_by_nsei(nsei, true);
507 if (!vtyvc) {
508 vty_out(vty, "Can not allocate space %s", VTY_NEWLINE);
509 return CMD_WARNING;
510 }
511
512 vtyvc->nsvci = nsvci;
513
514 return CMD_SUCCESS;
515}
516
517DEFUN(cfg_nse_remoteip, cfg_nse_remoteip_cmd,
518 "nse <0-65535> remote-ip " VTY_IPV46_CMD,
519 NSE_CMD_STR
520 "Remote IP Address\n"
521 "Remote IPv4 Address\n"
522 "Remote IPv6 Address\n")
523{
524 uint16_t nsei = atoi(argv[0]);
525 struct ns2_vty_vc *vtyvc;
526
527 vtyvc = vtyvc_by_nsei(nsei, true);
528 if (!vtyvc) {
529 vty_out(vty, "Can not allocate space %s", VTY_NEWLINE);
530 return CMD_WARNING;
531 }
532
533 osmo_sockaddr_str_from_str2(&vtyvc->remote, argv[1]);
534
535 return CMD_SUCCESS;
536}
537
538DEFUN(cfg_nse_remoteport, cfg_nse_remoteport_cmd,
539 "nse <0-65535> remote-port <0-65535>",
540 NSE_CMD_STR
541 "Remote UDP Port\n"
542 "Remote UDP Port Number\n")
543{
544 uint16_t nsei = atoi(argv[0]);
545 uint16_t port = atoi(argv[1]);
546 struct ns2_vty_vc *vtyvc;
547
548 vtyvc = vtyvc_by_nsei(nsei, true);
549 if (!vtyvc) {
550 vty_out(vty, "Can not allocate space %s", VTY_NEWLINE);
551 return CMD_WARNING;
552 }
553
554 vtyvc->remote.port = port;
555
556 return CMD_SUCCESS;
557}
558
559DEFUN(cfg_nse_fr_dlci, cfg_nse_fr_dlci_cmd,
560 "nse <0-65535> nsvci <0-65535> fr-dlci <16-1007>",
561 NSE_CMD_STR
562 "NS Virtual Connection\n"
563 "NS Virtual Connection ID (NSVCI)\n"
564 "Frame Relay DLCI\n"
565 "Frame Relay DLCI Number\n")
566{
567 uint16_t nsei = atoi(argv[0]);
568 uint16_t nsvci = atoi(argv[1]);
569 uint16_t dlci = atoi(argv[2]);
570 struct ns2_vty_vc *vtyvc;
571
572 vtyvc = vtyvc_by_nsei(nsei, true);
573 if (!vtyvc) {
574 vty_out(vty, "Can not allocate space %s", VTY_NEWLINE);
575 return CMD_WARNING;
576 }
577
578 vtyvc->frdlci = dlci;
579 vtyvc->nsvci = nsvci;
580
581 return CMD_SUCCESS;
582}
583
584DEFUN(cfg_nse_encaps, cfg_nse_encaps_cmd,
585 "nse <0-65535> encapsulation (udp|framerelay-gre)",
586 NSE_CMD_STR
587 "Encapsulation for NS\n"
588 "UDP/IP Encapsulation\n" "Frame-Relay/GRE/IP Encapsulation\n")
589{
590 uint16_t nsei = atoi(argv[0]);
591 struct ns2_vty_vc *vtyvc;
592
593 vtyvc = vtyvc_by_nsei(nsei, true);
594 if (!vtyvc) {
595 vty_out(vty, "Can not allocate space %s", VTY_NEWLINE);
596 return CMD_WARNING;
597 }
598
599 if (!strcmp(argv[1], "udp"))
600 vtyvc->ll = GPRS_NS2_LL_UDP;
601 else
602 vtyvc->ll = GPRS_NS2_LL_FR_GRE;
603
604 return CMD_SUCCESS;
605}
606
607DEFUN(cfg_nse_remoterole, cfg_nse_remoterole_cmd,
608 "nse <0-65535> remote-role (sgsn|bss)",
609 NSE_CMD_STR
610 "Remote NSE Role\n"
611 "Remote Peer is SGSN\n"
612 "Remote Peer is BSS\n")
613{
614 uint16_t nsei = atoi(argv[0]);
615 struct ns2_vty_vc *vtyvc;
616
617 vtyvc = vtyvc_by_nsei(nsei, true);
618 if (!vtyvc) {
619 vty_out(vty, "Can not allocate space %s", VTY_NEWLINE);
620 return CMD_WARNING;
621 }
622
623 if (!strcmp(argv[1], "sgsn"))
624 vtyvc->remote_end_is_sgsn = 1;
625 else
626 vtyvc->remote_end_is_sgsn = 0;
627
628 return CMD_SUCCESS;
629}
630
631DEFUN(cfg_no_nse, cfg_no_nse_cmd,
632 "no nse <0-65535>",
633 "Delete Persistent NS Entity\n"
634 "Delete " NSE_CMD_STR)
635{
636 uint16_t nsei = atoi(argv[0]);
637 struct ns2_vty_vc *vtyvc;
638
639 vtyvc = vtyvc_by_nsei(nsei, false);
640 if (!vtyvc) {
641 vty_out(vty, "The NSE %d does not exists.%s", nsei, VTY_NEWLINE);
642 return CMD_WARNING;
643 }
644
645 ns2_vc_free(vtyvc);
646
647 return CMD_SUCCESS;
648}
649
650DEFUN(cfg_ns_timer, cfg_ns_timer_cmd,
651 "timer " NS_TIMERS " <0-65535>",
652 "Network Service Timer\n"
653 NS_TIMERS_HELP "Timer Value\n")
654{
655 int idx = get_string_value(gprs_ns_timer_strs, argv[0]);
656 int val = atoi(argv[1]);
657
658 if (idx < 0 || idx >= ARRAY_SIZE(vty_nsi->timeout))
659 return CMD_WARNING;
660
661 vty_nsi->timeout[idx] = val;
662
663 return CMD_SUCCESS;
664}
665
666#define ENCAPS_STR "NS encapsulation options\n"
667
668DEFUN(cfg_nsip_local_ip, cfg_nsip_local_ip_cmd,
669 "encapsulation udp local-ip " VTY_IPV46_CMD,
670 ENCAPS_STR "NS over UDP Encapsulation\n"
671 "Set the IP address on which we listen for NS/UDP\n"
672 "IPv4 Address\n"
673 "IPv6 Address\n")
674{
675 osmo_sockaddr_str_from_str2(&priv.udp, argv[0]);
676
677 return CMD_SUCCESS;
678}
679
680DEFUN(cfg_nsip_local_port, cfg_nsip_local_port_cmd,
681 "encapsulation udp local-port <0-65535>",
682 ENCAPS_STR "NS over UDP Encapsulation\n"
683 "Set the UDP port on which we listen for NS/UDP\n"
684 "UDP port number\n")
685{
686 unsigned int port = atoi(argv[0]);
687
688 priv.udp.port = port;
689
690 return CMD_SUCCESS;
691}
692
693DEFUN(cfg_nsip_dscp, cfg_nsip_dscp_cmd,
694 "encapsulation udp dscp <0-255>",
695 ENCAPS_STR "NS over UDP Encapsulation\n"
696 "Set DSCP/TOS on the UDP socket\n" "DSCP Value\n")
697{
698 int dscp = atoi(argv[0]);
699 struct gprs_ns2_vc_bind *bind;
700
701 priv.dscp = dscp;
702
703 llist_for_each_entry(bind, &vty_nsi->binding, list) {
704 if (gprs_ns2_is_ip_bind(bind))
705 gprs_ns2_ip_bind_set_dscp(bind, dscp);
Alexander Couzens6a161492020-07-12 13:45:50 +0200706 }
707
708 return CMD_SUCCESS;
709}
710
Pau Espin Pedrolcaf53172021-01-28 13:37:50 +0100711DEFUN(cfg_nsip_res_block_unblock, cfg_nsip_res_block_unblock_cmd,
712 "encapsulation udp use-reset-block-unblock (enabled|disabled)",
713 ENCAPS_STR "NS over UDP Encapsulation\n"
714 "Use NS-{RESET,BLOCK,UNBLOCK} procedures in violation of 3GPP TS 48.016\n"
715 "Enable NS-{RESET,BLOCK,UNBLOCK}\n"
716 "Disable NS-{RESET,BLOCK,UNBLOCK}\n")
Alexander Couzens6a161492020-07-12 13:45:50 +0200717{
Pau Espin Pedrolcaf53172021-01-28 13:37:50 +0100718 enum gprs_ns2_vc_mode vc_mode;
719
720 if (!strcmp(argv[0], "enabled"))
721 vc_mode = GPRS_NS2_VC_MODE_BLOCKRESET;
722 else
723 vc_mode = GPRS_NS2_VC_MODE_ALIVE;
724
725 priv.vc_mode = vc_mode;
726
727 return CMD_SUCCESS;
Alexander Couzens6a161492020-07-12 13:45:50 +0200728}
729
Pau Espin Pedrolcaf53172021-01-28 13:37:50 +0100730DEFUN(cfg_frgre_local_ip, cfg_frgre_local_ip_cmd,
731 "encapsulation framerelay-gre local-ip " VTY_IPV46_CMD,
732 ENCAPS_STR "NS over Frame Relay over GRE Encapsulation\n"
733 "Set the IP address on which we listen for NS/FR/GRE\n"
734 "IPv4 Address\n"
735 "IPv6 Address\n")
Alexander Couzens6a161492020-07-12 13:45:50 +0200736{
Pau Espin Pedrolcaf53172021-01-28 13:37:50 +0100737 osmo_sockaddr_str_from_str2(&priv.frgreaddr, argv[0]);
738
739 return CMD_SUCCESS;
Alexander Couzens6a161492020-07-12 13:45:50 +0200740}
741
Pau Espin Pedrolcaf53172021-01-28 13:37:50 +0100742DEFUN(cfg_frgre_enable, cfg_frgre_enable_cmd,
743 "encapsulation framerelay-gre enabled (1|0)",
744 ENCAPS_STR "NS over Frame Relay over GRE Encapsulation\n"
745 "Enable or disable Frame Relay over GRE\n"
746 "Enable\n" "Disable\n")
747{
748 int enabled = atoi(argv[0]);
749
750 priv.frgre = enabled;
751
752 return CMD_SUCCESS;
753}
754
755/* TODO: allow vty to reset/block/unblock nsvc/nsei */
756
Daniel Willmann751977b2020-12-02 18:59:44 +0100757DEFUN(logging_fltr_nse,
758 logging_fltr_nse_cmd,
759 "logging filter nse nsei <0-65535>",
760 LOGGING_STR FILTER_STR
761 "Filter based on NS Entity\n"
762 "Identify NSE by NSEI\n"
763 "Numeric identifier\n")
764{
765 struct log_target *tgt;
766 struct gprs_ns2_nse *nse;
Daniel Willmann89106522020-12-04 01:36:59 +0100767 uint16_t id = atoi(argv[0]);
Daniel Willmann751977b2020-12-02 18:59:44 +0100768
769 log_tgt_mutex_lock();
770 tgt = osmo_log_vty2tgt(vty);
771 if (!tgt) {
772 log_tgt_mutex_unlock();
773 return CMD_WARNING;
774 }
775
776 nse = gprs_ns2_nse_by_nsei(vty_nsi, id);
777 if (!nse) {
778 vty_out(vty, "No NSE by that identifier%s", VTY_NEWLINE);
779 log_tgt_mutex_unlock();
780 return CMD_WARNING;
781 }
782
783 log_set_nse_filter(tgt, nse);
784 log_tgt_mutex_unlock();
785 return CMD_SUCCESS;
786}
787
Alexander Couzens6a161492020-07-12 13:45:50 +0200788/* TODO: add filter for single connection by description */
789DEFUN(logging_fltr_nsvc,
790 logging_fltr_nsvc_cmd,
791 "logging filter nsvc nsvci <0-65535>",
792 LOGGING_STR FILTER_STR
793 "Filter based on NS Virtual Connection\n"
794 "Identify NS-VC by NSVCI\n"
795 "Numeric identifier\n")
796{
797 struct log_target *tgt;
798 struct gprs_ns2_vc *nsvc;
Daniel Willmann89106522020-12-04 01:36:59 +0100799 uint16_t id = atoi(argv[0]);
Alexander Couzens6a161492020-07-12 13:45:50 +0200800
801 log_tgt_mutex_lock();
802 tgt = osmo_log_vty2tgt(vty);
803 if (!tgt) {
804 log_tgt_mutex_unlock();
805 return CMD_WARNING;
806 }
807
808 nsvc = gprs_ns2_nsvc_by_nsvci(vty_nsi, id);
809 if (!nsvc) {
810 vty_out(vty, "No NS-VC by that identifier%s", VTY_NEWLINE);
811 log_tgt_mutex_unlock();
812 return CMD_WARNING;
813 }
814
815 log_set_nsvc_filter(tgt, nsvc);
816 log_tgt_mutex_unlock();
817 return CMD_SUCCESS;
818}
819
Pau Espin Pedrolcaf53172021-01-28 13:37:50 +0100820/**
821 * gprs_ns2_vty_init initialize the vty
822 * \param[inout] nsi
823 * \param[in] default_bind set the default address to bind to. Can be NULL.
824 * \return 0 on success
825 */
826int gprs_ns2_vty_init(struct gprs_ns2_inst *nsi,
827 const struct osmo_sockaddr_str *default_bind)
Alexander Couzens6a161492020-07-12 13:45:50 +0200828{
Pau Espin Pedrolcaf53172021-01-28 13:37:50 +0100829 static bool vty_elements_installed = false;
Alexander Couzens6a161492020-07-12 13:45:50 +0200830
Pau Espin Pedrolcaf53172021-01-28 13:37:50 +0100831 vty_nsi = nsi;
832 memset(&priv, 0, sizeof(struct ns2_vty_priv));
833 INIT_LLIST_HEAD(&priv.vtyvc);
834 priv.vc_mode = GPRS_NS2_VC_MODE_BLOCKRESET;
835 if (default_bind)
836 memcpy(&priv.udp, default_bind, sizeof(*default_bind));
837
838 /* Regression test code may call this function repeatedly, so make sure
839 * that VTY elements are not duplicated, which would assert. */
840 if (vty_elements_installed)
841 return 0;
842 vty_elements_installed = true;
Alexander Couzens6a161492020-07-12 13:45:50 +0200843
Harald Welte25ee7552020-12-02 22:14:00 +0100844 install_lib_element_ve(&show_ns_cmd);
Harald Welte2fce19a2020-12-01 17:52:55 +0100845 install_lib_element_ve(&show_ns_binds_cmd);
846 install_lib_element_ve(&show_ns_entities_cmd);
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +0700847 install_lib_element_ve(&show_ns_pers_cmd);
848 install_lib_element_ve(&show_nse_cmd);
Daniel Willmann751977b2020-12-02 18:59:44 +0100849 install_lib_element_ve(&logging_fltr_nse_cmd);
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +0700850 install_lib_element_ve(&logging_fltr_nsvc_cmd);
Alexander Couzens6a161492020-07-12 13:45:50 +0200851
Daniel Willmanndbab7142020-11-18 14:19:56 +0100852 install_lib_element(ENABLE_NODE, &nsvc_force_unconf_cmd);
853
Daniel Willmann751977b2020-12-02 18:59:44 +0100854 install_lib_element(CFG_LOG_NODE, &logging_fltr_nse_cmd);
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +0700855 install_lib_element(CFG_LOG_NODE, &logging_fltr_nsvc_cmd);
Alexander Couzens6a161492020-07-12 13:45:50 +0200856
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +0700857 install_lib_element(CONFIG_NODE, &cfg_ns_cmd);
Alexander Couzens6a161492020-07-12 13:45:50 +0200858 install_node(&ns_node, config_write_ns);
Pau Espin Pedrolcaf53172021-01-28 13:37:50 +0100859 install_lib_element(L_NS_NODE, &cfg_nse_fr_cmd);
860 install_lib_element(L_NS_NODE, &cfg_nse_nsvci_cmd);
861 install_lib_element(L_NS_NODE, &cfg_nse_remoteip_cmd);
862 install_lib_element(L_NS_NODE, &cfg_nse_remoteport_cmd);
863 install_lib_element(L_NS_NODE, &cfg_nse_fr_dlci_cmd);
864 install_lib_element(L_NS_NODE, &cfg_nse_encaps_cmd);
865 install_lib_element(L_NS_NODE, &cfg_nse_remoterole_cmd);
866 install_lib_element(L_NS_NODE, &cfg_no_nse_cmd);
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +0700867 install_lib_element(L_NS_NODE, &cfg_ns_timer_cmd);
Pau Espin Pedrolcaf53172021-01-28 13:37:50 +0100868 install_lib_element(L_NS_NODE, &cfg_nsip_local_ip_cmd);
869 install_lib_element(L_NS_NODE, &cfg_nsip_local_port_cmd);
870 install_lib_element(L_NS_NODE, &cfg_nsip_dscp_cmd);
871 install_lib_element(L_NS_NODE, &cfg_nsip_res_block_unblock_cmd);
872 install_lib_element(L_NS_NODE, &cfg_frgre_enable_cmd);
873 install_lib_element(L_NS_NODE, &cfg_frgre_local_ip_cmd);
Alexander Couzens6a161492020-07-12 13:45:50 +0200874
Pau Espin Pedrolcaf53172021-01-28 13:37:50 +0100875 /* TODO: nsvc/nsei command to reset states or reset/block/unblock nsei/nsvcs */
Alexander Couzens6a161492020-07-12 13:45:50 +0200876
Pau Espin Pedrolcaf53172021-01-28 13:37:50 +0100877 return 0;
878}
879
880/*!
881 * \brief gprs_ns2_vty_create parse the vty tree into ns nodes
882 * It has to be in different steps to ensure the bind is created before creating VCs.
883 * \return 0 on success
884 */
885int gprs_ns2_vty_create() {
886 struct ns2_vty_vc *vtyvc;
887 struct gprs_ns2_vc_bind *bind, *fr;
888 struct gprs_ns2_nse *nse;
889 struct gprs_ns2_vc *nsvc;
890 struct osmo_sockaddr sockaddr;
891 enum gprs_ns2_dialect dialect = GPRS_NS2_DIALECT_UNDEF;
892 int rc = 0;
893
894 if (!vty_nsi)
895 return -1;
896
897 /* create binds, only support a single bind. either FR or UDP */
898 if (priv.frgre) {
899 /* TODO not yet supported !*/
900 return -1;
901 } else {
902 /* UDP */
903 osmo_sockaddr_str_to_sockaddr(&priv.udp, &sockaddr.u.sas);
904 if (gprs_ns2_ip_bind(vty_nsi, "vtybind", &sockaddr, priv.dscp, &bind)) {
905 /* TODO: could not bind on the specific address */
906 return -1;
907 }
908
909 bind->accept_ipaccess = priv.vc_mode == GPRS_NS2_VC_MODE_BLOCKRESET;
910 }
911
912 /* create vcs */
913 llist_for_each_entry(vtyvc, &priv.vtyvc, list) {
914 /* validate settings */
915 switch (vtyvc->ll) {
916 case GPRS_NS2_LL_UDP:
917 if (priv.vc_mode == GPRS_NS2_VC_MODE_BLOCKRESET)
918 dialect = GPRS_NS2_DIALECT_IPACCESS;
919 else
920 dialect = GPRS_NS2_DIALECT_STATIC_ALIVE;
921 if (strlen(vtyvc->remote.ip) == 0) {
922 /* Invalid IP for VC */
923 continue;
924 }
925
926 if (!vtyvc->remote.port) {
927 /* Invalid port for VC */
928 continue;
929 }
930
931 if (osmo_sockaddr_str_to_sockaddr(&vtyvc->remote, &sockaddr.u.sas)) {
932 /* Invalid sockaddr for VC */
933 continue;
934 }
935 break;
936 case GPRS_NS2_LL_FR:
937 dialect = GPRS_NS2_DIALECT_STATIC_RESETBLOCK;
938 break;
939 case GPRS_NS2_LL_FR_GRE:
940 dialect = GPRS_NS2_DIALECT_STATIC_RESETBLOCK;
941 continue;
942 case GPRS_NS2_LL_UNDEF:
943 /* should not happen */
944 OSMO_ASSERT(false);
945 }
946
947 nse = gprs_ns2_nse_by_nsei(vty_nsi, vtyvc->nsei);
948 if (!nse) {
949 nse = gprs_ns2_create_nse(vty_nsi, vtyvc->nsei, vtyvc->ll, dialect);
950 if (!nse) {
951 /* Could not create NSE for VTY */
952 continue;
953 }
954 }
955 nse->persistent = true;
956
957 switch (vtyvc->ll) {
958 case GPRS_NS2_LL_UDP:
959 nsvc = gprs_ns2_ip_connect(bind,
960 &sockaddr,
961 nse,
962 vtyvc->nsvci);
963 if (!nsvc) {
964 /* Could not create NSVC, connect failed */
965 continue;
966 }
967 nsvc->persistent = true;
968 break;
969 case GPRS_NS2_LL_FR: {
970 if (vty_fr_network == NULL) {
971 /* TODO: add a switch for BSS/SGSN/gbproxy */
972 vty_fr_network = osmo_fr_network_alloc(vty_nsi);
973 }
974 fr = gprs_ns2_fr_bind_by_netif(
975 vty_nsi,
976 vtyvc->netif);
977 if (!fr) {
978 rc = gprs_ns2_fr_bind(vty_nsi, vtyvc->netif, vtyvc->netif, vty_fr_network, vtyvc->fr.role, &fr);
979 if (rc < 0) {
980 LOGP(DLNS, LOGL_ERROR, "Can not create fr bind on device %s err: %d\n", vtyvc->netif, rc);
981 return rc;
982 }
983 }
984
985 nsvc = gprs_ns2_fr_connect(fr, nse, vtyvc->nsvci, vtyvc->frdlci);
986 if (!nsvc) {
987 /* Could not create NSVC, connect failed */
988 continue;
989 }
990 nsvc->persistent = true;
991 break;
992 }
993 case GPRS_NS2_LL_FR_GRE:
994 case GPRS_NS2_LL_UNDEF:
995 continue;
996 }
997 }
998
Alexander Couzens6a161492020-07-12 13:45:50 +0200999
1000 return 0;
1001}