blob: fd2c6adcf51e72b7fadf7e56ce6411c029a35c46 [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"
Pau Espin Pedrolf5fbb412019-08-21 18:49:44 +020043#include "sgsn.h"
Harald Weltedda21ed2017-08-12 15:07:02 +020044
45#define PREFIX_STR "Prefix (Network/Netmask)\n"
46#define IFCONFIG_STR "GGSN-based interface configuration\n"
47#define GGSN_STR "Gateway GPRS Support NODE (GGSN)\n"
48
49LLIST_HEAD(g_ggsn_list);
50
51enum ggsn_vty_node {
52 GGSN_NODE = _LAST_OSMOVTY_NODE + 1,
53 APN_NODE,
54};
55
56struct ggsn_ctx *ggsn_find(const char *name)
57{
58 struct ggsn_ctx *ggsn;
59
60 llist_for_each_entry(ggsn, &g_ggsn_list, list) {
61 if (!strcmp(ggsn->cfg.name, name))
62 return ggsn;
63 }
64 return NULL;
65}
66
67struct ggsn_ctx *ggsn_find_or_create(void *ctx, const char *name)
68{
69 struct ggsn_ctx *ggsn;
70
71 ggsn = ggsn_find(name);
72 if (ggsn)
73 return ggsn;
74
75 ggsn = talloc_zero(ctx, struct ggsn_ctx);
76 if (!ggsn)
77 return NULL;
78
79 ggsn->cfg.name = talloc_strdup(ggsn, name);
80 ggsn->cfg.state_dir = talloc_strdup(ggsn, "/tmp");
81 ggsn->cfg.shutdown = true;
82 INIT_LLIST_HEAD(&ggsn->apn_list);
Pau Espin Pedrolf5fbb412019-08-21 18:49:44 +020083 INIT_LLIST_HEAD(&ggsn->sgsn_list);
Harald Weltedda21ed2017-08-12 15:07:02 +020084
85 llist_add_tail(&ggsn->list, &g_ggsn_list);
86 return ggsn;
87}
88
89struct apn_ctx *ggsn_find_apn(struct ggsn_ctx *ggsn, const char *name)
90{
91 struct apn_ctx *apn;
92
93 llist_for_each_entry(apn, &ggsn->apn_list, list) {
94 if (!strcmp(apn->cfg.name, name))
95 return apn;
96 }
97 return NULL;
98}
99
100struct apn_ctx *ggsn_find_or_create_apn(struct ggsn_ctx *ggsn, const char *name)
101{
102 struct apn_ctx *apn = ggsn_find_apn(ggsn, name);
103 if (apn)
104 return apn;
105
106 apn = talloc_zero(ggsn, struct apn_ctx);
107 if (!apn)
108 return NULL;
109 apn->ggsn = ggsn;
110 apn->cfg.name = talloc_strdup(apn, name);
111 apn->cfg.shutdown = true;
Harald Welte93fed3b2017-09-24 11:43:17 +0800112 apn->cfg.tx_gpdu_seq = true;
Harald Weltedda21ed2017-08-12 15:07:02 +0200113 INIT_LLIST_HEAD(&apn->cfg.name_list);
114
115 llist_add_tail(&apn->list, &ggsn->apn_list);
116 return apn;
117}
118
119/* GGSN Node */
120
121static struct cmd_node ggsn_node = {
122 GGSN_NODE,
123 "%s(config-ggsn)# ",
124 1,
125};
126
127DEFUN(cfg_ggsn, cfg_ggsn_cmd,
128 "ggsn NAME",
129 "Configure the Gateway GPRS Support Node\n" "GGSN Name (has only local significance)\n")
130{
131 struct ggsn_ctx *ggsn;
132
133 ggsn = ggsn_find_or_create(tall_ggsn_ctx, argv[0]);
134 if (!ggsn)
135 return CMD_WARNING;
136
137 vty->node = GGSN_NODE;
138 vty->index = ggsn;
139 vty->index_sub = &ggsn->cfg.description;
140
141 return CMD_SUCCESS;
142}
143
144DEFUN(cfg_no_ggsn, cfg_no_ggsn_cmd,
145 "no ggsn NAME",
146 NO_STR "Remove the named Gateway GPRS Support Node\n"
147 "GGSN Name (has only local significance)\n")
148{
149 struct ggsn_ctx *ggsn;
150
151 ggsn = ggsn_find(argv[0]);
152 if (!ggsn) {
153 vty_out(vty, "%% No such GGSN '%s'%s", argv[0], VTY_NEWLINE);
154 return CMD_WARNING;
155 }
156
157 if (!ggsn->cfg.shutdown) {
158 vty_out(vty, "%% GGSN %s is still active, please shutdown first%s",
159 ggsn->cfg.name, VTY_NEWLINE);
160 return CMD_WARNING;
161 }
162
163 if (!llist_empty(&ggsn->apn_list)) {
164 vty_out(vty, "%% GGSN %s still has APNs configured, please remove first%s",
165 ggsn->cfg.name, VTY_NEWLINE);
166 return CMD_WARNING;
167 }
168
169 llist_del(&ggsn->list);
170 talloc_free(ggsn);
171
172 return CMD_SUCCESS;
173}
174
Harald Welte98146772017-09-05 17:41:20 +0200175DEFUN(cfg_ggsn_bind_ip, cfg_ggsn_bind_ip_cmd,
176 "gtp bind-ip A.B.C.D",
Harald Weltedda21ed2017-08-12 15:07:02 +0200177 "GTP Parameters\n"
178 "Set the IP address for the local GTP bind\n"
179 "IPv4 Address\n")
180{
181 struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
182 size_t t;
183
184 ippool_aton(&ggsn->cfg.listen_addr, &t, argv[0], 0);
185
186 return CMD_SUCCESS;
187}
188
Harald Welte98146772017-09-05 17:41:20 +0200189DEFUN(cfg_ggsn_gtpc_ip, cfg_ggsn_gtpc_ip_cmd,
190 "gtp control-ip A.B.C.D",
191 "GTP Parameters\n"
192 "Set the IP address states as local IP in GTP-C messages\n"
193 "IPv4 Address\n")
194{
195 struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
196 size_t t;
197
198 ippool_aton(&ggsn->cfg.gtpc_addr, &t, argv[0], 0);
199
200 return CMD_SUCCESS;
201}
202
203DEFUN(cfg_ggsn_gtpu_ip, cfg_ggsn_gtpu_ip_cmd,
204 "gtp user-ip A.B.C.D",
205 "GTP Parameters\n"
206 "Set the IP address states as local IP in GTP-U messages\n"
207 "IPv4 Address\n")
208{
209 struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
210 size_t t;
211
212 ippool_aton(&ggsn->cfg.gtpu_addr, &t, argv[0], 0);
213
214 return CMD_SUCCESS;
215}
216
Harald Weltedda21ed2017-08-12 15:07:02 +0200217DEFUN(cfg_ggsn_state_dir, cfg_ggsn_state_dir_cmd,
218 "gtp state-dir PATH",
219 "GTP Parameters\n"
220 "Set the directory for the GTP State file\n"
221 "Local Directory\n")
222{
223 struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
224
225 osmo_talloc_replace_string(ggsn, &ggsn->cfg.state_dir, argv[0]);
226
227 return CMD_SUCCESS;
228}
229
230DEFUN(cfg_ggsn_apn, cfg_ggsn_apn_cmd,
231 "apn NAME", "APN Configuration\n" "APN Name\n")
232{
233 struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
234 struct apn_ctx *apn;
235
236 apn = ggsn_find_or_create_apn(ggsn, argv[0]);
237 if (!apn)
238 return CMD_WARNING;
239
240 vty->node = APN_NODE;
241 vty->index = apn;
242 vty->index_sub = &ggsn->cfg.description;
243
244 return CMD_SUCCESS;
245}
246
247DEFUN(cfg_ggsn_no_apn, cfg_ggsn_no_apn_cmd,
248 "no apn NAME",
249 NO_STR "Remove APN Configuration\n" "APN Name\n")
250{
251 struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
252 struct apn_ctx *apn;
253
254 apn = ggsn_find_apn(ggsn, argv[0]);
255 if (!apn) {
256 vty_out(vty, "%% No such APN '%s'%s", argv[0], VTY_NEWLINE);
257 return CMD_WARNING;
258 }
259
260 if (!apn->cfg.shutdown) {
261 vty_out(vty, "%% APN %s still active, please shutdown first%s",
262 apn->cfg.name, VTY_NEWLINE);
263 return CMD_WARNING;
264 }
265
266 llist_del(&apn->list);
267 talloc_free(apn);
268
269 return CMD_SUCCESS;
270}
271
272DEFUN(cfg_ggsn_default_apn, cfg_ggsn_default_apn_cmd,
273 "default-apn NAME",
274 "Set a default-APN to be used if no other APN matches\n"
275 "APN Name\n")
276{
277 struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
278 struct apn_ctx *apn;
279
280 apn = ggsn_find_apn(ggsn, argv[0]);
281 if (!apn) {
282 vty_out(vty, "%% No APN of name '%s' found%s", argv[0], VTY_NEWLINE);
283 return CMD_WARNING;
284 }
285
286 ggsn->cfg.default_apn = apn;
287 return CMD_SUCCESS;
288}
289
290DEFUN(cfg_ggsn_no_default_apn, cfg_ggsn_no_default_apn_cmd,
291 "no default-apn",
292 NO_STR "Remove default-APN to be used if no other APN matches\n")
293{
294 struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
295 ggsn->cfg.default_apn = NULL;
296 return CMD_SUCCESS;
297}
298
299DEFUN(cfg_ggsn_shutdown, cfg_ggsn_shutdown_cmd,
300 "shutdown ggsn",
301 "Put the GGSN in administrative shut-down\n" GGSN_STR)
302{
303 struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
304
305 if (!ggsn->cfg.shutdown) {
306 if (ggsn_stop(ggsn)) {
307 vty_out(vty, "%% Failed to shutdown GGSN%s", VTY_NEWLINE);
308 return CMD_WARNING;
309 }
310 ggsn->cfg.shutdown = true;
311 }
312
313 return CMD_SUCCESS;
314}
315
316DEFUN(cfg_ggsn_no_shutdown, cfg_ggsn_no_shutdown_cmd,
317 "no shutdown ggsn",
318 NO_STR GGSN_STR "Remove the GGSN from administrative shut-down\n")
319{
320 struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
321
322 if (ggsn->cfg.shutdown) {
323 if (ggsn_start(ggsn) < 0) {
324 vty_out(vty, "%% Failed to start GGSN, check log for details%s", VTY_NEWLINE);
325 return CMD_WARNING;
326 }
327 ggsn->cfg.shutdown = false;
328 }
329
330 return CMD_SUCCESS;
331}
332
Pau Espin Pedrolf5fbb412019-08-21 18:49:44 +0200333static void show_one_sgsn(struct vty *vty, const struct sgsn_peer *sgsn, const char* prefix)
334{
335 char buf[INET_ADDRSTRLEN];
336
337 inet_ntop(AF_INET, &sgsn->addr, buf, sizeof(buf));
338 vty_out(vty, "%s(S)GSN %s%s", prefix, buf, VTY_NEWLINE);
339 vty_out(vty, "%s Restart Counter: %d%s", prefix, sgsn->remote_restart_ctr, VTY_NEWLINE);
340 vty_out(vty, "%s PDP contexts: %d%s", prefix, llist_count(&sgsn->pdp_list), VTY_NEWLINE);
341 vty_out(vty, "%s Echo Requests in-flight: %u%s", prefix, sgsn->tx_msgs_queued, VTY_NEWLINE);
342}
343
344DEFUN(cfg_ggsn_show_sgsn, cfg_ggsn_show_sgsn_cmd,
345 "show sgsn",
346 NO_STR GGSN_STR "Remove the GGSN from administrative shut-down\n")
347{
348 struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
349 struct sgsn_peer *sgsn;
350
351 llist_for_each_entry(sgsn, &ggsn->sgsn_list, entry) {
352 show_one_sgsn(vty, sgsn, "");
353 }
354
355 return CMD_SUCCESS;
356}
357
358/* Seee 3GPP TS 29.060 section 7.2.1 */
359DEFUN(cfg_ggsn_echo_interval, cfg_ggsn_echo_interval_cmd,
360 "echo-interval <1-36000>",
361 GGSN_STR "GGSN Number\n"
362 "Send an echo request to this static GGSN every interval\n"
363 "Interval between echo requests in seconds\n")
364{
365 struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
366 int prev_interval = ggsn->cfg.echo_interval;
367 struct sgsn_peer *sgsn;
368
369 ggsn->cfg.echo_interval = atoi(argv[0]);
370
371 if (ggsn->cfg.echo_interval < 60)
372 vty_out(vty, "%% 3GPP TS 29.060 section states interval should " \
373 "not be lower than 60 seconds, use this value for " \
374 "testing purposes only!%s", VTY_NEWLINE);
375
376 if (prev_interval == ggsn->cfg.echo_interval)
377 return CMD_SUCCESS;
378
379 /* Re-enable echo timer for all sgsn */
380 llist_for_each_entry(sgsn, &ggsn->sgsn_list, entry)
381 sgsn_echo_timer_start(sgsn);
382
383 return CMD_SUCCESS;
384}
385
386DEFUN(cfg_ggsn_no_echo_interval, cfg_ggsn_no_echo_interval_cmd,
387 "no echo-interval",
388 GGSN_STR "GGSN Number\n"
389 NO_STR "Send an echo request to this static GGSN every interval.\n")
390{
391 struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
392 int prev_interval = ggsn->cfg.echo_interval;
393 struct sgsn_peer *sgsn;
394
395 if (prev_interval == ggsn->cfg.echo_interval)
396 return CMD_SUCCESS;
397
398 ggsn->cfg.echo_interval = 0;
399
400 /* Disable echo timer for all sgsn */
401 llist_for_each_entry(sgsn, &ggsn->sgsn_list, entry)
402 sgsn_echo_timer_stop(sgsn);
403
404 return CMD_SUCCESS;
405}
406
Harald Weltedda21ed2017-08-12 15:07:02 +0200407/* APN Node */
408
409static struct cmd_node apn_node = {
410 APN_NODE,
411 "%s(config-ggsn-apn)# ",
412 1,
413};
414
415static const struct value_string pdp_type_names[] = {
416 { APN_TYPE_IPv4, "v4" },
417 { APN_TYPE_IPv6, "v6" },
418 { APN_TYPE_IPv4v6, "v4v6" },
419 { 0, NULL }
420};
421
422static const struct value_string apn_gtpu_mode_names[] = {
423 { APN_GTPU_MODE_TUN, "tun" },
424 { APN_GTPU_MODE_KERNEL_GTP, "kernel-gtp" },
425 { 0, NULL }
426};
427
428
429#define V4V6V46_STRING "IPv4(-only) PDP Type\n" \
430 "IPv6(-only) PDP Type\n" \
431 "IPv4v6 (dual-stack) PDP Type\n"
432
433DEFUN(cfg_apn_type_support, cfg_apn_type_support_cmd,
434 "type-support (v4|v6|v4v6)",
435 "Enable support for PDP Type\n"
436 V4V6V46_STRING)
437{
438 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
439 uint32_t type = get_string_value(pdp_type_names, argv[0]);
440
441 apn->cfg.apn_type_mask |= type;
442 return CMD_SUCCESS;
443}
444
445DEFUN(cfg_apn_no_type_support, cfg_apn_no_type_support_cmd,
446 "no type-support (v4|v6|v4v6)",
447 NO_STR "Disable support for PDP Type\n"
448 V4V6V46_STRING)
449{
450 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
451 uint32_t type = get_string_value(pdp_type_names, argv[0]);
452
453 apn->cfg.apn_type_mask &= ~type;
454 return CMD_SUCCESS;
455}
456
457DEFUN(cfg_apn_gtpu_mode, cfg_apn_gtpu_mode_cmd,
458 "gtpu-mode (tun|kernel-gtp)",
459 "Set the Mode for this APN (tun or Linux Kernel GTP)\n"
460 "GTP-U in userspace using TUN device\n"
461 "GTP-U in kernel using Linux Kernel GTP\n")
462{
463 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
464
465 apn->cfg.gtpu_mode = get_string_value(apn_gtpu_mode_names, argv[0]);
466 return CMD_SUCCESS;
467}
468
469DEFUN(cfg_apn_tun_dev_name, cfg_apn_tun_dev_name_cmd,
470 "tun-device NAME",
471 "Configure tun device name\n"
472 "TUN device name")
473{
474 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
475 osmo_talloc_replace_string(apn, &apn->tun.cfg.dev_name, argv[0]);
476 return CMD_SUCCESS;
477}
478
479DEFUN(cfg_apn_ipup_script, cfg_apn_ipup_script_cmd,
480 "ipup-script PATH",
481 "Configure name/path of ip-up script\n"
482 "File/Path name of ip-up script\n")
483{
484 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
485 osmo_talloc_replace_string(apn, &apn->tun.cfg.ipup_script, argv[0]);
486 return CMD_SUCCESS;
487}
488
489DEFUN(cfg_apn_no_ipup_script, cfg_apn_no_ipup_script_cmd,
490 "no ipup-script",
491 NO_STR "Disable ip-up script\n")
492{
493 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
494 talloc_free(apn->tun.cfg.ipup_script);
495 apn->tun.cfg.ipup_script = NULL;
496 return CMD_SUCCESS;
497}
498
499DEFUN(cfg_apn_ipdown_script, cfg_apn_ipdown_script_cmd,
500 "ipdown-script PATH",
501 "Configure name/path of ip-down script\n"
502 "File/Path name of ip-down script\n")
503{
504 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
505 osmo_talloc_replace_string(apn, &apn->tun.cfg.ipdown_script, argv[0]);
506 return CMD_SUCCESS;
507}
508
509/* convert prefix from "A.B.C.D/M" notation to in46_prefix */
510static void str2prefix(struct in46_prefix *pfx, const char *in)
511{
512 size_t t;
513
514 ippool_aton(&pfx->addr, &t, in, 0);
515 pfx->prefixlen = t;
516}
517
518DEFUN(cfg_apn_no_ipdown_script, cfg_apn_no_ipdown_script_cmd,
519 "no ipdown-script",
520 NO_STR "Disable ip-down script\n")
521{
522 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
523 talloc_free(apn->tun.cfg.ipdown_script);
524 apn->tun.cfg.ipdown_script = NULL;
525 return CMD_SUCCESS;
526}
527
528DEFUN(cfg_apn_ip_prefix, cfg_apn_ip_prefix_cmd,
529 "ip prefix (static|dynamic) A.B.C.D/M",
530 IP_STR PREFIX_STR "IPv4 Adress/Prefix-Length\n")
531{
532 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
533 struct in46_prefix *pfx;
534
535 /* first update our parsed prefix */
536 if (!strcmp(argv[0], "static"))
537 pfx = &apn->v4.cfg.static_prefix;
538 else
539 pfx = &apn->v4.cfg.dynamic_prefix;
540 str2prefix(pfx, argv[1]);
541
542 return CMD_SUCCESS;
543}
544
545DEFUN(cfg_apn_ip_ifconfig, cfg_apn_ip_ifconfig_cmd,
546 "ip ifconfig A.B.C.D/M",
547 IP_STR IFCONFIG_STR "IPv4 Adress/Prefix-Length\n")
548{
549 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
550 str2prefix(&apn->v4.cfg.ifconfig_prefix, argv[0]);
551 return CMD_SUCCESS;
552}
553
554DEFUN(cfg_apn_no_ip_ifconfig, cfg_apn_no_ip_ifconfig_cmd,
555 "no ip ifconfig",
556 NO_STR IP_STR IFCONFIG_STR)
557{
558 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
559 memset(&apn->v4.cfg.ifconfig_prefix, 0, sizeof(apn->v4.cfg.ifconfig_prefix));
560 return CMD_SUCCESS;
561}
562
563DEFUN(cfg_apn_ipv6_prefix, cfg_apn_ipv6_prefix_cmd,
564 "ipv6 prefix (static|dynamic) X:X::X:X/M",
565 IP6_STR PREFIX_STR "IPv6 Address/Prefix-Length\n")
566{
567 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
568 struct in46_prefix *pfx;
569
570 if (!strcmp(argv[0], "static"))
571 pfx = &apn->v6.cfg.static_prefix;
572 else
573 pfx = &apn->v6.cfg.dynamic_prefix;
574 str2prefix(pfx, argv[1]);
575 return CMD_SUCCESS;
576}
577
578DEFUN(cfg_apn_ipv6_ifconfig, cfg_apn_ipv6_ifconfig_cmd,
579 "ipv6 ifconfig X:X::X:X/M",
580 IP6_STR IFCONFIG_STR "IPv6 Adress/Prefix-Length\n")
581{
582 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
583 str2prefix(&apn->v6.cfg.ifconfig_prefix, argv[0]);
584 return CMD_SUCCESS;
585}
586
587DEFUN(cfg_apn_no_ipv6_ifconfig, cfg_apn_no_ipv6_ifconfig_cmd,
588 "no ipv6 ifconfig",
589 NO_STR IP6_STR IFCONFIG_STR)
590{
591 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
592 memset(&apn->v6.cfg.ifconfig_prefix, 0, sizeof(apn->v6.cfg.ifconfig_prefix));
593 return CMD_SUCCESS;
594}
595
Pau Espin Pedrol37c45e32017-12-14 14:09:13 +0100596DEFUN(cfg_apn_ipv6_linklocal, cfg_apn_ipv6_linklocal_cmd,
597 "ipv6 link-local X:X::X:X/M",
598 IP6_STR IFCONFIG_STR "IPv6 Link-local Adress/Prefix-Length\n")
599{
600 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
601 str2prefix(&apn->v6.cfg.ll_prefix, argv[0]);
602 return CMD_SUCCESS;
603}
604
605DEFUN(cfg_apn_no_ipv6_linklocal, cfg_apn_no_ipv6_linklocal_cmd,
606 "no ipv6 link-local",
607 NO_STR IP6_STR IFCONFIG_STR)
608{
609 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
610 memset(&apn->v6.cfg.ll_prefix, 0, sizeof(apn->v6.cfg.ll_prefix));
611 return CMD_SUCCESS;
612}
613
Harald Weltedda21ed2017-08-12 15:07:02 +0200614#define DNS_STRINGS "Configure DNS Server\n" "primary/secondary DNS\n" "IP address of DNS Sever\n"
615
616DEFUN(cfg_apn_ip_dns, cfg_apn_ip_dns_cmd,
617 "ip dns <0-1> A.B.C.D",
618 IP_STR DNS_STRINGS)
619{
620 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
621 int idx = atoi(argv[0]);
622 size_t dummy;
623
624 ippool_aton(&apn->v4.cfg.dns[idx], &dummy, argv[1], 0);
625
626 return CMD_SUCCESS;
627}
628
629DEFUN(cfg_apn_ipv6_dns, cfg_apn_ipv6_dns_cmd,
630 "ipv6 dns <0-1> X:X::X:X",
631 IP6_STR DNS_STRINGS)
632{
633 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
634 int idx = atoi(argv[0]);
635 size_t dummy;
636
637 ippool_aton(&apn->v6.cfg.dns[idx], &dummy, argv[1], 0);
638
639 return CMD_SUCCESS;
640}
641
642DEFUN(cfg_apn_no_dns, cfg_apn_no_dns_cmd,
643 "no (ip|ipv6) dns <0-1>",
644 NO_STR IP_STR IP6_STR "Disable DNS Server\n" "primary/secondary DNS\n")
645{
646 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
647 struct in46_addr *a;
648 int idx = atoi(argv[1]);
649
650 if (!strcmp(argv[0], "ip"))
651 a = &apn->v4.cfg.dns[idx];
652 else
653 a = &apn->v6.cfg.dns[idx];
654
655 memset(a, 0, sizeof(*a));
656
657 return CMD_SUCCESS;
658}
659
Harald Welte93fed3b2017-09-24 11:43:17 +0800660DEFUN(cfg_apn_gpdu_seq, cfg_apn_gpdu_seq_cmd,
661 "g-pdu tx-sequence-numbers",
662 "G-PDU Configuration\n" "Enable transmission of G-PDU sequence numbers\n")
663{
664 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
665 apn->cfg.tx_gpdu_seq = true;
666 return CMD_SUCCESS;
667}
668
669DEFUN(cfg_apn_no_gpdu_seq, cfg_apn_no_gpdu_seq_cmd,
670 "no g-pdu tx-sequence-numbers",
671 NO_STR "G-PDU Configuration\n" "Disable transmission of G-PDU sequence numbers\n")
672{
673 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
674 apn->cfg.tx_gpdu_seq = false;
675 return CMD_SUCCESS;
676}
677
Harald Weltedda21ed2017-08-12 15:07:02 +0200678DEFUN(cfg_apn_shutdown, cfg_apn_shutdown_cmd,
679 "shutdown",
680 "Put the APN in administrative shut-down\n")
681{
682 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
683
684 if (!apn->cfg.shutdown) {
Pau Espin Pedrol72ab4bc2019-05-29 19:08:26 +0200685 if (apn_stop(apn)) {
Harald Weltedda21ed2017-08-12 15:07:02 +0200686 vty_out(vty, "%% Failed to Stop APN%s", VTY_NEWLINE);
687 return CMD_WARNING;
688 }
689 apn->cfg.shutdown = true;
690 }
691
692 return CMD_SUCCESS;
693}
694
695DEFUN(cfg_apn_no_shutdown, cfg_apn_no_shutdown_cmd,
696 "no shutdown",
697 NO_STR "Remove the APN from administrative shut-down\n")
698{
699 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
700
701 if (apn->cfg.shutdown) {
702 if (apn_start(apn) < 0) {
703 vty_out(vty, "%% Failed to start APN, check log for details%s", VTY_NEWLINE);
704 return CMD_WARNING;
705 }
706 apn->cfg.shutdown = false;
707 }
708
709 return CMD_SUCCESS;
710}
711
712
713static void vty_dump_prefix(struct vty *vty, const char *pre, const struct in46_prefix *pfx)
714{
715 vty_out(vty, "%s %s%s", pre, in46p_ntoa(pfx), VTY_NEWLINE);
716}
717
718static void config_write_apn(struct vty *vty, struct apn_ctx *apn)
719{
720 unsigned int i;
721
722 vty_out(vty, " apn %s%s", apn->cfg.name, VTY_NEWLINE);
723 if (apn->cfg.description)
724 vty_out(vty, " description %s%s", apn->cfg.description, VTY_NEWLINE);
725 vty_out(vty, " gtpu-mode %s%s", get_value_string(apn_gtpu_mode_names, apn->cfg.gtpu_mode),
726 VTY_NEWLINE);
727 if (apn->tun.cfg.dev_name)
728 vty_out(vty, " tun-device %s%s", apn->tun.cfg.dev_name, VTY_NEWLINE);
729 if (apn->tun.cfg.ipup_script)
730 vty_out(vty, " ipup-script %s%s", apn->tun.cfg.ipup_script, VTY_NEWLINE);
731 if (apn->tun.cfg.ipdown_script)
732 vty_out(vty, " ipdown-script %s%s", apn->tun.cfg.ipdown_script, VTY_NEWLINE);
733
734 for (i = 0; i < 32; i++) {
Pau Espin Pedrol55600012019-05-30 17:29:09 +0200735 if (!(apn->cfg.apn_type_mask & (UINT32_C(1) << i)))
Harald Weltedda21ed2017-08-12 15:07:02 +0200736 continue;
Pau Espin Pedrol55600012019-05-30 17:29:09 +0200737 vty_out(vty, " type-support %s%s", get_value_string(pdp_type_names, (UINT32_C(1) << i)),
Harald Weltedda21ed2017-08-12 15:07:02 +0200738 VTY_NEWLINE);
739 }
740
Harald Welte93fed3b2017-09-24 11:43:17 +0800741 if (!apn->cfg.tx_gpdu_seq)
742 vty_out(vty, " no g-pdu tx-sequence-numbers%s", VTY_NEWLINE);
743
Harald Weltedda21ed2017-08-12 15:07:02 +0200744 /* IPv4 prefixes + DNS */
745 if (apn->v4.cfg.static_prefix.addr.len)
746 vty_dump_prefix(vty, " ip prefix static", &apn->v4.cfg.static_prefix);
747 if (apn->v4.cfg.dynamic_prefix.addr.len)
748 vty_dump_prefix(vty, " ip prefix dynamic", &apn->v4.cfg.dynamic_prefix);
749 for (i = 0; i < ARRAY_SIZE(apn->v4.cfg.dns); i++) {
750 if (!apn->v4.cfg.dns[i].len)
751 continue;
752 vty_out(vty, " ip dns %u %s%s", i, in46a_ntoa(&apn->v4.cfg.dns[i]), VTY_NEWLINE);
753 }
754 if (apn->v4.cfg.ifconfig_prefix.addr.len)
Harald Welteff438172017-09-24 22:48:46 +0800755 vty_dump_prefix(vty, " ip ifconfig", &apn->v4.cfg.ifconfig_prefix);
Harald Weltedda21ed2017-08-12 15:07:02 +0200756
757 /* IPv6 prefixes + DNS */
758 if (apn->v6.cfg.static_prefix.addr.len)
759 vty_dump_prefix(vty, " ipv6 prefix static", &apn->v6.cfg.static_prefix);
760 if (apn->v6.cfg.dynamic_prefix.addr.len)
761 vty_dump_prefix(vty, " ipv6 prefix dynamic", &apn->v6.cfg.dynamic_prefix);
762 for (i = 0; i < ARRAY_SIZE(apn->v6.cfg.dns); i++) {
763 if (!apn->v6.cfg.dns[i].len)
764 continue;
Harald Welte3ca419a2017-09-24 22:47:51 +0800765 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 +0200766 }
767 if (apn->v6.cfg.ifconfig_prefix.addr.len)
Harald Welteff438172017-09-24 22:48:46 +0800768 vty_dump_prefix(vty, " ipv6 ifconfig", &apn->v6.cfg.ifconfig_prefix);
Pau Espin Pedrole5a082d2017-12-15 15:55:04 +0100769 if (apn->v6.cfg.ll_prefix.addr.len)
770 vty_dump_prefix(vty, " ipv6 link-local", &apn->v6.cfg.ll_prefix);
Harald Weltedda21ed2017-08-12 15:07:02 +0200771
772 /* must be last */
773 vty_out(vty, " %sshutdown%s", apn->cfg.shutdown ? "" : "no ", VTY_NEWLINE);
774}
775
776static int config_write_ggsn(struct vty *vty)
777{
778 struct ggsn_ctx *ggsn;
779
780 llist_for_each_entry(ggsn, &g_ggsn_list, list) {
781 struct apn_ctx *apn;
782 vty_out(vty, "ggsn %s%s", ggsn->cfg.name, VTY_NEWLINE);
783 if (ggsn->cfg.description)
784 vty_out(vty, " description %s%s", ggsn->cfg.description, VTY_NEWLINE);
785 vty_out(vty, " gtp state-dir %s%s", ggsn->cfg.state_dir, VTY_NEWLINE);
Harald Welte98146772017-09-05 17:41:20 +0200786 vty_out(vty, " gtp bind-ip %s%s", in46a_ntoa(&ggsn->cfg.listen_addr), VTY_NEWLINE);
787 if (ggsn->cfg.gtpc_addr.v4.s_addr)
788 vty_out(vty, " gtp control-ip %s%s", in46a_ntoa(&ggsn->cfg.gtpc_addr), VTY_NEWLINE);
789 if (ggsn->cfg.gtpu_addr.v4.s_addr)
790 vty_out(vty, " gtp user-ip %s%s", in46a_ntoa(&ggsn->cfg.gtpu_addr), VTY_NEWLINE);
Harald Weltedda21ed2017-08-12 15:07:02 +0200791 llist_for_each_entry(apn, &ggsn->apn_list, list)
792 config_write_apn(vty, apn);
793 if (ggsn->cfg.default_apn)
794 vty_out(vty, " default-apn %s%s", ggsn->cfg.default_apn->cfg.name, VTY_NEWLINE);
Pau Espin Pedrolf5fbb412019-08-21 18:49:44 +0200795 if (ggsn->cfg.echo_interval)
796 vty_out(vty, " echo-interval %u%s", ggsn->cfg.echo_interval, VTY_NEWLINE);
Harald Weltedda21ed2017-08-12 15:07:02 +0200797 /* must be last */
798 vty_out(vty, " %sshutdown ggsn%s", ggsn->cfg.shutdown ? "" : "no ", VTY_NEWLINE);
799 }
800
801 return 0;
802}
803
804static const char *print_gsnaddr(const struct ul16_t *in)
805{
806 struct in46_addr in46;
807
808 in46.len = in->l;
809 OSMO_ASSERT(in->l <= sizeof(in46.v6));
810 memcpy(&in46.v6, in->v, in->l);
811
812 return in46a_ntoa(&in46);
813}
814
Pau Espin Pedrol310ea1d2019-08-20 14:15:25 +0200815/* Useful for v4v6 APNs, where we first iterate over v4 pool and then over v6
816 pool. param v4only can be used to avoid printing duplicates for pdp context
817 containing both IPv4 and IPv6 addresses. */
818static void show_one_pdp_v4only(struct vty *vty, struct pdp_t *pdp, bool v4only)
Harald Weltedda21ed2017-08-12 15:07:02 +0200819{
Pau Espin Pedrol310ea1d2019-08-20 14:15:25 +0200820 struct ippoolm_t *peer4, *peer6;
Vadim Yanitskiyd7030d22019-05-13 22:10:24 +0700821 char name_buf[256];
822 char *apn_name;
Vadim Yanitskiyfb625042019-05-19 02:00:31 +0700823 int rc;
824
Pau Espin Pedrol310ea1d2019-08-20 14:15:25 +0200825 peer4 = pdp_get_peer_ipv(pdp, false);
826 peer6 = pdp_get_peer_ipv(pdp, true);
827
828 if (v4only && peer6)
829 return;
830
Vadim Yanitskiyfb625042019-05-19 02:00:31 +0700831 /* Attempt to decode MSISDN */
832 rc = gsm48_decode_bcd_number2(name_buf, sizeof(name_buf),
833 pdp->msisdn.v, pdp->msisdn.l, 0);
Harald Weltedda21ed2017-08-12 15:07:02 +0200834
835 vty_out(vty, "IMSI: %s, NSAPI: %u, MSISDN: %s%s", imsi_gtp2str(&pdp->imsi), pdp->nsapi,
Vadim Yanitskiyfb625042019-05-19 02:00:31 +0700836 rc ? "(NONE)" : name_buf, VTY_NEWLINE);
Harald Weltedda21ed2017-08-12 15:07:02 +0200837
Pau Espin Pedrola0196312019-08-20 19:11:29 +0200838 vty_out(vty, " Version: %d", pdp->version);
839 if (pdp->version == 1) {
840 if (!pdp->secondary) {
841 vty_out(vty, ", Primary, Num Secondaries: %d%s%s",
842 pdp_count_secondary(pdp) - 1, /* primary included in count */
843 pdp->nodata ? ", No User Plane": "",
844 VTY_NEWLINE);
845 } else {
846 vty_out(vty, ", Secondary%s", VTY_NEWLINE);
847 }
848 } else {
849 vty_out(vty, "%s", VTY_NEWLINE);
850 }
851
Harald Weltedda21ed2017-08-12 15:07:02 +0200852 vty_out(vty, " Control: %s:%08x ", print_gsnaddr(&pdp->gsnlc), pdp->teic_own);
853 vty_out(vty, "<-> %s:%08x%s", print_gsnaddr(&pdp->gsnrc), pdp->teic_gn, VTY_NEWLINE);
854
855 vty_out(vty, " Data: %s:%08x ", print_gsnaddr(&pdp->gsnlu), pdp->teid_own);
856 vty_out(vty, "<-> %s:%08x%s", print_gsnaddr(&pdp->gsnru), pdp->teid_gn, VTY_NEWLINE);
857
Vadim Yanitskiyd7030d22019-05-13 22:10:24 +0700858 apn_name = osmo_apn_to_str(name_buf, pdp->apn_req.v, pdp->apn_req.l);
859 vty_out(vty, " APN requested: %s%s", apn_name ? name_buf : "(NONE)", VTY_NEWLINE);
860 apn_name = osmo_apn_to_str(name_buf, pdp->apn_use.v, pdp->apn_use.l);
861 vty_out(vty, " APN in use: %s%s", apn_name ? name_buf : "(NONE)", VTY_NEWLINE);
862
Pau Espin Pedrol310ea1d2019-08-20 14:15:25 +0200863 if (peer4)
Pau Espin Pedrol03cce862019-08-20 12:23:14 +0200864 vty_out(vty, " End-User Address (IPv4): %s%s",
Pau Espin Pedrol310ea1d2019-08-20 14:15:25 +0200865 in46a_ntop(&peer4->addr, name_buf, sizeof(name_buf)), VTY_NEWLINE);
866 if (peer6)
Pau Espin Pedrol03cce862019-08-20 12:23:14 +0200867 vty_out(vty, " End-User Address (IPv6): %s%s",
Pau Espin Pedrol310ea1d2019-08-20 14:15:25 +0200868 in46a_ntop(&peer6->addr, name_buf, sizeof(name_buf)), VTY_NEWLINE);
Harald Welte93fed3b2017-09-24 11:43:17 +0800869 vty_out(vty, " Transmit GTP Sequence Number for G-PDU: %s%s",
870 pdp->tx_gpdu_seq ? "Yes" : "No", VTY_NEWLINE);
Harald Weltedda21ed2017-08-12 15:07:02 +0200871}
872
Pau Espin Pedrol310ea1d2019-08-20 14:15:25 +0200873static void show_one_pdp(struct vty *vty, struct pdp_t *pdp)
874{
875 show_one_pdp_v4only(vty, pdp, false);
876}
877
Harald Weltedda21ed2017-08-12 15:07:02 +0200878DEFUN(show_pdpctx_imsi, show_pdpctx_imsi_cmd,
Pau Espin Pedrol7b52f002019-05-31 16:15:16 +0200879 "show pdp-context ggsn NAME imsi IMSI [<0-15>]",
Harald Weltedda21ed2017-08-12 15:07:02 +0200880 SHOW_STR "Display information on PDP Context\n"
Pau Espin Pedrol7b52f002019-05-31 16:15:16 +0200881 GGSN_STR "GGSN Name\n"
Harald Weltedda21ed2017-08-12 15:07:02 +0200882 "PDP contexts for given IMSI\n"
883 "PDP context for given NSAPI\n")
884{
Pau Espin Pedrol7b52f002019-05-31 16:15:16 +0200885 struct ggsn_ctx *ggsn;
886 uint64_t imsi;
Harald Weltedda21ed2017-08-12 15:07:02 +0200887 unsigned int nsapi;
888 struct pdp_t *pdp;
889 int num_found = 0;
890
Pau Espin Pedrol7b52f002019-05-31 16:15:16 +0200891 ggsn = ggsn_find(argv[0]);
892 if (!ggsn) {
893 vty_out(vty, "%% No such GGSN '%s'%s", argv[0], VTY_NEWLINE);
894 return CMD_WARNING;
895 }
896
Keithcbc07bd2020-10-10 12:17:26 +0200897 if (strlen(argv[1]) < 6 || strlen(argv[1]) > 15) {
898 vty_out(vty, "%% Invalid IMSI '%s'%s", argv[1], VTY_NEWLINE);
899 return CMD_WARNING;
900 }
901
902 imsi = imsi_str2gtp(argv[1]);
Pau Espin Pedrol7b52f002019-05-31 16:15:16 +0200903
904 if (argc > 2) {
905 nsapi = atoi(argv[2]);
Keith080dcfa2020-10-10 15:15:31 +0200906 if (!gtp_pdp_getimsi(ggsn->gsn, &pdp, imsi, nsapi)) {
Harald Weltedda21ed2017-08-12 15:07:02 +0200907 show_one_pdp(vty, pdp);
908 num_found++;
909 }
910 } else {
911 for (nsapi = 0; nsapi < PDP_MAXNSAPI; nsapi++) {
Pau Espin Pedrol7b52f002019-05-31 16:15:16 +0200912 if (gtp_pdp_getimsi(ggsn->gsn, &pdp, imsi, nsapi))
Harald Weltedda21ed2017-08-12 15:07:02 +0200913 continue;
914 show_one_pdp(vty, pdp);
915 num_found++;
916 }
917 }
918 if (num_found == 0) {
919 vty_out(vty, "%% No such PDP context found%s", VTY_NEWLINE);
920 return CMD_WARNING;
921 } else
922 return CMD_SUCCESS;
923}
924
Vadim Yanitskiyca276e02019-05-13 13:06:51 +0700925DEFUN(show_pdpctx_ip, show_pdpctx_ip_cmd,
926 "show pdp-context ggsn NAME ipv4 A.B.C.D",
927 SHOW_STR "Display information on PDP Context\n"
928 GGSN_STR "GGSN Name\n" "IPv4 address type\n" "IP address\n")
929{
930 struct ggsn_ctx *ggsn;
931 struct apn_ctx *apn;
932 unsigned int i;
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 /* Iterate over all APNs of a given GGSN */
941 llist_for_each_entry(apn, &ggsn->apn_list, list) {
942 struct ippool_t *pool = apn->v4.pool;
943
944 /* In some rare cases, if GGSN fails to init TUN/TAP interfaces
945 * (e.g. due to insufficient permissions), it will continue to
946 * work in such broken state, and pool would be NULL. */
947 if (!pool)
948 continue;
949
950 /* Iterate over all IPv4 pool members */
951 for (i = 0; i < pool->listsize; i++) {
952 struct ippoolm_t *member = &pool->member[i];
953 if (member->inuse == 0)
954 continue;
955 if (strcmp(argv[1], in46a_ntoa(&member->addr)) == 0) {
956 show_one_pdp(vty, member->peer);
957 return CMD_SUCCESS;
958 }
959 }
960 }
961
962 vty_out(vty, "%% No PDP context found for IP '%s'%s", argv[1], VTY_NEWLINE);
963 return CMD_WARNING;
964}
965
Harald Weltedda21ed2017-08-12 15:07:02 +0200966/* show all (active) PDP contexts within a pool */
Pau Espin Pedrol310ea1d2019-08-20 14:15:25 +0200967static void ippool_show_pdp_contexts(struct vty *vty, struct ippool_t *pool, bool pdp_v4only)
Harald Weltedda21ed2017-08-12 15:07:02 +0200968{
969 unsigned int i;
970
971 if (!pool)
972 return;
973
974 for (i = 0; i < pool->listsize; i++) {
975 struct ippoolm_t *member = &pool->member[i];
976 if (member->inuse == 0)
977 continue;
Pau Espin Pedrol310ea1d2019-08-20 14:15:25 +0200978 show_one_pdp_v4only(vty, member->peer, pdp_v4only);
Harald Weltedda21ed2017-08-12 15:07:02 +0200979 }
980}
981
982/* show all (active) PDP contexts within an APN */
983static void apn_show_pdp_contexts(struct vty *vty, struct apn_ctx *apn)
984{
Pau Espin Pedrol310ea1d2019-08-20 14:15:25 +0200985 ippool_show_pdp_contexts(vty, apn->v4.pool, true);
986 ippool_show_pdp_contexts(vty, apn->v6.pool, false);
Harald Weltedda21ed2017-08-12 15:07:02 +0200987}
988
989DEFUN(show_pdpctx, show_pdpctx_cmd,
Vadim Yanitskiy977b3392019-05-13 15:32:21 +0700990 "show pdp-context ggsn NAME",
Harald Weltedda21ed2017-08-12 15:07:02 +0200991 SHOW_STR "Show PDP Context Information\n"
Vadim Yanitskiy977b3392019-05-13 15:32:21 +0700992 GGSN_STR "GGSN Name\n")
Harald Weltedda21ed2017-08-12 15:07:02 +0200993{
994 struct ggsn_ctx *ggsn;
995 struct apn_ctx *apn;
996
997 ggsn = ggsn_find(argv[0]);
998 if (!ggsn) {
999 vty_out(vty, "%% No such GGSN '%s'%s", argv[0], VTY_NEWLINE);
1000 return CMD_WARNING;
1001 }
Vadim Yanitskiy977b3392019-05-13 15:32:21 +07001002
1003 llist_for_each_entry(apn, &ggsn->apn_list, list)
Harald Weltedda21ed2017-08-12 15:07:02 +02001004 apn_show_pdp_contexts(vty, apn);
Harald Weltedda21ed2017-08-12 15:07:02 +02001005
1006 return CMD_SUCCESS;
1007}
1008
Vadim Yanitskiy977b3392019-05-13 15:32:21 +07001009DEFUN(show_pdpctx_apn, show_pdpctx_apn_cmd,
1010 "show pdp-context ggsn NAME apn APN",
1011 SHOW_STR "Show PDP Context Information\n"
1012 GGSN_STR "GGSN Name\n" "Filter by APN\n" "APN name\n")
1013{
1014 struct ggsn_ctx *ggsn;
1015 struct apn_ctx *apn;
1016
1017 ggsn = ggsn_find(argv[0]);
1018 if (!ggsn) {
1019 vty_out(vty, "%% No such GGSN '%s'%s", argv[0], VTY_NEWLINE);
1020 return CMD_WARNING;
1021 }
1022
1023 apn = ggsn_find_apn(ggsn, argv[1]);
1024 if (!apn) {
1025 vty_out(vty, "%% No such APN '%s'%s", argv[1], VTY_NEWLINE);
1026 return CMD_WARNING;
1027 }
1028
1029 apn_show_pdp_contexts(vty, apn);
1030 return CMD_SUCCESS;
1031}
1032
1033/* Backwards compatibility: the VTY parser is (mis)interpreting
1034 * "[apn APN]" as two separate elements: "[apn" and "APN]",
1035 * but the first part somehow turns into command "ap". */
1036ALIAS_DEPRECATED(show_pdpctx_apn, show_deprecated_pdpctx_apn_cmd,
1037 "show pdp-context ggsn NAME ap APN",
1038 SHOW_STR "Show PDP Context Information\n"
1039 GGSN_STR "GGSN Name\n" "Filter by APN\n" "APN name\n");
1040
Harald Weltedda21ed2017-08-12 15:07:02 +02001041static void show_apn(struct vty *vty, struct apn_ctx *apn)
1042{
1043 vty_out(vty, " APN: %s%s", apn->cfg.name, VTY_NEWLINE);
1044 /* FIXME */
1045}
1046
1047static void show_one_ggsn(struct vty *vty, struct ggsn_ctx *ggsn)
1048{
1049 struct apn_ctx *apn;
Pau Espin Pedrolf5fbb412019-08-21 18:49:44 +02001050 struct sgsn_peer *sgsn;
Harald Weltedda21ed2017-08-12 15:07:02 +02001051 vty_out(vty, "GGSN %s: Bound to %s%s", ggsn->cfg.name, in46a_ntoa(&ggsn->cfg.listen_addr),
1052 VTY_NEWLINE);
1053 /* FIXME */
1054
1055 llist_for_each_entry(apn, &ggsn->apn_list, list)
1056 show_apn(vty, apn);
Pau Espin Pedrolf5fbb412019-08-21 18:49:44 +02001057 llist_for_each_entry(sgsn, &ggsn->sgsn_list, entry)
1058 show_one_sgsn(vty, sgsn, " ");
Harald Weltedda21ed2017-08-12 15:07:02 +02001059}
1060
1061DEFUN(show_ggsn, show_ggsn_cmd,
1062 "show ggsn [NAME]",
1063 SHOW_STR "Display information on the GGSN\n")
1064{
1065 struct ggsn_ctx *ggsn;
1066
1067 if (argc == 0) {
1068 llist_for_each_entry(ggsn, &g_ggsn_list, list)
1069 show_one_ggsn(vty, ggsn);
1070 } else {
1071 ggsn = ggsn_find(argv[0]);
1072 if (!ggsn)
1073 return CMD_WARNING;
1074 show_one_ggsn(vty, ggsn);
1075 }
1076
1077 return CMD_SUCCESS;
1078}
1079
1080int ggsn_vty_init(void)
1081{
1082 install_element_ve(&show_pdpctx_cmd);
Vadim Yanitskiy977b3392019-05-13 15:32:21 +07001083 install_element_ve(&show_pdpctx_apn_cmd);
1084 install_element_ve(&show_deprecated_pdpctx_apn_cmd);
Harald Weltedda21ed2017-08-12 15:07:02 +02001085 install_element_ve(&show_pdpctx_imsi_cmd);
Vadim Yanitskiyca276e02019-05-13 13:06:51 +07001086 install_element_ve(&show_pdpctx_ip_cmd);
Harald Weltedda21ed2017-08-12 15:07:02 +02001087 install_element_ve(&show_ggsn_cmd);
1088
1089 install_element(CONFIG_NODE, &cfg_ggsn_cmd);
1090 install_element(CONFIG_NODE, &cfg_no_ggsn_cmd);
Pau Espin Pedrol840ce8a2017-11-16 17:01:44 +01001091
Harald Weltedda21ed2017-08-12 15:07:02 +02001092 install_node(&ggsn_node, config_write_ggsn);
Harald Weltedda21ed2017-08-12 15:07:02 +02001093 install_element(GGSN_NODE, &cfg_description_cmd);
1094 install_element(GGSN_NODE, &cfg_no_description_cmd);
1095 install_element(GGSN_NODE, &cfg_ggsn_shutdown_cmd);
1096 install_element(GGSN_NODE, &cfg_ggsn_no_shutdown_cmd);
Harald Welte98146772017-09-05 17:41:20 +02001097 install_element(GGSN_NODE, &cfg_ggsn_bind_ip_cmd);
1098 install_element(GGSN_NODE, &cfg_ggsn_gtpc_ip_cmd);
1099 install_element(GGSN_NODE, &cfg_ggsn_gtpu_ip_cmd);
Harald Weltedda21ed2017-08-12 15:07:02 +02001100 install_element(GGSN_NODE, &cfg_ggsn_state_dir_cmd);
1101 install_element(GGSN_NODE, &cfg_ggsn_apn_cmd);
1102 install_element(GGSN_NODE, &cfg_ggsn_no_apn_cmd);
1103 install_element(GGSN_NODE, &cfg_ggsn_default_apn_cmd);
1104 install_element(GGSN_NODE, &cfg_ggsn_no_default_apn_cmd);
Pau Espin Pedrolf5fbb412019-08-21 18:49:44 +02001105 install_element(GGSN_NODE, &cfg_ggsn_show_sgsn_cmd);
1106 install_element(GGSN_NODE, &cfg_ggsn_echo_interval_cmd);
1107 install_element(GGSN_NODE, &cfg_ggsn_no_echo_interval_cmd);
Harald Weltedda21ed2017-08-12 15:07:02 +02001108
1109 install_node(&apn_node, NULL);
Harald Weltedda21ed2017-08-12 15:07:02 +02001110 install_element(APN_NODE, &cfg_description_cmd);
1111 install_element(APN_NODE, &cfg_no_description_cmd);
1112 install_element(APN_NODE, &cfg_apn_shutdown_cmd);
1113 install_element(APN_NODE, &cfg_apn_no_shutdown_cmd);
1114 install_element(APN_NODE, &cfg_apn_gtpu_mode_cmd);
1115 install_element(APN_NODE, &cfg_apn_type_support_cmd);
1116 install_element(APN_NODE, &cfg_apn_no_type_support_cmd);
1117 install_element(APN_NODE, &cfg_apn_tun_dev_name_cmd);
1118 install_element(APN_NODE, &cfg_apn_ipup_script_cmd);
1119 install_element(APN_NODE, &cfg_apn_no_ipup_script_cmd);
1120 install_element(APN_NODE, &cfg_apn_ipdown_script_cmd);
1121 install_element(APN_NODE, &cfg_apn_no_ipdown_script_cmd);
1122 install_element(APN_NODE, &cfg_apn_ip_prefix_cmd);
1123 install_element(APN_NODE, &cfg_apn_ipv6_prefix_cmd);
1124 install_element(APN_NODE, &cfg_apn_ip_dns_cmd);
1125 install_element(APN_NODE, &cfg_apn_ipv6_dns_cmd);
1126 install_element(APN_NODE, &cfg_apn_no_dns_cmd);
1127 install_element(APN_NODE, &cfg_apn_ip_ifconfig_cmd);
1128 install_element(APN_NODE, &cfg_apn_no_ip_ifconfig_cmd);
1129 install_element(APN_NODE, &cfg_apn_ipv6_ifconfig_cmd);
1130 install_element(APN_NODE, &cfg_apn_no_ipv6_ifconfig_cmd);
Pau Espin Pedrol37c45e32017-12-14 14:09:13 +01001131 install_element(APN_NODE, &cfg_apn_ipv6_linklocal_cmd);
1132 install_element(APN_NODE, &cfg_apn_no_ipv6_linklocal_cmd);
Harald Welte93fed3b2017-09-24 11:43:17 +08001133 install_element(APN_NODE, &cfg_apn_gpdu_seq_cmd);
1134 install_element(APN_NODE, &cfg_apn_no_gpdu_seq_cmd);
Harald Weltedda21ed2017-08-12 15:07:02 +02001135
1136 return 0;
1137}
1138
1139static int ggsn_vty_is_config_node(struct vty *vty, int node)
1140{
1141 switch (node) {
1142 case GGSN_NODE:
1143 case APN_NODE:
1144 return 1;
1145 default:
1146 return 0;
1147 }
1148}
1149
1150static int ggsn_vty_go_parent(struct vty *vty)
1151{
1152 switch (vty->node) {
1153 case GGSN_NODE:
1154 vty->node = CONFIG_NODE;
1155 vty->index = NULL;
1156 vty->index_sub = NULL;
1157 break;
1158 case APN_NODE:
1159 vty->node = GGSN_NODE;
1160 {
1161 struct apn_ctx *apn = vty->index;
1162 vty->index = apn->ggsn;
1163 vty->index_sub = &apn->ggsn->cfg.description;
1164 }
1165 break;
Vadim Yanitskiy906c2092018-05-09 23:11:27 +07001166 default:
1167 vty->node = CONFIG_NODE;
1168 vty->index = NULL;
1169 vty->index_sub = NULL;
Harald Weltedda21ed2017-08-12 15:07:02 +02001170 }
1171
1172 return vty->node;
1173}
1174
1175static const char ggsn_copyright[] =
1176 "Copyright (C) 2011-2017 Harald Welte <laforge@gnumonks.org>\r\n"
1177 "Copyright (C) 2012-2017 Holger Hans Peter Freyther <holger@moiji-mobile.com>\r\n"
1178 "Copyright (C) 2012-2017 sysmocom - s.f.m.c. GmbH\r\n"
1179 "Copyright (C) 2002-2005 Mondru AB\r\n"
1180 "License GPLv2: GNU GPL version 2 <http://gnu.org/licenses/gpl-2.0.html>\r\n"
1181 "This is free software: you are free to change and redistribute it.\r\n"
1182 "There is NO WARRANTY, to the extent permitted by law.\r\n";
1183
1184struct vty_app_info g_vty_info = {
Harald Welte632e8432017-09-05 18:12:14 +02001185 .name = "OsmoGGSN",
Harald Weltedda21ed2017-08-12 15:07:02 +02001186 .version = PACKAGE_VERSION,
1187 .copyright = ggsn_copyright,
1188 .go_parent_cb = ggsn_vty_go_parent,
1189 .is_config_node = ggsn_vty_is_config_node,
1190};