blob: b244a96afc12d4d15710a27f7756281a2fcc50df [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
4/* (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>
7 * 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
36#include <osmocom/core/msgb.h>
37#include <osmocom/core/byteswap.h>
Daniel Willmanndbab7142020-11-18 14:19:56 +010038#include <osmocom/core/fsm.h>
Alexander Couzens6a161492020-07-12 13:45:50 +020039#include <osmocom/core/talloc.h>
40#include <osmocom/core/select.h>
41#include <osmocom/core/rate_ctr.h>
42#include <osmocom/core/socket.h>
43#include <osmocom/core/sockaddr_str.h>
44#include <osmocom/core/linuxlist.h>
45#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>
49#include <osmocom/vty/vty.h>
50#include <osmocom/vty/command.h>
51#include <osmocom/vty/logging.h>
52#include <osmocom/vty/telnet_interface.h>
53#include <osmocom/vty/misc.h>
54
55#include "gprs_ns2_internal.h"
56
57struct ns2_vty_priv {
58 /* global listen */
59 struct osmo_sockaddr_str udp;
60 struct osmo_sockaddr_str frgreaddr;
61 int dscp;
62 enum gprs_ns2_vc_mode vc_mode;
63 /* force vc mode if another configuration forces
64 * the vc mode. E.g. SNS configuration */
65 bool force_vc_mode;
Daniel Willmann4fb27a82020-09-25 15:39:46 +020066 const char *force_vc_mode_reason;
Alexander Couzens6a161492020-07-12 13:45:50 +020067 bool frgre;
68
69 struct llist_head vtyvc;
70};
71
72struct ns2_vty_vc {
73 struct llist_head list;
74
75 struct osmo_sockaddr_str remote;
Alexander Couzens24a14ac2020-11-19 02:34:49 +010076 enum gprs_ns2_ll ll;
Alexander Couzens6a161492020-07-12 13:45:50 +020077
78 /* old vty code doesnt support multiple NSVCI per NSEI */
79 uint16_t nsei;
80 uint16_t nsvci;
81 uint16_t frdlci;
82
Alexander Couzens841817e2020-11-19 00:41:29 +010083 struct {
84 enum osmo_fr_role role;
85 } fr;
86
87 char netif[IF_NAMESIZE];
88
Alexander Couzens6a161492020-07-12 13:45:50 +020089 bool remote_end_is_sgsn;
90 bool configured;
91};
92
93static struct gprs_ns2_inst *vty_nsi = NULL;
94static struct ns2_vty_priv priv;
Alexander Couzens841817e2020-11-19 00:41:29 +010095static struct osmo_fr_network *vty_fr_network = NULL;
Alexander Couzens6a161492020-07-12 13:45:50 +020096
97/* FIXME: this should go to some common file as it is copied
98 * in vty_interface.c of the BSC */
99static const struct value_string gprs_ns_timer_strs[] = {
100 { 0, "tns-block" },
101 { 1, "tns-block-retries" },
102 { 2, "tns-reset" },
103 { 3, "tns-reset-retries" },
104 { 4, "tns-test" },
105 { 5, "tns-alive" },
106 { 6, "tns-alive-retries" },
107 { 7, "tsns-prov" },
108 { 0, NULL }
109};
110
111static void log_set_nsvc_filter(struct log_target *target,
112 struct gprs_ns2_vc *nsvc)
113{
114 if (nsvc) {
115 target->filter_map |= (1 << LOG_FLT_GB_NSVC);
116 target->filter_data[LOG_FLT_GB_NSVC] = nsvc;
117 } else if (target->filter_data[LOG_FLT_GB_NSVC]) {
118 target->filter_map = ~(1 << LOG_FLT_GB_NSVC);
119 target->filter_data[LOG_FLT_GB_NSVC] = NULL;
120 }
121}
122
123static struct cmd_node ns_node = {
124 L_NS_NODE,
125 "%s(config-ns)# ",
126 1,
127};
128
Harald Welte6d4db232020-11-25 19:33:42 +0100129static struct ns2_vty_vc *vtyvc_alloc(uint16_t nsei) {
Alexander Couzens6a161492020-07-12 13:45:50 +0200130 struct ns2_vty_vc *vtyvc = talloc_zero(vty_nsi, struct ns2_vty_vc);
131 if (!vtyvc)
132 return vtyvc;
133
134 vtyvc->nsei = nsei;
135
136 llist_add(&vtyvc->list, &priv.vtyvc);
137
138 return vtyvc;
139}
140
141static void ns2_vc_free(struct ns2_vty_vc *vtyvc) {
142 if (!vtyvc)
143 return;
144
145 llist_del(&vtyvc->list);
146 talloc_free(vtyvc);
147}
148
Harald Welte6d4db232020-11-25 19:33:42 +0100149static struct ns2_vty_vc *vtyvc_by_nsei(uint16_t nsei, bool alloc_missing) {
Alexander Couzens6a161492020-07-12 13:45:50 +0200150 struct ns2_vty_vc *vtyvc;
Alexander Couzens1f0625f2020-09-23 18:22:20 +0200151
Alexander Couzens6a161492020-07-12 13:45:50 +0200152 llist_for_each_entry(vtyvc, &priv.vtyvc, list) {
Harald Welte6d4db232020-11-25 19:33:42 +0100153 if (vtyvc->nsei == nsei)
Alexander Couzens6a161492020-07-12 13:45:50 +0200154 return vtyvc;
155 }
156
Alexander Couzens1f0625f2020-09-23 18:22:20 +0200157 if (!alloc_missing)
158 return NULL;
Alexander Couzens6a161492020-07-12 13:45:50 +0200159
Harald Welte6d4db232020-11-25 19:33:42 +0100160 vtyvc = vtyvc_alloc(nsei);
Alexander Couzens1f0625f2020-09-23 18:22:20 +0200161 if (!vtyvc)
162 return vtyvc;
Alexander Couzens6a161492020-07-12 13:45:50 +0200163
Alexander Couzens1f0625f2020-09-23 18:22:20 +0200164 vtyvc->nsei = nsei;
165 return vtyvc;
Alexander Couzens6a161492020-07-12 13:45:50 +0200166}
167
168static int config_write_ns(struct vty *vty)
169{
170 struct ns2_vty_vc *vtyvc;
171 unsigned int i;
172 struct osmo_sockaddr_str sockstr;
173
174 vty_out(vty, "ns%s", VTY_NEWLINE);
175
176 /* global configuration must be written first, as some of it may be
177 * relevant when creating the NSE/NSVC later below */
178
179 vty_out(vty, " encapsulation framerelay-gre enabled %u%s",
180 priv.frgre ? 1 : 0, VTY_NEWLINE);
181
182 if (priv.frgre) {
183 if (strlen(priv.frgreaddr.ip)) {
184 vty_out(vty, " encapsulation framerelay-gre local-ip %s%s",
185 sockstr.ip, VTY_NEWLINE);
186 }
187 } else {
188 if (strlen(priv.udp.ip)) {
189 vty_out(vty, " encapsulation udp local-ip %s%s",
190 priv.udp.ip, VTY_NEWLINE);
191 }
192
193 if (priv.udp.port)
194 vty_out(vty, " encapsulation udp local-port %u%s",
195 priv.udp.port, VTY_NEWLINE);
196 }
197
198 if (priv.dscp)
199 vty_out(vty, " encapsulation udp dscp %d%s",
200 priv.dscp, VTY_NEWLINE);
201
202 vty_out(vty, " encapsulation udp use-reset-block-unblock %s%s",
203 priv.vc_mode == NS2_VC_MODE_BLOCKRESET ? "enabled" : "disabled", VTY_NEWLINE);
204
205 llist_for_each_entry(vtyvc, &priv.vtyvc, list) {
206 vty_out(vty, " nse %u nsvci %u%s",
207 vtyvc->nsei, vtyvc->nsvci, VTY_NEWLINE);
208
Harald Welte6d4db232020-11-25 19:33:42 +0100209 vty_out(vty, " nse %u remote-role %s%s",
210 vtyvc->nsei, vtyvc->remote_end_is_sgsn ? "sgsn" : "bss",
211 VTY_NEWLINE);
Alexander Couzens6a161492020-07-12 13:45:50 +0200212
213 switch (vtyvc->ll) {
Alexander Couzens24a14ac2020-11-19 02:34:49 +0100214 case GPRS_NS2_LL_UDP:
Harald Welte6d4db232020-11-25 19:33:42 +0100215 vty_out(vty, " nse %u encapsulation udp%s", vtyvc->nsei, VTY_NEWLINE);
216 vty_out(vty, " nse %u remote-ip %s%s",
217 vtyvc->nsei,
218 vtyvc->remote.ip,
Alexander Couzens6a161492020-07-12 13:45:50 +0200219 VTY_NEWLINE);
Harald Welte6d4db232020-11-25 19:33:42 +0100220 vty_out(vty, " nse %u remote-port %u%s",
221 vtyvc->nsei, vtyvc->remote.port,
222 VTY_NEWLINE);
Alexander Couzens6a161492020-07-12 13:45:50 +0200223 break;
Alexander Couzens24a14ac2020-11-19 02:34:49 +0100224 case GPRS_NS2_LL_FR_GRE:
Harald Welte6d4db232020-11-25 19:33:42 +0100225 vty_out(vty, " nse %u encapsulation framerelay-gre%s",
226 vtyvc->nsei, VTY_NEWLINE);
227 vty_out(vty, " nse %u remote-ip %s%s",
228 vtyvc->nsei,
229 vtyvc->remote.ip,
230 VTY_NEWLINE);
231 vty_out(vty, " nse %u fr-dlci %u%s",
232 vtyvc->nsei, vtyvc->frdlci,
233 VTY_NEWLINE);
Alexander Couzens6a161492020-07-12 13:45:50 +0200234 break;
Alexander Couzens24a14ac2020-11-19 02:34:49 +0100235 case GPRS_NS2_LL_FR:
Harald Welte6d4db232020-11-25 19:33:42 +0100236 vty_out(vty, " nse %u fr %s dlci %u%s",
237 vtyvc->nsei, vtyvc->netif, vtyvc->frdlci,
238 VTY_NEWLINE);
Alexander Couzens841817e2020-11-19 00:41:29 +0100239 break;
Alexander Couzens6a161492020-07-12 13:45:50 +0200240 default:
241 break;
242 }
243 }
244
245 for (i = 0; i < ARRAY_SIZE(vty_nsi->timeout); i++)
246 vty_out(vty, " timer %s %u%s",
247 get_value_string(gprs_ns_timer_strs, i),
248 vty_nsi->timeout[i], VTY_NEWLINE);
249
250 return CMD_SUCCESS;
251}
252
253DEFUN(cfg_ns, cfg_ns_cmd,
254 "ns",
255 "Configure the GPRS Network Service")
256{
257 vty->node = L_NS_NODE;
258 return CMD_SUCCESS;
259}
260
261static void dump_nsvc(struct vty *vty, struct gprs_ns2_vc *nsvc, bool stats)
262{
Harald Weltedc2d0802020-12-01 18:17:28 +0100263 char nsvci_str[32];
264
265 if (nsvc->nsvci_is_valid)
266 snprintf(nsvci_str, sizeof(nsvci_str), "%05u", nsvc->nsvci);
267 else
268 snprintf(nsvci_str, sizeof(nsvci_str), "none");
269
270 vty_out(vty, " NSVCI %s: %s %s data_weight=%u sig_weight=%u %s%s", nsvci_str,
271 osmo_fsm_inst_state_name(nsvc->fi),
272 nsvc->persistent ? "PERSIST" : "DYNAMIC",
273 nsvc->data_weight, nsvc->sig_weight,
274 gprs_ns2_ll_str(nsvc), VTY_NEWLINE);
Alexander Couzens6a161492020-07-12 13:45:50 +0200275
276 if (stats) {
Harald Welte7aa60992020-12-01 17:53:17 +0100277 vty_out_rate_ctr_group(vty, " ", nsvc->ctrg);
278 vty_out_stat_item_group(vty, " ", nsvc->statg);
Alexander Couzens6a161492020-07-12 13:45:50 +0200279 }
280}
281
282static void dump_nse(struct vty *vty, const struct gprs_ns2_nse *nse, bool stats, bool persistent_only)
283{
284 struct gprs_ns2_vc *nsvc;
285
Harald Welte0ff12ad2020-12-01 17:51:07 +0100286 vty_out(vty, "NSEI %05u: %s, %s%s", nse->nsei, gprs_ns2_lltype_str(nse->ll),
287 nse->alive ? "ALIVE" : "DEAD", VTY_NEWLINE);
Alexander Couzens6a161492020-07-12 13:45:50 +0200288
289 gprs_ns2_sns_dump_vty(vty, nse, stats);
290 llist_for_each_entry(nsvc, &nse->nsvc, list) {
291 if (persistent_only) {
292 if (nsvc->persistent)
293 dump_nsvc(vty, nsvc, stats);
294 } else {
295 dump_nsvc(vty, nsvc, stats);
296 }
297 }
298}
299
Alexander Couzens22f34712020-10-02 02:34:39 +0200300static void dump_bind(struct vty *vty, const struct gprs_ns2_vc_bind *bind, bool stats)
301{
302 if (bind->dump_vty)
303 bind->dump_vty(bind, vty, stats);
304}
305
Harald Welte2fce19a2020-12-01 17:52:55 +0100306static void dump_ns_bind(struct vty *vty, const struct gprs_ns2_inst *nsi, bool stats)
Alexander Couzens6a161492020-07-12 13:45:50 +0200307{
Alexander Couzens22f34712020-10-02 02:34:39 +0200308 struct gprs_ns2_vc_bind *bind;
Alexander Couzens6a161492020-07-12 13:45:50 +0200309
Alexander Couzens22f34712020-10-02 02:34:39 +0200310 llist_for_each_entry(bind, &nsi->binding, list) {
311 dump_bind(vty, bind, stats);
312 }
Harald Welte2fce19a2020-12-01 17:52:55 +0100313}
314
315
316static void dump_ns_entities(struct vty *vty, const struct gprs_ns2_inst *nsi, bool stats, bool persistent_only)
317{
318 struct gprs_ns2_nse *nse;
Alexander Couzens22f34712020-10-02 02:34:39 +0200319
Alexander Couzens6a161492020-07-12 13:45:50 +0200320 llist_for_each_entry(nse, &nsi->nse, list) {
321 dump_nse(vty, nse, stats, persistent_only);
Alexander Couzens6a161492020-07-12 13:45:50 +0200322 }
Alexander Couzens6a161492020-07-12 13:45:50 +0200323}
324
Harald Welte2fce19a2020-12-01 17:52:55 +0100325DEFUN(show_ns_binds, show_ns_binds_cmd, "show ns binds [stats]",
326 SHOW_STR
327 "Display information about the NS protocol binds\n"
328 "Include statistic\n")
Alexander Couzens6a161492020-07-12 13:45:50 +0200329{
Harald Welte2fce19a2020-12-01 17:52:55 +0100330 bool stats = false;
331 if (argc > 0)
332 stats = true;
333
334 dump_ns_bind(vty, vty_nsi, stats);
Alexander Couzens6a161492020-07-12 13:45:50 +0200335 return CMD_SUCCESS;
336}
337
Harald Welte2fce19a2020-12-01 17:52:55 +0100338DEFUN(show_ns_entities, show_ns_entities_cmd, "show ns entities [stats]",
Alexander Couzens6a161492020-07-12 13:45:50 +0200339 SHOW_STR
Harald Welte2fce19a2020-12-01 17:52:55 +0100340 "Display information about the NS protocol entities (NSEs)\n"
Alexander Couzens6a161492020-07-12 13:45:50 +0200341 "Include statistics\n")
342{
Harald Welte2fce19a2020-12-01 17:52:55 +0100343 bool stats = false;
344 if (argc > 0)
345 stats = true;
346
347 dump_ns_entities(vty, vty_nsi, stats, false);
Alexander Couzens6a161492020-07-12 13:45:50 +0200348 return CMD_SUCCESS;
349}
350
351DEFUN(show_ns_pers, show_ns_pers_cmd, "show ns persistent",
352 SHOW_STR
353 "Display information about the NS protocol\n"
354 "Show only persistent NS\n")
355{
Harald Welte2fce19a2020-12-01 17:52:55 +0100356 dump_ns_entities(vty, vty_nsi, true, true);
Alexander Couzens6a161492020-07-12 13:45:50 +0200357 return CMD_SUCCESS;
358}
359
360DEFUN(show_nse, show_nse_cmd, "show ns (nsei|nsvc) <0-65535> [stats]",
361 SHOW_STR "Display information about the NS protocol\n"
362 "Select one NSE by its NSE Identifier\n"
363 "Select one NSE by its NS-VC Identifier\n"
364 "The Identifier of selected type\n"
365 "Include Statistics\n")
366{
367 struct gprs_ns2_inst *nsi = vty_nsi;
368 struct gprs_ns2_nse *nse;
369 struct gprs_ns2_vc *nsvc;
370 uint16_t id = atoi(argv[1]);
371 bool show_stats = false;
372
373 if (argc >= 3)
374 show_stats = true;
375
376 if (!strcmp(argv[0], "nsei")) {
377 nse = gprs_ns2_nse_by_nsei(nsi, id);
378 if (!nse) {
379 return CMD_WARNING;
380 }
381
382 dump_nse(vty, nse, show_stats, false);
383 } else {
384 nsvc = gprs_ns2_nsvc_by_nsvci(nsi, id);
385
386 if (!nsvc) {
387 vty_out(vty, "No such NS Entity%s", VTY_NEWLINE);
388 return CMD_WARNING;
389 }
390
391 dump_nsvc(vty, nsvc, show_stats);
392 }
393
394 return CMD_SUCCESS;
395}
396
Daniel Willmanndbab7142020-11-18 14:19:56 +0100397static int nsvc_force_unconf_cb(struct gprs_ns2_vc *nsvc, void *ctx)
398{
399 gprs_ns2_vc_force_unconfigured(nsvc);
400 return 0;
401}
402
403DEFUN_HIDDEN(nsvc_force_unconf, nsvc_force_unconf_cmd,
404 "nsvc nsei <0-65535> force-unconfigured",
405 "NS Virtual Connection\n"
406 "The NSEI\n"
407 "Reset the NSVCs back to initial state\n"
408 )
409{
410 struct gprs_ns2_inst *nsi = vty_nsi;
411 struct gprs_ns2_nse *nse;
412
413 uint16_t id = atoi(argv[0]);
414
415 nse = gprs_ns2_nse_by_nsei(nsi, id);
416 if (!nse) {
417 vty_out(vty, "Could not find NSE for NSEI %u%s", id, VTY_NEWLINE);
418 return CMD_WARNING;
419 }
420
421 /* Perform the operation for all nsvc */
422 gprs_ns2_nse_foreach_nsvc(nse, nsvc_force_unconf_cb, NULL);
423
424 return CMD_SUCCESS;
425}
426
Alexander Couzens6a161492020-07-12 13:45:50 +0200427#define NSE_CMD_STR "Persistent NS Entity\n" "NS Entity ID (NSEI)\n"
428
Alexander Couzens841817e2020-11-19 00:41:29 +0100429DEFUN(cfg_nse_fr, cfg_nse_fr_cmd,
430 "nse <0-65535> nsvci <0-65535> (fr|frnet) NETIF dlci <0-1023>",
431 NSE_CMD_STR
432 "NS Virtual Connection\n"
433 "NS Virtual Connection ID (NSVCI)\n"
Harald Welte92049192020-11-25 20:56:06 +0100434 "Frame Relay User-Side\n"
435 "Frame Relay Network-Side\n"
Alexander Couzens841817e2020-11-19 00:41:29 +0100436 IFNAME_STR
437 "Data Link connection identifier\n"
438 "Data Link connection identifier\n"
439 )
440{
441 struct ns2_vty_vc *vtyvc;
442
443 uint16_t nsei = atoi(argv[0]);
444 uint16_t nsvci = atoi(argv[1]);
445 const char *role = argv[2];
446 const char *name = argv[3];
447 uint16_t dlci = atoi(argv[4]);
448
Harald Welte6d4db232020-11-25 19:33:42 +0100449 vtyvc = vtyvc_by_nsei(nsei, true);
Alexander Couzens841817e2020-11-19 00:41:29 +0100450 if (!vtyvc) {
451 vty_out(vty, "Can not allocate space %s", VTY_NEWLINE);
452 return CMD_WARNING;
453 }
454
455 if (!strcmp(role, "fr"))
456 vtyvc->fr.role = FR_ROLE_USER_EQUIPMENT;
457 else if (!strcmp(role, "frnet"))
458 vtyvc->fr.role = FR_ROLE_NETWORK_EQUIPMENT;
459
460 osmo_strlcpy(vtyvc->netif, name, sizeof(vtyvc->netif));
461 vtyvc->frdlci = dlci;
462 vtyvc->nsvci = nsvci;
Alexander Couzens24a14ac2020-11-19 02:34:49 +0100463 vtyvc->ll = GPRS_NS2_LL_FR;
Alexander Couzens841817e2020-11-19 00:41:29 +0100464
465 return CMD_SUCCESS;
466}
467
Alexander Couzens6a161492020-07-12 13:45:50 +0200468DEFUN(cfg_nse_nsvc, cfg_nse_nsvci_cmd,
469 "nse <0-65535> nsvci <0-65535>",
470 NSE_CMD_STR
471 "NS Virtual Connection\n"
472 "NS Virtual Connection ID (NSVCI)\n"
473 )
474{
475 struct ns2_vty_vc *vtyvc;
476
477 uint16_t nsei = atoi(argv[0]);
478 uint16_t nsvci = atoi(argv[1]);
479
Harald Welte6d4db232020-11-25 19:33:42 +0100480 vtyvc = vtyvc_by_nsei(nsei, true);
Alexander Couzens6a161492020-07-12 13:45:50 +0200481 if (!vtyvc) {
482 vty_out(vty, "Can not allocate space %s", VTY_NEWLINE);
483 return CMD_WARNING;
484 }
485
486 vtyvc->nsvci = nsvci;
487
488 return CMD_SUCCESS;
489}
490
491DEFUN(cfg_nse_remoteip, cfg_nse_remoteip_cmd,
Harald Welte6d4db232020-11-25 19:33:42 +0100492 "nse <0-65535> remote-ip " VTY_IPV46_CMD,
Alexander Couzens6a161492020-07-12 13:45:50 +0200493 NSE_CMD_STR
494 "Remote IP Address\n"
Alexander Couzensc82c40a2020-09-24 05:55:48 +0200495 "Remote IPv4 Address\n"
496 "Remote IPv6 Address\n")
Alexander Couzens6a161492020-07-12 13:45:50 +0200497{
498 uint16_t nsei = atoi(argv[0]);
499 struct ns2_vty_vc *vtyvc;
500
Harald Welte6d4db232020-11-25 19:33:42 +0100501 vtyvc = vtyvc_by_nsei(nsei, true);
Alexander Couzens6a161492020-07-12 13:45:50 +0200502 if (!vtyvc) {
503 vty_out(vty, "Can not allocate space %s", VTY_NEWLINE);
504 return CMD_WARNING;
505 }
506
Harald Welte6d4db232020-11-25 19:33:42 +0100507 osmo_sockaddr_str_from_str2(&vtyvc->remote, argv[1]);
Alexander Couzens6a161492020-07-12 13:45:50 +0200508
509 return CMD_SUCCESS;
510}
511
512DEFUN(cfg_nse_remoteport, cfg_nse_remoteport_cmd,
Harald Welte6d4db232020-11-25 19:33:42 +0100513 "nse <0-65535> remote-port <0-65535>",
Alexander Couzens6a161492020-07-12 13:45:50 +0200514 NSE_CMD_STR
515 "Remote UDP Port\n"
516 "Remote UDP Port Number\n")
517{
518 uint16_t nsei = atoi(argv[0]);
Harald Welte6d4db232020-11-25 19:33:42 +0100519 uint16_t port = atoi(argv[1]);
Alexander Couzens6a161492020-07-12 13:45:50 +0200520 struct ns2_vty_vc *vtyvc;
521
Harald Welte6d4db232020-11-25 19:33:42 +0100522 vtyvc = vtyvc_by_nsei(nsei, true);
Alexander Couzens6a161492020-07-12 13:45:50 +0200523 if (!vtyvc) {
524 vty_out(vty, "Can not allocate space %s", VTY_NEWLINE);
525 return CMD_WARNING;
526 }
527
528 vtyvc->remote.port = port;
529
530 return CMD_SUCCESS;
531}
532
533DEFUN(cfg_nse_fr_dlci, cfg_nse_fr_dlci_cmd,
Alexander Couzens841817e2020-11-19 00:41:29 +0100534 "nse <0-65535> nsvci <0-65535> fr-dlci <16-1007>",
Alexander Couzens6a161492020-07-12 13:45:50 +0200535 NSE_CMD_STR
Harald Welte92049192020-11-25 20:56:06 +0100536 "NS Virtual Connection\n"
537 "NS Virtual Connection ID (NSVCI)\n"
Alexander Couzens6a161492020-07-12 13:45:50 +0200538 "Frame Relay DLCI\n"
539 "Frame Relay DLCI Number\n")
540{
541 uint16_t nsei = atoi(argv[0]);
Alexander Couzens841817e2020-11-19 00:41:29 +0100542 uint16_t nsvci = atoi(argv[1]);
543 uint16_t dlci = atoi(argv[2]);
Alexander Couzens6a161492020-07-12 13:45:50 +0200544 struct ns2_vty_vc *vtyvc;
545
Harald Welte6d4db232020-11-25 19:33:42 +0100546 vtyvc = vtyvc_by_nsei(nsei, true);
Alexander Couzens6a161492020-07-12 13:45:50 +0200547 if (!vtyvc) {
548 vty_out(vty, "Can not allocate space %s", VTY_NEWLINE);
549 return CMD_WARNING;
550 }
551
Alexander Couzens6a161492020-07-12 13:45:50 +0200552 vtyvc->frdlci = dlci;
Harald Welte6d4db232020-11-25 19:33:42 +0100553 vtyvc->nsvci = nsvci;
Alexander Couzens6a161492020-07-12 13:45:50 +0200554
555 return CMD_SUCCESS;
556}
557
558DEFUN(cfg_nse_encaps, cfg_nse_encaps_cmd,
Harald Welte6d4db232020-11-25 19:33:42 +0100559 "nse <0-65535> encapsulation (udp|framerelay-gre)",
Alexander Couzens6a161492020-07-12 13:45:50 +0200560 NSE_CMD_STR
561 "Encapsulation for NS\n"
562 "UDP/IP Encapsulation\n" "Frame-Relay/GRE/IP Encapsulation\n")
563{
564 uint16_t nsei = atoi(argv[0]);
565 struct ns2_vty_vc *vtyvc;
566
Harald Welte6d4db232020-11-25 19:33:42 +0100567 vtyvc = vtyvc_by_nsei(nsei, true);
Alexander Couzens6a161492020-07-12 13:45:50 +0200568 if (!vtyvc) {
569 vty_out(vty, "Can not allocate space %s", VTY_NEWLINE);
570 return CMD_WARNING;
571 }
572
Harald Welte6d4db232020-11-25 19:33:42 +0100573 if (!strcmp(argv[1], "udp"))
Alexander Couzens24a14ac2020-11-19 02:34:49 +0100574 vtyvc->ll = GPRS_NS2_LL_UDP;
Alexander Couzens6a161492020-07-12 13:45:50 +0200575 else
Alexander Couzens24a14ac2020-11-19 02:34:49 +0100576 vtyvc->ll = GPRS_NS2_LL_FR_GRE;
Alexander Couzens6a161492020-07-12 13:45:50 +0200577
578 return CMD_SUCCESS;
579}
580
581DEFUN(cfg_nse_remoterole, cfg_nse_remoterole_cmd,
Harald Welte6d4db232020-11-25 19:33:42 +0100582 "nse <0-65535> remote-role (sgsn|bss)",
Alexander Couzens6a161492020-07-12 13:45:50 +0200583 NSE_CMD_STR
584 "Remote NSE Role\n"
585 "Remote Peer is SGSN\n"
586 "Remote Peer is BSS\n")
587{
588 uint16_t nsei = atoi(argv[0]);
589 struct ns2_vty_vc *vtyvc;
590
Harald Welte6d4db232020-11-25 19:33:42 +0100591 vtyvc = vtyvc_by_nsei(nsei, true);
Alexander Couzens6a161492020-07-12 13:45:50 +0200592 if (!vtyvc) {
593 vty_out(vty, "Can not allocate space %s", VTY_NEWLINE);
594 return CMD_WARNING;
595 }
596
Harald Welte6d4db232020-11-25 19:33:42 +0100597 if (!strcmp(argv[1], "sgsn"))
Alexander Couzens6a161492020-07-12 13:45:50 +0200598 vtyvc->remote_end_is_sgsn = 1;
599 else
600 vtyvc->remote_end_is_sgsn = 0;
601
602 return CMD_SUCCESS;
603}
604
605DEFUN(cfg_no_nse, cfg_no_nse_cmd,
Harald Welte6d4db232020-11-25 19:33:42 +0100606 "no nse <0-65535>",
Alexander Couzens6a161492020-07-12 13:45:50 +0200607 "Delete Persistent NS Entity\n"
608 "Delete " NSE_CMD_STR)
609{
610 uint16_t nsei = atoi(argv[0]);
611 struct ns2_vty_vc *vtyvc;
612
Harald Welte6d4db232020-11-25 19:33:42 +0100613 vtyvc = vtyvc_by_nsei(nsei, false);
Alexander Couzens6a161492020-07-12 13:45:50 +0200614 if (!vtyvc) {
615 vty_out(vty, "The NSE %d does not exists.%s", nsei, VTY_NEWLINE);
616 return CMD_WARNING;
617 }
618
619 ns2_vc_free(vtyvc);
620
621 return CMD_SUCCESS;
622}
623
624DEFUN(cfg_ns_timer, cfg_ns_timer_cmd,
625 "timer " NS_TIMERS " <0-65535>",
626 "Network Service Timer\n"
627 NS_TIMERS_HELP "Timer Value\n")
628{
629 int idx = get_string_value(gprs_ns_timer_strs, argv[0]);
630 int val = atoi(argv[1]);
631
632 if (idx < 0 || idx >= ARRAY_SIZE(vty_nsi->timeout))
633 return CMD_WARNING;
634
635 vty_nsi->timeout[idx] = val;
636
637 return CMD_SUCCESS;
638}
639
640#define ENCAPS_STR "NS encapsulation options\n"
641
642DEFUN(cfg_nsip_local_ip, cfg_nsip_local_ip_cmd,
643 "encapsulation udp local-ip " VTY_IPV46_CMD,
644 ENCAPS_STR "NS over UDP Encapsulation\n"
645 "Set the IP address on which we listen for NS/UDP\n"
Alexander Couzensc82c40a2020-09-24 05:55:48 +0200646 "IPv4 Address\n"
647 "IPv6 Address\n")
Alexander Couzens6a161492020-07-12 13:45:50 +0200648{
649 osmo_sockaddr_str_from_str2(&priv.udp, argv[0]);
650
651 return CMD_SUCCESS;
652}
653
654DEFUN(cfg_nsip_local_port, cfg_nsip_local_port_cmd,
655 "encapsulation udp local-port <0-65535>",
656 ENCAPS_STR "NS over UDP Encapsulation\n"
657 "Set the UDP port on which we listen for NS/UDP\n"
658 "UDP port number\n")
659{
660 unsigned int port = atoi(argv[0]);
661
662 priv.udp.port = port;
663
664 return CMD_SUCCESS;
665}
666
667DEFUN(cfg_nsip_dscp, cfg_nsip_dscp_cmd,
668 "encapsulation udp dscp <0-255>",
669 ENCAPS_STR "NS over UDP Encapsulation\n"
670 "Set DSCP/TOS on the UDP socket\n" "DSCP Value\n")
671{
672 int dscp = atoi(argv[0]);
673 struct gprs_ns2_vc_bind *bind;
674
675 priv.dscp = dscp;
676
677 llist_for_each_entry(bind, &vty_nsi->binding, list) {
678 if (gprs_ns2_is_ip_bind(bind))
679 gprs_ns2_ip_bind_set_dscp(bind, dscp);
680 }
681
682 return CMD_SUCCESS;
683}
684
685DEFUN(cfg_nsip_res_block_unblock, cfg_nsip_res_block_unblock_cmd,
686 "encapsulation udp use-reset-block-unblock (enabled|disabled)",
687 ENCAPS_STR "NS over UDP Encapsulation\n"
688 "Use NS-{RESET,BLOCK,UNBLOCK} procedures in violation of 3GPP TS 48.016\n"
689 "Enable NS-{RESET,BLOCK,UNBLOCK}\n"
690 "Disable NS-{RESET,BLOCK,UNBLOCK}\n")
691{
692 enum gprs_ns2_vc_mode vc_mode;
693 struct gprs_ns2_vc_bind *bind;
694
695 if (!strcmp(argv[0], "enabled"))
696 vc_mode = NS2_VC_MODE_BLOCKRESET;
697 else
698 vc_mode = NS2_VC_MODE_ALIVE;
699
700 if (priv.force_vc_mode) {
701 if (priv.vc_mode != vc_mode)
702 {
703 vty_out(vty, "Ignoring use-reset-block because it's already set by %s.%s",
704 priv.force_vc_mode_reason, VTY_NEWLINE);
705 return CMD_WARNING;
706 }
707
708 return CMD_SUCCESS;
709 }
710
711 priv.vc_mode = vc_mode;
712
713 llist_for_each_entry(bind, &vty_nsi->binding, list) {
714 gprs_ns2_bind_set_mode(bind, priv.vc_mode);
715 }
716
717 return CMD_SUCCESS;
718}
719
720DEFUN(cfg_frgre_local_ip, cfg_frgre_local_ip_cmd,
721 "encapsulation framerelay-gre local-ip " VTY_IPV46_CMD,
722 ENCAPS_STR "NS over Frame Relay over GRE Encapsulation\n"
723 "Set the IP address on which we listen for NS/FR/GRE\n"
Alexander Couzensc82c40a2020-09-24 05:55:48 +0200724 "IPv4 Address\n"
725 "IPv6 Address\n")
Alexander Couzens6a161492020-07-12 13:45:50 +0200726{
727 osmo_sockaddr_str_from_str2(&priv.frgreaddr, argv[0]);
728
729 return CMD_SUCCESS;
730}
731
732DEFUN(cfg_frgre_enable, cfg_frgre_enable_cmd,
733 "encapsulation framerelay-gre enabled (1|0)",
734 ENCAPS_STR "NS over Frame Relay over GRE Encapsulation\n"
735 "Enable or disable Frame Relay over GRE\n"
736 "Enable\n" "Disable\n")
737{
738 int enabled = atoi(argv[0]);
739
740 priv.frgre = enabled;
741
742 return CMD_SUCCESS;
743}
744
745/* TODO: allow vty to reset/block/unblock nsvc/nsei */
746
747/* TODO: add filter for NSEI as ns1 code does */
748/* TODO: add filter for single connection by description */
749DEFUN(logging_fltr_nsvc,
750 logging_fltr_nsvc_cmd,
751 "logging filter nsvc nsvci <0-65535>",
752 LOGGING_STR FILTER_STR
753 "Filter based on NS Virtual Connection\n"
754 "Identify NS-VC by NSVCI\n"
755 "Numeric identifier\n")
756{
757 struct log_target *tgt;
758 struct gprs_ns2_vc *nsvc;
759 uint16_t id = atoi(argv[1]);
760
761 log_tgt_mutex_lock();
762 tgt = osmo_log_vty2tgt(vty);
763 if (!tgt) {
764 log_tgt_mutex_unlock();
765 return CMD_WARNING;
766 }
767
768 nsvc = gprs_ns2_nsvc_by_nsvci(vty_nsi, id);
769 if (!nsvc) {
770 vty_out(vty, "No NS-VC by that identifier%s", VTY_NEWLINE);
771 log_tgt_mutex_unlock();
772 return CMD_WARNING;
773 }
774
775 log_set_nsvc_filter(tgt, nsvc);
776 log_tgt_mutex_unlock();
777 return CMD_SUCCESS;
778}
779
Alexander Couzens1fac6f72020-10-01 19:08:38 +0200780/**
781 * gprs_ns2_vty_init initialize the vty
782 * \param[inout] nsi
783 * \param[in] default_bind set the default address to bind to. Can be NULL.
784 * \return 0 on success
785 */
Vadim Yanitskiya07f25e2020-10-09 21:47:01 +0700786int gprs_ns2_vty_init(struct gprs_ns2_inst *nsi,
787 const struct osmo_sockaddr_str *default_bind)
Alexander Couzens6a161492020-07-12 13:45:50 +0200788{
789 static bool vty_elements_installed = false;
790
791 vty_nsi = nsi;
792 memset(&priv, 0, sizeof(struct ns2_vty_priv));
793 INIT_LLIST_HEAD(&priv.vtyvc);
794 priv.vc_mode = NS2_VC_MODE_BLOCKRESET;
Alexander Couzens1fac6f72020-10-01 19:08:38 +0200795 if (default_bind)
796 memcpy(&priv.udp, default_bind, sizeof(*default_bind));
Alexander Couzens6a161492020-07-12 13:45:50 +0200797
798 /* Regression test code may call this function repeatedly, so make sure
799 * that VTY elements are not duplicated, which would assert. */
800 if (vty_elements_installed)
801 return 0;
802 vty_elements_installed = true;
803
Harald Welte2fce19a2020-12-01 17:52:55 +0100804 install_lib_element_ve(&show_ns_binds_cmd);
805 install_lib_element_ve(&show_ns_entities_cmd);
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +0700806 install_lib_element_ve(&show_ns_pers_cmd);
807 install_lib_element_ve(&show_nse_cmd);
808 install_lib_element_ve(&logging_fltr_nsvc_cmd);
Alexander Couzens6a161492020-07-12 13:45:50 +0200809
Daniel Willmanndbab7142020-11-18 14:19:56 +0100810 install_lib_element(ENABLE_NODE, &nsvc_force_unconf_cmd);
811
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +0700812 install_lib_element(CFG_LOG_NODE, &logging_fltr_nsvc_cmd);
Alexander Couzens6a161492020-07-12 13:45:50 +0200813
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +0700814 install_lib_element(CONFIG_NODE, &cfg_ns_cmd);
Alexander Couzens6a161492020-07-12 13:45:50 +0200815 install_node(&ns_node, config_write_ns);
Alexander Couzens841817e2020-11-19 00:41:29 +0100816 install_lib_element(L_NS_NODE, &cfg_nse_fr_cmd);
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +0700817 install_lib_element(L_NS_NODE, &cfg_nse_nsvci_cmd);
818 install_lib_element(L_NS_NODE, &cfg_nse_remoteip_cmd);
819 install_lib_element(L_NS_NODE, &cfg_nse_remoteport_cmd);
820 install_lib_element(L_NS_NODE, &cfg_nse_fr_dlci_cmd);
821 install_lib_element(L_NS_NODE, &cfg_nse_encaps_cmd);
822 install_lib_element(L_NS_NODE, &cfg_nse_remoterole_cmd);
823 install_lib_element(L_NS_NODE, &cfg_no_nse_cmd);
824 install_lib_element(L_NS_NODE, &cfg_ns_timer_cmd);
825 install_lib_element(L_NS_NODE, &cfg_nsip_local_ip_cmd);
826 install_lib_element(L_NS_NODE, &cfg_nsip_local_port_cmd);
827 install_lib_element(L_NS_NODE, &cfg_nsip_dscp_cmd);
828 install_lib_element(L_NS_NODE, &cfg_nsip_res_block_unblock_cmd);
829 install_lib_element(L_NS_NODE, &cfg_frgre_enable_cmd);
830 install_lib_element(L_NS_NODE, &cfg_frgre_local_ip_cmd);
Alexander Couzens6a161492020-07-12 13:45:50 +0200831
832 /* TODO: nsvc/nsei command to reset states or reset/block/unblock nsei/nsvcs */
833
834 return 0;
835}
836
837/*!
838 * \brief gprs_ns2_vty_create parse the vty tree into ns nodes
839 * It has to be in different steps to ensure the bind is created before creating VCs.
840 * \return 0 on success
841 */
842int gprs_ns2_vty_create() {
843 struct ns2_vty_vc *vtyvc;
Alexander Couzens841817e2020-11-19 00:41:29 +0100844 struct gprs_ns2_vc_bind *bind, *fr;
Alexander Couzens6a161492020-07-12 13:45:50 +0200845 struct gprs_ns2_nse *nse;
846 struct gprs_ns2_vc *nsvc;
847 struct osmo_sockaddr sockaddr;
Alexander Couzens841817e2020-11-19 00:41:29 +0100848 int rc = 0;
Alexander Couzens6a161492020-07-12 13:45:50 +0200849
850 if (!vty_nsi)
851 return -1;
852
853 /* create binds, only support a single bind. either FR or UDP */
854 if (priv.frgre) {
855 /* TODO not yet supported !*/
856 return -1;
857 } else {
858 /* UDP */
859 osmo_sockaddr_str_to_sockaddr(&priv.udp, &sockaddr.u.sas);
Alexander Couzens477ffb02020-10-01 19:05:28 +0200860 if (gprs_ns2_ip_bind(vty_nsi, &sockaddr, priv.dscp, &bind)) {
Alexander Couzens6a161492020-07-12 13:45:50 +0200861 /* TODO: could not bind on the specific address */
862 return -1;
863 }
864 gprs_ns2_bind_set_mode(bind, priv.vc_mode);
865 }
866
867 /* create vcs */
868 llist_for_each_entry(vtyvc, &priv.vtyvc, list) {
Alexander Couzens841817e2020-11-19 00:41:29 +0100869 /* validate settings */
870 switch (vtyvc->ll) {
Alexander Couzens24a14ac2020-11-19 02:34:49 +0100871 case GPRS_NS2_LL_UDP:
Alexander Couzens841817e2020-11-19 00:41:29 +0100872 if (strlen(vtyvc->remote.ip) == 0) {
873 /* Invalid IP for VC */
874 continue;
875 }
Alexander Couzens6a161492020-07-12 13:45:50 +0200876
Alexander Couzens841817e2020-11-19 00:41:29 +0100877 if (!vtyvc->remote.port) {
878 /* Invalid port for VC */
879 continue;
880 }
Alexander Couzens6a161492020-07-12 13:45:50 +0200881
Alexander Couzens841817e2020-11-19 00:41:29 +0100882 if (osmo_sockaddr_str_to_sockaddr(&vtyvc->remote, &sockaddr.u.sas)) {
883 /* Invalid sockaddr for VC */
884 continue;
885 }
886 break;
Alexander Couzens24a14ac2020-11-19 02:34:49 +0100887 case GPRS_NS2_LL_FR:
Alexander Couzens841817e2020-11-19 00:41:29 +0100888 break;
Alexander Couzens24a14ac2020-11-19 02:34:49 +0100889 case GPRS_NS2_LL_FR_GRE:
Alexander Couzens6a161492020-07-12 13:45:50 +0200890 continue;
891 }
892
893 nse = gprs_ns2_nse_by_nsei(vty_nsi, vtyvc->nsei);
894 if (!nse) {
Alexander Couzensaac90162020-11-19 02:44:04 +0100895 nse = gprs_ns2_create_nse(vty_nsi, vtyvc->nsei, vtyvc->ll);
Alexander Couzens6a161492020-07-12 13:45:50 +0200896 if (!nse) {
897 /* Could not create NSE for VTY */
898 continue;
899 }
900 }
901 nse->persistent = true;
902
Alexander Couzens841817e2020-11-19 00:41:29 +0100903 switch (vtyvc->ll) {
Alexander Couzens24a14ac2020-11-19 02:34:49 +0100904 case GPRS_NS2_LL_UDP:
Alexander Couzens841817e2020-11-19 00:41:29 +0100905 nsvc = gprs_ns2_ip_connect(bind,
906 &sockaddr,
907 nse,
908 vtyvc->nsvci);
909 if (!nsvc) {
910 /* Could not create NSVC, connect failed */
911 continue;
912 }
913 nsvc->persistent = true;
914 break;
Alexander Couzens24a14ac2020-11-19 02:34:49 +0100915 case GPRS_NS2_LL_FR: {
Alexander Couzens841817e2020-11-19 00:41:29 +0100916 if (vty_fr_network == NULL) {
917 /* TODO: add a switch for BSS/SGSN/gbproxy */
918 vty_fr_network = osmo_fr_network_alloc(vty_nsi);
919 }
920 fr = gprs_ns2_fr_bind_by_netif(
921 vty_nsi,
922 vtyvc->netif);
923 if (!fr) {
924 rc = gprs_ns2_fr_bind(vty_nsi, vtyvc->netif, vty_fr_network, vtyvc->fr.role, &fr);
925 if (rc < 0) {
926 LOGP(DLNS, LOGL_ERROR, "Can not create fr bind on device %s err: %d\n", vtyvc->netif, rc);
927 return rc;
928 }
929 }
930
931 nsvc = gprs_ns2_fr_connect(fr, vtyvc->nsei, vtyvc->nsvci, vtyvc->frdlci);
932 if (!nsvc) {
933 /* Could not create NSVC, connect failed */
934 continue;
935 }
936 nsvc->persistent = true;
937 break;
938 }
Alexander Couzens24a14ac2020-11-19 02:34:49 +0100939 case GPRS_NS2_LL_FR_GRE:
Alexander Couzensd745a0e2020-10-07 00:50:00 +0200940 continue;
Alexander Couzens6a161492020-07-12 13:45:50 +0200941 }
942 }
943
944
945 return 0;
946}
947
948/*!
949 * \brief ns2_vty_bind_apply will be called when a new bind is created to apply vty settings
950 * \param bind
951 * \return
952 */
953void ns2_vty_bind_apply(struct gprs_ns2_vc_bind *bind)
954{
955 gprs_ns2_bind_set_mode(bind, priv.vc_mode);
956}
957
958/*!
959 * \brief ns2_vty_force_vc_mode force a mode and prevents the vty from overwriting it.
960 * \param force if true mode and reason will be set. false to allow modification via vty.
961 * \param mode
962 * \param reason A description shown to the user when a vty command wants to change the mode.
963 */
Daniel Willmann4fb27a82020-09-25 15:39:46 +0200964void gprs_ns2_vty_force_vc_mode(bool force, enum gprs_ns2_vc_mode mode, const char *reason)
Alexander Couzens6a161492020-07-12 13:45:50 +0200965{
966 priv.force_vc_mode = force;
967
968 if (force) {
969 priv.vc_mode = mode;
970 priv.force_vc_mode_reason = reason;
971 }
972}