[MERGED] osmo-ggsn[master]: Set tun_addaddr ipv agnostic and add support for ipv6

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
Thu Dec 14 14:49:12 UTC 2017


Harald Welte has submitted this change and it was merged.

Change subject: Set tun_addaddr ipv agnostic and add support for ipv6
......................................................................


Set tun_addaddr ipv agnostic and add support for ipv6

sgsnemu (the only user of this API so far) has been modified to use the
new API with in46_addr.

FreeBSD code for IPv6 has not been tested.

Change-Id: Ie36afe6eaf393855a4a708000ef4ad0192bf4767
---
M lib/tun.c
M lib/tun.h
M sgsnemu/sgsnemu.c
3 files changed, 193 insertions(+), 18 deletions(-)

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



diff --git a/lib/tun.c b/lib/tun.c
index 3081575..f6d3503 100644
--- a/lib/tun.c
+++ b/lib/tun.c
@@ -295,7 +295,7 @@
 	}
 }
 
-int tun_addaddr(struct tun_t *this,
+static int tun_addaddr4(struct tun_t *this,
 		struct in_addr *addr,
 		struct in_addr *dstaddr, struct in_addr *netmask)
 {
@@ -465,6 +465,185 @@
 
 }
 
+static int tun_addaddr6(struct tun_t *this,
+		struct in6_addr *addr,
+		struct in6_addr *dstaddr, int prefixlen)
+{
+
+#if defined(__linux__)
+	struct {
+		struct nlmsghdr n;
+		struct ifaddrmsg i;
+		char buf[TUN_NLBUFSIZE];
+	} req;
+
+	struct sockaddr_nl local;
+	socklen_t addr_len;
+	int fd;
+	int status;
+
+	struct sockaddr_nl nladdr;
+	struct iovec iov;
+	struct msghdr msg;
+
+	if (!this->addrs)	/* Use ioctl for first addr to make ping work */
+		return tun_setaddr6(this, addr, dstaddr, prefixlen);
+
+	memset(&req, 0, sizeof(req));
+	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
+	req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
+	req.n.nlmsg_type = RTM_NEWADDR;
+	req.i.ifa_family = AF_INET6;
+	req.i.ifa_prefixlen = 64;	/* 64 FOR IPv6 */
+	req.i.ifa_flags = 0;
+	req.i.ifa_scope = RT_SCOPE_HOST;	/* TODO or 0 */
+	req.i.ifa_index = if_nametoindex(this->devname);
+	if (!req.i.ifa_index) {
+		SYS_ERR(DTUN, LOGL_ERROR, errno, "Unable to get ifindex for %s", this->devname);
+		return -1;
+	}
+
+	tun_nlattr(&req.n, sizeof(req), IFA_ADDRESS, addr, sizeof(*addr));
+	if (dstaddr)
+		tun_nlattr(&req.n, sizeof(req), IFA_LOCAL, dstaddr, sizeof(*dstaddr));
+
+	if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0) {
+		SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
+		return -1;
+	}
+
+	memset(&local, 0, sizeof(local));
+	local.nl_family = AF_NETLINK;
+	local.nl_groups = 0;
+
+	if (bind(fd, (struct sockaddr *)&local, sizeof(local)) < 0) {
+		SYS_ERR(DTUN, LOGL_ERROR, errno, "bind() failed");
+		close(fd);
+		return -1;
+	}
+
+	addr_len = sizeof(local);
+	if (getsockname(fd, (struct sockaddr *)&local, &addr_len) < 0) {
+		SYS_ERR(DTUN, LOGL_ERROR, errno,
+			"getsockname() failed");
+		close(fd);
+		return -1;
+	}
+
+	if (addr_len != sizeof(local)) {
+		SYS_ERR(DTUN, LOGL_ERROR, 0,
+			"Wrong address length %d", addr_len);
+		close(fd);
+		return -1;
+	}
+
+	if (local.nl_family != AF_NETLINK) {
+		SYS_ERR(DTUN, LOGL_ERROR, 0,
+			"Wrong address family %d", local.nl_family);
+		close(fd);
+		return -1;
+	}
+
+	iov.iov_base = (void *)&req.n;
+	iov.iov_len = req.n.nlmsg_len;
+
+	msg.msg_name = (void *)&nladdr;
+	msg.msg_namelen = sizeof(nladdr);
+	msg.msg_iov = &iov;
+	msg.msg_iovlen = 1;
+	msg.msg_control = NULL;
+	msg.msg_controllen = 0;
+	msg.msg_flags = 0;
+
+	memset(&nladdr, 0, sizeof(nladdr));
+	nladdr.nl_family = AF_NETLINK;
+	nladdr.nl_pid = 0;
+	nladdr.nl_groups = 0;
+
+	req.n.nlmsg_seq = 0;
+	req.n.nlmsg_flags |= NLM_F_ACK;
+
+	status = sendmsg(fd, &msg, 0);
+	if (status != req.n.nlmsg_len) {
+		SYS_ERR(DTUN, LOGL_ERROR, errno, "sendmsg() failed, returned %d", status);
+		close(fd);
+		return -1;
+	}
+
+	status = tun_sifflags(this, IFF_UP | IFF_RUNNING);
+	if (status == -1) {
+		close(fd);
+		return -1;
+	}
+
+
+	close(fd);
+	this->addrs++;
+	return 0;
+
+#elif defined (__FreeBSD__) || defined (__APPLE__)
+
+	int fd;
+	struct ifaliasreq areq;
+
+	/* TODO: Is this needed on FreeBSD? */
+	if (!this->addrs)	/* Use ioctl for first addr to make ping work */
+		return tun_setaddr6(this, addr, dstaddr, netmask);	/* TODO dstaddr */
+
+	memset(&areq, 0, sizeof(areq));
+
+	/* Set up interface name */
+	strncpy(areq.ifra_name, this->devname, IFNAMSIZ);
+	areq.ifra_name[IFNAMSIZ - 1] = 0;	/* Make sure to terminate */
+
+	((struct sockaddr_in6 *)&areq.ifra_addr)->sin6_family = AF_INET6;
+	((struct sockaddr_in6 *)&areq.ifra_addr)->sin6_len = sizeof(areq.ifra_addr);
+	((struct sockaddr_in6 *)&areq.ifra_addr)->sin6_addr.s6_addr = addr->s6_addr;
+
+	((struct sockaddr_in6 *)&areq.ifra_mask)->sin6_family = AF_INET6;
+	((struct sockaddr_in6 *)&areq.ifra_mask)->sin6_len = sizeof(areq.ifra_mask);
+	((struct sockaddr_in6 *)&areq.ifra_mask)->sin6_addr.s6_addr = netmask->s6_addr;
+
+	/* For some reason FreeBSD uses ifra_broadcast for specifying dstaddr */
+	((struct sockaddr_in6 *)&areq.ifra_broadaddr)->sin6_family = AF_INET6;
+	((struct sockaddr_in6 *)&areq.ifra_broadaddr)->sin6_len = sizeof(areq.ifra_broadaddr);
+	((struct sockaddr_in6 *)&areq.ifra_broadaddr)->sin6_addr.s6_addr = dstaddr->s6_addr;
+
+	/* Create a channel to the NET kernel. */
+	if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+		SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
+		return -1;
+	}
+
+	if (ioctl(fd, SIOCAIFADDR, (void *)&areq) < 0) {
+		SYS_ERR(DTUN, LOGL_ERROR, errno,
+			"ioctl(SIOCAIFADDR) failed");
+		close(fd);
+		return -1;
+	}
+
+	close(fd);
+	this->addrs++;
+	return 0;
+
+#endif
+
+}
+
+int tun_addaddr(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_addaddr4(this, &addr->v4, dstaddr ? &dstaddr->v4 : NULL, &netmask);
+	case 16:
+		return tun_addaddr6(this, &addr->v6, dstaddr ? &dstaddr->v6 : NULL, prefixlen);
+	default:
+		return -1;
+	}
+}
+
 static 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 411deea..6f7c0ff 100644
