[MERGED] openggsn[master]: Support setting TUN device IPv6 address + prefix

This is merely a historical archive of years 2008-2021, before the migration to mailman3.

A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.

Harald Welte gerrit-no-reply at lists.osmocom.org
Fri Aug 11 11:13:27 UTC 2017


Harald Welte has submitted this change and it was merged.

Change subject: Support setting TUN device IPv6 address + prefix
......................................................................


Support setting TUN device IPv6 address + prefix

As we can now have PDP contexts with IPv6 user IP payload,
it is useful to extend the TUN related code to be able to
configure the tun device IPv6 address + prefix length

Change-Id: I899d21e52d02e0b8384af29ddd489ff19c8f2cf6
---
M ggsn/ggsn.c
M lib/tun.c
M lib/tun.h
3 files changed, 127 insertions(+), 19 deletions(-)

Approvals:
  Harald Welte: Looks good to me, approved
  Jenkins Builder: Verified



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 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 @@
 	/* 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 @@
 		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 @@
 	}
 
 	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..11ea7bc 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 at 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
@@ -37,16 +38,17 @@
 #include <sys/socket.h>
 #include <errno.h>
 #include <net/route.h>
+#include <net/if.h>
 
 #if defined(__linux__)
-#include <linux/if.h>
 #include <linux/if_tun.h>
 #include <linux/netlink.h>
 #include <linux/rtnetlink.h>
 
 #elif defined (__FreeBSD__)
-#include <net/if.h>
 #include <net/if_tun.h>
+#include <net/if_var.h>
+#include <netinet/in_var.h>
 
 #elif defined (__APPLE__)
 #include <net/if.h>
@@ -65,7 +67,12 @@
 #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)
 {
@@ -247,7 +254,7 @@
 	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 +352,7 @@
 
 	/* 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 +398,7 @@
 #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 +410,8 @@
 
 }
 
-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 +504,112 @@
 	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;
+	int fd;
+
+	memset(&ifr, 0, sizeof(ifr));
+
+#if defined(__linux__)
+	ifr.ifr6_prefixlen = prefixlen;
+	ifr.ifr6_ifindex = if_nametoindex(this->devname);
+	if (ifr.ifr6_ifindex == 0) {
+		SYS_ERR(DTUN, LOGL_ERROR, 0, "Error getting ifindex for %s\n", this->devname);
+		return -1;
+	}
+#elif defined(__FreeBSD__) || defined (__APPLE__)
+	strncpy(ifr.ifr_name, this->devname, IFNAMSIZ);
+#endif
+
+	/* 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;
+	}
+
+#if defined(__linux__)
+	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
+
+#elif defined(__FreeBSD__) || defined (__APPLE__)
+	if (addr)
+		memcpy(&ifr.ifr_ifru.ifru_addr, addr, sizeof(ifr.ifr_ifru.ifru_addr));
+	if (dstaddr)
+		memcpy(&ifr.ifr_ifru.ifru_dstaddr, dstaddr, sizeof(ifr.ifr_ifru.ifru_dstaddr));
+
+	if (ioctl(fd, SIOCSIFADDR_IN6, (struct ifreq *)&ifr) < 0) {
+		SYS_ERR(DTUN, LOGL_ERROR, 0, "ioctl(SIOCSIFADDR_IN6) 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 at 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
@@ -11,6 +12,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
@@ -66,8 +69,8 @@
 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);

-- 
To view, visit https://gerrit.osmocom.org/3408
To unsubscribe, visit https://gerrit.osmocom.org/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I899d21e52d02e0b8384af29ddd489ff19c8f2cf6
Gerrit-PatchSet: 9
Gerrit-Project: openggsn
Gerrit-Branch: master
Gerrit-Owner: Harald Welte <laforge at gnumonks.org>
Gerrit-Reviewer: Harald Welte <laforge at gnumonks.org>
Gerrit-Reviewer: Jenkins Builder



More information about the gerrit-log mailing list