blob: 6ddfc0d1325e350590d620ed96240568d4abc6be [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
Pau Espin Pedrol37c45e32017-12-14 14:09:13 +0100516DEFUN(cfg_apn_ipv6_linklocal, cfg_apn_ipv6_linklocal_cmd,
517 "ipv6 link-local X:X::X:X/M",
518 IP6_STR IFCONFIG_STR "IPv6 Link-local Adress/Prefix-Length\n")
519{
520 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
521 str2prefix(&apn->v6.cfg.ll_prefix, argv[0]);
522 return CMD_SUCCESS;
523}
524
525DEFUN(cfg_apn_no_ipv6_linklocal, cfg_apn_no_ipv6_linklocal_cmd,
526 "no ipv6 link-local",
527 NO_STR IP6_STR IFCONFIG_STR)
528{
529 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
530 memset(&apn->v6.cfg.ll_prefix, 0, sizeof(apn->v6.cfg.ll_prefix));
531 return CMD_SUCCESS;
532}
533
Harald Weltedda21ed2017-08-12 15:07:02 +0200534#define DNS_STRINGS "Configure DNS Server\n" "primary/secondary DNS\n" "IP address of DNS Sever\n"
535
536DEFUN(cfg_apn_ip_dns, cfg_apn_ip_dns_cmd,
537 "ip dns <0-1> A.B.C.D",
538 IP_STR DNS_STRINGS)
539{
540 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
541 int idx = atoi(argv[0]);
542 size_t dummy;
543
544 ippool_aton(&apn->v4.cfg.dns[idx], &dummy, argv[1], 0);
545
546 return CMD_SUCCESS;
547}
548
549DEFUN(cfg_apn_ipv6_dns, cfg_apn_ipv6_dns_cmd,
550 "ipv6 dns <0-1> X:X::X:X",
551 IP6_STR DNS_STRINGS)
552{
553 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
554 int idx = atoi(argv[0]);
555 size_t dummy;
556
557 ippool_aton(&apn->v6.cfg.dns[idx], &dummy, argv[1], 0);
558
559 return CMD_SUCCESS;
560}
561
562DEFUN(cfg_apn_no_dns, cfg_apn_no_dns_cmd,
563 "no (ip|ipv6) dns <0-1>",
564 NO_STR IP_STR IP6_STR "Disable DNS Server\n" "primary/secondary DNS\n")
565{
566 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
567 struct in46_addr *a;
568 int idx = atoi(argv[1]);
569
570 if (!strcmp(argv[0], "ip"))
571 a = &apn->v4.cfg.dns[idx];
572 else
573 a = &apn->v6.cfg.dns[idx];
574
575 memset(a, 0, sizeof(*a));
576
577 return CMD_SUCCESS;
578}
579
Harald Welte93fed3b2017-09-24 11:43:17 +0800580DEFUN(cfg_apn_gpdu_seq, cfg_apn_gpdu_seq_cmd,
581 "g-pdu tx-sequence-numbers",
582 "G-PDU Configuration\n" "Enable transmission of G-PDU sequence numbers\n")
583{
584 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
585 apn->cfg.tx_gpdu_seq = true;
586 return CMD_SUCCESS;
587}
588
589DEFUN(cfg_apn_no_gpdu_seq, cfg_apn_no_gpdu_seq_cmd,
590 "no g-pdu tx-sequence-numbers",
591 NO_STR "G-PDU Configuration\n" "Disable transmission of G-PDU sequence numbers\n")
592{
593 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
594 apn->cfg.tx_gpdu_seq = false;
595 return CMD_SUCCESS;
596}
597
Harald Weltedda21ed2017-08-12 15:07:02 +0200598DEFUN(cfg_apn_shutdown, cfg_apn_shutdown_cmd,
599 "shutdown",
600 "Put the APN in administrative shut-down\n")
601{
602 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
603
604 if (!apn->cfg.shutdown) {
605 if (apn_stop(apn, false)) {
606 vty_out(vty, "%% Failed to Stop APN%s", VTY_NEWLINE);
607 return CMD_WARNING;
608 }
609 apn->cfg.shutdown = true;
610 }
611
612 return CMD_SUCCESS;
613}
614
615DEFUN(cfg_apn_no_shutdown, cfg_apn_no_shutdown_cmd,
616 "no shutdown",
617 NO_STR "Remove the APN from administrative shut-down\n")
618{
619 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
620
621 if (apn->cfg.shutdown) {
622 if (apn_start(apn) < 0) {
623 vty_out(vty, "%% Failed to start APN, check log for details%s", VTY_NEWLINE);
624 return CMD_WARNING;
625 }
626 apn->cfg.shutdown = false;
627 }
628
629 return CMD_SUCCESS;
630}
631
632
633static void vty_dump_prefix(struct vty *vty, const char *pre, const struct in46_prefix *pfx)
634{
635 vty_out(vty, "%s %s%s", pre, in46p_ntoa(pfx), VTY_NEWLINE);
636}
637
638static void config_write_apn(struct vty *vty, struct apn_ctx *apn)
639{
640 unsigned int i;
641
642 vty_out(vty, " apn %s%s", apn->cfg.name, VTY_NEWLINE);
643 if (apn->cfg.description)
644 vty_out(vty, " description %s%s", apn->cfg.description, VTY_NEWLINE);
645 vty_out(vty, " gtpu-mode %s%s", get_value_string(apn_gtpu_mode_names, apn->cfg.gtpu_mode),
646 VTY_NEWLINE);
647 if (apn->tun.cfg.dev_name)
648 vty_out(vty, " tun-device %s%s", apn->tun.cfg.dev_name, VTY_NEWLINE);
649 if (apn->tun.cfg.ipup_script)
650 vty_out(vty, " ipup-script %s%s", apn->tun.cfg.ipup_script, VTY_NEWLINE);
651 if (apn->tun.cfg.ipdown_script)
652 vty_out(vty, " ipdown-script %s%s", apn->tun.cfg.ipdown_script, VTY_NEWLINE);
653
654 for (i = 0; i < 32; i++) {
655 if (!(apn->cfg.apn_type_mask & (1 << i)))
656 continue;
657 vty_out(vty, " type-support %s%s", get_value_string(pdp_type_names, (1 << i)),
658 VTY_NEWLINE);
659 }
660
Harald Welte93fed3b2017-09-24 11:43:17 +0800661 if (!apn->cfg.tx_gpdu_seq)
662 vty_out(vty, " no g-pdu tx-sequence-numbers%s", VTY_NEWLINE);
663
Harald Weltedda21ed2017-08-12 15:07:02 +0200664 /* IPv4 prefixes + DNS */
665 if (apn->v4.cfg.static_prefix.addr.len)
666 vty_dump_prefix(vty, " ip prefix static", &apn->v4.cfg.static_prefix);
667 if (apn->v4.cfg.dynamic_prefix.addr.len)
668 vty_dump_prefix(vty, " ip prefix dynamic", &apn->v4.cfg.dynamic_prefix);
669 for (i = 0; i < ARRAY_SIZE(apn->v4.cfg.dns); i++) {
670 if (!apn->v4.cfg.dns[i].len)
671 continue;
672 vty_out(vty, " ip dns %u %s%s", i, in46a_ntoa(&apn->v4.cfg.dns[i]), VTY_NEWLINE);
673 }
674 if (apn->v4.cfg.ifconfig_prefix.addr.len)
Harald Welteff438172017-09-24 22:48:46 +0800675 vty_dump_prefix(vty, " ip ifconfig", &apn->v4.cfg.ifconfig_prefix);
Harald Weltedda21ed2017-08-12 15:07:02 +0200676
677 /* IPv6 prefixes + DNS */
678 if (apn->v6.cfg.static_prefix.addr.len)
679 vty_dump_prefix(vty, " ipv6 prefix static", &apn->v6.cfg.static_prefix);
680 if (apn->v6.cfg.dynamic_prefix.addr.len)
681 vty_dump_prefix(vty, " ipv6 prefix dynamic", &apn->v6.cfg.dynamic_prefix);
682 for (i = 0; i < ARRAY_SIZE(apn->v6.cfg.dns); i++) {
683 if (!apn->v6.cfg.dns[i].len)
684 continue;
Harald Welte3ca419a2017-09-24 22:47:51 +0800685 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 +0200686 }
687 if (apn->v6.cfg.ifconfig_prefix.addr.len)
Harald Welteff438172017-09-24 22:48:46 +0800688 vty_dump_prefix(vty, " ipv6 ifconfig", &apn->v6.cfg.ifconfig_prefix);
Pau Espin Pedrole5a082d2017-12-15 15:55:04 +0100689 if (apn->v6.cfg.ll_prefix.addr.len)
690 vty_dump_prefix(vty, " ipv6 link-local", &apn->v6.cfg.ll_prefix);
Harald Weltedda21ed2017-08-12 15:07:02 +0200691
692 /* must be last */
693 vty_out(vty, " %sshutdown%s", apn->cfg.shutdown ? "" : "no ", VTY_NEWLINE);
694}
695
696static int config_write_ggsn(struct vty *vty)
697{
698 struct ggsn_ctx *ggsn;
699
700 llist_for_each_entry(ggsn, &g_ggsn_list, list) {
701 struct apn_ctx *apn;
702 vty_out(vty, "ggsn %s%s", ggsn->cfg.name, VTY_NEWLINE);
703 if (ggsn->cfg.description)
704 vty_out(vty, " description %s%s", ggsn->cfg.description, VTY_NEWLINE);
705 vty_out(vty, " gtp state-dir %s%s", ggsn->cfg.state_dir, VTY_NEWLINE);
Harald Welte98146772017-09-05 17:41:20 +0200706 vty_out(vty, " gtp bind-ip %s%s", in46a_ntoa(&ggsn->cfg.listen_addr), VTY_NEWLINE);
707 if (ggsn->cfg.gtpc_addr.v4.s_addr)
708 vty_out(vty, " gtp control-ip %s%s", in46a_ntoa(&ggsn->cfg.gtpc_addr), VTY_NEWLINE);
709 if (ggsn->cfg.gtpu_addr.v4.s_addr)
710 vty_out(vty, " gtp user-ip %s%s", in46a_ntoa(&ggsn->cfg.gtpu_addr), VTY_NEWLINE);
Harald Weltedda21ed2017-08-12 15:07:02 +0200711 llist_for_each_entry(apn, &ggsn->apn_list, list)
712 config_write_apn(vty, apn);
713 if (ggsn->cfg.default_apn)
714 vty_out(vty, " default-apn %s%s", ggsn->cfg.default_apn->cfg.name, VTY_NEWLINE);
715 /* must be last */
716 vty_out(vty, " %sshutdown ggsn%s", ggsn->cfg.shutdown ? "" : "no ", VTY_NEWLINE);
717 }
718
719 return 0;
720}
721
722static const char *print_gsnaddr(const struct ul16_t *in)
723{
724 struct in46_addr in46;
725
726 in46.len = in->l;
727 OSMO_ASSERT(in->l <= sizeof(in46.v6));
728 memcpy(&in46.v6, in->v, in->l);
729
730 return in46a_ntoa(&in46);
731}
732
733static void show_one_pdp(struct vty *vty, struct pdp_t *pdp)
734{
735 struct in46_addr eua46;
736
737 vty_out(vty, "IMSI: %s, NSAPI: %u, MSISDN: %s%s", imsi_gtp2str(&pdp->imsi), pdp->nsapi,
738 osmo_hexdump_nospc(pdp->msisdn.v, pdp->msisdn.l), VTY_NEWLINE);
739
740 vty_out(vty, " Control: %s:%08x ", print_gsnaddr(&pdp->gsnlc), pdp->teic_own);
741 vty_out(vty, "<-> %s:%08x%s", print_gsnaddr(&pdp->gsnrc), pdp->teic_gn, VTY_NEWLINE);
742
743 vty_out(vty, " Data: %s:%08x ", print_gsnaddr(&pdp->gsnlu), pdp->teid_own);
744 vty_out(vty, "<-> %s:%08x%s", print_gsnaddr(&pdp->gsnru), pdp->teid_gn, VTY_NEWLINE);
745
746 in46a_from_eua(&pdp->eua, &eua46);
747 vty_out(vty, " End-User Address: %s%s", in46a_ntoa(&eua46), VTY_NEWLINE);
Harald Welte93fed3b2017-09-24 11:43:17 +0800748 vty_out(vty, " Transmit GTP Sequence Number for G-PDU: %s%s",
749 pdp->tx_gpdu_seq ? "Yes" : "No", VTY_NEWLINE);
Harald Weltedda21ed2017-08-12 15:07:02 +0200750}
751
752DEFUN(show_pdpctx_imsi, show_pdpctx_imsi_cmd,
753 "show pdp-context imsi IMSI [<0-15>]",
754 SHOW_STR "Display information on PDP Context\n"
755 "PDP contexts for given IMSI\n"
756 "PDP context for given NSAPI\n")
757{
758 uint64_t imsi = strtoull(argv[0], NULL, 10);
759 unsigned int nsapi;
760 struct pdp_t *pdp;
761 int num_found = 0;
762
763 if (argc > 1) {
764 nsapi = atoi(argv[1]);
765 if (pdp_getimsi(&pdp, imsi, nsapi)) {
766 show_one_pdp(vty, pdp);
767 num_found++;
768 }
769 } else {
770 for (nsapi = 0; nsapi < PDP_MAXNSAPI; nsapi++) {
771 if (pdp_getimsi(&pdp, imsi, nsapi))
772 continue;
773 show_one_pdp(vty, pdp);
774 num_found++;
775 }
776 }
777 if (num_found == 0) {
778 vty_out(vty, "%% No such PDP context found%s", VTY_NEWLINE);
779 return CMD_WARNING;
780 } else
781 return CMD_SUCCESS;
782}
783
784/* show all (active) PDP contexts within a pool */
785static void ippool_show_pdp_contexts(struct vty *vty, struct ippool_t *pool)
786{
787 unsigned int i;
788
789 if (!pool)
790 return;
791
792 for (i = 0; i < pool->listsize; i++) {
793 struct ippoolm_t *member = &pool->member[i];
794 if (member->inuse == 0)
795 continue;
796 show_one_pdp(vty, member->peer);
797 }
798}
799
800/* show all (active) PDP contexts within an APN */
801static void apn_show_pdp_contexts(struct vty *vty, struct apn_ctx *apn)
802{
803 ippool_show_pdp_contexts(vty, apn->v4.pool);
804 ippool_show_pdp_contexts(vty, apn->v6.pool);
805}
806
807DEFUN(show_pdpctx, show_pdpctx_cmd,
808 "show pdp-context ggsn NAME [apn APN]",
809 SHOW_STR "Show PDP Context Information\n"
810 GGSN_STR "GGSN Name\n") // FIXME
811{
812 struct ggsn_ctx *ggsn;
813 struct apn_ctx *apn;
814
815 ggsn = ggsn_find(argv[0]);
816 if (!ggsn) {
817 vty_out(vty, "%% No such GGSN '%s'%s", argv[0], VTY_NEWLINE);
818 return CMD_WARNING;
819 }
820 if (argc < 2) {
821 llist_for_each_entry(apn, &ggsn->apn_list, list)
822 apn_show_pdp_contexts(vty, apn);
823 } else {
824 apn = ggsn_find_apn(ggsn, argv[1]);
825 if (!apn) {
826 vty_out(vty, "%% No such APN '%s'%s", argv[1], VTY_NEWLINE);
827 return CMD_WARNING;
828 }
829 apn_show_pdp_contexts(vty, apn);
830 }
831
832 return CMD_SUCCESS;
833}
834
835static void show_apn(struct vty *vty, struct apn_ctx *apn)
836{
837 vty_out(vty, " APN: %s%s", apn->cfg.name, VTY_NEWLINE);
838 /* FIXME */
839}
840
841static void show_one_ggsn(struct vty *vty, struct ggsn_ctx *ggsn)
842{
843 struct apn_ctx *apn;
844 vty_out(vty, "GGSN %s: Bound to %s%s", ggsn->cfg.name, in46a_ntoa(&ggsn->cfg.listen_addr),
845 VTY_NEWLINE);
846 /* FIXME */
847
848 llist_for_each_entry(apn, &ggsn->apn_list, list)
849 show_apn(vty, apn);
850}
851
852DEFUN(show_ggsn, show_ggsn_cmd,
853 "show ggsn [NAME]",
854 SHOW_STR "Display information on the GGSN\n")
855{
856 struct ggsn_ctx *ggsn;
857
858 if (argc == 0) {
859 llist_for_each_entry(ggsn, &g_ggsn_list, list)
860 show_one_ggsn(vty, ggsn);
861 } else {
862 ggsn = ggsn_find(argv[0]);
863 if (!ggsn)
864 return CMD_WARNING;
865 show_one_ggsn(vty, ggsn);
866 }
867
868 return CMD_SUCCESS;
869}
870
871int ggsn_vty_init(void)
872{
873 install_element_ve(&show_pdpctx_cmd);
874 install_element_ve(&show_pdpctx_imsi_cmd);
875 install_element_ve(&show_ggsn_cmd);
876
877 install_element(CONFIG_NODE, &cfg_ggsn_cmd);
878 install_element(CONFIG_NODE, &cfg_no_ggsn_cmd);
Pau Espin Pedrol840ce8a2017-11-16 17:01:44 +0100879
Harald Weltedda21ed2017-08-12 15:07:02 +0200880 install_node(&ggsn_node, config_write_ggsn);
Harald Weltedda21ed2017-08-12 15:07:02 +0200881 install_element(GGSN_NODE, &cfg_description_cmd);
882 install_element(GGSN_NODE, &cfg_no_description_cmd);
883 install_element(GGSN_NODE, &cfg_ggsn_shutdown_cmd);
884 install_element(GGSN_NODE, &cfg_ggsn_no_shutdown_cmd);
Harald Welte98146772017-09-05 17:41:20 +0200885 install_element(GGSN_NODE, &cfg_ggsn_bind_ip_cmd);
886 install_element(GGSN_NODE, &cfg_ggsn_gtpc_ip_cmd);
887 install_element(GGSN_NODE, &cfg_ggsn_gtpu_ip_cmd);
Harald Weltedda21ed2017-08-12 15:07:02 +0200888 install_element(GGSN_NODE, &cfg_ggsn_state_dir_cmd);
889 install_element(GGSN_NODE, &cfg_ggsn_apn_cmd);
890 install_element(GGSN_NODE, &cfg_ggsn_no_apn_cmd);
891 install_element(GGSN_NODE, &cfg_ggsn_default_apn_cmd);
892 install_element(GGSN_NODE, &cfg_ggsn_no_default_apn_cmd);
893
894 install_node(&apn_node, NULL);
Harald Weltedda21ed2017-08-12 15:07:02 +0200895 install_element(APN_NODE, &cfg_description_cmd);
896 install_element(APN_NODE, &cfg_no_description_cmd);
897 install_element(APN_NODE, &cfg_apn_shutdown_cmd);
898 install_element(APN_NODE, &cfg_apn_no_shutdown_cmd);
899 install_element(APN_NODE, &cfg_apn_gtpu_mode_cmd);
900 install_element(APN_NODE, &cfg_apn_type_support_cmd);
901 install_element(APN_NODE, &cfg_apn_no_type_support_cmd);
902 install_element(APN_NODE, &cfg_apn_tun_dev_name_cmd);
903 install_element(APN_NODE, &cfg_apn_ipup_script_cmd);
904 install_element(APN_NODE, &cfg_apn_no_ipup_script_cmd);
905 install_element(APN_NODE, &cfg_apn_ipdown_script_cmd);
906 install_element(APN_NODE, &cfg_apn_no_ipdown_script_cmd);
907 install_element(APN_NODE, &cfg_apn_ip_prefix_cmd);
908 install_element(APN_NODE, &cfg_apn_ipv6_prefix_cmd);
909 install_element(APN_NODE, &cfg_apn_ip_dns_cmd);
910 install_element(APN_NODE, &cfg_apn_ipv6_dns_cmd);
911 install_element(APN_NODE, &cfg_apn_no_dns_cmd);
912 install_element(APN_NODE, &cfg_apn_ip_ifconfig_cmd);
913 install_element(APN_NODE, &cfg_apn_no_ip_ifconfig_cmd);
914 install_element(APN_NODE, &cfg_apn_ipv6_ifconfig_cmd);
915 install_element(APN_NODE, &cfg_apn_no_ipv6_ifconfig_cmd);
Pau Espin Pedrol37c45e32017-12-14 14:09:13 +0100916 install_element(APN_NODE, &cfg_apn_ipv6_linklocal_cmd);
917 install_element(APN_NODE, &cfg_apn_no_ipv6_linklocal_cmd);
Harald Welte93fed3b2017-09-24 11:43:17 +0800918 install_element(APN_NODE, &cfg_apn_gpdu_seq_cmd);
919 install_element(APN_NODE, &cfg_apn_no_gpdu_seq_cmd);
Harald Weltedda21ed2017-08-12 15:07:02 +0200920
921 return 0;
922}
923
924static int ggsn_vty_is_config_node(struct vty *vty, int node)
925{
926 switch (node) {
927 case GGSN_NODE:
928 case APN_NODE:
929 return 1;
930 default:
931 return 0;
932 }
933}
934
935static int ggsn_vty_go_parent(struct vty *vty)
936{
937 switch (vty->node) {
938 case GGSN_NODE:
939 vty->node = CONFIG_NODE;
940 vty->index = NULL;
941 vty->index_sub = NULL;
942 break;
943 case APN_NODE:
944 vty->node = GGSN_NODE;
945 {
946 struct apn_ctx *apn = vty->index;
947 vty->index = apn->ggsn;
948 vty->index_sub = &apn->ggsn->cfg.description;
949 }
950 break;
951 }
952
953 return vty->node;
954}
955
956static const char ggsn_copyright[] =
957 "Copyright (C) 2011-2017 Harald Welte <laforge@gnumonks.org>\r\n"
958 "Copyright (C) 2012-2017 Holger Hans Peter Freyther <holger@moiji-mobile.com>\r\n"
959 "Copyright (C) 2012-2017 sysmocom - s.f.m.c. GmbH\r\n"
960 "Copyright (C) 2002-2005 Mondru AB\r\n"
961 "License GPLv2: GNU GPL version 2 <http://gnu.org/licenses/gpl-2.0.html>\r\n"
962 "This is free software: you are free to change and redistribute it.\r\n"
963 "There is NO WARRANTY, to the extent permitted by law.\r\n";
964
965struct vty_app_info g_vty_info = {
Harald Welte632e8432017-09-05 18:12:14 +0200966 .name = "OsmoGGSN",
Harald Weltedda21ed2017-08-12 15:07:02 +0200967 .version = PACKAGE_VERSION,
968 .copyright = ggsn_copyright,
969 .go_parent_cb = ggsn_vty_go_parent,
970 .is_config_node = ggsn_vty_is_config_node,
971};