blob: 8b8a99988c0996e4f28b84a64f46775248e9cbf4 [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
Daniel Willmanncb3e9b52020-12-02 15:50:22 +010057#define SHOW_NS_STR "Display information about the NS protocol\n"
58
Alexander Couzens6a161492020-07-12 13:45:50 +020059struct ns2_vty_priv {
60 /* global listen */
61 struct osmo_sockaddr_str udp;
62 struct osmo_sockaddr_str frgreaddr;
63 int dscp;
64 enum gprs_ns2_vc_mode vc_mode;
65 /* force vc mode if another configuration forces
66 * the vc mode. E.g. SNS configuration */
67 bool force_vc_mode;
Daniel Willmann4fb27a82020-09-25 15:39:46 +020068 const char *force_vc_mode_reason;
Alexander Couzens6a161492020-07-12 13:45:50 +020069 bool frgre;
70
71 struct llist_head vtyvc;
72};
73
74struct ns2_vty_vc {
75 struct llist_head list;
76
77 struct osmo_sockaddr_str remote;
Alexander Couzens24a14ac2020-11-19 02:34:49 +010078 enum gprs_ns2_ll ll;
Alexander Couzens6a161492020-07-12 13:45:50 +020079
80 /* old vty code doesnt support multiple NSVCI per NSEI */
81 uint16_t nsei;
82 uint16_t nsvci;
83 uint16_t frdlci;
84
Alexander Couzens841817e2020-11-19 00:41:29 +010085 struct {
86 enum osmo_fr_role role;
87 } fr;
88
89 char netif[IF_NAMESIZE];
90
Alexander Couzens6a161492020-07-12 13:45:50 +020091 bool remote_end_is_sgsn;
92 bool configured;
93};
94
95static struct gprs_ns2_inst *vty_nsi = NULL;
96static struct ns2_vty_priv priv;
Alexander Couzens841817e2020-11-19 00:41:29 +010097static struct osmo_fr_network *vty_fr_network = NULL;
Alexander Couzens6a161492020-07-12 13:45:50 +020098
99/* FIXME: this should go to some common file as it is copied
100 * in vty_interface.c of the BSC */
101static const struct value_string gprs_ns_timer_strs[] = {
102 { 0, "tns-block" },
103 { 1, "tns-block-retries" },
104 { 2, "tns-reset" },
105 { 3, "tns-reset-retries" },
106 { 4, "tns-test" },
107 { 5, "tns-alive" },
108 { 6, "tns-alive-retries" },
109 { 7, "tsns-prov" },
110 { 0, NULL }
111};
112
113static void log_set_nsvc_filter(struct log_target *target,
114 struct gprs_ns2_vc *nsvc)
115{
116 if (nsvc) {
117 target->filter_map |= (1 << LOG_FLT_GB_NSVC);
118 target->filter_data[LOG_FLT_GB_NSVC] = nsvc;
119 } else if (target->filter_data[LOG_FLT_GB_NSVC]) {
120 target->filter_map = ~(1 << LOG_FLT_GB_NSVC);
121 target->filter_data[LOG_FLT_GB_NSVC] = NULL;
122 }
123}
124
125static struct cmd_node ns_node = {
126 L_NS_NODE,
127 "%s(config-ns)# ",
128 1,
129};
130
Harald Welte6d4db232020-11-25 19:33:42 +0100131static struct ns2_vty_vc *vtyvc_alloc(uint16_t nsei) {
Alexander Couzens6a161492020-07-12 13:45:50 +0200132 struct ns2_vty_vc *vtyvc = talloc_zero(vty_nsi, struct ns2_vty_vc);
133 if (!vtyvc)
134 return vtyvc;
135
136 vtyvc->nsei = nsei;
137
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 Welte6d4db232020-11-25 19:33:42 +0100151static struct ns2_vty_vc *vtyvc_by_nsei(uint16_t nsei, bool alloc_missing) {
Alexander Couzens6a161492020-07-12 13:45:50 +0200152 struct ns2_vty_vc *vtyvc;
Alexander Couzens1f0625f2020-09-23 18:22:20 +0200153
Alexander Couzens6a161492020-07-12 13:45:50 +0200154 llist_for_each_entry(vtyvc, &priv.vtyvc, list) {
Harald Welte6d4db232020-11-25 19:33:42 +0100155 if (vtyvc->nsei == nsei)
Alexander Couzens6a161492020-07-12 13:45:50 +0200156 return vtyvc;
157 }
158
Alexander Couzens1f0625f2020-09-23 18:22:20 +0200159 if (!alloc_missing)
160 return NULL;
Alexander Couzens6a161492020-07-12 13:45:50 +0200161
Harald Welte6d4db232020-11-25 19:33:42 +0100162 vtyvc = vtyvc_alloc(nsei);
Alexander Couzens1f0625f2020-09-23 18:22:20 +0200163 if (!vtyvc)
164 return vtyvc;
Alexander Couzens6a161492020-07-12 13:45:50 +0200165
Alexander Couzens1f0625f2020-09-23 18:22:20 +0200166 vtyvc->nsei = nsei;
167 return vtyvc;
Alexander Couzens6a161492020-07-12 13:45:50 +0200168}
169
170static int config_write_ns(struct vty *vty)
171{
172 struct ns2_vty_vc *vtyvc;
173 unsigned int i;
174 struct osmo_sockaddr_str sockstr;
175
176 vty_out(vty, "ns%s", VTY_NEWLINE);
177
178 /* global configuration must be written first, as some of it may be
179 * relevant when creating the NSE/NSVC later below */
180
181 vty_out(vty, " encapsulation framerelay-gre enabled %u%s",
182 priv.frgre ? 1 : 0, VTY_NEWLINE);
183
184 if (priv.frgre) {
185 if (strlen(priv.frgreaddr.ip)) {
186 vty_out(vty, " encapsulation framerelay-gre local-ip %s%s",
187 sockstr.ip, VTY_NEWLINE);
188 }
189 } else {
190 if (strlen(priv.udp.ip)) {
191 vty_out(vty, " encapsulation udp local-ip %s%s",
192 priv.udp.ip, VTY_NEWLINE);
193 }
194
195 if (priv.udp.port)
196 vty_out(vty, " encapsulation udp local-port %u%s",
197 priv.udp.port, VTY_NEWLINE);
198 }
199
200 if (priv.dscp)
201 vty_out(vty, " encapsulation udp dscp %d%s",
202 priv.dscp, VTY_NEWLINE);
203
204 vty_out(vty, " encapsulation udp use-reset-block-unblock %s%s",
205 priv.vc_mode == NS2_VC_MODE_BLOCKRESET ? "enabled" : "disabled", VTY_NEWLINE);
206
207 llist_for_each_entry(vtyvc, &priv.vtyvc, list) {
208 vty_out(vty, " nse %u nsvci %u%s",
209 vtyvc->nsei, vtyvc->nsvci, VTY_NEWLINE);
210
Harald Welte6d4db232020-11-25 19:33:42 +0100211 vty_out(vty, " nse %u remote-role %s%s",
212 vtyvc->nsei, vtyvc->remote_end_is_sgsn ? "sgsn" : "bss",
213 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 Welte6d4db232020-11-25 19:33:42 +0100217 vty_out(vty, " nse %u encapsulation udp%s", vtyvc->nsei, VTY_NEWLINE);
218 vty_out(vty, " nse %u remote-ip %s%s",
219 vtyvc->nsei,
220 vtyvc->remote.ip,
Alexander Couzens6a161492020-07-12 13:45:50 +0200221 VTY_NEWLINE);
Harald Welte6d4db232020-11-25 19:33:42 +0100222 vty_out(vty, " nse %u remote-port %u%s",
223 vtyvc->nsei, vtyvc->remote.port,
224 VTY_NEWLINE);
Alexander Couzens6a161492020-07-12 13:45:50 +0200225 break;
Alexander Couzens24a14ac2020-11-19 02:34:49 +0100226 case GPRS_NS2_LL_FR_GRE:
Harald Welte6d4db232020-11-25 19:33:42 +0100227 vty_out(vty, " nse %u encapsulation framerelay-gre%s",
228 vtyvc->nsei, VTY_NEWLINE);
229 vty_out(vty, " nse %u remote-ip %s%s",
230 vtyvc->nsei,
231 vtyvc->remote.ip,
232 VTY_NEWLINE);
233 vty_out(vty, " nse %u fr-dlci %u%s",
234 vtyvc->nsei, vtyvc->frdlci,
235 VTY_NEWLINE);
Alexander Couzens6a161492020-07-12 13:45:50 +0200236 break;
Alexander Couzens24a14ac2020-11-19 02:34:49 +0100237 case GPRS_NS2_LL_FR:
Harald Welte6d4db232020-11-25 19:33:42 +0100238 vty_out(vty, " nse %u fr %s dlci %u%s",
239 vtyvc->nsei, vtyvc->netif, vtyvc->frdlci,
240 VTY_NEWLINE);
Alexander Couzens841817e2020-11-19 00:41:29 +0100241 break;
Alexander Couzens6a161492020-07-12 13:45:50 +0200242 default:
243 break;
244 }
245 }
246
247 for (i = 0; i < ARRAY_SIZE(vty_nsi->timeout); i++)
248 vty_out(vty, " timer %s %u%s",
249 get_value_string(gprs_ns_timer_strs, i),
250 vty_nsi->timeout[i], VTY_NEWLINE);
251
252 return CMD_SUCCESS;
253}
254
255DEFUN(cfg_ns, cfg_ns_cmd,
256 "ns",
257 "Configure the GPRS Network Service")
258{
259 vty->node = L_NS_NODE;
260 return CMD_SUCCESS;
261}
262
263static void dump_nsvc(struct vty *vty, struct gprs_ns2_vc *nsvc, bool stats)
264{
Harald Weltedc2d0802020-12-01 18:17:28 +0100265 char nsvci_str[32];
266
267 if (nsvc->nsvci_is_valid)
268 snprintf(nsvci_str, sizeof(nsvci_str), "%05u", nsvc->nsvci);
269 else
270 snprintf(nsvci_str, sizeof(nsvci_str), "none");
271
272 vty_out(vty, " NSVCI %s: %s %s data_weight=%u sig_weight=%u %s%s", nsvci_str,
273 osmo_fsm_inst_state_name(nsvc->fi),
274 nsvc->persistent ? "PERSIST" : "DYNAMIC",
275 nsvc->data_weight, nsvc->sig_weight,
276 gprs_ns2_ll_str(nsvc), VTY_NEWLINE);
Alexander Couzens6a161492020-07-12 13:45:50 +0200277
278 if (stats) {
Harald Welte7aa60992020-12-01 17:53:17 +0100279 vty_out_rate_ctr_group(vty, " ", nsvc->ctrg);
280 vty_out_stat_item_group(vty, " ", nsvc->statg);
Alexander Couzens6a161492020-07-12 13:45:50 +0200281 }
282}
283
284static void dump_nse(struct vty *vty, const struct gprs_ns2_nse *nse, bool stats, bool persistent_only)
285{
286 struct gprs_ns2_vc *nsvc;
287
Harald Welte0ff12ad2020-12-01 17:51:07 +0100288 vty_out(vty, "NSEI %05u: %s, %s%s", nse->nsei, gprs_ns2_lltype_str(nse->ll),
289 nse->alive ? "ALIVE" : "DEAD", VTY_NEWLINE);
Alexander Couzens6a161492020-07-12 13:45:50 +0200290
291 gprs_ns2_sns_dump_vty(vty, nse, stats);
292 llist_for_each_entry(nsvc, &nse->nsvc, list) {
293 if (persistent_only) {
294 if (nsvc->persistent)
295 dump_nsvc(vty, nsvc, stats);
296 } else {
297 dump_nsvc(vty, nsvc, stats);
298 }
299 }
300}
301
Alexander Couzens22f34712020-10-02 02:34:39 +0200302static void dump_bind(struct vty *vty, const struct gprs_ns2_vc_bind *bind, bool stats)
303{
304 if (bind->dump_vty)
305 bind->dump_vty(bind, vty, stats);
306}
307
Harald Welte2fce19a2020-12-01 17:52:55 +0100308static void dump_ns_bind(struct vty *vty, const struct gprs_ns2_inst *nsi, bool stats)
Alexander Couzens6a161492020-07-12 13:45:50 +0200309{
Alexander Couzens22f34712020-10-02 02:34:39 +0200310 struct gprs_ns2_vc_bind *bind;
Alexander Couzens6a161492020-07-12 13:45:50 +0200311
Alexander Couzens22f34712020-10-02 02:34:39 +0200312 llist_for_each_entry(bind, &nsi->binding, list) {
313 dump_bind(vty, bind, stats);
314 }
Harald Welte2fce19a2020-12-01 17:52:55 +0100315}
316
317
318static void dump_ns_entities(struct vty *vty, const struct gprs_ns2_inst *nsi, bool stats, bool persistent_only)
319{
320 struct gprs_ns2_nse *nse;
Alexander Couzens22f34712020-10-02 02:34:39 +0200321
Alexander Couzens6a161492020-07-12 13:45:50 +0200322 llist_for_each_entry(nse, &nsi->nse, list) {
323 dump_nse(vty, nse, stats, persistent_only);
Alexander Couzens6a161492020-07-12 13:45:50 +0200324 }
Alexander Couzens6a161492020-07-12 13:45:50 +0200325}
326
Harald Welte25ee7552020-12-02 22:14:00 +0100327/* Backwards compatibility, among other things for the TestVTYGbproxy which expects
328 * 'show ns' to output something about binds */
329DEFUN_HIDDEN(show_ns, show_ns_cmd, "show ns",
330 SHOW_STR SHOW_NS_STR)
331{
332 dump_ns_entities(vty, vty_nsi, false, false);
333 dump_ns_bind(vty, vty_nsi, false);
334 return CMD_SUCCESS;
335}
336
337
Harald Welte2fce19a2020-12-01 17:52:55 +0100338DEFUN(show_ns_binds, show_ns_binds_cmd, "show ns binds [stats]",
Daniel Willmanncb3e9b52020-12-02 15:50:22 +0100339 SHOW_STR SHOW_NS_STR
Harald Welte2fce19a2020-12-01 17:52:55 +0100340 "Display information about the NS protocol binds\n"
341 "Include statistic\n")
Alexander Couzens6a161492020-07-12 13:45:50 +0200342{
Harald Welte2fce19a2020-12-01 17:52:55 +0100343 bool stats = false;
344 if (argc > 0)
345 stats = true;
346
347 dump_ns_bind(vty, vty_nsi, stats);
Alexander Couzens6a161492020-07-12 13:45:50 +0200348 return CMD_SUCCESS;
349}
350
Harald Welte2fce19a2020-12-01 17:52:55 +0100351DEFUN(show_ns_entities, show_ns_entities_cmd, "show ns entities [stats]",
Daniel Willmanncb3e9b52020-12-02 15:50:22 +0100352 SHOW_STR SHOW_NS_STR
Harald Welte2fce19a2020-12-01 17:52:55 +0100353 "Display information about the NS protocol entities (NSEs)\n"
Alexander Couzens6a161492020-07-12 13:45:50 +0200354 "Include statistics\n")
355{
Harald Welte2fce19a2020-12-01 17:52:55 +0100356 bool stats = false;
357 if (argc > 0)
358 stats = true;
359
360 dump_ns_entities(vty, vty_nsi, stats, false);
Alexander Couzens6a161492020-07-12 13:45:50 +0200361 return CMD_SUCCESS;
362}
363
364DEFUN(show_ns_pers, show_ns_pers_cmd, "show ns persistent",
Daniel Willmanncb3e9b52020-12-02 15:50:22 +0100365 SHOW_STR SHOW_NS_STR
Alexander Couzens6a161492020-07-12 13:45:50 +0200366 "Show only persistent NS\n")
367{
Harald Welte2fce19a2020-12-01 17:52:55 +0100368 dump_ns_entities(vty, vty_nsi, true, true);
Alexander Couzens6a161492020-07-12 13:45:50 +0200369 return CMD_SUCCESS;
370}
371
372DEFUN(show_nse, show_nse_cmd, "show ns (nsei|nsvc) <0-65535> [stats]",
Daniel Willmanncb3e9b52020-12-02 15:50:22 +0100373 SHOW_STR SHOW_NS_STR
Alexander Couzens6a161492020-07-12 13:45:50 +0200374 "Select one NSE by its NSE Identifier\n"
375 "Select one NSE by its NS-VC Identifier\n"
376 "The Identifier of selected type\n"
377 "Include Statistics\n")
378{
379 struct gprs_ns2_inst *nsi = vty_nsi;
380 struct gprs_ns2_nse *nse;
381 struct gprs_ns2_vc *nsvc;
382 uint16_t id = atoi(argv[1]);
383 bool show_stats = false;
384
385 if (argc >= 3)
386 show_stats = true;
387
388 if (!strcmp(argv[0], "nsei")) {
389 nse = gprs_ns2_nse_by_nsei(nsi, id);
390 if (!nse) {
391 return CMD_WARNING;
392 }
393
394 dump_nse(vty, nse, show_stats, false);
395 } else {
396 nsvc = gprs_ns2_nsvc_by_nsvci(nsi, id);
397
398 if (!nsvc) {
399 vty_out(vty, "No such NS Entity%s", VTY_NEWLINE);
400 return CMD_WARNING;
401 }
402
403 dump_nsvc(vty, nsvc, show_stats);
404 }
405
406 return CMD_SUCCESS;
407}
408
Daniel Willmanndbab7142020-11-18 14:19:56 +0100409static int nsvc_force_unconf_cb(struct gprs_ns2_vc *nsvc, void *ctx)
410{
411 gprs_ns2_vc_force_unconfigured(nsvc);
412 return 0;
413}
414
415DEFUN_HIDDEN(nsvc_force_unconf, nsvc_force_unconf_cmd,
416 "nsvc nsei <0-65535> force-unconfigured",
417 "NS Virtual Connection\n"
418 "The NSEI\n"
419 "Reset the NSVCs back to initial state\n"
420 )
421{
422 struct gprs_ns2_inst *nsi = vty_nsi;
423 struct gprs_ns2_nse *nse;
424
425 uint16_t id = atoi(argv[0]);
426
427 nse = gprs_ns2_nse_by_nsei(nsi, id);
428 if (!nse) {
429 vty_out(vty, "Could not find NSE for NSEI %u%s", id, VTY_NEWLINE);
430 return CMD_WARNING;
431 }
432
433 /* Perform the operation for all nsvc */
434 gprs_ns2_nse_foreach_nsvc(nse, nsvc_force_unconf_cb, NULL);
435
436 return CMD_SUCCESS;
437}
438
Alexander Couzens6a161492020-07-12 13:45:50 +0200439#define NSE_CMD_STR "Persistent NS Entity\n" "NS Entity ID (NSEI)\n"
440
Alexander Couzens841817e2020-11-19 00:41:29 +0100441DEFUN(cfg_nse_fr, cfg_nse_fr_cmd,
442 "nse <0-65535> nsvci <0-65535> (fr|frnet) NETIF dlci <0-1023>",
443 NSE_CMD_STR
444 "NS Virtual Connection\n"
445 "NS Virtual Connection ID (NSVCI)\n"
Harald Welte92049192020-11-25 20:56:06 +0100446 "Frame Relay User-Side\n"
447 "Frame Relay Network-Side\n"
Alexander Couzens841817e2020-11-19 00:41:29 +0100448 IFNAME_STR
449 "Data Link connection identifier\n"
450 "Data Link connection identifier\n"
451 )
452{
453 struct ns2_vty_vc *vtyvc;
454
455 uint16_t nsei = atoi(argv[0]);
456 uint16_t nsvci = atoi(argv[1]);
457 const char *role = argv[2];
458 const char *name = argv[3];
459 uint16_t dlci = atoi(argv[4]);
460
Harald Welte6d4db232020-11-25 19:33:42 +0100461 vtyvc = vtyvc_by_nsei(nsei, true);
Alexander Couzens841817e2020-11-19 00:41:29 +0100462 if (!vtyvc) {
463 vty_out(vty, "Can not allocate space %s", VTY_NEWLINE);
464 return CMD_WARNING;
465 }
466
467 if (!strcmp(role, "fr"))
468 vtyvc->fr.role = FR_ROLE_USER_EQUIPMENT;
469 else if (!strcmp(role, "frnet"))
470 vtyvc->fr.role = FR_ROLE_NETWORK_EQUIPMENT;
471
472 osmo_strlcpy(vtyvc->netif, name, sizeof(vtyvc->netif));
473 vtyvc->frdlci = dlci;
474 vtyvc->nsvci = nsvci;
Alexander Couzens24a14ac2020-11-19 02:34:49 +0100475 vtyvc->ll = GPRS_NS2_LL_FR;
Alexander Couzens841817e2020-11-19 00:41:29 +0100476
477 return CMD_SUCCESS;
478}
479
Alexander Couzens6a161492020-07-12 13:45:50 +0200480DEFUN(cfg_nse_nsvc, cfg_nse_nsvci_cmd,
481 "nse <0-65535> nsvci <0-65535>",
482 NSE_CMD_STR
483 "NS Virtual Connection\n"
484 "NS Virtual Connection ID (NSVCI)\n"
485 )
486{
487 struct ns2_vty_vc *vtyvc;
488
489 uint16_t nsei = atoi(argv[0]);
490 uint16_t nsvci = atoi(argv[1]);
491
Harald Welte6d4db232020-11-25 19:33:42 +0100492 vtyvc = vtyvc_by_nsei(nsei, true);
Alexander Couzens6a161492020-07-12 13:45:50 +0200493 if (!vtyvc) {
494 vty_out(vty, "Can not allocate space %s", VTY_NEWLINE);
495 return CMD_WARNING;
496 }
497
498 vtyvc->nsvci = nsvci;
499
500 return CMD_SUCCESS;
501}
502
503DEFUN(cfg_nse_remoteip, cfg_nse_remoteip_cmd,
Harald Welte6d4db232020-11-25 19:33:42 +0100504 "nse <0-65535> remote-ip " VTY_IPV46_CMD,
Alexander Couzens6a161492020-07-12 13:45:50 +0200505 NSE_CMD_STR
506 "Remote IP Address\n"
Alexander Couzensc82c40a2020-09-24 05:55:48 +0200507 "Remote IPv4 Address\n"
508 "Remote IPv6 Address\n")
Alexander Couzens6a161492020-07-12 13:45:50 +0200509{
510 uint16_t nsei = atoi(argv[0]);
511 struct ns2_vty_vc *vtyvc;
512
Harald Welte6d4db232020-11-25 19:33:42 +0100513 vtyvc = vtyvc_by_nsei(nsei, true);
Alexander Couzens6a161492020-07-12 13:45:50 +0200514 if (!vtyvc) {
515 vty_out(vty, "Can not allocate space %s", VTY_NEWLINE);
516 return CMD_WARNING;
517 }
518
Harald Welte6d4db232020-11-25 19:33:42 +0100519 osmo_sockaddr_str_from_str2(&vtyvc->remote, argv[1]);
Alexander Couzens6a161492020-07-12 13:45:50 +0200520
521 return CMD_SUCCESS;
522}
523
524DEFUN(cfg_nse_remoteport, cfg_nse_remoteport_cmd,
Harald Welte6d4db232020-11-25 19:33:42 +0100525 "nse <0-65535> remote-port <0-65535>",
Alexander Couzens6a161492020-07-12 13:45:50 +0200526 NSE_CMD_STR
527 "Remote UDP Port\n"
528 "Remote UDP Port Number\n")
529{
530 uint16_t nsei = atoi(argv[0]);
Harald Welte6d4db232020-11-25 19:33:42 +0100531 uint16_t port = atoi(argv[1]);
Alexander Couzens6a161492020-07-12 13:45:50 +0200532 struct ns2_vty_vc *vtyvc;
533
Harald Welte6d4db232020-11-25 19:33:42 +0100534 vtyvc = vtyvc_by_nsei(nsei, true);
Alexander Couzens6a161492020-07-12 13:45:50 +0200535 if (!vtyvc) {
536 vty_out(vty, "Can not allocate space %s", VTY_NEWLINE);
537 return CMD_WARNING;
538 }
539
540 vtyvc->remote.port = port;
541
542 return CMD_SUCCESS;
543}
544
545DEFUN(cfg_nse_fr_dlci, cfg_nse_fr_dlci_cmd,
Alexander Couzens841817e2020-11-19 00:41:29 +0100546 "nse <0-65535> nsvci <0-65535> fr-dlci <16-1007>",
Alexander Couzens6a161492020-07-12 13:45:50 +0200547 NSE_CMD_STR
Harald Welte92049192020-11-25 20:56:06 +0100548 "NS Virtual Connection\n"
549 "NS Virtual Connection ID (NSVCI)\n"
Alexander Couzens6a161492020-07-12 13:45:50 +0200550 "Frame Relay DLCI\n"
551 "Frame Relay DLCI Number\n")
552{
553 uint16_t nsei = atoi(argv[0]);
Alexander Couzens841817e2020-11-19 00:41:29 +0100554 uint16_t nsvci = atoi(argv[1]);
555 uint16_t dlci = atoi(argv[2]);
Alexander Couzens6a161492020-07-12 13:45:50 +0200556 struct ns2_vty_vc *vtyvc;
557
Harald Welte6d4db232020-11-25 19:33:42 +0100558 vtyvc = vtyvc_by_nsei(nsei, true);
Alexander Couzens6a161492020-07-12 13:45:50 +0200559 if (!vtyvc) {
560 vty_out(vty, "Can not allocate space %s", VTY_NEWLINE);
561 return CMD_WARNING;
562 }
563
Alexander Couzens6a161492020-07-12 13:45:50 +0200564 vtyvc->frdlci = dlci;
Harald Welte6d4db232020-11-25 19:33:42 +0100565 vtyvc->nsvci = nsvci;
Alexander Couzens6a161492020-07-12 13:45:50 +0200566
567 return CMD_SUCCESS;
568}
569
570DEFUN(cfg_nse_encaps, cfg_nse_encaps_cmd,
Harald Welte6d4db232020-11-25 19:33:42 +0100571 "nse <0-65535> encapsulation (udp|framerelay-gre)",
Alexander Couzens6a161492020-07-12 13:45:50 +0200572 NSE_CMD_STR
573 "Encapsulation for NS\n"
574 "UDP/IP Encapsulation\n" "Frame-Relay/GRE/IP Encapsulation\n")
575{
576 uint16_t nsei = atoi(argv[0]);
577 struct ns2_vty_vc *vtyvc;
578
Harald Welte6d4db232020-11-25 19:33:42 +0100579 vtyvc = vtyvc_by_nsei(nsei, true);
Alexander Couzens6a161492020-07-12 13:45:50 +0200580 if (!vtyvc) {
581 vty_out(vty, "Can not allocate space %s", VTY_NEWLINE);
582 return CMD_WARNING;
583 }
584
Harald Welte6d4db232020-11-25 19:33:42 +0100585 if (!strcmp(argv[1], "udp"))
Alexander Couzens24a14ac2020-11-19 02:34:49 +0100586 vtyvc->ll = GPRS_NS2_LL_UDP;
Alexander Couzens6a161492020-07-12 13:45:50 +0200587 else
Alexander Couzens24a14ac2020-11-19 02:34:49 +0100588 vtyvc->ll = GPRS_NS2_LL_FR_GRE;
Alexander Couzens6a161492020-07-12 13:45:50 +0200589
590 return CMD_SUCCESS;
591}
592
593DEFUN(cfg_nse_remoterole, cfg_nse_remoterole_cmd,
Harald Welte6d4db232020-11-25 19:33:42 +0100594 "nse <0-65535> remote-role (sgsn|bss)",
Alexander Couzens6a161492020-07-12 13:45:50 +0200595 NSE_CMD_STR
596 "Remote NSE Role\n"
597 "Remote Peer is SGSN\n"
598 "Remote Peer is BSS\n")
599{
600 uint16_t nsei = atoi(argv[0]);
601 struct ns2_vty_vc *vtyvc;
602
Harald Welte6d4db232020-11-25 19:33:42 +0100603 vtyvc = vtyvc_by_nsei(nsei, true);
Alexander Couzens6a161492020-07-12 13:45:50 +0200604 if (!vtyvc) {
605 vty_out(vty, "Can not allocate space %s", VTY_NEWLINE);
606 return CMD_WARNING;
607 }
608
Harald Welte6d4db232020-11-25 19:33:42 +0100609 if (!strcmp(argv[1], "sgsn"))
Alexander Couzens6a161492020-07-12 13:45:50 +0200610 vtyvc->remote_end_is_sgsn = 1;
611 else
612 vtyvc->remote_end_is_sgsn = 0;
613
614 return CMD_SUCCESS;
615}
616
617DEFUN(cfg_no_nse, cfg_no_nse_cmd,
Harald Welte6d4db232020-11-25 19:33:42 +0100618 "no nse <0-65535>",
Alexander Couzens6a161492020-07-12 13:45:50 +0200619 "Delete Persistent NS Entity\n"
620 "Delete " NSE_CMD_STR)
621{
622 uint16_t nsei = atoi(argv[0]);
623 struct ns2_vty_vc *vtyvc;
624
Harald Welte6d4db232020-11-25 19:33:42 +0100625 vtyvc = vtyvc_by_nsei(nsei, false);
Alexander Couzens6a161492020-07-12 13:45:50 +0200626 if (!vtyvc) {
627 vty_out(vty, "The NSE %d does not exists.%s", nsei, VTY_NEWLINE);
628 return CMD_WARNING;
629 }
630
631 ns2_vc_free(vtyvc);
632
633 return CMD_SUCCESS;
634}
635
636DEFUN(cfg_ns_timer, cfg_ns_timer_cmd,
637 "timer " NS_TIMERS " <0-65535>",
638 "Network Service Timer\n"
639 NS_TIMERS_HELP "Timer Value\n")
640{
641 int idx = get_string_value(gprs_ns_timer_strs, argv[0]);
642 int val = atoi(argv[1]);
643
644 if (idx < 0 || idx >= ARRAY_SIZE(vty_nsi->timeout))
645 return CMD_WARNING;
646
647 vty_nsi->timeout[idx] = val;
648
649 return CMD_SUCCESS;
650}
651
652#define ENCAPS_STR "NS encapsulation options\n"
653
654DEFUN(cfg_nsip_local_ip, cfg_nsip_local_ip_cmd,
655 "encapsulation udp local-ip " VTY_IPV46_CMD,
656 ENCAPS_STR "NS over UDP Encapsulation\n"
657 "Set the IP address on which we listen for NS/UDP\n"
Alexander Couzensc82c40a2020-09-24 05:55:48 +0200658 "IPv4 Address\n"
659 "IPv6 Address\n")
Alexander Couzens6a161492020-07-12 13:45:50 +0200660{
661 osmo_sockaddr_str_from_str2(&priv.udp, argv[0]);
662
663 return CMD_SUCCESS;
664}
665
666DEFUN(cfg_nsip_local_port, cfg_nsip_local_port_cmd,
667 "encapsulation udp local-port <0-65535>",
668 ENCAPS_STR "NS over UDP Encapsulation\n"
669 "Set the UDP port on which we listen for NS/UDP\n"
670 "UDP port number\n")
671{
672 unsigned int port = atoi(argv[0]);
673
674 priv.udp.port = port;
675
676 return CMD_SUCCESS;
677}
678
679DEFUN(cfg_nsip_dscp, cfg_nsip_dscp_cmd,
680 "encapsulation udp dscp <0-255>",
681 ENCAPS_STR "NS over UDP Encapsulation\n"
682 "Set DSCP/TOS on the UDP socket\n" "DSCP Value\n")
683{
684 int dscp = atoi(argv[0]);
685 struct gprs_ns2_vc_bind *bind;
686
687 priv.dscp = dscp;
688
689 llist_for_each_entry(bind, &vty_nsi->binding, list) {
690 if (gprs_ns2_is_ip_bind(bind))
691 gprs_ns2_ip_bind_set_dscp(bind, dscp);
692 }
693
694 return CMD_SUCCESS;
695}
696
697DEFUN(cfg_nsip_res_block_unblock, cfg_nsip_res_block_unblock_cmd,
698 "encapsulation udp use-reset-block-unblock (enabled|disabled)",
699 ENCAPS_STR "NS over UDP Encapsulation\n"
700 "Use NS-{RESET,BLOCK,UNBLOCK} procedures in violation of 3GPP TS 48.016\n"
701 "Enable NS-{RESET,BLOCK,UNBLOCK}\n"
702 "Disable NS-{RESET,BLOCK,UNBLOCK}\n")
703{
704 enum gprs_ns2_vc_mode vc_mode;
705 struct gprs_ns2_vc_bind *bind;
706
707 if (!strcmp(argv[0], "enabled"))
708 vc_mode = NS2_VC_MODE_BLOCKRESET;
709 else
710 vc_mode = NS2_VC_MODE_ALIVE;
711
712 if (priv.force_vc_mode) {
713 if (priv.vc_mode != vc_mode)
714 {
715 vty_out(vty, "Ignoring use-reset-block because it's already set by %s.%s",
716 priv.force_vc_mode_reason, VTY_NEWLINE);
717 return CMD_WARNING;
718 }
719
720 return CMD_SUCCESS;
721 }
722
723 priv.vc_mode = vc_mode;
724
725 llist_for_each_entry(bind, &vty_nsi->binding, list) {
726 gprs_ns2_bind_set_mode(bind, priv.vc_mode);
727 }
728
729 return CMD_SUCCESS;
730}
731
732DEFUN(cfg_frgre_local_ip, cfg_frgre_local_ip_cmd,
733 "encapsulation framerelay-gre local-ip " VTY_IPV46_CMD,
734 ENCAPS_STR "NS over Frame Relay over GRE Encapsulation\n"
735 "Set the IP address on which we listen for NS/FR/GRE\n"
Alexander Couzensc82c40a2020-09-24 05:55:48 +0200736 "IPv4 Address\n"
737 "IPv6 Address\n")
Alexander Couzens6a161492020-07-12 13:45:50 +0200738{
739 osmo_sockaddr_str_from_str2(&priv.frgreaddr, argv[0]);
740
741 return CMD_SUCCESS;
742}
743
744DEFUN(cfg_frgre_enable, cfg_frgre_enable_cmd,
745 "encapsulation framerelay-gre enabled (1|0)",
746 ENCAPS_STR "NS over Frame Relay over GRE Encapsulation\n"
747 "Enable or disable Frame Relay over GRE\n"
748 "Enable\n" "Disable\n")
749{
750 int enabled = atoi(argv[0]);
751
752 priv.frgre = enabled;
753
754 return CMD_SUCCESS;
755}
756
757/* TODO: allow vty to reset/block/unblock nsvc/nsei */
758
759/* TODO: add filter for NSEI as ns1 code does */
760/* TODO: add filter for single connection by description */
761DEFUN(logging_fltr_nsvc,
762 logging_fltr_nsvc_cmd,
763 "logging filter nsvc nsvci <0-65535>",
764 LOGGING_STR FILTER_STR
765 "Filter based on NS Virtual Connection\n"
766 "Identify NS-VC by NSVCI\n"
767 "Numeric identifier\n")
768{
769 struct log_target *tgt;
770 struct gprs_ns2_vc *nsvc;
771 uint16_t id = atoi(argv[1]);
772
773 log_tgt_mutex_lock();
774 tgt = osmo_log_vty2tgt(vty);
775 if (!tgt) {
776 log_tgt_mutex_unlock();
777 return CMD_WARNING;
778 }
779
780 nsvc = gprs_ns2_nsvc_by_nsvci(vty_nsi, id);
781 if (!nsvc) {
782 vty_out(vty, "No NS-VC by that identifier%s", VTY_NEWLINE);
783 log_tgt_mutex_unlock();
784 return CMD_WARNING;
785 }
786
787 log_set_nsvc_filter(tgt, nsvc);
788 log_tgt_mutex_unlock();
789 return CMD_SUCCESS;
790}
791
Alexander Couzens1fac6f72020-10-01 19:08:38 +0200792/**
793 * gprs_ns2_vty_init initialize the vty
794 * \param[inout] nsi
795 * \param[in] default_bind set the default address to bind to. Can be NULL.
796 * \return 0 on success
797 */
Vadim Yanitskiya07f25e2020-10-09 21:47:01 +0700798int gprs_ns2_vty_init(struct gprs_ns2_inst *nsi,
799 const struct osmo_sockaddr_str *default_bind)
Alexander Couzens6a161492020-07-12 13:45:50 +0200800{
801 static bool vty_elements_installed = false;
802
803 vty_nsi = nsi;
804 memset(&priv, 0, sizeof(struct ns2_vty_priv));
805 INIT_LLIST_HEAD(&priv.vtyvc);
806 priv.vc_mode = NS2_VC_MODE_BLOCKRESET;
Alexander Couzens1fac6f72020-10-01 19:08:38 +0200807 if (default_bind)
808 memcpy(&priv.udp, default_bind, sizeof(*default_bind));
Alexander Couzens6a161492020-07-12 13:45:50 +0200809
810 /* Regression test code may call this function repeatedly, so make sure
811 * that VTY elements are not duplicated, which would assert. */
812 if (vty_elements_installed)
813 return 0;
814 vty_elements_installed = true;
815
Harald Welte25ee7552020-12-02 22:14:00 +0100816 install_lib_element_ve(&show_ns_cmd);
Harald Welte2fce19a2020-12-01 17:52:55 +0100817 install_lib_element_ve(&show_ns_binds_cmd);
818 install_lib_element_ve(&show_ns_entities_cmd);
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +0700819 install_lib_element_ve(&show_ns_pers_cmd);
820 install_lib_element_ve(&show_nse_cmd);
821 install_lib_element_ve(&logging_fltr_nsvc_cmd);
Alexander Couzens6a161492020-07-12 13:45:50 +0200822
Daniel Willmanndbab7142020-11-18 14:19:56 +0100823 install_lib_element(ENABLE_NODE, &nsvc_force_unconf_cmd);
824
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +0700825 install_lib_element(CFG_LOG_NODE, &logging_fltr_nsvc_cmd);
Alexander Couzens6a161492020-07-12 13:45:50 +0200826
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +0700827 install_lib_element(CONFIG_NODE, &cfg_ns_cmd);
Alexander Couzens6a161492020-07-12 13:45:50 +0200828 install_node(&ns_node, config_write_ns);
Alexander Couzens841817e2020-11-19 00:41:29 +0100829 install_lib_element(L_NS_NODE, &cfg_nse_fr_cmd);
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +0700830 install_lib_element(L_NS_NODE, &cfg_nse_nsvci_cmd);
831 install_lib_element(L_NS_NODE, &cfg_nse_remoteip_cmd);
832 install_lib_element(L_NS_NODE, &cfg_nse_remoteport_cmd);
833 install_lib_element(L_NS_NODE, &cfg_nse_fr_dlci_cmd);
834 install_lib_element(L_NS_NODE, &cfg_nse_encaps_cmd);
835 install_lib_element(L_NS_NODE, &cfg_nse_remoterole_cmd);
836 install_lib_element(L_NS_NODE, &cfg_no_nse_cmd);
837 install_lib_element(L_NS_NODE, &cfg_ns_timer_cmd);
838 install_lib_element(L_NS_NODE, &cfg_nsip_local_ip_cmd);
839 install_lib_element(L_NS_NODE, &cfg_nsip_local_port_cmd);
840 install_lib_element(L_NS_NODE, &cfg_nsip_dscp_cmd);
841 install_lib_element(L_NS_NODE, &cfg_nsip_res_block_unblock_cmd);
842 install_lib_element(L_NS_NODE, &cfg_frgre_enable_cmd);
843 install_lib_element(L_NS_NODE, &cfg_frgre_local_ip_cmd);
Alexander Couzens6a161492020-07-12 13:45:50 +0200844
845 /* TODO: nsvc/nsei command to reset states or reset/block/unblock nsei/nsvcs */
846
847 return 0;
848}
849
850/*!
851 * \brief gprs_ns2_vty_create parse the vty tree into ns nodes
852 * It has to be in different steps to ensure the bind is created before creating VCs.
853 * \return 0 on success
854 */
855int gprs_ns2_vty_create() {
856 struct ns2_vty_vc *vtyvc;
Alexander Couzens841817e2020-11-19 00:41:29 +0100857 struct gprs_ns2_vc_bind *bind, *fr;
Alexander Couzens6a161492020-07-12 13:45:50 +0200858 struct gprs_ns2_nse *nse;
859 struct gprs_ns2_vc *nsvc;
860 struct osmo_sockaddr sockaddr;
Alexander Couzens841817e2020-11-19 00:41:29 +0100861 int rc = 0;
Alexander Couzens6a161492020-07-12 13:45:50 +0200862
863 if (!vty_nsi)
864 return -1;
865
866 /* create binds, only support a single bind. either FR or UDP */
867 if (priv.frgre) {
868 /* TODO not yet supported !*/
869 return -1;
870 } else {
871 /* UDP */
872 osmo_sockaddr_str_to_sockaddr(&priv.udp, &sockaddr.u.sas);
Alexander Couzens477ffb02020-10-01 19:05:28 +0200873 if (gprs_ns2_ip_bind(vty_nsi, &sockaddr, priv.dscp, &bind)) {
Alexander Couzens6a161492020-07-12 13:45:50 +0200874 /* TODO: could not bind on the specific address */
875 return -1;
876 }
877 gprs_ns2_bind_set_mode(bind, priv.vc_mode);
878 }
879
880 /* create vcs */
881 llist_for_each_entry(vtyvc, &priv.vtyvc, list) {
Alexander Couzens841817e2020-11-19 00:41:29 +0100882 /* validate settings */
883 switch (vtyvc->ll) {
Alexander Couzens24a14ac2020-11-19 02:34:49 +0100884 case GPRS_NS2_LL_UDP:
Alexander Couzens841817e2020-11-19 00:41:29 +0100885 if (strlen(vtyvc->remote.ip) == 0) {
886 /* Invalid IP for VC */
887 continue;
888 }
Alexander Couzens6a161492020-07-12 13:45:50 +0200889
Alexander Couzens841817e2020-11-19 00:41:29 +0100890 if (!vtyvc->remote.port) {
891 /* Invalid port for VC */
892 continue;
893 }
Alexander Couzens6a161492020-07-12 13:45:50 +0200894
Alexander Couzens841817e2020-11-19 00:41:29 +0100895 if (osmo_sockaddr_str_to_sockaddr(&vtyvc->remote, &sockaddr.u.sas)) {
896 /* Invalid sockaddr for VC */
897 continue;
898 }
899 break;
Alexander Couzens24a14ac2020-11-19 02:34:49 +0100900 case GPRS_NS2_LL_FR:
Alexander Couzens841817e2020-11-19 00:41:29 +0100901 break;
Alexander Couzens24a14ac2020-11-19 02:34:49 +0100902 case GPRS_NS2_LL_FR_GRE:
Alexander Couzens6a161492020-07-12 13:45:50 +0200903 continue;
904 }
905
906 nse = gprs_ns2_nse_by_nsei(vty_nsi, vtyvc->nsei);
907 if (!nse) {
Alexander Couzensaac90162020-11-19 02:44:04 +0100908 nse = gprs_ns2_create_nse(vty_nsi, vtyvc->nsei, vtyvc->ll);
Alexander Couzens6a161492020-07-12 13:45:50 +0200909 if (!nse) {
910 /* Could not create NSE for VTY */
911 continue;
912 }
913 }
914 nse->persistent = true;
915
Alexander Couzens841817e2020-11-19 00:41:29 +0100916 switch (vtyvc->ll) {
Alexander Couzens24a14ac2020-11-19 02:34:49 +0100917 case GPRS_NS2_LL_UDP:
Alexander Couzens841817e2020-11-19 00:41:29 +0100918 nsvc = gprs_ns2_ip_connect(bind,
919 &sockaddr,
920 nse,
921 vtyvc->nsvci);
922 if (!nsvc) {
923 /* Could not create NSVC, connect failed */
924 continue;
925 }
926 nsvc->persistent = true;
927 break;
Alexander Couzens24a14ac2020-11-19 02:34:49 +0100928 case GPRS_NS2_LL_FR: {
Alexander Couzens841817e2020-11-19 00:41:29 +0100929 if (vty_fr_network == NULL) {
930 /* TODO: add a switch for BSS/SGSN/gbproxy */
931 vty_fr_network = osmo_fr_network_alloc(vty_nsi);
932 }
933 fr = gprs_ns2_fr_bind_by_netif(
934 vty_nsi,
935 vtyvc->netif);
936 if (!fr) {
937 rc = gprs_ns2_fr_bind(vty_nsi, vtyvc->netif, vty_fr_network, vtyvc->fr.role, &fr);
938 if (rc < 0) {
939 LOGP(DLNS, LOGL_ERROR, "Can not create fr bind on device %s err: %d\n", vtyvc->netif, rc);
940 return rc;
941 }
942 }
943
944 nsvc = gprs_ns2_fr_connect(fr, vtyvc->nsei, vtyvc->nsvci, vtyvc->frdlci);
945 if (!nsvc) {
946 /* Could not create NSVC, connect failed */
947 continue;
948 }
949 nsvc->persistent = true;
950 break;
951 }
Alexander Couzens24a14ac2020-11-19 02:34:49 +0100952 case GPRS_NS2_LL_FR_GRE:
Alexander Couzensd745a0e2020-10-07 00:50:00 +0200953 continue;
Alexander Couzens6a161492020-07-12 13:45:50 +0200954 }
955 }
956
957
958 return 0;
959}
960
961/*!
962 * \brief ns2_vty_bind_apply will be called when a new bind is created to apply vty settings
963 * \param bind
964 * \return
965 */
966void ns2_vty_bind_apply(struct gprs_ns2_vc_bind *bind)
967{
968 gprs_ns2_bind_set_mode(bind, priv.vc_mode);
969}
970
971/*!
972 * \brief ns2_vty_force_vc_mode force a mode and prevents the vty from overwriting it.
973 * \param force if true mode and reason will be set. false to allow modification via vty.
974 * \param mode
975 * \param reason A description shown to the user when a vty command wants to change the mode.
976 */
Daniel Willmann4fb27a82020-09-25 15:39:46 +0200977void gprs_ns2_vty_force_vc_mode(bool force, enum gprs_ns2_vc_mode mode, const char *reason)
Alexander Couzens6a161492020-07-12 13:45:50 +0200978{
979 priv.force_vc_mode = force;
980
981 if (force) {
982 priv.vc_mode = mode;
983 priv.force_vc_mode_reason = reason;
984 }
985}