blob: 9591bf7d6c9a515b10efebc5c6df94ae34020821 [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;
Pau Espin Pedrolf5fbb412019-08-21 18:49:44 +0200392 struct sgsn_peer *sgsn;
393
Pau Espin Pedrol134ac7e2022-02-25 17:24:07 +0100394 if (ggsn->cfg.echo_interval == 0)
Pau Espin Pedrolf5fbb412019-08-21 18:49:44 +0200395 return CMD_SUCCESS;
396
397 ggsn->cfg.echo_interval = 0;
398
399 /* Disable echo timer for all sgsn */
400 llist_for_each_entry(sgsn, &ggsn->sgsn_list, entry)
401 sgsn_echo_timer_stop(sgsn);
402
403 return CMD_SUCCESS;
404}
405
Harald Weltedda21ed2017-08-12 15:07:02 +0200406/* APN Node */
407
408static struct cmd_node apn_node = {
409 APN_NODE,
410 "%s(config-ggsn-apn)# ",
411 1,
412};
413
414static const struct value_string pdp_type_names[] = {
415 { APN_TYPE_IPv4, "v4" },
416 { APN_TYPE_IPv6, "v6" },
417 { APN_TYPE_IPv4v6, "v4v6" },
418 { 0, NULL }
419};
420
421static const struct value_string apn_gtpu_mode_names[] = {
422 { APN_GTPU_MODE_TUN, "tun" },
423 { APN_GTPU_MODE_KERNEL_GTP, "kernel-gtp" },
424 { 0, NULL }
425};
426
427
428#define V4V6V46_STRING "IPv4(-only) PDP Type\n" \
429 "IPv6(-only) PDP Type\n" \
430 "IPv4v6 (dual-stack) PDP Type\n"
431
432DEFUN(cfg_apn_type_support, cfg_apn_type_support_cmd,
433 "type-support (v4|v6|v4v6)",
434 "Enable support for PDP Type\n"
435 V4V6V46_STRING)
436{
437 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
438 uint32_t type = get_string_value(pdp_type_names, argv[0]);
439
440 apn->cfg.apn_type_mask |= type;
441 return CMD_SUCCESS;
442}
443
444DEFUN(cfg_apn_no_type_support, cfg_apn_no_type_support_cmd,
445 "no type-support (v4|v6|v4v6)",
446 NO_STR "Disable support for PDP Type\n"
447 V4V6V46_STRING)
448{
449 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
450 uint32_t type = get_string_value(pdp_type_names, argv[0]);
451
452 apn->cfg.apn_type_mask &= ~type;
453 return CMD_SUCCESS;
454}
455
456DEFUN(cfg_apn_gtpu_mode, cfg_apn_gtpu_mode_cmd,
457 "gtpu-mode (tun|kernel-gtp)",
458 "Set the Mode for this APN (tun or Linux Kernel GTP)\n"
459 "GTP-U in userspace using TUN device\n"
460 "GTP-U in kernel using Linux Kernel GTP\n")
461{
462 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
463
464 apn->cfg.gtpu_mode = get_string_value(apn_gtpu_mode_names, argv[0]);
465 return CMD_SUCCESS;
466}
467
468DEFUN(cfg_apn_tun_dev_name, cfg_apn_tun_dev_name_cmd,
469 "tun-device NAME",
470 "Configure tun device name\n"
471 "TUN device name")
472{
473 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
474 osmo_talloc_replace_string(apn, &apn->tun.cfg.dev_name, argv[0]);
475 return CMD_SUCCESS;
476}
477
478DEFUN(cfg_apn_ipup_script, cfg_apn_ipup_script_cmd,
479 "ipup-script PATH",
480 "Configure name/path of ip-up script\n"
481 "File/Path name of ip-up script\n")
482{
483 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
484 osmo_talloc_replace_string(apn, &apn->tun.cfg.ipup_script, argv[0]);
485 return CMD_SUCCESS;
486}
487
488DEFUN(cfg_apn_no_ipup_script, cfg_apn_no_ipup_script_cmd,
489 "no ipup-script",
490 NO_STR "Disable ip-up script\n")
491{
492 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
493 talloc_free(apn->tun.cfg.ipup_script);
494 apn->tun.cfg.ipup_script = NULL;
495 return CMD_SUCCESS;
496}
497
498DEFUN(cfg_apn_ipdown_script, cfg_apn_ipdown_script_cmd,
499 "ipdown-script PATH",
500 "Configure name/path of ip-down script\n"
501 "File/Path name of ip-down script\n")
502{
503 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
504 osmo_talloc_replace_string(apn, &apn->tun.cfg.ipdown_script, argv[0]);
505 return CMD_SUCCESS;
506}
507
508/* convert prefix from "A.B.C.D/M" notation to in46_prefix */
509static void str2prefix(struct in46_prefix *pfx, const char *in)
510{
511 size_t t;
512
513 ippool_aton(&pfx->addr, &t, in, 0);
514 pfx->prefixlen = t;
515}
516
517DEFUN(cfg_apn_no_ipdown_script, cfg_apn_no_ipdown_script_cmd,
518 "no ipdown-script",
519 NO_STR "Disable ip-down script\n")
520{
521 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
522 talloc_free(apn->tun.cfg.ipdown_script);
523 apn->tun.cfg.ipdown_script = NULL;
524 return CMD_SUCCESS;
525}
526
527DEFUN(cfg_apn_ip_prefix, cfg_apn_ip_prefix_cmd,
528 "ip prefix (static|dynamic) A.B.C.D/M",
529 IP_STR PREFIX_STR "IPv4 Adress/Prefix-Length\n")
530{
531 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
532 struct in46_prefix *pfx;
533
534 /* first update our parsed prefix */
Harald Welte53792732021-03-27 19:03:30 +0100535 if (!strcmp(argv[0], "static")) {
Harald Weltedda21ed2017-08-12 15:07:02 +0200536 pfx = &apn->v4.cfg.static_prefix;
Harald Welte53792732021-03-27 19:03:30 +0100537 vty_out(vty, "%% static IP addresses currently not yet supported%s", VTY_NEWLINE);
538 return CMD_WARNING;
539 } else
Harald Weltedda21ed2017-08-12 15:07:02 +0200540 pfx = &apn->v4.cfg.dynamic_prefix;
541 str2prefix(pfx, argv[1]);
542
543 return CMD_SUCCESS;
544}
545
546DEFUN(cfg_apn_ip_ifconfig, cfg_apn_ip_ifconfig_cmd,
547 "ip ifconfig A.B.C.D/M",
548 IP_STR IFCONFIG_STR "IPv4 Adress/Prefix-Length\n")
549{
550 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
551 str2prefix(&apn->v4.cfg.ifconfig_prefix, argv[0]);
552 return CMD_SUCCESS;
553}
554
555DEFUN(cfg_apn_no_ip_ifconfig, cfg_apn_no_ip_ifconfig_cmd,
556 "no ip ifconfig",
557 NO_STR IP_STR IFCONFIG_STR)
558{
559 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
560 memset(&apn->v4.cfg.ifconfig_prefix, 0, sizeof(apn->v4.cfg.ifconfig_prefix));
561 return CMD_SUCCESS;
562}
563
564DEFUN(cfg_apn_ipv6_prefix, cfg_apn_ipv6_prefix_cmd,
565 "ipv6 prefix (static|dynamic) X:X::X:X/M",
566 IP6_STR PREFIX_STR "IPv6 Address/Prefix-Length\n")
567{
568 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
569 struct in46_prefix *pfx;
570
Harald Welte53792732021-03-27 19:03:30 +0100571 if (!strcmp(argv[0], "static")) {
Harald Weltedda21ed2017-08-12 15:07:02 +0200572 pfx = &apn->v6.cfg.static_prefix;
Harald Welte53792732021-03-27 19:03:30 +0100573 vty_out(vty, "%% static IP addresses currently not yet supported%s", VTY_NEWLINE);
574 return CMD_WARNING;
575 } else
Harald Weltedda21ed2017-08-12 15:07:02 +0200576 pfx = &apn->v6.cfg.dynamic_prefix;
577 str2prefix(pfx, argv[1]);
578 return CMD_SUCCESS;
579}
580
581DEFUN(cfg_apn_ipv6_ifconfig, cfg_apn_ipv6_ifconfig_cmd,
582 "ipv6 ifconfig X:X::X:X/M",
583 IP6_STR IFCONFIG_STR "IPv6 Adress/Prefix-Length\n")
584{
585 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
586 str2prefix(&apn->v6.cfg.ifconfig_prefix, argv[0]);
587 return CMD_SUCCESS;
588}
589
590DEFUN(cfg_apn_no_ipv6_ifconfig, cfg_apn_no_ipv6_ifconfig_cmd,
591 "no ipv6 ifconfig",
592 NO_STR IP6_STR IFCONFIG_STR)
593{
594 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
595 memset(&apn->v6.cfg.ifconfig_prefix, 0, sizeof(apn->v6.cfg.ifconfig_prefix));
596 return CMD_SUCCESS;
597}
598
Pau Espin Pedrol37c45e32017-12-14 14:09:13 +0100599DEFUN(cfg_apn_ipv6_linklocal, cfg_apn_ipv6_linklocal_cmd,
600 "ipv6 link-local X:X::X:X/M",
601 IP6_STR IFCONFIG_STR "IPv6 Link-local Adress/Prefix-Length\n")
602{
603 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
604 str2prefix(&apn->v6.cfg.ll_prefix, argv[0]);
605 return CMD_SUCCESS;
606}
607
608DEFUN(cfg_apn_no_ipv6_linklocal, cfg_apn_no_ipv6_linklocal_cmd,
609 "no ipv6 link-local",
610 NO_STR IP6_STR IFCONFIG_STR)
611{
612 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
613 memset(&apn->v6.cfg.ll_prefix, 0, sizeof(apn->v6.cfg.ll_prefix));
614 return CMD_SUCCESS;
615}
616
Harald Weltedda21ed2017-08-12 15:07:02 +0200617#define DNS_STRINGS "Configure DNS Server\n" "primary/secondary DNS\n" "IP address of DNS Sever\n"
618
619DEFUN(cfg_apn_ip_dns, cfg_apn_ip_dns_cmd,
620 "ip dns <0-1> A.B.C.D",
621 IP_STR DNS_STRINGS)
622{
623 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
624 int idx = atoi(argv[0]);
625 size_t dummy;
626
627 ippool_aton(&apn->v4.cfg.dns[idx], &dummy, argv[1], 0);
628
629 return CMD_SUCCESS;
630}
631
632DEFUN(cfg_apn_ipv6_dns, cfg_apn_ipv6_dns_cmd,
633 "ipv6 dns <0-1> X:X::X:X",
634 IP6_STR DNS_STRINGS)
635{
636 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
637 int idx = atoi(argv[0]);
638 size_t dummy;
639
640 ippool_aton(&apn->v6.cfg.dns[idx], &dummy, argv[1], 0);
641
642 return CMD_SUCCESS;
643}
644
645DEFUN(cfg_apn_no_dns, cfg_apn_no_dns_cmd,
646 "no (ip|ipv6) dns <0-1>",
647 NO_STR IP_STR IP6_STR "Disable DNS Server\n" "primary/secondary DNS\n")
648{
649 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
650 struct in46_addr *a;
651 int idx = atoi(argv[1]);
652
653 if (!strcmp(argv[0], "ip"))
654 a = &apn->v4.cfg.dns[idx];
655 else
656 a = &apn->v6.cfg.dns[idx];
657
658 memset(a, 0, sizeof(*a));
659
660 return CMD_SUCCESS;
661}
662
Harald Welte93fed3b2017-09-24 11:43:17 +0800663DEFUN(cfg_apn_gpdu_seq, cfg_apn_gpdu_seq_cmd,
664 "g-pdu tx-sequence-numbers",
665 "G-PDU Configuration\n" "Enable transmission of G-PDU sequence numbers\n")
666{
667 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
668 apn->cfg.tx_gpdu_seq = true;
669 return CMD_SUCCESS;
670}
671
672DEFUN(cfg_apn_no_gpdu_seq, cfg_apn_no_gpdu_seq_cmd,
673 "no g-pdu tx-sequence-numbers",
674 NO_STR "G-PDU Configuration\n" "Disable transmission of G-PDU sequence numbers\n")
675{
676 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
677 apn->cfg.tx_gpdu_seq = false;
678 return CMD_SUCCESS;
679}
680
Harald Weltedda21ed2017-08-12 15:07:02 +0200681DEFUN(cfg_apn_shutdown, cfg_apn_shutdown_cmd,
682 "shutdown",
683 "Put the APN in administrative shut-down\n")
684{
685 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
686
687 if (!apn->cfg.shutdown) {
Pau Espin Pedrol72ab4bc2019-05-29 19:08:26 +0200688 if (apn_stop(apn)) {
Harald Weltedda21ed2017-08-12 15:07:02 +0200689 vty_out(vty, "%% Failed to Stop APN%s", VTY_NEWLINE);
690 return CMD_WARNING;
691 }
692 apn->cfg.shutdown = true;
693 }
694
695 return CMD_SUCCESS;
696}
697
698DEFUN(cfg_apn_no_shutdown, cfg_apn_no_shutdown_cmd,
699 "no shutdown",
700 NO_STR "Remove the APN from administrative shut-down\n")
701{
702 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
703
704 if (apn->cfg.shutdown) {
Oliver Smith02a82c32021-02-05 14:15:20 +0100705 if (!apn->tun.cfg.dev_name) {
706 vty_out(vty, "%% Failed to start APN, tun-device is not configured%s", VTY_NEWLINE);
707 return CMD_WARNING;
708 }
Harald Weltedda21ed2017-08-12 15:07:02 +0200709 if (apn_start(apn) < 0) {
710 vty_out(vty, "%% Failed to start APN, check log for details%s", VTY_NEWLINE);
711 return CMD_WARNING;
712 }
713 apn->cfg.shutdown = false;
714 }
715
716 return CMD_SUCCESS;
717}
718
719
720static void vty_dump_prefix(struct vty *vty, const char *pre, const struct in46_prefix *pfx)
721{
722 vty_out(vty, "%s %s%s", pre, in46p_ntoa(pfx), VTY_NEWLINE);
723}
724
725static void config_write_apn(struct vty *vty, struct apn_ctx *apn)
726{
727 unsigned int i;
728
729 vty_out(vty, " apn %s%s", apn->cfg.name, VTY_NEWLINE);
730 if (apn->cfg.description)
731 vty_out(vty, " description %s%s", apn->cfg.description, VTY_NEWLINE);
732 vty_out(vty, " gtpu-mode %s%s", get_value_string(apn_gtpu_mode_names, apn->cfg.gtpu_mode),
733 VTY_NEWLINE);
734 if (apn->tun.cfg.dev_name)
735 vty_out(vty, " tun-device %s%s", apn->tun.cfg.dev_name, VTY_NEWLINE);
736 if (apn->tun.cfg.ipup_script)
737 vty_out(vty, " ipup-script %s%s", apn->tun.cfg.ipup_script, VTY_NEWLINE);
738 if (apn->tun.cfg.ipdown_script)
739 vty_out(vty, " ipdown-script %s%s", apn->tun.cfg.ipdown_script, VTY_NEWLINE);
740
741 for (i = 0; i < 32; i++) {
Pau Espin Pedrol55600012019-05-30 17:29:09 +0200742 if (!(apn->cfg.apn_type_mask & (UINT32_C(1) << i)))
Harald Weltedda21ed2017-08-12 15:07:02 +0200743 continue;
Pau Espin Pedrol55600012019-05-30 17:29:09 +0200744 vty_out(vty, " type-support %s%s", get_value_string(pdp_type_names, (UINT32_C(1) << i)),
Harald Weltedda21ed2017-08-12 15:07:02 +0200745 VTY_NEWLINE);
746 }
747
Harald Welte93fed3b2017-09-24 11:43:17 +0800748 if (!apn->cfg.tx_gpdu_seq)
749 vty_out(vty, " no g-pdu tx-sequence-numbers%s", VTY_NEWLINE);
750
Harald Weltedda21ed2017-08-12 15:07:02 +0200751 /* IPv4 prefixes + DNS */
752 if (apn->v4.cfg.static_prefix.addr.len)
753 vty_dump_prefix(vty, " ip prefix static", &apn->v4.cfg.static_prefix);
754 if (apn->v4.cfg.dynamic_prefix.addr.len)
755 vty_dump_prefix(vty, " ip prefix dynamic", &apn->v4.cfg.dynamic_prefix);
756 for (i = 0; i < ARRAY_SIZE(apn->v4.cfg.dns); i++) {
757 if (!apn->v4.cfg.dns[i].len)
758 continue;
759 vty_out(vty, " ip dns %u %s%s", i, in46a_ntoa(&apn->v4.cfg.dns[i]), VTY_NEWLINE);
760 }
761 if (apn->v4.cfg.ifconfig_prefix.addr.len)
Harald Welteff438172017-09-24 22:48:46 +0800762 vty_dump_prefix(vty, " ip ifconfig", &apn->v4.cfg.ifconfig_prefix);
Harald Weltedda21ed2017-08-12 15:07:02 +0200763
764 /* IPv6 prefixes + DNS */
765 if (apn->v6.cfg.static_prefix.addr.len)
766 vty_dump_prefix(vty, " ipv6 prefix static", &apn->v6.cfg.static_prefix);
767 if (apn->v6.cfg.dynamic_prefix.addr.len)
768 vty_dump_prefix(vty, " ipv6 prefix dynamic", &apn->v6.cfg.dynamic_prefix);
769 for (i = 0; i < ARRAY_SIZE(apn->v6.cfg.dns); i++) {
770 if (!apn->v6.cfg.dns[i].len)
771 continue;
Harald Welte3ca419a2017-09-24 22:47:51 +0800772 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 +0200773 }
774 if (apn->v6.cfg.ifconfig_prefix.addr.len)
Harald Welteff438172017-09-24 22:48:46 +0800775 vty_dump_prefix(vty, " ipv6 ifconfig", &apn->v6.cfg.ifconfig_prefix);
Pau Espin Pedrole5a082d2017-12-15 15:55:04 +0100776 if (apn->v6.cfg.ll_prefix.addr.len)
777 vty_dump_prefix(vty, " ipv6 link-local", &apn->v6.cfg.ll_prefix);
Harald Weltedda21ed2017-08-12 15:07:02 +0200778
779 /* must be last */
780 vty_out(vty, " %sshutdown%s", apn->cfg.shutdown ? "" : "no ", VTY_NEWLINE);
781}
782
783static int config_write_ggsn(struct vty *vty)
784{
785 struct ggsn_ctx *ggsn;
786
787 llist_for_each_entry(ggsn, &g_ggsn_list, list) {
788 struct apn_ctx *apn;
789 vty_out(vty, "ggsn %s%s", ggsn->cfg.name, VTY_NEWLINE);
790 if (ggsn->cfg.description)
791 vty_out(vty, " description %s%s", ggsn->cfg.description, VTY_NEWLINE);
792 vty_out(vty, " gtp state-dir %s%s", ggsn->cfg.state_dir, VTY_NEWLINE);
Harald Welte98146772017-09-05 17:41:20 +0200793 vty_out(vty, " gtp bind-ip %s%s", in46a_ntoa(&ggsn->cfg.listen_addr), VTY_NEWLINE);
794 if (ggsn->cfg.gtpc_addr.v4.s_addr)
795 vty_out(vty, " gtp control-ip %s%s", in46a_ntoa(&ggsn->cfg.gtpc_addr), VTY_NEWLINE);
796 if (ggsn->cfg.gtpu_addr.v4.s_addr)
797 vty_out(vty, " gtp user-ip %s%s", in46a_ntoa(&ggsn->cfg.gtpu_addr), VTY_NEWLINE);
Harald Weltedda21ed2017-08-12 15:07:02 +0200798 llist_for_each_entry(apn, &ggsn->apn_list, list)
799 config_write_apn(vty, apn);
800 if (ggsn->cfg.default_apn)
801 vty_out(vty, " default-apn %s%s", ggsn->cfg.default_apn->cfg.name, VTY_NEWLINE);
Pau Espin Pedrolf5fbb412019-08-21 18:49:44 +0200802 if (ggsn->cfg.echo_interval)
803 vty_out(vty, " echo-interval %u%s", ggsn->cfg.echo_interval, VTY_NEWLINE);
Harald Weltedda21ed2017-08-12 15:07:02 +0200804 /* must be last */
805 vty_out(vty, " %sshutdown ggsn%s", ggsn->cfg.shutdown ? "" : "no ", VTY_NEWLINE);
806 }
807
808 return 0;
809}
810
811static const char *print_gsnaddr(const struct ul16_t *in)
812{
813 struct in46_addr in46;
814
815 in46.len = in->l;
816 OSMO_ASSERT(in->l <= sizeof(in46.v6));
817 memcpy(&in46.v6, in->v, in->l);
818
819 return in46a_ntoa(&in46);
820}
821
Pau Espin Pedrol310ea1d2019-08-20 14:15:25 +0200822/* Useful for v4v6 APNs, where we first iterate over v4 pool and then over v6
823 pool. param v4only can be used to avoid printing duplicates for pdp context
824 containing both IPv4 and IPv6 addresses. */
825static void show_one_pdp_v4only(struct vty *vty, struct pdp_t *pdp, bool v4only)
Harald Weltedda21ed2017-08-12 15:07:02 +0200826{
Pau Espin Pedrol310ea1d2019-08-20 14:15:25 +0200827 struct ippoolm_t *peer4, *peer6;
Vadim Yanitskiyd7030d22019-05-13 22:10:24 +0700828 char name_buf[256];
829 char *apn_name;
Vadim Yanitskiyfb625042019-05-19 02:00:31 +0700830 int rc;
831
Pau Espin Pedrol310ea1d2019-08-20 14:15:25 +0200832 peer4 = pdp_get_peer_ipv(pdp, false);
833 peer6 = pdp_get_peer_ipv(pdp, true);
834
835 if (v4only && peer6)
836 return;
837
Vadim Yanitskiyfb625042019-05-19 02:00:31 +0700838 /* Attempt to decode MSISDN */
839 rc = gsm48_decode_bcd_number2(name_buf, sizeof(name_buf),
840 pdp->msisdn.v, pdp->msisdn.l, 0);
Harald Weltedda21ed2017-08-12 15:07:02 +0200841
842 vty_out(vty, "IMSI: %s, NSAPI: %u, MSISDN: %s%s", imsi_gtp2str(&pdp->imsi), pdp->nsapi,
Vadim Yanitskiyfb625042019-05-19 02:00:31 +0700843 rc ? "(NONE)" : name_buf, VTY_NEWLINE);
Harald Weltedda21ed2017-08-12 15:07:02 +0200844
Pau Espin Pedrola0196312019-08-20 19:11:29 +0200845 vty_out(vty, " Version: %d", pdp->version);
846 if (pdp->version == 1) {
847 if (!pdp->secondary) {
848 vty_out(vty, ", Primary, Num Secondaries: %d%s%s",
849 pdp_count_secondary(pdp) - 1, /* primary included in count */
850 pdp->nodata ? ", No User Plane": "",
851 VTY_NEWLINE);
852 } else {
853 vty_out(vty, ", Secondary%s", VTY_NEWLINE);
854 }
855 } else {
856 vty_out(vty, "%s", VTY_NEWLINE);
857 }
858
Harald Weltedda21ed2017-08-12 15:07:02 +0200859 vty_out(vty, " Control: %s:%08x ", print_gsnaddr(&pdp->gsnlc), pdp->teic_own);
860 vty_out(vty, "<-> %s:%08x%s", print_gsnaddr(&pdp->gsnrc), pdp->teic_gn, VTY_NEWLINE);
861
862 vty_out(vty, " Data: %s:%08x ", print_gsnaddr(&pdp->gsnlu), pdp->teid_own);
863 vty_out(vty, "<-> %s:%08x%s", print_gsnaddr(&pdp->gsnru), pdp->teid_gn, VTY_NEWLINE);
864
Vadim Yanitskiyd7030d22019-05-13 22:10:24 +0700865 apn_name = osmo_apn_to_str(name_buf, pdp->apn_req.v, pdp->apn_req.l);
866 vty_out(vty, " APN requested: %s%s", apn_name ? name_buf : "(NONE)", VTY_NEWLINE);
867 apn_name = osmo_apn_to_str(name_buf, pdp->apn_use.v, pdp->apn_use.l);
868 vty_out(vty, " APN in use: %s%s", apn_name ? name_buf : "(NONE)", VTY_NEWLINE);
869
Pau Espin Pedrol310ea1d2019-08-20 14:15:25 +0200870 if (peer4)
Pau Espin Pedrol03cce862019-08-20 12:23:14 +0200871 vty_out(vty, " End-User Address (IPv4): %s%s",
Pau Espin Pedrol310ea1d2019-08-20 14:15:25 +0200872 in46a_ntop(&peer4->addr, name_buf, sizeof(name_buf)), VTY_NEWLINE);
873 if (peer6)
Pau Espin Pedrol03cce862019-08-20 12:23:14 +0200874 vty_out(vty, " End-User Address (IPv6): %s%s",
Pau Espin Pedrol310ea1d2019-08-20 14:15:25 +0200875 in46a_ntop(&peer6->addr, name_buf, sizeof(name_buf)), VTY_NEWLINE);
Harald Welte93fed3b2017-09-24 11:43:17 +0800876 vty_out(vty, " Transmit GTP Sequence Number for G-PDU: %s%s",
877 pdp->tx_gpdu_seq ? "Yes" : "No", VTY_NEWLINE);
Harald Weltedda21ed2017-08-12 15:07:02 +0200878}
879
Pau Espin Pedrol310ea1d2019-08-20 14:15:25 +0200880static void show_one_pdp(struct vty *vty, struct pdp_t *pdp)
881{
882 show_one_pdp_v4only(vty, pdp, false);
883}
884
Harald Weltedda21ed2017-08-12 15:07:02 +0200885DEFUN(show_pdpctx_imsi, show_pdpctx_imsi_cmd,
Pau Espin Pedrol7b52f002019-05-31 16:15:16 +0200886 "show pdp-context ggsn NAME imsi IMSI [<0-15>]",
Harald Weltedda21ed2017-08-12 15:07:02 +0200887 SHOW_STR "Display information on PDP Context\n"
Pau Espin Pedrol7b52f002019-05-31 16:15:16 +0200888 GGSN_STR "GGSN Name\n"
Harald Weltedda21ed2017-08-12 15:07:02 +0200889 "PDP contexts for given IMSI\n"
890 "PDP context for given NSAPI\n")
891{
Pau Espin Pedrol7b52f002019-05-31 16:15:16 +0200892 struct ggsn_ctx *ggsn;
893 uint64_t imsi;
Harald Weltedda21ed2017-08-12 15:07:02 +0200894 unsigned int nsapi;
895 struct pdp_t *pdp;
896 int num_found = 0;
897
Pau Espin Pedrol7b52f002019-05-31 16:15:16 +0200898 ggsn = ggsn_find(argv[0]);
899 if (!ggsn) {
900 vty_out(vty, "%% No such GGSN '%s'%s", argv[0], VTY_NEWLINE);
901 return CMD_WARNING;
902 }
903
Keithcbc07bd2020-10-10 12:17:26 +0200904 if (strlen(argv[1]) < 6 || strlen(argv[1]) > 15) {
905 vty_out(vty, "%% Invalid IMSI '%s'%s", argv[1], VTY_NEWLINE);
906 return CMD_WARNING;
907 }
908
Keithfb2a7292020-10-12 15:32:07 +0200909 imsi = gtp_imsi_str2gtp(argv[1]);
Pau Espin Pedrol7b52f002019-05-31 16:15:16 +0200910
911 if (argc > 2) {
912 nsapi = atoi(argv[2]);
Keith080dcfa2020-10-10 15:15:31 +0200913 if (!gtp_pdp_getimsi(ggsn->gsn, &pdp, imsi, nsapi)) {
Harald Weltedda21ed2017-08-12 15:07:02 +0200914 show_one_pdp(vty, pdp);
915 num_found++;
916 }
917 } else {
918 for (nsapi = 0; nsapi < PDP_MAXNSAPI; nsapi++) {
Pau Espin Pedrol7b52f002019-05-31 16:15:16 +0200919 if (gtp_pdp_getimsi(ggsn->gsn, &pdp, imsi, nsapi))
Harald Weltedda21ed2017-08-12 15:07:02 +0200920 continue;
921 show_one_pdp(vty, pdp);
922 num_found++;
923 }
924 }
925 if (num_found == 0) {
926 vty_out(vty, "%% No such PDP context found%s", VTY_NEWLINE);
927 return CMD_WARNING;
928 } else
929 return CMD_SUCCESS;
930}
931
Vadim Yanitskiyca276e02019-05-13 13:06:51 +0700932DEFUN(show_pdpctx_ip, show_pdpctx_ip_cmd,
933 "show pdp-context ggsn NAME ipv4 A.B.C.D",
934 SHOW_STR "Display information on PDP Context\n"
935 GGSN_STR "GGSN Name\n" "IPv4 address type\n" "IP address\n")
936{
937 struct ggsn_ctx *ggsn;
938 struct apn_ctx *apn;
939 unsigned int i;
940
941 ggsn = ggsn_find(argv[0]);
942 if (!ggsn) {
943 vty_out(vty, "%% No such GGSN '%s'%s", argv[0], VTY_NEWLINE);
944 return CMD_WARNING;
945 }
946
947 /* Iterate over all APNs of a given GGSN */
948 llist_for_each_entry(apn, &ggsn->apn_list, list) {
949 struct ippool_t *pool = apn->v4.pool;
950
951 /* In some rare cases, if GGSN fails to init TUN/TAP interfaces
952 * (e.g. due to insufficient permissions), it will continue to
953 * work in such broken state, and pool would be NULL. */
954 if (!pool)
955 continue;
956
957 /* Iterate over all IPv4 pool members */
958 for (i = 0; i < pool->listsize; i++) {
959 struct ippoolm_t *member = &pool->member[i];
960 if (member->inuse == 0)
961 continue;
962 if (strcmp(argv[1], in46a_ntoa(&member->addr)) == 0) {
963 show_one_pdp(vty, member->peer);
964 return CMD_SUCCESS;
965 }
966 }
967 }
968
969 vty_out(vty, "%% No PDP context found for IP '%s'%s", argv[1], VTY_NEWLINE);
970 return CMD_WARNING;
971}
972
Harald Weltedda21ed2017-08-12 15:07:02 +0200973/* show all (active) PDP contexts within a pool */
Pau Espin Pedrol310ea1d2019-08-20 14:15:25 +0200974static void ippool_show_pdp_contexts(struct vty *vty, struct ippool_t *pool, bool pdp_v4only)
Harald Weltedda21ed2017-08-12 15:07:02 +0200975{
976 unsigned int i;
977
978 if (!pool)
979 return;
980
981 for (i = 0; i < pool->listsize; i++) {
982 struct ippoolm_t *member = &pool->member[i];
983 if (member->inuse == 0)
984 continue;
Pau Espin Pedrol310ea1d2019-08-20 14:15:25 +0200985 show_one_pdp_v4only(vty, member->peer, pdp_v4only);
Harald Weltedda21ed2017-08-12 15:07:02 +0200986 }
987}
988
989/* show all (active) PDP contexts within an APN */
990static void apn_show_pdp_contexts(struct vty *vty, struct apn_ctx *apn)
991{
Pau Espin Pedrol310ea1d2019-08-20 14:15:25 +0200992 ippool_show_pdp_contexts(vty, apn->v4.pool, true);
993 ippool_show_pdp_contexts(vty, apn->v6.pool, false);
Harald Weltedda21ed2017-08-12 15:07:02 +0200994}
995
996DEFUN(show_pdpctx, show_pdpctx_cmd,
Vadim Yanitskiy977b3392019-05-13 15:32:21 +0700997 "show pdp-context ggsn NAME",
Harald Weltedda21ed2017-08-12 15:07:02 +0200998 SHOW_STR "Show PDP Context Information\n"
Vadim Yanitskiy977b3392019-05-13 15:32:21 +0700999 GGSN_STR "GGSN Name\n")
Harald Weltedda21ed2017-08-12 15:07:02 +02001000{
1001 struct ggsn_ctx *ggsn;
1002 struct apn_ctx *apn;
1003
1004 ggsn = ggsn_find(argv[0]);
1005 if (!ggsn) {
1006 vty_out(vty, "%% No such GGSN '%s'%s", argv[0], VTY_NEWLINE);
1007 return CMD_WARNING;
1008 }
Vadim Yanitskiy977b3392019-05-13 15:32:21 +07001009
1010 llist_for_each_entry(apn, &ggsn->apn_list, list)
Harald Weltedda21ed2017-08-12 15:07:02 +02001011 apn_show_pdp_contexts(vty, apn);
Harald Weltedda21ed2017-08-12 15:07:02 +02001012
1013 return CMD_SUCCESS;
1014}
1015
Vadim Yanitskiy977b3392019-05-13 15:32:21 +07001016DEFUN(show_pdpctx_apn, show_pdpctx_apn_cmd,
1017 "show pdp-context ggsn NAME apn APN",
1018 SHOW_STR "Show PDP Context Information\n"
1019 GGSN_STR "GGSN Name\n" "Filter by APN\n" "APN name\n")
1020{
1021 struct ggsn_ctx *ggsn;
1022 struct apn_ctx *apn;
1023
1024 ggsn = ggsn_find(argv[0]);
1025 if (!ggsn) {
1026 vty_out(vty, "%% No such GGSN '%s'%s", argv[0], VTY_NEWLINE);
1027 return CMD_WARNING;
1028 }
1029
1030 apn = ggsn_find_apn(ggsn, argv[1]);
1031 if (!apn) {
1032 vty_out(vty, "%% No such APN '%s'%s", argv[1], VTY_NEWLINE);
1033 return CMD_WARNING;
1034 }
1035
1036 apn_show_pdp_contexts(vty, apn);
1037 return CMD_SUCCESS;
1038}
1039
1040/* Backwards compatibility: the VTY parser is (mis)interpreting
1041 * "[apn APN]" as two separate elements: "[apn" and "APN]",
1042 * but the first part somehow turns into command "ap". */
1043ALIAS_DEPRECATED(show_pdpctx_apn, show_deprecated_pdpctx_apn_cmd,
1044 "show pdp-context ggsn NAME ap APN",
1045 SHOW_STR "Show PDP Context Information\n"
1046 GGSN_STR "GGSN Name\n" "Filter by APN\n" "APN name\n");
1047
Harald Weltedda21ed2017-08-12 15:07:02 +02001048static void show_apn(struct vty *vty, struct apn_ctx *apn)
1049{
1050 vty_out(vty, " APN: %s%s", apn->cfg.name, VTY_NEWLINE);
1051 /* FIXME */
1052}
1053
1054static void show_one_ggsn(struct vty *vty, struct ggsn_ctx *ggsn)
1055{
1056 struct apn_ctx *apn;
Pau Espin Pedrolf5fbb412019-08-21 18:49:44 +02001057 struct sgsn_peer *sgsn;
Harald Weltedda21ed2017-08-12 15:07:02 +02001058 vty_out(vty, "GGSN %s: Bound to %s%s", ggsn->cfg.name, in46a_ntoa(&ggsn->cfg.listen_addr),
1059 VTY_NEWLINE);
1060 /* FIXME */
1061
1062 llist_for_each_entry(apn, &ggsn->apn_list, list)
1063 show_apn(vty, apn);
Pau Espin Pedrolf5fbb412019-08-21 18:49:44 +02001064 llist_for_each_entry(sgsn, &ggsn->sgsn_list, entry)
1065 show_one_sgsn(vty, sgsn, " ");
Harald Weltedda21ed2017-08-12 15:07:02 +02001066}
1067
1068DEFUN(show_ggsn, show_ggsn_cmd,
1069 "show ggsn [NAME]",
1070 SHOW_STR "Display information on the GGSN\n")
1071{
1072 struct ggsn_ctx *ggsn;
1073
1074 if (argc == 0) {
1075 llist_for_each_entry(ggsn, &g_ggsn_list, list)
1076 show_one_ggsn(vty, ggsn);
1077 } else {
1078 ggsn = ggsn_find(argv[0]);
1079 if (!ggsn)
1080 return CMD_WARNING;
1081 show_one_ggsn(vty, ggsn);
1082 }
1083
1084 return CMD_SUCCESS;
1085}
1086
1087int ggsn_vty_init(void)
1088{
1089 install_element_ve(&show_pdpctx_cmd);
Vadim Yanitskiy977b3392019-05-13 15:32:21 +07001090 install_element_ve(&show_pdpctx_apn_cmd);
1091 install_element_ve(&show_deprecated_pdpctx_apn_cmd);
Harald Weltedda21ed2017-08-12 15:07:02 +02001092 install_element_ve(&show_pdpctx_imsi_cmd);
Vadim Yanitskiyca276e02019-05-13 13:06:51 +07001093 install_element_ve(&show_pdpctx_ip_cmd);
Harald Weltedda21ed2017-08-12 15:07:02 +02001094 install_element_ve(&show_ggsn_cmd);
1095
1096 install_element(CONFIG_NODE, &cfg_ggsn_cmd);
1097 install_element(CONFIG_NODE, &cfg_no_ggsn_cmd);
Pau Espin Pedrol840ce8a2017-11-16 17:01:44 +01001098
Harald Weltedda21ed2017-08-12 15:07:02 +02001099 install_node(&ggsn_node, config_write_ggsn);
Harald Weltedda21ed2017-08-12 15:07:02 +02001100 install_element(GGSN_NODE, &cfg_description_cmd);
1101 install_element(GGSN_NODE, &cfg_no_description_cmd);
1102 install_element(GGSN_NODE, &cfg_ggsn_shutdown_cmd);
1103 install_element(GGSN_NODE, &cfg_ggsn_no_shutdown_cmd);
Harald Welte98146772017-09-05 17:41:20 +02001104 install_element(GGSN_NODE, &cfg_ggsn_bind_ip_cmd);
1105 install_element(GGSN_NODE, &cfg_ggsn_gtpc_ip_cmd);
1106 install_element(GGSN_NODE, &cfg_ggsn_gtpu_ip_cmd);
Harald Weltedda21ed2017-08-12 15:07:02 +02001107 install_element(GGSN_NODE, &cfg_ggsn_state_dir_cmd);
1108 install_element(GGSN_NODE, &cfg_ggsn_apn_cmd);
1109 install_element(GGSN_NODE, &cfg_ggsn_no_apn_cmd);
1110 install_element(GGSN_NODE, &cfg_ggsn_default_apn_cmd);
1111 install_element(GGSN_NODE, &cfg_ggsn_no_default_apn_cmd);
Pau Espin Pedrolf5fbb412019-08-21 18:49:44 +02001112 install_element(GGSN_NODE, &cfg_ggsn_show_sgsn_cmd);
1113 install_element(GGSN_NODE, &cfg_ggsn_echo_interval_cmd);
1114 install_element(GGSN_NODE, &cfg_ggsn_no_echo_interval_cmd);
Harald Weltedda21ed2017-08-12 15:07:02 +02001115
1116 install_node(&apn_node, NULL);
Harald Weltedda21ed2017-08-12 15:07:02 +02001117 install_element(APN_NODE, &cfg_description_cmd);
1118 install_element(APN_NODE, &cfg_no_description_cmd);
1119 install_element(APN_NODE, &cfg_apn_shutdown_cmd);
1120 install_element(APN_NODE, &cfg_apn_no_shutdown_cmd);
1121 install_element(APN_NODE, &cfg_apn_gtpu_mode_cmd);
1122 install_element(APN_NODE, &cfg_apn_type_support_cmd);
1123 install_element(APN_NODE, &cfg_apn_no_type_support_cmd);
1124 install_element(APN_NODE, &cfg_apn_tun_dev_name_cmd);
1125 install_element(APN_NODE, &cfg_apn_ipup_script_cmd);
1126 install_element(APN_NODE, &cfg_apn_no_ipup_script_cmd);
1127 install_element(APN_NODE, &cfg_apn_ipdown_script_cmd);
1128 install_element(APN_NODE, &cfg_apn_no_ipdown_script_cmd);
1129 install_element(APN_NODE, &cfg_apn_ip_prefix_cmd);
1130 install_element(APN_NODE, &cfg_apn_ipv6_prefix_cmd);
1131 install_element(APN_NODE, &cfg_apn_ip_dns_cmd);
1132 install_element(APN_NODE, &cfg_apn_ipv6_dns_cmd);
1133 install_element(APN_NODE, &cfg_apn_no_dns_cmd);
1134 install_element(APN_NODE, &cfg_apn_ip_ifconfig_cmd);
1135 install_element(APN_NODE, &cfg_apn_no_ip_ifconfig_cmd);
1136 install_element(APN_NODE, &cfg_apn_ipv6_ifconfig_cmd);
1137 install_element(APN_NODE, &cfg_apn_no_ipv6_ifconfig_cmd);
Pau Espin Pedrol37c45e32017-12-14 14:09:13 +01001138 install_element(APN_NODE, &cfg_apn_ipv6_linklocal_cmd);
1139 install_element(APN_NODE, &cfg_apn_no_ipv6_linklocal_cmd);
Harald Welte93fed3b2017-09-24 11:43:17 +08001140 install_element(APN_NODE, &cfg_apn_gpdu_seq_cmd);
1141 install_element(APN_NODE, &cfg_apn_no_gpdu_seq_cmd);
Harald Weltedda21ed2017-08-12 15:07:02 +02001142
1143 return 0;
1144}
1145
1146static int ggsn_vty_is_config_node(struct vty *vty, int node)
1147{
1148 switch (node) {
1149 case GGSN_NODE:
1150 case APN_NODE:
1151 return 1;
1152 default:
1153 return 0;
1154 }
1155}
1156
1157static int ggsn_vty_go_parent(struct vty *vty)
1158{
1159 switch (vty->node) {
1160 case GGSN_NODE:
1161 vty->node = CONFIG_NODE;
1162 vty->index = NULL;
1163 vty->index_sub = NULL;
1164 break;
1165 case APN_NODE:
1166 vty->node = GGSN_NODE;
1167 {
1168 struct apn_ctx *apn = vty->index;
1169 vty->index = apn->ggsn;
1170 vty->index_sub = &apn->ggsn->cfg.description;
1171 }
1172 break;
Vadim Yanitskiy906c2092018-05-09 23:11:27 +07001173 default:
1174 vty->node = CONFIG_NODE;
1175 vty->index = NULL;
1176 vty->index_sub = NULL;
Harald Weltedda21ed2017-08-12 15:07:02 +02001177 }
1178
1179 return vty->node;
1180}
1181
1182static const char ggsn_copyright[] =
1183 "Copyright (C) 2011-2017 Harald Welte <laforge@gnumonks.org>\r\n"
1184 "Copyright (C) 2012-2017 Holger Hans Peter Freyther <holger@moiji-mobile.com>\r\n"
1185 "Copyright (C) 2012-2017 sysmocom - s.f.m.c. GmbH\r\n"
1186 "Copyright (C) 2002-2005 Mondru AB\r\n"
1187 "License GPLv2: GNU GPL version 2 <http://gnu.org/licenses/gpl-2.0.html>\r\n"
1188 "This is free software: you are free to change and redistribute it.\r\n"
1189 "There is NO WARRANTY, to the extent permitted by law.\r\n";
1190
1191struct vty_app_info g_vty_info = {
Harald Welte632e8432017-09-05 18:12:14 +02001192 .name = "OsmoGGSN",
Harald Weltedda21ed2017-08-12 15:07:02 +02001193 .version = PACKAGE_VERSION,
1194 .copyright = ggsn_copyright,
1195 .go_parent_cb = ggsn_vty_go_parent,
1196 .is_config_node = ggsn_vty_is_config_node,
1197};