blob: 81f88b627ca32ad1ca1e2c463e821fa56992636e [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>
34
35#include <osmocom/core/msgb.h>
36#include <osmocom/core/byteswap.h>
37#include <osmocom/core/talloc.h>
38#include <osmocom/core/select.h>
39#include <osmocom/core/rate_ctr.h>
40#include <osmocom/core/socket.h>
41#include <osmocom/core/sockaddr_str.h>
42#include <osmocom/core/linuxlist.h>
43#include <osmocom/core/socket.h>
44#include <osmocom/gprs/gprs_ns2.h>
45#include <osmocom/gsm/tlv.h>
46#include <osmocom/vty/vty.h>
47#include <osmocom/vty/command.h>
48#include <osmocom/vty/logging.h>
49#include <osmocom/vty/telnet_interface.h>
50#include <osmocom/vty/misc.h>
51
52#include "gprs_ns2_internal.h"
53
54struct ns2_vty_priv {
55 /* global listen */
56 struct osmo_sockaddr_str udp;
57 struct osmo_sockaddr_str frgreaddr;
58 int dscp;
59 enum gprs_ns2_vc_mode vc_mode;
60 /* force vc mode if another configuration forces
61 * the vc mode. E.g. SNS configuration */
62 bool force_vc_mode;
63 char *force_vc_mode_reason;
64 bool frgre;
65
66 struct llist_head vtyvc;
67};
68
69struct ns2_vty_vc {
70 struct llist_head list;
71
72 struct osmo_sockaddr_str remote;
73 enum gprs_ns_ll ll;
74
75 /* old vty code doesnt support multiple NSVCI per NSEI */
76 uint16_t nsei;
77 uint16_t nsvci;
78 uint16_t frdlci;
79
80 bool remote_end_is_sgsn;
81 bool configured;
82};
83
84static struct gprs_ns2_inst *vty_nsi = NULL;
85static struct ns2_vty_priv priv;
86
87/* FIXME: this should go to some common file as it is copied
88 * in vty_interface.c of the BSC */
89static const struct value_string gprs_ns_timer_strs[] = {
90 { 0, "tns-block" },
91 { 1, "tns-block-retries" },
92 { 2, "tns-reset" },
93 { 3, "tns-reset-retries" },
94 { 4, "tns-test" },
95 { 5, "tns-alive" },
96 { 6, "tns-alive-retries" },
97 { 7, "tsns-prov" },
98 { 0, NULL }
99};
100
101static void log_set_nsvc_filter(struct log_target *target,
102 struct gprs_ns2_vc *nsvc)
103{
104 if (nsvc) {
105 target->filter_map |= (1 << LOG_FLT_GB_NSVC);
106 target->filter_data[LOG_FLT_GB_NSVC] = nsvc;
107 } else if (target->filter_data[LOG_FLT_GB_NSVC]) {
108 target->filter_map = ~(1 << LOG_FLT_GB_NSVC);
109 target->filter_data[LOG_FLT_GB_NSVC] = NULL;
110 }
111}
112
113static struct cmd_node ns_node = {
114 L_NS_NODE,
115 "%s(config-ns)# ",
116 1,
117};
118
119static struct ns2_vty_vc *vtyvc_alloc(uint16_t nsei) {
120 struct ns2_vty_vc *vtyvc = talloc_zero(vty_nsi, struct ns2_vty_vc);
121 if (!vtyvc)
122 return vtyvc;
123
124 vtyvc->nsei = nsei;
125
126 llist_add(&vtyvc->list, &priv.vtyvc);
127
128 return vtyvc;
129}
130
131static void ns2_vc_free(struct ns2_vty_vc *vtyvc) {
132 if (!vtyvc)
133 return;
134
135 llist_del(&vtyvc->list);
136 talloc_free(vtyvc);
137}
138
139static struct ns2_vty_vc *vtyvc_by_nsei(uint16_t nsei, bool alloc_missing) {
140 struct ns2_vty_vc *vtyvc;
141 llist_for_each_entry(vtyvc, &priv.vtyvc, list) {
142 if (vtyvc->nsei == nsei)
143 return vtyvc;
144 }
145
146 if (alloc_missing) {
147 vtyvc = vtyvc_alloc(nsei);
148 if (!vtyvc)
149 return vtyvc;
150
151 vtyvc->nsei = nsei;
152 }
153
154 return NULL;
155}
156
157static int config_write_ns(struct vty *vty)
158{
159 struct ns2_vty_vc *vtyvc;
160 unsigned int i;
161 struct osmo_sockaddr_str sockstr;
162
163 vty_out(vty, "ns%s", VTY_NEWLINE);
164
165 /* global configuration must be written first, as some of it may be
166 * relevant when creating the NSE/NSVC later below */
167
168 vty_out(vty, " encapsulation framerelay-gre enabled %u%s",
169 priv.frgre ? 1 : 0, VTY_NEWLINE);
170
171 if (priv.frgre) {
172 if (strlen(priv.frgreaddr.ip)) {
173 vty_out(vty, " encapsulation framerelay-gre local-ip %s%s",
174 sockstr.ip, VTY_NEWLINE);
175 }
176 } else {
177 if (strlen(priv.udp.ip)) {
178 vty_out(vty, " encapsulation udp local-ip %s%s",
179 priv.udp.ip, VTY_NEWLINE);
180 }
181
182 if (priv.udp.port)
183 vty_out(vty, " encapsulation udp local-port %u%s",
184 priv.udp.port, VTY_NEWLINE);
185 }
186
187 if (priv.dscp)
188 vty_out(vty, " encapsulation udp dscp %d%s",
189 priv.dscp, VTY_NEWLINE);
190
191 vty_out(vty, " encapsulation udp use-reset-block-unblock %s%s",
192 priv.vc_mode == NS2_VC_MODE_BLOCKRESET ? "enabled" : "disabled", VTY_NEWLINE);
193
194 llist_for_each_entry(vtyvc, &priv.vtyvc, list) {
195 vty_out(vty, " nse %u nsvci %u%s",
196 vtyvc->nsei, vtyvc->nsvci, VTY_NEWLINE);
197
198 vty_out(vty, " nse %u remote-role %s%s",
199 vtyvc->nsei, vtyvc->remote_end_is_sgsn ? "sgsn" : "bss",
200 VTY_NEWLINE);
201
202 switch (vtyvc->ll) {
203 case GPRS_NS_LL_UDP:
204 vty_out(vty, " nse %u encapsulation udp%s", vtyvc->nsei, VTY_NEWLINE);
205 vty_out(vty, " nse %u remote-ip %s%s",
206 vtyvc->nsei,
207 vtyvc->remote.ip,
208 VTY_NEWLINE);
209 vty_out(vty, " nse %u remote-port %u%s",
210 vtyvc->nsei, vtyvc->remote.port,
211 VTY_NEWLINE);
212 break;
213 case GPRS_NS_LL_FR_GRE:
214 vty_out(vty, " nse %u encapsulation framerelay-gre%s",
215 vtyvc->nsei, VTY_NEWLINE);
216 vty_out(vty, " nse %u remote-ip %s%s",
217 vtyvc->nsei,
218 vtyvc->remote.ip,
219 VTY_NEWLINE);
220 vty_out(vty, " nse %u fr-dlci %u%s",
221 vtyvc->nsei, vtyvc->frdlci,
222 VTY_NEWLINE);
223 break;
224 default:
225 break;
226 }
227 }
228
229 for (i = 0; i < ARRAY_SIZE(vty_nsi->timeout); i++)
230 vty_out(vty, " timer %s %u%s",
231 get_value_string(gprs_ns_timer_strs, i),
232 vty_nsi->timeout[i], VTY_NEWLINE);
233
234 return CMD_SUCCESS;
235}
236
237DEFUN(cfg_ns, cfg_ns_cmd,
238 "ns",
239 "Configure the GPRS Network Service")
240{
241 vty->node = L_NS_NODE;
242 return CMD_SUCCESS;
243}
244
245static void dump_nsvc(struct vty *vty, struct gprs_ns2_vc *nsvc, bool stats)
246{
247 struct osmo_sockaddr_str remote;
248 struct osmo_sockaddr_str local;
249 struct osmo_sockaddr *sockaddr;
250
251 switch (nsvc->ll) {
252 case GPRS_NS_LL_UDP: {
253 sockaddr = gprs_ns2_ip_vc_sockaddr(nsvc);
254 if (!sockaddr) {
255 vty_out(vty, "unknown");
256 break;
257 }
258
259 if (osmo_sockaddr_str_from_sockaddr(
260 &remote,
261 &sockaddr->u.sas)) {
262 vty_out(vty, "unknown");
263 break;
264 }
265
266 vty_out(vty, "%s:%u <> %s:%u", local.ip, local.port, remote.ip, remote.port);
267 break;
268 }
269 case GPRS_NS_LL_FR_GRE:
270 /* TODO: implement dump_nse for FR GRE */
271 case GPRS_NS_LL_E1:
272 /* TODO: implement dump_nse for E1 */
273 break;
274 }
275
276 vty_out(vty, "Remote: %s ",
277 gprs_ns2_ll_str(nsvc));
278
279 vty_out(vty, "%s%s", nsvc->ll == GPRS_NS_LL_UDP ? "UDP" : "FR-GRE", VTY_NEWLINE);
280
281 if (stats) {
282 vty_out_rate_ctr_group(vty, " ", nsvc->ctrg);
283 vty_out_stat_item_group(vty, " ", nsvc->statg);
284 }
285}
286
287static void dump_nse(struct vty *vty, const struct gprs_ns2_nse *nse, bool stats, bool persistent_only)
288{
289 struct gprs_ns2_vc *nsvc;
290
291 vty_out(vty, "NSEI %5u%s",
292 nse->nsei, VTY_NEWLINE);
293
294 gprs_ns2_sns_dump_vty(vty, nse, stats);
295 llist_for_each_entry(nsvc, &nse->nsvc, list) {
296 if (persistent_only) {
297 if (nsvc->persistent)
298 dump_nsvc(vty, nsvc, stats);
299 } else {
300 dump_nsvc(vty, nsvc, stats);
301 }
302 }
303}
304
305static void dump_ns(struct vty *vty, const struct gprs_ns2_inst *nsi, bool stats, bool persistent_only)
306{
307 struct gprs_ns2_nse *nse;
308
309 llist_for_each_entry(nse, &nsi->nse, list) {
310 dump_nse(vty, nse, stats, persistent_only);
311 break;
312 }
313
314}
315
316DEFUN(show_ns, show_ns_cmd, "show ns",
317 SHOW_STR "Display information about the NS protocol")
318{
319 dump_ns(vty, vty_nsi, false, false);
320 return CMD_SUCCESS;
321}
322
323DEFUN(show_ns_stats, show_ns_stats_cmd, "show ns stats",
324 SHOW_STR
325 "Display information about the NS protocol\n"
326 "Include statistics\n")
327{
328 dump_ns(vty, vty_nsi, true, false);
329 return CMD_SUCCESS;
330}
331
332DEFUN(show_ns_pers, show_ns_pers_cmd, "show ns persistent",
333 SHOW_STR
334 "Display information about the NS protocol\n"
335 "Show only persistent NS\n")
336{
337 dump_ns(vty, vty_nsi, true, true);
338 return CMD_SUCCESS;
339}
340
341DEFUN(show_nse, show_nse_cmd, "show ns (nsei|nsvc) <0-65535> [stats]",
342 SHOW_STR "Display information about the NS protocol\n"
343 "Select one NSE by its NSE Identifier\n"
344 "Select one NSE by its NS-VC Identifier\n"
345 "The Identifier of selected type\n"
346 "Include Statistics\n")
347{
348 struct gprs_ns2_inst *nsi = vty_nsi;
349 struct gprs_ns2_nse *nse;
350 struct gprs_ns2_vc *nsvc;
351 uint16_t id = atoi(argv[1]);
352 bool show_stats = false;
353
354 if (argc >= 3)
355 show_stats = true;
356
357 if (!strcmp(argv[0], "nsei")) {
358 nse = gprs_ns2_nse_by_nsei(nsi, id);
359 if (!nse) {
360 return CMD_WARNING;
361 }
362
363 dump_nse(vty, nse, show_stats, false);
364 } else {
365 nsvc = gprs_ns2_nsvc_by_nsvci(nsi, id);
366
367 if (!nsvc) {
368 vty_out(vty, "No such NS Entity%s", VTY_NEWLINE);
369 return CMD_WARNING;
370 }
371
372 dump_nsvc(vty, nsvc, show_stats);
373 }
374
375 return CMD_SUCCESS;
376}
377
378#define NSE_CMD_STR "Persistent NS Entity\n" "NS Entity ID (NSEI)\n"
379
380DEFUN(cfg_nse_nsvc, cfg_nse_nsvci_cmd,
381 "nse <0-65535> nsvci <0-65535>",
382 NSE_CMD_STR
383 "NS Virtual Connection\n"
384 "NS Virtual Connection ID (NSVCI)\n"
385 )
386{
387 struct ns2_vty_vc *vtyvc;
388
389 uint16_t nsei = atoi(argv[0]);
390 uint16_t nsvci = atoi(argv[1]);
391
392 vtyvc = vtyvc_by_nsei(nsei, true);
393 if (!vtyvc) {
394 vty_out(vty, "Can not allocate space %s", VTY_NEWLINE);
395 return CMD_WARNING;
396 }
397
398 vtyvc->nsvci = nsvci;
399
400 return CMD_SUCCESS;
401}
402
403DEFUN(cfg_nse_remoteip, cfg_nse_remoteip_cmd,
404 "nse <0-65535> remote-ip " VTY_IPV46_CMD,
405 NSE_CMD_STR
406 "Remote IP Address\n"
407 "Remote IP Address\n")
408{
409 uint16_t nsei = atoi(argv[0]);
410 struct ns2_vty_vc *vtyvc;
411
412 vtyvc = vtyvc_by_nsei(nsei, true);
413 if (!vtyvc) {
414 vty_out(vty, "Can not allocate space %s", VTY_NEWLINE);
415 return CMD_WARNING;
416 }
417
418 osmo_sockaddr_str_from_str2(&vtyvc->remote, argv[1]);
419
420 return CMD_SUCCESS;
421}
422
423DEFUN(cfg_nse_remoteport, cfg_nse_remoteport_cmd,
424 "nse <0-65535> remote-port <0-65535>",
425 NSE_CMD_STR
426 "Remote UDP Port\n"
427 "Remote UDP Port Number\n")
428{
429 uint16_t nsei = atoi(argv[0]);
430 uint16_t port = atoi(argv[1]);
431 struct ns2_vty_vc *vtyvc;
432
433 vtyvc = vtyvc_by_nsei(nsei, true);
434 if (!vtyvc) {
435 vty_out(vty, "Can not allocate space %s", VTY_NEWLINE);
436 return CMD_WARNING;
437 }
438
439 vtyvc->remote.port = port;
440
441 return CMD_SUCCESS;
442}
443
444DEFUN(cfg_nse_fr_dlci, cfg_nse_fr_dlci_cmd,
445 "nse <0-65535> fr-dlci <16-1007>",
446 NSE_CMD_STR
447 "Frame Relay DLCI\n"
448 "Frame Relay DLCI Number\n")
449{
450 uint16_t nsei = atoi(argv[0]);
451 uint16_t dlci = atoi(argv[1]);
452 struct ns2_vty_vc *vtyvc;
453
454 vtyvc = vtyvc_by_nsei(nsei, true);
455 if (!vtyvc) {
456 vty_out(vty, "Can not allocate space %s", VTY_NEWLINE);
457 return CMD_WARNING;
458 }
459
460 if (vtyvc->ll != GPRS_NS_LL_FR_GRE) {
461 vty_out(vty, "Warning: seting FR DLCI on non-FR NSE%s",
462 VTY_NEWLINE);
463 }
464
465 vtyvc->frdlci = dlci;
466
467 return CMD_SUCCESS;
468}
469
470DEFUN(cfg_nse_encaps, cfg_nse_encaps_cmd,
471 "nse <0-65535> encapsulation (udp|framerelay-gre)",
472 NSE_CMD_STR
473 "Encapsulation for NS\n"
474 "UDP/IP Encapsulation\n" "Frame-Relay/GRE/IP Encapsulation\n")
475{
476 uint16_t nsei = atoi(argv[0]);
477 struct ns2_vty_vc *vtyvc;
478
479 vtyvc = vtyvc_by_nsei(nsei, true);
480 if (!vtyvc) {
481 vty_out(vty, "Can not allocate space %s", VTY_NEWLINE);
482 return CMD_WARNING;
483 }
484
485 if (!strcmp(argv[1], "udp"))
486 vtyvc->ll = GPRS_NS_LL_UDP;
487 else
488 vtyvc->ll = GPRS_NS_LL_FR_GRE;
489
490 return CMD_SUCCESS;
491}
492
493DEFUN(cfg_nse_remoterole, cfg_nse_remoterole_cmd,
494 "nse <0-65535> remote-role (sgsn|bss)",
495 NSE_CMD_STR
496 "Remote NSE Role\n"
497 "Remote Peer is SGSN\n"
498 "Remote Peer is BSS\n")
499{
500 uint16_t nsei = atoi(argv[0]);
501 struct ns2_vty_vc *vtyvc;
502
503 vtyvc = vtyvc_by_nsei(nsei, true);
504 if (!vtyvc) {
505 vty_out(vty, "Can not allocate space %s", VTY_NEWLINE);
506 return CMD_WARNING;
507 }
508
509 if (!strcmp(argv[1], "sgsn"))
510 vtyvc->remote_end_is_sgsn = 1;
511 else
512 vtyvc->remote_end_is_sgsn = 0;
513
514 return CMD_SUCCESS;
515}
516
517DEFUN(cfg_no_nse, cfg_no_nse_cmd,
518 "no nse <0-65535>",
519 "Delete Persistent NS Entity\n"
520 "Delete " NSE_CMD_STR)
521{
522 uint16_t nsei = atoi(argv[0]);
523 struct ns2_vty_vc *vtyvc;
524
525 vtyvc = vtyvc_by_nsei(nsei, false);
526 if (!vtyvc) {
527 vty_out(vty, "The NSE %d does not exists.%s", nsei, VTY_NEWLINE);
528 return CMD_WARNING;
529 }
530
531 ns2_vc_free(vtyvc);
532
533 return CMD_SUCCESS;
534}
535
536DEFUN(cfg_ns_timer, cfg_ns_timer_cmd,
537 "timer " NS_TIMERS " <0-65535>",
538 "Network Service Timer\n"
539 NS_TIMERS_HELP "Timer Value\n")
540{
541 int idx = get_string_value(gprs_ns_timer_strs, argv[0]);
542 int val = atoi(argv[1]);
543
544 if (idx < 0 || idx >= ARRAY_SIZE(vty_nsi->timeout))
545 return CMD_WARNING;
546
547 vty_nsi->timeout[idx] = val;
548
549 return CMD_SUCCESS;
550}
551
552#define ENCAPS_STR "NS encapsulation options\n"
553
554DEFUN(cfg_nsip_local_ip, cfg_nsip_local_ip_cmd,
555 "encapsulation udp local-ip " VTY_IPV46_CMD,
556 ENCAPS_STR "NS over UDP Encapsulation\n"
557 "Set the IP address on which we listen for NS/UDP\n"
558 "IP Address\n")
559{
560 osmo_sockaddr_str_from_str2(&priv.udp, argv[0]);
561
562 return CMD_SUCCESS;
563}
564
565DEFUN(cfg_nsip_local_port, cfg_nsip_local_port_cmd,
566 "encapsulation udp local-port <0-65535>",
567 ENCAPS_STR "NS over UDP Encapsulation\n"
568 "Set the UDP port on which we listen for NS/UDP\n"
569 "UDP port number\n")
570{
571 unsigned int port = atoi(argv[0]);
572
573 priv.udp.port = port;
574
575 return CMD_SUCCESS;
576}
577
578DEFUN(cfg_nsip_dscp, cfg_nsip_dscp_cmd,
579 "encapsulation udp dscp <0-255>",
580 ENCAPS_STR "NS over UDP Encapsulation\n"
581 "Set DSCP/TOS on the UDP socket\n" "DSCP Value\n")
582{
583 int dscp = atoi(argv[0]);
584 struct gprs_ns2_vc_bind *bind;
585
586 priv.dscp = dscp;
587
588 llist_for_each_entry(bind, &vty_nsi->binding, list) {
589 if (gprs_ns2_is_ip_bind(bind))
590 gprs_ns2_ip_bind_set_dscp(bind, dscp);
591 }
592
593 return CMD_SUCCESS;
594}
595
596DEFUN(cfg_nsip_res_block_unblock, cfg_nsip_res_block_unblock_cmd,
597 "encapsulation udp use-reset-block-unblock (enabled|disabled)",
598 ENCAPS_STR "NS over UDP Encapsulation\n"
599 "Use NS-{RESET,BLOCK,UNBLOCK} procedures in violation of 3GPP TS 48.016\n"
600 "Enable NS-{RESET,BLOCK,UNBLOCK}\n"
601 "Disable NS-{RESET,BLOCK,UNBLOCK}\n")
602{
603 enum gprs_ns2_vc_mode vc_mode;
604 struct gprs_ns2_vc_bind *bind;
605
606 if (!strcmp(argv[0], "enabled"))
607 vc_mode = NS2_VC_MODE_BLOCKRESET;
608 else
609 vc_mode = NS2_VC_MODE_ALIVE;
610
611 if (priv.force_vc_mode) {
612 if (priv.vc_mode != vc_mode)
613 {
614 vty_out(vty, "Ignoring use-reset-block because it's already set by %s.%s",
615 priv.force_vc_mode_reason, VTY_NEWLINE);
616 return CMD_WARNING;
617 }
618
619 return CMD_SUCCESS;
620 }
621
622 priv.vc_mode = vc_mode;
623
624 llist_for_each_entry(bind, &vty_nsi->binding, list) {
625 gprs_ns2_bind_set_mode(bind, priv.vc_mode);
626 }
627
628 return CMD_SUCCESS;
629}
630
631DEFUN(cfg_frgre_local_ip, cfg_frgre_local_ip_cmd,
632 "encapsulation framerelay-gre local-ip " VTY_IPV46_CMD,
633 ENCAPS_STR "NS over Frame Relay over GRE Encapsulation\n"
634 "Set the IP address on which we listen for NS/FR/GRE\n"
635 "IP Address\n")
636{
637 osmo_sockaddr_str_from_str2(&priv.frgreaddr, argv[0]);
638
639 return CMD_SUCCESS;
640}
641
642DEFUN(cfg_frgre_enable, cfg_frgre_enable_cmd,
643 "encapsulation framerelay-gre enabled (1|0)",
644 ENCAPS_STR "NS over Frame Relay over GRE Encapsulation\n"
645 "Enable or disable Frame Relay over GRE\n"
646 "Enable\n" "Disable\n")
647{
648 int enabled = atoi(argv[0]);
649
650 priv.frgre = enabled;
651
652 return CMD_SUCCESS;
653}
654
655/* TODO: allow vty to reset/block/unblock nsvc/nsei */
656
657/* TODO: add filter for NSEI as ns1 code does */
658/* TODO: add filter for single connection by description */
659DEFUN(logging_fltr_nsvc,
660 logging_fltr_nsvc_cmd,
661 "logging filter nsvc nsvci <0-65535>",
662 LOGGING_STR FILTER_STR
663 "Filter based on NS Virtual Connection\n"
664 "Identify NS-VC by NSVCI\n"
665 "Numeric identifier\n")
666{
667 struct log_target *tgt;
668 struct gprs_ns2_vc *nsvc;
669 uint16_t id = atoi(argv[1]);
670
671 log_tgt_mutex_lock();
672 tgt = osmo_log_vty2tgt(vty);
673 if (!tgt) {
674 log_tgt_mutex_unlock();
675 return CMD_WARNING;
676 }
677
678 nsvc = gprs_ns2_nsvc_by_nsvci(vty_nsi, id);
679 if (!nsvc) {
680 vty_out(vty, "No NS-VC by that identifier%s", VTY_NEWLINE);
681 log_tgt_mutex_unlock();
682 return CMD_WARNING;
683 }
684
685 log_set_nsvc_filter(tgt, nsvc);
686 log_tgt_mutex_unlock();
687 return CMD_SUCCESS;
688}
689
690int gprs_ns2_vty_init(struct gprs_ns2_inst *nsi)
691{
692 static bool vty_elements_installed = false;
693
694 vty_nsi = nsi;
695 memset(&priv, 0, sizeof(struct ns2_vty_priv));
696 INIT_LLIST_HEAD(&priv.vtyvc);
697 priv.vc_mode = NS2_VC_MODE_BLOCKRESET;
698
699 /* Regression test code may call this function repeatedly, so make sure
700 * that VTY elements are not duplicated, which would assert. */
701 if (vty_elements_installed)
702 return 0;
703 vty_elements_installed = true;
704
705 install_element_ve(&show_ns_cmd);
706 install_element_ve(&show_ns_stats_cmd);
707 install_element_ve(&show_ns_pers_cmd);
708 install_element_ve(&show_nse_cmd);
709 install_element_ve(&logging_fltr_nsvc_cmd);
710
711 install_element(CFG_LOG_NODE, &logging_fltr_nsvc_cmd);
712
713 install_element(CONFIG_NODE, &cfg_ns_cmd);
714 install_node(&ns_node, config_write_ns);
715 install_element(L_NS_NODE, &cfg_nse_nsvci_cmd);
716 install_element(L_NS_NODE, &cfg_nse_remoteip_cmd);
717 install_element(L_NS_NODE, &cfg_nse_remoteport_cmd);
718 install_element(L_NS_NODE, &cfg_nse_fr_dlci_cmd);
719 install_element(L_NS_NODE, &cfg_nse_encaps_cmd);
720 install_element(L_NS_NODE, &cfg_nse_remoterole_cmd);
721 install_element(L_NS_NODE, &cfg_no_nse_cmd);
722 install_element(L_NS_NODE, &cfg_ns_timer_cmd);
723 install_element(L_NS_NODE, &cfg_nsip_local_ip_cmd);
724 install_element(L_NS_NODE, &cfg_nsip_local_port_cmd);
725 install_element(L_NS_NODE, &cfg_nsip_dscp_cmd);
726 install_element(L_NS_NODE, &cfg_nsip_res_block_unblock_cmd);
727 install_element(L_NS_NODE, &cfg_frgre_enable_cmd);
728 install_element(L_NS_NODE, &cfg_frgre_local_ip_cmd);
729
730 /* TODO: nsvc/nsei command to reset states or reset/block/unblock nsei/nsvcs */
731
732 return 0;
733}
734
735/*!
736 * \brief gprs_ns2_vty_create parse the vty tree into ns nodes
737 * It has to be in different steps to ensure the bind is created before creating VCs.
738 * \return 0 on success
739 */
740int gprs_ns2_vty_create() {
741 struct ns2_vty_vc *vtyvc;
742 struct gprs_ns2_vc_bind *bind;
743 struct gprs_ns2_nse *nse;
744 struct gprs_ns2_vc *nsvc;
745 struct osmo_sockaddr sockaddr;
746
747 if (!vty_nsi)
748 return -1;
749
750 /* create binds, only support a single bind. either FR or UDP */
751 if (priv.frgre) {
752 /* TODO not yet supported !*/
753 return -1;
754 } else {
755 /* UDP */
756 osmo_sockaddr_str_to_sockaddr(&priv.udp, &sockaddr.u.sas);
757 gprs_ns2_ip_bind(vty_nsi, &sockaddr, priv.dscp, &bind);
758 if (!bind) {
759 /* TODO: could not bind on the specific address */
760 return -1;
761 }
762 gprs_ns2_bind_set_mode(bind, priv.vc_mode);
763 }
764
765 /* create vcs */
766 llist_for_each_entry(vtyvc, &priv.vtyvc, list) {
767 if (strlen(vtyvc->remote.ip) == 0) {
768 /* Invalid IP for VC */
769 continue;
770 }
771
772 if (!vtyvc->remote.port) {
773 /* Invalid port for VC */
774 continue;
775 }
776
777 if (osmo_sockaddr_str_to_sockaddr(&vtyvc->remote, &sockaddr.u.sas)) {
778 /* Invalid sockaddr for VC */
779 continue;
780 }
781
782 nse = gprs_ns2_nse_by_nsei(vty_nsi, vtyvc->nsei);
783 if (!nse) {
784 nse = gprs_ns2_create_nse(vty_nsi, vtyvc->nsei);
785 if (!nse) {
786 /* Could not create NSE for VTY */
787 continue;
788 }
789 }
790 nse->persistent = true;
791
792 if (bind) {
793 nsvc = gprs_ns2_ip_connect(bind,
794 &sockaddr,
795 nse,
796 vtyvc->nsvci);
797 if (!nsvc) {
798 /* Could not create NSVC, connect failed */
799 continue;
800 }
801 nsvc->persistent = true;
802 }
803 }
804
805
806 return 0;
807}
808
809/*!
810 * \brief ns2_vty_bind_apply will be called when a new bind is created to apply vty settings
811 * \param bind
812 * \return
813 */
814void ns2_vty_bind_apply(struct gprs_ns2_vc_bind *bind)
815{
816 gprs_ns2_bind_set_mode(bind, priv.vc_mode);
817}
818
819/*!
820 * \brief ns2_vty_force_vc_mode force a mode and prevents the vty from overwriting it.
821 * \param force if true mode and reason will be set. false to allow modification via vty.
822 * \param mode
823 * \param reason A description shown to the user when a vty command wants to change the mode.
824 */
825void gprs_ns2_vty_force_vc_mode(bool force, enum gprs_ns2_vc_mode mode, char *reason)
826{
827 priv.force_vc_mode = force;
828
829 if (force) {
830 priv.vc_mode = mode;
831 priv.force_vc_mode_reason = reason;
832 }
833}