[PATCH] openggsn[master]: ippool: Add IPv6 support to IP pool implementation

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
Wed Aug 2 23:37:10 UTC 2017


Review at  https://gerrit.osmocom.org/3403

ippool: Add IPv6 support to IP pool implementation

Change-Id: Ib98cc4bf634d6be9a7bf8c03a24e629455fcafc8
---
M ggsn/ggsn.c
M ggsn/gtp-kernel.c
M ggsn/gtp-kernel.h
M lib/Makefile.am
A lib/in46_addr.c
A lib/in46_addr.h
M lib/ippool.c
M lib/ippool.h
M sgsnemu/sgsnemu.c
9 files changed, 372 insertions(+), 169 deletions(-)


  git pull ssh://gerrit.osmocom.org:29418/openggsn refs/changes/03/3403/1

diff --git a/ggsn/ggsn.c b/ggsn/ggsn.c
index 4d07f11..00c7174 100644
--- a/ggsn/ggsn.c
+++ b/ggsn/ggsn.c
@@ -56,6 +56,7 @@
 #include "../lib/tun.h"
 #include "../lib/ippool.h"
 #include "../lib/syserr.h"
+#include "../lib/in46_addr.h"
 #include "../gtp/pdp.h"
 #include "../gtp/gtp.h"
 #include "cmdline.h"
@@ -65,7 +66,8 @@
 int maxfd = 0;			/* For select()            */
 
 struct in_addr listen_;
-struct in_addr netaddr, destaddr, net, mask;	/* Network interface       */
+struct in_addr netaddr, destaddr, net;	/* Network interface       */
+size_t prefixlen;
 struct in_addr dns1, dns2;	/* PCO DNS address         */
 char *ipup, *ipdown;		/* Filename of scripts     */
 int debug;			/* Print debug output      */
