blob: cb92a8a93371f2fa88715f4fdc225472a2e29424 [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
897 imsi = strtoull(argv[1], NULL, 10);
898
899 if (argc > 2) {
900 nsapi = atoi(argv[2]);
901 if (gtp_pdp_getimsi(ggsn->gsn, &pdp, imsi, nsapi)) {
Harald Weltedda21ed2017-08-12 15:07:02 +0200902 show_one_pdp(vty, pdp);
903 num_found++;
904 }
905 } else {
906 for (nsapi = 0; nsapi < PDP_MAXNSAPI; nsapi++) {
Pau Espin Pedrol7b52f002019-05-31 16:15:16 +0200907 if (gtp_pdp_getimsi(ggsn->gsn, &pdp, imsi, nsapi))
Harald Weltedda21ed2017-08-12 15:07:02 +0200908 continue;
909 show_one_pdp(vty, pdp);
910 num_found++;
911 }
912 }
913 if (num_found == 0) {
914 vty_out(vty, "%% No such PDP context found%s", VTY_NEWLINE);
915 return CMD_WARNING;
916 } else
917 return CMD_SUCCESS;
918}
919
Vadim Yanitskiyca276e02019-05-13 13:06:51 +0700920DEFUN(show_pdpctx_ip, show_pdpctx_ip_cmd,
921 "show pdp-context ggsn NAME ipv4 A.B.C.D",
922 SHOW_STR "Display information on PDP Context\n"
923 GGSN_STR "GGSN Name\n" "IPv4 address type\n" "IP address\n")
924{
925 struct ggsn_ctx *ggsn;
926 struct apn_ctx *apn;
927 unsigned int i;
928
929 ggsn = ggsn_find(argv[0]);
930 if (!ggsn) {
931 vty_out(vty, "%% No such GGSN '%s'%s", argv[0], VTY_NEWLINE);
932 return CMD_WARNING;
933 }
934
935 /* Iterate over all APNs of a given GGSN */
936 llist_for_each_entry(apn, &ggsn->apn_list, list) {
937 struct ippool_t *pool = apn->v4.pool;
938
939 /* In some rare cases, if GGSN fails to init TUN/TAP interfaces
940 * (e.g. due to insufficient permissions), it will continue to
941 * work in such broken state, and pool would be NULL. */
942 if (!pool)
943 continue;
944
945 /* Iterate over all IPv4 pool members */
946 for (i = 0; i < pool->listsize; i++) {
947 struct ippoolm_t *member = &pool->member[i];
948 if (member->inuse == 0)
949 continue;
950 if (strcmp(argv[1], in46a_ntoa(&member->addr)) == 0) {
951 show_one_pdp(vty, member->peer);
952 return CMD_SUCCESS;
953 }
954 }
955 }
956
957 vty_out(vty, "%% No PDP context found for IP '%s'%s", argv[1], VTY_NEWLINE);
958 return CMD_WARNING;
959}
960
Harald Weltedda21ed2017-08-12 15:07:02 +0200961/* show all (active) PDP contexts within a pool */
Pau Espin Pedrol310ea1d2019-08-20 14:15:25 +0200962static void ippool_show_pdp_contexts(struct vty *vty, struct ippool_t *pool, bool pdp_v4only)
Harald Weltedda21ed2017-08-12 15:07:02 +0200963{
964 unsigned int i;
965
966 if (!pool)
967 return;
968
969 for (i = 0; i < pool->listsize; i++) {
970 struct ippoolm_t *member = &pool->member[i];
971 if (member->inuse == 0)
972 continue;
Pau Espin Pedrol310ea1d2019-08-20 14:15:25 +0200973 show_one_pdp_v4only(vty, member->peer, pdp_v4only);
Harald Weltedda21ed2017-08-12 15:07:02 +0200974 }
975}
976
977/* show all (active) PDP contexts within an APN */
978static void apn_show_pdp_contexts(struct vty *vty, struct apn_ctx *apn)
979{
Pau Espin Pedrol310ea1d2019-08-20 14:15:25 +0200980 ippool_show_pdp_contexts(vty, apn->v4.pool, true);
981 ippool_show_pdp_contexts(vty, apn->v6.pool, false);
Harald Weltedda21ed2017-08-12 15:07:02 +0200982}
983
984DEFUN(show_pdpctx, show_pdpctx_cmd,
Vadim Yanitskiy977b3392019-05-13 15:32:21 +0700985 "show pdp-context ggsn NAME",
Harald Weltedda21ed2017-08-12 15:07:02 +0200986 SHOW_STR "Show PDP Context Information\n"
Vadim Yanitskiy977b3392019-05-13 15:32:21 +0700987 GGSN_STR "GGSN Name\n")
Harald Weltedda21ed2017-08-12 15:07:02 +0200988{
989 struct ggsn_ctx *ggsn;
990 struct apn_ctx *apn;
991
992 ggsn = ggsn_find(argv[0]);
993 if (!ggsn) {
994 vty_out(vty, "%% No such GGSN '%s'%s", argv[0], VTY_NEWLINE);
995 return CMD_WARNING;
996 }
Vadim Yanitskiy977b3392019-05-13 15:32:21 +0700997
998 llist_for_each_entry(apn, &ggsn->apn_list, list)
Harald Weltedda21ed2017-08-12 15:07:02 +0200999 apn_show_pdp_contexts(vty, apn);
Harald Weltedda21ed2017-08-12 15:07:02 +02001000
1001 return CMD_SUCCESS;
1002}
1003
Vadim Yanitskiy977b3392019-05-13 15:32:21 +07001004DEFUN(show_pdpctx_apn, show_pdpctx_apn_cmd,
1005 "show pdp-context ggsn NAME apn APN",
1006 SHOW_STR "Show PDP Context Information\n"
1007 GGSN_STR "GGSN Name\n" "Filter by APN\n" "APN name\n")
1008{
1009 struct ggsn_ctx *ggsn;
1010 struct apn_ctx *apn;
1011
1012 ggsn = ggsn_find(argv[0]);
1013 if (!ggsn) {
1014 vty_out(vty, "%% No such GGSN '%s'%s", argv[0], VTY_NEWLINE);
1015 return CMD_WARNING;
1016 }
1017
1018 apn = ggsn_find_apn(ggsn, argv[1]);
1019 if (!apn) {
1020 vty_out(vty, "%% No such APN '%s'%s", argv[1], VTY_NEWLINE);
1021 return CMD_WARNING;
1022 }
1023
1024 apn_show_pdp_contexts(vty, apn);
1025 return CMD_SUCCESS;
1026}
1027
1028/* Backwards compatibility: the VTY parser is (mis)interpreting
1029 * "[apn APN]" as two separate elements: "[apn" and "APN]",
1030 * but the first part somehow turns into command "ap". */
1031ALIAS_DEPRECATED(show_pdpctx_apn, show_deprecated_pdpctx_apn_cmd,
1032 "show pdp-context ggsn NAME ap APN",
1033 SHOW_STR "Show PDP Context Information\n"
1034 GGSN_STR "GGSN Name\n" "Filter by APN\n" "APN name\n");
1035
Harald Weltedda21ed2017-08-12 15:07:02 +02001036static void show_apn(struct vty *vty, struct apn_ctx *apn)
1037{
1038 vty_out(vty, " APN: %s%s", apn->cfg.name, VTY_NEWLINE);
1039 /* FIXME */
1040}
1041
1042static void show_one_ggsn(struct vty *vty, struct ggsn_ctx *ggsn)
1043{
1044 struct apn_ctx *apn;
Pau Espin Pedrolf5fbb412019-08-21 18:49:44 +02001045 struct sgsn_peer *sgsn;
Harald Weltedda21ed2017-08-12 15:07:02 +02001046 vty_out(vty, "GGSN %s: Bound to %s%s", ggsn->cfg.name, in46a_ntoa(&ggsn->cfg.listen_addr),
1047 VTY_NEWLINE);
1048 /* FIXME */
1049
1050 llist_for_each_entry(apn, &ggsn->apn_list, list)
1051 show_apn(vty, apn);
Pau Espin Pedrolf5fbb412019-08-21 18:49:44 +02001052 llist_for_each_entry(sgsn, &ggsn->sgsn_list, entry)
1053 show_one_sgsn(vty, sgsn, " ");
Harald Weltedda21ed2017-08-12 15:07:02 +02001054}
1055
1056DEFUN(show_ggsn, show_ggsn_cmd,
1057 "show ggsn [NAME]",
1058 SHOW_STR "Display information on the GGSN\n")
1059{
1060 struct ggsn_ctx *ggsn;
1061
1062 if (argc == 0) {
1063 llist_for_each_entry(ggsn, &g_ggsn_list, list)
1064 show_one_ggsn(vty, ggsn);
1065 } else {
1066 ggsn = ggsn_find(argv[0]);
1067 if (!ggsn)
1068 return CMD_WARNING;
1069 show_one_ggsn(vty, ggsn);
1070 }
1071
1072 return CMD_SUCCESS;
1073}
1074
1075int ggsn_vty_init(void)
1076{
1077 install_element_ve(&show_pdpctx_cmd);
Vadim Yanitskiy977b3392019-05-13 15:32:21 +07001078 install_element_ve(&show_pdpctx_apn_cmd);
1079 install_element_ve(&show_deprecated_pdpctx_apn_cmd);
Harald Weltedda21ed2017-08-12 15:07:02 +02001080 install_element_ve(&show_pdpctx_imsi_cmd);
Vadim Yanitskiyca276e02019-05-13 13:06:51 +07001081 install_element_ve(&show_pdpctx_ip_cmd);
Harald Weltedda21ed2017-08-12 15:07:02 +02001082 install_element_ve(&show_ggsn_cmd);
1083
1084 install_element(CONFIG_NODE, &cfg_ggsn_cmd);
1085 install_element(CONFIG_NODE, &cfg_no_ggsn_cmd);
Pau Espin Pedrol840ce8a2017-11-16 17:01:44 +01001086
Harald Weltedda21ed2017-08-12 15:07:02 +02001087 install_node(&ggsn_node, config_write_ggsn);
Harald Weltedda21ed2017-08-12 15:07:02 +02001088 install_element(GGSN_NODE, &cfg_description_cmd);
1089 install_element(GGSN_NODE, &cfg_no_description_cmd);
1090 install_element(GGSN_NODE, &cfg_ggsn_shutdown_cmd);
1091 install_element(GGSN_NODE, &cfg_ggsn_no_shutdown_cmd);
Harald Welte98146772017-09-05 17:41:20 +02001092 install_element(GGSN_NODE, &cfg_ggsn_bind_ip_cmd);
1093 install_element(GGSN_NODE, &cfg_ggsn_gtpc_ip_cmd);
1094 install_element(GGSN_NODE, &cfg_ggsn_gtpu_ip_cmd);
Harald Weltedda21ed2017-08-12 15:07:02 +02001095 install_element(GGSN_NODE, &cfg_ggsn_state_dir_cmd);
1096 install_element(GGSN_NODE, &cfg_ggsn_apn_cmd);
1097 install_element(GGSN_NODE, &cfg_ggsn_no_apn_cmd);
1098 install_element(GGSN_NODE, &cfg_ggsn_default_apn_cmd);
1099 install_element(GGSN_NODE, &cfg_ggsn_no_default_apn_cmd);
Pau Espin Pedrolf5fbb412019-08-21 18:49:44 +02001100 install_element(GGSN_NODE, &cfg_ggsn_show_sgsn_cmd);
1101 install_element(GGSN_NODE, &cfg_ggsn_echo_interval_cmd);
1102 install_element(GGSN_NODE, &cfg_ggsn_no_echo_interval_cmd);
Harald Weltedda21ed2017-08-12 15:07:02 +02001103
1104 install_node(&apn_node, NULL);
Harald Weltedda21ed2017-08-12 15:07:02 +02001105 install_element(APN_NODE, &cfg_description_cmd);
1106 install_element(APN_NODE, &cfg_no_description_cmd);
1107 install_element(APN_NODE, &cfg_apn_shutdown_cmd);
1108 install_element(APN_NODE, &cfg_apn_no_shutdown_cmd);
1109 install_element(APN_NODE, &cfg_apn_gtpu_mode_cmd);
1110 install_element(APN_NODE, &cfg_apn_type_support_cmd);
1111 install_element(APN_NODE, &cfg_apn_no_type_support_cmd);
1112 install_element(APN_NODE, &cfg_apn_tun_dev_name_cmd);
1113 install_element(APN_NODE, &cfg_apn_ipup_script_cmd);
1114 install_element(APN_NODE, &cfg_apn_no_ipup_script_cmd);
1115 install_element(APN_NODE, &cfg_apn_ipdown_script_cmd);
1116 install_element(APN_NODE, &cfg_apn_no_ipdown_script_cmd);
1117 install_element(APN_NODE, &cfg_apn_ip_prefix_cmd);
1118 install_element(APN_NODE, &cfg_apn_ipv6_prefix_cmd);
1119 install_element(APN_NODE, &cfg_apn_ip_dns_cmd);
1120 install_element(APN_NODE, &cfg_apn_ipv6_dns_cmd);
1121 install_element(APN_NODE, &cfg_apn_no_dns_cmd);
1122 install_element(APN_NODE, &cfg_apn_ip_ifconfig_cmd);
1123 install_element(APN_NODE, &cfg_apn_no_ip_ifconfig_cmd);
1124 install_element(APN_NODE, &cfg_apn_ipv6_ifconfig_cmd);
1125 install_element(APN_NODE, &cfg_apn_no_ipv6_ifconfig_cmd);
Pau Espin Pedrol37c45e32017-12-14 14:09:13 +01001126 install_element(APN_NODE, &cfg_apn_ipv6_linklocal_cmd);
1127 install_element(APN_NODE, &cfg_apn_no_ipv6_linklocal_cmd);
Harald Welte93fed3b2017-09-24 11:43:17 +08001128 install_element(APN_NODE, &cfg_apn_gpdu_seq_cmd);
1129 install_element(APN_NODE, &cfg_apn_no_gpdu_seq_cmd);
Harald Weltedda21ed2017-08-12 15:07:02 +02001130
1131 return 0;
1132}
1133
1134static int ggsn_vty_is_config_node(struct vty *vty, int node)
1135{
1136 switch (node) {
1137 case GGSN_NODE:
1138 case APN_NODE:
1139 return 1;
1140 default:
1141 return 0;
1142 }
1143}
1144
1145static int ggsn_vty_go_parent(struct vty *vty)
1146{
1147 switch (vty->node) {
1148 case GGSN_NODE:
1149 vty->node = CONFIG_NODE;
1150 vty->index = NULL;
1151 vty->index_sub = NULL;
1152 break;
1153 case APN_NODE:
1154 vty->node = GGSN_NODE;
1155 {
1156 struct apn_ctx *apn = vty->index;
1157 vty->index = apn->ggsn;
1158 vty->index_sub = &apn->ggsn->cfg.description;
1159 }
1160 break;
Vadim Yanitskiy906c2092018-05-09 23:11:27 +07001161 default:
1162 vty->node = CONFIG_NODE;
1163 vty->index = NULL;
1164 vty->index_sub = NULL;
Harald Weltedda21ed2017-08-12 15:07:02 +02001165 }
1166
1167 return vty->node;
1168}
1169
1170static const char ggsn_copyright[] =
1171 "Copyright (C) 2011-2017 Harald Welte <laforge@gnumonks.org>\r\n"
1172 "Copyright (C) 2012-2017 Holger Hans Peter Freyther <holger@moiji-mobile.com>\r\n"
1173 "Copyright (C) 2012-2017 sysmocom - s.f.m.c. GmbH\r\n"
1174 "Copyright (C) 2002-2005 Mondru AB\r\n"
1175 "License GPLv2: GNU GPL version 2 <http://gnu.org/licenses/gpl-2.0.html>\r\n"
1176 "This is free software: you are free to change and redistribute it.\r\n"
1177 "There is NO WARRANTY, to the extent permitted by law.\r\n";
1178
1179struct vty_app_info g_vty_info = {
Harald Welte632e8432017-09-05 18:12:14 +02001180 .name = "OsmoGGSN",
Harald Weltedda21ed2017-08-12 15:07:02 +02001181 .version = PACKAGE_VERSION,
1182 .copyright = ggsn_copyright,
1183 .go_parent_cb = ggsn_vty_go_parent,
1184 .is_config_node = ggsn_vty_is_config_node,
1185};