diff options
author | Pablo Neira Ayuso <pablo@netfilter.org> | 2023-11-03 17:58:43 +0100 |
---|---|---|
committer | Oliver Smith <osmith@sysmocom.de> | 2024-02-15 15:14:08 +0100 |
commit | 38b5e239d6b1967c21cbd43a7a281411cd69ed67 (patch) | |
tree | 3958a722c9a54787e601ff6b815dc8b08e2c7c8e /tools/gtp-link.c | |
parent | f3253354e36caa07fba4e8437c58e7d430ccea93 (diff) |
gtp-link: add IPv6 support
Update tool to allow to specify family that is used for the outer network
header in GTP tunnels.
./gtp-link add gtp0 ip # IPv4 GGSN
./gtp-link add gtp0 ip --sgsn # IPv4 SGSN
./gtp-link add gtp0 ip6 # IPv6 GGSN
./gtp-link add gtp0 ip6 --sgsn # IPv6 SGSN
Change-Id: I201c32a1bf9a2ab7a228287590bc7ec19c4997b9
Diffstat (limited to 'tools/gtp-link.c')
-rw-r--r-- | tools/gtp-link.c | 129 |
1 files changed, 97 insertions, 32 deletions
diff --git a/tools/gtp-link.c b/tools/gtp-link.c index d342f2b..feb2efe 100644 --- a/tools/gtp-link.c +++ b/tools/gtp-link.c @@ -40,13 +40,75 @@ #include <libgtpnl/gtpnl.h> +struct gtp_server_sock { + int family; + int fd1; + int fd2; + socklen_t len; + struct { + union { + struct sockaddr_in in; + struct sockaddr_in6 in6; + } fd1; + union { + struct sockaddr_in in; + struct sockaddr_in6 in6; + } fd2; + } sockaddr; +}; + +static void setup_sockaddr_in(struct sockaddr_in *sockaddr, uint16_t port) +{ + sockaddr->sin_family = AF_INET; + sockaddr->sin_port = htons(port); + sockaddr->sin_addr.s_addr = INADDR_ANY; +} + +static void setup_sockaddr_in6(struct sockaddr_in6 *sockaddr, uint16_t port) +{ + sockaddr->sin6_family = AF_INET6; + sockaddr->sin6_port = htons(port); + sockaddr->sin6_addr = in6addr_any; +} + +static int setup_socket(struct gtp_server_sock *gtp_sock, int family) +{ + int fd1 = socket(family, SOCK_DGRAM, 0); + int fd2 = socket(family, SOCK_DGRAM, 0); + + if (fd1 < 0 || fd2 < 0) + return -1; + + memset(gtp_sock, 0, sizeof(*gtp_sock)); + + gtp_sock->family = family; + gtp_sock->fd1 = fd1; + gtp_sock->fd2 = fd2; + + switch (family) { + case AF_INET: + gtp_sock->len = sizeof(struct sockaddr_in); + setup_sockaddr_in(>p_sock->sockaddr.fd1.in, 3386); + setup_sockaddr_in(>p_sock->sockaddr.fd2.in, 2152); + break; + case AF_INET6: + gtp_sock->len = sizeof(struct sockaddr_in6); + setup_sockaddr_in6(>p_sock->sockaddr.fd1.in6, 3386); + setup_sockaddr_in6(>p_sock->sockaddr.fd2.in6, 2152); + break; + } + + return 0; +} + int main(int argc, char *argv[]) { char buf[MNL_SOCKET_BUFFER_SIZE]; - int ret, sgsn_mode = 0; + struct gtp_server_sock gtp_sock; + int ret, sgsn_mode = 0, family; if (argc < 3) { - printf("Usage: %s add <device> [--sgsn]\n", argv[0]); + printf("Usage: %s add <device> <family> [--sgsn]\n", argv[0]); printf(" %s del <device>\n", argv[0]); exit(EXIT_FAILURE); } @@ -57,43 +119,44 @@ int main(int argc, char *argv[]) perror("gtp_dev_destroy"); return 0; + } else if (!strcmp(argv[1], "add")) { + if (argc < 4) { + printf("Usage: %s add <device> <family> [--sgsn]\n", argv[0]); + exit(EXIT_FAILURE); + } + + if (argc == 5 && !strcmp(argv[4], "--sgsn")) + sgsn_mode = 1; + } + + if (!strcmp(argv[3], "ip")) + family = AF_INET; + else if (!strcmp(argv[3], "ip6")) + family = AF_INET6; + else { + fprintf(stderr, "unsupport family `%s', expecting `ip' or `ip6'\n", + argv[3]); + exit(EXIT_FAILURE); + } + + if (setup_socket(>p_sock, family) < 0) { + perror("socket"); + exit(EXIT_FAILURE); } - if (argc > 3 && !strcmp(argv[3], "--sgsn")) - sgsn_mode = 1; - - int fd1 = socket(AF_INET, SOCK_DGRAM, 0); - int fd2 = socket(AF_INET, SOCK_DGRAM, 0); - struct sockaddr_in sockaddr_fd1 = { - .sin_family = AF_INET, - .sin_port = htons(3386), - .sin_addr = { - .s_addr = INADDR_ANY, - }, - }; - struct sockaddr_in sockaddr_fd2 = { - .sin_family = AF_INET, - .sin_port = htons(2152), - .sin_addr = { - .s_addr = INADDR_ANY, - }, - }; - - if (bind(fd1, (struct sockaddr *) &sockaddr_fd1, - sizeof(sockaddr_fd1)) < 0) { + if (bind(gtp_sock.fd1, (struct sockaddr *) >p_sock.sockaddr.fd1, gtp_sock.len) < 0) { perror("bind"); exit(EXIT_FAILURE); } - if (bind(fd2, (struct sockaddr *) &sockaddr_fd2, - sizeof(sockaddr_fd2)) < 0) { + if (bind(gtp_sock.fd2, (struct sockaddr *) >p_sock.sockaddr.fd2, gtp_sock.len) < 0) { perror("bind"); exit(EXIT_FAILURE); } if (sgsn_mode) - ret = gtp_dev_create_sgsn(-1, argv[2], fd1, fd2); + ret = gtp_dev_create_sgsn(-1, argv[2], gtp_sock.fd1, gtp_sock.fd2); else - ret = gtp_dev_create(-1, argv[2], fd1, fd2); + ret = gtp_dev_create(-1, argv[2], gtp_sock.fd1, gtp_sock.fd2); if (ret < 0) { perror("cannot create GTP device\n"); exit(EXIT_FAILURE); @@ -103,11 +166,13 @@ int main(int argc, char *argv[]) "this process running for testing purposes.\n"); while (1) { - struct sockaddr_in addr; - socklen_t len = sizeof(addr); + union { + struct sockaddr_in addr; + struct sockaddr_in6 addr6; + } sock; - ret = recvfrom(fd1, buf, sizeof(buf), 0, - (struct sockaddr *)&addr, &len); + ret = recvfrom(gtp_sock.fd1, buf, sizeof(buf), 0, + (struct sockaddr *)&sock, >p_sock.len); printf("received %d bytes via UDP socket\n", ret); } |