--- a/lib/tun.h
+++ b/lib/tun.h
@@ -80,8 +80,8 @@
 extern int tun_decaps(struct tun_t *this);
 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_addaddr(struct tun_t *this, struct in46_addr *addr,
+		       struct in46_addr *dstaddr, size_t prefixlen);
 
 extern int tun_setaddr(struct tun_t *this, struct in46_addr *our_adr,
 		       struct in46_addr *his_adr, size_t prefixlen);
diff --git a/sgsnemu/sgsnemu.c b/sgsnemu/sgsnemu.c
index 275c583..bb55b1c 100644
--- a/sgsnemu/sgsnemu.c
+++ b/sgsnemu/sgsnemu.c
@@ -85,7 +85,7 @@
 	int debug;		/* Print debug messages */
 	int createif;		/* Create local network interface */
 	char *tun_dev_name;
-	struct in_addr netaddr, destaddr, net;	/* Network interface  */
+	struct in46_addr netaddr, destaddr, net;	/* Network interface  */
 	size_t prefixlen;
 	char *ipup, *ipdown;	/* Filename of scripts */
 	int defaultroute;	/* Set up default route */
@@ -873,23 +873,21 @@
 	/* net                                                          */
 	/* Store net as in_addr net and mask                            */
 	if (args_info.net_arg) {
-		struct in46_addr in46;
 		if (ippool_aton
-		    (&in46, &options.prefixlen, args_info.net_arg, 0)) {
+		    (&options.net, &options.prefixlen, args_info.net_arg, 0)) {
 			SYS_ERR(DSGSN, LOGL_ERROR, 0,
 				"Invalid network address: %s!",
 				args_info.net_arg);
 			exit(1);
 		}
-		options.net.s_addr = in46.v4.s_addr;
-		options.netaddr.s_addr = options.net.s_addr;
-		options.destaddr.s_addr = options.net.s_addr;
+		options.netaddr = options.net;
+		options.destaddr = options.net;
 
 	} else {
-		options.net.s_addr = 0;
+		memset(&options.net, 0, sizeof(options.net));
 		options.prefixlen = 0;
-		options.netaddr.s_addr = 0;
-		options.destaddr.s_addr = 0;
+		memset(&options.netaddr, 0, sizeof(options.netaddr));
+		memset(&options.destaddr, 0, sizeof(options.destaddr));
 	}
 
 	/* ipup */
