blob: 9f343ec7c157d3979d0ed834536ecd11af88a4df [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>
29#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
30
31#include <osmocom/vty/command.h>
32#include <osmocom/vty/vty.h>
33#include <osmocom/vty/misc.h>
34
35#include "../gtp/gtp.h"
36#include "../gtp/pdp.h"
37
38#include "ggsn.h"
39
40#define PREFIX_STR "Prefix (Network/Netmask)\n"
41#define IFCONFIG_STR "GGSN-based interface configuration\n"
42#define GGSN_STR "Gateway GPRS Support NODE (GGSN)\n"
43
44LLIST_HEAD(g_ggsn_list);
45
46enum ggsn_vty_node {
47 GGSN_NODE = _LAST_OSMOVTY_NODE + 1,
48 APN_NODE,
49};
50
51struct ggsn_ctx *ggsn_find(const char *name)
52{
53 struct ggsn_ctx *ggsn;
54
55 llist_for_each_entry(ggsn, &g_ggsn_list, list) {
56 if (!strcmp(ggsn->cfg.name, name))
57 return ggsn;
58 }
59 return NULL;
60}
61
62struct ggsn_ctx *ggsn_find_or_create(void *ctx, const char *name)
63{
64 struct ggsn_ctx *ggsn;
65
66 ggsn = ggsn_find(name);
67 if (ggsn)
68 return ggsn;
69
70 ggsn = talloc_zero(ctx, struct ggsn_ctx);
71 if (!ggsn)
72 return NULL;
73
74 ggsn->cfg.name = talloc_strdup(ggsn, name);
75 ggsn->cfg.state_dir = talloc_strdup(ggsn, "/tmp");
76 ggsn->cfg.shutdown = true;
77 INIT_LLIST_HEAD(&ggsn->apn_list);
78
79 llist_add_tail(&ggsn->list, &g_ggsn_list);
80 return ggsn;
81}
82
83struct apn_ctx *ggsn_find_apn(struct ggsn_ctx *ggsn, const char *name)
84{
85 struct apn_ctx *apn;
86
87 llist_for_each_entry(apn, &ggsn->apn_list, list) {
88 if (!strcmp(apn->cfg.name, name))
89 return apn;
90 }
91 return NULL;
92}
93
94struct apn_ctx *ggsn_find_or_create_apn(struct ggsn_ctx *ggsn, const char *name)
95{
96 struct apn_ctx *apn = ggsn_find_apn(ggsn, name);
97 if (apn)
98 return apn;
99
100 apn = talloc_zero(ggsn, struct apn_ctx);
101 if (!apn)
102 return NULL;
103 apn->ggsn = ggsn;
104 apn->cfg.name = talloc_strdup(apn, name);
105 apn->cfg.shutdown = true;
Harald Welte93fed3b2017-09-24 11:43:17 +0800106 apn->cfg.tx_gpdu_seq = true;
Harald Weltedda21ed2017-08-12 15:07:02 +0200107 INIT_LLIST_HEAD(&apn->cfg.name_list);
108
109 llist_add_tail(&apn->list, &ggsn->apn_list);
110 return apn;
111}
112
113/* GGSN Node */
114
115static struct cmd_node ggsn_node = {
116 GGSN_NODE,
117 "%s(config-ggsn)# ",
118 1,
119};
120
121DEFUN(cfg_ggsn, cfg_ggsn_cmd,
122 "ggsn NAME",
123 "Configure the Gateway GPRS Support Node\n" "GGSN Name (has only local significance)\n")
124{
125 struct ggsn_ctx *ggsn;
126
127 ggsn = ggsn_find_or_create(tall_ggsn_ctx, argv[0]);
128 if (!ggsn)
129 return CMD_WARNING;
130
131 vty->node = GGSN_NODE;
132 vty->index = ggsn;
133 vty->index_sub = &ggsn->cfg.description;
134
135 return CMD_SUCCESS;
136}
137
138DEFUN(cfg_no_ggsn, cfg_no_ggsn_cmd,
139 "no ggsn NAME",
140 NO_STR "Remove the named Gateway GPRS Support Node\n"
141 "GGSN Name (has only local significance)\n")
142{
143 struct ggsn_ctx *ggsn;
144
145 ggsn = ggsn_find(argv[0]);
146 if (!ggsn) {
147 vty_out(vty, "%% No such GGSN '%s'%s", argv[0], VTY_NEWLINE);
148 return CMD_WARNING;
149 }
150
151 if (!ggsn->cfg.shutdown) {
152 vty_out(vty, "%% GGSN %s is still active, please shutdown first%s",
153 ggsn->cfg.name, VTY_NEWLINE);
154 return CMD_WARNING;
155 }
156
157 if (!llist_empty(&ggsn->apn_list)) {
158 vty_out(vty, "%% GGSN %s still has APNs configured, please remove first%s",
159 ggsn->cfg.name, VTY_NEWLINE);
160 return CMD_WARNING;
161 }
162
163 llist_del(&ggsn->list);
164 talloc_free(ggsn);
165
166 return CMD_SUCCESS;
167}
168
Harald Welte98146772017-09-05 17:41:20 +0200169DEFUN(cfg_ggsn_bind_ip, cfg_ggsn_bind_ip_cmd,
170 "gtp bind-ip A.B.C.D",
Harald Weltedda21ed2017-08-12 15:07:02 +0200171 "GTP Parameters\n"
172 "Set the IP address for the local GTP bind\n"
173 "IPv4 Address\n")
174{
175 struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
176 size_t t;
177
178 ippool_aton(&ggsn->cfg.listen_addr, &t, argv[0], 0);
179
180 return CMD_SUCCESS;
181}
182
Harald Welte98146772017-09-05 17:41:20 +0200183DEFUN(cfg_ggsn_gtpc_ip, cfg_ggsn_gtpc_ip_cmd,
184 "gtp control-ip A.B.C.D",
185 "GTP Parameters\n"
186 "Set the IP address states as local IP in GTP-C messages\n"
187 "IPv4 Address\n")
188{
189 struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
190 size_t t;
191
192 ippool_aton(&ggsn->cfg.gtpc_addr, &t, argv[0], 0);
193
194 return CMD_SUCCESS;
195}
196
197DEFUN(cfg_ggsn_gtpu_ip, cfg_ggsn_gtpu_ip_cmd,
198 "gtp user-ip A.B.C.D",
199 "GTP Parameters\n"
200 "Set the IP address states as local IP in GTP-U messages\n"
201 "IPv4 Address\n")
202{
203 struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
204 size_t t;
205
206 ippool_aton(&ggsn->cfg.gtpu_addr, &t, argv[0], 0);
207
208 return CMD_SUCCESS;
209}
210
Harald Weltedda21ed2017-08-12 15:07:02 +0200211DEFUN(cfg_ggsn_state_dir, cfg_ggsn_state_dir_cmd,
212 "gtp state-dir PATH",
213 "GTP Parameters\n"
214 "Set the directory for the GTP State file\n"
215 "Local Directory\n")
216{
217 struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
218
219 osmo_talloc_replace_string(ggsn, &ggsn->cfg.state_dir, argv[0]);
220
221 return CMD_SUCCESS;
222}
223
224DEFUN(cfg_ggsn_apn, cfg_ggsn_apn_cmd,
225 "apn NAME", "APN Configuration\n" "APN Name\n")
226{
227 struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
228 struct apn_ctx *apn;
229
230 apn = ggsn_find_or_create_apn(ggsn, argv[0]);
231 if (!apn)
232 return CMD_WARNING;
233
234 vty->node = APN_NODE;
235 vty->index = apn;
236 vty->index_sub = &ggsn->cfg.description;
237
238 return CMD_SUCCESS;
239}
240
241DEFUN(cfg_ggsn_no_apn, cfg_ggsn_no_apn_cmd,
242 "no apn NAME",
243 NO_STR "Remove APN Configuration\n" "APN Name\n")
244{
245 struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
246 struct apn_ctx *apn;
247
248 apn = ggsn_find_apn(ggsn, argv[0]);
249 if (!apn) {
250 vty_out(vty, "%% No such APN '%s'%s", argv[0], VTY_NEWLINE);
251 return CMD_WARNING;
252 }
253
254 if (!apn->cfg.shutdown) {
255 vty_out(vty, "%% APN %s still active, please shutdown first%s",
256 apn->cfg.name, VTY_NEWLINE);
257 return CMD_WARNING;
258 }
259
260 llist_del(&apn->list);
261 talloc_free(apn);
262
263 return CMD_SUCCESS;
264}
265
266DEFUN(cfg_ggsn_default_apn, cfg_ggsn_default_apn_cmd,
267 "default-apn NAME",
268 "Set a default-APN to be used if no other APN matches\n"
269 "APN Name\n")
270{
271 struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
272 struct apn_ctx *apn;
273
274 apn = ggsn_find_apn(ggsn, argv[0]);
275 if (!apn) {
276 vty_out(vty, "%% No APN of name '%s' found%s", argv[0], VTY_NEWLINE);
277 return CMD_WARNING;
278 }
279
280 ggsn->cfg.default_apn = apn;
281 return CMD_SUCCESS;
282}
283
284DEFUN(cfg_ggsn_no_default_apn, cfg_ggsn_no_default_apn_cmd,
285 "no default-apn",
286 NO_STR "Remove default-APN to be used if no other APN matches\n")
287{
288 struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
289 ggsn->cfg.default_apn = NULL;
290 return CMD_SUCCESS;
291}
292
293DEFUN(cfg_ggsn_shutdown, cfg_ggsn_shutdown_cmd,
294 "shutdown ggsn",
295 "Put the GGSN in administrative shut-down\n" GGSN_STR)
296{
297 struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
298
299 if (!ggsn->cfg.shutdown) {
300 if (ggsn_stop(ggsn)) {
301 vty_out(vty, "%% Failed to shutdown GGSN%s", VTY_NEWLINE);
302 return CMD_WARNING;
303 }
304 ggsn->cfg.shutdown = true;
305 }
306
307 return CMD_SUCCESS;
308}
309
310DEFUN(cfg_ggsn_no_shutdown, cfg_ggsn_no_shutdown_cmd,
311 "no shutdown ggsn",
312 NO_STR GGSN_STR "Remove the GGSN from administrative shut-down\n")
313{
314 struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
315
316 if (ggsn->cfg.shutdown) {
317 if (ggsn_start(ggsn) < 0) {
318 vty_out(vty, "%% Failed to start GGSN, check log for details%s", VTY_NEWLINE);
319 return CMD_WARNING;
320 }
321 ggsn->cfg.shutdown = false;
322 }
323
324 return CMD_SUCCESS;
325}
326
327/* APN Node */
328
329static struct cmd_node apn_node = {
330 APN_NODE,
331 "%s(config-ggsn-apn)# ",
332 1,
333};
334
335static const struct value_string pdp_type_names[] = {
336 { APN_TYPE_IPv4, "v4" },
337 { APN_TYPE_IPv6, "v6" },
338 { APN_TYPE_IPv4v6, "v4v6" },
339 { 0, NULL }
340};
341
342static const struct value_string apn_gtpu_mode_names[] = {
343 { APN_GTPU_MODE_TUN, "tun" },
344 { APN_GTPU_MODE_KERNEL_GTP, "kernel-gtp" },
345 { 0, NULL }
346};
347
348
349#define V4V6V46_STRING "IPv4(-only) PDP Type\n" \
350 "IPv6(-only) PDP Type\n" \
351 "IPv4v6 (dual-stack) PDP Type\n"
352
353DEFUN(cfg_apn_type_support, cfg_apn_type_support_cmd,
354 "type-support (v4|v6|v4v6)",
355 "Enable support for PDP Type\n"
356 V4V6V46_STRING)
357{
358 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
359 uint32_t type = get_string_value(pdp_type_names, argv[0]);
360
361 apn->cfg.apn_type_mask |= type;
362 return CMD_SUCCESS;
363}
364
365DEFUN(cfg_apn_no_type_support, cfg_apn_no_type_support_cmd,
366 "no type-support (v4|v6|v4v6)",
367 NO_STR "Disable support for PDP Type\n"
368 V4V6V46_STRING)
369{
370 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
371 uint32_t type = get_string_value(pdp_type_names, argv[0]);
372
373 apn->cfg.apn_type_mask &= ~type;
374 return CMD_SUCCESS;
375}
376
377DEFUN(cfg_apn_gtpu_mode, cfg_apn_gtpu_mode_cmd,
378 "gtpu-mode (tun|kernel-gtp)",
379 "Set the Mode for this APN (tun or Linux Kernel GTP)\n"
380 "GTP-U in userspace using TUN device\n"
381 "GTP-U in kernel using Linux Kernel GTP\n")
382{
383 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
384
385 apn->cfg.gtpu_mode = get_string_value(apn_gtpu_mode_names, argv[0]);
386 return CMD_SUCCESS;
387}
388
389DEFUN(cfg_apn_tun_dev_name, cfg_apn_tun_dev_name_cmd,
390 "tun-device NAME",
391 "Configure tun device name\n"
392 "TUN device name")
393{
394 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
395 osmo_talloc_replace_string(apn, &apn->tun.cfg.dev_name, argv[0]);
396 return CMD_SUCCESS;
397}
398
399DEFUN(cfg_apn_ipup_script, cfg_apn_ipup_script_cmd,
400 "ipup-script PATH",
401 "Configure name/path of ip-up script\n"
402 "File/Path name of ip-up script\n")
403{
404 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
405 osmo_talloc_replace_string(apn, &apn->tun.cfg.ipup_script, argv[0]);
406 return CMD_SUCCESS;
407}
408
409DEFUN(cfg_apn_no_ipup_script, cfg_apn_no_ipup_script_cmd,
410 "no ipup-script",
411 NO_STR "Disable ip-up script\n")
412{
413 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
414 talloc_free(apn->tun.cfg.ipup_script);
415 apn->tun.cfg.ipup_script = NULL;
416 return CMD_SUCCESS;
417}
418
419DEFUN(cfg_apn_ipdown_script, cfg_apn_ipdown_script_cmd,
420 "ipdown-script PATH",
421 "Configure name/path of ip-down script\n"
422 "File/Path name of ip-down script\n")
423{
424 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
425 osmo_talloc_replace_string(apn, &apn->tun.cfg.ipdown_script, argv[0]);
426 return CMD_SUCCESS;
427}
428
429/* convert prefix from "A.B.C.D/M" notation to in46_prefix */
430static void str2prefix(struct in46_prefix *pfx, const char *in)
431{
432 size_t t;
433
434 ippool_aton(&pfx->addr, &t, in, 0);
435 pfx->prefixlen = t;
436}
437
438DEFUN(cfg_apn_no_ipdown_script, cfg_apn_no_ipdown_script_cmd,
439 "no ipdown-script",
440 NO_STR "Disable ip-down script\n")
441{
442 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
443 talloc_free(apn->tun.cfg.ipdown_script);
444 apn->tun.cfg.ipdown_script = NULL;
445 return CMD_SUCCESS;
446}
447
448DEFUN(cfg_apn_ip_prefix, cfg_apn_ip_prefix_cmd,
449 "ip prefix (static|dynamic) A.B.C.D/M",
450 IP_STR PREFIX_STR "IPv4 Adress/Prefix-Length\n")
451{
452 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
453 struct in46_prefix *pfx;
454
455 /* first update our parsed prefix */
456 if (!strcmp(argv[0], "static"))
457 pfx = &apn->v4.cfg.static_prefix;
458 else
459 pfx = &apn->v4.cfg.dynamic_prefix;
460 str2prefix(pfx, argv[1]);
461
462 return CMD_SUCCESS;
463}
464
465DEFUN(cfg_apn_ip_ifconfig, cfg_apn_ip_ifconfig_cmd,
466 "ip ifconfig A.B.C.D/M",
467 IP_STR IFCONFIG_STR "IPv4 Adress/Prefix-Length\n")
468{
469 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
470 str2prefix(&apn->v4.cfg.ifconfig_prefix, argv[0]);
471 return CMD_SUCCESS;
472}
473
474DEFUN(cfg_apn_no_ip_ifconfig, cfg_apn_no_ip_ifconfig_cmd,
475 "no ip ifconfig",
476 NO_STR IP_STR IFCONFIG_STR)
477{
478 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
479 memset(&apn->v4.cfg.ifconfig_prefix, 0, sizeof(apn->v4.cfg.ifconfig_prefix));
480 return CMD_SUCCESS;
481}
482
483DEFUN(cfg_apn_ipv6_prefix, cfg_apn_ipv6_prefix_cmd,
484 "ipv6 prefix (static|dynamic) X:X::X:X/M",
485 IP6_STR PREFIX_STR "IPv6 Address/Prefix-Length\n")
486{
487 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
488 struct in46_prefix *pfx;
489
490 if (!strcmp(argv[0], "static"))
491 pfx = &apn->v6.cfg.static_prefix;
492 else
493 pfx = &apn->v6.cfg.dynamic_prefix;
494 str2prefix(pfx, argv[1]);
495 return CMD_SUCCESS;
496}
497
498DEFUN(cfg_apn_ipv6_ifconfig, cfg_apn_ipv6_ifconfig_cmd,
499 "ipv6 ifconfig X:X::X:X/M",
500 IP6_STR IFCONFIG_STR "IPv6 Adress/Prefix-Length\n")
501{
502 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
503 str2prefix(&apn->v6.cfg.ifconfig_prefix, argv[0]);
504 return CMD_SUCCESS;
505}
506
507DEFUN(cfg_apn_no_ipv6_ifconfig, cfg_apn_no_ipv6_ifconfig_cmd,
508 "no ipv6 ifconfig",
509 NO_STR IP6_STR IFCONFIG_STR)
510{
511 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
512 memset(&apn->v6.cfg.ifconfig_prefix, 0, sizeof(apn->v6.cfg.ifconfig_prefix));
513 return CMD_SUCCESS;
514}
515
516#define DNS_STRINGS "Configure DNS Server\n" "primary/secondary DNS\n" "IP address of DNS Sever\n"
517
518DEFUN(cfg_apn_ip_dns, cfg_apn_ip_dns_cmd,
519 "ip dns <0-1> A.B.C.D",
520 IP_STR DNS_STRINGS)
521{
522 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
523 int idx = atoi(argv[0]);
524 size_t dummy;
525
526 ippool_aton(&apn->v4.cfg.dns[idx], &dummy, argv[1], 0);
527
528 return CMD_SUCCESS;
529}
530
531DEFUN(cfg_apn_ipv6_dns, cfg_apn_ipv6_dns_cmd,
532 "ipv6 dns <0-1> X:X::X:X",
533 IP6_STR DNS_STRINGS)
534{
535 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
536 int idx = atoi(argv[0]);
537 size_t dummy;
538
539 ippool_aton(&apn->v6.cfg.dns[idx], &dummy, argv[1], 0);
540
541 return CMD_SUCCESS;
542}
543
544DEFUN(cfg_apn_no_dns, cfg_apn_no_dns_cmd,
545 "no (ip|ipv6) dns <0-1>",
546 NO_STR IP_STR IP6_STR "Disable DNS Server\n" "primary/secondary DNS\n")
547{
548 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
549 struct in46_addr *a;
550 int idx = atoi(argv[1]);
551
552 if (!strcmp(argv[0], "ip"))
553 a = &apn->v4.cfg.dns[idx];
554 else
555 a = &apn->v6.cfg.dns[idx];
556
557 memset(a, 0, sizeof(*a));
558
559 return CMD_SUCCESS;
560}
561
Harald Welte93fed3b2017-09-24 11:43:17 +0800562DEFUN(cfg_apn_gpdu_seq, cfg_apn_gpdu_seq_cmd,
563 "g-pdu tx-sequence-numbers",
564 "G-PDU Configuration\n" "Enable transmission of G-PDU sequence numbers\n")
565{
566 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
567 apn->cfg.tx_gpdu_seq = true;
568 return CMD_SUCCESS;
569}
570
571DEFUN(cfg_apn_no_gpdu_seq, cfg_apn_no_gpdu_seq_cmd,
572 "no g-pdu tx-sequence-numbers",
573 NO_STR "G-PDU Configuration\n" "Disable transmission of G-PDU sequence numbers\n")
574{
575 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
576 apn->cfg.tx_gpdu_seq = false;
577 return CMD_SUCCESS;
578}
579
Harald Weltedda21ed2017-08-12 15:07:02 +0200580DEFUN(cfg_apn_shutdown, cfg_apn_shutdown_cmd,
581 "shutdown",
582 "Put the APN in administrative shut-down\n")
583{
584 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
585
586 if (!apn->cfg.shutdown) {
587 if (apn_stop(apn, false)) {
588 vty_out(vty, "%% Failed to Stop APN%s", VTY_NEWLINE);
589 return CMD_WARNING;
590 }
591 apn->cfg.shutdown = true;
592 }
593
594 return CMD_SUCCESS;
595}
596
597DEFUN(cfg_apn_no_shutdown, cfg_apn_no_shutdown_cmd,
598 "no shutdown",
599 NO_STR "Remove the APN from administrative shut-down\n")
600{
601 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
602
603 if (apn->cfg.shutdown) {
604 if (apn_start(apn) < 0) {
605 vty_out(vty, "%% Failed to start APN, check log for details%s", VTY_NEWLINE);
606 return CMD_WARNING;
607 }
608 apn->cfg.shutdown = false;
609 }
610
611 return CMD_SUCCESS;
612}
613
614
615static void vty_dump_prefix(struct vty *vty, const char *pre, const struct in46_prefix *pfx)
616{
617 vty_out(vty, "%s %s%s", pre, in46p_ntoa(pfx), VTY_NEWLINE);
618}
619
620static void config_write_apn(struct vty *vty, struct apn_ctx *apn)
621{
622 unsigned int i;
623
624 vty_out(vty, " apn %s%s", apn->cfg.name, VTY_NEWLINE);
625 if (apn->cfg.description)
626 vty_out(vty, " description %s%s", apn->cfg.description, VTY_NEWLINE);
627 vty_out(vty, " gtpu-mode %s%s", get_value_string(apn_gtpu_mode_names, apn->cfg.gtpu_mode),
628 VTY_NEWLINE);
629 if (apn->tun.cfg.dev_name)
630 vty_out(vty, " tun-device %s%s", apn->tun.cfg.dev_name, VTY_NEWLINE);
631 if (apn->tun.cfg.ipup_script)
632 vty_out(vty, " ipup-script %s%s", apn->tun.cfg.ipup_script, VTY_NEWLINE);
633 if (apn->tun.cfg.ipdown_script)
634 vty_out(vty, " ipdown-script %s%s", apn->tun.cfg.ipdown_script, VTY_NEWLINE);
635
636 for (i = 0; i < 32; i++) {
637 if (!(apn->cfg.apn_type_mask & (1 << i)))
638 continue;
639 vty_out(vty, " type-support %s%s", get_value_string(pdp_type_names, (1 << i)),
640 VTY_NEWLINE);
641 }
642
Harald Welte93fed3b2017-09-24 11:43:17 +0800643 if (!apn->cfg.tx_gpdu_seq)
644 vty_out(vty, " no g-pdu tx-sequence-numbers%s", VTY_NEWLINE);
645
Harald Weltedda21ed2017-08-12 15:07:02 +0200646 /* IPv4 prefixes + DNS */
647 if (apn->v4.cfg.static_prefix.addr.len)
648 vty_dump_prefix(vty, " ip prefix static", &apn->v4.cfg.static_prefix);
649 if (apn->v4.cfg.dynamic_prefix.addr.len)
650 vty_dump_prefix(vty, " ip prefix dynamic", &apn->v4.cfg.dynamic_prefix);
651 for (i = 0; i < ARRAY_SIZE(apn->v4.cfg.dns); i++) {
652 if (!apn->v4.cfg.dns[i].len)
653 continue;
654 vty_out(vty, " ip dns %u %s%s", i, in46a_ntoa(&apn->v4.cfg.dns[i]), VTY_NEWLINE);
655 }
656 if (apn->v4.cfg.ifconfig_prefix.addr.len)
Harald Welteff438172017-09-24 22:48:46 +0800657 vty_dump_prefix(vty, " ip ifconfig", &apn->v4.cfg.ifconfig_prefix);
Harald Weltedda21ed2017-08-12 15:07:02 +0200658
659 /* IPv6 prefixes + DNS */
660 if (apn->v6.cfg.static_prefix.addr.len)
661 vty_dump_prefix(vty, " ipv6 prefix static", &apn->v6.cfg.static_prefix);
662 if (apn->v6.cfg.dynamic_prefix.addr.len)
663 vty_dump_prefix(vty, " ipv6 prefix dynamic", &apn->v6.cfg.dynamic_prefix);
664 for (i = 0; i < ARRAY_SIZE(apn->v6.cfg.dns); i++) {
665 if (!apn->v6.cfg.dns[i].len)
666 continue;
Harald Welte3ca419a2017-09-24 22:47:51 +0800667 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 +0200668 }
669 if (apn->v6.cfg.ifconfig_prefix.addr.len)
Harald Welteff438172017-09-24 22:48:46 +0800670 vty_dump_prefix(vty, " ipv6 ifconfig", &apn->v6.cfg.ifconfig_prefix);
Harald Weltedda21ed2017-08-12 15:07:02 +0200671
672 /* must be last */
673 vty_out(vty, " %sshutdown%s", apn->cfg.shutdown ? "" : "no ", VTY_NEWLINE);
674}
675
676static int config_write_ggsn(struct vty *vty)
677{
678 struct ggsn_ctx *ggsn;
679
680 llist_for_each_entry(ggsn, &g_ggsn_list, list) {
681 struct apn_ctx *apn;
682 vty_out(vty, "ggsn %s%s", ggsn->cfg.name, VTY_NEWLINE);
683 if (ggsn->cfg.description)
684 vty_out(vty, " description %s%s", ggsn->cfg.description, VTY_NEWLINE);
685 vty_out(vty, " gtp state-dir %s%s", ggsn->cfg.state_dir, VTY_NEWLINE);
Harald Welte98146772017-09-05 17:41:20 +0200686 vty_out(vty, " gtp bind-ip %s%s", in46a_ntoa(&ggsn->cfg.listen_addr), VTY_NEWLINE);
687 if (ggsn->cfg.gtpc_addr.v4.s_addr)
688 vty_out(vty, " gtp control-ip %s%s", in46a_ntoa(&ggsn->cfg.gtpc_addr), VTY_NEWLINE);
689 if (ggsn->cfg.gtpu_addr.v4.s_addr)
690 vty_out(vty, " gtp user-ip %s%s", in46a_ntoa(&ggsn->cfg.gtpu_addr), VTY_NEWLINE);
Harald Weltedda21ed2017-08-12 15:07:02 +0200691 llist_for_each_entry(apn, &ggsn->apn_list, list)
692 config_write_apn(vty, apn);
693 if (ggsn->cfg.default_apn)
694 vty_out(vty, " default-apn %s%s", ggsn->cfg.default_apn->cfg.name, VTY_NEWLINE);
695 /* must be last */
696 vty_out(vty, " %sshutdown ggsn%s", ggsn->cfg.shutdown ? "" : "no ", VTY_NEWLINE);
697 }
698
699 return 0;
700}
701
702static const char *print_gsnaddr(const struct ul16_t *in)
703{
704 struct in46_addr in46;
705
706 in46.len = in->l;
707 OSMO_ASSERT(in->l <= sizeof(in46.v6));
708 memcpy(&in46.v6, in->v, in->l);
709
710 return in46a_ntoa(&in46);
711}
712
713static void show_one_pdp(struct vty *vty, struct pdp_t *pdp)
714{
715 struct in46_addr eua46;
716
717 vty_out(vty, "IMSI: %s, NSAPI: %u, MSISDN: %s%s", imsi_gtp2str(&pdp->imsi), pdp->nsapi,
718 osmo_hexdump_nospc(pdp->msisdn.v, pdp->msisdn.l), VTY_NEWLINE);
719
720 vty_out(vty, " Control: %s:%08x ", print_gsnaddr(&pdp->gsnlc), pdp->teic_own);
721 vty_out(vty, "<-> %s:%08x%s", print_gsnaddr(&pdp->gsnrc), pdp->teic_gn, VTY_NEWLINE);
722
723 vty_out(vty, " Data: %s:%08x ", print_gsnaddr(&pdp->gsnlu), pdp->teid_own);
724 vty_out(vty, "<-> %s:%08x%s", print_gsnaddr(&pdp->gsnru), pdp->teid_gn, VTY_NEWLINE);
725
726 in46a_from_eua(&pdp->eua, &eua46);
727 vty_out(vty, " End-User Address: %s%s", in46a_ntoa(&eua46), VTY_NEWLINE);
Harald Welte93fed3b2017-09-24 11:43:17 +0800728 vty_out(vty, " Transmit GTP Sequence Number for G-PDU: %s%s",
729 pdp->tx_gpdu_seq ? "Yes" : "No", VTY_NEWLINE);
Harald Weltedda21ed2017-08-12 15:07:02 +0200730}
731
732DEFUN(show_pdpctx_imsi, show_pdpctx_imsi_cmd,
733 "show pdp-context imsi IMSI [<0-15>]",
734 SHOW_STR "Display information on PDP Context\n"
735 "PDP contexts for given IMSI\n"
736 "PDP context for given NSAPI\n")
737{
738 uint64_t imsi = strtoull(argv[0], NULL, 10);
739 unsigned int nsapi;
740 struct pdp_t *pdp;
741 int num_found = 0;
742
743 if (argc > 1) {
744 nsapi = atoi(argv[1]);
745 if (pdp_getimsi(&pdp, imsi, nsapi)) {
746 show_one_pdp(vty, pdp);
747 num_found++;
748 }
749 } else {
750 for (nsapi = 0; nsapi < PDP_MAXNSAPI; nsapi++) {
751 if (pdp_getimsi(&pdp, imsi, nsapi))
752 continue;
753 show_one_pdp(vty, pdp);
754 num_found++;
755 }
756 }
757 if (num_found == 0) {
758 vty_out(vty, "%% No such PDP context found%s", VTY_NEWLINE);
759 return CMD_WARNING;
760 } else
761 return CMD_SUCCESS;
762}
763
764/* show all (active) PDP contexts within a pool */
765static void ippool_show_pdp_contexts(struct vty *vty, struct ippool_t *pool)
766{
767 unsigned int i;
768
769 if (!pool)
770 return;
771
772 for (i = 0; i < pool->listsize; i++) {
773 struct ippoolm_t *member = &pool->member[i];
774 if (member->inuse == 0)
775 continue;
776 show_one_pdp(vty, member->peer);
777 }
778}
779
780/* show all (active) PDP contexts within an APN */
781static void apn_show_pdp_contexts(struct vty *vty, struct apn_ctx *apn)
782{
783 ippool_show_pdp_contexts(vty, apn->v4.pool);
784 ippool_show_pdp_contexts(vty, apn->v6.pool);
785}
786
787DEFUN(show_pdpctx, show_pdpctx_cmd,
788 "show pdp-context ggsn NAME [apn APN]",
789 SHOW_STR "Show PDP Context Information\n"
790 GGSN_STR "GGSN Name\n") // FIXME
791{
792 struct ggsn_ctx *ggsn;
793 struct apn_ctx *apn;
794
795 ggsn = ggsn_find(argv[0]);
796 if (!ggsn) {
797 vty_out(vty, "%% No such GGSN '%s'%s", argv[0], VTY_NEWLINE);
798 return CMD_WARNING;
799 }
800 if (argc < 2) {
801 llist_for_each_entry(apn, &ggsn->apn_list, list)
802 apn_show_pdp_contexts(vty, apn);
803 } else {
804 apn = ggsn_find_apn(ggsn, argv[1]);
805 if (!apn) {
806 vty_out(vty, "%% No such APN '%s'%s", argv[1], VTY_NEWLINE);
807 return CMD_WARNING;
808 }
809 apn_show_pdp_contexts(vty, apn);
810 }
811
812 return CMD_SUCCESS;
813}
814
815static void show_apn(struct vty *vty, struct apn_ctx *apn)
816{
817 vty_out(vty, " APN: %s%s", apn->cfg.name, VTY_NEWLINE);
818 /* FIXME */
819}
820
821static void show_one_ggsn(struct vty *vty, struct ggsn_ctx *ggsn)
822{
823 struct apn_ctx *apn;
824 vty_out(vty, "GGSN %s: Bound to %s%s", ggsn->cfg.name, in46a_ntoa(&ggsn->cfg.listen_addr),
825 VTY_NEWLINE);
826 /* FIXME */
827
828 llist_for_each_entry(apn, &ggsn->apn_list, list)
829 show_apn(vty, apn);
830}
831
832DEFUN(show_ggsn, show_ggsn_cmd,
833 "show ggsn [NAME]",
834 SHOW_STR "Display information on the GGSN\n")
835{
836 struct ggsn_ctx *ggsn;
837
838 if (argc == 0) {
839 llist_for_each_entry(ggsn, &g_ggsn_list, list)
840 show_one_ggsn(vty, ggsn);
841 } else {
842 ggsn = ggsn_find(argv[0]);
843 if (!ggsn)
844 return CMD_WARNING;
845 show_one_ggsn(vty, ggsn);
846 }
847
848 return CMD_SUCCESS;
849}
850
851int ggsn_vty_init(void)
852{
853 install_element_ve(&show_pdpctx_cmd);
854 install_element_ve(&show_pdpctx_imsi_cmd);
855 install_element_ve(&show_ggsn_cmd);
856
857 install_element(CONFIG_NODE, &cfg_ggsn_cmd);
858 install_element(CONFIG_NODE, &cfg_no_ggsn_cmd);
859 install_node(&ggsn_node, config_write_ggsn);
860 vty_install_default(GGSN_NODE);
861 install_element(GGSN_NODE, &cfg_description_cmd);
862 install_element(GGSN_NODE, &cfg_no_description_cmd);
863 install_element(GGSN_NODE, &cfg_ggsn_shutdown_cmd);
864 install_element(GGSN_NODE, &cfg_ggsn_no_shutdown_cmd);
Harald Welte98146772017-09-05 17:41:20 +0200865 install_element(GGSN_NODE, &cfg_ggsn_bind_ip_cmd);
866 install_element(GGSN_NODE, &cfg_ggsn_gtpc_ip_cmd);
867 install_element(GGSN_NODE, &cfg_ggsn_gtpu_ip_cmd);
Harald Weltedda21ed2017-08-12 15:07:02 +0200868 install_element(GGSN_NODE, &cfg_ggsn_state_dir_cmd);
869 install_element(GGSN_NODE, &cfg_ggsn_apn_cmd);
870 install_element(GGSN_NODE, &cfg_ggsn_no_apn_cmd);
871 install_element(GGSN_NODE, &cfg_ggsn_default_apn_cmd);
872 install_element(GGSN_NODE, &cfg_ggsn_no_default_apn_cmd);
873
874 install_node(&apn_node, NULL);
875 vty_install_default(APN_NODE);
876 install_element(APN_NODE, &cfg_description_cmd);
877 install_element(APN_NODE, &cfg_no_description_cmd);
878 install_element(APN_NODE, &cfg_apn_shutdown_cmd);
879 install_element(APN_NODE, &cfg_apn_no_shutdown_cmd);
880 install_element(APN_NODE, &cfg_apn_gtpu_mode_cmd);
881 install_element(APN_NODE, &cfg_apn_type_support_cmd);
882 install_element(APN_NODE, &cfg_apn_no_type_support_cmd);
883 install_element(APN_NODE, &cfg_apn_tun_dev_name_cmd);
884 install_element(APN_NODE, &cfg_apn_ipup_script_cmd);
885 install_element(APN_NODE, &cfg_apn_no_ipup_script_cmd);
886 install_element(APN_NODE, &cfg_apn_ipdown_script_cmd);
887 install_element(APN_NODE, &cfg_apn_no_ipdown_script_cmd);
888 install_element(APN_NODE, &cfg_apn_ip_prefix_cmd);
889 install_element(APN_NODE, &cfg_apn_ipv6_prefix_cmd);
890 install_element(APN_NODE, &cfg_apn_ip_dns_cmd);
891 install_element(APN_NODE, &cfg_apn_ipv6_dns_cmd);
892 install_element(APN_NODE, &cfg_apn_no_dns_cmd);
893 install_element(APN_NODE, &cfg_apn_ip_ifconfig_cmd);
894 install_element(APN_NODE, &cfg_apn_no_ip_ifconfig_cmd);
895 install_element(APN_NODE, &cfg_apn_ipv6_ifconfig_cmd);
896 install_element(APN_NODE, &cfg_apn_no_ipv6_ifconfig_cmd);
Harald Welte93fed3b2017-09-24 11:43:17 +0800897 install_element(APN_NODE, &cfg_apn_gpdu_seq_cmd);
898 install_element(APN_NODE, &cfg_apn_no_gpdu_seq_cmd);
Harald Weltedda21ed2017-08-12 15:07:02 +0200899
900 return 0;
901}
902
903static int ggsn_vty_is_config_node(struct vty *vty, int node)
904{
905 switch (node) {
906 case GGSN_NODE:
907 case APN_NODE:
908 return 1;
909 default:
910 return 0;
911 }
912}
913
914static int ggsn_vty_go_parent(struct vty *vty)
915{
916 switch (vty->node) {
917 case GGSN_NODE:
918 vty->node = CONFIG_NODE;
919 vty->index = NULL;
920 vty->index_sub = NULL;
921 break;
922 case APN_NODE:
923 vty->node = GGSN_NODE;
924 {
925 struct apn_ctx *apn = vty->index;
926 vty->index = apn->ggsn;
927 vty->index_sub = &apn->ggsn->cfg.description;
928 }
929 break;
930 }
931
932 return vty->node;
933}
934
935static const char ggsn_copyright[] =
936 "Copyright (C) 2011-2017 Harald Welte <laforge@gnumonks.org>\r\n"
937 "Copyright (C) 2012-2017 Holger Hans Peter Freyther <holger@moiji-mobile.com>\r\n"
938 "Copyright (C) 2012-2017 sysmocom - s.f.m.c. GmbH\r\n"
939 "Copyright (C) 2002-2005 Mondru AB\r\n"
940 "License GPLv2: GNU GPL version 2 <http://gnu.org/licenses/gpl-2.0.html>\r\n"
941 "This is free software: you are free to change and redistribute it.\r\n"
942 "There is NO WARRANTY, to the extent permitted by law.\r\n";
943
944struct vty_app_info g_vty_info = {
Harald Welte632e8432017-09-05 18:12:14 +0200945 .name = "OsmoGGSN",
Harald Weltedda21ed2017-08-12 15:07:02 +0200946 .version = PACKAGE_VERSION,
947 .copyright = ggsn_copyright,
948 .go_parent_cb = ggsn_vty_go_parent,
949 .is_config_node = ggsn_vty_is_config_node,
950};