aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ggsn/ggsn.c16
-rw-r--r--lib/tun.c105
-rw-r--r--lib/tun.h7
3 files changed, 111 insertions, 17 deletions
diff --git a/ggsn/ggsn.c b/ggsn/ggsn.c
index 991b54c..05a56ae 100644
--- a/ggsn/ggsn.c
+++ b/ggsn/ggsn.c
@@ -70,7 +70,7 @@ int end = 0;
int maxfd = 0; /* For select() */
struct in_addr listen_;
-struct in_addr netaddr, destaddr, net; /* Network interface */
+struct in46_addr netaddr, destaddr, net; /* Network interface */
size_t prefixlen;
struct in46_addr dns1, dns2; /* PCO DNS address */
char *ipup, *ipdown; /* Filename of scripts */
@@ -547,16 +547,16 @@ int main(int argc, char **argv)
/* net */
/* Store net as in_addr net and mask */
if (args_info.net_arg) {
- struct in46_addr in46;
- if (ippool_aton(&in46, &prefixlen, args_info.net_arg, 0)) {
+ if (ippool_aton(&net, &prefixlen, args_info.net_arg, 0)) {
SYS_ERR(DGGSN, LOGL_ERROR, 0,
"Invalid network address: %s!",
args_info.net_arg);
exit(1);
}
- net.s_addr = in46.v4.s_addr;
- netaddr.s_addr = htonl(ntohl(net.s_addr) + 1);
- destaddr.s_addr = htonl(ntohl(net.s_addr) + 1);
+ /* default for network + destination address = net + 1 */
+ netaddr = net;
+ in46a_inc(&netaddr);
+ destaddr = netaddr;
} else {
SYS_ERR(DGGSN, LOGL_ERROR, 0,
"Network address must be specified: %s!",
@@ -710,7 +710,7 @@ int main(int argc, char **argv)
maxfd = gsn->fd1u;
/* use GTP kernel module for data packet encapsulation */
- if (gtp_kernel_init(gsn, &net, prefixlen, &args_info) < 0)
+ if (gtp_kernel_init(gsn, &net.v4, prefixlen, &args_info) < 0)
goto err;
gtp_set_cb_data_ind(gsn, encaps_tun);
@@ -735,7 +735,7 @@ int main(int argc, char **argv)
}
DEBUGP(DGGSN, "Setting tun IP address\n");
- if (tun_setaddr(tun, &netaddr, &destaddr, &prefixlen)) {
+ if (tun_setaddr(tun, &netaddr, &destaddr, prefixlen)) {
SYS_ERR(DGGSN, LOGL_ERROR, 0, "Failed to set tun IP address");
exit(1);
}
diff --git a/lib/tun.c b/lib/tun.c
index 8325f5d..7e0af12 100644
--- a/lib/tun.c
+++ b/lib/tun.c
@@ -1,6 +1,7 @@
/*
* TUN interface functions.
* Copyright (C) 2002, 2003, 2004 Mondru AB.
+ * Copyright (C) 2017 by Harald Welte <laforge@gnumonks.org>
*
* The contents of this file may be used under the terms of the GNU
* General Public License Version 2, provided that the above copyright
@@ -65,8 +66,13 @@
#include "tun.h"
#include "syserr.h"
+static int tun_setaddr4(struct tun_t *this, struct in_addr *addr,
+ struct in_addr *dstaddr, struct in_addr *netmask);
+
#if defined(__linux__)
+#include <linux/ipv6.h>
+
int tun_nlattr(struct nlmsghdr *n, int nsize, int type, void *d, int dlen)
{
int len = RTA_LENGTH(dlen);
@@ -247,7 +253,7 @@ int tun_addaddr(struct tun_t *this,
struct msghdr msg;
if (!this->addrs) /* Use ioctl for first addr to make ping work */
- return tun_setaddr(this, addr, dstaddr, netmask);
+ return tun_setaddr4(this, addr, dstaddr, netmask);
memset(&req, 0, sizeof(req));
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
@@ -345,7 +351,7 @@ int tun_addaddr(struct tun_t *this,
/* TODO: Is this needed on FreeBSD? */
if (!this->addrs) /* Use ioctl for first addr to make ping work */
- return tun_setaddr(this, addr, dstaddr, netmask); /* TODO dstaddr */
+ return tun_setaddr4(this, addr, dstaddr, netmask); /* TODO dstaddr */
memset(&areq, 0, sizeof(areq));
@@ -391,7 +397,7 @@ int tun_addaddr(struct tun_t *this,
#elif defined (__sun__)
if (!this->addrs) /* Use ioctl for first addr to make ping work */
- return tun_setaddr(this, addr, dstaddr, netmask);
+ return tun_setaddr4(this, addr, dstaddr, netmask);
SYS_ERR(DTUN, LOGL_ERROR, errno,
"Setting multiple addresses not possible on Solaris");
@@ -403,9 +409,8 @@ int tun_addaddr(struct tun_t *this,
}
-int tun_setaddr(struct tun_t *this,
- struct in_addr *addr,
- struct in_addr *dstaddr, struct in_addr *netmask)
+static int tun_setaddr4(struct tun_t *this, struct in_addr *addr,
+ struct in_addr *dstaddr, struct in_addr *netmask)
{
struct ifreq ifr;
int fd;
@@ -498,13 +503,99 @@ int tun_setaddr(struct tun_t *this,
tun_sifflags(this, IFF_UP | IFF_RUNNING);
#if defined(__FreeBSD__) || defined (__APPLE__)
- tun_addroute(this, dstaddr, addr, netmask);
+ tun_addroute(this, dstaddr, addr, &this->netmask);
+ this->routes = 1;
+#endif
+
+ return 0;
+}
+
+static int tun_setaddr6(struct tun_t *this, struct in6_addr *addr, struct in6_addr *dstaddr,
+ size_t prefixlen)
+{
+ struct in6_ifreq ifr;
+ struct ifreq namereq;
+ int fd;
+
+ memset(&ifr, 0, sizeof(ifr));
+ memset(&namereq, 0, sizeof(namereq));
+
+ /* Create a channel to the NET kernel */
+ if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ SYS_ERR(DTUN, LOGL_ERROR, 0, "socket() failed");
+ return -1;
+ }
+
+ /* get interface index */
+ strncpy(namereq.ifr_name, this->devname, IFNAMSIZ);
+ namereq.ifr_name[IFNAMSIZ-1] = 0;
+ if (ioctl(fd, SIOGIFINDEX, &namereq) < 0) {
+ SYS_ERR(DTUN, LOGL_ERROR, 0, "Could not get ifindex");
+ return -1;
+ }
+ ifr.ifr6_ifindex = namereq.ifr_ifindex;
+ ifr.ifr6_prefixlen = prefixlen;
+
+ if (addr) {
+ memcpy(&this->addr, addr, sizeof(*addr));
+ memcpy(&ifr.ifr6_addr, addr, sizeof(*addr));
+ if (ioctl(fd, SIOCSIFADDR, (void *) &ifr) < 0) {
+ if (errno != EEXIST) {
+ SYS_ERR(DTUN, LOGL_ERROR, 0, "ioctl(SIOCSIFADDR) failed");
+ } else {
+ SYS_ERR(DTUN, LOGL_NOTICE, 0, "ioctl(SIOCSIFADDR): Address alreadsy exists");
+ }
+ close(fd);
+ return -1;
+ }
+ }
+
+#if 0
+ /* FIXME: looks like this is not possible/necessary for IPv6? */
+ if (dstaddr) {
+ memcpy(&this->dstaddr, dstaddr, sizeof(*dstaddr));
+ memcpy(&ifr.ifr6_addr, dstaddr, sizeof(*dstaddr));
+ if (ioctl(fd, SIOCSIFDSTADDR, (caddr_t *) &ifr) < 0) {
+ SYS_ERR(DTUN, LOGL_ERROR, "ioctl(SIOCSIFDSTADDR) failed");
+ close(fd);
+ return -1;
+ }
+ }
+#endif
+
+ close(fd);
+ this->addrs++;
+
+ /* On linux the route to the interface is set automatically
+ on FreeBSD we have to do this manually */
+
+ /* TODO: How does it work on Solaris? */
+
+ tun_sifflags(this, IFF_UP | IFF_RUNNING);
+
+#if 0 /* FIXME */
+//#if defined(__FreeBSD__) || defined (__APPLE__)
+ tun_addroute6(this, dstaddr, addr, prefixlen);
this->routes = 1;
#endif
return 0;
}
+int tun_setaddr(struct tun_t *this, struct in46_addr *addr, struct in46_addr *dstaddr, size_t prefixlen)
+{
+ struct in_addr netmask;
+ switch (addr->len) {
+ case 4:
+ netmask.s_addr = htonl(0xffffffff << (32 - prefixlen));
+ return tun_setaddr4(this, &addr->v4, dstaddr ? &dstaddr->v4 : NULL, &netmask);
+ case 16:
+ return tun_setaddr6(this, &addr->v6, dstaddr ? &dstaddr->v6 : NULL, prefixlen);
+ default:
+ return -1;
+ }
+}
+
int tun_route(struct tun_t *this,
struct in_addr *dst,
struct in_addr *gateway, struct in_addr *mask, int delete)
diff --git a/lib/tun.h b/lib/tun.h
index c50bdf9..1cd0767 100644
--- a/lib/tun.h
+++ b/lib/tun.h
@@ -1,6 +1,7 @@
/*
* TUN interface functions.
* Copyright (C) 2002, 2003 Mondru AB.
+ * Copyright (C) 2017 by Harald Welte <laforge@gnumonks.org>
*
* The contents of this file may be used under the terms of the GNU
* General Public License Version 2, provided that the above copyright
@@ -12,6 +13,8 @@
#ifndef _TUN_H
#define _TUN_H
+#include "../lib/in46_addr.h"
+
#define PACKET_MAX 8196 /* Maximum packet size we receive */
#define TUN_SCRIPTSIZE 256
#define TUN_ADDRSIZE 128
@@ -66,8 +69,8 @@ extern int tun_encaps(struct tun_t *tun, void *pack, unsigned len);
extern int tun_addaddr(struct tun_t *this, struct in_addr *addr,
struct in_addr *dstaddr, struct in_addr *netmask);
-extern int tun_setaddr(struct tun_t *this, struct in_addr *our_adr,
- struct in_addr *his_adr, struct in_addr *net_mask);
+extern int tun_setaddr(struct tun_t *this, struct in46_addr *our_adr,
+ struct in46_addr *his_adr, size_t prefixlen);
int tun_addroute(struct tun_t *this, struct in_addr *dst,
struct in_addr *gateway, struct in_addr *mask);