blob: fa9b97b867f12f63b19fc546c80bf810e9c1461c [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 Welte0bd8a4b2020-11-13 20:09:33 +0100129static struct ns2_vty_vc *vtyvc_alloc(uint16_t nsei, uint16_t nsvci)
130{
Alexander Couzens6a161492020-07-12 13:45:50 +0200131 struct ns2_vty_vc *vtyvc = talloc_zero(vty_nsi, struct ns2_vty_vc);
132 if (!vtyvc)
133 return vtyvc;
134
135 vtyvc->nsei = nsei;
Harald Welte0bd8a4b2020-11-13 20:09:33 +0100136 vtyvc->nsvci = nsvci;
Alexander Couzens6a161492020-07-12 13:45:50 +0200137
138 llist_add(&vtyvc->list, &priv.vtyvc);
139
140 return vtyvc;
141}
142
143static void ns2_vc_free(struct ns2_vty_vc *vtyvc) {
144 if (!vtyvc)
145 return;
146
147 llist_del(&vtyvc->list);
148 talloc_free(vtyvc);
149}
150
Harald Welte0bd8a4b2020-11-13 20:09:33 +0100151static struct ns2_vty_vc *vtyvc_by_nsei_nsvci(uint16_t nsei, uint16_t nsvci, bool alloc_missing)
152{
Alexander Couzens6a161492020-07-12 13:45:50 +0200153 struct ns2_vty_vc *vtyvc;
Alexander Couzens1f0625f2020-09-23 18:22:20 +0200154
Alexander Couzens6a161492020-07-12 13:45:50 +0200155 llist_for_each_entry(vtyvc, &priv.vtyvc, list) {
Harald Welte0bd8a4b2020-11-13 20:09:33 +0100156 if (vtyvc->nsei == nsei && vtyvc->nsvci == nsvci)
Alexander Couzens6a161492020-07-12 13:45:50 +0200157 return vtyvc;
158 }
159
Alexander Couzens1f0625f2020-09-23 18:22:20 +0200160 if (!alloc_missing)
161 return NULL;
Alexander Couzens6a161492020-07-12 13:45:50 +0200162
Harald Welte0bd8a4b2020-11-13 20:09:33 +0100163 vtyvc = vtyvc_alloc(nsei, nsvci);
Alexander Couzens1f0625f2020-09-23 18:22:20 +0200164 if (!vtyvc)
165 return vtyvc;
Alexander Couzens6a161492020-07-12 13:45:50 +0200166
Alexander Couzens1f0625f2020-09-23 18:22:20 +0200167 vtyvc->nsei = nsei;
168 return vtyvc;
Alexander Couzens6a161492020-07-12 13:45:50 +0200169}
170
171static int config_write_ns(struct vty *vty)
172{
173 struct ns2_vty_vc *vtyvc;
174 unsigned int i;
175 struct osmo_sockaddr_str sockstr;
176
177 vty_out(vty, "ns%s", VTY_NEWLINE);
178
179 /* global configuration must be written first, as some of it may be
180 * relevant when creating the NSE/NSVC later below */
181
182 vty_out(vty, " encapsulation framerelay-gre enabled %u%s",
183 priv.frgre ? 1 : 0, VTY_NEWLINE);
184
185 if (priv.frgre) {
186 if (strlen(priv.frgreaddr.ip)) {
187 vty_out(vty, " encapsulation framerelay-gre local-ip %s%s",
188 sockstr.ip, VTY_NEWLINE);
189 }
190 } else {
191 if (strlen(priv.udp.ip)) {
192 vty_out(vty, " encapsulation udp local-ip %s%s",
193 priv.udp.ip, VTY_NEWLINE);
194 }
195
196 if (priv.udp.port)
197 vty_out(vty, " encapsulation udp local-port %u%s",
198 priv.udp.port, VTY_NEWLINE);
199 }
200
201 if (priv.dscp)
202 vty_out(vty, " encapsulation udp dscp %d%s",
203 priv.dscp, VTY_NEWLINE);
204
205 vty_out(vty, " encapsulation udp use-reset-block-unblock %s%s",
206 priv.vc_mode == NS2_VC_MODE_BLOCKRESET ? "enabled" : "disabled", VTY_NEWLINE);
207
208 llist_for_each_entry(vtyvc, &priv.vtyvc, list) {
209 vty_out(vty, " nse %u nsvci %u%s",
210 vtyvc->nsei, vtyvc->nsvci, VTY_NEWLINE);
211
Harald Welte0bd8a4b2020-11-13 20:09:33 +0100212 vty_out(vty, " nse %u nsvci %u remote-role %s%s", vtyvc->nsei, vtyvc->nsvci,
213 vtyvc->remote_end_is_sgsn ? "sgsn" : "bss", VTY_NEWLINE);
Alexander Couzens6a161492020-07-12 13:45:50 +0200214
215 switch (vtyvc->ll) {
Alexander Couzens24a14ac2020-11-19 02:34:49 +0100216 case GPRS_NS2_LL_UDP:
Harald Welte0bd8a4b2020-11-13 20:09:33 +0100217 vty_out(vty, " nse %u nsvci %u encapsulation udp%s", vtyvc->nsei, vtyvc->nsvci,
Alexander Couzens6a161492020-07-12 13:45:50 +0200218 VTY_NEWLINE);
Harald Welte0bd8a4b2020-11-13 20:09:33 +0100219 vty_out(vty, " nse %u nsvci %u remote-ip %s%s", vtyvc->nsei, vtyvc->nsvci,
220 vtyvc->remote.ip, VTY_NEWLINE);
221 vty_out(vty, " nse %u nsvci %u remote-port %u%s", vtyvc->nsei, vtyvc->nsvci,
222 vtyvc->remote.port, 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 Welte0bd8a4b2020-11-13 20:09:33 +0100225 vty_out(vty, " nse %u nsvci %u encapsulation framerelay-gre%s", vtyvc->nsei,
226 vtyvc->nsvci, VTY_NEWLINE);
227 vty_out(vty, " nse %u nsvci %u remote-ip %s%s", vtyvc->nsei, vtyvc->nsvci,
228 vtyvc->remote.ip, VTY_NEWLINE);
229 vty_out(vty, " nse %u nsvci %u fr-dlci %u%s", vtyvc->nsei, vtyvc->nsvci,
230 vtyvc->frdlci, VTY_NEWLINE);
Alexander Couzens6a161492020-07-12 13:45:50 +0200231 break;
Alexander Couzens24a14ac2020-11-19 02:34:49 +0100232 case GPRS_NS2_LL_FR:
Harald Welte0bd8a4b2020-11-13 20:09:33 +0100233 vty_out(vty, " nse %u nsvci %u fr %s dlci %u%s", vtyvc->nsei, vtyvc->nsvci,
234 vtyvc->netif, vtyvc->frdlci, VTY_NEWLINE);
Alexander Couzens841817e2020-11-19 00:41:29 +0100235 break;
Alexander Couzens6a161492020-07-12 13:45:50 +0200236 default:
237 break;
238 }
239 }
240
241 for (i = 0; i < ARRAY_SIZE(vty_nsi->timeout); i++)
242 vty_out(vty, " timer %s %u%s",
243 get_value_string(gprs_ns_timer_strs, i),
244 vty_nsi->timeout[i], VTY_NEWLINE);
245
246 return CMD_SUCCESS;
247}
248
249DEFUN(cfg_ns, cfg_ns_cmd,
250 "ns",
251 "Configure the GPRS Network Service")
252{
253 vty->node = L_NS_NODE;
254 return CMD_SUCCESS;
255}
256
257static void dump_nsvc(struct vty *vty, struct gprs_ns2_vc *nsvc, bool stats)
258{
Daniel Willmanned1fa012020-11-06 15:40:27 +0100259 vty_out(vty, " %s%s", gprs_ns2_ll_str(nsvc), VTY_NEWLINE);
Alexander Couzens6a161492020-07-12 13:45:50 +0200260
261 if (stats) {
262 vty_out_rate_ctr_group(vty, " ", nsvc->ctrg);
263 vty_out_stat_item_group(vty, " ", nsvc->statg);
264 }
265}
266
267static void dump_nse(struct vty *vty, const struct gprs_ns2_nse *nse, bool stats, bool persistent_only)
268{
269 struct gprs_ns2_vc *nsvc;
270
271 vty_out(vty, "NSEI %5u%s",
272 nse->nsei, VTY_NEWLINE);
273
274 gprs_ns2_sns_dump_vty(vty, nse, stats);
275 llist_for_each_entry(nsvc, &nse->nsvc, list) {
276 if (persistent_only) {
277 if (nsvc->persistent)
278 dump_nsvc(vty, nsvc, stats);
279 } else {
280 dump_nsvc(vty, nsvc, stats);
281 }
282 }
283}
284
Alexander Couzens22f34712020-10-02 02:34:39 +0200285static void dump_bind(struct vty *vty, const struct gprs_ns2_vc_bind *bind, bool stats)
286{
287 if (bind->dump_vty)
288 bind->dump_vty(bind, vty, stats);
289}
290
Alexander Couzens6a161492020-07-12 13:45:50 +0200291static void dump_ns(struct vty *vty, const struct gprs_ns2_inst *nsi, bool stats, bool persistent_only)
292{
Alexander Couzens22f34712020-10-02 02:34:39 +0200293 struct gprs_ns2_vc_bind *bind;
Alexander Couzens6a161492020-07-12 13:45:50 +0200294 struct gprs_ns2_nse *nse;
295
Alexander Couzens22f34712020-10-02 02:34:39 +0200296 llist_for_each_entry(bind, &nsi->binding, list) {
297 dump_bind(vty, bind, stats);
298 }
299
Alexander Couzens6a161492020-07-12 13:45:50 +0200300 llist_for_each_entry(nse, &nsi->nse, list) {
301 dump_nse(vty, nse, stats, persistent_only);
Alexander Couzens6a161492020-07-12 13:45:50 +0200302 }
303
304}
305
306DEFUN(show_ns, show_ns_cmd, "show ns",
307 SHOW_STR "Display information about the NS protocol")
308{
309 dump_ns(vty, vty_nsi, false, false);
310 return CMD_SUCCESS;
311}
312
313DEFUN(show_ns_stats, show_ns_stats_cmd, "show ns stats",
314 SHOW_STR
315 "Display information about the NS protocol\n"
316 "Include statistics\n")
317{
318 dump_ns(vty, vty_nsi, true, false);
319 return CMD_SUCCESS;
320}
321
322DEFUN(show_ns_pers, show_ns_pers_cmd, "show ns persistent",
323 SHOW_STR
324 "Display information about the NS protocol\n"
325 "Show only persistent NS\n")
326{
327 dump_ns(vty, vty_nsi, true, true);
328 return CMD_SUCCESS;
329}
330
331DEFUN(show_nse, show_nse_cmd, "show ns (nsei|nsvc) <0-65535> [stats]",
332 SHOW_STR "Display information about the NS protocol\n"
333 "Select one NSE by its NSE Identifier\n"
334 "Select one NSE by its NS-VC Identifier\n"
335 "The Identifier of selected type\n"
336 "Include Statistics\n")
337{
338 struct gprs_ns2_inst *nsi = vty_nsi;
339 struct gprs_ns2_nse *nse;
340 struct gprs_ns2_vc *nsvc;
341 uint16_t id = atoi(argv[1]);
342 bool show_stats = false;
343
344 if (argc >= 3)
345 show_stats = true;
346
347 if (!strcmp(argv[0], "nsei")) {
348 nse = gprs_ns2_nse_by_nsei(nsi, id);
349 if (!nse) {
350 return CMD_WARNING;
351 }
352
353 dump_nse(vty, nse, show_stats, false);
354 } else {
355 nsvc = gprs_ns2_nsvc_by_nsvci(nsi, id);
356
357 if (!nsvc) {
358 vty_out(vty, "No such NS Entity%s", VTY_NEWLINE);
359 return CMD_WARNING;
360 }
361
362 dump_nsvc(vty, nsvc, show_stats);
363 }
364
365 return CMD_SUCCESS;
366}
367
Daniel Willmanndbab7142020-11-18 14:19:56 +0100368static int nsvc_force_unconf_cb(struct gprs_ns2_vc *nsvc, void *ctx)
369{
370 gprs_ns2_vc_force_unconfigured(nsvc);
371 return 0;
372}
373
374DEFUN_HIDDEN(nsvc_force_unconf, nsvc_force_unconf_cmd,
375 "nsvc nsei <0-65535> force-unconfigured",
376 "NS Virtual Connection\n"
377 "The NSEI\n"
378 "Reset the NSVCs back to initial state\n"
379 )
380{
381 struct gprs_ns2_inst *nsi = vty_nsi;
382 struct gprs_ns2_nse *nse;
383
384 uint16_t id = atoi(argv[0]);
385
386 nse = gprs_ns2_nse_by_nsei(nsi, id);
387 if (!nse) {
388 vty_out(vty, "Could not find NSE for NSEI %u%s", id, VTY_NEWLINE);
389 return CMD_WARNING;
390 }
391
392 /* Perform the operation for all nsvc */
393 gprs_ns2_nse_foreach_nsvc(nse, nsvc_force_unconf_cb, NULL);
394
395 return CMD_SUCCESS;
396}
397
Alexander Couzens6a161492020-07-12 13:45:50 +0200398#define NSE_CMD_STR "Persistent NS Entity\n" "NS Entity ID (NSEI)\n"
399
Alexander Couzens841817e2020-11-19 00:41:29 +0100400DEFUN(cfg_nse_fr, cfg_nse_fr_cmd,
401 "nse <0-65535> nsvci <0-65535> (fr|frnet) NETIF dlci <0-1023>",
402 NSE_CMD_STR
403 "NS Virtual Connection\n"
404 "NS Virtual Connection ID (NSVCI)\n"
405 "frame relay\n"
406 IFNAME_STR
407 "Data Link connection identifier\n"
408 "Data Link connection identifier\n"
409 )
410{
411 struct ns2_vty_vc *vtyvc;
412
413 uint16_t nsei = atoi(argv[0]);
414 uint16_t nsvci = atoi(argv[1]);
415 const char *role = argv[2];
416 const char *name = argv[3];
417 uint16_t dlci = atoi(argv[4]);
418
Harald Welte0bd8a4b2020-11-13 20:09:33 +0100419 vtyvc = vtyvc_by_nsei_nsvci(nsei, nsvci, true);
Alexander Couzens841817e2020-11-19 00:41:29 +0100420 if (!vtyvc) {
421 vty_out(vty, "Can not allocate space %s", VTY_NEWLINE);
422 return CMD_WARNING;
423 }
424
425 if (!strcmp(role, "fr"))
426 vtyvc->fr.role = FR_ROLE_USER_EQUIPMENT;
427 else if (!strcmp(role, "frnet"))
428 vtyvc->fr.role = FR_ROLE_NETWORK_EQUIPMENT;
429
430 osmo_strlcpy(vtyvc->netif, name, sizeof(vtyvc->netif));
431 vtyvc->frdlci = dlci;
432 vtyvc->nsvci = nsvci;
Alexander Couzens24a14ac2020-11-19 02:34:49 +0100433 vtyvc->ll = GPRS_NS2_LL_FR;
Alexander Couzens841817e2020-11-19 00:41:29 +0100434
435 return CMD_SUCCESS;
436}
437
Alexander Couzens6a161492020-07-12 13:45:50 +0200438DEFUN(cfg_nse_nsvc, cfg_nse_nsvci_cmd,
439 "nse <0-65535> nsvci <0-65535>",
440 NSE_CMD_STR
441 "NS Virtual Connection\n"
442 "NS Virtual Connection ID (NSVCI)\n"
443 )
444{
445 struct ns2_vty_vc *vtyvc;
446
447 uint16_t nsei = atoi(argv[0]);
448 uint16_t nsvci = atoi(argv[1]);
449
Harald Welte0bd8a4b2020-11-13 20:09:33 +0100450 vtyvc = vtyvc_by_nsei_nsvci(nsei, nsvci, true);
Alexander Couzens6a161492020-07-12 13:45:50 +0200451 if (!vtyvc) {
452 vty_out(vty, "Can not allocate space %s", VTY_NEWLINE);
453 return CMD_WARNING;
454 }
455
456 vtyvc->nsvci = nsvci;
457
458 return CMD_SUCCESS;
459}
460
461DEFUN(cfg_nse_remoteip, cfg_nse_remoteip_cmd,
Harald Welte0bd8a4b2020-11-13 20:09:33 +0100462 "nse <0-65535> nsvci <0-65535> remote-ip " VTY_IPV46_CMD,
Alexander Couzens6a161492020-07-12 13:45:50 +0200463 NSE_CMD_STR
Harald Welte0bd8a4b2020-11-13 20:09:33 +0100464 "NS Virtual Connection\n"
465 "NS Virtual Connection ID (NSVCI)\n"
Alexander Couzens6a161492020-07-12 13:45:50 +0200466 "Remote IP Address\n"
Alexander Couzensc82c40a2020-09-24 05:55:48 +0200467 "Remote IPv4 Address\n"
468 "Remote IPv6 Address\n")
Alexander Couzens6a161492020-07-12 13:45:50 +0200469{
470 uint16_t nsei = atoi(argv[0]);
Harald Welte0bd8a4b2020-11-13 20:09:33 +0100471 uint16_t nsvci = atoi(argv[1]);
Alexander Couzens6a161492020-07-12 13:45:50 +0200472 struct ns2_vty_vc *vtyvc;
473
Harald Welte0bd8a4b2020-11-13 20:09:33 +0100474 vtyvc = vtyvc_by_nsei_nsvci(nsei, nsvci, true);
Alexander Couzens6a161492020-07-12 13:45:50 +0200475 if (!vtyvc) {
476 vty_out(vty, "Can not allocate space %s", VTY_NEWLINE);
477 return CMD_WARNING;
478 }
479
Harald Welte0bd8a4b2020-11-13 20:09:33 +0100480 osmo_sockaddr_str_from_str2(&vtyvc->remote, argv[2]);
Alexander Couzens6a161492020-07-12 13:45:50 +0200481
482 return CMD_SUCCESS;
483}
484
485DEFUN(cfg_nse_remoteport, cfg_nse_remoteport_cmd,
Harald Welte0bd8a4b2020-11-13 20:09:33 +0100486 "nse <0-65535> nsvci <0-65535> remote-port <0-65535>",
Alexander Couzens6a161492020-07-12 13:45:50 +0200487 NSE_CMD_STR
Harald Welte0bd8a4b2020-11-13 20:09:33 +0100488 "NS Virtual Connection\n"
489 "NS Virtual Connection ID (NSVCI)\n"
Alexander Couzens6a161492020-07-12 13:45:50 +0200490 "Remote UDP Port\n"
491 "Remote UDP Port Number\n")
492{
493 uint16_t nsei = atoi(argv[0]);
Harald Welte0bd8a4b2020-11-13 20:09:33 +0100494 uint16_t nsvci = atoi(argv[1]);
495 uint16_t port = atoi(argv[2]);
Alexander Couzens6a161492020-07-12 13:45:50 +0200496 struct ns2_vty_vc *vtyvc;
497
Harald Welte0bd8a4b2020-11-13 20:09:33 +0100498 vtyvc = vtyvc_by_nsei_nsvci(nsei, nsvci, true);
Alexander Couzens6a161492020-07-12 13:45:50 +0200499 if (!vtyvc) {
500 vty_out(vty, "Can not allocate space %s", VTY_NEWLINE);
501 return CMD_WARNING;
502 }
503
504 vtyvc->remote.port = port;
505
506 return CMD_SUCCESS;
507}
508
509DEFUN(cfg_nse_fr_dlci, cfg_nse_fr_dlci_cmd,
Alexander Couzens841817e2020-11-19 00:41:29 +0100510 "nse <0-65535> nsvci <0-65535> fr-dlci <16-1007>",
Alexander Couzens6a161492020-07-12 13:45:50 +0200511 NSE_CMD_STR
Harald Welte0bd8a4b2020-11-13 20:09:33 +0100512 "NS Virtual Connection\n"
513 "NS Virtual Connection ID (NSVCI)\n"
Alexander Couzens6a161492020-07-12 13:45:50 +0200514 "Frame Relay DLCI\n"
515 "Frame Relay DLCI Number\n")
516{
517 uint16_t nsei = atoi(argv[0]);
Alexander Couzens841817e2020-11-19 00:41:29 +0100518 uint16_t nsvci = atoi(argv[1]);
519 uint16_t dlci = atoi(argv[2]);
Alexander Couzens6a161492020-07-12 13:45:50 +0200520 struct ns2_vty_vc *vtyvc;
521
Harald Welte0bd8a4b2020-11-13 20:09:33 +0100522 vtyvc = vtyvc_by_nsei_nsvci(nsei, nsvci, 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
Alexander Couzens24a14ac2020-11-19 02:34:49 +0100528 if (vtyvc->ll != GPRS_NS2_LL_FR_GRE) {
Alexander Couzens6a161492020-07-12 13:45:50 +0200529 vty_out(vty, "Warning: seting FR DLCI on non-FR NSE%s",
530 VTY_NEWLINE);
531 }
532
533 vtyvc->frdlci = dlci;
534
535 return CMD_SUCCESS;
536}
537
538DEFUN(cfg_nse_encaps, cfg_nse_encaps_cmd,
Harald Welte0bd8a4b2020-11-13 20:09:33 +0100539 "nse <0-65535> nsvci <0-65535> encapsulation (udp|framerelay-gre)",
Alexander Couzens6a161492020-07-12 13:45:50 +0200540 NSE_CMD_STR
Harald Welte0bd8a4b2020-11-13 20:09:33 +0100541 "NS Virtual Connection\n"
542 "NS Virtual Connection ID (NSVCI)\n"
Alexander Couzens6a161492020-07-12 13:45:50 +0200543 "Encapsulation for NS\n"
544 "UDP/IP Encapsulation\n" "Frame-Relay/GRE/IP Encapsulation\n")
545{
546 uint16_t nsei = atoi(argv[0]);
Harald Welte0bd8a4b2020-11-13 20:09:33 +0100547 uint16_t nsvci = atoi(argv[1]);
Alexander Couzens6a161492020-07-12 13:45:50 +0200548 struct ns2_vty_vc *vtyvc;
549
Harald Welte0bd8a4b2020-11-13 20:09:33 +0100550 vtyvc = vtyvc_by_nsei_nsvci(nsei, nsvci, true);
Alexander Couzens6a161492020-07-12 13:45:50 +0200551 if (!vtyvc) {
552 vty_out(vty, "Can not allocate space %s", VTY_NEWLINE);
553 return CMD_WARNING;
554 }
555
Harald Welte0bd8a4b2020-11-13 20:09:33 +0100556 if (!strcmp(argv[2], "udp"))
Alexander Couzens24a14ac2020-11-19 02:34:49 +0100557 vtyvc->ll = GPRS_NS2_LL_UDP;
Alexander Couzens6a161492020-07-12 13:45:50 +0200558 else
Alexander Couzens24a14ac2020-11-19 02:34:49 +0100559 vtyvc->ll = GPRS_NS2_LL_FR_GRE;
Alexander Couzens6a161492020-07-12 13:45:50 +0200560
561 return CMD_SUCCESS;
562}
563
564DEFUN(cfg_nse_remoterole, cfg_nse_remoterole_cmd,
Harald Welte0bd8a4b2020-11-13 20:09:33 +0100565 "nse <0-65535> nsvci <0-65535> remote-role (sgsn|bss)",
Alexander Couzens6a161492020-07-12 13:45:50 +0200566 NSE_CMD_STR
Harald Welte0bd8a4b2020-11-13 20:09:33 +0100567 "NS Virtual Connection\n"
568 "NS Virtual Connection ID (NSVCI)\n"
Alexander Couzens6a161492020-07-12 13:45:50 +0200569 "Remote NSE Role\n"
570 "Remote Peer is SGSN\n"
571 "Remote Peer is BSS\n")
572{
573 uint16_t nsei = atoi(argv[0]);
Harald Welte0bd8a4b2020-11-13 20:09:33 +0100574 uint16_t nsvci = atoi(argv[1]);
Alexander Couzens6a161492020-07-12 13:45:50 +0200575 struct ns2_vty_vc *vtyvc;
576
Harald Welte0bd8a4b2020-11-13 20:09:33 +0100577 vtyvc = vtyvc_by_nsei_nsvci(nsei, nsvci, true);
Alexander Couzens6a161492020-07-12 13:45:50 +0200578 if (!vtyvc) {
579 vty_out(vty, "Can not allocate space %s", VTY_NEWLINE);
580 return CMD_WARNING;
581 }
582
Harald Welte0bd8a4b2020-11-13 20:09:33 +0100583 if (!strcmp(argv[2], "sgsn"))
Alexander Couzens6a161492020-07-12 13:45:50 +0200584 vtyvc->remote_end_is_sgsn = 1;
585 else
586 vtyvc->remote_end_is_sgsn = 0;
587
588 return CMD_SUCCESS;
589}
590
591DEFUN(cfg_no_nse, cfg_no_nse_cmd,
Harald Welte0bd8a4b2020-11-13 20:09:33 +0100592 "no nse <0-65535> nsvci <0-65535>",
Alexander Couzens6a161492020-07-12 13:45:50 +0200593 "Delete Persistent NS Entity\n"
594 "Delete " NSE_CMD_STR)
595{
596 uint16_t nsei = atoi(argv[0]);
Harald Welte0bd8a4b2020-11-13 20:09:33 +0100597 uint16_t nsvci = atoi(argv[1]);
Alexander Couzens6a161492020-07-12 13:45:50 +0200598 struct ns2_vty_vc *vtyvc;
599
Harald Welte0bd8a4b2020-11-13 20:09:33 +0100600 vtyvc = vtyvc_by_nsei_nsvci(nsei, nsvci, false);
Alexander Couzens6a161492020-07-12 13:45:50 +0200601 if (!vtyvc) {
602 vty_out(vty, "The NSE %d does not exists.%s", nsei, VTY_NEWLINE);
603 return CMD_WARNING;
604 }
605
606 ns2_vc_free(vtyvc);
607
608 return CMD_SUCCESS;
609}
610
611DEFUN(cfg_ns_timer, cfg_ns_timer_cmd,
612 "timer " NS_TIMERS " <0-65535>",
613 "Network Service Timer\n"
614 NS_TIMERS_HELP "Timer Value\n")
615{
616 int idx = get_string_value(gprs_ns_timer_strs, argv[0]);
617 int val = atoi(argv[1]);
618
619 if (idx < 0 || idx >= ARRAY_SIZE(vty_nsi->timeout))
620 return CMD_WARNING;
621
622 vty_nsi->timeout[idx] = val;
623
624 return CMD_SUCCESS;
625}
626
627#define ENCAPS_STR "NS encapsulation options\n"
628
629DEFUN(cfg_nsip_local_ip, cfg_nsip_local_ip_cmd,
630 "encapsulation udp local-ip " VTY_IPV46_CMD,
631 ENCAPS_STR "NS over UDP Encapsulation\n"
632 "Set the IP address on which we listen for NS/UDP\n"
Alexander Couzensc82c40a2020-09-24 05:55:48 +0200633 "IPv4 Address\n"
634 "IPv6 Address\n")
Alexander Couzens6a161492020-07-12 13:45:50 +0200635{
636 osmo_sockaddr_str_from_str2(&priv.udp, argv[0]);
637
638 return CMD_SUCCESS;
639}
640
641DEFUN(cfg_nsip_local_port, cfg_nsip_local_port_cmd,
642 "encapsulation udp local-port <0-65535>",
643 ENCAPS_STR "NS over UDP Encapsulation\n"
644 "Set the UDP port on which we listen for NS/UDP\n"
645 "UDP port number\n")
646{
647 unsigned int port = atoi(argv[0]);
648
649 priv.udp.port = port;
650
651 return CMD_SUCCESS;
652}
653
654DEFUN(cfg_nsip_dscp, cfg_nsip_dscp_cmd,
655 "encapsulation udp dscp <0-255>",
656 ENCAPS_STR "NS over UDP Encapsulation\n"
657 "Set DSCP/TOS on the UDP socket\n" "DSCP Value\n")
658{
659 int dscp = atoi(argv[0]);
660 struct gprs_ns2_vc_bind *bind;
661
662 priv.dscp = dscp;
663
664 llist_for_each_entry(bind, &vty_nsi->binding, list) {
665 if (gprs_ns2_is_ip_bind(bind))
666 gprs_ns2_ip_bind_set_dscp(bind, dscp);
667 }
668
669 return CMD_SUCCESS;
670}
671
672DEFUN(cfg_nsip_res_block_unblock, cfg_nsip_res_block_unblock_cmd,
673 "encapsulation udp use-reset-block-unblock (enabled|disabled)",
674 ENCAPS_STR "NS over UDP Encapsulation\n"
675 "Use NS-{RESET,BLOCK,UNBLOCK} procedures in violation of 3GPP TS 48.016\n"
676 "Enable NS-{RESET,BLOCK,UNBLOCK}\n"
677 "Disable NS-{RESET,BLOCK,UNBLOCK}\n")
678{
679 enum gprs_ns2_vc_mode vc_mode;
680 struct gprs_ns2_vc_bind *bind;
681
682 if (!strcmp(argv[0], "enabled"))
683 vc_mode = NS2_VC_MODE_BLOCKRESET;
684 else
685 vc_mode = NS2_VC_MODE_ALIVE;
686
687 if (priv.force_vc_mode) {
688 if (priv.vc_mode != vc_mode)
689 {
690 vty_out(vty, "Ignoring use-reset-block because it's already set by %s.%s",
691 priv.force_vc_mode_reason, VTY_NEWLINE);
692 return CMD_WARNING;
693 }
694
695 return CMD_SUCCESS;
696 }
697
698 priv.vc_mode = vc_mode;
699
700 llist_for_each_entry(bind, &vty_nsi->binding, list) {
701 gprs_ns2_bind_set_mode(bind, priv.vc_mode);
702 }
703
704 return CMD_SUCCESS;
705}
706
707DEFUN(cfg_frgre_local_ip, cfg_frgre_local_ip_cmd,
708 "encapsulation framerelay-gre local-ip " VTY_IPV46_CMD,
709 ENCAPS_STR "NS over Frame Relay over GRE Encapsulation\n"
710 "Set the IP address on which we listen for NS/FR/GRE\n"
Alexander Couzensc82c40a2020-09-24 05:55:48 +0200711 "IPv4 Address\n"
712 "IPv6 Address\n")
Alexander Couzens6a161492020-07-12 13:45:50 +0200713{
714 osmo_sockaddr_str_from_str2(&priv.frgreaddr, argv[0]);
715
716 return CMD_SUCCESS;
717}
718
719DEFUN(cfg_frgre_enable, cfg_frgre_enable_cmd,
720 "encapsulation framerelay-gre enabled (1|0)",
721 ENCAPS_STR "NS over Frame Relay over GRE Encapsulation\n"
722 "Enable or disable Frame Relay over GRE\n"
723 "Enable\n" "Disable\n")
724{
725 int enabled = atoi(argv[0]);
726
727 priv.frgre = enabled;
728
729 return CMD_SUCCESS;
730}
731
732/* TODO: allow vty to reset/block/unblock nsvc/nsei */
733
734/* TODO: add filter for NSEI as ns1 code does */
735/* TODO: add filter for single connection by description */
736DEFUN(logging_fltr_nsvc,
737 logging_fltr_nsvc_cmd,
738 "logging filter nsvc nsvci <0-65535>",
739 LOGGING_STR FILTER_STR
740 "Filter based on NS Virtual Connection\n"
741 "Identify NS-VC by NSVCI\n"
742 "Numeric identifier\n")
743{
744 struct log_target *tgt;
745 struct gprs_ns2_vc *nsvc;
746 uint16_t id = atoi(argv[1]);
747
748 log_tgt_mutex_lock();
749 tgt = osmo_log_vty2tgt(vty);
750 if (!tgt) {
751 log_tgt_mutex_unlock();
752 return CMD_WARNING;
753 }
754
755 nsvc = gprs_ns2_nsvc_by_nsvci(vty_nsi, id);
756 if (!nsvc) {
757 vty_out(vty, "No NS-VC by that identifier%s", VTY_NEWLINE);
758 log_tgt_mutex_unlock();
759 return CMD_WARNING;
760 }
761
762 log_set_nsvc_filter(tgt, nsvc);
763 log_tgt_mutex_unlock();
764 return CMD_SUCCESS;
765}
766
Alexander Couzens1fac6f72020-10-01 19:08:38 +0200767/**
768 * gprs_ns2_vty_init initialize the vty
769 * \param[inout] nsi
770 * \param[in] default_bind set the default address to bind to. Can be NULL.
771 * \return 0 on success
772 */
Vadim Yanitskiya07f25e2020-10-09 21:47:01 +0700773int gprs_ns2_vty_init(struct gprs_ns2_inst *nsi,
774 const struct osmo_sockaddr_str *default_bind)
Alexander Couzens6a161492020-07-12 13:45:50 +0200775{
776 static bool vty_elements_installed = false;
777
778 vty_nsi = nsi;
779 memset(&priv, 0, sizeof(struct ns2_vty_priv));
780 INIT_LLIST_HEAD(&priv.vtyvc);
781 priv.vc_mode = NS2_VC_MODE_BLOCKRESET;
Alexander Couzens1fac6f72020-10-01 19:08:38 +0200782 if (default_bind)
783 memcpy(&priv.udp, default_bind, sizeof(*default_bind));
Alexander Couzens6a161492020-07-12 13:45:50 +0200784
785 /* Regression test code may call this function repeatedly, so make sure
786 * that VTY elements are not duplicated, which would assert. */
787 if (vty_elements_installed)
788 return 0;
789 vty_elements_installed = true;
790
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +0700791 install_lib_element_ve(&show_ns_cmd);
792 install_lib_element_ve(&show_ns_stats_cmd);
793 install_lib_element_ve(&show_ns_pers_cmd);
794 install_lib_element_ve(&show_nse_cmd);
795 install_lib_element_ve(&logging_fltr_nsvc_cmd);
Alexander Couzens6a161492020-07-12 13:45:50 +0200796
Daniel Willmanndbab7142020-11-18 14:19:56 +0100797 install_lib_element(ENABLE_NODE, &nsvc_force_unconf_cmd);
798
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +0700799 install_lib_element(CFG_LOG_NODE, &logging_fltr_nsvc_cmd);
Alexander Couzens6a161492020-07-12 13:45:50 +0200800
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +0700801 install_lib_element(CONFIG_NODE, &cfg_ns_cmd);
Alexander Couzens6a161492020-07-12 13:45:50 +0200802 install_node(&ns_node, config_write_ns);
Alexander Couzens841817e2020-11-19 00:41:29 +0100803 install_lib_element(L_NS_NODE, &cfg_nse_fr_cmd);
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +0700804 install_lib_element(L_NS_NODE, &cfg_nse_nsvci_cmd);
805 install_lib_element(L_NS_NODE, &cfg_nse_remoteip_cmd);
806 install_lib_element(L_NS_NODE, &cfg_nse_remoteport_cmd);
807 install_lib_element(L_NS_NODE, &cfg_nse_fr_dlci_cmd);
808 install_lib_element(L_NS_NODE, &cfg_nse_encaps_cmd);
809 install_lib_element(L_NS_NODE, &cfg_nse_remoterole_cmd);
810 install_lib_element(L_NS_NODE, &cfg_no_nse_cmd);
811 install_lib_element(L_NS_NODE, &cfg_ns_timer_cmd);
812 install_lib_element(L_NS_NODE, &cfg_nsip_local_ip_cmd);
813 install_lib_element(L_NS_NODE, &cfg_nsip_local_port_cmd);
814 install_lib_element(L_NS_NODE, &cfg_nsip_dscp_cmd);
815 install_lib_element(L_NS_NODE, &cfg_nsip_res_block_unblock_cmd);
816 install_lib_element(L_NS_NODE, &cfg_frgre_enable_cmd);
817 install_lib_element(L_NS_NODE, &cfg_frgre_local_ip_cmd);
Alexander Couzens6a161492020-07-12 13:45:50 +0200818
819 /* TODO: nsvc/nsei command to reset states or reset/block/unblock nsei/nsvcs */
820
821 return 0;
822}
823
824/*!
825 * \brief gprs_ns2_vty_create parse the vty tree into ns nodes
826 * It has to be in different steps to ensure the bind is created before creating VCs.
827 * \return 0 on success
828 */
829int gprs_ns2_vty_create() {
830 struct ns2_vty_vc *vtyvc;
Alexander Couzens841817e2020-11-19 00:41:29 +0100831 struct gprs_ns2_vc_bind *bind, *fr;
Alexander Couzens6a161492020-07-12 13:45:50 +0200832 struct gprs_ns2_nse *nse;
833 struct gprs_ns2_vc *nsvc;
834 struct osmo_sockaddr sockaddr;
Alexander Couzens841817e2020-11-19 00:41:29 +0100835 int rc = 0;
Alexander Couzens6a161492020-07-12 13:45:50 +0200836
837 if (!vty_nsi)
838 return -1;
839
840 /* create binds, only support a single bind. either FR or UDP */
841 if (priv.frgre) {
842 /* TODO not yet supported !*/
843 return -1;
844 } else {
845 /* UDP */
846 osmo_sockaddr_str_to_sockaddr(&priv.udp, &sockaddr.u.sas);
Alexander Couzens477ffb02020-10-01 19:05:28 +0200847 if (gprs_ns2_ip_bind(vty_nsi, &sockaddr, priv.dscp, &bind)) {
Alexander Couzens6a161492020-07-12 13:45:50 +0200848 /* TODO: could not bind on the specific address */
849 return -1;
850 }
851 gprs_ns2_bind_set_mode(bind, priv.vc_mode);
852 }
853
854 /* create vcs */
855 llist_for_each_entry(vtyvc, &priv.vtyvc, list) {
Alexander Couzens841817e2020-11-19 00:41:29 +0100856 /* validate settings */
857 switch (vtyvc->ll) {
Alexander Couzens24a14ac2020-11-19 02:34:49 +0100858 case GPRS_NS2_LL_UDP:
Alexander Couzens841817e2020-11-19 00:41:29 +0100859 if (strlen(vtyvc->remote.ip) == 0) {
860 /* Invalid IP for VC */
861 continue;
862 }
Alexander Couzens6a161492020-07-12 13:45:50 +0200863
Alexander Couzens841817e2020-11-19 00:41:29 +0100864 if (!vtyvc->remote.port) {
865 /* Invalid port for VC */
866 continue;
867 }
Alexander Couzens6a161492020-07-12 13:45:50 +0200868
Alexander Couzens841817e2020-11-19 00:41:29 +0100869 if (osmo_sockaddr_str_to_sockaddr(&vtyvc->remote, &sockaddr.u.sas)) {
870 /* Invalid sockaddr for VC */
871 continue;
872 }
873 break;
Alexander Couzens24a14ac2020-11-19 02:34:49 +0100874 case GPRS_NS2_LL_FR:
Alexander Couzens841817e2020-11-19 00:41:29 +0100875 break;
Alexander Couzens24a14ac2020-11-19 02:34:49 +0100876 case GPRS_NS2_LL_FR_GRE:
877 case GPRS_NS2_LL_E1:
Alexander Couzens6a161492020-07-12 13:45:50 +0200878 continue;
879 }
880
881 nse = gprs_ns2_nse_by_nsei(vty_nsi, vtyvc->nsei);
882 if (!nse) {
883 nse = gprs_ns2_create_nse(vty_nsi, vtyvc->nsei);
884 if (!nse) {
885 /* Could not create NSE for VTY */
886 continue;
887 }
888 }
889 nse->persistent = true;
890
Alexander Couzens841817e2020-11-19 00:41:29 +0100891 switch (vtyvc->ll) {
Alexander Couzens24a14ac2020-11-19 02:34:49 +0100892 case GPRS_NS2_LL_UDP:
Alexander Couzens841817e2020-11-19 00:41:29 +0100893 nsvc = gprs_ns2_ip_connect(bind,
894 &sockaddr,
895 nse,
896 vtyvc->nsvci);
897 if (!nsvc) {
898 /* Could not create NSVC, connect failed */
899 continue;
900 }
901 nsvc->persistent = true;
902 break;
Alexander Couzens24a14ac2020-11-19 02:34:49 +0100903 case GPRS_NS2_LL_FR: {
Alexander Couzens841817e2020-11-19 00:41:29 +0100904 if (vty_fr_network == NULL) {
905 /* TODO: add a switch for BSS/SGSN/gbproxy */
906 vty_fr_network = osmo_fr_network_alloc(vty_nsi);
907 }
908 fr = gprs_ns2_fr_bind_by_netif(
909 vty_nsi,
910 vtyvc->netif);
911 if (!fr) {
912 rc = gprs_ns2_fr_bind(vty_nsi, vtyvc->netif, vty_fr_network, vtyvc->fr.role, &fr);
913 if (rc < 0) {
914 LOGP(DLNS, LOGL_ERROR, "Can not create fr bind on device %s err: %d\n", vtyvc->netif, rc);
915 return rc;
916 }
917 }
918
919 nsvc = gprs_ns2_fr_connect(fr, vtyvc->nsei, vtyvc->nsvci, vtyvc->frdlci);
920 if (!nsvc) {
921 /* Could not create NSVC, connect failed */
922 continue;
923 }
924 nsvc->persistent = true;
925 break;
926 }
Alexander Couzens24a14ac2020-11-19 02:34:49 +0100927 case GPRS_NS2_LL_FR_GRE:
928 case GPRS_NS2_LL_E1:
Alexander Couzensd745a0e2020-10-07 00:50:00 +0200929 continue;
Alexander Couzens6a161492020-07-12 13:45:50 +0200930 }
931 }
932
933
934 return 0;
935}
936
937/*!
938 * \brief ns2_vty_bind_apply will be called when a new bind is created to apply vty settings
939 * \param bind
940 * \return
941 */
942void ns2_vty_bind_apply(struct gprs_ns2_vc_bind *bind)
943{
944 gprs_ns2_bind_set_mode(bind, priv.vc_mode);
945}
946
947/*!
948 * \brief ns2_vty_force_vc_mode force a mode and prevents the vty from overwriting it.
949 * \param force if true mode and reason will be set. false to allow modification via vty.
950 * \param mode
951 * \param reason A description shown to the user when a vty command wants to change the mode.
952 */
Daniel Willmann4fb27a82020-09-25 15:39:46 +0200953void gprs_ns2_vty_force_vc_mode(bool force, enum gprs_ns2_vc_mode mode, const char *reason)
Alexander Couzens6a161492020-07-12 13:45:50 +0200954{
955 priv.force_vc_mode = force;
956
957 if (force) {
958 priv.vc_mode = mode;
959 priv.force_vc_mode_reason = reason;
960 }
961}