blob: b85df77f582b8da2dfa8de9fd156c8281e1c5aa8 [file] [log] [blame]
Harald Weltedda21ed2017-08-12 15:07:02 +02001/*
2 * (C) 2017 by Harald Welte <laforge@gnumonks.org>
3 * All Rights Reserved
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 */
19
20#include <string.h>
21#include <stdint.h>
22#include <inttypes.h>
23#include <netinet/in.h>
24#include <arpa/inet.h>
25
26#include <osmocom/core/talloc.h>
27#include <osmocom/core/utils.h>
28#include <osmocom/core/rate_ctr.h>
Vadim Yanitskiyd7030d22019-05-13 22:10:24 +070029#include <osmocom/gsm/apn.h>
Vadim Yanitskiyfb625042019-05-19 02:00:31 +070030#include <osmocom/gsm/gsm48_ie.h>
Harald Weltedda21ed2017-08-12 15:07:02 +020031#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
32
33#include <osmocom/vty/command.h>
34#include <osmocom/vty/vty.h>
35#include <osmocom/vty/misc.h>
36
37#include "../gtp/gtp.h"
38#include "../gtp/pdp.h"
39
Pau Espin Pedrolf612ffe2019-08-20 13:24:55 +020040#include "../lib/util.h"
41
Harald Weltedda21ed2017-08-12 15:07:02 +020042#include "ggsn.h"
43
44#define PREFIX_STR "Prefix (Network/Netmask)\n"
45#define IFCONFIG_STR "GGSN-based interface configuration\n"
46#define GGSN_STR "Gateway GPRS Support NODE (GGSN)\n"
47
48LLIST_HEAD(g_ggsn_list);
49
50enum ggsn_vty_node {
51 GGSN_NODE = _LAST_OSMOVTY_NODE + 1,
52 APN_NODE,
53};
54
55struct ggsn_ctx *ggsn_find(const char *name)
56{
57 struct ggsn_ctx *ggsn;
58
59 llist_for_each_entry(ggsn, &g_ggsn_list, list) {
60 if (!strcmp(ggsn->cfg.name, name))
61 return ggsn;
62 }
63 return NULL;
64}
65
66struct ggsn_ctx *ggsn_find_or_create(void *ctx, const char *name)
67{
68 struct ggsn_ctx *ggsn;
69
70 ggsn = ggsn_find(name);
71 if (ggsn)
72 return ggsn;
73
74 ggsn = talloc_zero(ctx, struct ggsn_ctx);
75 if (!ggsn)
76 return NULL;
77
78 ggsn->cfg.name = talloc_strdup(ggsn, name);
79 ggsn->cfg.state_dir = talloc_strdup(ggsn, "/tmp");
80 ggsn->cfg.shutdown = true;
81 INIT_LLIST_HEAD(&ggsn->apn_list);
82
83 llist_add_tail(&ggsn->list, &g_ggsn_list);
84 return ggsn;
85}
86
87struct apn_ctx *ggsn_find_apn(struct ggsn_ctx *ggsn, const char *name)
88{
89 struct apn_ctx *apn;
90
91 llist_for_each_entry(apn, &ggsn->apn_list, list) {
92 if (!strcmp(apn->cfg.name, name))
93 return apn;
94 }
95 return NULL;
96}
97
98struct apn_ctx *ggsn_find_or_create_apn(struct ggsn_ctx *ggsn, const char *name)
99{
100 struct apn_ctx *apn = ggsn_find_apn(ggsn, name);
101 if (apn)
102 return apn;
103
104 apn = talloc_zero(ggsn, struct apn_ctx);
105 if (!apn)
106 return NULL;
107 apn->ggsn = ggsn;
108 apn->cfg.name = talloc_strdup(apn, name);
109 apn->cfg.shutdown = true;
Harald Welte93fed3b2017-09-24 11:43:17 +0800110 apn->cfg.tx_gpdu_seq = true;
Harald Weltedda21ed2017-08-12 15:07:02 +0200111 INIT_LLIST_HEAD(&apn->cfg.name_list);
112
113 llist_add_tail(&apn->list, &ggsn->apn_list);
114 return apn;
115}
116
117/* GGSN Node */
118
119static struct cmd_node ggsn_node = {
120 GGSN_NODE,
121 "%s(config-ggsn)# ",
122 1,
123};
124
125DEFUN(cfg_ggsn, cfg_ggsn_cmd,
126 "ggsn NAME",
127 "Configure the Gateway GPRS Support Node\n" "GGSN Name (has only local significance)\n")
128{
129 struct ggsn_ctx *ggsn;
130
131 ggsn = ggsn_find_or_create(tall_ggsn_ctx, argv[0]);
132 if (!ggsn)
133 return CMD_WARNING;
134
135 vty->node = GGSN_NODE;
136 vty->index = ggsn;
137 vty->index_sub = &ggsn->cfg.description;
138
139 return CMD_SUCCESS;
140}
141
142DEFUN(cfg_no_ggsn, cfg_no_ggsn_cmd,
143 "no ggsn NAME",
144 NO_STR "Remove the named Gateway GPRS Support Node\n"
145 "GGSN Name (has only local significance)\n")
146{
147 struct ggsn_ctx *ggsn;
148
149 ggsn = ggsn_find(argv[0]);
150 if (!ggsn) {
151 vty_out(vty, "%% No such GGSN '%s'%s", argv[0], VTY_NEWLINE);
152 return CMD_WARNING;
153 }
154
155 if (!ggsn->cfg.shutdown) {
156 vty_out(vty, "%% GGSN %s is still active, please shutdown first%s",
157 ggsn->cfg.name, VTY_NEWLINE);
158 return CMD_WARNING;
159 }
160
161 if (!llist_empty(&ggsn->apn_list)) {
162 vty_out(vty, "%% GGSN %s still has APNs configured, please remove first%s",
163 ggsn->cfg.name, VTY_NEWLINE);
164 return CMD_WARNING;
165 }
166
167 llist_del(&ggsn->list);
168 talloc_free(ggsn);
169
170 return CMD_SUCCESS;
171}
172
Harald Welte98146772017-09-05 17:41:20 +0200173DEFUN(cfg_ggsn_bind_ip, cfg_ggsn_bind_ip_cmd,
174 "gtp bind-ip A.B.C.D",
Harald Weltedda21ed2017-08-12 15:07:02 +0200175 "GTP Parameters\n"
176 "Set the IP address for the local GTP bind\n"
177 "IPv4 Address\n")
178{
179 struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
180 size_t t;
181
182 ippool_aton(&ggsn->cfg.listen_addr, &t, argv[0], 0);
183
184 return CMD_SUCCESS;
185}
186
Harald Welte98146772017-09-05 17:41:20 +0200187DEFUN(cfg_ggsn_gtpc_ip, cfg_ggsn_gtpc_ip_cmd,
188 "gtp control-ip A.B.C.D",
189 "GTP Parameters\n"
190 "Set the IP address states as local IP in GTP-C messages\n"
191 "IPv4 Address\n")
192{
193 struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
194 size_t t;
195
196 ippool_aton(&ggsn->cfg.gtpc_addr, &t, argv[0], 0);
197
198 return CMD_SUCCESS;
199}
200
201DEFUN(cfg_ggsn_gtpu_ip, cfg_ggsn_gtpu_ip_cmd,
202 "gtp user-ip A.B.C.D",
203 "GTP Parameters\n"
204 "Set the IP address states as local IP in GTP-U messages\n"
205 "IPv4 Address\n")
206{
207 struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
208 size_t t;
209
210 ippool_aton(&ggsn->cfg.gtpu_addr, &t, argv[0], 0);
211
212 return CMD_SUCCESS;
213}
214
Harald Weltedda21ed2017-08-12 15:07:02 +0200215DEFUN(cfg_ggsn_state_dir, cfg_ggsn_state_dir_cmd,
216 "gtp state-dir PATH",
217 "GTP Parameters\n"
218 "Set the directory for the GTP State file\n"
219 "Local Directory\n")
220{
221 struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
222
223 osmo_talloc_replace_string(ggsn, &ggsn->cfg.state_dir, argv[0]);
224
225 return CMD_SUCCESS;
226}
227
228DEFUN(cfg_ggsn_apn, cfg_ggsn_apn_cmd,
229 "apn NAME", "APN Configuration\n" "APN Name\n")
230{
231 struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
232 struct apn_ctx *apn;
233
234 apn = ggsn_find_or_create_apn(ggsn, argv[0]);
235 if (!apn)
236 return CMD_WARNING;
237
238 vty->node = APN_NODE;
239 vty->index = apn;
240 vty->index_sub = &ggsn->cfg.description;
241
242 return CMD_SUCCESS;
243}
244
245DEFUN(cfg_ggsn_no_apn, cfg_ggsn_no_apn_cmd,
246 "no apn NAME",
247 NO_STR "Remove APN Configuration\n" "APN Name\n")
248{
249 struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
250 struct apn_ctx *apn;
251
252 apn = ggsn_find_apn(ggsn, argv[0]);
253 if (!apn) {
254 vty_out(vty, "%% No such APN '%s'%s", argv[0], VTY_NEWLINE);
255 return CMD_WARNING;
256 }
257
258 if (!apn->cfg.shutdown) {
259 vty_out(vty, "%% APN %s still active, please shutdown first%s",
260 apn->cfg.name, VTY_NEWLINE);
261 return CMD_WARNING;
262 }
263
264 llist_del(&apn->list);
265 talloc_free(apn);
266
267 return CMD_SUCCESS;
268}
269
270DEFUN(cfg_ggsn_default_apn, cfg_ggsn_default_apn_cmd,
271 "default-apn NAME",
272 "Set a default-APN to be used if no other APN matches\n"
273 "APN Name\n")
274{
275 struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
276 struct apn_ctx *apn;
277
278 apn = ggsn_find_apn(ggsn, argv[0]);
279 if (!apn) {
280 vty_out(vty, "%% No APN of name '%s' found%s", argv[0], VTY_NEWLINE);
281 return CMD_WARNING;
282 }
283
284 ggsn->cfg.default_apn = apn;
285 return CMD_SUCCESS;
286}
287
288DEFUN(cfg_ggsn_no_default_apn, cfg_ggsn_no_default_apn_cmd,
289 "no default-apn",
290 NO_STR "Remove default-APN to be used if no other APN matches\n")
291{
292 struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
293 ggsn->cfg.default_apn = NULL;
294 return CMD_SUCCESS;
295}
296
297DEFUN(cfg_ggsn_shutdown, cfg_ggsn_shutdown_cmd,
298 "shutdown ggsn",
299 "Put the GGSN in administrative shut-down\n" GGSN_STR)
300{
301 struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
302
303 if (!ggsn->cfg.shutdown) {
304 if (ggsn_stop(ggsn)) {
305 vty_out(vty, "%% Failed to shutdown GGSN%s", VTY_NEWLINE);
306 return CMD_WARNING;
307 }
308 ggsn->cfg.shutdown = true;
309 }
310
311 return CMD_SUCCESS;
312}
313
314DEFUN(cfg_ggsn_no_shutdown, cfg_ggsn_no_shutdown_cmd,
315 "no shutdown ggsn",
316 NO_STR GGSN_STR "Remove the GGSN from administrative shut-down\n")
317{
318 struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
319
320 if (ggsn->cfg.shutdown) {
321 if (ggsn_start(ggsn) < 0) {
322 vty_out(vty, "%% Failed to start GGSN, check log for details%s", VTY_NEWLINE);
323 return CMD_WARNING;
324 }
325 ggsn->cfg.shutdown = false;
326 }
327
328 return CMD_SUCCESS;
329}
330
331/* APN Node */
332
333static struct cmd_node apn_node = {
334 APN_NODE,
335 "%s(config-ggsn-apn)# ",
336 1,
337};
338
339static const struct value_string pdp_type_names[] = {
340 { APN_TYPE_IPv4, "v4" },
341 { APN_TYPE_IPv6, "v6" },
342 { APN_TYPE_IPv4v6, "v4v6" },
343 { 0, NULL }
344};
345
346static const struct value_string apn_gtpu_mode_names[] = {
347 { APN_GTPU_MODE_TUN, "tun" },
348 { APN_GTPU_MODE_KERNEL_GTP, "kernel-gtp" },
349 { 0, NULL }
350};
351
352
353#define V4V6V46_STRING "IPv4(-only) PDP Type\n" \
354 "IPv6(-only) PDP Type\n" \
355 "IPv4v6 (dual-stack) PDP Type\n"
356
357DEFUN(cfg_apn_type_support, cfg_apn_type_support_cmd,
358 "type-support (v4|v6|v4v6)",
359 "Enable support for PDP Type\n"
360 V4V6V46_STRING)
361{
362 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
363 uint32_t type = get_string_value(pdp_type_names, argv[0]);
364
365 apn->cfg.apn_type_mask |= type;
366 return CMD_SUCCESS;
367}
368
369DEFUN(cfg_apn_no_type_support, cfg_apn_no_type_support_cmd,
370 "no type-support (v4|v6|v4v6)",
371 NO_STR "Disable support for PDP Type\n"
372 V4V6V46_STRING)
373{
374 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
375 uint32_t type = get_string_value(pdp_type_names, argv[0]);
376
377 apn->cfg.apn_type_mask &= ~type;
378 return CMD_SUCCESS;
379}
380
381DEFUN(cfg_apn_gtpu_mode, cfg_apn_gtpu_mode_cmd,
382 "gtpu-mode (tun|kernel-gtp)",
383 "Set the Mode for this APN (tun or Linux Kernel GTP)\n"
384 "GTP-U in userspace using TUN device\n"
385 "GTP-U in kernel using Linux Kernel GTP\n")
386{
387 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
388
389 apn->cfg.gtpu_mode = get_string_value(apn_gtpu_mode_names, argv[0]);
390 return CMD_SUCCESS;
391}
392
393DEFUN(cfg_apn_tun_dev_name, cfg_apn_tun_dev_name_cmd,
394 "tun-device NAME",
395 "Configure tun device name\n"
396 "TUN device name")
397{
398 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
399 osmo_talloc_replace_string(apn, &apn->tun.cfg.dev_name, argv[0]);
400 return CMD_SUCCESS;
401}
402
403DEFUN(cfg_apn_ipup_script, cfg_apn_ipup_script_cmd,
404 "ipup-script PATH",
405 "Configure name/path of ip-up script\n"
406 "File/Path name of ip-up script\n")
407{
408 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
409 osmo_talloc_replace_string(apn, &apn->tun.cfg.ipup_script, argv[0]);
410 return CMD_SUCCESS;
411}
412
413DEFUN(cfg_apn_no_ipup_script, cfg_apn_no_ipup_script_cmd,
414 "no ipup-script",
415 NO_STR "Disable ip-up script\n")
416{
417 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
418 talloc_free(apn->tun.cfg.ipup_script);
419 apn->tun.cfg.ipup_script = NULL;
420 return CMD_SUCCESS;
421}
422
423DEFUN(cfg_apn_ipdown_script, cfg_apn_ipdown_script_cmd,
424 "ipdown-script PATH",
425 "Configure name/path of ip-down script\n"
426 "File/Path name of ip-down script\n")
427{
428 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
429 osmo_talloc_replace_string(apn, &apn->tun.cfg.ipdown_script, argv[0]);
430 return CMD_SUCCESS;
431}
432
433/* convert prefix from "A.B.C.D/M" notation to in46_prefix */
434static void str2prefix(struct in46_prefix *pfx, const char *in)
435{
436 size_t t;
437
438 ippool_aton(&pfx->addr, &t, in, 0);
439 pfx->prefixlen = t;
440}
441
442DEFUN(cfg_apn_no_ipdown_script, cfg_apn_no_ipdown_script_cmd,
443 "no ipdown-script",
444 NO_STR "Disable ip-down script\n")
445{
446 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
447 talloc_free(apn->tun.cfg.ipdown_script);
448 apn->tun.cfg.ipdown_script = NULL;
449 return CMD_SUCCESS;
450}
451
452DEFUN(cfg_apn_ip_prefix, cfg_apn_ip_prefix_cmd,
453 "ip prefix (static|dynamic) A.B.C.D/M",
454 IP_STR PREFIX_STR "IPv4 Adress/Prefix-Length\n")
455{
456 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
457 struct in46_prefix *pfx;
458
459 /* first update our parsed prefix */
460 if (!strcmp(argv[0], "static"))
461 pfx = &apn->v4.cfg.static_prefix;
462 else
463 pfx = &apn->v4.cfg.dynamic_prefix;
464 str2prefix(pfx, argv[1]);
465
466 return CMD_SUCCESS;
467}
468
469DEFUN(cfg_apn_ip_ifconfig, cfg_apn_ip_ifconfig_cmd,
470 "ip ifconfig A.B.C.D/M",
471 IP_STR IFCONFIG_STR "IPv4 Adress/Prefix-Length\n")
472{
473 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
474 str2prefix(&apn->v4.cfg.ifconfig_prefix, argv[0]);
475 return CMD_SUCCESS;
476}
477
478DEFUN(cfg_apn_no_ip_ifconfig, cfg_apn_no_ip_ifconfig_cmd,
479 "no ip ifconfig",
480 NO_STR IP_STR IFCONFIG_STR)
481{
482 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
483 memset(&apn->v4.cfg.ifconfig_prefix, 0, sizeof(apn->v4.cfg.ifconfig_prefix));
484 return CMD_SUCCESS;
485}
486
487DEFUN(cfg_apn_ipv6_prefix, cfg_apn_ipv6_prefix_cmd,
488 "ipv6 prefix (static|dynamic) X:X::X:X/M",
489 IP6_STR PREFIX_STR "IPv6 Address/Prefix-Length\n")
490{
491 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
492 struct in46_prefix *pfx;
493
494 if (!strcmp(argv[0], "static"))
495 pfx = &apn->v6.cfg.static_prefix;
496 else
497 pfx = &apn->v6.cfg.dynamic_prefix;
498 str2prefix(pfx, argv[1]);
499 return CMD_SUCCESS;
500}
501
502DEFUN(cfg_apn_ipv6_ifconfig, cfg_apn_ipv6_ifconfig_cmd,
503 "ipv6 ifconfig X:X::X:X/M",
504 IP6_STR IFCONFIG_STR "IPv6 Adress/Prefix-Length\n")
505{
506 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
507 str2prefix(&apn->v6.cfg.ifconfig_prefix, argv[0]);
508 return CMD_SUCCESS;
509}
510
511DEFUN(cfg_apn_no_ipv6_ifconfig, cfg_apn_no_ipv6_ifconfig_cmd,
512 "no ipv6 ifconfig",
513 NO_STR IP6_STR IFCONFIG_STR)
514{
515 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
516 memset(&apn->v6.cfg.ifconfig_prefix, 0, sizeof(apn->v6.cfg.ifconfig_prefix));
517 return CMD_SUCCESS;
518}
519
Pau Espin Pedrol37c45e32017-12-14 14:09:13 +0100520DEFUN(cfg_apn_ipv6_linklocal, cfg_apn_ipv6_linklocal_cmd,
521 "ipv6 link-local X:X::X:X/M",
522 IP6_STR IFCONFIG_STR "IPv6 Link-local Adress/Prefix-Length\n")
523{
524 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
525 str2prefix(&apn->v6.cfg.ll_prefix, argv[0]);
526 return CMD_SUCCESS;
527}
528
529DEFUN(cfg_apn_no_ipv6_linklocal, cfg_apn_no_ipv6_linklocal_cmd,
530 "no ipv6 link-local",
531 NO_STR IP6_STR IFCONFIG_STR)
532{
533 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
534 memset(&apn->v6.cfg.ll_prefix, 0, sizeof(apn->v6.cfg.ll_prefix));
535 return CMD_SUCCESS;
536}
537
Harald Weltedda21ed2017-08-12 15:07:02 +0200538#define DNS_STRINGS "Configure DNS Server\n" "primary/secondary DNS\n" "IP address of DNS Sever\n"
539
540DEFUN(cfg_apn_ip_dns, cfg_apn_ip_dns_cmd,
541 "ip dns <0-1> A.B.C.D",
542 IP_STR DNS_STRINGS)
543{
544 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
545 int idx = atoi(argv[0]);
546 size_t dummy;
547
548 ippool_aton(&apn->v4.cfg.dns[idx], &dummy, argv[1], 0);
549
550 return CMD_SUCCESS;
551}
552
553DEFUN(cfg_apn_ipv6_dns, cfg_apn_ipv6_dns_cmd,
554 "ipv6 dns <0-1> X:X::X:X",
555 IP6_STR DNS_STRINGS)
556{
557 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
558 int idx = atoi(argv[0]);
559 size_t dummy;
560
561 ippool_aton(&apn->v6.cfg.dns[idx], &dummy, argv[1], 0);
562
563 return CMD_SUCCESS;
564}
565
566DEFUN(cfg_apn_no_dns, cfg_apn_no_dns_cmd,
567 "no (ip|ipv6) dns <0-1>",
568 NO_STR IP_STR IP6_STR "Disable DNS Server\n" "primary/secondary DNS\n")
569{
570 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
571 struct in46_addr *a;
572 int idx = atoi(argv[1]);
573
574 if (!strcmp(argv[0], "ip"))
575 a = &apn->v4.cfg.dns[idx];
576 else
577 a = &apn->v6.cfg.dns[idx];
578
579 memset(a, 0, sizeof(*a));
580
581 return CMD_SUCCESS;
582}
583
Harald Welte93fed3b2017-09-24 11:43:17 +0800584DEFUN(cfg_apn_gpdu_seq, cfg_apn_gpdu_seq_cmd,
585 "g-pdu tx-sequence-numbers",
586 "G-PDU Configuration\n" "Enable transmission of G-PDU sequence numbers\n")
587{
588 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
589 apn->cfg.tx_gpdu_seq = true;
590 return CMD_SUCCESS;
591}
592
593DEFUN(cfg_apn_no_gpdu_seq, cfg_apn_no_gpdu_seq_cmd,
594 "no g-pdu tx-sequence-numbers",
595 NO_STR "G-PDU Configuration\n" "Disable transmission of G-PDU sequence numbers\n")
596{
597 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
598 apn->cfg.tx_gpdu_seq = false;
599 return CMD_SUCCESS;
600}
601
Harald Weltedda21ed2017-08-12 15:07:02 +0200602DEFUN(cfg_apn_shutdown, cfg_apn_shutdown_cmd,
603 "shutdown",
604 "Put the APN in administrative shut-down\n")
605{
606 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
607
608 if (!apn->cfg.shutdown) {
Pau Espin Pedrol72ab4bc2019-05-29 19:08:26 +0200609 if (apn_stop(apn)) {
Harald Weltedda21ed2017-08-12 15:07:02 +0200610 vty_out(vty, "%% Failed to Stop APN%s", VTY_NEWLINE);
611 return CMD_WARNING;
612 }
613 apn->cfg.shutdown = true;
614 }
615
616 return CMD_SUCCESS;
617}
618
619DEFUN(cfg_apn_no_shutdown, cfg_apn_no_shutdown_cmd,
620 "no shutdown",
621 NO_STR "Remove the APN from administrative shut-down\n")
622{
623 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
624
625 if (apn->cfg.shutdown) {
626 if (apn_start(apn) < 0) {
627 vty_out(vty, "%% Failed to start APN, check log for details%s", VTY_NEWLINE);
628 return CMD_WARNING;
629 }
630 apn->cfg.shutdown = false;
631 }
632
633 return CMD_SUCCESS;
634}
635
636
637static void vty_dump_prefix(struct vty *vty, const char *pre, const struct in46_prefix *pfx)
638{
639 vty_out(vty, "%s %s%s", pre, in46p_ntoa(pfx), VTY_NEWLINE);
640}
641
642static void config_write_apn(struct vty *vty, struct apn_ctx *apn)
643{
644 unsigned int i;
645
646 vty_out(vty, " apn %s%s", apn->cfg.name, VTY_NEWLINE);
647 if (apn->cfg.description)
648 vty_out(vty, " description %s%s", apn->cfg.description, VTY_NEWLINE);
649 vty_out(vty, " gtpu-mode %s%s", get_value_string(apn_gtpu_mode_names, apn->cfg.gtpu_mode),
650 VTY_NEWLINE);
651 if (apn->tun.cfg.dev_name)
652 vty_out(vty, " tun-device %s%s", apn->tun.cfg.dev_name, VTY_NEWLINE);
653 if (apn->tun.cfg.ipup_script)
654 vty_out(vty, " ipup-script %s%s", apn->tun.cfg.ipup_script, VTY_NEWLINE);
655 if (apn->tun.cfg.ipdown_script)
656 vty_out(vty, " ipdown-script %s%s", apn->tun.cfg.ipdown_script, VTY_NEWLINE);
657
658 for (i = 0; i < 32; i++) {
Pau Espin Pedrol55600012019-05-30 17:29:09 +0200659 if (!(apn->cfg.apn_type_mask & (UINT32_C(1) << i)))
Harald Weltedda21ed2017-08-12 15:07:02 +0200660 continue;
Pau Espin Pedrol55600012019-05-30 17:29:09 +0200661 vty_out(vty, " type-support %s%s", get_value_string(pdp_type_names, (UINT32_C(1) << i)),
Harald Weltedda21ed2017-08-12 15:07:02 +0200662 VTY_NEWLINE);
663 }
664
Harald Welte93fed3b2017-09-24 11:43:17 +0800665 if (!apn->cfg.tx_gpdu_seq)
666 vty_out(vty, " no g-pdu tx-sequence-numbers%s", VTY_NEWLINE);
667
Harald Weltedda21ed2017-08-12 15:07:02 +0200668 /* IPv4 prefixes + DNS */
669 if (apn->v4.cfg.static_prefix.addr.len)
670 vty_dump_prefix(vty, " ip prefix static", &apn->v4.cfg.static_prefix);
671 if (apn->v4.cfg.dynamic_prefix.addr.len)
672 vty_dump_prefix(vty, " ip prefix dynamic", &apn->v4.cfg.dynamic_prefix);
673 for (i = 0; i < ARRAY_SIZE(apn->v4.cfg.dns); i++) {
674 if (!apn->v4.cfg.dns[i].len)
675 continue;
676 vty_out(vty, " ip dns %u %s%s", i, in46a_ntoa(&apn->v4.cfg.dns[i]), VTY_NEWLINE);
677 }
678 if (apn->v4.cfg.ifconfig_prefix.addr.len)
Harald Welteff438172017-09-24 22:48:46 +0800679 vty_dump_prefix(vty, " ip ifconfig", &apn->v4.cfg.ifconfig_prefix);
Harald Weltedda21ed2017-08-12 15:07:02 +0200680
681 /* IPv6 prefixes + DNS */
682 if (apn->v6.cfg.static_prefix.addr.len)
683 vty_dump_prefix(vty, " ipv6 prefix static", &apn->v6.cfg.static_prefix);
684 if (apn->v6.cfg.dynamic_prefix.addr.len)
685 vty_dump_prefix(vty, " ipv6 prefix dynamic", &apn->v6.cfg.dynamic_prefix);
686 for (i = 0; i < ARRAY_SIZE(apn->v6.cfg.dns); i++) {
687 if (!apn->v6.cfg.dns[i].len)
688 continue;
Harald Welte3ca419a2017-09-24 22:47:51 +0800689 vty_out(vty, " ipv6 dns %u %s%s", i, in46a_ntoa(&apn->v6.cfg.dns[i]), VTY_NEWLINE);
Harald Weltedda21ed2017-08-12 15:07:02 +0200690 }
691 if (apn->v6.cfg.ifconfig_prefix.addr.len)
Harald Welteff438172017-09-24 22:48:46 +0800692 vty_dump_prefix(vty, " ipv6 ifconfig", &apn->v6.cfg.ifconfig_prefix);
Pau Espin Pedrole5a082d2017-12-15 15:55:04 +0100693 if (apn->v6.cfg.ll_prefix.addr.len)
694 vty_dump_prefix(vty, " ipv6 link-local", &apn->v6.cfg.ll_prefix);
Harald Weltedda21ed2017-08-12 15:07:02 +0200695
696 /* must be last */
697 vty_out(vty, " %sshutdown%s", apn->cfg.shutdown ? "" : "no ", VTY_NEWLINE);
698}
699
700static int config_write_ggsn(struct vty *vty)
701{
702 struct ggsn_ctx *ggsn;
703
704 llist_for_each_entry(ggsn, &g_ggsn_list, list) {
705 struct apn_ctx *apn;
706 vty_out(vty, "ggsn %s%s", ggsn->cfg.name, VTY_NEWLINE);
707 if (ggsn->cfg.description)
708 vty_out(vty, " description %s%s", ggsn->cfg.description, VTY_NEWLINE);
709 vty_out(vty, " gtp state-dir %s%s", ggsn->cfg.state_dir, VTY_NEWLINE);
Harald Welte98146772017-09-05 17:41:20 +0200710 vty_out(vty, " gtp bind-ip %s%s", in46a_ntoa(&ggsn->cfg.listen_addr), VTY_NEWLINE);
711 if (ggsn->cfg.gtpc_addr.v4.s_addr)
712 vty_out(vty, " gtp control-ip %s%s", in46a_ntoa(&ggsn->cfg.gtpc_addr), VTY_NEWLINE);
713 if (ggsn->cfg.gtpu_addr.v4.s_addr)
714 vty_out(vty, " gtp user-ip %s%s", in46a_ntoa(&ggsn->cfg.gtpu_addr), VTY_NEWLINE);
Harald Weltedda21ed2017-08-12 15:07:02 +0200715 llist_for_each_entry(apn, &ggsn->apn_list, list)
716 config_write_apn(vty, apn);
717 if (ggsn->cfg.default_apn)
718 vty_out(vty, " default-apn %s%s", ggsn->cfg.default_apn->cfg.name, VTY_NEWLINE);
719 /* must be last */
720 vty_out(vty, " %sshutdown ggsn%s", ggsn->cfg.shutdown ? "" : "no ", VTY_NEWLINE);
721 }
722
723 return 0;
724}
725
726static const char *print_gsnaddr(const struct ul16_t *in)
727{
728 struct in46_addr in46;
729
730 in46.len = in->l;
731 OSMO_ASSERT(in->l <= sizeof(in46.v6));
732 memcpy(&in46.v6, in->v, in->l);
733
734 return in46a_ntoa(&in46);
735}
736
737static void show_one_pdp(struct vty *vty, struct pdp_t *pdp)
738{
Pau Espin Pedrol03cce862019-08-20 12:23:14 +0200739 struct ippoolm_t *peer;
Vadim Yanitskiyd7030d22019-05-13 22:10:24 +0700740 char name_buf[256];
741 char *apn_name;
Vadim Yanitskiyfb625042019-05-19 02:00:31 +0700742 int rc;
743
744 /* Attempt to decode MSISDN */
745 rc = gsm48_decode_bcd_number2(name_buf, sizeof(name_buf),
746 pdp->msisdn.v, pdp->msisdn.l, 0);
Harald Weltedda21ed2017-08-12 15:07:02 +0200747
748 vty_out(vty, "IMSI: %s, NSAPI: %u, MSISDN: %s%s", imsi_gtp2str(&pdp->imsi), pdp->nsapi,
Vadim Yanitskiyfb625042019-05-19 02:00:31 +0700749 rc ? "(NONE)" : name_buf, VTY_NEWLINE);
Harald Weltedda21ed2017-08-12 15:07:02 +0200750
751 vty_out(vty, " Control: %s:%08x ", print_gsnaddr(&pdp->gsnlc), pdp->teic_own);
752 vty_out(vty, "<-> %s:%08x%s", print_gsnaddr(&pdp->gsnrc), pdp->teic_gn, VTY_NEWLINE);
753
754 vty_out(vty, " Data: %s:%08x ", print_gsnaddr(&pdp->gsnlu), pdp->teid_own);
755 vty_out(vty, "<-> %s:%08x%s", print_gsnaddr(&pdp->gsnru), pdp->teid_gn, VTY_NEWLINE);
756
Vadim Yanitskiyd7030d22019-05-13 22:10:24 +0700757 apn_name = osmo_apn_to_str(name_buf, pdp->apn_req.v, pdp->apn_req.l);
758 vty_out(vty, " APN requested: %s%s", apn_name ? name_buf : "(NONE)", VTY_NEWLINE);
759 apn_name = osmo_apn_to_str(name_buf, pdp->apn_use.v, pdp->apn_use.l);
760 vty_out(vty, " APN in use: %s%s", apn_name ? name_buf : "(NONE)", VTY_NEWLINE);
761
Pau Espin Pedrol03cce862019-08-20 12:23:14 +0200762 if ((peer = pdp_get_peer_ipv(pdp, false)))
763 vty_out(vty, " End-User Address (IPv4): %s%s",
764 in46a_ntop(&peer->addr, name_buf, sizeof(name_buf)), VTY_NEWLINE);
765 if ((peer = pdp_get_peer_ipv(pdp, true)))
766 vty_out(vty, " End-User Address (IPv6): %s%s",
767 in46a_ntop(&peer->addr, name_buf, sizeof(name_buf)), VTY_NEWLINE);
Harald Welte93fed3b2017-09-24 11:43:17 +0800768 vty_out(vty, " Transmit GTP Sequence Number for G-PDU: %s%s",
769 pdp->tx_gpdu_seq ? "Yes" : "No", VTY_NEWLINE);
Harald Weltedda21ed2017-08-12 15:07:02 +0200770}
771
772DEFUN(show_pdpctx_imsi, show_pdpctx_imsi_cmd,
Pau Espin Pedrol7b52f002019-05-31 16:15:16 +0200773 "show pdp-context ggsn NAME imsi IMSI [<0-15>]",
Harald Weltedda21ed2017-08-12 15:07:02 +0200774 SHOW_STR "Display information on PDP Context\n"
Pau Espin Pedrol7b52f002019-05-31 16:15:16 +0200775 GGSN_STR "GGSN Name\n"
Harald Weltedda21ed2017-08-12 15:07:02 +0200776 "PDP contexts for given IMSI\n"
777 "PDP context for given NSAPI\n")
778{
Pau Espin Pedrol7b52f002019-05-31 16:15:16 +0200779 struct ggsn_ctx *ggsn;
780 uint64_t imsi;
Harald Weltedda21ed2017-08-12 15:07:02 +0200781 unsigned int nsapi;
782 struct pdp_t *pdp;
783 int num_found = 0;
784
Pau Espin Pedrol7b52f002019-05-31 16:15:16 +0200785 ggsn = ggsn_find(argv[0]);
786 if (!ggsn) {
787 vty_out(vty, "%% No such GGSN '%s'%s", argv[0], VTY_NEWLINE);
788 return CMD_WARNING;
789 }
790
791 imsi = strtoull(argv[1], NULL, 10);
792
793 if (argc > 2) {
794 nsapi = atoi(argv[2]);
795 if (gtp_pdp_getimsi(ggsn->gsn, &pdp, imsi, nsapi)) {
Harald Weltedda21ed2017-08-12 15:07:02 +0200796 show_one_pdp(vty, pdp);
797 num_found++;
798 }
799 } else {
800 for (nsapi = 0; nsapi < PDP_MAXNSAPI; nsapi++) {
Pau Espin Pedrol7b52f002019-05-31 16:15:16 +0200801 if (gtp_pdp_getimsi(ggsn->gsn, &pdp, imsi, nsapi))
Harald Weltedda21ed2017-08-12 15:07:02 +0200802 continue;
803 show_one_pdp(vty, pdp);
804 num_found++;
805 }
806 }
807 if (num_found == 0) {
808 vty_out(vty, "%% No such PDP context found%s", VTY_NEWLINE);
809 return CMD_WARNING;
810 } else
811 return CMD_SUCCESS;
812}
813
Vadim Yanitskiyca276e02019-05-13 13:06:51 +0700814DEFUN(show_pdpctx_ip, show_pdpctx_ip_cmd,
815 "show pdp-context ggsn NAME ipv4 A.B.C.D",
816 SHOW_STR "Display information on PDP Context\n"
817 GGSN_STR "GGSN Name\n" "IPv4 address type\n" "IP address\n")
818{
819 struct ggsn_ctx *ggsn;
820 struct apn_ctx *apn;
821 unsigned int i;
822
823 ggsn = ggsn_find(argv[0]);
824 if (!ggsn) {
825 vty_out(vty, "%% No such GGSN '%s'%s", argv[0], VTY_NEWLINE);
826 return CMD_WARNING;
827 }
828
829 /* Iterate over all APNs of a given GGSN */
830 llist_for_each_entry(apn, &ggsn->apn_list, list) {
831 struct ippool_t *pool = apn->v4.pool;
832
833 /* In some rare cases, if GGSN fails to init TUN/TAP interfaces
834 * (e.g. due to insufficient permissions), it will continue to
835 * work in such broken state, and pool would be NULL. */
836 if (!pool)
837 continue;
838
839 /* Iterate over all IPv4 pool members */
840 for (i = 0; i < pool->listsize; i++) {
841 struct ippoolm_t *member = &pool->member[i];
842 if (member->inuse == 0)
843 continue;
844 if (strcmp(argv[1], in46a_ntoa(&member->addr)) == 0) {
845 show_one_pdp(vty, member->peer);
846 return CMD_SUCCESS;
847 }
848 }
849 }
850
851 vty_out(vty, "%% No PDP context found for IP '%s'%s", argv[1], VTY_NEWLINE);
852 return CMD_WARNING;
853}
854
Harald Weltedda21ed2017-08-12 15:07:02 +0200855/* show all (active) PDP contexts within a pool */
856static void ippool_show_pdp_contexts(struct vty *vty, struct ippool_t *pool)
857{
858 unsigned int i;
859
860 if (!pool)
861 return;
862
863 for (i = 0; i < pool->listsize; i++) {
864 struct ippoolm_t *member = &pool->member[i];
865 if (member->inuse == 0)
866 continue;
867 show_one_pdp(vty, member->peer);
868 }
869}
870
871/* show all (active) PDP contexts within an APN */
872static void apn_show_pdp_contexts(struct vty *vty, struct apn_ctx *apn)
873{
874 ippool_show_pdp_contexts(vty, apn->v4.pool);
875 ippool_show_pdp_contexts(vty, apn->v6.pool);
876}
877
878DEFUN(show_pdpctx, show_pdpctx_cmd,
Vadim Yanitskiy977b3392019-05-13 15:32:21 +0700879 "show pdp-context ggsn NAME",
Harald Weltedda21ed2017-08-12 15:07:02 +0200880 SHOW_STR "Show PDP Context Information\n"
Vadim Yanitskiy977b3392019-05-13 15:32:21 +0700881 GGSN_STR "GGSN Name\n")
Harald Weltedda21ed2017-08-12 15:07:02 +0200882{
883 struct ggsn_ctx *ggsn;
884 struct apn_ctx *apn;
885
886 ggsn = ggsn_find(argv[0]);
887 if (!ggsn) {
888 vty_out(vty, "%% No such GGSN '%s'%s", argv[0], VTY_NEWLINE);
889 return CMD_WARNING;
890 }
Vadim Yanitskiy977b3392019-05-13 15:32:21 +0700891
892 llist_for_each_entry(apn, &ggsn->apn_list, list)
Harald Weltedda21ed2017-08-12 15:07:02 +0200893 apn_show_pdp_contexts(vty, apn);
Harald Weltedda21ed2017-08-12 15:07:02 +0200894
895 return CMD_SUCCESS;
896}
897
Vadim Yanitskiy977b3392019-05-13 15:32:21 +0700898DEFUN(show_pdpctx_apn, show_pdpctx_apn_cmd,
899 "show pdp-context ggsn NAME apn APN",
900 SHOW_STR "Show PDP Context Information\n"
901 GGSN_STR "GGSN Name\n" "Filter by APN\n" "APN name\n")
902{
903 struct ggsn_ctx *ggsn;
904 struct apn_ctx *apn;
905
906 ggsn = ggsn_find(argv[0]);
907 if (!ggsn) {
908 vty_out(vty, "%% No such GGSN '%s'%s", argv[0], VTY_NEWLINE);
909 return CMD_WARNING;
910 }
911
912 apn = ggsn_find_apn(ggsn, argv[1]);
913 if (!apn) {
914 vty_out(vty, "%% No such APN '%s'%s", argv[1], VTY_NEWLINE);
915 return CMD_WARNING;
916 }
917
918 apn_show_pdp_contexts(vty, apn);
919 return CMD_SUCCESS;
920}
921
922/* Backwards compatibility: the VTY parser is (mis)interpreting
923 * "[apn APN]" as two separate elements: "[apn" and "APN]",
924 * but the first part somehow turns into command "ap". */
925ALIAS_DEPRECATED(show_pdpctx_apn, show_deprecated_pdpctx_apn_cmd,
926 "show pdp-context ggsn NAME ap APN",
927 SHOW_STR "Show PDP Context Information\n"
928 GGSN_STR "GGSN Name\n" "Filter by APN\n" "APN name\n");
929
Harald Weltedda21ed2017-08-12 15:07:02 +0200930static void show_apn(struct vty *vty, struct apn_ctx *apn)
931{
932 vty_out(vty, " APN: %s%s", apn->cfg.name, VTY_NEWLINE);
933 /* FIXME */
934}
935
936static void show_one_ggsn(struct vty *vty, struct ggsn_ctx *ggsn)
937{
938 struct apn_ctx *apn;
939 vty_out(vty, "GGSN %s: Bound to %s%s", ggsn->cfg.name, in46a_ntoa(&ggsn->cfg.listen_addr),
940 VTY_NEWLINE);
941 /* FIXME */
942
943 llist_for_each_entry(apn, &ggsn->apn_list, list)
944 show_apn(vty, apn);
945}
946
947DEFUN(show_ggsn, show_ggsn_cmd,
948 "show ggsn [NAME]",
949 SHOW_STR "Display information on the GGSN\n")
950{
951 struct ggsn_ctx *ggsn;
952
953 if (argc == 0) {
954 llist_for_each_entry(ggsn, &g_ggsn_list, list)
955 show_one_ggsn(vty, ggsn);
956 } else {
957 ggsn = ggsn_find(argv[0]);
958 if (!ggsn)
959 return CMD_WARNING;
960 show_one_ggsn(vty, ggsn);
961 }
962
963 return CMD_SUCCESS;
964}
965
966int ggsn_vty_init(void)
967{
968 install_element_ve(&show_pdpctx_cmd);
Vadim Yanitskiy977b3392019-05-13 15:32:21 +0700969 install_element_ve(&show_pdpctx_apn_cmd);
970 install_element_ve(&show_deprecated_pdpctx_apn_cmd);
Harald Weltedda21ed2017-08-12 15:07:02 +0200971 install_element_ve(&show_pdpctx_imsi_cmd);
Vadim Yanitskiyca276e02019-05-13 13:06:51 +0700972 install_element_ve(&show_pdpctx_ip_cmd);
Harald Weltedda21ed2017-08-12 15:07:02 +0200973 install_element_ve(&show_ggsn_cmd);
974
975 install_element(CONFIG_NODE, &cfg_ggsn_cmd);
976 install_element(CONFIG_NODE, &cfg_no_ggsn_cmd);
Pau Espin Pedrol840ce8a2017-11-16 17:01:44 +0100977
Harald Weltedda21ed2017-08-12 15:07:02 +0200978 install_node(&ggsn_node, config_write_ggsn);
Harald Weltedda21ed2017-08-12 15:07:02 +0200979 install_element(GGSN_NODE, &cfg_description_cmd);
980 install_element(GGSN_NODE, &cfg_no_description_cmd);
981 install_element(GGSN_NODE, &cfg_ggsn_shutdown_cmd);
982 install_element(GGSN_NODE, &cfg_ggsn_no_shutdown_cmd);
Harald Welte98146772017-09-05 17:41:20 +0200983 install_element(GGSN_NODE, &cfg_ggsn_bind_ip_cmd);
984 install_element(GGSN_NODE, &cfg_ggsn_gtpc_ip_cmd);
985 install_element(GGSN_NODE, &cfg_ggsn_gtpu_ip_cmd);
Harald Weltedda21ed2017-08-12 15:07:02 +0200986 install_element(GGSN_NODE, &cfg_ggsn_state_dir_cmd);
987 install_element(GGSN_NODE, &cfg_ggsn_apn_cmd);
988 install_element(GGSN_NODE, &cfg_ggsn_no_apn_cmd);
989 install_element(GGSN_NODE, &cfg_ggsn_default_apn_cmd);
990 install_element(GGSN_NODE, &cfg_ggsn_no_default_apn_cmd);
991
992 install_node(&apn_node, NULL);
Harald Weltedda21ed2017-08-12 15:07:02 +0200993 install_element(APN_NODE, &cfg_description_cmd);
994 install_element(APN_NODE, &cfg_no_description_cmd);
995 install_element(APN_NODE, &cfg_apn_shutdown_cmd);
996 install_element(APN_NODE, &cfg_apn_no_shutdown_cmd);
997 install_element(APN_NODE, &cfg_apn_gtpu_mode_cmd);
998 install_element(APN_NODE, &cfg_apn_type_support_cmd);
999 install_element(APN_NODE, &cfg_apn_no_type_support_cmd);
1000 install_element(APN_NODE, &cfg_apn_tun_dev_name_cmd);
1001 install_element(APN_NODE, &cfg_apn_ipup_script_cmd);
1002 install_element(APN_NODE, &cfg_apn_no_ipup_script_cmd);
1003 install_element(APN_NODE, &cfg_apn_ipdown_script_cmd);
1004 install_element(APN_NODE, &cfg_apn_no_ipdown_script_cmd);
1005 install_element(APN_NODE, &cfg_apn_ip_prefix_cmd);
1006 install_element(APN_NODE, &cfg_apn_ipv6_prefix_cmd);
1007 install_element(APN_NODE, &cfg_apn_ip_dns_cmd);
1008 install_element(APN_NODE, &cfg_apn_ipv6_dns_cmd);
1009 install_element(APN_NODE, &cfg_apn_no_dns_cmd);
1010 install_element(APN_NODE, &cfg_apn_ip_ifconfig_cmd);
1011 install_element(APN_NODE, &cfg_apn_no_ip_ifconfig_cmd);
1012 install_element(APN_NODE, &cfg_apn_ipv6_ifconfig_cmd);
1013 install_element(APN_NODE, &cfg_apn_no_ipv6_ifconfig_cmd);
Pau Espin Pedrol37c45e32017-12-14 14:09:13 +01001014 install_element(APN_NODE, &cfg_apn_ipv6_linklocal_cmd);
1015 install_element(APN_NODE, &cfg_apn_no_ipv6_linklocal_cmd);
Harald Welte93fed3b2017-09-24 11:43:17 +08001016 install_element(APN_NODE, &cfg_apn_gpdu_seq_cmd);
1017 install_element(APN_NODE, &cfg_apn_no_gpdu_seq_cmd);
Harald Weltedda21ed2017-08-12 15:07:02 +02001018
1019 return 0;
1020}
1021
1022static int ggsn_vty_is_config_node(struct vty *vty, int node)
1023{
1024 switch (node) {
1025 case GGSN_NODE:
1026 case APN_NODE:
1027 return 1;
1028 default:
1029 return 0;
1030 }
1031}
1032
1033static int ggsn_vty_go_parent(struct vty *vty)
1034{
1035 switch (vty->node) {
1036 case GGSN_NODE:
1037 vty->node = CONFIG_NODE;
1038 vty->index = NULL;
1039 vty->index_sub = NULL;
1040 break;
1041 case APN_NODE:
1042 vty->node = GGSN_NODE;
1043 {
1044 struct apn_ctx *apn = vty->index;
1045 vty->index = apn->ggsn;
1046 vty->index_sub = &apn->ggsn->cfg.description;
1047 }
1048 break;
Vadim Yanitskiy906c2092018-05-09 23:11:27 +07001049 default:
1050 vty->node = CONFIG_NODE;
1051 vty->index = NULL;
1052 vty->index_sub = NULL;
Harald Weltedda21ed2017-08-12 15:07:02 +02001053 }
1054
1055 return vty->node;
1056}
1057
1058static const char ggsn_copyright[] =
1059 "Copyright (C) 2011-2017 Harald Welte <laforge@gnumonks.org>\r\n"
1060 "Copyright (C) 2012-2017 Holger Hans Peter Freyther <holger@moiji-mobile.com>\r\n"
1061 "Copyright (C) 2012-2017 sysmocom - s.f.m.c. GmbH\r\n"
1062 "Copyright (C) 2002-2005 Mondru AB\r\n"
1063 "License GPLv2: GNU GPL version 2 <http://gnu.org/licenses/gpl-2.0.html>\r\n"
1064 "This is free software: you are free to change and redistribute it.\r\n"
1065 "There is NO WARRANTY, to the extent permitted by law.\r\n";
1066
1067struct vty_app_info g_vty_info = {
Harald Welte632e8432017-09-05 18:12:14 +02001068 .name = "OsmoGGSN",
Harald Weltedda21ed2017-08-12 15:07:02 +02001069 .version = PACKAGE_VERSION,
1070 .copyright = ggsn_copyright,
1071 .go_parent_cb = ggsn_vty_go_parent,
1072 .is_config_node = ggsn_vty_is_config_node,
1073};