@@ -135,9 +137,12 @@
 
 static bool send_trap(const struct gsn_t *gsn, const struct pdp_t *pdp, const struct ippoolm_t *member, const char *var)
 {
+	char addrbuf[256];
 	char val[NAMESIZE];
 
-	snprintf(val, sizeof(val), "%" PRIu64 ",%s", pdp->imsi, inet_ntoa(member->addr));
+	const char *addrstr = in46a_ntop(&member->addr, addrbuf, sizeof(addrbuf));
+
+	snprintf(val, sizeof(val), "%" PRIu64 ",%s", pdp->imsi, addrstr);
 
 	if (ctrl_cmd_send_trap(gsn->ctrl, var, val) < 0) {
 		LOGP(DGGSN, LOGL_ERROR, "Failed to create and send TRAP for IMSI %" PRIu64 " [%s].\n", pdp->imsi, var);
@@ -168,7 +173,7 @@
 
 int create_context_ind(struct pdp_t *pdp)
 {
-	struct in_addr addr;
+	struct in46_addr addr;
 	struct ippoolm_t *member;
 
 	DEBUGP(DGGSN, "Received create PDP context request\n");
@@ -181,8 +186,8 @@
 	memcpy(pdp->qos_neg.v, pdp->qos_req.v, pdp->qos_req.l);	/* TODO */
 	pdp->qos_neg.l = pdp->qos_req.l;
 
-	if (pdp_euaton(&pdp->eua, &addr)) {
-		addr.s_addr = 0;	/* Request dynamic */
+	if (pdp_euaton(&pdp->eua, &addr.v4)) {
+		addr.v4.s_addr = 0;	/* Request dynamic */
 	}
 
 	if (ippool_newip(ippool, &member, &addr, 0)) {
@@ -190,7 +195,7 @@
 		return 0;	/* Allready in use, or no more available */
 	}
 
-	pdp_ntoeua(&member->addr, &pdp->eua);
+	pdp_ntoeua(&member->addr.v4, &pdp->eua);
 	pdp->peer = member;
 	pdp->ipif = tun;	/* TODO */
 	member->peer = pdp;
@@ -213,10 +218,18 @@
 int cb_tun_ind(struct tun_t *tun, void *pack, unsigned len)
 {
 	struct ippoolm_t *ipm;
-	struct in_addr dst;
+	struct in46_addr dst;
 	struct tun_packet_t *iph = (struct tun_packet_t *)pack;
 
-	dst.s_addr = iph->dst;
+	if (iph->ver == 4) {
+		if (len < sizeof(*iph) || len < 4*iph->ihl)
+			return -1;
+		dst.len = 4;
+		dst.v4.s_addr = iph->dst;
+	} else {
+		LOGP(DGGSN, LOGL_NOTICE, "non-IPv4 packet received from tun\n");
+		return -1;
+	}
 
 	DEBUGP(DGGSN, "Received packet from tun!\n");
 
@@ -381,12 +394,14 @@
 	/* net                                                          */
 	/* Store net as in_addr net and mask                            */
 	if (args_info.net_arg) {
-		if (ippool_aton(&net, &mask, args_info.net_arg, 0)) {
+		struct in46_addr in46;
+		if (ippool_aton(&in46, &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);
 	} else {
@@ -545,7 +560,7 @@
 		maxfd = gsn->fd1u;
 
 	/* use GTP kernel module for data packet encapsulation */
-	if (gtp_kernel_init(gsn, &net, &mask, &args_info) < 0)
+	if (gtp_kernel_init(gsn, &net, prefixlen, &args_info) < 0)
 		goto err;
 
 	gtp_set_cb_data_ind(gsn, encaps_tun);
@@ -570,7 +585,7 @@
 	}
 
 	DEBUGP(DGGSN, "Setting tun IP address\n");
-	if (tun_setaddr(tun, &netaddr, &destaddr, &mask)) {
+	if (tun_setaddr(tun, &netaddr, &destaddr, &prefixlen)) {
 		SYS_ERR(DGGSN, LOGL_ERROR, 0, "Failed to set tun IP address");
 		exit(1);
 	}
diff --git a/ggsn/gtp-kernel.c b/ggsn/gtp-kernel.c
index dbe5a9f..458ac27 100644
--- a/ggsn/gtp-kernel.c
+++ b/ggsn/gtp-kernel.c
@@ -70,17 +70,6 @@
 	printf("\n");
 }
 
-static int mask2prefix(struct in_addr *mask)
-{
-	uint32_t tmp = ntohl(mask->s_addr);
-	int k;
-
-	for (k=0; tmp > 0; k++)
-		tmp = (tmp << 1);
-
-	return k;
-}
-
 static struct {
 	int			genl_id;
 	struct mnl_socket	*nl;
@@ -91,7 +80,7 @@
 #define GTP_DEVNAME	"gtp0"
 
 int gtp_kernel_init(struct gsn_t *gsn, struct in_addr *net,
-		    struct in_addr *mask,
+		    size_t prefixlen,
 		    struct gengetopt_args_info *args_info)
 {
 	if (!args_info->gtp_linux_given)
@@ -126,7 +115,7 @@
 	DEBUGP(DGGSN, "Setting route to reach %s via %s\n",
 	       args_info->net_arg, GTP_DEVNAME);
 
-	if (gtp_dev_config(GTP_DEVNAME, net, mask2prefix(mask)) < 0) {
+	if (gtp_dev_config(GTP_DEVNAME, net, prefixlen) < 0) {
 		SYS_ERR(DGGSN, LOGL_ERROR, 0,
 			"Cannot add route to reach network %s\n",
 			args_info->net_arg);
diff --git a/ggsn/gtp-kernel.h b/ggsn/gtp-kernel.h
index 83280a0..b3b29e3 100644
--- a/ggsn/gtp-kernel.h
+++ b/ggsn/gtp-kernel.h
@@ -8,7 +8,7 @@
 
 #ifdef GTP_KERNEL
 int gtp_kernel_init(struct gsn_t *gsn, struct in_addr *net,
-		    struct in_addr *mask,
+		    size_t prefixlen,
 		    struct gengetopt_args_info *args_info);
 void gtp_kernel_stop(void);
 
@@ -19,7 +19,7 @@
 
 #else
 static inline int gtp_kernel_init(struct gsn_t *gsn, struct in_addr *net,
-				  struct in_addr *mask,
+				  size_t prefixlen,
 				  struct gengetopt_args_info *args_info)
 {
 	if (args_info->gtp_linux_given) {
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 756d566..632990c 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -1,7 +1,7 @@
 noinst_LIBRARIES = libmisc.a
 
-noinst_HEADERS = gnugetopt.h ippool.h lookup.h syserr.h tun.h
+noinst_HEADERS = gnugetopt.h ippool.h lookup.h syserr.h tun.h in46_addr.h
 
 AM_CFLAGS = -O2 -fno-builtin -Wall -DSBINDIR='"$(sbindir)"' -ggdb $(LIBOSMOCORE_CFLAGS)
 
-libmisc_a_SOURCES = getopt1.c getopt.c ippool.c lookup.c tun.c debug.c
+libmisc_a_SOURCES = getopt1.c getopt.c ippool.c lookup.c tun.c debug.c in46_addr.c
diff --git a/lib/in46_addr.c b/lib/in46_addr.c
new file mode 100644
index 0000000..f9bb020
--- /dev/null
+++ b/lib/in46_addr.c
@@ -0,0 +1,148 @@
+/*
+ * IPv4/v6 address functions.
+ * 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
+ * notice and this permission notice is included in all copies or
+ * substantial portions of the software.
+ * 
+ */
+
+#include "lib/in46_addr.h"
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*! Return the address family of given \reff in46_addr argument */
+int in46a_to_af(const struct in46_addr *in)
+{
+	switch (in->len) {
+	case 4:
+		return AF_INET;
+	case 16:
+		return AF_INET6;
+	default:
+		return -1;
+	}
+}
+
+/*! Convert \ref in46_addr to sockaddr_storage */
+int in46a_to_sas(struct sockaddr_storage *out, const struct in46_addr *in)
+{
+	struct sockaddr_in *sin = (struct sockaddr_in *)out;
+	struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)out;
+
+	switch (in->len) {
+	case 4:
+		sin->sin_family = AF_INET;
+		sin->sin_addr = in->v4;
+		break;
+	case 16:
+		sin6->sin6_family = AF_INET;
+		sin6->sin6_addr = in->v6;
+		break;
+	default:
+		return -1;
+	}
+
+	return 0;
+}
+
+/*! Convenience wrapper around inet_ntop() for \ref in46_addr */
+const char *in46a_ntop(const struct in46_addr *in, char *dst, socklen_t dst_size)
+{
+	int af = in46a_to_af(in);
+	if (af < 0)
+		return NULL;
+
+	return inet_ntop(af, (const void *) &in->v4, dst, dst_size);
+}
+
+/*! Determine if two in46_addr are equal or not
+ *  \returns 1 in case they are equal; 0 otherwise */
+int in46a_equal(const struct in46_addr *a, const struct in46_addr *b)
+{
+	if (a->len == b->len && !memcmp(&a->v6, &b->v6, a->len))
+		return 1;
+	else
+		return 0;
+}
+
+/*! Match if IPv6 addr1 + addr2 are within same \a mask */
+static int ipv6_within_mask(const struct in6_addr *addr1, const struct in6_addr *addr2,
+			    const struct in6_addr *mask)
+{
+	struct in6_addr masked = *addr2;
+#if defined(__linux__)
+	masked.s6_addr32[0] &= mask->s6_addr32[0];
+	masked.s6_addr32[1] &= mask->s6_addr32[1];
+	masked.s6_addr32[2] &= mask->s6_addr32[2];
+	masked.s6_addr32[3] &= mask->s6_addr32[3];
+#else
+	masked.__u6_addr.__u6_addr32[0] &= mask->__u6_addr.__u6_addr32[0];
+	masked.__u6_addr.__u6_addr32[1] &= mask->__u6_addr.__u6_addr32[1];
+	masked.__u6_addr.__u6_addr32[2] &= mask->__u6_addr.__u6_addr32[2];
+	masked.__u6_addr.__u6_addr32[3] &= mask->__u6_addr.__u6_addr32[3];
+#endif
+	if (!memcmp(addr1, &masked, sizeof(struct in6_addr)))
+		return 1;
+	else
+		return 0;
+}
+
+/*! Create an IPv6 netmask from the given prefix length */
+static void create_ipv6_netmask(struct in6_addr *netmask, int prefixlen)
+{
+	uint32_t *p_netmask;
+	memset(netmask, 0, sizeof(struct in6_addr));
+	if (prefixlen < 0)
+		prefixlen = 0;
+	else if (128 < prefixlen)
+		prefixlen = 128;
+
+#if defined(__linux__)
+	p_netmask = &netmask->s6_addr32[0];
+#else
+	p_netmask = &netmask->__u6_addr.__u6_addr32[0];
+#endif
+	while (32 < prefixlen) {
+		*p_netmask = 0xffffffff;
+		p_netmask++;
+		prefixlen -= 32;
+	}
+	if (prefixlen != 0) {
+		*p_netmask = htonl(0xFFFFFFFF << (32 - prefixlen));
+	}
+}
+
+/*! Determine if given \a addr is within given \a net + \a prefixlen
+ *  Builds the netmask from \a net + \a prefixlen and matches it to \a addr
+ *  \returns 1 in case of a match, 0 otherwise */
+int in46a_within_mask(const struct in46_addr *addr, const struct in46_addr *net, size_t prefixlen)
+{
+	struct in_addr netmask;
+	struct in6_addr netmask6;
+
+	if (addr->len != net->len)
+		return 0;
+
+	switch (addr->len) {
+	case 4:
+		netmask.s_addr = htonl(0xFFFFFFFF << (32 - prefixlen));
+		if ((addr->v4.s_addr & netmask.s_addr) == net->v4.s_addr)
+			return 1;
+		else
+			return 0;
+	case 16:
+		create_ipv6_netmask(&netmask6, prefixlen);
+		return ipv6_within_mask(&addr->v6, &net->v6, &netmask6);
+	default:
+		return 0;
+	}
+}
diff --git a/lib/in46_addr.h b/lib/in46_addr.h
new file mode 100644
index 0000000..f28fd8e
--- /dev/null
+++ b/lib/in46_addr.h
@@ -0,0 +1,19 @@
+#pragma once
+#include <stdint.h>
+#include <netinet/in.h>
+
+/* a simple wrapper around an in6_addr to also contain the length of the address,
+ * thereby implicitly indicating the address family of the address */
+struct in46_addr {
+	uint8_t len;
+	union {
+		struct in_addr v4;
+		struct in6_addr v6;
+	};
+};
+
+extern int in46a_to_af(const struct in46_addr *in);
+extern int in46a_to_sas(struct sockaddr_storage *out, const struct in46_addr *in);
+extern const char *in46a_ntop(const struct in46_addr *in, char *dst, socklen_t dst_size);
+extern int in46a_equal(const struct in46_addr *a, const struct in46_addr *b);
+extern int in46a_within_mask(const struct in46_addr *addr, const struct in46_addr *net, size_t prefixlen);
diff --git a/lib/ippool.c b/lib/ippool.c
index 1f79a77..6c43ad7 100644
--- a/lib/ippool.c
+++ b/lib/ippool.c
@@ -1,6 +1,7 @@
 /*
  * IP address pool functions.
  * Copyright (C) 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
@@ -13,9 +14,11 @@
 #include <netinet/in.h>		/* in_addr */
 #include <stdlib.h>		/* calloc */
 #include <stdio.h>		/* sscanf */
+#include <alloca.h>
 #include <string.h>
 #include <sys/socket.h>
 #include <arpa/inet.h>
+#include <netdb.h>
 #include "syserr.h"
 #include "ippool.h"
 #include "lookup.h"
@@ -31,13 +34,14 @@
 	printf("Listsize %d\n", this->listsize);
 
 	for (n = 0; n < this->listsize; n++) {
-		printf("Unit %d inuse %d prev %d next %d addr %s %x\n",
+		char s[256];
+		in46a_ntop(&this->member[n].addr, s, sizeof(s));
+		printf("Unit %d inuse %d prev %d next %d addr %s\n",
 		       n,
 		       this->member[n].inuse,
 		       this->member[n].prev - this->member,
 		       this->member[n].next - this->member,
-		       inet_ntoa(this->member[n].addr),
-		       this->member[n].addr.s_addr);
+		       s);
 	}
 	return 0;
 }
@@ -49,7 +53,7 @@
 	struct ippoolm_t *p_prev = NULL;
 
 	/* Insert into hash table */
-	hash = ippool_hash4(&member->addr) & this->hashmask;
+	hash = ippool_hash(&member->addr) & this->hashmask;
 	for (p = this->hash[hash]; p; p = p->nexthash)
 		p_prev = p;
 	if (!p_prev)
@@ -66,7 +70,7 @@
 	struct ippoolm_t *p_prev = NULL;
 
 	/* Find in hash table */
-	hash = ippool_hash4(&member->addr) & this->hashmask;
+	hash = ippool_hash(&member->addr) & this->hashmask;
 	for (p = this->hash[hash]; p; p = p->nexthash) {
 		if (p == member) {
 			break;
@@ -88,73 +92,98 @@
 	return 0;
 }
 
-unsigned long int ippool_hash4(struct in_addr *addr)
+static unsigned long int ippool_hash4(struct in_addr *addr)
 {
 	return lookup((unsigned char *)&addr->s_addr, sizeof(addr->s_addr), 0);
 }
 
-#ifndef IPPOOL_NOIP6
-unsigned long int ippool_hash6(struct in6_addr *addr)
+static unsigned long int ippool_hash6(struct in6_addr *addr)
 {
-	return lookup((unsigned char *)addr->u6_addr8, sizeof(addr->u6_addr8),
+	/* TODO: Review hash spread for IPv6 */
+	return lookup((unsigned char *)addr->s6_addr, sizeof(addr->s6_addr),
 		      0);
 }
-#endif
+
+unsigned long int ippool_hash(struct in46_addr *addr)
+{
+	if (addr->len == 4)
+		return ippool_hash4(&addr->v4);
+	else
+		return ippool_hash6(&addr->v6);
+}
 
 /* Get IP address and mask */
-int ippool_aton(struct in_addr *addr, struct in_addr *mask,
-		char *pool, int number)
+int ippool_aton(struct in46_addr *addr, size_t *prefixlen, const char *pool_in, int number)
 {
+	struct addrinfo *ai;
+	struct addrinfo hints = {
+		.ai_family = AF_UNSPEC,
+		.ai_socktype = SOCK_DGRAM,
+		.ai_flags = 0,
+		.ai_protocol = 0
+	};
+	char *pool = alloca(strlen(pool_in)+1);
 
-	/* Parse only first instance of network for now */
-	/* Eventually "number" will indicate the token which we want to parse */
+	strcpy(pool, pool_in);
 
-	unsigned int a1, a2, a3, a4;
-	unsigned int m1, m2, m3, m4;
-	int c;
-	int m;
-	int masklog;
+	int err;
 
-	c = sscanf(pool, "%u.%u.%u.%u/%u.%u.%u.%u",
-		   &a1, &a2, &a3, &a4, &m1, &m2, &m3, &m4);
-	switch (c) {
-	case 4:
-		mask->s_addr = 0xffffffff;
-		break;
-	case 5:
-		if (m1 > 32) {
-			SYS_ERR(DIP, LOGL_ERROR, 0, "Invalid mask");
-			return -1;	/* Invalid mask */
+	/* Find '/' and point to first char after it */
+	char *prefixlen_str = strchr(pool, '/');
+	if (prefixlen_str) {
+		*prefixlen_str = '\0';
+		prefixlen_str++;
+		if (*prefixlen_str == '\0') {
+			SYS_ERR(DIP, LOGL_ERROR, 0, "Empty prefix length specified");
+			return -1;
 		}
-		mask->s_addr = htonl(0xffffffff << (32 - m1));
-		break;
-	case 8:
-		if (m1 >= 256 || m2 >= 256 || m3 >= 256 || m4 >= 256) {
-			SYS_ERR(DIP, LOGL_ERROR, 0, "Invalid mask");
-			return -1;	/* Wrong mask format */
-		}
-		m = m1 * 0x1000000 + m2 * 0x10000 + m3 * 0x100 + m4;
-		for (masklog = 0; ((1 << masklog) < ((~m) + 1)); masklog++) ;
-		if (((~m) + 1) != (1 << masklog)) {
-			SYS_ERR(DIP, LOGL_ERROR, 0, "Invalid mask");
-			return -1;	/* Wrong mask format (not all ones followed by all zeros) */
-		}
-		mask->s_addr = htonl(m);
-		break;
-	default:
-		SYS_ERR(DIP, LOGL_ERROR, 0, "Invalid mask");
-		return -1;	/* Invalid mask */
 	}
 
-	if (a1 >= 256 || a2 >= 256 || a3 >= 256 || a4 >= 256) {
-		SYS_ERR(DIP, LOGL_ERROR, 0,
-			"Wrong IP address format");
+	/* convert address */
+	if ((err = getaddrinfo(pool, NULL, &hints, &ai))) {
+		SYS_ERR(DIP, LOGL_ERROR, 0, "Bad address");
 		return -1;
-	} else
-		addr->s_addr =
-		    htonl(a1 * 0x1000000 + a2 * 0x10000 + a3 * 0x100 + a4);
+	}
+
+	/* Copy address, set lengths */
+	if (ai->ai_family == AF_INET) {
+		*prefixlen = 32;
+		addr->len = sizeof(struct in_addr);
+		addr->v4 = ((struct sockaddr_in*)ai->ai_addr)->sin_addr;
+	} else {
+		*prefixlen = 128;
+		addr->len = sizeof(struct in6_addr);
+		addr->v6 = ((struct sockaddr_in6*)ai->ai_addr)->sin6_addr;
+	}
+	freeaddrinfo(ai);
+
+	/* parse prefixlen */
+	if (prefixlen_str) {
+		char *e;
+		*prefixlen = strtol(prefixlen_str, &e, 10);
+		if (*e != '\0') {
+			SYS_ERR(DIP, LOGL_ERROR, 0, "Prefixlen is not an int");
+			return -1;
+		}
+	}
+
+	if (*prefixlen > (addr->len * 8)) {
+		SYS_ERR(DIP, LOGL_ERROR, 0, "Perfixlen too big");
+		return -1;
+	}
 
 	return 0;
+}
+
+/* Increase IPv4/IPv6 address by 1 */
+void in46a_inc(struct in46_addr *addr)
+{
+	size_t addrlen;
+	uint8_t *a = (uint8_t *)&addr->v6;
+	for (addrlen = addr->len; addrlen > 0; addrlen--) {
+		if (++a[addrlen-1])
+			break;
+	}
 }
 
 /* Create new address pool */
@@ -165,11 +194,10 @@
 	/* Parse only first instance of pool for now */
 
 	int i;
-	struct in_addr addr;
-	struct in_addr mask;
-	struct in_addr stataddr;
-	struct in_addr statmask;
-	unsigned int m;
+	struct in46_addr addr;
+	size_t addrprefixlen;
+	struct in46_addr stataddr;
+	size_t stataddrprefixlen;
 	int listsize;
 	int dynsize;
 	unsigned int statsize;
@@ -177,7 +205,7 @@
 	if (!allowdyn) {
 		dynsize = 0;
 	} else {
-		if (ippool_aton(&addr, &mask, dyn, 0)) {
+		if (ippool_aton(&addr, &addrprefixlen, dyn, 0)) {
 			SYS_ERR(DIP, LOGL_ERROR, 0,
 				"Failed to parse dynamic pool");
 			return -1;
@@ -188,8 +216,7 @@
 			flags |= IPPOOL_NONETWORK;
 		}
 
-		m = ntohl(mask.s_addr);
-		dynsize = ((~m) + 1);
+		dynsize = (1 << (addr.len*8 - addrprefixlen)) -1;
 		if (flags & IPPOOL_NONETWORK)	/* Exclude network address from pool */
 			dynsize--;
 		if (flags & IPPOOL_NOGATEWAY)	/* Exclude gateway address from pool */
@@ -200,17 +227,16 @@
 
 	if (!allowstat) {
 		statsize = 0;
-		stataddr.s_addr = 0;
-		statmask.s_addr = 0;
+		stataddr.len = 0;
+		stataddrprefixlen = 0;
 	} else {
-		if (ippool_aton(&stataddr, &statmask, stat, 0)) {
+		if (ippool_aton(&stataddr, &stataddrprefixlen, stat, 0)) {
 			SYS_ERR(DIP, LOGL_ERROR, 0,
 				"Failed to parse static range");
 			return -1;
 		}
 
-		m = ntohl(statmask.s_addr);
-		statsize = ((~m) + 1);
+		statsize = (1 << (addr.len - addrprefixlen + 1)) -1;
 		if (statsize > IPPOOL_STATSIZE)
 			statsize = IPPOOL_STATSIZE;
 	}
@@ -225,8 +251,9 @@
 
 	(*this)->allowdyn = allowdyn;
 	(*this)->allowstat = allowstat;
-	(*this)->stataddr = stataddr;
-	(*this)->statmask = statmask;
+	if (stataddr.len > 0)
+		(*this)->stataddr = stataddr;
+	(*this)->stataddrprefixlen = stataddrprefixlen;
 
 	(*this)->listsize += listsize;
 	if (!((*this)->member = calloc(sizeof(struct ippoolm_t), listsize))) {
@@ -255,17 +282,15 @@
 
 	(*this)->firstdyn = NULL;
 	(*this)->lastdyn = NULL;
+	if (flags & IPPOOL_NOGATEWAY) {
+		in46a_inc(&addr);
+		in46a_inc(&addr);
+	} else if (flags & IPPOOL_NONETWORK) {
+		in46a_inc(&addr);
+	}
 	for (i = 0; i < dynsize; i++) {
-
-		if (flags & IPPOOL_NOGATEWAY)
-			(*this)->member[i].addr.s_addr =
-			    htonl(ntohl(addr.s_addr) + i + 2);
-		else if (flags & IPPOOL_NONETWORK)
-			(*this)->member[i].addr.s_addr =
-			    htonl(ntohl(addr.s_addr) + i + 1);
-		else
-			(*this)->member[i].addr.s_addr =
-			    htonl(ntohl(addr.s_addr) + i);
+		(*this)->member[i].addr = addr;
+		in46a_inc(&addr);
 
 		(*this)->member[i].inuse = 0;
 
@@ -285,8 +310,8 @@
 	(*this)->firststat = NULL;
 	(*this)->laststat = NULL;
 	for (i = dynsize; i < listsize; i++) {
-
-		(*this)->member[i].addr.s_addr = 0;
+		struct in46_addr *i6al = &(*this)->member[i].addr;
+		memset(i6al, 0, sizeof(*i6al));
 		(*this)->member[i].inuse = 0;
 
 		/* Insert into list of unused */
@@ -316,15 +341,15 @@
 
 /* Find an IP address in the pool */
 int ippool_getip(struct ippool_t *this, struct ippoolm_t **member,
-		 struct in_addr *addr)
+		 struct in46_addr *addr)
 {
 	struct ippoolm_t *p;
 	uint32_t hash;
 
 	/* Find in hash table */
-	hash = ippool_hash4(addr) & this->hashmask;
+	hash = ippool_hash(addr) & this->hashmask;
 	for (p = this->hash[hash]; p; p = p->nexthash) {
-		if ((p->addr.s_addr == addr->s_addr) && (p->inuse)) {
+		if (in46a_equal(&p->addr, addr)) {
 			if (member)
 				*member = p;
 			return 0;
@@ -344,7 +369,7 @@
  * address space.
 **/
 int ippool_newip(struct ippool_t *this, struct ippoolm_t **member,
-		 struct in_addr *addr, int statip)
+		 struct in46_addr *addr, int statip)
 {
 	struct ippoolm_t *p;
 	struct ippoolm_t *p2 = NULL;
@@ -365,17 +390,23 @@
 	if (0)
 		(void)ippool_printaddr(this);
 
+	int specified = 0;
+	if (addr) {
+		if (addr->len == 4 && addr->v4.s_addr)
+			specified = 1;
+		if (addr->len == 16 && !IN6_IS_ADDR_UNSPECIFIED(&addr->v6))
+			specified = 1;
+	}
+
 	/* First check to see if this type of address is allowed */
-	if ((addr) && (addr->s_addr) && statip) {	/* IP address given */
+	if (specified && statip) {	/* IP address given */
 		if (!this->allowstat) {
 			SYS_ERR(DIP, LOGL_ERROR, 0,
 				"Static IP address not allowed");
 			return -1;
 		}
-		if ((addr->s_addr & this->statmask.s_addr) !=
-		    this->stataddr.s_addr) {
-			SYS_ERR(DIP, LOGL_ERROR, 0,
-				"Static out of range");
+		if (!in46a_within_mask(addr, &this->stataddr, this->stataddrprefixlen)) {
+			SYS_ERR(DIP, LOGL_ERROR, 0, "Static out of range");
 			return -1;
 		}
 	} else {
@@ -387,11 +418,11 @@
 	}
 
 	/* If IP address given try to find it in dynamic address pool */
-	if ((addr) && (addr->s_addr)) {	/* IP address given */
+	if (specified) {	/* IP address given */
 		/* Find in hash table */
-		hash = ippool_hash4(addr) & this->hashmask;
+		hash = ippool_hash(addr) & this->hashmask;
 		for (p = this->hash[hash]; p; p = p->nexthash) {
-			if ((p->addr.s_addr == addr->s_addr)) {
+			if (in46a_equal(&p->addr, addr)) {
 				p2 = p;
 				break;
 			}
@@ -420,6 +451,11 @@
 			return -1;	/* Allready in use / Should not happen */
 		}
 
+		if (p2->addr.len != addr->len) {
+			SYS_ERR(DIP, LOGL_ERROR, 0, "MS requested unsupported PDP context type");
+			return -1;
+		}
+
 		/* Remove from linked list of free dynamic addresses */
 		if (p2->prev)
 			p2->prev->next = p2->next;
@@ -442,13 +478,18 @@
 	/* It was not possible to allocate from dynamic address pool */
 	/* Try to allocate from static address space */
 
-	if ((addr) && (addr->s_addr) && (statip)) {	/* IP address given */
+	if (specified  && (statip)) {	/* IP address given */
 		if (!this->firststat) {
 			SYS_ERR(DIP, LOGL_ERROR, 0,
 				"No more IP addresses available");
 			return -1;	/* No more available */
 		} else
 			p2 = this->firststat;
+
+		if (p2->addr.len != addr->len) {
+			SYS_ERR(DIP, LOGL_ERROR, 0, "MS requested unsupported PDP context type");
+			return -1;
+		}
 
 		/* Remove from linked list of free static addresses */
 		if (p2->prev)
@@ -518,7 +559,7 @@
 		this->laststat = member;
 
 		member->inuse = 0;
-		member->addr.s_addr = 0;
+		memset(&member->addr, 0, sizeof(member->addr));
 		member->peer = NULL;
 		member->nexthash = NULL;
 		if (0)
@@ -530,9 +571,3 @@
 		return -1;
 	}
 }
-
-#ifndef IPPOOL_NOIP6
-extern unsigned long int ippool_hash6(struct in6_addr *addr);
-extern int ippool_getip6(struct ippool_t *this, struct in6_addr *addr);
-extern int ippool_returnip6(struct ippool_t *this, struct in6_addr *addr);
-#endif
diff --git a/lib/ippool.h b/lib/ippool.h
index 534140d..53154f2 100644
--- a/lib/ippool.h
+++ b/lib/ippool.h
@@ -12,6 +12,8 @@
 #ifndef _IPPOOL_H
 #define _IPPOOL_H
 
+#include "../lib/in46_addr.h"
+
 /* Assuming that the address space is fragmented we need a hash table
    in order to return the addresses.
 
@@ -26,8 +28,6 @@
    in RFC2373.
 */
 
-#define IPPOOL_NOIP6
-
 #define IPPOOL_NONETWORK   0x01
 #define IPPOOL_NOBROADCAST 0x02
 #define IPPOOL_NOGATEWAY   0x04
@@ -40,8 +40,8 @@
 	unsigned int listsize;	/* Total number of addresses */
 	int allowdyn;		/* Allow dynamic IP address allocation */
 	int allowstat;		/* Allow static IP address allocation */
-	struct in_addr stataddr;	/* Static address range network address */
-	struct in_addr statmask;	/* Static address range network mask */
+	struct in46_addr stataddr;	/* Static address range network address */
+	size_t stataddrprefixlen;	/* IPv6 prefix length of stataddr */
 	struct ippoolm_t *member;	/* Listsize array of members */
 	unsigned int hashsize;	/* Size of hash table */
 	int hashlog;		/* Log2 size of hash table */
@@ -54,11 +54,7 @@
 };
 
 struct ippoolm_t {
-#ifndef IPPOOL_NOIP6
-	struct in6_addr addr;	/* IP address of this member */
-#else
-	struct in_addr addr;	/* IP address of this member */
-#endif
+	struct in46_addr addr;	/* IP address of this member */
 	int inuse;		/* 0=available; 1= dynamic; 2 = static */
 	struct ippoolm_t *nexthash;	/* Linked list part of hash table */
 	struct ippoolm_t *prev, *next;	/* Linked list of free dynamic or static */
@@ -70,7 +66,7 @@
    bytes for each address. */
 
 /* Hash an IP address using code based on Bob Jenkins lookupa */
-extern unsigned long int ippool_hash4(struct in_addr *addr);
+extern unsigned long int ippool_hash(struct in46_addr *addr);
 
 /* Create new address pool */
 extern int ippool_new(struct ippool_t **this, char *dyn, char *stat,
@@ -81,24 +77,20 @@
 
 /* Find an IP address in the pool */
 extern int ippool_getip(struct ippool_t *this, struct ippoolm_t **member,
-			struct in_addr *addr);
+			struct in46_addr *addr);
 
 /* Get an IP address. If addr = 0.0.0.0 get a dynamic IP address. Otherwise
    check to see if the given address is available */
 extern int ippool_newip(struct ippool_t *this, struct ippoolm_t **member,
-			struct in_addr *addr, int statip);
+			struct in46_addr *addr, int statip);
 
 /* Return a previously allocated IP address */
 extern int ippool_freeip(struct ippool_t *this, struct ippoolm_t *member);
 
 /* Get net and mask based on ascii string */
-extern int ippool_aton(struct in_addr *addr, struct in_addr *mask,
-		       char *pool, int number);
+int ippool_aton(struct in46_addr *addr, size_t *prefixlen, const char *pool, int number);
 
-#ifndef IPPOOL_NOIP6
-extern unsigned long int ippool_hash6(struct in6_addr *addr);
-extern int ippool_getip6(struct ippool_t *this, struct in6_addr *addr);
-extern int ippool_returnip6(struct ippool_t *this, struct in6_addr *addr);
-#endif
+/* Increase IPv4/IPv6 address by 1 */
+extern void in46a_inc(struct in46_addr *addr);
 
 #endif /* !_IPPOOL_H */
diff --git a/sgsnemu/sgsnemu.c b/sgsnemu/sgsnemu.c
index 1567e7e..90a6200 100644
--- a/sgsnemu/sgsnemu.c
+++ b/sgsnemu/sgsnemu.c
@@ -58,7 +58,7 @@
 	uint8_t inuse;		/* 0=free. 1=used by somebody */
 	struct iphash_t *ipnext;
 	struct pdp_t *pdp;
-	struct in_addr addr;
+	struct in46_addr addr;
 };
 struct iphash_t iparr[MAXCONTEXTS];
 struct iphash_t *iphash[MAXCONTEXTS];
@@ -81,7 +81,8 @@
 struct {
 	int debug;		/* Print debug messages */
 	int createif;		/* Create local network interface */
-	struct in_addr netaddr, destaddr, net, mask;	/* Network interface  */
+	struct in_addr netaddr, destaddr, net;	/* Network interface  */
+	size_t prefixlen;
 	char *ipup, *ipdown;	/* Filename of scripts */
 	int defaultroute;	/* Set up default route */
 	struct in_addr pinghost;	/* Remote ping host    */
@@ -160,13 +161,13 @@
 		state = 3;  /* Tell main loop to finish. */
 }
 
-int ipset(struct iphash_t *ipaddr, struct in_addr *addr)
+int ipset(struct iphash_t *ipaddr, struct in46_addr *addr)
 {
-	int hash = ippool_hash4(addr) % MAXCONTEXTS;
+	int hash = ippool_hash(addr) % MAXCONTEXTS;
 	struct iphash_t *h;
 	struct iphash_t *prev = NULL;
 	ipaddr->ipnext = NULL;
-	ipaddr->addr.s_addr = addr->s_addr;
+	ipaddr->addr = *addr;
 	for (h = iphash[hash]; h; h = h->ipnext)
 		prev = h;
 	if (!prev)
@@ -178,7 +179,7 @@
 
 int ipdel(struct iphash_t *ipaddr)
 {
-	int hash = ippool_hash4(&ipaddr->addr) % MAXCONTEXTS;
+	int hash = ippool_hash(&ipaddr->addr) % MAXCONTEXTS;
 	struct iphash_t *h;
 	struct iphash_t *prev = NULL;
 	for (h = iphash[hash]; h; h = h->ipnext) {
@@ -194,12 +195,12 @@
 	return EOF;		/* End of linked list and not found */
 }
 
-int ipget(struct iphash_t **ipaddr, struct in_addr *addr)
+int ipget(struct iphash_t **ipaddr, struct in46_addr *addr)
 {
-	int hash = ippool_hash4(addr) % MAXCONTEXTS;
+	int hash = ippool_hash(addr) % MAXCONTEXTS;
 	struct iphash_t *h;
 	for (h = iphash[hash]; h; h = h->ipnext) {
-		if ((h->addr.s_addr == addr->s_addr)) {
+		if (in46a_equal(&h->addr, addr)) {
 			*ipaddr = h;
 			return 0;
 		}
@@ -859,15 +860,17 @@
 	/* net                                                          */
 	/* Store net as in_addr net and mask                            */
 	if (args_info.net_arg) {
+		struct in46_addr in46;
 		if (ippool_aton
-		    (&options.net, &options.mask, args_info.net_arg, 0)) {
+		    (&in46, &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;
 #if defined (__sun__)
-		options.netaddr.s_addr = htonl(ntohl(options.net.s_addr) + 1);
+		options.netaddrs_addr = htonl(ntohl(options.net.s_addr) + 1);
 		options.destaddr.s_addr = htonl(ntohl(options.net.s_addr) + 1);
 #else
 		options.netaddr.s_addr = options.net.s_addr;
@@ -876,7 +879,7 @@
 
 	} else {
 		options.net.s_addr = 0;
-		options.mask.s_addr = 0;
+		options.prefixlen = 0;
 		options.netaddr.s_addr = 0;
 		options.destaddr.s_addr = 0;
 	}
@@ -1277,14 +1280,15 @@
 int cb_tun_ind(struct tun_t *tun, void *pack, unsigned len)
 {
 	struct iphash_t *ipm;
-	struct in_addr src;
+	struct in46_addr src;
 	struct tun_packet_t *iph = (struct tun_packet_t *)pack;
 
-	src.s_addr = iph->src;
+	src.len = 4;
+	src.v4.s_addr = iph->src;
 
 	if (ipget(&ipm, &src)) {
 		printf("Dropping packet from invalid source address: %s\n",
-		       inet_ntoa(src));
+		       inet_ntoa(src.v4));
 		return 0;
 	}
 
@@ -1295,7 +1299,7 @@
 
 int create_pdp_conf(struct pdp_t *pdp, void *cbp, int cause)
 {
-	struct in_addr addr;
+	struct in46_addr addr;
 
 	struct iphash_t *iph = (struct iphash_t *)cbp;
 
@@ -1324,7 +1328,7 @@
 		return EOF;	/* Not what we expected */
 	}
 
-	if (pdp_euaton(&pdp->eua, &addr)) {
+	if (pdp_euaton(&pdp->eua, &addr.v4)) {
 		printf
 		    ("Received create PDP context response. Cause value: %d\n",
 		     cause);
@@ -1335,7 +1339,7 @@
 	}
 
 	printf("Received create PDP context response. IP address: %s\n",
-	       inet_ntoa(addr));
+	       inet_ntoa(addr.v4));
 
 	if ((options.createif) && (!options.net.s_addr)) {
 		struct in_addr m;
@@ -1345,11 +1349,11 @@
 		m.s_addr = -1;
 #endif
 		/* printf("Setting up interface and routing\n"); */
-		tun_addaddr(tun, &addr, &addr, &m);
+		tun_addaddr(tun, &addr.v4, &addr.v4, &m);
 		if (options.defaultroute) {
 			struct in_addr rm;
 			rm.s_addr = 0;
-			tun_addroute(tun, &rm, &addr, &rm);
+			tun_addroute(tun, &rm, &addr.v4, &rm);
 		}
 		if (options.ipup)
 			tun_runscript(tun, options.ipup);
@@ -1472,9 +1476,10 @@
 	}
 
 	if ((options.createif) && (options.net.s_addr)) {
+		struct in_addr mask;
+		mask.s_addr = options.prefixlen ? (0xFFFFFFFF >> (32 - options.prefixlen)) : 0;
 		/* printf("Setting up interface and routing\n"); */
-		tun_addaddr(tun, &options.netaddr, &options.destaddr,
-			    &options.mask);
+		tun_addaddr(tun, &options.netaddr, &options.destaddr, &mask);
 		if (options.defaultroute) {
 			struct in_addr rm;
 			rm.s_addr = 0;

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

Gerrit-MessageType: newchange
Gerrit-Change-Id: Ib98cc4bf634d6be9a7bf8c03a24e629455fcafc8
Gerrit-PatchSet: 1
Gerrit-Project: openggsn
Gerrit-Branch: master
Gerrit-Owner: Harald Welte <laforge at gnumonks.org>



More information about the gerrit-log mailing list