blob: a649173c05cd69d4497ad401475bb9a36bc98b64 [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
Pau Espin Pedrol310ea1d2019-08-20 14:15:25 +0200737/* Useful for v4v6 APNs, where we first iterate over v4 pool and then over v6
738 pool. param v4only can be used to avoid printing duplicates for pdp context
739 containing both IPv4 and IPv6 addresses. */
740static void show_one_pdp_v4only(struct vty *vty, struct pdp_t *pdp, bool v4only)
Harald Weltedda21ed2017-08-12 15:07:02 +0200741{
Pau Espin Pedrol310ea1d2019-08-20 14:15:25 +0200742 struct ippoolm_t *peer4, *peer6;
Vadim Yanitskiyd7030d22019-05-13 22:10:24 +0700743 char name_buf[256];
744 char *apn_name;
Vadim Yanitskiyfb625042019-05-19 02:00:31 +0700745 int rc;
746
Pau Espin Pedrol310ea1d2019-08-20 14:15:25 +0200747 peer4 = pdp_get_peer_ipv(pdp, false);
748 peer6 = pdp_get_peer_ipv(pdp, true);
749
750 if (v4only && peer6)
751 return;
752
Vadim Yanitskiyfb625042019-05-19 02:00:31 +0700753 /* Attempt to decode MSISDN */
754 rc = gsm48_decode_bcd_number2(name_buf, sizeof(name_buf),
755 pdp->msisdn.v, pdp->msisdn.l, 0);
Harald Weltedda21ed2017-08-12 15:07:02 +0200756
757 vty_out(vty, "IMSI: %s, NSAPI: %u, MSISDN: %s%s", imsi_gtp2str(&pdp->imsi), pdp->nsapi,
Vadim Yanitskiyfb625042019-05-19 02:00:31 +0700758 rc ? "(NONE)" : name_buf, VTY_NEWLINE);
Harald Weltedda21ed2017-08-12 15:07:02 +0200759
760 vty_out(vty, " Control: %s:%08x ", print_gsnaddr(&pdp->gsnlc), pdp->teic_own);
761 vty_out(vty, "<-> %s:%08x%s", print_gsnaddr(&pdp->gsnrc), pdp->teic_gn, VTY_NEWLINE);
762
763 vty_out(vty, " Data: %s:%08x ", print_gsnaddr(&pdp->gsnlu), pdp->teid_own);
764 vty_out(vty, "<-> %s:%08x%s", print_gsnaddr(&pdp->gsnru), pdp->teid_gn, VTY_NEWLINE);
765
Vadim Yanitskiyd7030d22019-05-13 22:10:24 +0700766 apn_name = osmo_apn_to_str(name_buf, pdp->apn_req.v, pdp->apn_req.l);
767 vty_out(vty, " APN requested: %s%s", apn_name ? name_buf : "(NONE)", VTY_NEWLINE);
768 apn_name = osmo_apn_to_str(name_buf, pdp->apn_use.v, pdp->apn_use.l);
769 vty_out(vty, " APN in use: %s%s", apn_name ? name_buf : "(NONE)", VTY_NEWLINE);
770
Pau Espin Pedrol310ea1d2019-08-20 14:15:25 +0200771 if (peer4)
Pau Espin Pedrol03cce862019-08-20 12:23:14 +0200772 vty_out(vty, " End-User Address (IPv4): %s%s",
Pau Espin Pedrol310ea1d2019-08-20 14:15:25 +0200773 in46a_ntop(&peer4->addr, name_buf, sizeof(name_buf)), VTY_NEWLINE);
774 if (peer6)
Pau Espin Pedrol03cce862019-08-20 12:23:14 +0200775 vty_out(vty, " End-User Address (IPv6): %s%s",
Pau Espin Pedrol310ea1d2019-08-20 14:15:25 +0200776 in46a_ntop(&peer6->addr, name_buf, sizeof(name_buf)), VTY_NEWLINE);
Harald Welte93fed3b2017-09-24 11:43:17 +0800777 vty_out(vty, " Transmit GTP Sequence Number for G-PDU: %s%s",
778 pdp->tx_gpdu_seq ? "Yes" : "No", VTY_NEWLINE);
Harald Weltedda21ed2017-08-12 15:07:02 +0200779}
780
Pau Espin Pedrol310ea1d2019-08-20 14:15:25 +0200781static void show_one_pdp(struct vty *vty, struct pdp_t *pdp)
782{
783 show_one_pdp_v4only(vty, pdp, false);
784}
785
Harald Weltedda21ed2017-08-12 15:07:02 +0200786DEFUN(show_pdpctx_imsi, show_pdpctx_imsi_cmd,
Pau Espin Pedrol7b52f002019-05-31 16:15:16 +0200787 "show pdp-context ggsn NAME imsi IMSI [<0-15>]",
Harald Weltedda21ed2017-08-12 15:07:02 +0200788 SHOW_STR "Display information on PDP Context\n"
Pau Espin Pedrol7b52f002019-05-31 16:15:16 +0200789 GGSN_STR "GGSN Name\n"
Harald Weltedda21ed2017-08-12 15:07:02 +0200790 "PDP contexts for given IMSI\n"
791 "PDP context for given NSAPI\n")
792{
Pau Espin Pedrol7b52f002019-05-31 16:15:16 +0200793 struct ggsn_ctx *ggsn;
794 uint64_t imsi;
Harald Weltedda21ed2017-08-12 15:07:02 +0200795 unsigned int nsapi;
796 struct pdp_t *pdp;
797 int num_found = 0;
798
Pau Espin Pedrol7b52f002019-05-31 16:15:16 +0200799 ggsn = ggsn_find(argv[0]);
800 if (!ggsn) {
801 vty_out(vty, "%% No such GGSN '%s'%s", argv[0], VTY_NEWLINE);
802 return CMD_WARNING;
803 }
804
805 imsi = strtoull(argv[1], NULL, 10);
806
807 if (argc > 2) {
808 nsapi = atoi(argv[2]);
809 if (gtp_pdp_getimsi(ggsn->gsn, &pdp, imsi, nsapi)) {
Harald Weltedda21ed2017-08-12 15:07:02 +0200810 show_one_pdp(vty, pdp);
811 num_found++;
812 }
813 } else {
814 for (nsapi = 0; nsapi < PDP_MAXNSAPI; nsapi++) {
Pau Espin Pedrol7b52f002019-05-31 16:15:16 +0200815 if (gtp_pdp_getimsi(ggsn->gsn, &pdp, imsi, nsapi))
Harald Weltedda21ed2017-08-12 15:07:02 +0200816 continue;
817 show_one_pdp(vty, pdp);
818 num_found++;
819 }
820 }
821 if (num_found == 0) {
822 vty_out(vty, "%% No such PDP context found%s", VTY_NEWLINE);
823 return CMD_WARNING;
824 } else
825 return CMD_SUCCESS;
826}
827
Vadim Yanitskiyca276e02019-05-13 13:06:51 +0700828DEFUN(show_pdpctx_ip, show_pdpctx_ip_cmd,
829 "show pdp-context ggsn NAME ipv4 A.B.C.D",
830 SHOW_STR "Display information on PDP Context\n"
831 GGSN_STR "GGSN Name\n" "IPv4 address type\n" "IP address\n")
832{
833 struct ggsn_ctx *ggsn;
834 struct apn_ctx *apn;
835 unsigned int i;
836
837 ggsn = ggsn_find(argv[0]);
838 if (!ggsn) {
839 vty_out(vty, "%% No such GGSN '%s'%s", argv[0], VTY_NEWLINE);
840 return CMD_WARNING;
841 }
842
843 /* Iterate over all APNs of a given GGSN */
844 llist_for_each_entry(apn, &ggsn->apn_list, list) {
845 struct ippool_t *pool = apn->v4.pool;
846
847 /* In some rare cases, if GGSN fails to init TUN/TAP interfaces
848 * (e.g. due to insufficient permissions), it will continue to
849 * work in such broken state, and pool would be NULL. */
850 if (!pool)
851 continue;
852
853 /* Iterate over all IPv4 pool members */
854 for (i = 0; i < pool->listsize; i++) {
855 struct ippoolm_t *member = &pool->member[i];
856 if (member->inuse == 0)
857 continue;
858 if (strcmp(argv[1], in46a_ntoa(&member->addr)) == 0) {
859 show_one_pdp(vty, member->peer);
860 return CMD_SUCCESS;
861 }
862 }
863 }
864
865 vty_out(vty, "%% No PDP context found for IP '%s'%s", argv[1], VTY_NEWLINE);
866 return CMD_WARNING;
867}
868
Harald Weltedda21ed2017-08-12 15:07:02 +0200869/* show all (active) PDP contexts within a pool */
Pau Espin Pedrol310ea1d2019-08-20 14:15:25 +0200870static void ippool_show_pdp_contexts(struct vty *vty, struct ippool_t *pool, bool pdp_v4only)
Harald Weltedda21ed2017-08-12 15:07:02 +0200871{
872 unsigned int i;
873
874 if (!pool)
875 return;
876
877 for (i = 0; i < pool->listsize; i++) {
878 struct ippoolm_t *member = &pool->member[i];
879 if (member->inuse == 0)
880 continue;
Pau Espin Pedrol310ea1d2019-08-20 14:15:25 +0200881 show_one_pdp_v4only(vty, member->peer, pdp_v4only);
Harald Weltedda21ed2017-08-12 15:07:02 +0200882 }
883}
884
885/* show all (active) PDP contexts within an APN */
886static void apn_show_pdp_contexts(struct vty *vty, struct apn_ctx *apn)
887{
Pau Espin Pedrol310ea1d2019-08-20 14:15:25 +0200888 ippool_show_pdp_contexts(vty, apn->v4.pool, true);
889 ippool_show_pdp_contexts(vty, apn->v6.pool, false);
Harald Weltedda21ed2017-08-12 15:07:02 +0200890}
891
892DEFUN(show_pdpctx, show_pdpctx_cmd,
Vadim Yanitskiy977b3392019-05-13 15:32:21 +0700893 "show pdp-context ggsn NAME",
Harald Weltedda21ed2017-08-12 15:07:02 +0200894 SHOW_STR "Show PDP Context Information\n"
Vadim Yanitskiy977b3392019-05-13 15:32:21 +0700895 GGSN_STR "GGSN Name\n")
Harald Weltedda21ed2017-08-12 15:07:02 +0200896{
897 struct ggsn_ctx *ggsn;
898 struct apn_ctx *apn;
899
900 ggsn = ggsn_find(argv[0]);
901 if (!ggsn) {
902 vty_out(vty, "%% No such GGSN '%s'%s", argv[0], VTY_NEWLINE);
903 return CMD_WARNING;
904 }
Vadim Yanitskiy977b3392019-05-13 15:32:21 +0700905
906 llist_for_each_entry(apn, &ggsn->apn_list, list)
Harald Weltedda21ed2017-08-12 15:07:02 +0200907 apn_show_pdp_contexts(vty, apn);
Harald Weltedda21ed2017-08-12 15:07:02 +0200908
909 return CMD_SUCCESS;
910}
911
Vadim Yanitskiy977b3392019-05-13 15:32:21 +0700912DEFUN(show_pdpctx_apn, show_pdpctx_apn_cmd,
913 "show pdp-context ggsn NAME apn APN",
914 SHOW_STR "Show PDP Context Information\n"
915 GGSN_STR "GGSN Name\n" "Filter by APN\n" "APN name\n")
916{
917 struct ggsn_ctx *ggsn;
918 struct apn_ctx *apn;
919
920 ggsn = ggsn_find(argv[0]);
921 if (!ggsn) {
922 vty_out(vty, "%% No such GGSN '%s'%s", argv[0], VTY_NEWLINE);
923 return CMD_WARNING;
924 }
925
926 apn = ggsn_find_apn(ggsn, argv[1]);
927 if (!apn) {
928 vty_out(vty, "%% No such APN '%s'%s", argv[1], VTY_NEWLINE);
929 return CMD_WARNING;
930 }
931
932 apn_show_pdp_contexts(vty, apn);
933 return CMD_SUCCESS;
934}
935
936/* Backwards compatibility: the VTY parser is (mis)interpreting
937 * "[apn APN]" as two separate elements: "[apn" and "APN]",
938 * but the first part somehow turns into command "ap". */
939ALIAS_DEPRECATED(show_pdpctx_apn, show_deprecated_pdpctx_apn_cmd,
940 "show pdp-context ggsn NAME ap APN",
941 SHOW_STR "Show PDP Context Information\n"
942 GGSN_STR "GGSN Name\n" "Filter by APN\n" "APN name\n");
943
Harald Weltedda21ed2017-08-12 15:07:02 +0200944static void show_apn(struct vty *vty, struct apn_ctx *apn)
945{
946 vty_out(vty, " APN: %s%s", apn->cfg.name, VTY_NEWLINE);
947 /* FIXME */
948}
949
950static void show_one_ggsn(struct vty *vty, struct ggsn_ctx *ggsn)
951{
952 struct apn_ctx *apn;
953 vty_out(vty, "GGSN %s: Bound to %s%s", ggsn->cfg.name, in46a_ntoa(&ggsn->cfg.listen_addr),
954 VTY_NEWLINE);
955 /* FIXME */
956
957 llist_for_each_entry(apn, &ggsn->apn_list, list)
958 show_apn(vty, apn);
959}
960
961DEFUN(show_ggsn, show_ggsn_cmd,
962 "show ggsn [NAME]",
963 SHOW_STR "Display information on the GGSN\n")
964{
965 struct ggsn_ctx *ggsn;
966
967 if (argc == 0) {
968 llist_for_each_entry(ggsn, &g_ggsn_list, list)
969 show_one_ggsn(vty, ggsn);
970 } else {
971 ggsn = ggsn_find(argv[0]);
972 if (!ggsn)
973 return CMD_WARNING;
974 show_one_ggsn(vty, ggsn);
975 }
976
977 return CMD_SUCCESS;
978}
979
980int ggsn_vty_init(void)
981{
982 install_element_ve(&show_pdpctx_cmd);
Vadim Yanitskiy977b3392019-05-13 15:32:21 +0700983 install_element_ve(&show_pdpctx_apn_cmd);
984 install_element_ve(&show_deprecated_pdpctx_apn_cmd);
Harald Weltedda21ed2017-08-12 15:07:02 +0200985 install_element_ve(&show_pdpctx_imsi_cmd);
Vadim Yanitskiyca276e02019-05-13 13:06:51 +0700986 install_element_ve(&show_pdpctx_ip_cmd);
Harald Weltedda21ed2017-08-12 15:07:02 +0200987 install_element_ve(&show_ggsn_cmd);
988
989 install_element(CONFIG_NODE, &cfg_ggsn_cmd);
990 install_element(CONFIG_NODE, &cfg_no_ggsn_cmd);
Pau Espin Pedrol840ce8a2017-11-16 17:01:44 +0100991
Harald Weltedda21ed2017-08-12 15:07:02 +0200992 install_node(&ggsn_node, config_write_ggsn);
Harald Weltedda21ed2017-08-12 15:07:02 +0200993 install_element(GGSN_NODE, &cfg_description_cmd);
994 install_element(GGSN_NODE, &cfg_no_description_cmd);
995 install_element(GGSN_NODE, &cfg_ggsn_shutdown_cmd);
996 install_element(GGSN_NODE, &cfg_ggsn_no_shutdown_cmd);
Harald Welte98146772017-09-05 17:41:20 +0200997 install_element(GGSN_NODE, &cfg_ggsn_bind_ip_cmd);
998 install_element(GGSN_NODE, &cfg_ggsn_gtpc_ip_cmd);
999 install_element(GGSN_NODE, &cfg_ggsn_gtpu_ip_cmd);
Harald Weltedda21ed2017-08-12 15:07:02 +02001000 install_element(GGSN_NODE, &cfg_ggsn_state_dir_cmd);
1001 install_element(GGSN_NODE, &cfg_ggsn_apn_cmd);
1002 install_element(GGSN_NODE, &cfg_ggsn_no_apn_cmd);
1003 install_element(GGSN_NODE, &cfg_ggsn_default_apn_cmd);
1004 install_element(GGSN_NODE, &cfg_ggsn_no_default_apn_cmd);
1005
1006 install_node(&apn_node, NULL);
Harald Weltedda21ed2017-08-12 15:07:02 +02001007 install_element(APN_NODE, &cfg_description_cmd);
1008 install_element(APN_NODE, &cfg_no_description_cmd);
1009 install_element(APN_NODE, &cfg_apn_shutdown_cmd);
1010 install_element(APN_NODE, &cfg_apn_no_shutdown_cmd);
1011 install_element(APN_NODE, &cfg_apn_gtpu_mode_cmd);
1012 install_element(APN_NODE, &cfg_apn_type_support_cmd);
1013 install_element(APN_NODE, &cfg_apn_no_type_support_cmd);
1014 install_element(APN_NODE, &cfg_apn_tun_dev_name_cmd);
1015 install_element(APN_NODE, &cfg_apn_ipup_script_cmd);
1016 install_element(APN_NODE, &cfg_apn_no_ipup_script_cmd);
1017 install_element(APN_NODE, &cfg_apn_ipdown_script_cmd);
1018 install_element(APN_NODE, &cfg_apn_no_ipdown_script_cmd);
1019 install_element(APN_NODE, &cfg_apn_ip_prefix_cmd);
1020 install_element(APN_NODE, &cfg_apn_ipv6_prefix_cmd);
1021 install_element(APN_NODE, &cfg_apn_ip_dns_cmd);
1022 install_element(APN_NODE, &cfg_apn_ipv6_dns_cmd);
1023 install_element(APN_NODE, &cfg_apn_no_dns_cmd);
1024 install_element(APN_NODE, &cfg_apn_ip_ifconfig_cmd);
1025 install_element(APN_NODE, &cfg_apn_no_ip_ifconfig_cmd);
1026 install_element(APN_NODE, &cfg_apn_ipv6_ifconfig_cmd);
1027 install_element(APN_NODE, &cfg_apn_no_ipv6_ifconfig_cmd);
Pau Espin Pedrol37c45e32017-12-14 14:09:13 +01001028 install_element(APN_NODE, &cfg_apn_ipv6_linklocal_cmd);
1029 install_element(APN_NODE, &cfg_apn_no_ipv6_linklocal_cmd);
Harald Welte93fed3b2017-09-24 11:43:17 +08001030 install_element(APN_NODE, &cfg_apn_gpdu_seq_cmd);
1031 install_element(APN_NODE, &cfg_apn_no_gpdu_seq_cmd);
Harald Weltedda21ed2017-08-12 15:07:02 +02001032
1033 return 0;
1034}
1035
1036static int ggsn_vty_is_config_node(struct vty *vty, int node)
1037{
1038 switch (node) {
1039 case GGSN_NODE:
1040 case APN_NODE:
1041 return 1;
1042 default:
1043 return 0;
1044 }
1045}
1046
1047static int ggsn_vty_go_parent(struct vty *vty)
1048{
1049 switch (vty->node) {
1050 case GGSN_NODE:
1051 vty->node = CONFIG_NODE;
1052 vty->index = NULL;
1053 vty->index_sub = NULL;
1054 break;
1055 case APN_NODE:
1056 vty->node = GGSN_NODE;
1057 {
1058 struct apn_ctx *apn = vty->index;
1059 vty->index = apn->ggsn;
1060 vty->index_sub = &apn->ggsn->cfg.description;
1061 }
1062 break;
Vadim Yanitskiy906c2092018-05-09 23:11:27 +07001063 default:
1064 vty->node = CONFIG_NODE;
1065 vty->index = NULL;
1066 vty->index_sub = NULL;
Harald Weltedda21ed2017-08-12 15:07:02 +02001067 }
1068
1069 return vty->node;
1070}
1071
1072static const char ggsn_copyright[] =
1073 "Copyright (C) 2011-2017 Harald Welte <laforge@gnumonks.org>\r\n"
1074 "Copyright (C) 2012-2017 Holger Hans Peter Freyther <holger@moiji-mobile.com>\r\n"
1075 "Copyright (C) 2012-2017 sysmocom - s.f.m.c. GmbH\r\n"
1076 "Copyright (C) 2002-2005 Mondru AB\r\n"
1077 "License GPLv2: GNU GPL version 2 <http://gnu.org/licenses/gpl-2.0.html>\r\n"
1078 "This is free software: you are free to change and redistribute it.\r\n"
1079 "There is NO WARRANTY, to the extent permitted by law.\r\n";
1080
1081struct vty_app_info g_vty_info = {
Harald Welte632e8432017-09-05 18:12:14 +02001082 .name = "OsmoGGSN",
Harald Weltedda21ed2017-08-12 15:07:02 +02001083 .version = PACKAGE_VERSION,
1084 .copyright = ggsn_copyright,
1085 .go_parent_cb = ggsn_vty_go_parent,
1086 .is_config_node = ggsn_vty_is_config_node,
1087};