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 <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..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 <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
@@ -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 <lafo...@gnumonks.org>

Reply via email to