@@ -1427,7 +1425,7 @@
 		break;
 	}
 
-	if ((options.createif) && (!options.net.s_addr)) {
+	if ((options.createif) && (!options.net.len)) {
 		size_t prefixlen = 32;
 		if (addr.len == 16)
 			prefixlen = 64;
@@ -1580,15 +1578,13 @@
 			maxfd = tun->fd;
 	}
 
-	if ((options.createif) && (options.net.s_addr)) {
-		struct in_addr mask;
-		mask.s_addr = options.prefixlen ? (0xFFFFFFFF >> (32 - options.prefixlen)) : 0;
+	if ((options.createif) && (options.net.len)) {
 		/* printf("Setting up interface and routing\n"); */
-		tun_addaddr(tun, &options.netaddr, &options.destaddr, &mask);
+		tun_addaddr(tun, &options.netaddr, &options.destaddr, options.prefixlen);
 		if (options.defaultroute) {
 			struct in_addr rm;
 			rm.s_addr = 0;
-			tun_addroute(tun, &rm, &options.destaddr, &rm);
+			tun_addroute(tun, &rm, &options.destaddr.v4, &rm);
 		}
 		if (options.ipup)
 			tun_runscript(tun, options.ipup);

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

Gerrit-MessageType: merged
Gerrit-Change-Id: Ie36afe6eaf393855a4a708000ef4ad0192bf4767
Gerrit-PatchSet: 2
Gerrit-Project: osmo-ggsn
Gerrit-Branch: master
Gerrit-Owner: Pau Espin Pedrol <pespin at sysmocom.de>
Gerrit-Reviewer: Harald Welte <laforge at gnumonks.org>
Gerrit-Reviewer: Jenkins Builder



More information about the gerrit-log mailing list