diff options
-rw-r--r-- | ggsn/ggsn.c | 16 | ||||
-rw-r--r-- | lib/tun.c | 105 | ||||
-rw-r--r-- | lib/tun.h | 7 |
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); } @@ -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) @@ -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); |