blob: 62149079e81523d23194b3944b66a60f8723787b [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
Alexander Couzensda1bf8e2021-01-25 16:27:33 +01004/* (C) 2020 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
Alexander Couzens6a161492020-07-12 13:45:50 +02005 * Author: Alexander Couzens <lynxis@fe80.eu>
6 *
7 * All Rights Reserved
8 *
9 * SPDX-License-Identifier: GPL-2.0+
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program. If not, see <http://www.gnu.org/licenses/>.
23 *
24 */
25
26#include <stdlib.h>
27#include <unistd.h>
28#include <errno.h>
29#include <stdint.h>
30
31#include <arpa/inet.h>
Alexander Couzens841817e2020-11-19 00:41:29 +010032#include <net/if.h>
Alexander Couzens6a161492020-07-12 13:45:50 +020033
Alexander Couzens6a161492020-07-12 13:45:50 +020034#include <osmocom/core/byteswap.h>
Daniel Willmanndbab7142020-11-18 14:19:56 +010035#include <osmocom/core/fsm.h>
Pau Espin Pedrolcaf53172021-01-28 13:37:50 +010036#include <osmocom/core/linuxlist.h>
Alexander Couzensda1bf8e2021-01-25 16:27:33 +010037#include <osmocom/core/msgb.h>
38#include <osmocom/core/rate_ctr.h>
39#include <osmocom/core/select.h>
40#include <osmocom/core/talloc.h>
41#include <osmocom/core/sockaddr_str.h>
Alexander Couzens6a161492020-07-12 13:45:50 +020042#include <osmocom/core/socket.h>
Alexander Couzens841817e2020-11-19 00:41:29 +010043#include <osmocom/gprs/frame_relay.h>
Alexander Couzens6a161492020-07-12 13:45:50 +020044#include <osmocom/gprs/gprs_ns2.h>
45#include <osmocom/gsm/tlv.h>
Alexander Couzens6a161492020-07-12 13:45:50 +020046#include <osmocom/vty/command.h>
47#include <osmocom/vty/logging.h>
Pau Espin Pedrolcaf53172021-01-28 13:37:50 +010048#include <osmocom/vty/misc.h>
Alexander Couzensda1bf8e2021-01-25 16:27:33 +010049#include <osmocom/vty/telnet_interface.h>
50#include <osmocom/vty/vty.h>
Alexander Couzens6a161492020-07-12 13:45:50 +020051
52#include "gprs_ns2_internal.h"
53
Daniel Willmanncb3e9b52020-12-02 15:50:22 +010054#define SHOW_NS_STR "Display information about the NS protocol\n"
Alexander Couzensda1bf8e2021-01-25 16:27:33 +010055#define NSVCI_STR "NS Virtual Connection ID (NS-VCI)\n"
56#define DLCI_STR "Data Link connection identifier\n"
Pau Espin Pedrolcaf53172021-01-28 13:37:50 +010057
58static struct gprs_ns2_inst *vty_nsi = NULL;
Pau Espin Pedrolcaf53172021-01-28 13:37:50 +010059static struct osmo_fr_network *vty_fr_network = NULL;
Alexander Couzensda1bf8e2021-01-25 16:27:33 +010060static struct llist_head binds;
Alexander Couzens6b9d2322021-02-12 03:17:59 +010061static struct llist_head nses;
Pau Espin Pedrolcaf53172021-01-28 13:37:50 +010062
Alexander Couzensda1bf8e2021-01-25 16:27:33 +010063struct vty_bind {
64 struct llist_head list;
65 const char *name;
66 enum gprs_ns2_ll ll;
67 int dscp;
68 bool accept_ipaccess;
69 bool accept_sns;
Alexander Couzensc4704762021-02-08 23:13:12 +010070 uint8_t ip_sns_sig_weight;
71 uint8_t ip_sns_data_weight;
Alexander Couzensda1bf8e2021-01-25 16:27:33 +010072};
73
Alexander Couzens6b9d2322021-02-12 03:17:59 +010074struct vty_nse {
75 struct llist_head list;
76 uint16_t nsei;
77 /* list of binds which are valid for this nse. Only IP-SNS uses this
78 * to allow `no listen ..` in the bind context. So "half" created binds are valid for
79 * IP-SNS. This allows changing the bind ip without modifying all NSEs afterwards */
80 struct llist_head binds;
81};
82
83/* used by IP-SNS to connect multiple vty_nse_bind to a vty_nse */
84struct vty_nse_bind {
85 struct llist_head list;
86 struct vty_bind *vbind;
87};
88
Alexander Couzensda1bf8e2021-01-25 16:27:33 +010089/* TODO: this should into osmo timer */
Alexander Couzens6a161492020-07-12 13:45:50 +020090static const struct value_string gprs_ns_timer_strs[] = {
91 { 0, "tns-block" },
92 { 1, "tns-block-retries" },
93 { 2, "tns-reset" },
94 { 3, "tns-reset-retries" },
95 { 4, "tns-test" },
96 { 5, "tns-alive" },
97 { 6, "tns-alive-retries" },
98 { 7, "tsns-prov" },
Harald Welte33c3c062020-12-16 11:59:19 +010099 { 8, "tsns-size-retries" },
100 { 9, "tsns-config-retries" },
Alexander Couzens6a161492020-07-12 13:45:50 +0200101 { 0, NULL }
102};
103
Alexander Couzensda1bf8e2021-01-25 16:27:33 +0100104const struct value_string vty_fr_role_names[] = {
105 { FR_ROLE_USER_EQUIPMENT, "fr" },
106 { FR_ROLE_NETWORK_EQUIPMENT, "frnet" },
107 { 0, NULL }
108};
109
110const struct value_string vty_ll_names[] = {
111 { GPRS_NS2_LL_FR, "fr" },
112 { GPRS_NS2_LL_FR_GRE, "frgre" },
113 { GPRS_NS2_LL_UDP, "udp" },
114 { 0, NULL }
115};
116
117static struct vty_bind *vty_bind_by_name(const char *name)
Daniel Willmann751977b2020-12-02 18:59:44 +0100118{
Alexander Couzensda1bf8e2021-01-25 16:27:33 +0100119 struct vty_bind *vbind;
120 llist_for_each_entry(vbind, &binds, list) {
Alexander Couzensb7921732021-02-12 03:08:42 +0100121 if (!strcmp(vbind->name, name))
Alexander Couzensda1bf8e2021-01-25 16:27:33 +0100122 return vbind;
Daniel Willmann751977b2020-12-02 18:59:44 +0100123 }
Alexander Couzensda1bf8e2021-01-25 16:27:33 +0100124 return NULL;
Daniel Willmann751977b2020-12-02 18:59:44 +0100125}
126
Alexander Couzensda1bf8e2021-01-25 16:27:33 +0100127static struct vty_bind *vty_bind_alloc(const char *name)
Alexander Couzens6a161492020-07-12 13:45:50 +0200128{
Alexander Couzensda1bf8e2021-01-25 16:27:33 +0100129 struct vty_bind *vbind = talloc_zero(vty_nsi, struct vty_bind);
130 if (!vbind)
131 return NULL;
132
133 vbind->name = talloc_strdup(vty_nsi, name);
134 if (!vbind->name) {
135 talloc_free(vbind);
136 return NULL;
Alexander Couzens6a161492020-07-12 13:45:50 +0200137 }
Alexander Couzensda1bf8e2021-01-25 16:27:33 +0100138
Alexander Couzensc4704762021-02-08 23:13:12 +0100139 vbind->ip_sns_sig_weight = 1;
140 vbind->ip_sns_data_weight = 1;
Alexander Couzensda1bf8e2021-01-25 16:27:33 +0100141 llist_add(&vbind->list, &binds);
142 return vbind;
143}
144
145static void vty_bind_free(struct vty_bind *vbind)
146{
147 if (!vbind)
148 return;
149
150 llist_del(&vbind->list);
Alexander Couzens3e2e4a02021-02-09 16:15:06 +0100151 talloc_free(vbind);
Alexander Couzens6a161492020-07-12 13:45:50 +0200152}
153
Alexander Couzens6b9d2322021-02-12 03:17:59 +0100154static struct vty_nse *vty_nse_by_nsei(uint16_t nsei)
155{
156 struct vty_nse *vnse;
157 llist_for_each_entry(vnse, &nses, list) {
158 if (vnse->nsei == nsei)
159 return vnse;
160 }
161 return NULL;
162}
163
164static struct vty_nse *vty_nse_alloc(uint16_t nsei)
165{
166 struct vty_nse *vnse = talloc_zero(vty_nsi, struct vty_nse);
167 if (!vnse)
168 return NULL;
169
170 vnse->nsei = nsei;
171 INIT_LLIST_HEAD(&vnse->binds);
172 llist_add(&vnse->list, &nses);
173 return vnse;
174}
175
176static void vty_nse_free(struct vty_nse *vnse)
177{
178 if (!vnse)
179 return;
180
181 llist_del(&vnse->list);
182 /* all vbind of the nse will be freed by talloc */
183 talloc_free(vnse);
184}
185
186static int vty_nse_add_vbind(struct vty_nse *vnse, struct vty_bind *vbind)
187{
188 struct vty_nse_bind *vnse_bind;
189
190 if (vbind->ll != GPRS_NS2_LL_UDP)
191 return -EINVAL;
192
193 llist_for_each_entry(vnse_bind, &vnse->binds, list) {
194 if (vnse_bind->vbind == vbind)
195 return -EALREADY;
196 }
197
198 vnse_bind = talloc(vnse, struct vty_nse_bind);
199 if (!vnse_bind)
200 return -ENOMEM;
201 vnse_bind->vbind = vbind;
202
203 llist_add_tail(&vnse_bind->list, &vnse->binds);
204 return 0;
205}
206
207static int vty_nse_remove_vbind(struct vty_nse *vnse, struct vty_bind *vbind)
208{
209 struct vty_nse_bind *vnse_bind, *tmp;
210 if (vbind->ll != GPRS_NS2_LL_UDP)
211 return -EINVAL;
212
213 llist_for_each_entry_safe(vnse_bind, tmp, &vnse->binds, list) {
214 if (vnse_bind->vbind == vbind) {
215 llist_del(&vnse_bind->list);
216 talloc_free(vnse_bind);
217 }
218 }
219
220 return -ENOENT;
221}
222
223/* check if the NSE still has SNS configuration */
224static bool vty_nse_check_sns(struct gprs_ns2_nse *nse) {
225 struct vty_nse *vnse = vty_nse_by_nsei(nse->nsei);
226
227 int count = gprs_ns2_sns_count(nse);
228 if (count > 0) {
229 /* there are other sns endpoints */
230 return true;
231 }
232
233 if (!vnse)
234 return false;
235
236 if (llist_empty(&vnse->binds))
237 return false;
238
239 return true;
240}
241
Alexander Couzens6a161492020-07-12 13:45:50 +0200242static struct cmd_node ns_node = {
243 L_NS_NODE,
244 "%s(config-ns)# ",
245 1,
246};
247
Alexander Couzens6a161492020-07-12 13:45:50 +0200248DEFUN(cfg_ns, cfg_ns_cmd,
249 "ns",
250 "Configure the GPRS Network Service")
251{
252 vty->node = L_NS_NODE;
253 return CMD_SUCCESS;
254}
255
Alexander Couzensda1bf8e2021-01-25 16:27:33 +0100256DEFUN(cfg_ns_timer, cfg_ns_timer_cmd,
257 "timer " NS_TIMERS " <0-65535>",
258 "Network Service Timer\n"
259 NS_TIMERS_HELP "Timer Value\n")
260{
261 int idx = get_string_value(gprs_ns_timer_strs, argv[0]);
262 int val = atoi(argv[1]);
263
264 if (idx < 0 || idx >= ARRAY_SIZE(vty_nsi->timeout))
265 return CMD_WARNING;
266
267 vty_nsi->timeout[idx] = val;
268
269 return CMD_SUCCESS;
270}
271
272DEFUN(cfg_ns_nsei, cfg_ns_nsei_cmd,
273 "nse <0-65535>",
274 "Persistent NS Entity\n"
275 "NS Entity ID (NSEI)\n"
276 )
277{
278 struct gprs_ns2_nse *nse;
Alexander Couzens6b9d2322021-02-12 03:17:59 +0100279 struct vty_nse *vnse;
Alexander Couzensda1bf8e2021-01-25 16:27:33 +0100280 uint16_t nsei = atoi(argv[0]);
Alexander Couzens6b9d2322021-02-12 03:17:59 +0100281 bool free_vnse = false;
282
283 vnse = vty_nse_by_nsei(nsei);
284 if (!vnse) {
285 vnse = vty_nse_alloc(nsei);
286 if (!vnse) {
287 vty_out(vty, "Failed to create vty NSE!%s", VTY_NEWLINE);
288 return CMD_ERR_INCOMPLETE;
289 }
290 free_vnse = true;
291 }
Alexander Couzensda1bf8e2021-01-25 16:27:33 +0100292
293 nse = gprs_ns2_nse_by_nsei(vty_nsi, nsei);
294 if (!nse) {
295 nse = gprs_ns2_create_nse(vty_nsi, nsei, GPRS_NS2_LL_UNDEF, GPRS_NS2_DIALECT_UNDEF);
296 if (!nse) {
297 vty_out(vty, "Failed to create NSE!%s", VTY_NEWLINE);
Alexander Couzens6b9d2322021-02-12 03:17:59 +0100298 goto err;
Alexander Couzensda1bf8e2021-01-25 16:27:33 +0100299 }
300 nse->persistent = true;
301 }
302
303 if (!nse->persistent) {
304 /* TODO: should the dynamic NSE removed? */
305 vty_out(vty, "A dynamic NSE with the specified NSEI already exists%s", VTY_NEWLINE);
Alexander Couzens6b9d2322021-02-12 03:17:59 +0100306 goto err;
Alexander Couzensda1bf8e2021-01-25 16:27:33 +0100307 }
308
309 vty->node = L_NS_NSE_NODE;
310 vty->index = nse;
311
312 return CMD_SUCCESS;
Alexander Couzens6b9d2322021-02-12 03:17:59 +0100313
314err:
315 if (free_vnse)
316 talloc_free(vnse);
317
318 return CMD_ERR_INCOMPLETE;
Alexander Couzensda1bf8e2021-01-25 16:27:33 +0100319}
320
321DEFUN(cfg_no_ns_nsei, cfg_no_ns_nsei_cmd,
322 "no nse <0-65535>",
323 NO_STR
324 "Delete a Persistent NS Entity\n"
325 "NS Entity ID (NSEI)\n"
326 )
327{
328 struct gprs_ns2_nse *nse;
Alexander Couzens6b9d2322021-02-12 03:17:59 +0100329 struct vty_nse *vnse;
Alexander Couzensda1bf8e2021-01-25 16:27:33 +0100330 uint16_t nsei = atoi(argv[0]);
331
332 nse = gprs_ns2_nse_by_nsei(vty_nsi, nsei);
333 if (!nse) {
334 vty_out(vty, "Can not find NS Entity %s%s", argv[0], VTY_NEWLINE);
335 return CMD_ERR_NOTHING_TODO;
336 }
337
338 if (!nse->persistent) {
339 vty_out(vty, "Ignoring non-persistent NS Entity%s", VTY_NEWLINE);
340 return CMD_WARNING;
341 }
342
343 vty_out(vty, "Deleting NS Entity %u%s", nse->nsei, VTY_NEWLINE);
344 gprs_ns2_free_nse(nse);
Alexander Couzens6b9d2322021-02-12 03:17:59 +0100345
346 vnse = vty_nse_by_nsei(nsei);
347 vty_nse_free(vnse);
348
Alexander Couzensda1bf8e2021-01-25 16:27:33 +0100349 return CMD_SUCCESS;
350}
351
352/* TODO: add fr/gre */
353DEFUN(cfg_ns_bind, cfg_ns_bind_cmd,
354 "bind (fr|udp) ID",
355 "Binding\n"
356 "Frame Relay\n" "UDP/IP\n"
357 "a unique identifier for this bind to reference NS-VCs\n"
358 )
359{
360 const char *nstype = argv[0];
361 const char *name = argv[1];
362 struct vty_bind *vbind;
363 enum gprs_ns2_ll ll;
364 int rc;
365
366 rc = get_string_value(vty_ll_names, nstype);
367 if (rc < 0)
368 return CMD_WARNING;
369 ll = (enum gprs_ns2_ll) rc;
370
371 if (!osmo_identifier_valid(name)) {
372 vty_out(vty, "Invalid ID. The ID should be only alphanumeric.%s", VTY_NEWLINE);
373 return CMD_WARNING;
374 }
375
376 vbind = vty_bind_by_name(name);
377 if (vbind) {
378 if (vbind->ll != ll) {
379 vty_out(vty, "A bind with the specified ID already exists with a different type (fr|frgre|udp)!%s",
380 VTY_NEWLINE);
381 return CMD_WARNING;
382 }
383 } else {
384 vbind = vty_bind_alloc(name);
385 if (!vbind) {
386 vty_out(vty, "Can not create bind - out of memory%s", VTY_NEWLINE);
387 return CMD_WARNING;
388 }
389 vbind->ll = ll;
390 }
391
392 vty->index = vbind;
393 vty->node = L_NS_BIND_NODE;
394
395 return CMD_SUCCESS;
396}
397
398DEFUN(cfg_no_ns_bind, cfg_no_ns_bind_cmd,
399 "no bind ID",
400 NO_STR
401 "Delete a binding\n"
402 "a unique identifier for this bind to reference NS-VCs\n"
403 )
404{
405 struct vty_bind *vbind;
406 struct gprs_ns2_vc_bind *bind;
407 const char *name = argv[0];
408
409 vbind = vty_bind_by_name(name);
410 if (!vbind) {
411 vty_out(vty, "bind %s does not exist!%s", name, VTY_NEWLINE);
412 return CMD_WARNING;
413 }
414 vty_bind_free(vbind);
415 bind = gprs_ns2_bind_by_name(vty_nsi, name);
416 if (bind)
417 bind->driver->free_bind(bind);
418 return CMD_SUCCESS;
419}
420
421
422static void config_write_vbind(struct vty *vty, struct vty_bind *vbind)
423{
424 struct gprs_ns2_vc_bind *bind;
425 const struct osmo_sockaddr *addr;
426 struct osmo_sockaddr_str addr_str;
427 const char *netif, *frrole_str, *llstr;
428 enum osmo_fr_role frrole;
429
430 llstr = get_value_string_or_null(vty_ll_names, vbind->ll);
431 if (!llstr)
432 return;
433 vty_out(vty, " bind %s %s%s", llstr, vbind->name, VTY_NEWLINE);
434
435 bind = gprs_ns2_bind_by_name(vty_nsi, vbind->name);
436 switch (vbind->ll) {
437 case GPRS_NS2_LL_FR:
438 if (bind) {
439 netif = gprs_ns2_fr_bind_netif(bind);
440 if (!netif)
441 return;
442 frrole = gprs_ns2_fr_bind_role(bind);
443 if ((int) frrole == -1)
444 return;
445 frrole_str = get_value_string_or_null(vty_fr_role_names, frrole);
446 if (netif && frrole_str)
447 vty_out(vty, " fr %s %s%s", netif, frrole_str, VTY_NEWLINE);
448 }
449 break;
450 case GPRS_NS2_LL_UDP:
451 if (bind) {
452 addr = gprs_ns2_ip_bind_sockaddr(bind);
453 if (!osmo_sockaddr_str_from_sockaddr(&addr_str, &addr->u.sas)) {
454 vty_out(vty, " listen %s %u%s", addr_str.ip, addr_str.port,
455 VTY_NEWLINE);
456 }
457 }
458 if (vbind->accept_ipaccess)
459 vty_out(vty, " accept-ipaccess%s", VTY_NEWLINE);
460 if (vbind->dscp)
461 vty_out(vty, " dscp %u%s", vbind->dscp, VTY_NEWLINE);
Daniel Willmann64db6362021-02-12 12:21:45 +0100462 vty_out(vty, " ip-sns signalling-weight %u data-weight %u%s",
Alexander Couzensc4704762021-02-08 23:13:12 +0100463 vbind->ip_sns_sig_weight, vbind->ip_sns_data_weight, VTY_NEWLINE);
Alexander Couzensda1bf8e2021-01-25 16:27:33 +0100464 break;
465 default:
466 return;
467 }
468}
469
470static void config_write_nsvc(struct vty *vty, const struct gprs_ns2_vc *nsvc)
471{
472 const char *netif;
473 uint16_t dlci;
474 const struct osmo_sockaddr *addr;
475 struct osmo_sockaddr_str addr_str;
476
477 switch (nsvc->nse->ll) {
478 case GPRS_NS2_LL_UNDEF:
479 break;
480 case GPRS_NS2_LL_UDP:
481 switch (nsvc->nse->dialect) {
482 case GPRS_NS2_DIALECT_IPACCESS:
483 addr = gprs_ns2_ip_vc_remote(nsvc);
484 if (!addr)
485 break;
486 if (osmo_sockaddr_str_from_sockaddr(&addr_str, &addr->u.sas))
487 break;
488 vty_out(vty, " nsvc ipa %s %s %u nsvci %u%s",
489 nsvc->bind->name, addr_str.ip, addr_str.port,
490 nsvc->nsvci, VTY_NEWLINE);
491 break;
492 case GPRS_NS2_DIALECT_STATIC_ALIVE:
493 addr = gprs_ns2_ip_vc_remote(nsvc);
494 if (!addr)
495 break;
496 if (osmo_sockaddr_str_from_sockaddr(&addr_str, &addr->u.sas))
497 break;
498 vty_out(vty, " nsvc udp %s %s %u%s",
499 nsvc->bind->name, addr_str.ip, addr_str.port, VTY_NEWLINE);
500 break;
501 default:
502 break;
503 }
504 break;
505 case GPRS_NS2_LL_FR:
506 netif = gprs_ns2_fr_bind_netif(nsvc->bind);
507 if (!netif)
508 break;
509 dlci = gprs_ns2_fr_nsvc_dlci(nsvc);
510 if (!dlci)
511 break;
512 OSMO_ASSERT(nsvc->nsvci_is_valid);
513 vty_out(vty, " nsvc fr %s dlci %u nsvci %u%s",
514 netif, dlci, nsvc->nsvci, VTY_NEWLINE);
515 break;
516 case GPRS_NS2_LL_FR_GRE:
517 break;
518 }
519}
520
521static void _config_write_ns_nse(struct vty *vty, struct gprs_ns2_nse *nse)
522{
523 struct gprs_ns2_vc *nsvc;
Alexander Couzens6b9d2322021-02-12 03:17:59 +0100524 struct vty_nse *vnse = vty_nse_by_nsei(nse->nsei);
525 struct vty_nse_bind *vbind;
526
527 OSMO_ASSERT(vnse);
Alexander Couzensda1bf8e2021-01-25 16:27:33 +0100528
529 vty_out(vty, " nse %u%s", nse->nsei, VTY_NEWLINE);
530 switch (nse->dialect) {
531 case GPRS_NS2_DIALECT_SNS:
532 ns2_sns_write_vty(vty, nse);
Alexander Couzens6b9d2322021-02-12 03:17:59 +0100533 llist_for_each_entry(vbind, &vnse->binds, list) {
534 vty_out(vty, " ip-sns-bind %s%s", vbind->vbind->name, VTY_NEWLINE);
535 }
Alexander Couzensda1bf8e2021-01-25 16:27:33 +0100536 break;
537 default:
538 llist_for_each_entry(nsvc, &nse->nsvc, list) {
539 config_write_nsvc(vty, nsvc);
540 }
541 break;
542 }
543}
544
Alexander Couzensda1bf8e2021-01-25 16:27:33 +0100545static int config_write_ns_nse(struct vty *vty)
546{
547 struct gprs_ns2_nse *nse;
548
549 llist_for_each_entry(nse, &vty_nsi->nse, list) {
550 if (!nse->persistent)
551 continue;
552
553 _config_write_ns_nse(vty, nse);
554 }
555
556 return 0;
557}
558
559static int config_write_ns_bind(struct vty *vty)
560{
561 struct vty_bind *vbind;
562
563 llist_for_each_entry(vbind, &binds, list) {
564 config_write_vbind(vty, vbind);
565 }
566
567 return 0;
568}
569
Alexander Couzens260cd522021-01-28 20:31:31 +0100570static int config_write_ns(struct vty *vty)
571{
572 unsigned int i;
573 int ret;
574
575 vty_out(vty, "ns%s", VTY_NEWLINE);
576
577 for (i = 0; i < ARRAY_SIZE(vty_nsi->timeout); i++)
578 vty_out(vty, " timer %s %u%s",
579 get_value_string(gprs_ns_timer_strs, i),
580 vty_nsi->timeout[i], VTY_NEWLINE);
581
582 ret = config_write_ns_bind(vty);
583 if (ret)
584 return ret;
585
586 ret = config_write_ns_nse(vty);
587 if (ret)
588 return ret;
589
590 return 0;
591}
592
593
Alexander Couzensda1bf8e2021-01-25 16:27:33 +0100594static struct cmd_node ns_bind_node = {
595 L_NS_BIND_NODE,
596 "%s(config-ns-bind)# ",
597 1,
598};
599
600DEFUN(cfg_ns_bind_listen, cfg_ns_bind_listen_cmd,
601 "listen " VTY_IPV46_CMD " <1-65535>",
602 "Binding\n"
603 "IPv4 Address\n" "IPv6 Address\n"
604 "Port\n"
605 )
606{
607 struct vty_bind *vbind = vty->index;
608 struct gprs_ns2_vc_bind *bind;
Alexander Couzens6b9d2322021-02-12 03:17:59 +0100609 int rc;
Alexander Couzensda1bf8e2021-01-25 16:27:33 +0100610 const char *addr_str = argv[0];
611 unsigned int port = atoi(argv[1]);
612 struct osmo_sockaddr_str sockaddr_str;
613 struct osmo_sockaddr sockaddr;
614
615 if (vbind->ll != GPRS_NS2_LL_UDP) {
616 vty_out(vty, "listen can be only used with UDP bind%s",
617 VTY_NEWLINE);
618 return CMD_WARNING;
619 }
620
621 if (osmo_sockaddr_str_from_str(&sockaddr_str, addr_str, port)) {
622 vty_out(vty, "Can not parse the Address %s %s%s", argv[0], argv[1], VTY_NEWLINE);
623 return CMD_WARNING;
624 }
625 osmo_sockaddr_str_to_sockaddr(&sockaddr_str, &sockaddr.u.sas);
626 if (gprs_ns2_ip_bind_by_sockaddr(vty_nsi, &sockaddr)) {
627 vty_out(vty, "A bind with the specified address already exists!%s", VTY_NEWLINE);
628 return CMD_WARNING;
629 }
630
Alexander Couzens6b9d2322021-02-12 03:17:59 +0100631 rc = gprs_ns2_ip_bind(vty_nsi, vbind->name, &sockaddr, vbind->dscp, &bind);
632 if (rc != 0) {
633 vty_out(vty, "Failed to create the bind (rc %d)!%s", rc, VTY_NEWLINE);
Alexander Couzensda1bf8e2021-01-25 16:27:33 +0100634 return CMD_WARNING;
635 }
636
637 bind->accept_ipaccess = vbind->accept_ipaccess;
638 bind->accept_sns = vbind->accept_sns;
639
640 return CMD_SUCCESS;
641}
642
643DEFUN(cfg_no_ns_bind_listen, cfg_no_ns_bind_listen_cmd,
644 "no listen",
645 NO_STR
646 "Delete a IP/Port assignment\n"
647 )
648{
649 struct vty_bind *vbind = vty->index;
650 struct gprs_ns2_vc_bind *bind;
651
652 if (vbind->ll != GPRS_NS2_LL_UDP) {
653 vty_out(vty, "no listen can be only used with UDP bind%s", VTY_NEWLINE);
654 return CMD_WARNING;
655 }
656
657 bind = gprs_ns2_bind_by_name(vty_nsi, vbind->name);
658 if (!bind)
659 return CMD_ERR_NOTHING_TODO;
660
Daniel Willmann90432052021-01-26 16:09:18 +0100661 OSMO_ASSERT(bind->ll == GPRS_NS2_LL_UDP);
Alexander Couzensda1bf8e2021-01-25 16:27:33 +0100662 bind->driver->free_bind(bind);
663 return CMD_SUCCESS;
664}
665
666DEFUN(cfg_ns_bind_dscp, cfg_ns_bind_dscp_cmd,
667 "dscp <0-255>",
668 "Set DSCP/TOS on the UDP socket\n" "DSCP Value\n")
669{
670 struct vty_bind *vbind = vty->index;
671 struct gprs_ns2_vc_bind *bind;
672 uint16_t dscp = atoi(argv[0]);
673
674 if (vbind->ll != GPRS_NS2_LL_UDP) {
675 vty_out(vty, "dscp can be only used with UDP bind%s",
676 VTY_NEWLINE);
677 return CMD_WARNING;
678 }
679
680 vbind->dscp = dscp;
681 bind = gprs_ns2_bind_by_name(vty_nsi, vbind->name);
682 if (bind)
683 gprs_ns2_ip_bind_set_dscp(bind, dscp);
684
685 return CMD_SUCCESS;
686}
687
688DEFUN(cfg_no_ns_bind_dscp, cfg_no_ns_bind_dscp_cmd,
689 "no dscp",
690 "Set DSCP/TOS on the UDP socket\n" "DSCP Value\n")
691{
692 struct vty_bind *vbind = vty->index;
693 struct gprs_ns2_vc_bind *bind;
694 uint16_t dscp = 0;
695
696 if (vbind->ll != GPRS_NS2_LL_UDP) {
697 vty_out(vty, "dscp can be only used with UDP bind%s",
698 VTY_NEWLINE);
699 return CMD_WARNING;
700 }
701
702 vbind->dscp = dscp;
703 bind = gprs_ns2_bind_by_name(vty_nsi, vbind->name);
704 if (bind)
705 gprs_ns2_ip_bind_set_dscp(bind, dscp);
706
707 return CMD_SUCCESS;
708}
709
710DEFUN(cfg_ns_bind_ipaccess, cfg_ns_bind_ipaccess_cmd,
711 "accept-ipaccess",
712 "Allow to create dynamic NS Entity by NS Reset PDU on UDP (ip.access style)\n"
713 )
714{
715 struct vty_bind *vbind = vty->index;
716 struct gprs_ns2_vc_bind *bind;
717
718 if (vbind->ll != GPRS_NS2_LL_UDP) {
719 vty_out(vty, "accept-ipaccess can be only used with UDP bind%s",
720 VTY_NEWLINE);
721 return CMD_WARNING;
722 }
723
724 vbind->accept_ipaccess = true;
725 bind = gprs_ns2_bind_by_name(vty_nsi, vbind->name);
726 if (bind)
727 bind->accept_ipaccess = true;
728
729 return CMD_SUCCESS;
730}
731
732DEFUN(cfg_no_ns_bind_ipaccess, cfg_no_ns_bind_ipaccess_cmd,
733 "no accept-ipaccess",
734 NO_STR
735 "Reject NS Reset PDU on UDP (ip.access style)\n"
736 )
737{
738 struct vty_bind *vbind = vty->index;
739 struct gprs_ns2_vc_bind *bind;
740
741 if (vbind->ll != GPRS_NS2_LL_UDP) {
742 vty_out(vty, "no accept-ipaccess can be only used with UDP bind%s",
743 VTY_NEWLINE);
744 return CMD_WARNING;
745 }
746
747 vbind->accept_ipaccess = false;
748 bind = gprs_ns2_bind_by_name(vty_nsi, vbind->name);
749 if (bind)
750 bind->accept_ipaccess = false;
751
752 return CMD_SUCCESS;
753}
754
Alexander Couzensc4704762021-02-08 23:13:12 +0100755DEFUN(cfg_ns_bind_ip_sns_weight, cfg_ns_bind_ip_sns_weight_cmd,
756 "ip-sns signalling-weight <0-254> data-weight <0-254>",
757 "IP SNS\n"
758 "signalling weight used by IP-SNS dynamic configuration\n"
759 "signalling weight used by IP-SNS dynamic configuration\n"
760 "data weight used by IP-SNS dynamic configuration\n"
761 "data weight used by IP-SNS dynamic configuration\n")
762{
763 struct vty_bind *vbind = vty->index;
764 struct gprs_ns2_vc_bind *bind;
765
766 int signalling = atoi(argv[0]);
767 int data = atoi(argv[1]);
768
769 if (vbind->ll != GPRS_NS2_LL_UDP) {
770 vty_out(vty, "ip-sns signalling-weight <0-254> data-weight <0-254> can be only used with UDP bind%s",
771 VTY_NEWLINE);
772 return CMD_WARNING;
773 }
774
775 vbind->ip_sns_data_weight = data;
776 vbind->ip_sns_sig_weight = signalling;
777 bind = gprs_ns2_bind_by_name(vty_nsi, vbind->name);
778 if (bind)
779 gprs_ns2_ip_bind_set_sns_weight(bind, signalling, data);
780
781 return CMD_SUCCESS;
782}
Alexander Couzensda1bf8e2021-01-25 16:27:33 +0100783
784DEFUN(cfg_ns_bind_fr, cfg_ns_bind_fr_cmd,
785 "fr NETIF (fr|frnet)",
786 "frame relay\n"
787 IFNAME_STR
788 "fr (user) is used by BSS or SGSN attached to UNI of a FR network\n"
789 "frnet (network) is used by SGSN if BSS is directly attached\n"
790 )
791{
792 struct vty_bind *vbind = vty->index;
793 struct gprs_ns2_vc_bind *bind;
794 const char *netif = argv[0];
795 const char *role = argv[1];
796
797 int rc = 0;
798 enum osmo_fr_role frrole;
799
800 if (vbind->ll != GPRS_NS2_LL_FR) {
801 vty_out(vty, "fr can be only used with frame relay bind%s", VTY_NEWLINE);
802 return CMD_WARNING;
803 }
804
805 if (!strcmp(role, "fr"))
806 frrole = FR_ROLE_USER_EQUIPMENT;
807 else if (!strcmp(role, "frnet"))
808 frrole = FR_ROLE_NETWORK_EQUIPMENT;
809 else
810 return CMD_WARNING;
811
812 bind = gprs_ns2_fr_bind_by_netif(vty_nsi, netif);
813 if (bind) {
814 vty_out(vty, "Interface %s already used.%s", netif, VTY_NEWLINE);
815 return CMD_WARNING;
816 }
817
818 rc = gprs_ns2_fr_bind(vty_nsi, vbind->name, netif, vty_fr_network, frrole, &bind);
819 if (rc < 0) {
820 LOGP(DLNS, LOGL_ERROR, "Failed to bind interface %s on fr. Err: %d\n", netif, rc);
821 return CMD_WARNING;
822 }
823
824 return CMD_SUCCESS;
825}
826
827DEFUN(cfg_no_ns_bind_fr, cfg_no_ns_bind_fr_cmd,
828 "no fr NETIF",
829 NO_STR
830 "Delete a frame relay link\n"
831 "Delete a frame relay link\n"
832 IFNAME_STR
833 )
834{
835 struct vty_bind *vbind = vty->index;
836 struct gprs_ns2_vc_bind *bind;
837 const char *netif = argv[0];
838
839 if (vbind->ll != GPRS_NS2_LL_FR) {
840 vty_out(vty, "fr can be only used with frame relay bind%s",
841 VTY_NEWLINE);
842 return CMD_WARNING;
843 }
844
845 bind = gprs_ns2_fr_bind_by_netif(vty_nsi, netif);
846 if (!bind) {
847 vty_out(vty, "Interface not found.%s", VTY_NEWLINE);
848 return CMD_WARNING;
849 }
850
851 if (strcmp(bind->name, vbind->name)) {
852 vty_out(vty, "The specified interface is not bound to this bind.%s", VTY_NEWLINE);
853 return CMD_WARNING;
854 }
855
856 bind->driver->free_bind(bind);
857 return CMD_SUCCESS;
858}
859
860
861static struct cmd_node ns_nse_node = {
862 L_NS_NSE_NODE,
863 "%s(config-ns-nse)# ",
864 1,
865};
866
867DEFUN(cfg_ns_nse_nsvc_fr, cfg_ns_nse_nsvc_fr_cmd,
868 "nsvc fr NETIF dlci <16-1007> nsvci <0-65535>",
869 "NS Virtual Connection\n"
870 "frame relay\n"
871 "frame relay interface. Must be registered via fr vty\n"
872 NSVCI_STR
873 NSVCI_STR
874 DLCI_STR
875 DLCI_STR
876 )
877{
878 struct gprs_ns2_vc_bind *bind;
879 struct gprs_ns2_vc *nsvc;
880 struct gprs_ns2_nse *nse = vty->index;
881 const char *netif = argv[0];
882 uint16_t dlci = atoi(argv[1]);
883 uint16_t nsvci = atoi(argv[2]);
884 bool dialect_modified = false;
885 bool ll_modified = false;
886
887 if (nse->ll != GPRS_NS2_LL_FR && nse->ll != GPRS_NS2_LL_UNDEF) {
888 vty_out(vty, "Can not mix NS-VC with different link layer%s", VTY_NEWLINE);
889 goto err;
890 }
891
892 if (nse->dialect != GPRS_NS2_DIALECT_STATIC_RESETBLOCK && nse->dialect != GPRS_NS2_DIALECT_UNDEF) {
893 vty_out(vty, "Can not mix NS-VC with different dialects%s", VTY_NEWLINE);
894 goto err;
895 }
896
897 if (nse->ll == GPRS_NS2_LL_UNDEF) {
898 nse->ll = GPRS_NS2_LL_FR;
899 ll_modified = true;
900 }
901
902 if (nse->dialect == GPRS_NS2_DIALECT_UNDEF) {
903 nse->dialect = GPRS_NS2_DIALECT_STATIC_RESETBLOCK;
904 dialect_modified = true;
905 }
906
907
908 bind = gprs_ns2_fr_bind_by_netif(vty_nsi, netif);
909 if (!bind) {
910 vty_out(vty, "Can not find fr interface \"%s\". Please configure it via fr vty.%s",
911 netif, VTY_NEWLINE);
912 goto err;
913 }
914
915 if (gprs_ns2_fr_nsvc_by_dlci(bind, dlci)) {
916 vty_out(vty, "A NS-VC with the specified DLCI already exist!%s", VTY_NEWLINE);
917 goto err;
918 }
919
920 if (gprs_ns2_nsvc_by_nsvci(vty_nsi, nsvci)) {
921 vty_out(vty, "A NS-VC with the specified NS-VCI already exist!%s", VTY_NEWLINE);
922 goto err;
923 }
924
925 nsvc = gprs_ns2_fr_connect(bind, nse, nsvci, dlci);
926 if (!nsvc) {
927 /* Could not create NS-VC, connect failed */
928 vty_out(vty, "Failed to create the NS-VC%s", VTY_NEWLINE);
929 goto err;
930 }
931 nsvc->persistent = true;
932 return CMD_SUCCESS;
933
934err:
935 if (ll_modified)
936 nse->ll = GPRS_NS2_LL_UNDEF;
937 if (dialect_modified)
938 nse->dialect = GPRS_NS2_DIALECT_UNDEF;
939
940 return CMD_WARNING;
941}
942
943DEFUN(cfg_no_ns_nse_nsvc_fr_dlci, cfg_no_ns_nse_nsvc_fr_dlci_cmd,
944 "no nsvc fr NETIF dlci <16-1007>",
945 NO_STR
946 "Delete frame relay NS-VC\n"
947 "frame relay\n"
948 "frame relay interface. Must be registered via fr vty\n"
949 DLCI_STR
950 DLCI_STR
951 )
952{
953 struct gprs_ns2_vc_bind *bind;
954 struct gprs_ns2_vc *nsvc;
955 struct gprs_ns2_nse *nse = vty->index;
956 const char *netif = argv[0];
957 uint16_t dlci = atoi(argv[1]);
958
959 if (nse->ll != GPRS_NS2_LL_FR) {
960 vty_out(vty, "This NSE doesn't support frame relay.%s", VTY_NEWLINE);
961 return CMD_WARNING;
962 }
963
964 bind = gprs_ns2_fr_bind_by_netif(vty_nsi, netif);
965 if (!bind) {
966 vty_out(vty, "Can not find fr interface \"%s\"%s",
967 netif, VTY_NEWLINE);
968 return CMD_ERR_NOTHING_TODO;
969 }
970
971 nsvc = gprs_ns2_fr_nsvc_by_dlci(bind, dlci);
972 if (!nsvc) {
973 vty_out(vty, "Can not find a NS-VC on fr interface %s with dlci %u%s",
974 netif, dlci, VTY_NEWLINE);
975 return CMD_WARNING;
976 }
977
978 if (nse != nsvc->nse) {
979 vty_out(vty, "The specified NS-VC is not a part of the NSE %u!%s"
980 "To remove this NS-VC go to the vty node 'nse %u'%s",
981 nse->nsei, VTY_NEWLINE,
982 nsvc->nse->nsei, VTY_NEWLINE);
983 return CMD_WARNING;
984 }
985
986 gprs_ns2_free_nsvc(nsvc);
987 if (llist_empty(&nse->nsvc)) {
988 nse->ll = GPRS_NS2_LL_UNDEF;
989 nse->dialect = GPRS_NS2_DIALECT_UNDEF;
990 }
991
992 return CMD_SUCCESS;
993}
994
995DEFUN(cfg_no_ns_nse_nsvci, cfg_no_ns_nse_nsvci_cmd,
996 "no nsvc nsvci <0-65535>",
997 NO_STR
998 "Delete NSVC\n"
999 NSVCI_STR
1000 NSVCI_STR
1001 )
1002{
1003 struct gprs_ns2_vc *nsvc;
1004 struct gprs_ns2_nse *nse = vty->index;
1005 uint16_t nsvci = atoi(argv[0]);
1006
1007 switch (nse->dialect) {
1008 case GPRS_NS2_DIALECT_SNS:
1009 case GPRS_NS2_DIALECT_STATIC_ALIVE:
1010 vty_out(vty, "NSE doesn't support NSVCI.%s", VTY_NEWLINE);
1011 return CMD_WARNING;
1012 case GPRS_NS2_DIALECT_UNDEF:
1013 vty_out(vty, "No NSVCs configured%s", VTY_NEWLINE);
1014 return CMD_WARNING;
1015 case GPRS_NS2_DIALECT_IPACCESS:
1016 case GPRS_NS2_DIALECT_STATIC_RESETBLOCK:
1017 break;
1018 }
1019
1020 nsvc = gprs_ns2_nsvc_by_nsvci(vty_nsi, nsvci);
1021 if (!nsvc) {
1022 vty_out(vty, "Can not find NS-VC with NS-VCI %u%s", nsvci, VTY_NEWLINE);
1023 return CMD_WARNING;
1024 }
1025
1026 if (nse != nsvc->nse) {
1027 vty_out(vty, "NS-VC with NS-VCI %u is not part of this NSE!%s",
1028 nsvci, VTY_NEWLINE);
1029 return CMD_WARNING;
1030 }
1031
1032 gprs_ns2_free_nsvc(nsvc);
1033 if (llist_empty(&nse->nsvc)) {
1034 nse->ll = GPRS_NS2_LL_UNDEF;
1035 nse->dialect = GPRS_NS2_DIALECT_UNDEF;
1036 }
1037
1038 return CMD_SUCCESS;
1039}
1040
Alexander Couzensbf5d0db2021-02-12 04:04:13 +01001041static int ns_nse_nsvc_udp_cmds(struct vty *vty, const char *bind_name, const char *remote_char, uint16_t port,
1042 uint16_t sig_weight, uint16_t data_weight)
Alexander Couzensda1bf8e2021-01-25 16:27:33 +01001043{
1044 struct gprs_ns2_vc_bind *bind;
1045 struct gprs_ns2_vc *nsvc;
1046 struct gprs_ns2_nse *nse = vty->index;
1047 bool dialect_modified = false;
1048 bool ll_modified = false;
1049
Alexander Couzensda1bf8e2021-01-25 16:27:33 +01001050 struct osmo_sockaddr_str remote_str;
1051 struct osmo_sockaddr remote;
Alexander Couzensda1bf8e2021-01-25 16:27:33 +01001052
1053 if (nse->ll == GPRS_NS2_LL_UNDEF) {
1054 nse->ll = GPRS_NS2_LL_UDP;
1055 ll_modified = true;
1056 }
1057
1058 if (nse->dialect == GPRS_NS2_DIALECT_UNDEF) {
1059 nse->dialect = GPRS_NS2_DIALECT_STATIC_ALIVE;
1060 dialect_modified = true;
1061 }
1062
1063 if (nse->ll != GPRS_NS2_LL_UDP) {
1064 vty_out(vty, "Can not mix NS-VC with different link layer%s", VTY_NEWLINE);
1065 goto err;
1066 }
1067
1068 if (nse->dialect != GPRS_NS2_DIALECT_STATIC_ALIVE) {
1069 vty_out(vty, "Can not mix NS-VC with different dialects%s", VTY_NEWLINE);
1070 goto err;
1071 }
1072
Alexander Couzensbf5d0db2021-02-12 04:04:13 +01001073 if (osmo_sockaddr_str_from_str(&remote_str, remote_char, port)) {
Alexander Couzensda1bf8e2021-01-25 16:27:33 +01001074 vty_out(vty, "Can not parse IPv4/IPv6 or port.%s", VTY_NEWLINE);
1075 goto err;
1076 }
1077
1078 if (osmo_sockaddr_str_to_sockaddr(&remote_str, &remote.u.sas)) {
1079 vty_out(vty, "Can not parse IPv4/IPv6 or port.%s", VTY_NEWLINE);
1080 goto err;
1081 }
1082
1083 bind = gprs_ns2_bind_by_name(vty_nsi, bind_name);
1084 if (!bind) {
1085 vty_out(vty, "Can not find bind with name %s%s",
1086 bind_name, VTY_NEWLINE);
1087 goto err;
1088 }
1089
1090 if (bind->ll != GPRS_NS2_LL_UDP) {
1091 vty_out(vty, "Bind %s is not an UDP bind.%s",
1092 bind_name, VTY_NEWLINE);
1093 goto err;
1094 }
1095
Alexander Couzens7bb39e32021-02-16 23:06:53 +01001096 nsvc = gprs_ns2_nsvc_by_sockaddr_bind(bind, &remote);
1097 if (nsvc) {
1098 if (nsvc->nse == nse)
1099 vty_out(vty, "Specified NSVC is already present in this NSE.%s", VTY_NEWLINE);
1100 else
1101 vty_out(vty, "Specified NSVC is already present in another NSE%05u.%s", nsvc->nse->nsei, VTY_NEWLINE);
1102 goto err;
1103 }
1104
Alexander Couzensda1bf8e2021-01-25 16:27:33 +01001105 nsvc = gprs_ns2_ip_connect(bind, &remote, nse, 0);
1106 if (!nsvc) {
1107 vty_out(vty, "Can not create NS-VC.%s", VTY_NEWLINE);
1108 goto err;
1109 }
Alexander Couzensbf5d0db2021-02-12 04:04:13 +01001110 nsvc->sig_weight = sig_weight;
1111 nsvc->data_weight = data_weight;
Alexander Couzensda1bf8e2021-01-25 16:27:33 +01001112 nsvc->persistent = true;
1113
1114 return CMD_SUCCESS;
1115
1116err:
1117 if (ll_modified)
1118 nse->ll = GPRS_NS2_LL_UNDEF;
1119 if (dialect_modified)
1120 nse->dialect = GPRS_NS2_DIALECT_UNDEF;
1121 return CMD_WARNING;
1122}
1123
Alexander Couzensbf5d0db2021-02-12 04:04:13 +01001124DEFUN(cfg_ns_nse_nsvc_udp, cfg_ns_nse_nsvc_udp_cmd,
1125 "nsvc udp BIND " VTY_IPV46_CMD " <1-65535>",
1126 "NS Virtual Connection\n"
1127 "NS over UDP\n"
1128 "A unique bind identifier created by ns bind\n"
1129 "Remote IPv4 Address\n" "Remote IPv6 Address\n"
1130 "Remote UDP Port\n")
1131{
1132 const char *bind_name = argv[0];
1133 const char *remote = argv[1];
1134 uint16_t port = atoi(argv[2]);
1135 uint16_t sig_weight = 1;
1136 uint16_t data_weight = 1;
1137
1138 return ns_nse_nsvc_udp_cmds(vty, bind_name, remote, port, sig_weight, data_weight);
1139}
1140
1141DEFUN(cfg_ns_nse_nsvc_udp_weights, cfg_ns_nse_nsvc_udp_weights_cmd,
1142 "nsvc udp BIND " VTY_IPV46_CMD " <1-65535> signalling-weight <0-254> data-weight <0-254>",
1143 "NS Virtual Connection\n"
1144 "NS over UDP\n"
1145 "A unique bind identifier created by ns bind\n"
1146 "Remote IPv4 Address\n" "Remote IPv6 Address\n"
1147 "Remote UDP Port\n"
1148 "Signalling weight of the NSVC (default = 1)\n"
1149 "Signalling weight of the NSVC (default = 1)\n"
1150 "Data weight of the NSVC (default = 1)\n"
1151 "Data weight of the NSVC (default = 1)\n"
1152 )
1153{
1154 const char *bind_name = argv[0];
1155 const char *remote = argv[1];
1156 uint16_t port = atoi(argv[2]);
1157 uint16_t sig_weight = atoi(argv[3]);
1158 uint16_t data_weight = atoi(argv[4]);
1159
1160 return ns_nse_nsvc_udp_cmds(vty, bind_name, remote, port, sig_weight, data_weight);
1161}
1162
Alexander Couzensda1bf8e2021-01-25 16:27:33 +01001163DEFUN(cfg_no_ns_nse_nsvc_udp, cfg_no_ns_nse_nsvc_udp_cmd,
1164 "no nsvc udp BIND " VTY_IPV46_CMD " <1-65535>",
1165 NO_STR
1166 "Delete a NS Virtual Connection\n"
1167 "NS over UDP\n"
1168 "A unique bind identifier created by ns bind\n"
1169 "Remote IPv4 Address\n" "Remote IPv6 Address\n"
1170 "Remote UDP Port\n"
1171 )
1172{
1173 struct gprs_ns2_vc_bind *bind;
1174 struct gprs_ns2_vc *nsvc;
1175 struct gprs_ns2_nse *nse = vty->index;
1176 const char *bind_name = argv[0];
1177 struct osmo_sockaddr_str remote_str;
1178 struct osmo_sockaddr remote;
1179 uint16_t port = atoi(argv[2]);
1180
1181 if (nse->ll != GPRS_NS2_LL_UDP) {
1182 vty_out(vty, "This NSE doesn't support UDP.%s", VTY_NEWLINE);
1183 return CMD_WARNING;
1184 }
1185
1186 if (nse->dialect != GPRS_NS2_DIALECT_STATIC_ALIVE) {
1187 vty_out(vty, "This NSE doesn't support UDP with dialect static alive.%s", VTY_NEWLINE);
1188 return CMD_WARNING;
1189 }
1190
1191 bind = gprs_ns2_bind_by_name(vty_nsi, bind_name);
1192 if (!bind) {
1193 vty_out(vty, "Can not find bind with name %s%s",
1194 bind_name, VTY_NEWLINE);
1195 return CMD_WARNING;
1196 }
1197
1198 if (bind->ll != GPRS_NS2_LL_UDP) {
1199 vty_out(vty, "Bind %s is not an UDP bind.%s",
1200 bind_name, VTY_NEWLINE);
1201 return CMD_WARNING;
1202 }
1203
1204 if (osmo_sockaddr_str_from_str(&remote_str, argv[1], port)) {
1205 vty_out(vty, "Can not parse IPv4/IPv6 or port.%s", VTY_NEWLINE);
1206 return CMD_WARNING;
1207 }
1208
1209 if (osmo_sockaddr_str_to_sockaddr(&remote_str, &remote.u.sas)) {
1210 vty_out(vty, "Can not parse IPv4/IPv6 or port.%s", VTY_NEWLINE);
1211 return CMD_WARNING;
1212 }
1213
1214 nsvc = gprs_ns2_nsvc_by_sockaddr_bind(bind, &remote);
1215 if (!nsvc) {
1216 vty_out(vty, "Can not find NS-VC with remote %s:%u%s",
1217 remote_str.ip, remote_str.port, VTY_NEWLINE);
1218 return CMD_WARNING;
1219 }
1220
1221 if (!nsvc->persistent) {
1222 vty_out(vty, "NS-VC with remote %s:%u is a dynamic NS-VC. Not configured by vty.%s",
1223 remote_str.ip, remote_str.port, VTY_NEWLINE);
1224 return CMD_WARNING;
1225 }
1226
1227 if (nsvc->nse != nse) {
1228 vty_out(vty, "NS-VC is not part of this NSE!%s", VTY_NEWLINE);
1229 return CMD_WARNING;
1230 }
1231
1232 gprs_ns2_free_nsvc(nsvc);
1233 if (llist_empty(&nse->nsvc)) {
1234 nse->ll = GPRS_NS2_LL_UNDEF;
1235 nse->dialect = GPRS_NS2_DIALECT_UNDEF;
1236 }
1237
1238 return CMD_SUCCESS;
1239}
1240
1241DEFUN(cfg_ns_nse_nsvc_ipa, cfg_ns_nse_nsvc_ipa_cmd,
1242 "nsvc ipa BIND " VTY_IPV46_CMD " <1-65535> nsvci <0-65535>" ,
1243 "NS Virtual Connection\n"
1244 "NS over UDP ip.access style (uses RESET/BLOCK)\n"
1245 "A unique bind identifier created by ns bind\n"
1246 "Remote IPv4 Address\n" "Remote IPv6 Address\n"
1247 "Remote UDP Port\n"
1248 NSVCI_STR
1249 NSVCI_STR
1250 )
1251{
1252 struct gprs_ns2_vc_bind *bind;
1253 struct gprs_ns2_vc *nsvc;
1254 struct gprs_ns2_nse *nse = vty->index;
1255 bool dialect_modified = false;
1256 bool ll_modified = false;
1257
1258 const char *bind_name = argv[0];
1259 struct osmo_sockaddr_str remote_str;
1260 struct osmo_sockaddr remote;
1261 uint16_t port = atoi(argv[2]);
1262 uint16_t nsvci = atoi(argv[3]);
1263
1264 if (nse->ll == GPRS_NS2_LL_UNDEF) {
1265 nse->ll = GPRS_NS2_LL_UDP;
1266 ll_modified = true;
1267 }
1268
1269 if (nse->dialect == GPRS_NS2_DIALECT_UNDEF) {
1270 nse->dialect = GPRS_NS2_DIALECT_IPACCESS;
1271 dialect_modified = true;
1272 }
1273
1274 if (nse->ll != GPRS_NS2_LL_UDP) {
1275 vty_out(vty, "Can not mix NS-VC with different link layer%s", VTY_NEWLINE);
1276 goto err;
1277 }
1278
1279 if (nse->dialect != GPRS_NS2_DIALECT_IPACCESS) {
1280 vty_out(vty, "Can not mix NS-VC with different dialects%s", VTY_NEWLINE);
1281 goto err;
1282 }
1283
1284 if (osmo_sockaddr_str_from_str(&remote_str, argv[1], port)) {
1285 vty_out(vty, "Can not parse IPv4/IPv6 or port.%s", VTY_NEWLINE);
1286 goto err;
1287 }
1288
1289 if (osmo_sockaddr_str_to_sockaddr(&remote_str, &remote.u.sas)) {
1290 vty_out(vty, "Can not parse IPv4/IPv6 or port.%s", VTY_NEWLINE);
1291 goto err;
1292 }
1293
1294 bind = gprs_ns2_bind_by_name(vty_nsi, bind_name);
1295 if (!bind) {
1296 vty_out(vty, "Can not find bind with name %s%s",
1297 bind_name, VTY_NEWLINE);
1298 goto err;
1299 }
1300
1301 if (bind->ll != GPRS_NS2_LL_UDP) {
1302 vty_out(vty, "Bind %s is not an UDP bind.%s",
1303 bind_name, VTY_NEWLINE);
1304 goto err;
1305 }
1306
1307 nsvc = gprs_ns2_ip_connect(bind, &remote, nse, nsvci);
1308 if (!nsvc) {
1309 vty_out(vty, "Can not create NS-VC.%s", VTY_NEWLINE);
1310 goto err;
1311 }
1312 nsvc->persistent = true;
1313
1314 return CMD_SUCCESS;
1315
1316err:
1317 if (ll_modified)
1318 nse->ll = GPRS_NS2_LL_UNDEF;
1319 if (dialect_modified)
1320 nse->dialect = GPRS_NS2_DIALECT_UNDEF;
1321 return CMD_WARNING;
1322}
1323
1324DEFUN(cfg_no_ns_nse_nsvc_ipa, cfg_no_ns_nse_nsvc_ipa_cmd,
1325 "no nsvc ipa BIND " VTY_IPV46_CMD " <1-65535> nsvci <0-65535>",
1326 NO_STR
1327 "Delete a NS Virtual Connection\n"
1328 "NS over UDP\n"
1329 "A unique bind identifier created by ns bind\n"
1330 "Remote IPv4 Address\n" "Remote IPv6 Address\n"
1331 "Remote UDP Port\n"
1332 NSVCI_STR
1333 NSVCI_STR
1334 )
1335{
1336 struct gprs_ns2_vc_bind *bind;
1337 struct gprs_ns2_vc *nsvc;
1338 struct gprs_ns2_nse *nse = vty->index;
1339 const char *bind_name = argv[0];
1340 struct osmo_sockaddr_str remote_str;
1341 struct osmo_sockaddr remote;
1342 uint16_t port = atoi(argv[2]);
1343 uint16_t nsvci = atoi(argv[3]);
1344
1345 if (nse->ll != GPRS_NS2_LL_UDP) {
1346 vty_out(vty, "This NSE doesn't support UDP.%s", VTY_NEWLINE);
1347 return CMD_WARNING;
1348 }
1349
1350 if (nse->dialect != GPRS_NS2_DIALECT_IPACCESS) {
1351 vty_out(vty, "This NSE doesn't support UDP with dialect ipaccess.%s", VTY_NEWLINE);
1352 return CMD_WARNING;
1353 }
1354
1355 bind = gprs_ns2_bind_by_name(vty_nsi, bind_name);
1356 if (!bind) {
1357 vty_out(vty, "Can not find bind with name %s%s",
1358 bind_name, VTY_NEWLINE);
1359 return CMD_WARNING;
1360 }
1361
1362 if (bind->ll != GPRS_NS2_LL_UDP) {
1363 vty_out(vty, "Bind %s is not an UDP bind.%s",
1364 bind_name, VTY_NEWLINE);
1365 return CMD_WARNING;
1366 }
1367
1368 if (osmo_sockaddr_str_from_str(&remote_str, argv[1], port)) {
1369 vty_out(vty, "Can not parse IPv4/IPv6 or port.%s", VTY_NEWLINE);
1370 return CMD_WARNING;
1371 }
1372
1373 if (osmo_sockaddr_str_to_sockaddr(&remote_str, &remote.u.sas)) {
1374 vty_out(vty, "Can not parse IPv4/IPv6 or port.%s", VTY_NEWLINE);
1375 return CMD_WARNING;
1376 }
1377
1378 nsvc = gprs_ns2_nsvc_by_sockaddr_bind(bind, &remote);
1379 if (!nsvc) {
1380 vty_out(vty, "Can not find NS-VC with remote %s:%u%s",
1381 remote_str.ip, remote_str.port, VTY_NEWLINE);
1382 return CMD_WARNING;
1383 }
1384
1385 if (!nsvc->persistent) {
1386 vty_out(vty, "NS-VC with remote %s:%u is a dynamic NS-VC. Not configured by vty.%s",
1387 remote_str.ip, remote_str.port, VTY_NEWLINE);
1388 return CMD_WARNING;
1389 }
1390
1391 if (nsvc->nse != nse) {
1392 vty_out(vty, "NS-VC is not part of this NSE!%s", VTY_NEWLINE);
1393 return CMD_WARNING;
1394 }
1395
1396 if (!nsvc->nsvci_is_valid) {
1397 vty_out(vty, "NS-VC doesn't have a nsvci!%s", VTY_NEWLINE);
1398 return CMD_WARNING;
1399 }
1400
1401 if (nsvc->nsvci != nsvci) {
1402 vty_out(vty, "NS-VC has a different nsvci (%u)!%s",
1403 nsvc->nsvci, VTY_NEWLINE);
1404 return CMD_WARNING;
1405 }
1406
1407 gprs_ns2_free_nsvc(nsvc);
1408 if (llist_empty(&nse->nsvc)) {
1409 nse->ll = GPRS_NS2_LL_UNDEF;
1410 nse->dialect = GPRS_NS2_DIALECT_UNDEF;
1411 }
1412
1413 return CMD_SUCCESS;
1414}
1415
Alexander Couzens5fa431c2021-02-08 23:21:54 +01001416DEFUN(cfg_ns_nse_ip_sns_remote, cfg_ns_nse_ip_sns_remote_cmd,
1417 "ip-sns-remote " VTY_IPV46_CMD " <1-65535>",
Alexander Couzensda1bf8e2021-01-25 16:27:33 +01001418 "SNS Initial Endpoint\n"
1419 "SGSN IPv4 Address\n" "SGSN IPv6 Address\n"
1420 "SGSN UDP Port\n"
1421 )
1422{
1423 struct gprs_ns2_nse *nse = vty->index;
1424 bool dialect_modified = false;
1425 bool ll_modified = false;
1426 int rc;
1427
1428 /* argv[0] */
1429 struct osmo_sockaddr_str remote_str;
1430 struct osmo_sockaddr remote;
1431 uint16_t port = atoi(argv[1]);
1432
1433 if (nse->ll == GPRS_NS2_LL_UNDEF) {
1434 nse->ll = GPRS_NS2_LL_UDP;
1435 ll_modified = true;
1436 }
1437
1438 if (nse->dialect == GPRS_NS2_DIALECT_UNDEF) {
1439 char sns[16];
1440 snprintf(sns, sizeof(sns), "NSE%05u-SNS", nse->nsei);
1441 nse->bss_sns_fi = ns2_sns_bss_fsm_alloc(nse, sns);
1442 if (!nse->bss_sns_fi)
1443 goto err;
1444 nse->dialect = GPRS_NS2_DIALECT_SNS;
1445 dialect_modified = true;
1446 }
1447
1448 if (nse->ll != GPRS_NS2_LL_UDP) {
1449 vty_out(vty, "Can not mix NS-VC with different link layer%s", VTY_NEWLINE);
1450 goto err;
1451 }
1452
1453 if (nse->dialect != GPRS_NS2_DIALECT_SNS) {
1454 vty_out(vty, "Can not mix NS-VC with different dialects%s", VTY_NEWLINE);
1455 goto err;
1456 }
1457
1458 if (osmo_sockaddr_str_from_str(&remote_str, argv[0], port)) {
1459 vty_out(vty, "Can not parse IPv4/IPv6 or port.%s", VTY_NEWLINE);
1460 goto err;
1461 }
1462
1463 if (osmo_sockaddr_str_to_sockaddr(&remote_str, &remote.u.sas)) {
1464 vty_out(vty, "Can not parse IPv4/IPv6 or port.%s", VTY_NEWLINE);
1465 goto err;
1466 }
1467
1468 rc = gprs_ns2_sns_add_endpoint(nse, &remote);
1469 switch (rc) {
1470 case 0:
1471 return CMD_SUCCESS;
1472 case -EADDRINUSE:
1473 vty_out(vty, "Specified SNS endpoint already part of the NSE.%s", VTY_NEWLINE);
1474 return CMD_WARNING;
1475 default:
1476 vty_out(vty, "Can not add specified SNS endpoint.%s", VTY_NEWLINE);
1477 return CMD_WARNING;
1478 }
1479
1480err:
1481 if (ll_modified)
1482 nse->ll = GPRS_NS2_LL_UNDEF;
1483 if (dialect_modified)
1484 nse->dialect = GPRS_NS2_DIALECT_UNDEF;
1485 return CMD_WARNING;
1486}
1487
Alexander Couzens5fa431c2021-02-08 23:21:54 +01001488DEFUN(cfg_no_ns_nse_ip_sns_remote, cfg_no_ns_nse_ip_sns_remote_cmd,
1489 "no ip-sns-remote " VTY_IPV46_CMD " <1-65535>",
Alexander Couzensda1bf8e2021-01-25 16:27:33 +01001490 NO_STR
1491 "Delete a SNS Initial Endpoint\n"
1492 "SGSN IPv4 Address\n" "SGSN IPv6 Address\n"
1493 "SGSN UDP Port\n"
1494 )
1495{
1496 struct gprs_ns2_nse *nse = vty->index;
1497 struct osmo_sockaddr_str remote_str; /* argv[0] */
1498 struct osmo_sockaddr remote;
1499 uint16_t port = atoi(argv[1]);
Alexander Couzensda1bf8e2021-01-25 16:27:33 +01001500
1501 if (nse->ll != GPRS_NS2_LL_UDP) {
1502 vty_out(vty, "This NSE doesn't support UDP.%s", VTY_NEWLINE);
1503 return CMD_WARNING;
1504 }
1505
1506 if (nse->dialect != GPRS_NS2_DIALECT_SNS) {
1507 vty_out(vty, "This NSE doesn't support UDP with dialect ip-sns.%s", VTY_NEWLINE);
1508 return CMD_WARNING;
1509 }
1510
1511 if (osmo_sockaddr_str_from_str(&remote_str, argv[0], port)) {
1512 vty_out(vty, "Can not parse IPv4/IPv6 or port.%s", VTY_NEWLINE);
1513 return CMD_WARNING;
1514 }
1515
1516 if (osmo_sockaddr_str_to_sockaddr(&remote_str, &remote.u.sas)) {
1517 vty_out(vty, "Can not parse IPv4/IPv6 or port.%s", VTY_NEWLINE);
1518 return CMD_WARNING;
1519 }
1520
1521 if (gprs_ns2_sns_del_endpoint(nse, &remote)) {
1522 vty_out(vty, "Can not remove specified SNS endpoint.%s", VTY_NEWLINE);
1523 return CMD_WARNING;
1524 }
1525
Alexander Couzens6b9d2322021-02-12 03:17:59 +01001526 if (vty_nse_check_sns(nse)) {
1527 /* there is still sns configuration valid */
Alexander Couzensda1bf8e2021-01-25 16:27:33 +01001528 return CMD_SUCCESS;
Alexander Couzensda1bf8e2021-01-25 16:27:33 +01001529 } else {
1530 /* clean up nse to allow other nsvc commands */
1531 osmo_fsm_inst_term(nse->bss_sns_fi, OSMO_FSM_TERM_REQUEST, NULL);
1532 nse->bss_sns_fi = NULL;
1533 nse->ll = GPRS_NS2_LL_UNDEF;
1534 nse->dialect = GPRS_NS2_DIALECT_UNDEF;
1535 }
1536
1537 return CMD_SUCCESS;
1538}
1539
Alexander Couzens6b9d2322021-02-12 03:17:59 +01001540DEFUN(cfg_ns_nse_ip_sns_bind, cfg_ns_nse_ip_sns_bind_cmd,
1541 "ip-sns-bind BINDID",
1542 "IP SNS binds\n"
1543 "A udp bind which this SNS will be used. The bind must be already exists. Can be given multiple times.\n")
1544{
1545 struct gprs_ns2_nse *nse = vty->index;
1546 struct gprs_ns2_vc_bind *bind;
1547 struct vty_bind *vbind;
1548 struct vty_nse *vnse;
1549 const char *name = argv[0];
1550 bool ll_modified = false;
1551 bool dialect_modified = false;
1552 int rc;
1553
1554 if (nse->ll == GPRS_NS2_LL_UNDEF) {
1555 nse->ll = GPRS_NS2_LL_UDP;
1556 ll_modified = true;
1557 }
1558
1559 if (nse->dialect == GPRS_NS2_DIALECT_UNDEF) {
1560 char sns[16];
1561 snprintf(sns, sizeof(sns), "NSE%05u-SNS", nse->nsei);
1562 nse->bss_sns_fi = ns2_sns_bss_fsm_alloc(nse, sns);
1563 if (!nse->bss_sns_fi)
1564 goto err;
1565 nse->dialect = GPRS_NS2_DIALECT_SNS;
1566 dialect_modified = true;
1567 }
1568
1569 if (nse->ll != GPRS_NS2_LL_UDP) {
1570 vty_out(vty, "Can not mix NS-VC with different link layer%s", VTY_NEWLINE);
1571 goto err;
1572 }
1573
1574 if (nse->dialect != GPRS_NS2_DIALECT_SNS) {
1575 vty_out(vty, "Can not mix NS-VC with different dialects%s", VTY_NEWLINE);
1576 goto err;
1577 }
1578
1579 vbind = vty_bind_by_name(name);
1580 if (!vbind) {
1581 vty_out(vty, "Can not find the given bind '%s'%s", name, VTY_NEWLINE);
1582 goto err;
1583 }
1584
1585 if (vbind->ll != GPRS_NS2_LL_UDP) {
1586 vty_out(vty, "ip-sns-bind can only be used with UDP bind%s",
1587 VTY_NEWLINE);
1588 goto err;
1589 }
1590
1591 /* the vnse has been created together when creating the nse node. The parent node should check this already! */
1592 vnse = vty_nse_by_nsei(nse->nsei);
1593 OSMO_ASSERT(vnse);
1594
1595 rc = vty_nse_add_vbind(vnse, vbind);
1596 switch (rc) {
1597 case 0:
1598 break;
1599 case -EALREADY:
1600 vty_out(vty, "Failed to add ip-sns-bind %s already present%s", name, VTY_NEWLINE);
1601 goto err;
1602 case -ENOMEM:
1603 vty_out(vty, "Failed to add ip-sns-bind %s out of memory%s", name, VTY_NEWLINE);
1604 goto err;
1605 default:
1606 vty_out(vty, "Failed to add ip-sns-bind %s! %d%s", name, rc, VTY_NEWLINE);
1607 goto err;
1608 }
1609
1610 /* the bind might not yet created because "listen" is missing. */
1611 bind = gprs_ns2_bind_by_name(vty_nsi, name);
1612 if (!bind)
1613 return CMD_SUCCESS;
1614
1615 rc = gprs_ns2_sns_add_bind(nse, bind);
1616 switch (rc) {
1617 case 0:
1618 break;
1619 case -EALREADY:
1620 vty_out(vty, "Failed to add ip-sns-bind %s already present%s", name, VTY_NEWLINE);
1621 goto err;
1622 case -ENOMEM:
1623 vty_out(vty, "Failed to add ip-sns-bind %s out of memory%s", name, VTY_NEWLINE);
1624 goto err;
1625 default:
1626 vty_out(vty, "Failed to add ip-sns-bind %s! %d%s", name, rc, VTY_NEWLINE);
1627 goto err;
1628 }
1629
1630 return CMD_SUCCESS;
1631err:
1632 if (ll_modified)
1633 nse->ll = GPRS_NS2_LL_UNDEF;
1634 if (dialect_modified)
1635 nse->dialect = GPRS_NS2_DIALECT_UNDEF;
1636
1637 return CMD_WARNING;
1638}
1639
1640DEFUN(cfg_no_ns_nse_ip_sns_bind, cfg_no_ns_nse_ip_sns_bind_cmd,
1641 "no ip-sns-bind BINDID",
1642 NO_STR
1643 "IP SNS binds\n"
1644 "A udp bind which this SNS will be used.\n")
1645{
1646 struct gprs_ns2_nse *nse = vty->index;
1647 struct gprs_ns2_vc_bind *bind;
1648 struct vty_bind *vbind;
1649 struct vty_nse *vnse;
1650 const char *name = argv[0];
1651 int rc;
1652
1653 if (nse->ll != GPRS_NS2_LL_UDP) {
1654 vty_out(vty, "This NSE doesn't support UDP.%s", VTY_NEWLINE);
1655 return CMD_WARNING;
1656 }
1657
1658 if (nse->dialect != GPRS_NS2_DIALECT_SNS) {
1659 vty_out(vty, "This NSE doesn't support UDP with dialect ip-sns.%s", VTY_NEWLINE);
1660 return CMD_WARNING;
1661 }
1662
1663 vbind = vty_bind_by_name(name);
1664 if (!vbind) {
1665 vty_out(vty, "Can not find the given bind '%s'%s", name, VTY_NEWLINE);
1666 return CMD_WARNING;
1667 }
1668
1669 if (vbind->ll != GPRS_NS2_LL_UDP) {
1670 vty_out(vty, "no ip-sns-bind can only be used with UDP bind%s",
1671 VTY_NEWLINE);
1672 return CMD_WARNING;
1673 }
1674
1675 /* the vnse has been created together when creating the nse node. The parent node should check this already! */
1676 vnse = vty_nse_by_nsei(nse->nsei);
1677 OSMO_ASSERT(vnse);
1678
1679 rc = vty_nse_remove_vbind(vnse, vbind);
1680 switch(rc) {
1681 case 0:
1682 break;
1683 case -ENOENT:
1684 vty_out(vty, "Bind %s is not part of this NSE%s", name, VTY_NEWLINE);
1685 return CMD_WARNING;
1686 case -EINVAL:
1687 vty_out(vty, "no ip-sns-bind can only be used with UDP bind%s",
1688 VTY_NEWLINE);
1689 return CMD_WARNING;
1690 default:
1691 return CMD_WARNING;
1692 }
1693
1694 /* the bind might not exists yet */
1695 bind = gprs_ns2_bind_by_name(vty_nsi, name);
1696 if (bind)
1697 gprs_ns2_sns_del_bind(nse, bind);
1698
1699 if (!vty_nse_check_sns(nse)) {
1700 /* clean up nse to allow other nsvc commands */
1701 osmo_fsm_inst_term(nse->bss_sns_fi, OSMO_FSM_TERM_REQUEST, NULL);
1702 nse->bss_sns_fi = NULL;
1703 nse->ll = GPRS_NS2_LL_UNDEF;
1704 nse->dialect = GPRS_NS2_DIALECT_UNDEF;
1705 }
1706
1707 return CMD_SUCCESS;
1708}
Alexander Couzensda1bf8e2021-01-25 16:27:33 +01001709
1710/* non-config commands */
Alexander Couzens6a161492020-07-12 13:45:50 +02001711static void dump_nsvc(struct vty *vty, struct gprs_ns2_vc *nsvc, bool stats)
1712{
Harald Weltedc2d0802020-12-01 18:17:28 +01001713 char nsvci_str[32];
1714
1715 if (nsvc->nsvci_is_valid)
1716 snprintf(nsvci_str, sizeof(nsvci_str), "%05u", nsvc->nsvci);
1717 else
1718 snprintf(nsvci_str, sizeof(nsvci_str), "none");
1719
1720 vty_out(vty, " NSVCI %s: %s %s data_weight=%u sig_weight=%u %s%s", nsvci_str,
1721 osmo_fsm_inst_state_name(nsvc->fi),
1722 nsvc->persistent ? "PERSIST" : "DYNAMIC",
1723 nsvc->data_weight, nsvc->sig_weight,
1724 gprs_ns2_ll_str(nsvc), VTY_NEWLINE);
Alexander Couzens6a161492020-07-12 13:45:50 +02001725
1726 if (stats) {
Harald Welte7aa60992020-12-01 17:53:17 +01001727 vty_out_rate_ctr_group(vty, " ", nsvc->ctrg);
1728 vty_out_stat_item_group(vty, " ", nsvc->statg);
Alexander Couzens6a161492020-07-12 13:45:50 +02001729 }
1730}
1731
1732static void dump_nse(struct vty *vty, const struct gprs_ns2_nse *nse, bool stats, bool persistent_only)
1733{
1734 struct gprs_ns2_vc *nsvc;
1735
Harald Welte0ff12ad2020-12-01 17:51:07 +01001736 vty_out(vty, "NSEI %05u: %s, %s%s", nse->nsei, gprs_ns2_lltype_str(nse->ll),
1737 nse->alive ? "ALIVE" : "DEAD", VTY_NEWLINE);
Alexander Couzens6a161492020-07-12 13:45:50 +02001738
Alexander Couzens8dfc24c2021-01-25 16:09:23 +01001739 ns2_sns_dump_vty(vty, " ", nse, stats);
Alexander Couzens6a161492020-07-12 13:45:50 +02001740 llist_for_each_entry(nsvc, &nse->nsvc, list) {
1741 if (persistent_only) {
1742 if (nsvc->persistent)
1743 dump_nsvc(vty, nsvc, stats);
1744 } else {
1745 dump_nsvc(vty, nsvc, stats);
1746 }
1747 }
1748}
1749
Alexander Couzens22f34712020-10-02 02:34:39 +02001750static void dump_bind(struct vty *vty, const struct gprs_ns2_vc_bind *bind, bool stats)
1751{
1752 if (bind->dump_vty)
1753 bind->dump_vty(bind, vty, stats);
Harald Welte76346072021-01-31 11:54:02 +01001754
1755 if (stats) {
1756 vty_out_stat_item_group(vty, " ", bind->statg);
1757 }
Alexander Couzens22f34712020-10-02 02:34:39 +02001758}
1759
Harald Welte2fce19a2020-12-01 17:52:55 +01001760static void dump_ns_bind(struct vty *vty, const struct gprs_ns2_inst *nsi, bool stats)
Alexander Couzens6a161492020-07-12 13:45:50 +02001761{
Alexander Couzens22f34712020-10-02 02:34:39 +02001762 struct gprs_ns2_vc_bind *bind;
Alexander Couzens6a161492020-07-12 13:45:50 +02001763
Alexander Couzens22f34712020-10-02 02:34:39 +02001764 llist_for_each_entry(bind, &nsi->binding, list) {
1765 dump_bind(vty, bind, stats);
1766 }
Harald Welte2fce19a2020-12-01 17:52:55 +01001767}
1768
1769
1770static void dump_ns_entities(struct vty *vty, const struct gprs_ns2_inst *nsi, bool stats, bool persistent_only)
1771{
1772 struct gprs_ns2_nse *nse;
Alexander Couzens22f34712020-10-02 02:34:39 +02001773
Alexander Couzens6a161492020-07-12 13:45:50 +02001774 llist_for_each_entry(nse, &nsi->nse, list) {
1775 dump_nse(vty, nse, stats, persistent_only);
Alexander Couzens6a161492020-07-12 13:45:50 +02001776 }
Alexander Couzens6a161492020-07-12 13:45:50 +02001777}
1778
Harald Welte25ee7552020-12-02 22:14:00 +01001779/* Backwards compatibility, among other things for the TestVTYGbproxy which expects
1780 * 'show ns' to output something about binds */
1781DEFUN_HIDDEN(show_ns, show_ns_cmd, "show ns",
1782 SHOW_STR SHOW_NS_STR)
1783{
1784 dump_ns_entities(vty, vty_nsi, false, false);
1785 dump_ns_bind(vty, vty_nsi, false);
1786 return CMD_SUCCESS;
1787}
1788
1789
Harald Welte2fce19a2020-12-01 17:52:55 +01001790DEFUN(show_ns_binds, show_ns_binds_cmd, "show ns binds [stats]",
Daniel Willmanncb3e9b52020-12-02 15:50:22 +01001791 SHOW_STR SHOW_NS_STR
Harald Welte2fce19a2020-12-01 17:52:55 +01001792 "Display information about the NS protocol binds\n"
1793 "Include statistic\n")
Alexander Couzens6a161492020-07-12 13:45:50 +02001794{
Harald Welte2fce19a2020-12-01 17:52:55 +01001795 bool stats = false;
1796 if (argc > 0)
1797 stats = true;
1798
1799 dump_ns_bind(vty, vty_nsi, stats);
Alexander Couzens6a161492020-07-12 13:45:50 +02001800 return CMD_SUCCESS;
1801}
1802
Harald Welte2fce19a2020-12-01 17:52:55 +01001803DEFUN(show_ns_entities, show_ns_entities_cmd, "show ns entities [stats]",
Daniel Willmanncb3e9b52020-12-02 15:50:22 +01001804 SHOW_STR SHOW_NS_STR
Harald Welte2fce19a2020-12-01 17:52:55 +01001805 "Display information about the NS protocol entities (NSEs)\n"
Alexander Couzens6a161492020-07-12 13:45:50 +02001806 "Include statistics\n")
1807{
Harald Welte2fce19a2020-12-01 17:52:55 +01001808 bool stats = false;
1809 if (argc > 0)
1810 stats = true;
1811
1812 dump_ns_entities(vty, vty_nsi, stats, false);
Alexander Couzens6a161492020-07-12 13:45:50 +02001813 return CMD_SUCCESS;
1814}
1815
1816DEFUN(show_ns_pers, show_ns_pers_cmd, "show ns persistent",
Daniel Willmanncb3e9b52020-12-02 15:50:22 +01001817 SHOW_STR SHOW_NS_STR
Alexander Couzens6a161492020-07-12 13:45:50 +02001818 "Show only persistent NS\n")
1819{
Harald Welte2fce19a2020-12-01 17:52:55 +01001820 dump_ns_entities(vty, vty_nsi, true, true);
Alexander Couzens6a161492020-07-12 13:45:50 +02001821 return CMD_SUCCESS;
1822}
1823
1824DEFUN(show_nse, show_nse_cmd, "show ns (nsei|nsvc) <0-65535> [stats]",
Daniel Willmanncb3e9b52020-12-02 15:50:22 +01001825 SHOW_STR SHOW_NS_STR
Alexander Couzens6a161492020-07-12 13:45:50 +02001826 "Select one NSE by its NSE Identifier\n"
1827 "Select one NSE by its NS-VC Identifier\n"
1828 "The Identifier of selected type\n"
1829 "Include Statistics\n")
1830{
1831 struct gprs_ns2_inst *nsi = vty_nsi;
1832 struct gprs_ns2_nse *nse;
1833 struct gprs_ns2_vc *nsvc;
1834 uint16_t id = atoi(argv[1]);
1835 bool show_stats = false;
1836
1837 if (argc >= 3)
1838 show_stats = true;
1839
1840 if (!strcmp(argv[0], "nsei")) {
1841 nse = gprs_ns2_nse_by_nsei(nsi, id);
1842 if (!nse) {
1843 return CMD_WARNING;
1844 }
1845
1846 dump_nse(vty, nse, show_stats, false);
1847 } else {
1848 nsvc = gprs_ns2_nsvc_by_nsvci(nsi, id);
1849
1850 if (!nsvc) {
1851 vty_out(vty, "No such NS Entity%s", VTY_NEWLINE);
1852 return CMD_WARNING;
1853 }
1854
1855 dump_nsvc(vty, nsvc, show_stats);
1856 }
1857
1858 return CMD_SUCCESS;
1859}
1860
Daniel Willmanndbab7142020-11-18 14:19:56 +01001861static int nsvc_force_unconf_cb(struct gprs_ns2_vc *nsvc, void *ctx)
1862{
Alexander Couzens8dfc24c2021-01-25 16:09:23 +01001863 ns2_vc_force_unconfigured(nsvc);
Harald Welte7fe8d712021-01-31 18:40:54 +01001864 ns2_vc_fsm_start(nsvc);
Daniel Willmanndbab7142020-11-18 14:19:56 +01001865 return 0;
1866}
1867
1868DEFUN_HIDDEN(nsvc_force_unconf, nsvc_force_unconf_cmd,
1869 "nsvc nsei <0-65535> force-unconfigured",
1870 "NS Virtual Connection\n"
1871 "The NSEI\n"
1872 "Reset the NSVCs back to initial state\n"
1873 )
1874{
1875 struct gprs_ns2_inst *nsi = vty_nsi;
1876 struct gprs_ns2_nse *nse;
1877
1878 uint16_t id = atoi(argv[0]);
1879
1880 nse = gprs_ns2_nse_by_nsei(nsi, id);
1881 if (!nse) {
1882 vty_out(vty, "Could not find NSE for NSEI %u%s", id, VTY_NEWLINE);
1883 return CMD_WARNING;
1884 }
1885
Alexander Couzensda1bf8e2021-01-25 16:27:33 +01001886 if (!nse->persistent) {
1887 gprs_ns2_free_nse(nse);
1888 } else if (nse->dialect == GPRS_NS2_DIALECT_SNS) {
Alexander Couzens280ed782020-12-21 18:25:41 +01001889 gprs_ns2_free_nsvcs(nse);
1890 } else {
1891 /* Perform the operation for all nsvc */
1892 gprs_ns2_nse_foreach_nsvc(nse, nsvc_force_unconf_cb, NULL);
1893 }
Daniel Willmanndbab7142020-11-18 14:19:56 +01001894
1895 return CMD_SUCCESS;
1896}
1897
Alexander Couzensda1bf8e2021-01-25 16:27:33 +01001898DEFUN(nsvc_block, nsvc_block_cmd,
1899 "nsvc <0-65535> (block|unblock)",
1900 "NS Virtual Connection\n"
1901 NSVCI_STR
1902 "Block a NSVC. As cause code O&M intervention will be used.\n"
1903 "Unblock a NSVC. As cause code O&M intervention will be used.\n")
Alexander Couzens841817e2020-11-19 00:41:29 +01001904{
Alexander Couzensda1bf8e2021-01-25 16:27:33 +01001905 struct gprs_ns2_inst *nsi = vty_nsi;
1906 struct gprs_ns2_vc *nsvc;
Alexander Couzens841817e2020-11-19 00:41:29 +01001907
Alexander Couzensda1bf8e2021-01-25 16:27:33 +01001908 uint16_t id = atoi(argv[0]);
Alexander Couzens841817e2020-11-19 00:41:29 +01001909
Alexander Couzensda1bf8e2021-01-25 16:27:33 +01001910 nsvc = gprs_ns2_nsvc_by_nsvci(nsi, id);
1911 if (!nsvc) {
1912 vty_out(vty, "Could not find NSVCI %05u%s", id, VTY_NEWLINE);
Alexander Couzens841817e2020-11-19 00:41:29 +01001913 return CMD_WARNING;
1914 }
1915
Alexander Couzensda1bf8e2021-01-25 16:27:33 +01001916 if (!strcmp(argv[1], "block")) {
1917 ns2_vc_block(nsvc);
1918 } else {
1919 ns2_vc_unblock(nsvc);
Alexander Couzens6a161492020-07-12 13:45:50 +02001920 }
1921
1922 return CMD_SUCCESS;
1923}
1924
Alexander Couzensda1bf8e2021-01-25 16:27:33 +01001925static void log_set_nse_filter(struct log_target *target,
1926 struct gprs_ns2_nse *nse)
Alexander Couzens6a161492020-07-12 13:45:50 +02001927{
Alexander Couzensda1bf8e2021-01-25 16:27:33 +01001928 if (nse) {
1929 target->filter_map |= (1 << LOG_FLT_GB_NSE);
1930 target->filter_data[LOG_FLT_GB_NSE] = nse;
1931 } else if (target->filter_data[LOG_FLT_GB_NSE]) {
1932 target->filter_map = ~(1 << LOG_FLT_GB_NSE);
1933 target->filter_data[LOG_FLT_GB_NSE] = NULL;
1934 }
Alexander Couzens6a161492020-07-12 13:45:50 +02001935}
1936
Alexander Couzensda1bf8e2021-01-25 16:27:33 +01001937static void log_set_nsvc_filter(struct log_target *target,
1938 struct gprs_ns2_vc *nsvc)
Alexander Couzens6a161492020-07-12 13:45:50 +02001939{
Alexander Couzensda1bf8e2021-01-25 16:27:33 +01001940 if (nsvc) {
1941 target->filter_map |= (1 << LOG_FLT_GB_NSVC);
1942 target->filter_data[LOG_FLT_GB_NSVC] = nsvc;
1943 } else if (target->filter_data[LOG_FLT_GB_NSVC]) {
1944 target->filter_map = ~(1 << LOG_FLT_GB_NSVC);
1945 target->filter_data[LOG_FLT_GB_NSVC] = NULL;
1946 }
Alexander Couzens6a161492020-07-12 13:45:50 +02001947}
1948
Daniel Willmann751977b2020-12-02 18:59:44 +01001949DEFUN(logging_fltr_nse,
1950 logging_fltr_nse_cmd,
1951 "logging filter nse nsei <0-65535>",
1952 LOGGING_STR FILTER_STR
1953 "Filter based on NS Entity\n"
1954 "Identify NSE by NSEI\n"
1955 "Numeric identifier\n")
1956{
1957 struct log_target *tgt;
1958 struct gprs_ns2_nse *nse;
Daniel Willmann89106522020-12-04 01:36:59 +01001959 uint16_t id = atoi(argv[0]);
Daniel Willmann751977b2020-12-02 18:59:44 +01001960
1961 log_tgt_mutex_lock();
1962 tgt = osmo_log_vty2tgt(vty);
1963 if (!tgt) {
1964 log_tgt_mutex_unlock();
1965 return CMD_WARNING;
1966 }
1967
1968 nse = gprs_ns2_nse_by_nsei(vty_nsi, id);
1969 if (!nse) {
1970 vty_out(vty, "No NSE by that identifier%s", VTY_NEWLINE);
1971 log_tgt_mutex_unlock();
1972 return CMD_WARNING;
1973 }
1974
1975 log_set_nse_filter(tgt, nse);
1976 log_tgt_mutex_unlock();
1977 return CMD_SUCCESS;
1978}
1979
Alexander Couzens6a161492020-07-12 13:45:50 +02001980/* TODO: add filter for single connection by description */
1981DEFUN(logging_fltr_nsvc,
1982 logging_fltr_nsvc_cmd,
1983 "logging filter nsvc nsvci <0-65535>",
1984 LOGGING_STR FILTER_STR
1985 "Filter based on NS Virtual Connection\n"
1986 "Identify NS-VC by NSVCI\n"
1987 "Numeric identifier\n")
1988{
1989 struct log_target *tgt;
1990 struct gprs_ns2_vc *nsvc;
Daniel Willmann89106522020-12-04 01:36:59 +01001991 uint16_t id = atoi(argv[0]);
Alexander Couzens6a161492020-07-12 13:45:50 +02001992
1993 log_tgt_mutex_lock();
1994 tgt = osmo_log_vty2tgt(vty);
1995 if (!tgt) {
1996 log_tgt_mutex_unlock();
1997 return CMD_WARNING;
1998 }
1999
2000 nsvc = gprs_ns2_nsvc_by_nsvci(vty_nsi, id);
2001 if (!nsvc) {
2002 vty_out(vty, "No NS-VC by that identifier%s", VTY_NEWLINE);
2003 log_tgt_mutex_unlock();
2004 return CMD_WARNING;
2005 }
2006
2007 log_set_nsvc_filter(tgt, nsvc);
2008 log_tgt_mutex_unlock();
2009 return CMD_SUCCESS;
2010}
2011
Alexander Couzense43b46e2021-01-27 21:52:08 +01002012/*! initialized a reduced vty interface which excludes the configuration nodes besides timeouts.
2013 * This can be used by the PCU which can be only configured by the BTS/BSC and not by the vty.
2014 * \param[in] nsi NS instance on which we operate
2015 * \return 0 on success.
2016 */
2017int gprs_ns2_vty_init_reduced(struct gprs_ns2_inst *nsi)
Alexander Couzens6a161492020-07-12 13:45:50 +02002018{
Pau Espin Pedrolcaf53172021-01-28 13:37:50 +01002019 vty_nsi = nsi;
Alexander Couzensda1bf8e2021-01-25 16:27:33 +01002020 INIT_LLIST_HEAD(&binds);
Alexander Couzens6b9d2322021-02-12 03:17:59 +01002021 INIT_LLIST_HEAD(&nses);
Pau Espin Pedrolcaf53172021-01-28 13:37:50 +01002022
Alexander Couzensda1bf8e2021-01-25 16:27:33 +01002023 vty_fr_network = osmo_fr_network_alloc(nsi);
2024 if (!vty_fr_network)
2025 return -ENOMEM;
Alexander Couzens6a161492020-07-12 13:45:50 +02002026
Harald Welte25ee7552020-12-02 22:14:00 +01002027 install_lib_element_ve(&show_ns_cmd);
Harald Welte2fce19a2020-12-01 17:52:55 +01002028 install_lib_element_ve(&show_ns_binds_cmd);
2029 install_lib_element_ve(&show_ns_entities_cmd);
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07002030 install_lib_element_ve(&show_ns_pers_cmd);
2031 install_lib_element_ve(&show_nse_cmd);
Daniel Willmann751977b2020-12-02 18:59:44 +01002032 install_lib_element_ve(&logging_fltr_nse_cmd);
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07002033 install_lib_element_ve(&logging_fltr_nsvc_cmd);
Alexander Couzens6a161492020-07-12 13:45:50 +02002034
Daniel Willmanndbab7142020-11-18 14:19:56 +01002035 install_lib_element(ENABLE_NODE, &nsvc_force_unconf_cmd);
Alexander Couzensda1bf8e2021-01-25 16:27:33 +01002036 install_lib_element(ENABLE_NODE, &nsvc_block_cmd);
Daniel Willmanndbab7142020-11-18 14:19:56 +01002037
Daniel Willmann751977b2020-12-02 18:59:44 +01002038 install_lib_element(CFG_LOG_NODE, &logging_fltr_nse_cmd);
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07002039 install_lib_element(CFG_LOG_NODE, &logging_fltr_nsvc_cmd);
Alexander Couzens6a161492020-07-12 13:45:50 +02002040
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07002041 install_lib_element(CONFIG_NODE, &cfg_ns_cmd);
Alexander Couzensda1bf8e2021-01-25 16:27:33 +01002042
Alexander Couzens6a161492020-07-12 13:45:50 +02002043 install_node(&ns_node, config_write_ns);
Alexander Couzensda1bf8e2021-01-25 16:27:33 +01002044 /* TODO: convert into osmo timer */
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07002045 install_lib_element(L_NS_NODE, &cfg_ns_timer_cmd);
Alexander Couzense43b46e2021-01-27 21:52:08 +01002046
2047 return 0;
2048}
2049
2050int gprs_ns2_vty_init(struct gprs_ns2_inst *nsi)
2051{
2052 int rc = gprs_ns2_vty_init_reduced(nsi);
2053 if (rc)
2054 return rc;
2055
Alexander Couzensda1bf8e2021-01-25 16:27:33 +01002056 install_lib_element(L_NS_NODE, &cfg_ns_nsei_cmd);
2057 install_lib_element(L_NS_NODE, &cfg_no_ns_nsei_cmd);
2058 install_lib_element(L_NS_NODE, &cfg_ns_bind_cmd);
2059 install_lib_element(L_NS_NODE, &cfg_no_ns_bind_cmd);
Alexander Couzens6a161492020-07-12 13:45:50 +02002060
Alexander Couzens260cd522021-01-28 20:31:31 +01002061 install_node(&ns_bind_node, NULL);
Alexander Couzensda1bf8e2021-01-25 16:27:33 +01002062 install_lib_element(L_NS_BIND_NODE, &cfg_ns_bind_listen_cmd);
2063 install_lib_element(L_NS_BIND_NODE, &cfg_no_ns_bind_listen_cmd);
2064 install_lib_element(L_NS_BIND_NODE, &cfg_ns_bind_dscp_cmd);
2065 install_lib_element(L_NS_BIND_NODE, &cfg_no_ns_bind_dscp_cmd);
Alexander Couzensc4704762021-02-08 23:13:12 +01002066 install_lib_element(L_NS_BIND_NODE, &cfg_ns_bind_ip_sns_weight_cmd);
Alexander Couzensda1bf8e2021-01-25 16:27:33 +01002067 install_lib_element(L_NS_BIND_NODE, &cfg_ns_bind_ipaccess_cmd);
2068 install_lib_element(L_NS_BIND_NODE, &cfg_no_ns_bind_ipaccess_cmd);
2069 install_lib_element(L_NS_BIND_NODE, &cfg_ns_bind_fr_cmd);
2070 install_lib_element(L_NS_BIND_NODE, &cfg_no_ns_bind_fr_cmd);
2071 /* TODO: accept-ip-sns when SGSN SNS has been implemented */
Alexander Couzens6a161492020-07-12 13:45:50 +02002072
Alexander Couzens260cd522021-01-28 20:31:31 +01002073 install_node(&ns_nse_node, NULL);
Alexander Couzensda1bf8e2021-01-25 16:27:33 +01002074 install_lib_element(L_NS_NSE_NODE, &cfg_ns_nse_nsvc_fr_cmd);
2075 install_lib_element(L_NS_NSE_NODE, &cfg_no_ns_nse_nsvci_cmd);
2076 install_lib_element(L_NS_NSE_NODE, &cfg_no_ns_nse_nsvc_fr_dlci_cmd);
2077 install_lib_element(L_NS_NSE_NODE, &cfg_ns_nse_nsvc_udp_cmd);
Alexander Couzensbf5d0db2021-02-12 04:04:13 +01002078 install_lib_element(L_NS_NSE_NODE, &cfg_ns_nse_nsvc_udp_weights_cmd);
Alexander Couzensda1bf8e2021-01-25 16:27:33 +01002079 install_lib_element(L_NS_NSE_NODE, &cfg_no_ns_nse_nsvc_udp_cmd);
2080 install_lib_element(L_NS_NSE_NODE, &cfg_ns_nse_nsvc_ipa_cmd);
2081 install_lib_element(L_NS_NSE_NODE, &cfg_no_ns_nse_nsvc_ipa_cmd);
Alexander Couzens5fa431c2021-02-08 23:21:54 +01002082 install_lib_element(L_NS_NSE_NODE, &cfg_ns_nse_ip_sns_remote_cmd);
2083 install_lib_element(L_NS_NSE_NODE, &cfg_no_ns_nse_ip_sns_remote_cmd);
Alexander Couzens6b9d2322021-02-12 03:17:59 +01002084 install_lib_element(L_NS_NSE_NODE, &cfg_ns_nse_ip_sns_bind_cmd);
2085 install_lib_element(L_NS_NSE_NODE, &cfg_no_ns_nse_ip_sns_bind_cmd);
Alexander Couzens6a161492020-07-12 13:45:50 +02002086
2087 return 0;
2088}