blob: 2d49a945c46124e9431b4cf3df9159370ca33319 [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>
Pau Espin Pedrol9f1f7472022-11-02 19:52:19 +010036#include <osmocom/vty/tdef_vty.h>
Harald Weltedda21ed2017-08-12 15:07:02 +020037
38#include "../gtp/gtp.h"
39#include "../gtp/pdp.h"
40
Pau Espin Pedrolf612ffe2019-08-20 13:24:55 +020041#include "../lib/util.h"
42
Harald Weltedda21ed2017-08-12 15:07:02 +020043#include "ggsn.h"
Pau Espin Pedrolf5fbb412019-08-21 18:49:44 +020044#include "sgsn.h"
Harald Weltedda21ed2017-08-12 15:07:02 +020045
46#define PREFIX_STR "Prefix (Network/Netmask)\n"
47#define IFCONFIG_STR "GGSN-based interface configuration\n"
48#define GGSN_STR "Gateway GPRS Support NODE (GGSN)\n"
49
50LLIST_HEAD(g_ggsn_list);
51
52enum ggsn_vty_node {
53 GGSN_NODE = _LAST_OSMOVTY_NODE + 1,
54 APN_NODE,
55};
56
57struct ggsn_ctx *ggsn_find(const char *name)
58{
59 struct ggsn_ctx *ggsn;
60
61 llist_for_each_entry(ggsn, &g_ggsn_list, list) {
62 if (!strcmp(ggsn->cfg.name, name))
63 return ggsn;
64 }
65 return NULL;
66}
67
68struct ggsn_ctx *ggsn_find_or_create(void *ctx, const char *name)
69{
70 struct ggsn_ctx *ggsn;
71
72 ggsn = ggsn_find(name);
73 if (ggsn)
74 return ggsn;
75
76 ggsn = talloc_zero(ctx, struct ggsn_ctx);
77 if (!ggsn)
78 return NULL;
79
80 ggsn->cfg.name = talloc_strdup(ggsn, name);
81 ggsn->cfg.state_dir = talloc_strdup(ggsn, "/tmp");
82 ggsn->cfg.shutdown = true;
83 INIT_LLIST_HEAD(&ggsn->apn_list);
Pau Espin Pedrolf5fbb412019-08-21 18:49:44 +020084 INIT_LLIST_HEAD(&ggsn->sgsn_list);
Harald Weltedda21ed2017-08-12 15:07:02 +020085
86 llist_add_tail(&ggsn->list, &g_ggsn_list);
87 return ggsn;
88}
89
90struct apn_ctx *ggsn_find_apn(struct ggsn_ctx *ggsn, const char *name)
91{
92 struct apn_ctx *apn;
93
94 llist_for_each_entry(apn, &ggsn->apn_list, list) {
95 if (!strcmp(apn->cfg.name, name))
96 return apn;
97 }
98 return NULL;
99}
100
101struct apn_ctx *ggsn_find_or_create_apn(struct ggsn_ctx *ggsn, const char *name)
102{
103 struct apn_ctx *apn = ggsn_find_apn(ggsn, name);
104 if (apn)
105 return apn;
106
107 apn = talloc_zero(ggsn, struct apn_ctx);
108 if (!apn)
109 return NULL;
110 apn->ggsn = ggsn;
111 apn->cfg.name = talloc_strdup(apn, name);
112 apn->cfg.shutdown = true;
Harald Welte93fed3b2017-09-24 11:43:17 +0800113 apn->cfg.tx_gpdu_seq = true;
Harald Weltedda21ed2017-08-12 15:07:02 +0200114 INIT_LLIST_HEAD(&apn->cfg.name_list);
115
116 llist_add_tail(&apn->list, &ggsn->apn_list);
117 return apn;
118}
119
120/* GGSN Node */
121
122static struct cmd_node ggsn_node = {
123 GGSN_NODE,
124 "%s(config-ggsn)# ",
125 1,
126};
127
128DEFUN(cfg_ggsn, cfg_ggsn_cmd,
129 "ggsn NAME",
130 "Configure the Gateway GPRS Support Node\n" "GGSN Name (has only local significance)\n")
131{
132 struct ggsn_ctx *ggsn;
133
134 ggsn = ggsn_find_or_create(tall_ggsn_ctx, argv[0]);
135 if (!ggsn)
136 return CMD_WARNING;
137
138 vty->node = GGSN_NODE;
139 vty->index = ggsn;
140 vty->index_sub = &ggsn->cfg.description;
141
142 return CMD_SUCCESS;
143}
144
145DEFUN(cfg_no_ggsn, cfg_no_ggsn_cmd,
146 "no ggsn NAME",
147 NO_STR "Remove the named Gateway GPRS Support Node\n"
148 "GGSN Name (has only local significance)\n")
149{
150 struct ggsn_ctx *ggsn;
151
152 ggsn = ggsn_find(argv[0]);
153 if (!ggsn) {
154 vty_out(vty, "%% No such GGSN '%s'%s", argv[0], VTY_NEWLINE);
155 return CMD_WARNING;
156 }
157
158 if (!ggsn->cfg.shutdown) {
159 vty_out(vty, "%% GGSN %s is still active, please shutdown first%s",
160 ggsn->cfg.name, VTY_NEWLINE);
161 return CMD_WARNING;
162 }
163
164 if (!llist_empty(&ggsn->apn_list)) {
165 vty_out(vty, "%% GGSN %s still has APNs configured, please remove first%s",
166 ggsn->cfg.name, VTY_NEWLINE);
167 return CMD_WARNING;
168 }
169
170 llist_del(&ggsn->list);
171 talloc_free(ggsn);
172
173 return CMD_SUCCESS;
174}
175
Harald Welte98146772017-09-05 17:41:20 +0200176DEFUN(cfg_ggsn_bind_ip, cfg_ggsn_bind_ip_cmd,
177 "gtp bind-ip A.B.C.D",
Harald Weltedda21ed2017-08-12 15:07:02 +0200178 "GTP Parameters\n"
179 "Set the IP address for the local GTP bind\n"
180 "IPv4 Address\n")
181{
182 struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
183 size_t t;
184
185 ippool_aton(&ggsn->cfg.listen_addr, &t, argv[0], 0);
186
187 return CMD_SUCCESS;
188}
189
Harald Welte98146772017-09-05 17:41:20 +0200190DEFUN(cfg_ggsn_gtpc_ip, cfg_ggsn_gtpc_ip_cmd,
191 "gtp control-ip A.B.C.D",
192 "GTP Parameters\n"
193 "Set the IP address states as local IP in GTP-C messages\n"
194 "IPv4 Address\n")
195{
196 struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
197 size_t t;
198
199 ippool_aton(&ggsn->cfg.gtpc_addr, &t, argv[0], 0);
200
201 return CMD_SUCCESS;
202}
203
204DEFUN(cfg_ggsn_gtpu_ip, cfg_ggsn_gtpu_ip_cmd,
205 "gtp user-ip A.B.C.D",
206 "GTP Parameters\n"
207 "Set the IP address states as local IP in GTP-U messages\n"
208 "IPv4 Address\n")
209{
210 struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
211 size_t t;
212
213 ippool_aton(&ggsn->cfg.gtpu_addr, &t, argv[0], 0);
214
215 return CMD_SUCCESS;
216}
217
Harald Weltedda21ed2017-08-12 15:07:02 +0200218DEFUN(cfg_ggsn_state_dir, cfg_ggsn_state_dir_cmd,
219 "gtp state-dir PATH",
220 "GTP Parameters\n"
221 "Set the directory for the GTP State file\n"
222 "Local Directory\n")
223{
224 struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
225
226 osmo_talloc_replace_string(ggsn, &ggsn->cfg.state_dir, argv[0]);
227
228 return CMD_SUCCESS;
229}
230
231DEFUN(cfg_ggsn_apn, cfg_ggsn_apn_cmd,
232 "apn NAME", "APN Configuration\n" "APN Name\n")
233{
234 struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
235 struct apn_ctx *apn;
236
237 apn = ggsn_find_or_create_apn(ggsn, argv[0]);
238 if (!apn)
239 return CMD_WARNING;
240
241 vty->node = APN_NODE;
242 vty->index = apn;
243 vty->index_sub = &ggsn->cfg.description;
244
245 return CMD_SUCCESS;
246}
247
248DEFUN(cfg_ggsn_no_apn, cfg_ggsn_no_apn_cmd,
249 "no apn NAME",
250 NO_STR "Remove APN Configuration\n" "APN Name\n")
251{
252 struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
253 struct apn_ctx *apn;
254
255 apn = ggsn_find_apn(ggsn, argv[0]);
256 if (!apn) {
257 vty_out(vty, "%% No such APN '%s'%s", argv[0], VTY_NEWLINE);
258 return CMD_WARNING;
259 }
260
261 if (!apn->cfg.shutdown) {
262 vty_out(vty, "%% APN %s still active, please shutdown first%s",
263 apn->cfg.name, VTY_NEWLINE);
264 return CMD_WARNING;
265 }
266
267 llist_del(&apn->list);
268 talloc_free(apn);
269
270 return CMD_SUCCESS;
271}
272
273DEFUN(cfg_ggsn_default_apn, cfg_ggsn_default_apn_cmd,
274 "default-apn NAME",
275 "Set a default-APN to be used if no other APN matches\n"
276 "APN Name\n")
277{
278 struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
279 struct apn_ctx *apn;
280
281 apn = ggsn_find_apn(ggsn, argv[0]);
282 if (!apn) {
283 vty_out(vty, "%% No APN of name '%s' found%s", argv[0], VTY_NEWLINE);
284 return CMD_WARNING;
285 }
286
287 ggsn->cfg.default_apn = apn;
288 return CMD_SUCCESS;
289}
290
291DEFUN(cfg_ggsn_no_default_apn, cfg_ggsn_no_default_apn_cmd,
292 "no default-apn",
293 NO_STR "Remove default-APN to be used if no other APN matches\n")
294{
295 struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
296 ggsn->cfg.default_apn = NULL;
297 return CMD_SUCCESS;
298}
299
300DEFUN(cfg_ggsn_shutdown, cfg_ggsn_shutdown_cmd,
301 "shutdown ggsn",
Pau Espin Pedrol4fac8422023-01-17 14:17:18 +0100302 "Put the GGSN in administrative shutdown\n" GGSN_STR)
Harald Weltedda21ed2017-08-12 15:07:02 +0200303{
304 struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
305
306 if (!ggsn->cfg.shutdown) {
307 if (ggsn_stop(ggsn)) {
308 vty_out(vty, "%% Failed to shutdown GGSN%s", VTY_NEWLINE);
309 return CMD_WARNING;
310 }
311 ggsn->cfg.shutdown = true;
312 }
313
314 return CMD_SUCCESS;
315}
316
317DEFUN(cfg_ggsn_no_shutdown, cfg_ggsn_no_shutdown_cmd,
318 "no shutdown ggsn",
Pau Espin Pedrol4fac8422023-01-17 14:17:18 +0100319 NO_STR GGSN_STR "Remove the GGSN from administrative shutdown\n")
Harald Weltedda21ed2017-08-12 15:07:02 +0200320{
321 struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
322
323 if (ggsn->cfg.shutdown) {
324 if (ggsn_start(ggsn) < 0) {
325 vty_out(vty, "%% Failed to start GGSN, check log for details%s", VTY_NEWLINE);
326 return CMD_WARNING;
327 }
328 ggsn->cfg.shutdown = false;
329 }
330
331 return CMD_SUCCESS;
332}
333
Pau Espin Pedrolf5fbb412019-08-21 18:49:44 +0200334static void show_one_sgsn(struct vty *vty, const struct sgsn_peer *sgsn, const char* prefix)
335{
336 char buf[INET_ADDRSTRLEN];
337
338 inet_ntop(AF_INET, &sgsn->addr, buf, sizeof(buf));
339 vty_out(vty, "%s(S)GSN %s%s", prefix, buf, VTY_NEWLINE);
340 vty_out(vty, "%s Restart Counter: %d%s", prefix, sgsn->remote_restart_ctr, VTY_NEWLINE);
341 vty_out(vty, "%s PDP contexts: %d%s", prefix, llist_count(&sgsn->pdp_list), VTY_NEWLINE);
342 vty_out(vty, "%s Echo Requests in-flight: %u%s", prefix, sgsn->tx_msgs_queued, VTY_NEWLINE);
343}
344
345DEFUN(cfg_ggsn_show_sgsn, cfg_ggsn_show_sgsn_cmd,
346 "show sgsn",
Pau Espin Pedrol4fac8422023-01-17 14:17:18 +0100347 NO_STR GGSN_STR "Remove the GGSN from administrative shutdown\n")
Pau Espin Pedrolf5fbb412019-08-21 18:49:44 +0200348{
349 struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
350 struct sgsn_peer *sgsn;
351
352 llist_for_each_entry(sgsn, &ggsn->sgsn_list, entry) {
353 show_one_sgsn(vty, sgsn, "");
354 }
355
356 return CMD_SUCCESS;
357}
358
359/* Seee 3GPP TS 29.060 section 7.2.1 */
360DEFUN(cfg_ggsn_echo_interval, cfg_ggsn_echo_interval_cmd,
361 "echo-interval <1-36000>",
362 GGSN_STR "GGSN Number\n"
363 "Send an echo request to this static GGSN every interval\n"
364 "Interval between echo requests in seconds\n")
365{
366 struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
367 int prev_interval = ggsn->cfg.echo_interval;
368 struct sgsn_peer *sgsn;
369
370 ggsn->cfg.echo_interval = atoi(argv[0]);
371
372 if (ggsn->cfg.echo_interval < 60)
373 vty_out(vty, "%% 3GPP TS 29.060 section states interval should " \
374 "not be lower than 60 seconds, use this value for " \
375 "testing purposes only!%s", VTY_NEWLINE);
376
377 if (prev_interval == ggsn->cfg.echo_interval)
378 return CMD_SUCCESS;
379
380 /* Re-enable echo timer for all sgsn */
381 llist_for_each_entry(sgsn, &ggsn->sgsn_list, entry)
382 sgsn_echo_timer_start(sgsn);
383
384 return CMD_SUCCESS;
385}
386
387DEFUN(cfg_ggsn_no_echo_interval, cfg_ggsn_no_echo_interval_cmd,
388 "no echo-interval",
389 GGSN_STR "GGSN Number\n"
390 NO_STR "Send an echo request to this static GGSN every interval.\n")
391{
392 struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
Pau Espin Pedrolf5fbb412019-08-21 18:49:44 +0200393 struct sgsn_peer *sgsn;
394
Pau Espin Pedrol134ac7e2022-02-25 17:24:07 +0100395 if (ggsn->cfg.echo_interval == 0)
Pau Espin Pedrolf5fbb412019-08-21 18:49:44 +0200396 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 */
Harald Welte53792732021-03-27 19:03:30 +0100536 if (!strcmp(argv[0], "static")) {
Harald Weltedda21ed2017-08-12 15:07:02 +0200537 pfx = &apn->v4.cfg.static_prefix;
Harald Welte53792732021-03-27 19:03:30 +0100538 vty_out(vty, "%% static IP addresses currently not yet supported%s", VTY_NEWLINE);
539 return CMD_WARNING;
540 } else
Harald Weltedda21ed2017-08-12 15:07:02 +0200541 pfx = &apn->v4.cfg.dynamic_prefix;
542 str2prefix(pfx, argv[1]);
543
544 return CMD_SUCCESS;
545}
546
547DEFUN(cfg_apn_ip_ifconfig, cfg_apn_ip_ifconfig_cmd,
548 "ip ifconfig A.B.C.D/M",
549 IP_STR IFCONFIG_STR "IPv4 Adress/Prefix-Length\n")
550{
551 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
552 str2prefix(&apn->v4.cfg.ifconfig_prefix, argv[0]);
553 return CMD_SUCCESS;
554}
555
556DEFUN(cfg_apn_no_ip_ifconfig, cfg_apn_no_ip_ifconfig_cmd,
557 "no ip ifconfig",
558 NO_STR IP_STR IFCONFIG_STR)
559{
560 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
561 memset(&apn->v4.cfg.ifconfig_prefix, 0, sizeof(apn->v4.cfg.ifconfig_prefix));
562 return CMD_SUCCESS;
563}
564
565DEFUN(cfg_apn_ipv6_prefix, cfg_apn_ipv6_prefix_cmd,
566 "ipv6 prefix (static|dynamic) X:X::X:X/M",
567 IP6_STR PREFIX_STR "IPv6 Address/Prefix-Length\n")
568{
569 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
570 struct in46_prefix *pfx;
571
Harald Welte53792732021-03-27 19:03:30 +0100572 if (!strcmp(argv[0], "static")) {
Harald Weltedda21ed2017-08-12 15:07:02 +0200573 pfx = &apn->v6.cfg.static_prefix;
Harald Welte53792732021-03-27 19:03:30 +0100574 vty_out(vty, "%% static IP addresses currently not yet supported%s", VTY_NEWLINE);
575 return CMD_WARNING;
576 } else
Harald Weltedda21ed2017-08-12 15:07:02 +0200577 pfx = &apn->v6.cfg.dynamic_prefix;
578 str2prefix(pfx, argv[1]);
579 return CMD_SUCCESS;
580}
581
582DEFUN(cfg_apn_ipv6_ifconfig, cfg_apn_ipv6_ifconfig_cmd,
583 "ipv6 ifconfig X:X::X:X/M",
584 IP6_STR IFCONFIG_STR "IPv6 Adress/Prefix-Length\n")
585{
586 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
587 str2prefix(&apn->v6.cfg.ifconfig_prefix, argv[0]);
588 return CMD_SUCCESS;
589}
590
591DEFUN(cfg_apn_no_ipv6_ifconfig, cfg_apn_no_ipv6_ifconfig_cmd,
592 "no ipv6 ifconfig",
593 NO_STR IP6_STR IFCONFIG_STR)
594{
595 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
596 memset(&apn->v6.cfg.ifconfig_prefix, 0, sizeof(apn->v6.cfg.ifconfig_prefix));
597 return CMD_SUCCESS;
598}
599
Pau Espin Pedrol37c45e32017-12-14 14:09:13 +0100600DEFUN(cfg_apn_ipv6_linklocal, cfg_apn_ipv6_linklocal_cmd,
601 "ipv6 link-local X:X::X:X/M",
602 IP6_STR IFCONFIG_STR "IPv6 Link-local Adress/Prefix-Length\n")
603{
604 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
605 str2prefix(&apn->v6.cfg.ll_prefix, argv[0]);
606 return CMD_SUCCESS;
607}
608
609DEFUN(cfg_apn_no_ipv6_linklocal, cfg_apn_no_ipv6_linklocal_cmd,
610 "no ipv6 link-local",
611 NO_STR IP6_STR IFCONFIG_STR)
612{
613 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
614 memset(&apn->v6.cfg.ll_prefix, 0, sizeof(apn->v6.cfg.ll_prefix));
615 return CMD_SUCCESS;
616}
617
Harald Weltedda21ed2017-08-12 15:07:02 +0200618#define DNS_STRINGS "Configure DNS Server\n" "primary/secondary DNS\n" "IP address of DNS Sever\n"
619
620DEFUN(cfg_apn_ip_dns, cfg_apn_ip_dns_cmd,
621 "ip dns <0-1> A.B.C.D",
622 IP_STR DNS_STRINGS)
623{
624 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
625 int idx = atoi(argv[0]);
626 size_t dummy;
627
628 ippool_aton(&apn->v4.cfg.dns[idx], &dummy, argv[1], 0);
629
630 return CMD_SUCCESS;
631}
632
633DEFUN(cfg_apn_ipv6_dns, cfg_apn_ipv6_dns_cmd,
634 "ipv6 dns <0-1> X:X::X:X",
635 IP6_STR DNS_STRINGS)
636{
637 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
638 int idx = atoi(argv[0]);
639 size_t dummy;
640
641 ippool_aton(&apn->v6.cfg.dns[idx], &dummy, argv[1], 0);
642
643 return CMD_SUCCESS;
644}
645
646DEFUN(cfg_apn_no_dns, cfg_apn_no_dns_cmd,
647 "no (ip|ipv6) dns <0-1>",
648 NO_STR IP_STR IP6_STR "Disable DNS Server\n" "primary/secondary DNS\n")
649{
650 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
651 struct in46_addr *a;
652 int idx = atoi(argv[1]);
653
654 if (!strcmp(argv[0], "ip"))
655 a = &apn->v4.cfg.dns[idx];
656 else
657 a = &apn->v6.cfg.dns[idx];
658
659 memset(a, 0, sizeof(*a));
660
661 return CMD_SUCCESS;
662}
663
Harald Welte93fed3b2017-09-24 11:43:17 +0800664DEFUN(cfg_apn_gpdu_seq, cfg_apn_gpdu_seq_cmd,
665 "g-pdu tx-sequence-numbers",
666 "G-PDU Configuration\n" "Enable transmission of G-PDU sequence numbers\n")
667{
668 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
669 apn->cfg.tx_gpdu_seq = true;
670 return CMD_SUCCESS;
671}
672
673DEFUN(cfg_apn_no_gpdu_seq, cfg_apn_no_gpdu_seq_cmd,
674 "no g-pdu tx-sequence-numbers",
675 NO_STR "G-PDU Configuration\n" "Disable transmission of G-PDU sequence numbers\n")
676{
677 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
678 apn->cfg.tx_gpdu_seq = false;
679 return CMD_SUCCESS;
680}
681
Harald Weltedda21ed2017-08-12 15:07:02 +0200682DEFUN(cfg_apn_shutdown, cfg_apn_shutdown_cmd,
683 "shutdown",
Pau Espin Pedrol4fac8422023-01-17 14:17:18 +0100684 "Put the APN in administrative shutdown\n")
Harald Weltedda21ed2017-08-12 15:07:02 +0200685{
686 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
687
688 if (!apn->cfg.shutdown) {
Pau Espin Pedrol72ab4bc2019-05-29 19:08:26 +0200689 if (apn_stop(apn)) {
Harald Weltedda21ed2017-08-12 15:07:02 +0200690 vty_out(vty, "%% Failed to Stop APN%s", VTY_NEWLINE);
691 return CMD_WARNING;
692 }
693 apn->cfg.shutdown = true;
694 }
695
696 return CMD_SUCCESS;
697}
698
699DEFUN(cfg_apn_no_shutdown, cfg_apn_no_shutdown_cmd,
700 "no shutdown",
Pau Espin Pedrol4fac8422023-01-17 14:17:18 +0100701 NO_STR "Remove the APN from administrative shutdown\n")
Harald Weltedda21ed2017-08-12 15:07:02 +0200702{
703 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
704
705 if (apn->cfg.shutdown) {
Oliver Smith02a82c32021-02-05 14:15:20 +0100706 if (!apn->tun.cfg.dev_name) {
707 vty_out(vty, "%% Failed to start APN, tun-device is not configured%s", VTY_NEWLINE);
708 return CMD_WARNING;
709 }
Harald Weltedda21ed2017-08-12 15:07:02 +0200710 if (apn_start(apn) < 0) {
711 vty_out(vty, "%% Failed to start APN, check log for details%s", VTY_NEWLINE);
712 return CMD_WARNING;
713 }
714 apn->cfg.shutdown = false;
715 }
716
717 return CMD_SUCCESS;
718}
719
720
721static void vty_dump_prefix(struct vty *vty, const char *pre, const struct in46_prefix *pfx)
722{
723 vty_out(vty, "%s %s%s", pre, in46p_ntoa(pfx), VTY_NEWLINE);
724}
725
726static void config_write_apn(struct vty *vty, struct apn_ctx *apn)
727{
728 unsigned int i;
729
730 vty_out(vty, " apn %s%s", apn->cfg.name, VTY_NEWLINE);
731 if (apn->cfg.description)
732 vty_out(vty, " description %s%s", apn->cfg.description, VTY_NEWLINE);
733 vty_out(vty, " gtpu-mode %s%s", get_value_string(apn_gtpu_mode_names, apn->cfg.gtpu_mode),
734 VTY_NEWLINE);
735 if (apn->tun.cfg.dev_name)
736 vty_out(vty, " tun-device %s%s", apn->tun.cfg.dev_name, VTY_NEWLINE);
737 if (apn->tun.cfg.ipup_script)
738 vty_out(vty, " ipup-script %s%s", apn->tun.cfg.ipup_script, VTY_NEWLINE);
739 if (apn->tun.cfg.ipdown_script)
740 vty_out(vty, " ipdown-script %s%s", apn->tun.cfg.ipdown_script, VTY_NEWLINE);
741
742 for (i = 0; i < 32; i++) {
Pau Espin Pedrol55600012019-05-30 17:29:09 +0200743 if (!(apn->cfg.apn_type_mask & (UINT32_C(1) << i)))
Harald Weltedda21ed2017-08-12 15:07:02 +0200744 continue;
Pau Espin Pedrol55600012019-05-30 17:29:09 +0200745 vty_out(vty, " type-support %s%s", get_value_string(pdp_type_names, (UINT32_C(1) << i)),
Harald Weltedda21ed2017-08-12 15:07:02 +0200746 VTY_NEWLINE);
747 }
748
Harald Welte93fed3b2017-09-24 11:43:17 +0800749 if (!apn->cfg.tx_gpdu_seq)
750 vty_out(vty, " no g-pdu tx-sequence-numbers%s", VTY_NEWLINE);
751
Harald Weltedda21ed2017-08-12 15:07:02 +0200752 /* IPv4 prefixes + DNS */
753 if (apn->v4.cfg.static_prefix.addr.len)
754 vty_dump_prefix(vty, " ip prefix static", &apn->v4.cfg.static_prefix);
755 if (apn->v4.cfg.dynamic_prefix.addr.len)
756 vty_dump_prefix(vty, " ip prefix dynamic", &apn->v4.cfg.dynamic_prefix);
757 for (i = 0; i < ARRAY_SIZE(apn->v4.cfg.dns); i++) {
758 if (!apn->v4.cfg.dns[i].len)
759 continue;
760 vty_out(vty, " ip dns %u %s%s", i, in46a_ntoa(&apn->v4.cfg.dns[i]), VTY_NEWLINE);
761 }
762 if (apn->v4.cfg.ifconfig_prefix.addr.len)
Harald Welteff438172017-09-24 22:48:46 +0800763 vty_dump_prefix(vty, " ip ifconfig", &apn->v4.cfg.ifconfig_prefix);
Harald Weltedda21ed2017-08-12 15:07:02 +0200764
765 /* IPv6 prefixes + DNS */
766 if (apn->v6.cfg.static_prefix.addr.len)
767 vty_dump_prefix(vty, " ipv6 prefix static", &apn->v6.cfg.static_prefix);
768 if (apn->v6.cfg.dynamic_prefix.addr.len)
769 vty_dump_prefix(vty, " ipv6 prefix dynamic", &apn->v6.cfg.dynamic_prefix);
770 for (i = 0; i < ARRAY_SIZE(apn->v6.cfg.dns); i++) {
771 if (!apn->v6.cfg.dns[i].len)
772 continue;
Harald Welte3ca419a2017-09-24 22:47:51 +0800773 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 +0200774 }
775 if (apn->v6.cfg.ifconfig_prefix.addr.len)
Harald Welteff438172017-09-24 22:48:46 +0800776 vty_dump_prefix(vty, " ipv6 ifconfig", &apn->v6.cfg.ifconfig_prefix);
Pau Espin Pedrole5a082d2017-12-15 15:55:04 +0100777 if (apn->v6.cfg.ll_prefix.addr.len)
778 vty_dump_prefix(vty, " ipv6 link-local", &apn->v6.cfg.ll_prefix);
Harald Weltedda21ed2017-08-12 15:07:02 +0200779
780 /* must be last */
781 vty_out(vty, " %sshutdown%s", apn->cfg.shutdown ? "" : "no ", VTY_NEWLINE);
782}
783
784static int config_write_ggsn(struct vty *vty)
785{
786 struct ggsn_ctx *ggsn;
787
788 llist_for_each_entry(ggsn, &g_ggsn_list, list) {
789 struct apn_ctx *apn;
790 vty_out(vty, "ggsn %s%s", ggsn->cfg.name, VTY_NEWLINE);
791 if (ggsn->cfg.description)
792 vty_out(vty, " description %s%s", ggsn->cfg.description, VTY_NEWLINE);
793 vty_out(vty, " gtp state-dir %s%s", ggsn->cfg.state_dir, VTY_NEWLINE);
Harald Welte98146772017-09-05 17:41:20 +0200794 vty_out(vty, " gtp bind-ip %s%s", in46a_ntoa(&ggsn->cfg.listen_addr), VTY_NEWLINE);
795 if (ggsn->cfg.gtpc_addr.v4.s_addr)
796 vty_out(vty, " gtp control-ip %s%s", in46a_ntoa(&ggsn->cfg.gtpc_addr), VTY_NEWLINE);
797 if (ggsn->cfg.gtpu_addr.v4.s_addr)
798 vty_out(vty, " gtp user-ip %s%s", in46a_ntoa(&ggsn->cfg.gtpu_addr), VTY_NEWLINE);
Pau Espin Pedrol9f1f7472022-11-02 19:52:19 +0100799 osmo_tdef_vty_groups_write(vty, " ");
Harald Weltedda21ed2017-08-12 15:07:02 +0200800 llist_for_each_entry(apn, &ggsn->apn_list, list)
801 config_write_apn(vty, apn);
802 if (ggsn->cfg.default_apn)
803 vty_out(vty, " default-apn %s%s", ggsn->cfg.default_apn->cfg.name, VTY_NEWLINE);
Pau Espin Pedrolf5fbb412019-08-21 18:49:44 +0200804 if (ggsn->cfg.echo_interval)
805 vty_out(vty, " echo-interval %u%s", ggsn->cfg.echo_interval, VTY_NEWLINE);
Harald Weltedda21ed2017-08-12 15:07:02 +0200806 /* must be last */
807 vty_out(vty, " %sshutdown ggsn%s", ggsn->cfg.shutdown ? "" : "no ", VTY_NEWLINE);
808 }
809
810 return 0;
811}
812
813static const char *print_gsnaddr(const struct ul16_t *in)
814{
815 struct in46_addr in46;
816
817 in46.len = in->l;
818 OSMO_ASSERT(in->l <= sizeof(in46.v6));
819 memcpy(&in46.v6, in->v, in->l);
820
821 return in46a_ntoa(&in46);
822}
823
Pau Espin Pedrol310ea1d2019-08-20 14:15:25 +0200824/* Useful for v4v6 APNs, where we first iterate over v4 pool and then over v6
825 pool. param v4only can be used to avoid printing duplicates for pdp context
826 containing both IPv4 and IPv6 addresses. */
827static void show_one_pdp_v4only(struct vty *vty, struct pdp_t *pdp, bool v4only)
Harald Weltedda21ed2017-08-12 15:07:02 +0200828{
Pau Espin Pedrol310ea1d2019-08-20 14:15:25 +0200829 struct ippoolm_t *peer4, *peer6;
Vadim Yanitskiyd7030d22019-05-13 22:10:24 +0700830 char name_buf[256];
831 char *apn_name;
Vadim Yanitskiyfb625042019-05-19 02:00:31 +0700832 int rc;
833
Pau Espin Pedrol310ea1d2019-08-20 14:15:25 +0200834 peer4 = pdp_get_peer_ipv(pdp, false);
835 peer6 = pdp_get_peer_ipv(pdp, true);
836
837 if (v4only && peer6)
838 return;
839
Vadim Yanitskiyfb625042019-05-19 02:00:31 +0700840 /* Attempt to decode MSISDN */
841 rc = gsm48_decode_bcd_number2(name_buf, sizeof(name_buf),
842 pdp->msisdn.v, pdp->msisdn.l, 0);
Harald Weltedda21ed2017-08-12 15:07:02 +0200843
844 vty_out(vty, "IMSI: %s, NSAPI: %u, MSISDN: %s%s", imsi_gtp2str(&pdp->imsi), pdp->nsapi,
Vadim Yanitskiyfb625042019-05-19 02:00:31 +0700845 rc ? "(NONE)" : name_buf, VTY_NEWLINE);
Harald Weltedda21ed2017-08-12 15:07:02 +0200846
Pau Espin Pedrola0196312019-08-20 19:11:29 +0200847 vty_out(vty, " Version: %d", pdp->version);
848 if (pdp->version == 1) {
849 if (!pdp->secondary) {
850 vty_out(vty, ", Primary, Num Secondaries: %d%s%s",
851 pdp_count_secondary(pdp) - 1, /* primary included in count */
852 pdp->nodata ? ", No User Plane": "",
853 VTY_NEWLINE);
854 } else {
855 vty_out(vty, ", Secondary%s", VTY_NEWLINE);
856 }
857 } else {
858 vty_out(vty, "%s", VTY_NEWLINE);
859 }
860
Harald Weltedda21ed2017-08-12 15:07:02 +0200861 vty_out(vty, " Control: %s:%08x ", print_gsnaddr(&pdp->gsnlc), pdp->teic_own);
862 vty_out(vty, "<-> %s:%08x%s", print_gsnaddr(&pdp->gsnrc), pdp->teic_gn, VTY_NEWLINE);
863
864 vty_out(vty, " Data: %s:%08x ", print_gsnaddr(&pdp->gsnlu), pdp->teid_own);
865 vty_out(vty, "<-> %s:%08x%s", print_gsnaddr(&pdp->gsnru), pdp->teid_gn, VTY_NEWLINE);
866
Vadim Yanitskiyd7030d22019-05-13 22:10:24 +0700867 apn_name = osmo_apn_to_str(name_buf, pdp->apn_req.v, pdp->apn_req.l);
868 vty_out(vty, " APN requested: %s%s", apn_name ? name_buf : "(NONE)", VTY_NEWLINE);
869 apn_name = osmo_apn_to_str(name_buf, pdp->apn_use.v, pdp->apn_use.l);
870 vty_out(vty, " APN in use: %s%s", apn_name ? name_buf : "(NONE)", VTY_NEWLINE);
871
Pau Espin Pedrol310ea1d2019-08-20 14:15:25 +0200872 if (peer4)
Pau Espin Pedrol03cce862019-08-20 12:23:14 +0200873 vty_out(vty, " End-User Address (IPv4): %s%s",
Pau Espin Pedrol310ea1d2019-08-20 14:15:25 +0200874 in46a_ntop(&peer4->addr, name_buf, sizeof(name_buf)), VTY_NEWLINE);
875 if (peer6)
Pau Espin Pedrol03cce862019-08-20 12:23:14 +0200876 vty_out(vty, " End-User Address (IPv6): %s%s",
Pau Espin Pedrol310ea1d2019-08-20 14:15:25 +0200877 in46a_ntop(&peer6->addr, name_buf, sizeof(name_buf)), VTY_NEWLINE);
Harald Welte93fed3b2017-09-24 11:43:17 +0800878 vty_out(vty, " Transmit GTP Sequence Number for G-PDU: %s%s",
879 pdp->tx_gpdu_seq ? "Yes" : "No", VTY_NEWLINE);
Harald Weltedda21ed2017-08-12 15:07:02 +0200880}
881
Pau Espin Pedrol310ea1d2019-08-20 14:15:25 +0200882static void show_one_pdp(struct vty *vty, struct pdp_t *pdp)
883{
884 show_one_pdp_v4only(vty, pdp, false);
885}
886
Harald Weltedda21ed2017-08-12 15:07:02 +0200887DEFUN(show_pdpctx_imsi, show_pdpctx_imsi_cmd,
Pau Espin Pedrol7b52f002019-05-31 16:15:16 +0200888 "show pdp-context ggsn NAME imsi IMSI [<0-15>]",
Harald Weltedda21ed2017-08-12 15:07:02 +0200889 SHOW_STR "Display information on PDP Context\n"
Pau Espin Pedrol7b52f002019-05-31 16:15:16 +0200890 GGSN_STR "GGSN Name\n"
Harald Weltedda21ed2017-08-12 15:07:02 +0200891 "PDP contexts for given IMSI\n"
892 "PDP context for given NSAPI\n")
893{
Pau Espin Pedrol7b52f002019-05-31 16:15:16 +0200894 struct ggsn_ctx *ggsn;
895 uint64_t imsi;
Harald Weltedda21ed2017-08-12 15:07:02 +0200896 unsigned int nsapi;
897 struct pdp_t *pdp;
898 int num_found = 0;
899
Pau Espin Pedrol7b52f002019-05-31 16:15:16 +0200900 ggsn = ggsn_find(argv[0]);
901 if (!ggsn) {
902 vty_out(vty, "%% No such GGSN '%s'%s", argv[0], VTY_NEWLINE);
903 return CMD_WARNING;
904 }
905
Keithcbc07bd2020-10-10 12:17:26 +0200906 if (strlen(argv[1]) < 6 || strlen(argv[1]) > 15) {
907 vty_out(vty, "%% Invalid IMSI '%s'%s", argv[1], VTY_NEWLINE);
908 return CMD_WARNING;
909 }
910
Keithfb2a7292020-10-12 15:32:07 +0200911 imsi = gtp_imsi_str2gtp(argv[1]);
Pau Espin Pedrol7b52f002019-05-31 16:15:16 +0200912
913 if (argc > 2) {
914 nsapi = atoi(argv[2]);
Keith080dcfa2020-10-10 15:15:31 +0200915 if (!gtp_pdp_getimsi(ggsn->gsn, &pdp, imsi, nsapi)) {
Harald Weltedda21ed2017-08-12 15:07:02 +0200916 show_one_pdp(vty, pdp);
917 num_found++;
918 }
919 } else {
920 for (nsapi = 0; nsapi < PDP_MAXNSAPI; nsapi++) {
Pau Espin Pedrol7b52f002019-05-31 16:15:16 +0200921 if (gtp_pdp_getimsi(ggsn->gsn, &pdp, imsi, nsapi))
Harald Weltedda21ed2017-08-12 15:07:02 +0200922 continue;
923 show_one_pdp(vty, pdp);
924 num_found++;
925 }
926 }
927 if (num_found == 0) {
928 vty_out(vty, "%% No such PDP context found%s", VTY_NEWLINE);
929 return CMD_WARNING;
930 } else
931 return CMD_SUCCESS;
932}
933
Vadim Yanitskiyca276e02019-05-13 13:06:51 +0700934DEFUN(show_pdpctx_ip, show_pdpctx_ip_cmd,
935 "show pdp-context ggsn NAME ipv4 A.B.C.D",
936 SHOW_STR "Display information on PDP Context\n"
937 GGSN_STR "GGSN Name\n" "IPv4 address type\n" "IP address\n")
938{
939 struct ggsn_ctx *ggsn;
940 struct apn_ctx *apn;
941 unsigned int i;
942
943 ggsn = ggsn_find(argv[0]);
944 if (!ggsn) {
945 vty_out(vty, "%% No such GGSN '%s'%s", argv[0], VTY_NEWLINE);
946 return CMD_WARNING;
947 }
948
949 /* Iterate over all APNs of a given GGSN */
950 llist_for_each_entry(apn, &ggsn->apn_list, list) {
951 struct ippool_t *pool = apn->v4.pool;
952
953 /* In some rare cases, if GGSN fails to init TUN/TAP interfaces
954 * (e.g. due to insufficient permissions), it will continue to
955 * work in such broken state, and pool would be NULL. */
956 if (!pool)
957 continue;
958
959 /* Iterate over all IPv4 pool members */
960 for (i = 0; i < pool->listsize; i++) {
961 struct ippoolm_t *member = &pool->member[i];
962 if (member->inuse == 0)
963 continue;
964 if (strcmp(argv[1], in46a_ntoa(&member->addr)) == 0) {
965 show_one_pdp(vty, member->peer);
966 return CMD_SUCCESS;
967 }
968 }
969 }
970
971 vty_out(vty, "%% No PDP context found for IP '%s'%s", argv[1], VTY_NEWLINE);
972 return CMD_WARNING;
973}
974
Harald Weltedda21ed2017-08-12 15:07:02 +0200975/* show all (active) PDP contexts within a pool */
Pau Espin Pedrol310ea1d2019-08-20 14:15:25 +0200976static void ippool_show_pdp_contexts(struct vty *vty, struct ippool_t *pool, bool pdp_v4only)
Harald Weltedda21ed2017-08-12 15:07:02 +0200977{
978 unsigned int i;
979
980 if (!pool)
981 return;
982
983 for (i = 0; i < pool->listsize; i++) {
984 struct ippoolm_t *member = &pool->member[i];
985 if (member->inuse == 0)
986 continue;
Pau Espin Pedrol310ea1d2019-08-20 14:15:25 +0200987 show_one_pdp_v4only(vty, member->peer, pdp_v4only);
Harald Weltedda21ed2017-08-12 15:07:02 +0200988 }
989}
990
991/* show all (active) PDP contexts within an APN */
992static void apn_show_pdp_contexts(struct vty *vty, struct apn_ctx *apn)
993{
Pau Espin Pedrol310ea1d2019-08-20 14:15:25 +0200994 ippool_show_pdp_contexts(vty, apn->v4.pool, true);
995 ippool_show_pdp_contexts(vty, apn->v6.pool, false);
Harald Weltedda21ed2017-08-12 15:07:02 +0200996}
997
998DEFUN(show_pdpctx, show_pdpctx_cmd,
Vadim Yanitskiy977b3392019-05-13 15:32:21 +0700999 "show pdp-context ggsn NAME",
Harald Weltedda21ed2017-08-12 15:07:02 +02001000 SHOW_STR "Show PDP Context Information\n"
Vadim Yanitskiy977b3392019-05-13 15:32:21 +07001001 GGSN_STR "GGSN Name\n")
Harald Weltedda21ed2017-08-12 15:07:02 +02001002{
1003 struct ggsn_ctx *ggsn;
1004 struct apn_ctx *apn;
1005
1006 ggsn = ggsn_find(argv[0]);
1007 if (!ggsn) {
1008 vty_out(vty, "%% No such GGSN '%s'%s", argv[0], VTY_NEWLINE);
1009 return CMD_WARNING;
1010 }
Vadim Yanitskiy977b3392019-05-13 15:32:21 +07001011
1012 llist_for_each_entry(apn, &ggsn->apn_list, list)
Harald Weltedda21ed2017-08-12 15:07:02 +02001013 apn_show_pdp_contexts(vty, apn);
Harald Weltedda21ed2017-08-12 15:07:02 +02001014
1015 return CMD_SUCCESS;
1016}
1017
Vadim Yanitskiy977b3392019-05-13 15:32:21 +07001018DEFUN(show_pdpctx_apn, show_pdpctx_apn_cmd,
1019 "show pdp-context ggsn NAME apn APN",
1020 SHOW_STR "Show PDP Context Information\n"
1021 GGSN_STR "GGSN Name\n" "Filter by APN\n" "APN name\n")
1022{
1023 struct ggsn_ctx *ggsn;
1024 struct apn_ctx *apn;
1025
1026 ggsn = ggsn_find(argv[0]);
1027 if (!ggsn) {
1028 vty_out(vty, "%% No such GGSN '%s'%s", argv[0], VTY_NEWLINE);
1029 return CMD_WARNING;
1030 }
1031
1032 apn = ggsn_find_apn(ggsn, argv[1]);
1033 if (!apn) {
1034 vty_out(vty, "%% No such APN '%s'%s", argv[1], VTY_NEWLINE);
1035 return CMD_WARNING;
1036 }
1037
1038 apn_show_pdp_contexts(vty, apn);
1039 return CMD_SUCCESS;
1040}
1041
1042/* Backwards compatibility: the VTY parser is (mis)interpreting
1043 * "[apn APN]" as two separate elements: "[apn" and "APN]",
1044 * but the first part somehow turns into command "ap". */
1045ALIAS_DEPRECATED(show_pdpctx_apn, show_deprecated_pdpctx_apn_cmd,
1046 "show pdp-context ggsn NAME ap APN",
1047 SHOW_STR "Show PDP Context Information\n"
1048 GGSN_STR "GGSN Name\n" "Filter by APN\n" "APN name\n");
1049
Harald Weltedda21ed2017-08-12 15:07:02 +02001050static void show_apn(struct vty *vty, struct apn_ctx *apn)
1051{
1052 vty_out(vty, " APN: %s%s", apn->cfg.name, VTY_NEWLINE);
1053 /* FIXME */
1054}
1055
1056static void show_one_ggsn(struct vty *vty, struct ggsn_ctx *ggsn)
1057{
1058 struct apn_ctx *apn;
Pau Espin Pedrolf5fbb412019-08-21 18:49:44 +02001059 struct sgsn_peer *sgsn;
Harald Weltedda21ed2017-08-12 15:07:02 +02001060 vty_out(vty, "GGSN %s: Bound to %s%s", ggsn->cfg.name, in46a_ntoa(&ggsn->cfg.listen_addr),
1061 VTY_NEWLINE);
1062 /* FIXME */
1063
1064 llist_for_each_entry(apn, &ggsn->apn_list, list)
1065 show_apn(vty, apn);
Pau Espin Pedrolf5fbb412019-08-21 18:49:44 +02001066 llist_for_each_entry(sgsn, &ggsn->sgsn_list, entry)
1067 show_one_sgsn(vty, sgsn, " ");
Harald Weltedda21ed2017-08-12 15:07:02 +02001068}
1069
1070DEFUN(show_ggsn, show_ggsn_cmd,
1071 "show ggsn [NAME]",
1072 SHOW_STR "Display information on the GGSN\n")
1073{
1074 struct ggsn_ctx *ggsn;
1075
1076 if (argc == 0) {
1077 llist_for_each_entry(ggsn, &g_ggsn_list, list)
1078 show_one_ggsn(vty, ggsn);
1079 } else {
1080 ggsn = ggsn_find(argv[0]);
1081 if (!ggsn)
1082 return CMD_WARNING;
1083 show_one_ggsn(vty, ggsn);
1084 }
1085
1086 return CMD_SUCCESS;
1087}
1088
1089int ggsn_vty_init(void)
1090{
1091 install_element_ve(&show_pdpctx_cmd);
Vadim Yanitskiy977b3392019-05-13 15:32:21 +07001092 install_element_ve(&show_pdpctx_apn_cmd);
1093 install_element_ve(&show_deprecated_pdpctx_apn_cmd);
Harald Weltedda21ed2017-08-12 15:07:02 +02001094 install_element_ve(&show_pdpctx_imsi_cmd);
Vadim Yanitskiyca276e02019-05-13 13:06:51 +07001095 install_element_ve(&show_pdpctx_ip_cmd);
Harald Weltedda21ed2017-08-12 15:07:02 +02001096 install_element_ve(&show_ggsn_cmd);
1097
1098 install_element(CONFIG_NODE, &cfg_ggsn_cmd);
1099 install_element(CONFIG_NODE, &cfg_no_ggsn_cmd);
Pau Espin Pedrol840ce8a2017-11-16 17:01:44 +01001100
Harald Weltedda21ed2017-08-12 15:07:02 +02001101 install_node(&ggsn_node, config_write_ggsn);
Harald Weltedda21ed2017-08-12 15:07:02 +02001102 install_element(GGSN_NODE, &cfg_description_cmd);
1103 install_element(GGSN_NODE, &cfg_no_description_cmd);
1104 install_element(GGSN_NODE, &cfg_ggsn_shutdown_cmd);
1105 install_element(GGSN_NODE, &cfg_ggsn_no_shutdown_cmd);
Harald Welte98146772017-09-05 17:41:20 +02001106 install_element(GGSN_NODE, &cfg_ggsn_bind_ip_cmd);
1107 install_element(GGSN_NODE, &cfg_ggsn_gtpc_ip_cmd);
1108 install_element(GGSN_NODE, &cfg_ggsn_gtpu_ip_cmd);
Harald Weltedda21ed2017-08-12 15:07:02 +02001109 install_element(GGSN_NODE, &cfg_ggsn_state_dir_cmd);
1110 install_element(GGSN_NODE, &cfg_ggsn_apn_cmd);
1111 install_element(GGSN_NODE, &cfg_ggsn_no_apn_cmd);
1112 install_element(GGSN_NODE, &cfg_ggsn_default_apn_cmd);
1113 install_element(GGSN_NODE, &cfg_ggsn_no_default_apn_cmd);
Pau Espin Pedrolf5fbb412019-08-21 18:49:44 +02001114 install_element(GGSN_NODE, &cfg_ggsn_show_sgsn_cmd);
1115 install_element(GGSN_NODE, &cfg_ggsn_echo_interval_cmd);
1116 install_element(GGSN_NODE, &cfg_ggsn_no_echo_interval_cmd);
Harald Weltedda21ed2017-08-12 15:07:02 +02001117
Pau Espin Pedrol9f1f7472022-11-02 19:52:19 +01001118 osmo_tdef_vty_groups_init(GGSN_NODE, ggsn_tdef_group);
1119
Harald Weltedda21ed2017-08-12 15:07:02 +02001120 install_node(&apn_node, NULL);
Harald Weltedda21ed2017-08-12 15:07:02 +02001121 install_element(APN_NODE, &cfg_description_cmd);
1122 install_element(APN_NODE, &cfg_no_description_cmd);
1123 install_element(APN_NODE, &cfg_apn_shutdown_cmd);
1124 install_element(APN_NODE, &cfg_apn_no_shutdown_cmd);
1125 install_element(APN_NODE, &cfg_apn_gtpu_mode_cmd);
1126 install_element(APN_NODE, &cfg_apn_type_support_cmd);
1127 install_element(APN_NODE, &cfg_apn_no_type_support_cmd);
1128 install_element(APN_NODE, &cfg_apn_tun_dev_name_cmd);
1129 install_element(APN_NODE, &cfg_apn_ipup_script_cmd);
1130 install_element(APN_NODE, &cfg_apn_no_ipup_script_cmd);
1131 install_element(APN_NODE, &cfg_apn_ipdown_script_cmd);
1132 install_element(APN_NODE, &cfg_apn_no_ipdown_script_cmd);
1133 install_element(APN_NODE, &cfg_apn_ip_prefix_cmd);
1134 install_element(APN_NODE, &cfg_apn_ipv6_prefix_cmd);
1135 install_element(APN_NODE, &cfg_apn_ip_dns_cmd);
1136 install_element(APN_NODE, &cfg_apn_ipv6_dns_cmd);
1137 install_element(APN_NODE, &cfg_apn_no_dns_cmd);
1138 install_element(APN_NODE, &cfg_apn_ip_ifconfig_cmd);
1139 install_element(APN_NODE, &cfg_apn_no_ip_ifconfig_cmd);
1140 install_element(APN_NODE, &cfg_apn_ipv6_ifconfig_cmd);
1141 install_element(APN_NODE, &cfg_apn_no_ipv6_ifconfig_cmd);
Pau Espin Pedrol37c45e32017-12-14 14:09:13 +01001142 install_element(APN_NODE, &cfg_apn_ipv6_linklocal_cmd);
1143 install_element(APN_NODE, &cfg_apn_no_ipv6_linklocal_cmd);
Harald Welte93fed3b2017-09-24 11:43:17 +08001144 install_element(APN_NODE, &cfg_apn_gpdu_seq_cmd);
1145 install_element(APN_NODE, &cfg_apn_no_gpdu_seq_cmd);
Harald Weltedda21ed2017-08-12 15:07:02 +02001146
1147 return 0;
1148}
1149
1150static int ggsn_vty_is_config_node(struct vty *vty, int node)
1151{
1152 switch (node) {
1153 case GGSN_NODE:
1154 case APN_NODE:
1155 return 1;
1156 default:
1157 return 0;
1158 }
1159}
1160
1161static int ggsn_vty_go_parent(struct vty *vty)
1162{
1163 switch (vty->node) {
1164 case GGSN_NODE:
1165 vty->node = CONFIG_NODE;
1166 vty->index = NULL;
1167 vty->index_sub = NULL;
1168 break;
1169 case APN_NODE:
1170 vty->node = GGSN_NODE;
1171 {
1172 struct apn_ctx *apn = vty->index;
1173 vty->index = apn->ggsn;
1174 vty->index_sub = &apn->ggsn->cfg.description;
1175 }
1176 break;
Vadim Yanitskiy906c2092018-05-09 23:11:27 +07001177 default:
1178 vty->node = CONFIG_NODE;
1179 vty->index = NULL;
1180 vty->index_sub = NULL;
Harald Weltedda21ed2017-08-12 15:07:02 +02001181 }
1182
1183 return vty->node;
1184}
1185
1186static const char ggsn_copyright[] =
1187 "Copyright (C) 2011-2017 Harald Welte <laforge@gnumonks.org>\r\n"
1188 "Copyright (C) 2012-2017 Holger Hans Peter Freyther <holger@moiji-mobile.com>\r\n"
1189 "Copyright (C) 2012-2017 sysmocom - s.f.m.c. GmbH\r\n"
1190 "Copyright (C) 2002-2005 Mondru AB\r\n"
1191 "License GPLv2: GNU GPL version 2 <http://gnu.org/licenses/gpl-2.0.html>\r\n"
1192 "This is free software: you are free to change and redistribute it.\r\n"
1193 "There is NO WARRANTY, to the extent permitted by law.\r\n";
1194
1195struct vty_app_info g_vty_info = {
Harald Welte632e8432017-09-05 18:12:14 +02001196 .name = "OsmoGGSN",
Harald Weltedda21ed2017-08-12 15:07:02 +02001197 .version = PACKAGE_VERSION,
1198 .copyright = ggsn_copyright,
1199 .go_parent_cb = ggsn_vty_go_parent,
1200 .is_config_node = ggsn_vty_is_config_node,
1201};