Harald Welte has submitted this change and it was merged.

Change subject: ippool: Add IPv6 support to IP pool implementation
......................................................................


ippool: Add IPv6 support to IP pool implementation

Extend the IP pool implementation to be able to manage both pools
of 32bit addresses (IPv4) as well as pools of 128bit addresses (IPv6)

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, 371 insertions(+), 169 deletions(-)

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



diff --git a/ggsn/ggsn.c b/ggsn/ggsn.c
index 168e907..0d0e569 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");
@@ -183,8 +188,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)) {
@@ -192,7 +197,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;
@@ -215,10 +220,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");
 
@@ -383,12 +396,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 {
@@ -547,7 +562,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);
@@ -572,7 +587,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..903ceec
--- /dev/null
+++ b/lib/in46_addr.c
@@ -0,0 +1,148 @@
+/*
+ * IPv4/v6 address functions.
+ * Copyright (C) 2017 by Harald Welte <lafo...@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..c3eb267 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 <lafo...@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
@@ -16,6 +17,7 @@
 #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 +33,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 +52,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 +69,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 +91,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[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 +193,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 +204,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 +215,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 +226,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 +250,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 +281,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 +309,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 +340,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 +368,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 +389,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 +417,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 +450,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 +477,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 +558,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 +570,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: merged
Gerrit-Change-Id: Ib98cc4bf634d6be9a7bf8c03a24e629455fcafc8
Gerrit-PatchSet: 5
Gerrit-Project: openggsn
Gerrit-Branch: master
Gerrit-Owner: Harald Welte <lafo...@gnumonks.org>
Gerrit-Reviewer: Harald Welte <lafo...@gnumonks.org>
Gerrit-Reviewer: Jenkins Builder

Reply via email to