blob: 5684f5ae0d175cd8f790ff2c13ffa929dd66aef0 [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
Pau Espin Pedrola0196312019-08-20 19:11:29 +0200760 vty_out(vty, " Version: %d", pdp->version);
761 if (pdp->version == 1) {
762 if (!pdp->secondary) {
763 vty_out(vty, ", Primary, Num Secondaries: %d%s%s",
764 pdp_count_secondary(pdp) - 1, /* primary included in count */
765 pdp->nodata ? ", No User Plane": "",
766 VTY_NEWLINE);
767 } else {
768 vty_out(vty, ", Secondary%s", VTY_NEWLINE);
769 }
770 } else {
771 vty_out(vty, "%s", VTY_NEWLINE);
772 }
773
Harald Weltedda21ed2017-08-12 15:07:02 +0200774 vty_out(vty, " Control: %s:%08x ", print_gsnaddr(&pdp->gsnlc), pdp->teic_own);
775 vty_out(vty, "<-> %s:%08x%s", print_gsnaddr(&pdp->gsnrc), pdp->teic_gn, VTY_NEWLINE);
776
777 vty_out(vty, " Data: %s:%08x ", print_gsnaddr(&pdp->gsnlu), pdp->teid_own);
778 vty_out(vty, "<-> %s:%08x%s", print_gsnaddr(&pdp->gsnru), pdp->teid_gn, VTY_NEWLINE);
779
Vadim Yanitskiyd7030d22019-05-13 22:10:24 +0700780 apn_name = osmo_apn_to_str(name_buf, pdp->apn_req.v, pdp->apn_req.l);
781 vty_out(vty, " APN requested: %s%s", apn_name ? name_buf : "(NONE)", VTY_NEWLINE);
782 apn_name = osmo_apn_to_str(name_buf, pdp->apn_use.v, pdp->apn_use.l);
783 vty_out(vty, " APN in use: %s%s", apn_name ? name_buf : "(NONE)", VTY_NEWLINE);
784
Pau Espin Pedrol310ea1d2019-08-20 14:15:25 +0200785 if (peer4)
Pau Espin Pedrol03cce862019-08-20 12:23:14 +0200786 vty_out(vty, " End-User Address (IPv4): %s%s",
Pau Espin Pedrol310ea1d2019-08-20 14:15:25 +0200787 in46a_ntop(&peer4->addr, name_buf, sizeof(name_buf)), VTY_NEWLINE);
788 if (peer6)
Pau Espin Pedrol03cce862019-08-20 12:23:14 +0200789 vty_out(vty, " End-User Address (IPv6): %s%s",
Pau Espin Pedrol310ea1d2019-08-20 14:15:25 +0200790 in46a_ntop(&peer6->addr, name_buf, sizeof(name_buf)), VTY_NEWLINE);
Harald Welte93fed3b2017-09-24 11:43:17 +0800791 vty_out(vty, " Transmit GTP Sequence Number for G-PDU: %s%s",
792 pdp->tx_gpdu_seq ? "Yes" : "No", VTY_NEWLINE);
Harald Weltedda21ed2017-08-12 15:07:02 +0200793}
794
Pau Espin Pedrol310ea1d2019-08-20 14:15:25 +0200795static void show_one_pdp(struct vty *vty, struct pdp_t *pdp)
796{
797 show_one_pdp_v4only(vty, pdp, false);
798}
799
Harald Weltedda21ed2017-08-12 15:07:02 +0200800DEFUN(show_pdpctx_imsi, show_pdpctx_imsi_cmd,
Pau Espin Pedrol7b52f002019-05-31 16:15:16 +0200801 "show pdp-context ggsn NAME imsi IMSI [<0-15>]",
Harald Weltedda21ed2017-08-12 15:07:02 +0200802 SHOW_STR "Display information on PDP Context\n"
Pau Espin Pedrol7b52f002019-05-31 16:15:16 +0200803 GGSN_STR "GGSN Name\n"
Harald Weltedda21ed2017-08-12 15:07:02 +0200804 "PDP contexts for given IMSI\n"
805 "PDP context for given NSAPI\n")
806{
Pau Espin Pedrol7b52f002019-05-31 16:15:16 +0200807 struct ggsn_ctx *ggsn;
808 uint64_t imsi;
Harald Weltedda21ed2017-08-12 15:07:02 +0200809 unsigned int nsapi;
810 struct pdp_t *pdp;
811 int num_found = 0;
812
Pau Espin Pedrol7b52f002019-05-31 16:15:16 +0200813 ggsn = ggsn_find(argv[0]);
814 if (!ggsn) {
815 vty_out(vty, "%% No such GGSN '%s'%s", argv[0], VTY_NEWLINE);
816 return CMD_WARNING;
817 }
818
819 imsi = strtoull(argv[1], NULL, 10);
820
821 if (argc > 2) {
822 nsapi = atoi(argv[2]);
823 if (gtp_pdp_getimsi(ggsn->gsn, &pdp, imsi, nsapi)) {
Harald Weltedda21ed2017-08-12 15:07:02 +0200824 show_one_pdp(vty, pdp);
825 num_found++;
826 }
827 } else {
828 for (nsapi = 0; nsapi < PDP_MAXNSAPI; nsapi++) {
Pau Espin Pedrol7b52f002019-05-31 16:15:16 +0200829 if (gtp_pdp_getimsi(ggsn->gsn, &pdp, imsi, nsapi))
Harald Weltedda21ed2017-08-12 15:07:02 +0200830 continue;
831 show_one_pdp(vty, pdp);
832 num_found++;
833 }
834 }
835 if (num_found == 0) {
836 vty_out(vty, "%% No such PDP context found%s", VTY_NEWLINE);
837 return CMD_WARNING;
838 } else
839 return CMD_SUCCESS;
840}
841
Vadim Yanitskiyca276e02019-05-13 13:06:51 +0700842DEFUN(show_pdpctx_ip, show_pdpctx_ip_cmd,
843 "show pdp-context ggsn NAME ipv4 A.B.C.D",
844 SHOW_STR "Display information on PDP Context\n"
845 GGSN_STR "GGSN Name\n" "IPv4 address type\n" "IP address\n")
846{
847 struct ggsn_ctx *ggsn;
848 struct apn_ctx *apn;
849 unsigned int i;
850
851 ggsn = ggsn_find(argv[0]);
852 if (!ggsn) {
853 vty_out(vty, "%% No such GGSN '%s'%s", argv[0], VTY_NEWLINE);
854 return CMD_WARNING;
855 }
856
857 /* Iterate over all APNs of a given GGSN */
858 llist_for_each_entry(apn, &ggsn->apn_list, list) {
859 struct ippool_t *pool = apn->v4.pool;
860
861 /* In some rare cases, if GGSN fails to init TUN/TAP interfaces
862 * (e.g. due to insufficient permissions), it will continue to
863 * work in such broken state, and pool would be NULL. */
864 if (!pool)
865 continue;
866
867 /* Iterate over all IPv4 pool members */
868 for (i = 0; i < pool->listsize; i++) {
869 struct ippoolm_t *member = &pool->member[i];
870 if (member->inuse == 0)
871 continue;
872 if (strcmp(argv[1], in46a_ntoa(&member->addr)) == 0) {
873 show_one_pdp(vty, member->peer);
874 return CMD_SUCCESS;
875 }
876 }
877 }
878
879 vty_out(vty, "%% No PDP context found for IP '%s'%s", argv[1], VTY_NEWLINE);
880 return CMD_WARNING;
881}
882
Harald Weltedda21ed2017-08-12 15:07:02 +0200883/* show all (active) PDP contexts within a pool */
Pau Espin Pedrol310ea1d2019-08-20 14:15:25 +0200884static void ippool_show_pdp_contexts(struct vty *vty, struct ippool_t *pool, bool pdp_v4only)
Harald Weltedda21ed2017-08-12 15:07:02 +0200885{
886 unsigned int i;
887
888 if (!pool)
889 return;
890
891 for (i = 0; i < pool->listsize; i++) {
892 struct ippoolm_t *member = &pool->member[i];
893 if (member->inuse == 0)
894 continue;
Pau Espin Pedrol310ea1d2019-08-20 14:15:25 +0200895 show_one_pdp_v4only(vty, member->peer, pdp_v4only);
Harald Weltedda21ed2017-08-12 15:07:02 +0200896 }
897}
898
899/* show all (active) PDP contexts within an APN */
900static void apn_show_pdp_contexts(struct vty *vty, struct apn_ctx *apn)
901{
Pau Espin Pedrol310ea1d2019-08-20 14:15:25 +0200902 ippool_show_pdp_contexts(vty, apn->v4.pool, true);
903 ippool_show_pdp_contexts(vty, apn->v6.pool, false);
Harald Weltedda21ed2017-08-12 15:07:02 +0200904}
905
906DEFUN(show_pdpctx, show_pdpctx_cmd,
Vadim Yanitskiy977b3392019-05-13 15:32:21 +0700907 "show pdp-context ggsn NAME",
Harald Weltedda21ed2017-08-12 15:07:02 +0200908 SHOW_STR "Show PDP Context Information\n"
Vadim Yanitskiy977b3392019-05-13 15:32:21 +0700909 GGSN_STR "GGSN Name\n")
Harald Weltedda21ed2017-08-12 15:07:02 +0200910{
911 struct ggsn_ctx *ggsn;
912 struct apn_ctx *apn;
913
914 ggsn = ggsn_find(argv[0]);
915 if (!ggsn) {
916 vty_out(vty, "%% No such GGSN '%s'%s", argv[0], VTY_NEWLINE);
917 return CMD_WARNING;
918 }
Vadim Yanitskiy977b3392019-05-13 15:32:21 +0700919
920 llist_for_each_entry(apn, &ggsn->apn_list, list)
Harald Weltedda21ed2017-08-12 15:07:02 +0200921 apn_show_pdp_contexts(vty, apn);
Harald Weltedda21ed2017-08-12 15:07:02 +0200922
923 return CMD_SUCCESS;
924}
925
Vadim Yanitskiy977b3392019-05-13 15:32:21 +0700926DEFUN(show_pdpctx_apn, show_pdpctx_apn_cmd,
927 "show pdp-context ggsn NAME apn APN",
928 SHOW_STR "Show PDP Context Information\n"
929 GGSN_STR "GGSN Name\n" "Filter by APN\n" "APN name\n")
930{
931 struct ggsn_ctx *ggsn;
932 struct apn_ctx *apn;
933
934 ggsn = ggsn_find(argv[0]);
935 if (!ggsn) {
936 vty_out(vty, "%% No such GGSN '%s'%s", argv[0], VTY_NEWLINE);
937 return CMD_WARNING;
938 }
939
940 apn = ggsn_find_apn(ggsn, argv[1]);
941 if (!apn) {
942 vty_out(vty, "%% No such APN '%s'%s", argv[1], VTY_NEWLINE);
943 return CMD_WARNING;
944 }
945
946 apn_show_pdp_contexts(vty, apn);
947 return CMD_SUCCESS;
948}
949
950/* Backwards compatibility: the VTY parser is (mis)interpreting
951 * "[apn APN]" as two separate elements: "[apn" and "APN]",
952 * but the first part somehow turns into command "ap". */
953ALIAS_DEPRECATED(show_pdpctx_apn, show_deprecated_pdpctx_apn_cmd,
954 "show pdp-context ggsn NAME ap APN",
955 SHOW_STR "Show PDP Context Information\n"
956 GGSN_STR "GGSN Name\n" "Filter by APN\n" "APN name\n");
957
Harald Weltedda21ed2017-08-12 15:07:02 +0200958static void show_apn(struct vty *vty, struct apn_ctx *apn)
959{
960 vty_out(vty, " APN: %s%s", apn->cfg.name, VTY_NEWLINE);
961 /* FIXME */
962}
963
964static void show_one_ggsn(struct vty *vty, struct ggsn_ctx *ggsn)
965{
966 struct apn_ctx *apn;
967 vty_out(vty, "GGSN %s: Bound to %s%s", ggsn->cfg.name, in46a_ntoa(&ggsn->cfg.listen_addr),
968 VTY_NEWLINE);
969 /* FIXME */
970
971 llist_for_each_entry(apn, &ggsn->apn_list, list)
972 show_apn(vty, apn);
973}
974
975DEFUN(show_ggsn, show_ggsn_cmd,
976 "show ggsn [NAME]",
977 SHOW_STR "Display information on the GGSN\n")
978{
979 struct ggsn_ctx *ggsn;
980
981 if (argc == 0) {
982 llist_for_each_entry(ggsn, &g_ggsn_list, list)
983 show_one_ggsn(vty, ggsn);
984 } else {
985 ggsn = ggsn_find(argv[0]);
986 if (!ggsn)
987 return CMD_WARNING;
988 show_one_ggsn(vty, ggsn);
989 }
990
991 return CMD_SUCCESS;
992}
993
994int ggsn_vty_init(void)
995{
996 install_element_ve(&show_pdpctx_cmd);
Vadim Yanitskiy977b3392019-05-13 15:32:21 +0700997 install_element_ve(&show_pdpctx_apn_cmd);
998 install_element_ve(&show_deprecated_pdpctx_apn_cmd);
Harald Weltedda21ed2017-08-12 15:07:02 +0200999 install_element_ve(&show_pdpctx_imsi_cmd);
Vadim Yanitskiyca276e02019-05-13 13:06:51 +07001000 install_element_ve(&show_pdpctx_ip_cmd);
Harald Weltedda21ed2017-08-12 15:07:02 +02001001 install_element_ve(&show_ggsn_cmd);
1002
1003 install_element(CONFIG_NODE, &cfg_ggsn_cmd);
1004 install_element(CONFIG_NODE, &cfg_no_ggsn_cmd);
Pau Espin Pedrol840ce8a2017-11-16 17:01:44 +01001005
Harald Weltedda21ed2017-08-12 15:07:02 +02001006 install_node(&ggsn_node, config_write_ggsn);
Harald Weltedda21ed2017-08-12 15:07:02 +02001007 install_element(GGSN_NODE, &cfg_description_cmd);
1008 install_element(GGSN_NODE, &cfg_no_description_cmd);
1009 install_element(GGSN_NODE, &cfg_ggsn_shutdown_cmd);
1010 install_element(GGSN_NODE, &cfg_ggsn_no_shutdown_cmd);
Harald Welte98146772017-09-05 17:41:20 +02001011 install_element(GGSN_NODE, &cfg_ggsn_bind_ip_cmd);
1012 install_element(GGSN_NODE, &cfg_ggsn_gtpc_ip_cmd);
1013 install_element(GGSN_NODE, &cfg_ggsn_gtpu_ip_cmd);
Harald Weltedda21ed2017-08-12 15:07:02 +02001014 install_element(GGSN_NODE, &cfg_ggsn_state_dir_cmd);
1015 install_element(GGSN_NODE, &cfg_ggsn_apn_cmd);
1016 install_element(GGSN_NODE, &cfg_ggsn_no_apn_cmd);
1017 install_element(GGSN_NODE, &cfg_ggsn_default_apn_cmd);
1018 install_element(GGSN_NODE, &cfg_ggsn_no_default_apn_cmd);
1019
1020 install_node(&apn_node, NULL);
Harald Weltedda21ed2017-08-12 15:07:02 +02001021 install_element(APN_NODE, &cfg_description_cmd);
1022 install_element(APN_NODE, &cfg_no_description_cmd);
1023 install_element(APN_NODE, &cfg_apn_shutdown_cmd);
1024 install_element(APN_NODE, &cfg_apn_no_shutdown_cmd);
1025 install_element(APN_NODE, &cfg_apn_gtpu_mode_cmd);
1026 install_element(APN_NODE, &cfg_apn_type_support_cmd);
1027 install_element(APN_NODE, &cfg_apn_no_type_support_cmd);
1028 install_element(APN_NODE, &cfg_apn_tun_dev_name_cmd);
1029 install_element(APN_NODE, &cfg_apn_ipup_script_cmd);
1030 install_element(APN_NODE, &cfg_apn_no_ipup_script_cmd);
1031 install_element(APN_NODE, &cfg_apn_ipdown_script_cmd);
1032 install_element(APN_NODE, &cfg_apn_no_ipdown_script_cmd);
1033 install_element(APN_NODE, &cfg_apn_ip_prefix_cmd);
1034 install_element(APN_NODE, &cfg_apn_ipv6_prefix_cmd);
1035 install_element(APN_NODE, &cfg_apn_ip_dns_cmd);
1036 install_element(APN_NODE, &cfg_apn_ipv6_dns_cmd);
1037 install_element(APN_NODE, &cfg_apn_no_dns_cmd);
1038 install_element(APN_NODE, &cfg_apn_ip_ifconfig_cmd);
1039 install_element(APN_NODE, &cfg_apn_no_ip_ifconfig_cmd);
1040 install_element(APN_NODE, &cfg_apn_ipv6_ifconfig_cmd);
1041 install_element(APN_NODE, &cfg_apn_no_ipv6_ifconfig_cmd);
Pau Espin Pedrol37c45e32017-12-14 14:09:13 +01001042 install_element(APN_NODE, &cfg_apn_ipv6_linklocal_cmd);
1043 install_element(APN_NODE, &cfg_apn_no_ipv6_linklocal_cmd);
Harald Welte93fed3b2017-09-24 11:43:17 +08001044 install_element(APN_NODE, &cfg_apn_gpdu_seq_cmd);
1045 install_element(APN_NODE, &cfg_apn_no_gpdu_seq_cmd);
Harald Weltedda21ed2017-08-12 15:07:02 +02001046
1047 return 0;
1048}
1049
1050static int ggsn_vty_is_config_node(struct vty *vty, int node)
1051{
1052 switch (node) {
1053 case GGSN_NODE:
1054 case APN_NODE:
1055 return 1;
1056 default:
1057 return 0;
1058 }
1059}
1060
1061static int ggsn_vty_go_parent(struct vty *vty)
1062{
1063 switch (vty->node) {
1064 case GGSN_NODE:
1065 vty->node = CONFIG_NODE;
1066 vty->index = NULL;
1067 vty->index_sub = NULL;
1068 break;
1069 case APN_NODE:
1070 vty->node = GGSN_NODE;
1071 {
1072 struct apn_ctx *apn = vty->index;
1073 vty->index = apn->ggsn;
1074 vty->index_sub = &apn->ggsn->cfg.description;
1075 }
1076 break;
Vadim Yanitskiy906c2092018-05-09 23:11:27 +07001077 default:
1078 vty->node = CONFIG_NODE;
1079 vty->index = NULL;
1080 vty->index_sub = NULL;
Harald Weltedda21ed2017-08-12 15:07:02 +02001081 }
1082
1083 return vty->node;
1084}
1085
1086static const char ggsn_copyright[] =
1087 "Copyright (C) 2011-2017 Harald Welte <laforge@gnumonks.org>\r\n"
1088 "Copyright (C) 2012-2017 Holger Hans Peter Freyther <holger@moiji-mobile.com>\r\n"
1089 "Copyright (C) 2012-2017 sysmocom - s.f.m.c. GmbH\r\n"
1090 "Copyright (C) 2002-2005 Mondru AB\r\n"
1091 "License GPLv2: GNU GPL version 2 <http://gnu.org/licenses/gpl-2.0.html>\r\n"
1092 "This is free software: you are free to change and redistribute it.\r\n"
1093 "There is NO WARRANTY, to the extent permitted by law.\r\n";
1094
1095struct vty_app_info g_vty_info = {
Harald Welte632e8432017-09-05 18:12:14 +02001096 .name = "OsmoGGSN",
Harald Weltedda21ed2017-08-12 15:07:02 +02001097 .version = PACKAGE_VERSION,
1098 .copyright = ggsn_copyright,
1099 .go_parent_cb = ggsn_vty_go_parent,
1100 .is_config_node = ggsn_vty_is_config_node,
1101};