[PATCH] openggsn[master]: IPv6 support

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/.

groos@xiplink.com gerrit-no-reply at lists.osmocom.org
Fri Jun 9 19:43:18 UTC 2017


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

IPv6 support

Change-Id: If8ce8b4b8cd2ba97f7ba122de4703983111046e4
---
M ggsn/ggsn.c
M ggsn/ippool.c
M ggsn/ippool.h
M ggsn/tun.c
M ggsn/tun.h
M gtp/gtp.c
M gtp/gtp.h
M gtp/pdp.c
M gtp/pdp.h
M gtp/queue.c
M gtp/queue.h
M sgsnemu/ippool.c
M sgsnemu/ippool.h
M sgsnemu/sgsnemu.c
M sgsnemu/tun.c
M sgsnemu/tun.h
16 files changed, 1,215 insertions(+), 480 deletions(-)


  git pull ssh://gerrit.osmocom.org:29418/openggsn refs/changes/70/2870/1

diff --git a/ggsn/ggsn.c b/ggsn/ggsn.c
index 52a2022..3efba0b 100644
--- a/ggsn/ggsn.c
+++ b/ggsn/ggsn.c
@@ -57,9 +57,11 @@
 int end = 0;
 int maxfd = 0;	                /* For select()            */
 
-struct in_addr listen_;
-struct in_addr netaddr, destaddr, net, mask;  /* Network interface       */
-struct in_addr dns1, dns2;      /* PCO DNS address         */
+struct sockaddr_storage listen_;
+struct in6_addr netaddr, destaddr, net;  /* Network interface       */
+size_t netaddrlen, destaddrlen, netlen, prefixlen;
+struct in6_addr dns1, dns2;      /* PCO DNS address         */
+size_t dns1len, dns2len;
 char *ipup, *ipdown;            /* Filename of scripts     */
 int debug;                      /* Print debug output      */
 struct ul255_t pco;
@@ -147,7 +149,8 @@
 
 
 int create_context_ind(struct pdp_t *pdp) {
-  struct in_addr addr;
+  struct in6_addr addr;
+  size_t addrlen = 0;
   struct ippoolm_t *member;
 
   if (debug) printf("Received create PDP context request\n");
@@ -160,17 +163,23 @@
 
   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 (ippool_newip(ippool, &member, &addr, 0)) {
+  if (pdp_euaton(&pdp->eua, (struct in_addr*)&addr) == 0)
+    addrlen = 4;
+  else if (pdp_euaton6(&pdp->eua, &addr) == 0)
+    addrlen = 16;
+  else
+    memset(&addr, 0, sizeof(addr)); /* Request dynamic */
+
+  if (ippool_newip(ippool, &member, (struct in_addr*)&addr, addrlen, 0)) {
     gtp_create_context_resp(gsn, pdp, GTPCAUSE_NO_RESOURCES);
     return 0; /* Allready in use, or no more available */
   }
 
-  pdp_ntoeua(&member->addr, &pdp->eua);
+  if (member->addrlen == 4)
+    pdp_ntoeua((struct in_addr*)&member->addr, &pdp->eua);
+  else
+    pdp_ntoeua6(&member->addr, &pdp->eua);
   pdp->peer = member;
   pdp->ipif = tun; /* TODO */
   member->peer = pdp;
@@ -183,14 +192,23 @@
 /* Callback for receiving messages from tun */
 int cb_tun_ind(struct tun_t *tun, void *pack, unsigned len) {
   struct ippoolm_t *ipm;
-  struct in_addr dst;
+  struct in6_addr dst;
+  size_t dstlen;
   struct tun_packet_t *iph = (struct tun_packet_t*) pack;
+  struct tun_packet6_t *ip6h = (struct tun_packet6_t*) pack;
   
-  dst.s_addr = iph->dst;
+  if (iph->ver == 4) {
+    memcpy(&dst, &iph->dst, sizeof(struct in_addr));
+    dstlen = 4;
+  }
+  else {
+    memcpy(&dst, ip6h->dst, sizeof(struct in6_addr));
+    dstlen = 16;
+  }
 
   if (debug) printf("Received packet from tun!\n");
 
-  if (ippool_getip(ippool, &ipm, &dst)) {
+  if (ippool_getip(ippool, &ipm, (struct in_addr*)&dst, dstlen)) {
     if (debug) printf("Received packet with no destination!!!\n");
     return 0;
   }
@@ -208,6 +226,16 @@
 
 int main(int argc, char **argv)
 {
+  struct addrinfo* ai;
+
+  struct addrinfo hints;
+  hints.ai_family = AF_UNSPEC;
+  hints.ai_socktype = SOCK_DGRAM;
+  hints.ai_flags = 0;
+  hints.ai_protocol = 0;
+
+  int err;
+
   /* gengeopt declarations */
   struct gengetopt_args_info args_info;
 
@@ -291,13 +319,14 @@
   /* Any port listening is not possible as a valid address is     */
   /* required for create_pdp_context_response messages            */
   if (args_info.listen_arg) {
-    if (!(host = gethostbyname(args_info.listen_arg))) {
+    if ((err = getaddrinfo(args_info.listen_arg, NULL, &hints, &ai))) {
       sys_err(LOG_ERR, __FILE__, __LINE__, 0,
 	      "Invalid listening address: %s!", args_info.listen_arg);
       exit(1);
     }
     else {
-      memcpy(&listen_.s_addr, host->h_addr, host->h_length);
+      memcpy(&listen_, ai->ai_addr, ai->ai_addrlen);
+      freeaddrinfo(ai);
     }
   }
   else {
@@ -307,18 +336,22 @@
 	    "edit %s configuration file\n", args_info.conf_arg);
     exit(1);
   }
-  
 
   /* 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 in6_addr net;
+    size_t netlen;
+    if(ippool_aton(&net, &netlen, &prefixlen, args_info.net_arg, 0)) {
       sys_err(LOG_ERR, __FILE__, __LINE__, 0,
 	      "Invalid network address: %s!", args_info.net_arg);
       exit(1);
     }
-    netaddr.s_addr = htonl(ntohl(net.s_addr) + 1);
-    destaddr.s_addr = htonl(ntohl(net.s_addr) + 1);
+    inc_addr((struct in_addr*)&net, netlen);
+    memcpy(&netaddr, &net, netlen);
+    netaddrlen = netlen;
+    memcpy(&destaddr, &net, netlen);
+    destaddrlen = netlen;
   }
   else {
     sys_err(LOG_ERR, __FILE__, __LINE__, 0,
@@ -346,21 +379,37 @@
 
   /* DNS1 and DNS2 */
 #ifdef HAVE_INET_ATON
-  dns1.s_addr = 0;
   if (args_info.pcodns1_arg) {
-    if (0 == inet_aton(args_info.pcodns1_arg, &dns1)) {
+    if ((err = getaddrinfo(args_info.pcodns1_arg, NULL, &hints, &ai))) {
       sys_err(LOG_ERR, __FILE__, __LINE__, 0,
 	      "Failed to convert pcodns1!");
       exit(1);
     }
+    if (ai->ai_family == AF_INET) {
+      dns1len = sizeof(struct in_addr);
+      memcpy(&dns1, &((struct sockaddr_in*)&ai->ai_addr)->sin_addr, dns1len);
+    }
+    else {
+      dns1len = sizeof(struct in6_addr);
+      memcpy(&dns1, &((struct sockaddr_in6*)&ai->ai_addr)->sin6_addr, dns1len);
+    }
+    freeaddrinfo(ai);
   }
-  dns2.s_addr = 0;
   if (args_info.pcodns2_arg) {
-    if (0 == inet_aton(args_info.pcodns2_arg, &dns2)) {
+    if ((err = getaddrinfo(args_info.pcodns2_arg, NULL, &hints, &ai))) {
       sys_err(LOG_ERR, __FILE__, __LINE__, 0,
 	      "Failed to convert pcodns2!");
       exit(1);
     }
+    if (ai->ai_family == AF_INET) {
+      dns2len = sizeof(struct in_addr);
+      memcpy(&dns2, &((struct sockaddr_in*)&ai->ai_addr)->sin_addr, dns2len);
+    }
+    else {
+      dns2len = sizeof(struct in6_addr);
+      memcpy(&dns2, &((struct sockaddr_in6*)&ai->ai_addr)->sin6_addr, dns2len);
+    }
+    freeaddrinfo(ai);
   }
 #else
   dns1.s_addr = 0;
@@ -382,7 +431,6 @@
     }
   }
 #endif
-
 
   pco.l = 20;
   pco.v[0] = 0x80; /* x0000yyy x=1, yyy=000: PPP */
@@ -450,7 +498,7 @@
 
   if (debug) printf("gtpclient: Initialising GTP tunnel\n");
   
-  if (gtp_new(&gsn, args_info.statedir_arg,  &listen_, GTP_MODE_GGSN)) {
+  if (gtp_new(&gsn, args_info.statedir_arg,  (struct sockaddr*)&listen_, GTP_MODE_GGSN)) {
     sys_err(LOG_ERR, __FILE__, __LINE__, 0,
 	    "Failed to create gtp");
     exit(1);
@@ -474,7 +522,10 @@
   }
 
   if (debug) printf("Setting tun IP address\n");
-  if (tun_setaddr(tun, &netaddr, &destaddr, &mask)) {
+  struct in_addr mask;
+  mask.s_addr = htons(0xffffffff << (32 - prefixlen));
+  if ((netaddrlen == 4 && tun_setaddr(tun, (struct in_addr*)&netaddr, (struct in_addr*)&destaddr, &mask))
+      || (netaddrlen == 16 && tun_setaddr6(tun, &netaddr, &destaddr, prefixlen))) {
     sys_err(LOG_ERR, __FILE__, __LINE__, 0,
 	    "Failed to set tun IP address");
     if (debug) printf("Failed to set tun IP address\n");
diff --git a/ggsn/ippool.c b/ggsn/ippool.c
index fa3d8af..5f25458 100644
--- a/ggsn/ippool.c
+++ b/ggsn/ippool.c
@@ -16,6 +16,7 @@
 #include <syslog.h>
 #include <string.h>
 #include <sys/socket.h>
+#include <netdb.h>
 #include <arpa/inet.h>
 #include "syserr.h"
 #include "ippool.h"
@@ -32,13 +33,16 @@
   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 [1024];
+    char s2 [1024];
+    getnameinfo(&this->member[n].addr, this->member[n].addrlen, s, sizeof(s), s2, sizeof(s2), NI_NUMERICHOST);
+
+    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;
@@ -50,12 +54,15 @@
   struct ippoolm_t *p_prev = NULL; 
 
   /* Insert into hash table */
-  hash = ippool_hash4(&member->addr) & this->hashmask;
+  if (member->addrlen == 4)
+	hash = ippool_hash4((struct in_addr*)&member->addr) & this->hashmask;
+  else
+	hash = ippool_hash6(&member->addr) & this->hashmask;
   for (p = this->hash[hash]; p; p = p->nexthash)
     p_prev = p;
   if (!p_prev)
     this->hash[hash] = member;
-  else 
+  else
     p_prev->nexthash = member;
   return 0; /* Always OK to insert */
 }
@@ -66,7 +73,10 @@
   struct ippoolm_t *p_prev = NULL; 
 
   /* Find in hash table */
-  hash = ippool_hash4(&member->addr) & this->hashmask;
+  if (member->addrlen == 4)
+	hash = ippool_hash4((struct in_addr*)&member->addr) & this->hashmask;
+  else
+	hash = ippool_hash6(&member->addr) & this->hashmask;
   for (p = this->hash[hash]; p; p = p->nexthash) {
     if (p == member) {
       break;
@@ -93,66 +103,80 @@
   return lookup((unsigned char*) &addr->s_addr, sizeof(addr->s_addr), 0);
 }
 
-#ifndef IPPOOL_NOIP6
 unsigned long int ippool_hash6(struct in6_addr *addr) {
-  return lookup((unsigned char*) addr->u6_addr8, sizeof(addr->u6_addr8), 0);
+ // TODO: review hash spread for ipv6
+  return lookup((unsigned char*) addr->s6_addr32, sizeof(addr->s6_addr32), 0);
 }
-#endif
-
 
 /* Get IP address and mask */
-int ippool_aton(struct in_addr *addr, struct in_addr *mask,
-		char *pool, int number) {
+int ippool_aton(struct in_addr *addr, size_t *addrlen, size_t* prefixlen, char *pool, int number) {
+	struct addrinfo* ai;
 
-  /* Parse only first instance of network for now */
-  /* Eventually "number" will indicate the token which we want to parse */
+	struct addrinfo hints;
+	hints.ai_family = AF_UNSPEC;
+	hints.ai_socktype = SOCK_DGRAM;
+	hints.ai_flags = 0;
+	hints.ai_protocol = 0;
 
-  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(LOG_ERR, __FILE__, __LINE__, 0, "Invalid mask");
-      return -1; /* Invalid mask */
-    }
-    mask->s_addr = htonl(0xffffffff << (32 - m1));
-    break;
-  case 8:
-    if (m1 >= 256 ||  m2 >= 256 || m3 >= 256 || m4 >= 256) {
-      sys_err(LOG_ERR, __FILE__, __LINE__, 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(LOG_ERR, __FILE__, __LINE__, 0, "Invalid mask");
-      return -1; /* Wrong mask format (not all ones followed by all zeros)*/
-    }
-    mask->s_addr = htonl(m);
-    break;
-  default:
-    sys_err(LOG_ERR, __FILE__, __LINE__, 0, "Invalid mask");
-    return -1; /* Invalid mask */
-  }
+	// Find '/'
+	char* prefixlen_str = strchr(pool, '/');
+	if (prefixlen_str) {
+		*prefixlen_str = '\0';
+		prefixlen_str++;
+		if (*prefixlen_str == '\0') {
+			sys_err(LOG_ERR, __FILE__, __LINE__, 0, "Empty prefix length specified");
+			return -1;
+		}
+	}
 
-  if (a1 >= 256 ||  a2 >= 256 || a3 >= 256 || a4 >= 256) {
-    sys_err(LOG_ERR, __FILE__, __LINE__, 0, "Wrong IP address format");
-    return -1;
-  }
-  else
-    addr->s_addr = htonl(a1 * 0x1000000 + a2 * 0x10000 + a3 * 0x100 + a4);
+	// Convert address
+	if ((err = getaddrinfo(pool, NULL, &hints, &ai))) {
+		sys_err(LOG_ERR, __FILE__, __LINE__, 0, "Bad address");
+		return -1;
+	}
 
-  return 0;
+	// Copy address, set lengths
+	if (ai->ai_family == AF_INET) {
+		*prefixlen = 32;
+		*addrlen = sizeof(struct in_addr);
+		memcpy(addr, &((struct sockaddr_in*)ai->ai_addr)->sin_addr, sizeof(struct in_addr));
+	}
+	else {
+		*prefixlen = 128;
+		*addrlen = sizeof(struct in6_addr);
+		memcpy(addr, &((struct sockaddr_in6*)ai->ai_addr)->sin6_addr, sizeof(struct in6_addr));
+	}
+
+	freeaddrinfo(ai);
+
+	// Parse prefixlen
+	if (prefixlen_str) {
+		char* e;
+		*prefixlen = strtol(prefixlen_str, &e, 10);
+		if (*e != '\0') {
+			sys_err(LOG_ERR, __FILE__, __LINE__, 0, "Prefixlen is not an int");
+			return -1;
+		}
+	}
+
+	if (*prefixlen > (*addrlen * 8)) {
+		sys_err(LOG_ERR, __FILE__, __LINE__, 0, "Prefixlen too big");
+		return -1;
+	}
+
+	return 0;
+}
+
+/* Increases ip4/6 address by 1 */
+void inc_addr(struct in_addr* addr, size_t addrlen)
+{
+	uint8_t* a = (uint8_t*)&addr->s_addr;
+	for (; addrlen > 0; addrlen--) {
+		if (++a[addrlen-1])
+			break;
+	}
 }
 
 /* Create new address pool */
@@ -162,10 +186,12 @@
   /* 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;
+  struct in6_addr addr;
+  size_t addrlen;
+  size_t addrprefixlen;
+  struct in6_addr stataddr;
+  size_t stataddrlen;
+  size_t stataddrprefixlen;
   unsigned int m;
   int listsize;
   int dynsize;
@@ -175,7 +201,7 @@
     dynsize = 0;
   }
   else {
-    if (ippool_aton(&addr, &mask, dyn, 0)) {
+    if (ippool_aton(&addr, &addrlen, &addrprefixlen, dyn, 0)) {
       sys_err(LOG_ERR, __FILE__, __LINE__, 0, 
 	      "Failed to parse dynamic pool");
       return -1;
@@ -186,8 +212,10 @@
       flags |= IPPOOL_NONETWORK;
     }
     
-    m = ntohl(mask.s_addr);
-    dynsize = ((~m)+1);
+    dynsize = (1 << (addrlen - addrprefixlen + 1)) - 1;
+
+    //m = ntohl(mask.s_addr);
+    //dynsize = ((~m)+1);
     if (flags & IPPOOL_NONETWORK)   /* Exclude network address from pool */
       dynsize--;
     if (flags & IPPOOL_NOGATEWAY)   /* Exclude gateway address from pool */
@@ -198,18 +226,17 @@
 
   if (!allowstat) {
     statsize = 0;
-    stataddr.s_addr = 0;
-    statmask.s_addr = 0;
+    stataddrlen = 0;
+    stataddrprefixlen = 0;
   }
   else {
-    if (ippool_aton(&stataddr, &statmask, stat, 0)) {
+    if (ippool_aton(&stataddr, &stataddrlen, &stataddrprefixlen, stat, 0)) {
       sys_err(LOG_ERR, __FILE__, __LINE__, 0,
 	      "Failed to parse static range");
       return -1;
     }
 
-    m = ntohl(statmask.s_addr);
-    statsize = ((~m)+1);
+    statsize = (1 << (addrlen - addrprefixlen + 1)) - 1;
     if (statsize > IPPOOL_STATSIZE) statsize = IPPOOL_STATSIZE;
   }
 
@@ -223,8 +250,10 @@
   
   (*this)->allowdyn  = allowdyn;
   (*this)->allowstat = allowstat;
-  (*this)->stataddr  = stataddr;
-  (*this)->statmask  = statmask;
+  if (stataddrlen > 0)
+	memcpy(&(*this)->stataddr, &stataddr, stataddrlen);
+  (*this)->stataddrlen  = stataddrlen;
+  (*this)->stataddrprefixlen  = stataddrprefixlen;
 
   (*this)->listsize += listsize;
   if (!((*this)->member = calloc(sizeof(struct ippoolm_t), listsize))){
@@ -252,15 +281,20 @@
   
   (*this)->firstdyn = NULL;
   (*this)->lastdyn = NULL;
+
+  if (flags & IPPOOL_NOGATEWAY) {
+	inc_addr(&addr, addrlen);
+	inc_addr(&addr, addrlen);
+  }
+  else if (flags & IPPOOL_NONETWORK)
+	inc_addr(&addr, addrlen);
+
   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);
-    
+    memcpy(&(*this)->member[i].addr, &addr, addrlen);
+    (*this)->member[i].addrlen = addrlen;
+    inc_addr(&addr, addrlen);
+
     (*this)->member[i].inuse = 0;
 
     /* Insert into list of unused */
@@ -280,8 +314,10 @@
   (*this)->firststat = NULL;
   (*this)->laststat = NULL;
   for (i = dynsize; i<listsize; i++) {
+    // TODO: set addrlen to 0?
+    memset(&(*this)->member[i].addr, 0, addrlen);
+    (*this)->member[i].addrlen = addrlen;
 
-    (*this)->member[i].addr.s_addr = 0;
     (*this)->member[i].inuse = 0;
 
     /* Insert into list of unused */
@@ -312,14 +348,19 @@
 
 /* Find an IP address in the pool */
 int ippool_getip(struct ippool_t *this, struct ippoolm_t **member,
-		 struct in_addr *addr) {
+		 struct in_addr *addr, size_t addrlen) {
   struct ippoolm_t *p;
   uint32_t hash;
 
   /* Find in hash table */
-  hash = ippool_hash4(addr) & this->hashmask;
+  if (addrlen == 4)
+    hash = ippool_hash4(addr) & this->hashmask;
+  else
+    hash = ippool_hash6(addr) & this->hashmask;
   for (p = this->hash[hash]; p; p = p->nexthash) {
-    if ((p->addr.s_addr == addr->s_addr) && (p->inuse)) {
+    if (p->addrlen == addrlen
+        && memcmp(&p->addr, addr, addrlen) == 0
+        && (p->inuse)) {
       if (member) *member = p;
       return 0;
     }
@@ -337,7 +378,7 @@
  * address space.
 **/
 int ippool_newip(struct ippool_t *this, struct ippoolm_t **member,
-		 struct in_addr *addr, int statip) {
+		 struct in_addr *addr, size_t addrlen, int statip) {
   struct ippoolm_t *p;
   struct ippoolm_t *p2 = NULL;
   uint32_t hash;
@@ -357,15 +398,25 @@
   if (0) (void)ippool_printaddr(this);
 
   /* First check to see if this type of address is allowed */
-  if ((addr) && (addr->s_addr) && statip) { /* IP address given */
+  int specified = 0;
+  if (addr) {
+    if (addrlen == 4 && ((struct in_addr*)&addr)->s_addr)
+      specified = 1;
+    if (addrlen == 16 && !IN6_IS_ADDR_UNSPECIFIED(addr))
+      specified = 1;
+  }
+  if (specified && statip) { /* IP address given */
     if (!this->allowstat) {
       sys_err(LOG_ERR, __FILE__, __LINE__, 0, "Static IP address not allowed");
       return -1;
     }
+    // TODO: ipv6 mask check
+/*
     if ((addr->s_addr & this->statmask.s_addr) != this->stataddr.s_addr) {
       sys_err(LOG_ERR, __FILE__, __LINE__, 0, "Static out of range");
       return -1;
     }
+*/
   }
   else {
     if (!this->allowdyn) {
@@ -376,11 +427,14 @@
   }
 
   /* 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;
+    if (addrlen == 4)
+      hash = ippool_hash4(addr) & this->hashmask;
+    else
+      hash = ippool_hash6(addr) & this->hashmask;
     for (p = this->hash[hash]; p; p = p->nexthash) {
-      if ((p->addr.s_addr == addr->s_addr)) {
+      if (p->addrlen == addrlen && !memcmp(&p->addr, &addr, addrlen)) {
 	p2 = p;
 	break;
       }
@@ -431,7 +485,7 @@
   /* 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(LOG_ERR, __FILE__, __LINE__, 0, 
 	      "No more IP addresses available");
@@ -452,7 +506,7 @@
     p2->next = NULL;
     p2->prev = NULL;
     p2->inuse = 2; /* Static address in use */
-    memcpy(&p2->addr, addr, sizeof(addr));
+    memcpy(&p2->addr, addr, addrlen);
     *member = p2;
     (void)ippool_hashadd(this, *member);
     if (0) (void)ippool_printaddr(this);
@@ -507,7 +561,8 @@
     this->laststat = member;
     
     member->inuse = 0;
-    member->addr.s_addr = 0;
+    memset(&member->addr, 0, sizeof(member->addr));
+    member->addrlen = 0;
     member->peer = NULL;
     member->nexthash = NULL;
     if (0) (void)ippool_printaddr(this);
@@ -517,10 +572,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/ggsn/ippool.h b/ggsn/ippool.h
index 02691a6..432fa8d 100644
--- a/ggsn/ippool.h
+++ b/ggsn/ippool.h
@@ -26,8 +26,6 @@
    in RFC2373.
 */
 
-#define IPPOOL_NOIP6
-
 #define IPPOOL_NONETWORK   0x01
 #define IPPOOL_NOBROADCAST 0x02
 #define IPPOOL_NOGATEWAY   0x04
@@ -40,7 +38,9 @@
   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 in6_addr stataddr;       /* Static address range network address */
+  size_t stataddrlen;
+  size_t stataddrprefixlen;
   struct in_addr statmask;       /* Static address range network mask */
   struct ippoolm_t *member;      /* Listsize array of members */
   unsigned int hashsize;                  /* Size of hash table */
@@ -54,11 +54,8 @@
 };
 
 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
+  size_t addrlen;
   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 */
@@ -81,25 +78,22 @@
 
 /* Find an IP address in the pool */
 extern int ippool_getip(struct ippool_t *this, struct ippoolm_t **member,
-		 struct in_addr *addr);
+		 struct in_addr *addr, size_t addrlen);
 
 /* 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 in_addr *addr, size_t addrlen, 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,
+extern int ippool_aton(struct in_addr *addr, size_t *addrlen, size_t *prefixlen,
 		       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
+
+void inc_addr(struct in_addr* addr, size_t addrlen);
 
 #endif	/* !_IPPOOL_H */
diff --git a/ggsn/tun.c b/ggsn/tun.c
index 1cc706b..96ee7ca 100644
--- a/ggsn/tun.c
+++ b/ggsn/tun.c
@@ -71,6 +71,13 @@
 
 #if defined(__linux__)
 
+// From linux/ipv6.h
+struct in6_ifreq {
+	struct in6_addr	ifr6_addr;
+	__u32		ifr6_prefixlen;
+	int		ifr6_ifindex;
+};
+
 int tun_nlattr(struct nlmsghdr *n, int nsize, int type, void *d, int dlen)
 {
   int len = RTA_LENGTH(dlen);
@@ -230,6 +237,9 @@
 }
 */
 
+#ifdef foo
+// None of this is currently required for IPv6
+//
 int tun_addaddr(struct tun_t *this,
 		struct in_addr *addr,
 		struct in_addr *dstaddr,
@@ -396,6 +406,7 @@
 #endif
   
 }
+#endif //foo
 
 
 int tun_setaddr(struct tun_t *this,
@@ -431,7 +442,7 @@
   }
 
   if (addr) { /* Set the interface address */
-    this->addr.s_addr = addr->s_addr;
+    ((struct in_addr*)&this->addr)->s_addr = addr->s_addr;
     ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr.s_addr = addr->s_addr;
     if (ioctl(fd, SIOCSIFADDR, (void *) &ifr) < 0) {
       if (errno != EEXIST) {
@@ -448,7 +459,7 @@
   }
 
   if (dstaddr) { /* Set the destination address */
-    this->dstaddr.s_addr = dstaddr->s_addr;
+    ((struct in_addr*)&this->dstaddr)->s_addr = addr->s_addr;
     ((struct sockaddr_in *) &ifr.ifr_dstaddr)->sin_addr.s_addr = 
       dstaddr->s_addr;
     if (ioctl(fd, SIOCSIFDSTADDR, (caddr_t) &ifr) < 0) {
@@ -501,6 +512,88 @@
 
   return 0;
 }
+
+int tun_setaddr6(struct tun_t *this,
+		struct in6_addr *addr,
+		struct in6_addr *dstaddr,
+		size_t prefixlen)
+{
+  struct in6_ifreq   ifr;
+  struct ifreq namereq;
+  int fd;
+
+  this->ip6 = 1;
+
+  memset (&ifr, '\0', sizeof (ifr));
+  memset (&namereq, '\0', sizeof (namereq));
+
+  /* Create a channel to the NET kernel. */
+  if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+    sys_err(LOG_ERR, __FILE__, __LINE__, errno,
+	    "socket() failed");
+    return -1;
+  }
+
+  // Get interface index
+  strncpy(namereq.ifr_name, this->devname, IFNAMSIZ);
+  namereq.ifr_name[IFNAMSIZ-1] = 0; /* Make sure to terminate */
+  if (ioctl(fd, SIOGIFINDEX, &namereq) < 0) {
+    sys_err(LOG_ERR, __FILE__, __LINE__, errno, "Could not get ifindex");
+    return -1;
+  }
+  ifr.ifr6_ifindex = namereq.ifr_ifindex;
+
+  ifr.ifr6_prefixlen = prefixlen;
+
+  if (addr) { /* Set the interface address */
+    memcpy(&this->addr, addr, sizeof(struct in6_addr));
+    memcpy(&ifr.ifr6_addr, addr, sizeof(struct in6_addr));
+    if (ioctl(fd, SIOCSIFADDR, (void *) &ifr) < 0) {
+      if (errno != EEXIST) {
+	sys_err(LOG_ERR, __FILE__, __LINE__, errno,
+		"ioctl(SIOCSIFADDR) failed");
+      }
+      else {
+	sys_err(LOG_WARNING, __FILE__, __LINE__, errno,
+		"ioctl(SIOCSIFADDR): Address already exists");
+      }
+      close(fd);
+      return -1;
+    }
+  }
+
+#ifdef xxx
+  // Looks like this is not possible/necessary for ipv6?
+  if (dstaddr) { /* Set the destination address */
+    memcpy(&this->dstaddr, dstaddr, sizeof(struct in6_addr));
+    memcpy(&ifr.ifr6_addr, dstaddr, sizeof(struct in6_addr));
+    if (ioctl(fd, SIOCSIFDSTADDR, (caddr_t) &ifr) < 0) {
+      sys_err(LOG_ERR, __FILE__, __LINE__, errno,
+	      "ioctl(SIOCSIFDSTADDR) failed");
+      close(fd);
+      return -1;
+    }
+  }
+#endif //xxx
+
+  close(fd);
+  this->addrs++;
+
+  /* On linux the route to the interface is set automatically
+     on FreeBSD we have to do this manually */
+
+  /* TODO: How does it work on Solaris? */
+
+  tun_sifflags(this, IFF_UP | IFF_RUNNING);
+
+#if defined(__FreeBSD__) || defined (__APPLE__)
+  tun_addroute(this, dstaddr, addr, netmask);
+  this->routes = 1;
+#endif
+
+  return 0;
+}
+
 
 int tun_route(struct tun_t *this,
 	      struct in_addr *dst,
@@ -666,6 +759,7 @@
   (*tun)->cb_ind = NULL;
   (*tun)->addrs = 0;
   (*tun)->routes = 0;
+  (*tun)->ip6 = 0;
   
 #if defined(__linux__)
   /* Open the actual tun device */
@@ -797,8 +891,9 @@
 int tun_free(struct tun_t *tun)
 {
 
+  // TODO: ipv6
   if (tun->routes) {
-    tun_delroute(tun, &tun->dstaddr, &tun->addr, &tun->netmask);
+    //tun_delroute(tun, &tun->dstaddr, &tun->addr, &tun->netmask);
   }
 
   if (close(tun->fd)) {
@@ -883,9 +978,10 @@
   char snet[TUN_ADDRSIZE];
   char smask[TUN_ADDRSIZE];
 
-  strncpy(snet, inet_ntoa(tun->addr), sizeof(snet));
+  //TODO: fix for IPv6
+  //strncpy(snet, inet_ntoa(tun->addr), sizeof(snet));
   snet[sizeof(snet)-1] = 0;
-  strncpy(smask, inet_ntoa(tun->netmask), sizeof(smask));
+  //strncpy(smask, inet_ntoa(tun->netmask), sizeof(smask));
   smask[sizeof(smask)-1] = 0;
   
   /* system("ipup /dev/tun0 192.168.0.10 255.255.255.0"); */
diff --git a/ggsn/tun.h b/ggsn/tun.h
index 7972c53..1c239eb 100644
--- a/ggsn/tun.h
+++ b/ggsn/tun.h
@@ -17,9 +17,18 @@
 #define TUN_ADDRSIZE     128
 #define TUN_NLBUFSIZE   1024
 
+/* This should not be re-declared!  Why not just use netinet/ip(6).h?
+ * It is not completely functional as it is here.  Just the minimum
+ * amount of hacking has been done to make it work. */
 struct tun_packet_t {
+#if BYTE_ORDER == LITTLE_ENDIAN
+  unsigned int ihl:4;
+  unsigned int ver:4;
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
   unsigned int ver:4;
   unsigned int ihl:4;
+#endif
   unsigned int dscp:6;
   unsigned int ecn:2;
   unsigned int length:16;
@@ -29,8 +38,19 @@
   unsigned int ttl:8;
   unsigned int protocol:8;
   unsigned int check:16;
-  unsigned int src:32;
-  unsigned int dst:32;
+  uint32_t src;
+  uint32_t dst;
+};
+
+struct tun_packet6_t {
+  unsigned int ver:4;
+  unsigned int tclass:8;
+  unsigned int flowlabel:20;
+  unsigned int payloadlen:16;
+  unsigned int nexthdr:8;
+  unsigned int hoplim:8;
+  unsigned char src[16];
+  unsigned char dst[16];
 };
 
 
@@ -39,10 +59,12 @@
  *************************************************************/
 
 struct tun_t {
+  int ip6;
   int fd;                /* File descriptor to tun interface */
-  struct in_addr addr;
-  struct in_addr dstaddr;
-  struct in_addr netmask;
+  struct in6_addr addr;
+  struct in6_addr dstaddr;
+  struct in_addr netmask; //ipv4
+  size_t prefixlen;
   int addrs;             /* Number of allocated IP addresses */
   int routes;            /* One if we allocated an automatic route */
   char devname[IFNAMSIZ];/* Name of the tun device */
@@ -55,19 +77,13 @@
 extern int tun_decaps(struct tun_t *this);
 extern int tun_encaps(struct tun_t *tun, void *pack, unsigned len);
 
-extern int tun_addaddr(struct tun_t *this, struct in_addr *addr,
-		       struct in_addr *dstaddr, struct in_addr *netmask);
-
-
 extern int tun_setaddr(struct tun_t *this, struct in_addr *our_adr, 
 		       struct in_addr *his_adr, struct in_addr *net_mask);
-
-int tun_addroute(struct tun_t *this, struct in_addr *dst, 
-		 struct in_addr *gateway, struct in_addr *mask);
+extern int tun_setaddr6(struct tun_t *this, struct in6_addr *our_adr,
+		       struct in6_addr *his_adr, size_t prefixlen);
 
 extern int tun_set_cb_ind(struct tun_t *this, 
      int (*cb_ind) (struct tun_t *tun, void *pack, unsigned len));
-
 
 extern int tun_runscript(struct tun_t *tun, char* script);
 
diff --git a/gtp/gtp.c b/gtp/gtp.c
index 5e6c4ab..cceabda 100644
--- a/gtp/gtp.c
+++ b/gtp/gtp.c
@@ -42,6 +42,7 @@
 #include <string.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <netdb.h>
 
 #include <arpa/inet.h>
 
@@ -132,13 +133,13 @@
 
 
 int gtp_set_cb_unsup_ind(struct gsn_t *gsn,
-			 int (*cb) (struct sockaddr_in *peer)) {
+			 int (*cb) (struct sockaddr *peer)) {
   gsn->cb_unsup_ind = cb;
   return 0;
 }
 
 int gtp_set_cb_extheader_ind(struct gsn_t *gsn,
-			     int (*cb) (struct sockaddr_in *peer)) {
+			     int (*cb) (struct sockaddr *peer)) {
   gsn->cb_extheader_ind = cb;
   return 0;
 }
@@ -397,20 +398,13 @@
 
 int gtp_req(struct gsn_t *gsn, int version, struct pdp_t *pdp,
 	    union gtp_packet *packet, int len, 
-	    struct in_addr *inetaddr, void *cbp) {
-  struct sockaddr_in addr;
+	    struct sockaddr *sockaddr, void *cbp) {
   struct qmsg_t *qmsg;
   int fd;
 
-  memset(&addr, 0, sizeof(addr));
-  addr.sin_family = AF_INET;
-  addr.sin_addr = *inetaddr;
-#if defined(__FreeBSD__) || defined(__APPLE__)
-  addr.sin_len = sizeof(addr);
-#endif
-
   if ((packet->flags & 0xe0) == 0x00) { /* Version 0 */
-    addr.sin_port = htons(GTP0_PORT);
+    // sin_port in same place for sockaddr_in and sockaddr_in6
+    ((struct sockaddr_in*)sockaddr)->sin_port = htons(GTP0_PORT);
     packet->gtp0.h.length = hton16(len - GTP0_HEADER_SIZE);
     packet->gtp0.h.seq = hton16(gsn->seq_next);
     if (pdp)
@@ -424,7 +418,7 @@
     fd = gsn->fd0;
   }
   else if ((packet->flags & 0xe2) == 0x22) { /* Version 1 with seq */
-    addr.sin_port = htons(GTP1C_PORT);
+    ((struct sockaddr_in*)sockaddr)->sin_port = htons(GTP1C_PORT);
     packet->gtp1l.h.length = hton16(len - GTP1_HEADER_SIZE_SHORT);
     packet->gtp1l.h.seq = hton16(gsn->seq_next);
     if (pdp && ((packet->gtp1l.h.type == GTP_GPDU) ||
@@ -439,14 +433,14 @@
   }
   
   if (sendto(fd, packet, len, 0,
-	     (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+	     sockaddr, sockaddr->sa_family==AF_INET?sizeof(struct sockaddr_in):sizeof(struct sockaddr_in6)) < 0) {
     gsn->err_sendto++;
     gtp_err(LOG_ERR, __FILE__, __LINE__, "Sendto(fd=%d, msg=%lx, len=%d) failed: Error = %s", fd, (unsigned long) &packet, len, strerror(errno));
     return -1;
   }
 
   /* Use new queue structure */
-  if (queue_newmsg(gsn->queue_req, &qmsg, &addr, gsn->seq_next)) {
+  if (queue_newmsg(gsn->queue_req, &qmsg, sockaddr, gsn->seq_next)) {
     gsn->err_queuefull++;
     gtp_err(LOG_ERR, __FILE__, __LINE__, "Retransmit queue is full");
   }
@@ -509,7 +503,7 @@
     }
     else {
       if (sendto(qmsg->fd, &qmsg->p, qmsg->l, 0,
-		 (struct sockaddr *) &qmsg->peer, sizeof(struct sockaddr_in)) < 0) {
+		 (struct sockaddr *) &qmsg->peer, qmsg->peer.ss_family == AF_INET?sizeof(struct sockaddr_in):sizeof(struct sockaddr_in6)) < 0) {
 	gsn->err_sendto++;
 	gtp_err(LOG_ERR, __FILE__, __LINE__, "Sendto(fd0=%d, msg=%lx, len=%d) failed: Error = %s", gsn->fd0, (unsigned long) &qmsg->p, qmsg->l, strerror(errno));
       }
@@ -550,7 +544,7 @@
 
 int gtp_resp(int version, struct gsn_t *gsn, struct pdp_t *pdp, 
 	     union gtp_packet *packet, int len,
-	     struct sockaddr_in *peer, int fd, 
+	     struct sockaddr *peer, int fd,
 	     uint16_t seq, uint64_t tid) {
   struct qmsg_t *qmsg;
 
@@ -583,7 +577,7 @@
   }
 
   if (sendto(fd, packet, len, 0,
-	     (struct sockaddr *) peer, sizeof(struct sockaddr_in)) < 0) {
+	     peer, peer->sa_family==AF_INET?sizeof(struct sockaddr_in):sizeof(struct sockaddr_in6)) < 0) {
     gsn->err_sendto++;
     gtp_err(LOG_ERR, __FILE__, __LINE__, "Sendto(fd=%d, msg=%lx, len=%d) failed: Error = %s", fd, (unsigned long) &packet, len, strerror(errno));
     return -1;
@@ -651,7 +645,7 @@
 }
 
 int gtp_dublicate(struct gsn_t *gsn, int version,  
-		  struct sockaddr_in *peer, uint16_t seq) {
+		  struct sockaddr *peer, uint16_t seq) {
   struct qmsg_t *qmsg;
 
   if(queue_seqget(gsn->queue_resp, &qmsg, peer, seq)) {
@@ -664,7 +658,7 @@
   }
   
   if (sendto(qmsg->fd, &qmsg->p, qmsg->l, 0,
-	     (struct sockaddr *) peer, sizeof(struct sockaddr_in)) < 0) {
+	     peer, peer->sa_family==AF_INET?sizeof(struct sockaddr_in):sizeof(struct sockaddr_in6)) < 0) {
     gsn->err_sendto++;
     gtp_err(LOG_ERR, __FILE__, __LINE__, "Sendto(fd=%d, msg=%lx, len=%d) failed: Error = %s", qmsg->fd, (unsigned long) &qmsg->p, qmsg->l, strerror(errno));
   }
@@ -718,10 +712,10 @@
 
 
 
-int gtp_new(struct gsn_t **gsn, char *statedir, struct in_addr *listen,
+int gtp_new(struct gsn_t **gsn, char *statedir, struct sockaddr *listen,
 	    int mode) 
 {
-  struct sockaddr_in addr;
+  struct sockaddr_storage addr;
   
   syslog(LOG_ERR, "GTP: gtp_newgsn() started");
 
@@ -748,71 +742,74 @@
   (*gsn)->cb_data_ind = 0;
 
   /* Store function parameters */
-  (*gsn)->gsnc = *listen;
-  (*gsn)->gsnu = *listen;
+  memcpy(&(*gsn)->gsnc, listen, listen->sa_family == AF_INET?sizeof(struct sockaddr_in):sizeof(struct sockaddr_in6));
+  memcpy(&(*gsn)->gsnu, listen, listen->sa_family == AF_INET?sizeof(struct sockaddr_in):sizeof(struct sockaddr_in6));
   (*gsn)->mode = mode;
 
+  int domain = AF_INET;
+  socklen_t sa_len = sizeof(struct sockaddr_in);
+  if (listen->sa_family == AF_INET6) {
+    domain = AF_INET6;
+    sa_len = sizeof(struct sockaddr_in6);
+  }
 
   /* Create GTP version 0 socket */
-  if (((*gsn)->fd0 = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {
+  if (((*gsn)->fd0 = socket(domain, SOCK_DGRAM, 0)) < 0 ) {
     (*gsn)->err_socket++;
     gtp_err(LOG_ERR, __FILE__, __LINE__, "socket(domain=%d, type=%d, protocol=%d) failed: Error = %s", AF_INET, SOCK_DGRAM, 0, strerror(errno));
     return -1;
   }
     
-  memset(&addr, 0, sizeof(addr));
-  addr.sin_family = AF_INET;
-  addr.sin_addr = *listen;  /* Same IP for user traffic and signalling*/
-  addr.sin_port = htons(GTP0_PORT);
 #if defined(__FreeBSD__) || defined(__APPLE__)
   addr.sin_len = sizeof(addr);
 #endif
+  memcpy(&addr, listen, sa_len);
+  // port member in same position for sockaddr_in and sockaddr_in6
+  ((struct sockaddr_in*)&addr)->sin_port = htons(GTP0_PORT);
   
-  if (bind((*gsn)->fd0, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+  if (bind((*gsn)->fd0, (struct sockaddr *) &addr, sa_len) < 0) {
     (*gsn)->err_socket++;
-    gtp_err(LOG_ERR, __FILE__, __LINE__, "bind(fd0=%d, addr=%lx, len=%d) failed: Error = %s", (*gsn)->fd0, (unsigned long) &addr, sizeof(addr), strerror(errno));
+    gtp_err(LOG_ERR, __FILE__, __LINE__, "bind(fd0=%d, addr=%lx, len=%d) failed: Error = %s", (*gsn)->fd0, (unsigned long) &addr, sa_len, strerror(errno));
     return -1;
   }
 
   /* Create GTP version 1 control plane socket */
-  if (((*gsn)->fd1c = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {
+  if (((*gsn)->fd1c = socket(domain, SOCK_DGRAM, 0)) < 0 ) {
     (*gsn)->err_socket++;
     gtp_err(LOG_ERR, __FILE__, __LINE__, "socket(domain=%d, type=%d, protocol=%d) failed: Error = %s", AF_INET, SOCK_DGRAM, 0, strerror(errno));
     return -1;
   }
 
-  memset(&addr, 0, sizeof(addr));
-  addr.sin_family = AF_INET;
-  addr.sin_addr = *listen;  /* Same IP for user traffic and signalling*/
-  addr.sin_port = htons(GTP1C_PORT);
 #if defined(__FreeBSD__) || defined(__APPLE__)
   addr.sin_len = sizeof(addr);
 #endif
+   memcpy(&addr, listen, sa_len);
+  // port member in same position for sockaddr_in and sockaddr_in6
+  ((struct sockaddr_in*)&addr)->sin_port = htons(GTP1C_PORT);
   
-  if (bind((*gsn)->fd1c, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+  if (bind((*gsn)->fd1c, (struct sockaddr *) &addr, sa_len) < 0) {
     (*gsn)->err_socket++;
-    gtp_err(LOG_ERR, __FILE__, __LINE__, "bind(fd1c=%d, addr=%lx, len=%d) failed: Error = %s", (*gsn)->fd1c, (unsigned long) &addr, sizeof(addr), strerror(errno));
+    gtp_err(LOG_ERR, __FILE__, __LINE__, "bind(fd1c=%d, addr=%lx, len=%d) failed: Error = %s", (*gsn)->fd1c, (unsigned long) &addr, sa_len, strerror(errno));
     return -1;
   }
 
   /* Create GTP version 1 user plane socket */
-  if (((*gsn)->fd1u = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {
+  if (((*gsn)->fd1u = socket(domain, SOCK_DGRAM, 0)) < 0 ) {
     (*gsn)->err_socket++;
     gtp_err(LOG_ERR, __FILE__, __LINE__, "socket(domain=%d, type=%d, protocol=%d) failed: Error = %s", AF_INET, SOCK_DGRAM, 0, strerror(errno));
     return -1;
   }
 
-  memset(&addr, 0, sizeof(addr));
-  addr.sin_family = AF_INET;
-  addr.sin_addr = *listen;  /* Same IP for user traffic and signalling*/
-  addr.sin_port = htons(GTP1U_PORT);
 #if defined(__FreeBSD__) || defined(__APPLE__)
   addr.sin_len = sizeof(addr);
 #endif
+  memcpy(&addr, listen, sa_len);
+  // port member in same position for sockaddr_in and sockaddr_in6
+  ((struct sockaddr_in*)&addr)->sin_port = htons(GTP1U_PORT);
   
-  if (bind((*gsn)->fd1u, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+  if (bind((*gsn)->fd1u, (struct sockaddr *) &addr, sa_len) < 0) {
     (*gsn)->err_socket++;
-    gtp_err(LOG_ERR, __FILE__, __LINE__, "bind(fd1c=%d, addr=%lx, len=%d) failed: Error = %s", (*gsn)->fd1c, (unsigned long) &addr, sizeof(addr), strerror(errno));
+    gtp_err(LOG_ERR, __FILE__, __LINE__, "bind(fd1c=%d, addr=%lx, len=%d) failed: Error = %s", (*gsn)->fd1c, (unsigned long) &addr, sa_len, strerror(errno));
     return -1;
   }
 
@@ -864,7 +861,7 @@
 
 /* Send off an echo request */
 int gtp_echo_req(struct gsn_t *gsn, int version, void *cbp,
-		 struct in_addr *inetaddr)
+		 struct sockaddr *inetaddr)
 {
   union gtp_packet packet;
   unsigned int length = get_default_gtp(version, GTP_ECHO_REQ, &packet);
@@ -873,7 +870,7 @@
 
 /* Send off an echo reply */
 int gtp_echo_resp(struct gsn_t *gsn, int version,
-		  struct sockaddr_in *peer, int fd,
+		  struct sockaddr *peer, int fd,
 		  void *pack, unsigned len)
 {
   union gtp_packet packet;
@@ -885,7 +882,7 @@
 
 
 /* Handle a received echo request */
-int gtp_echo_ind(struct gsn_t *gsn, int version, struct sockaddr_in *peer, 
+int gtp_echo_ind(struct gsn_t *gsn, int version, struct sockaddr *peer, 
 		 int fd, void *pack, unsigned len) {
 
   /* Check if it was a dublicate request */
@@ -896,7 +893,7 @@
 }
 
 /* Handle a received echo reply */
-int gtp_echo_conf(struct gsn_t *gsn, int version, struct sockaddr_in *peer,
+int gtp_echo_conf(struct gsn_t *gsn, int version, struct sockaddr *peer,
 		  void *pack, unsigned len) {
   union gtpie_member *ie[GTPIE_SIZE];
   unsigned char recovery;
@@ -942,7 +939,7 @@
  * only listen to the GTP0 port, and therefore will never receive
  * anything else than GTP0 */
 
-int gtp_unsup_req(struct gsn_t *gsn, int version, struct sockaddr_in *peer,
+int gtp_unsup_req(struct gsn_t *gsn, int version, struct sockaddr *peer,
 		  int fd, void *pack, unsigned len)
 {
   union gtp_packet packet;
@@ -954,7 +951,7 @@
 }
 
 /* Handle a Version Not Supported message */
-int gtp_unsup_ind(struct gsn_t *gsn, struct sockaddr_in *peer, 
+int gtp_unsup_ind(struct gsn_t *gsn, struct sockaddr *peer,
 		  void *pack, unsigned len) {
 
   if (gsn->cb_unsup_ind) gsn->cb_unsup_ind(peer);
@@ -963,7 +960,7 @@
 }
 
 /* Send off an Supported Extension Headers Notification */
-int gtp_extheader_req(struct gsn_t *gsn, int version, struct sockaddr_in *peer,
+int gtp_extheader_req(struct gsn_t *gsn, int version, struct sockaddr *peer,
 		      int fd, void *pack, unsigned len)
 {
   union gtp_packet packet;
@@ -983,7 +980,7 @@
 }
 
 /* Handle a Supported Extension Headers Notification */
-int gtp_extheader_ind(struct gsn_t *gsn, struct sockaddr_in *peer, 
+int gtp_extheader_ind(struct gsn_t *gsn, struct sockaddr *peer,
 		      void *pack, unsigned len) {
 
   if (gsn->cb_extheader_ind) gsn->cb_extheader_ind(peer);
@@ -1223,7 +1220,7 @@
 
 /* Handle Create PDP Context Request */
 int gtp_create_pdp_ind(struct gsn_t *gsn, int version,
-			struct sockaddr_in *peer, int fd, 
+			struct sockaddr *peer, int fd,
 			void *pack, unsigned len) {
   struct pdp_t *pdp, *pdp_old; 
   struct pdp_t pdp_buf;
@@ -1246,7 +1243,7 @@
   }
 
   pdp->seq = seq;
-  pdp->sa_peer = *peer;
+  memcpy(&pdp->sa_peer, peer, peer->sa_family==AF_INET?sizeof(struct sockaddr_in):sizeof(struct sockaddr_in6));
   pdp->fd = fd;
   pdp->version = version;
 
@@ -1473,9 +1470,32 @@
   }
 
   /* Initialize our own IP addresses */
-  in_addr2gsna(&pdp->gsnlc, &gsn->gsnc);
-  in_addr2gsna(&pdp->gsnlu, &gsn->gsnu);
-  
+  struct in6_addr* a;
+  size_t alen;
+  if (gsn->gsnc.ss_family == AF_INET) {
+    struct sockaddr_in* s = (struct sockaddr_in*)&gsn->gsnc;
+    a = (struct in6_addr*)&s->sin_addr;
+    alen = sizeof(struct in_addr);
+  }
+  else {
+    struct sockaddr_in6* s = (struct sockaddr_in6*)&gsn->gsnc;
+    a = &s->sin6_addr;
+    alen = sizeof(struct in6_addr);
+  }
+  in_addr2gsna(&pdp->gsnlc, a, alen);
+
+  if (gsn->gsnu.ss_family == AF_INET) {
+    struct sockaddr_in* s = (struct sockaddr_in*)&gsn->gsnu;
+    a = (struct in6_addr*)&s->sin_addr;
+    alen = sizeof(struct in_addr);
+  }
+  else {
+    struct sockaddr_in6* s = (struct sockaddr_in6*)&gsn->gsnu;
+    a = &s->sin6_addr;
+    alen = sizeof(struct in6_addr);
+  }
+  in_addr2gsna(&pdp->gsnlu, a, alen);
+ 
   if (GTP_DEBUG) printf("gtp_create_pdp_ind: Before pdp_tidget\n");
   
   if (!pdp_getimsi(&pdp_old, pdp->imsi, pdp->nsapi)) {
@@ -1550,7 +1570,7 @@
 
 /* Handle Create PDP Context Response */
 int gtp_create_pdp_conf(struct gsn_t *gsn, int version,
-			 struct sockaddr_in *peer, 
+			 struct sockaddr *peer,
 			 void *pack, unsigned len) {
   struct pdp_t *pdp; 
   union gtpie_member *ie[GTPIE_SIZE];
@@ -1741,7 +1761,7 @@
 
 /* API: Send Update PDP Context Request */
 int gtp_update_context(struct gsn_t *gsn, struct pdp_t *pdp, void *cbp,
-		       struct in_addr* inetaddr) {
+		       struct sockaddr* inetaddr) {
   union gtp_packet packet;
   unsigned int length = get_default_gtp(pdp->version, GTP_UPDATE_PDP_REQ, &packet);
   
@@ -1817,7 +1837,7 @@
 
 /* Send Update PDP Context Response */
 int gtp_update_pdp_resp(struct gsn_t *gsn, int version, 
-			struct sockaddr_in *peer, int fd, 
+			struct sockaddr *peer, int fd,
 			void *pack, unsigned len,
 			struct pdp_t *pdp, uint8_t cause) {
   
@@ -1878,7 +1898,7 @@
 
 /* Handle Update PDP Context Request */
 int gtp_update_pdp_ind(struct gsn_t *gsn, int version,
-		       struct sockaddr_in *peer, int fd, 
+		       struct sockaddr *peer, int fd,
 		       void *pack, unsigned len) {
   struct pdp_t *pdp;
   struct pdp_t pdp_backup;
@@ -2098,7 +2118,7 @@
 
 /* Handle Update PDP Context Response */
 int gtp_update_pdp_conf(struct gsn_t *gsn, int version,
-			struct sockaddr_in *peer, 
+			struct sockaddr *peer,
 			void *pack, unsigned len) {
   struct pdp_t *pdp; 
   union gtpie_member *ie[GTPIE_SIZE];
@@ -2199,13 +2219,15 @@
 			   int teardown) {
   union gtp_packet packet;
   unsigned int length = get_default_gtp(pdp->version, GTP_DELETE_PDP_REQ, &packet);
-  struct in_addr addr;
+  struct in6_addr addr;
+  size_t addrlen;
   struct pdp_t *linked_pdp;
   struct pdp_t *secondary_pdp;
   int n;
   int count = 0;
 
-  if (gsna2in_addr(&addr, &pdp->gsnrc)) {
+  addrlen = sizeof(addr);
+  if (gsna2in_addr(&addr, &addrlen, &pdp->gsnrc)) {
     gsn->err_address++;
     gtp_err(LOG_ERR, __FILE__, __LINE__, "GSN address conversion failed");
     return EOF;
@@ -2235,7 +2257,22 @@
 	      pdp->nsapi);
   }
 
-  gtp_req(gsn, pdp->version, pdp, &packet, length, &addr, cbp);
+  struct sockaddr_storage sa_s;
+  struct sockaddr_in* sa_in = (struct sockaddr_in*)&sa_s;
+  struct sockaddr_in6* sa_in6 = (struct sockaddr_in*)&sa_s;
+  memset(&sa_s, 0, sizeof(sa_s));
+  void* p;
+  if (addrlen == 4) {
+    sa_in->sin_family = AF_INET;
+    p = &sa_in->sin_addr;
+  }
+  else {
+    sa_in6->sin6_family = AF_INET6;
+    p = &sa_in6->sin6_addr;
+  }
+  memcpy(p, &addr, addrlen);
+
+  gtp_req(gsn, pdp->version, pdp, &packet, length, (struct sockaddr*)&sa_s, cbp);
 
   if (teardown) { /* Remove all contexts */
     for (n=0; n< PDP_MAXNSAPI; n++) {
@@ -2268,7 +2305,7 @@
 
 /* Send Delete PDP Context Response */
 int gtp_delete_pdp_resp(struct gsn_t *gsn, int version,
-			struct sockaddr_in *peer, int fd,
+			struct sockaddr *peer, int fd,
 			void *pack, unsigned len, 
 			struct pdp_t *pdp, struct pdp_t *linked_pdp, 
 			uint8_t cause, int teardown)
@@ -2317,7 +2354,7 @@
 
 /* Handle Delete PDP Context Request */
 int gtp_delete_pdp_ind(struct gsn_t *gsn, int version,
-		       struct sockaddr_in *peer, int fd,
+		       struct sockaddr *peer, int fd,
 		       void *pack, unsigned len) {
   struct pdp_t *pdp = NULL;
   struct pdp_t *linked_pdp = NULL;
@@ -2399,7 +2436,7 @@
 
 /* Handle Delete PDP Context Response */
 int gtp_delete_pdp_conf(struct gsn_t *gsn, int version,
-			struct sockaddr_in *peer, 
+			struct sockaddr *peer,
 			void *pack, unsigned len) {
   union gtpie_member *ie[GTPIE_SIZE];
   uint8_t cause;
@@ -2445,7 +2482,7 @@
 
 /* Send Error Indication (response to a GPDU message */
 int gtp_error_ind_resp(struct gsn_t *gsn, int version,
-		       struct sockaddr_in *peer, int fd,
+		       struct sockaddr *peer, int fd,
 		       void *pack, unsigned len)
 {
   union gtp_packet packet;
@@ -2457,7 +2494,7 @@
 
 /* Handle Error Indication */
 int gtp_error_ind_conf(struct gsn_t *gsn, int version,
-		       struct sockaddr_in *peer, 
+		       struct sockaddr *peer,
 		       void *pack, unsigned len) {
   struct pdp_t *pdp; 
   
@@ -2479,7 +2516,7 @@
 }
 
 int gtp_gpdu_ind(struct gsn_t *gsn, int version,
-		  struct sockaddr_in *peer, int fd,
+		  struct sockaddr *peer, int fd,
 		  void *pack, unsigned len) {
 
   int hlen = GTP1_HEADER_SIZE_SHORT;
@@ -2516,7 +2553,16 @@
   }
   
   /* If the GPDU was not from the peer GSN tell him to delete context */
-  if (memcmp(&peer->sin_addr, pdp->gsnru.v, pdp->gsnru.l)) { /* TODO Range? */
+  struct sockaddr_in* peer4 = NULL;
+  struct sockaddr_in6* peer6 = NULL;
+  if (peer->sa_family == AF_INET)
+    peer4 = (struct sockaddr_in*)peer;
+  else
+    peer6 = (struct sockaddr_in6*)peer;
+
+  if ((peer4 && memcmp(&peer4->sin_addr, pdp->gsnru.v, pdp->gsnru.l))
+	|| (peer6 && memcmp(&peer6->sin6_addr, pdp->gsnru.v, pdp->gsnru.l)) )  {
+
     gsn->err_unknownpdp++;
     gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
 		"Unknown PDP context");
@@ -2540,7 +2586,7 @@
 int gtp_decaps0(struct gsn_t *gsn)
 {
   unsigned char buffer[PACKET_MAX];
-  struct sockaddr_in peer;
+  struct sockaddr_storage peer;
   size_t peerlen;
   int status;
   struct gtp0_header *pheader;
@@ -2672,7 +2718,7 @@
 int gtp_decaps1c(struct gsn_t *gsn)
 {
   unsigned char buffer[PACKET_MAX];
-  struct sockaddr_in peer;
+  struct sockaddr_storage peer;
   size_t peerlen;
   int status;
   struct gtp1_header_short *pheader;
@@ -2831,7 +2877,7 @@
 int gtp_decaps1u(struct gsn_t *gsn)
 {
   unsigned char buffer[PACKET_MAX];
-  struct sockaddr_in peer;
+  struct sockaddr_storage peer;
   size_t peerlen;
   int status;
   struct gtp1_header_short *pheader;
@@ -2953,22 +2999,33 @@
 	     void *pack, unsigned len)
 {
   union gtp_packet packet;
-  struct sockaddr_in addr;
+  struct sockaddr_storage addr_s;
+  struct sockaddr_in* addr = (struct sockaddr_in*)&addr_s;
+  struct sockaddr_in6* addr6 = (struct sockaddr_in6*)&addr_s;
   int fd;
   int length;
 
-  memset(&addr, 0, sizeof(addr));
-  addr.sin_family = AF_INET;
-#if defined(__FreeBSD__) || defined(__APPLE__)
-  addr.sin_len = sizeof(addr);
-#endif
+  memset(&addr_s, 0, sizeof(addr_s));
 
-  memcpy(&addr.sin_addr, pdp->gsnru.v,pdp->gsnru.l); /* TODO range check */
+  if (pdp->gsnru.l == 4) {
+    addr->sin_family = AF_INET;
+    memcpy(&addr->sin_addr, pdp->gsnru.v,pdp->gsnru.l); /* TODO range check */
+#if defined(__FreeBSD__) || defined(__APPLE__)
+    addr->sin_len = sizeof(*addr);
+#endif
+  }
+  else {
+    addr6->sin6_family = AF_INET6;
+    memcpy(&addr6->sin6_addr, pdp->gsnru.v,pdp->gsnru.l); /* TODO range check */
+#if defined(__FreeBSD__) || defined(__APPLE__)
+    addr6->sin6_len = sizeof(*addr6);
+#endif
+  }
 
   if (pdp->version == 0) {
     
     length = GTP0_HEADER_SIZE+len;
-    addr.sin_port = htons(GTP0_PORT);
+    addr->sin_port = htons(GTP0_PORT);
     fd = gsn->fd0;
     
     get_default_gtp(0, GTP_GPDU, &packet);
@@ -2989,7 +3046,7 @@
   else if (pdp->version == 1) {
     
     length = GTP1_HEADER_SIZE_LONG+len;
-    addr.sin_port = htons(GTP1U_PORT);
+    addr->sin_port = htons(GTP1U_PORT);
     fd = gsn->fd1u;
 
     get_default_gtp(1, GTP_GPDU, &packet);
@@ -3019,7 +3076,7 @@
   }
 
   if (sendto(fd, &packet, length, 0,
-	     (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+	     (struct sockaddr *) &addr_s, addr_s.ss_family==AF_INET?sizeof(struct sockaddr_in):sizeof(struct sockaddr_in6)) < 0) {
     gsn->err_sendto++;
     gtp_err(LOG_ERR, __FILE__, __LINE__, "Sendto(fd=%d, msg=%lx, len=%d) failed: Error = %s", fd, (unsigned long) &packet, GTP0_HEADER_SIZE+len, strerror(errno));
     return EOF;
@@ -3067,6 +3124,7 @@
   return 0;
 }
 
+// TODO: ipv6?
 int eua2ipv4(struct in_addr *dst, struct ul66_t *eua) {
   if ((eua->l != 6) || 
       (eua->v[0] != 0xf1) || 
@@ -3076,16 +3134,21 @@
   return 0;
 }
 
-int gsna2in_addr(struct in_addr *dst, struct ul16_t *gsna) {
-  memset(dst, 0, sizeof(struct in_addr));
-  if (gsna->l != 4) return EOF; /* Return if not IPv4 */
+int gsna2in_addr(struct in6_addr *dst, size_t* dstlen, struct ul16_t *gsna) {
+  memset(dst, 0, *dstlen);
+  if (gsna->l != 4 && gsna->l != 16) return EOF; /* Return if not IPv4 */
+  if (*dstlen < gsna->l) {
+	gtp_err(LOG_ERR, __FILE__, __LINE__, "dstlen too short");
+	return EOF;
+  }
   memcpy(dst, gsna->v, gsna->l);
+  *dstlen = gsna->l;
   return 0;
 }
 
-int in_addr2gsna(struct ul16_t *gsna, struct in_addr *src) {
+int in_addr2gsna(struct ul16_t *gsna, struct in6_addr *src, size_t srclen) {
   memset(gsna, 0, sizeof(struct ul16_t));
-  gsna->l = 4;
+  gsna->l = srclen;
   memcpy(gsna->v, src, gsna->l);
   return 0;
 }
diff --git a/gtp/gtp.h b/gtp/gtp.h
index 7b5083b..5ca22eb 100644
--- a/gtp/gtp.h
+++ b/gtp/gtp.h
@@ -239,8 +239,8 @@
   int         fd1c;     /* GTP1 control plane file descriptor */
   int         fd1u;     /* GTP0 user plane file descriptor */
   int       mode;       /* Mode of operation: GGSN or SGSN */
-  struct in_addr gsnc;  /* IP address of this gsn for signalling */
-  struct in_addr gsnu;  /* IP address of this gsn for user traffic */
+  struct sockaddr_storage gsnc;  /* IP address of this gsn for signalling */
+  struct sockaddr_storage gsnu;  /* IP address of this gsn for user traffic */
 
   /* Parameters related to signalling messages */
   uint16_t seq_next;    /* Next sequence number to use */
@@ -256,8 +256,8 @@
   /* Call back functions */
   int (*cb_delete_context) (struct pdp_t*);
   int (*cb_create_context_ind) (struct pdp_t*);
-  int (*cb_unsup_ind) (struct sockaddr_in *peer);
-  int (*cb_extheader_ind) (struct sockaddr_in *peer);
+  int (*cb_unsup_ind) (struct sockaddr *peer);
+  int (*cb_extheader_ind) (struct sockaddr *peer);
   int (*cb_conf) (int type, int cause, struct pdp_t *pdp, void* cbp);
   int (*cb_data_ind) (struct pdp_t* pdp, void* pack, unsigned len);
 
@@ -290,7 +290,7 @@
 /* External API functions */
 
 extern const char* gtp_version();
-extern int gtp_new(struct gsn_t **gsn, char *statedir, struct in_addr *listen,
+extern int gtp_new(struct gsn_t **gsn, char *statedir, struct sockaddr *listen,
 		   int mode);
 
 extern int gtp_free(struct gsn_t *gsn);
@@ -309,7 +309,7 @@
 				   int cause);
 
 extern int gtp_update_context(struct gsn_t *gsn, struct pdp_t *pdp, 
-			      void *cbp, struct in_addr* inetaddr);
+			      void *cbp, struct sockaddr* inetaddr);
 
 extern int gtp_delete_context_req(struct gsn_t *gsn, struct pdp_t *pdp, 
 				  void *cbp, int teardown);
@@ -334,10 +334,10 @@
   int (*cb_create_context) (struct pdp_t* pdp)); */
 
 extern int gtp_set_cb_unsup_ind(struct gsn_t *gsn,
-				int (*cb) (struct sockaddr_in *peer));
+				int (*cb) (struct sockaddr *peer));
 
 extern int gtp_set_cb_extheader_ind(struct gsn_t *gsn,
-				    int (*cb) (struct sockaddr_in *peer));
+				    int (*cb) (struct sockaddr *peer));
 
 
 extern int gtp_set_cb_conf(struct gsn_t *gsn,
@@ -347,58 +347,58 @@
 /* Internal functions (not part of the API */
 
 extern int gtp_echo_req(struct gsn_t *gsn, int version, void *cbp,
-			struct in_addr *inetaddrs);
+			struct sockaddr *inetaddrs);
 extern int gtp_echo_resp(struct gsn_t *gsn, int version, 
-			 struct sockaddr_in *peer, int fd,
+			 struct sockaddr *peer, int fd,
 			 void *pack, unsigned len);
 extern int gtp_echo_ind(struct gsn_t *gsn, int version, 
-			struct sockaddr_in *peer, int fd, 
+			struct sockaddr *peer, int fd,
 			void *pack, unsigned len);
 extern int gtp_echo_conf(struct gsn_t *gsn, int version, 
-			 struct sockaddr_in *peer,
+			 struct sockaddr *peer,
 			 void *pack, unsigned len);
 
 extern int gtp_unsup_req(struct gsn_t *gsn, int version, 
-			 struct sockaddr_in *peer,
+			 struct sockaddr *peer,
 			 int fd, void *pack, unsigned len);
-extern int gtp_unsup_ind(struct gsn_t *gsn, struct sockaddr_in *peer,
+extern int gtp_unsup_ind(struct gsn_t *gsn, struct sockaddr *peer,
 			 void *pack, unsigned len);
 
 extern int gtp_create_pdp_resp(struct gsn_t *gsn, int version, 
 			       struct pdp_t *pdp, uint8_t cause);
 
 extern int gtp_create_pdp_ind(struct gsn_t *gsn, int version,
-			      struct sockaddr_in *peer, int fd,
+			      struct sockaddr *peer, int fd,
 			      void *pack, unsigned len);
 
 extern int gtp_create_pdp_conf(struct gsn_t *gsn, int version,
-			       struct sockaddr_in *peer,
+			       struct sockaddr *peer,
 			       void *pack, unsigned len);
 
 extern int gtp_update_pdp_req(struct gsn_t *gsn, int version, void *cbp,
-			      struct in_addr* inetaddr, struct pdp_t *pdp);
+			      struct sockaddr* inetaddr, struct pdp_t *pdp);
 
 extern int gtp_delete_pdp_req(struct gsn_t *gsn, int version, void *cbp,
 			      struct pdp_t *pdp);
 
 extern int gtp_delete_pdp_resp(struct gsn_t *gsn, int version,
-			       struct sockaddr_in *peer, int fd,
+			       struct sockaddr *peer, int fd,
 			       void *pack, unsigned len, 
 			       struct pdp_t *pdp, struct pdp_t *linked_pdp,
 			       uint8_t cause, int teardown);
 
 extern int gtp_delete_pdp_ind(struct gsn_t *gsn, int version,
-			      struct sockaddr_in *peer, int fd,
+			      struct sockaddr *peer, int fd,
 			      void *pack, unsigned len);
 
 extern int gtp_delete_pdp_conf(struct gsn_t *gsn, int version,
-			       struct sockaddr_in *peer,
+			       struct sockaddr *peer,
 			       void *pack, unsigned len);
 
 
 extern int ipv42eua(struct ul66_t *eua, struct in_addr *src);
 extern int eua2ipv4(struct in_addr *dst, struct ul66_t *eua);
-extern int gsna2in_addr(struct in_addr *dst, struct ul16_t *gsna);
-extern int in_addr2gsna(struct ul16_t *gsna, struct in_addr *src);
+extern int gsna2in_addr(struct in6_addr *dst, size_t* dstlen, struct ul16_t *gsna);
+extern int in_addr2gsna(struct ul16_t *gsna, struct in6_addr *src, size_t srclen);
 
 #endif	/* !_GTP_H */
diff --git a/gtp/pdp.c b/gtp/pdp.c
index 648d70d..642547c 100644
--- a/gtp/pdp.c
+++ b/gtp/pdp.c
@@ -328,6 +328,14 @@
   return 0;
 }
 
+int pdp_ntoeua6(struct in6_addr *src, struct ul66_t *eua) {
+  eua->l=18;
+  eua->v[0]=0xf1; /* IETF */
+  eua->v[1]=0x57; /* IPv6 */
+  memcpy(&eua->v[2], src, 16); /* Copy a 16 byte address */
+  return 0;
+}
+
 int pdp_euaton(struct ul66_t *eua, struct in_addr *dst) {
   if((eua->l!=6) || (eua->v[0]!=0xf1) || (eua->v[1]!=0x21)) {
     return EOF;
@@ -335,6 +343,15 @@
   memcpy(dst, &eua->v[2], 4); /* Copy a 4 byte address */
   return 0;
 }
+
+int pdp_euaton6(struct ul66_t *eua, struct in6_addr *dst) {
+  if((eua->l!=18) || (eua->v[0]!=0xf1) || (eua->v[1]!=0x57)) {
+    return EOF;
+  }
+  memcpy(dst, &eua->v[2], 16); /* Copy a 16 byte address */
+  return 0;
+}
+
 
 uint64_t pdp_gettid(uint64_t imsi, uint8_t nsapi) {
   return (imsi & 0x0fffffffffffffffull) + ((uint64_t)nsapi << 60);
@@ -349,3 +366,18 @@
   }
   else return EOF;
 }
+
+int pdp_testip(struct ul66_t* eua) {
+  unsigned char type = eua->v[1];
+
+  switch (type) {
+  case 0x21: /* IPv4 */
+    return 4;
+  case 0x57: /* IPv6 */
+    return 6;
+  case 0x8d: /* IPv4v6 */
+    return 8; // for lack of anything better...
+  default:
+    return -1;
+  }
+}
diff --git a/gtp/pdp.h b/gtp/pdp.h
index 624c4ac..6723b05 100644
--- a/gtp/pdp.h
+++ b/gtp/pdp.h
@@ -158,9 +158,13 @@
   /*struct ul16_t ggsnu;  * The IP address of the GGSN currently used. (User plane) */
 
   struct ul16_t gsnlc;  /* The IP address of the local GSN. (Control plane) */
+  size_t gsnlc_len;
   struct ul16_t gsnlu;  /* The IP address of the local GSN. (User plane) */
+  size_t gsnlu_len;
   struct ul16_t gsnrc;  /* The IP address of the remote GSN. (Control plane) */
+  size_t gsnrc_len;
   struct ul16_t gsnru;  /* The IP address of the remote GSN. (User plane) */
+  size_t gsnru_len;
 
   uint8_t  vplmn_allow; /* Specifies whether the MS is allowed to use the APN in the domain of the HPLMN only, or additionally the APN in the domain of the VPLMN. (1 bit) */
   uint8_t qos_sub0[3];  /* The quality of service profile subscribed. */
@@ -195,7 +199,7 @@
 
   uint64_t    tid;      /* Combination of imsi and nsapi */
   uint16_t   seq;      /* Sequence number of last request */
-  struct sockaddr_in sa_peer; /* Address of last request */
+  struct sockaddr_storage sa_peer; /* Address of last request */
   int fd;               /* File descriptor request was received on */
 
   uint8_t teic_confirmed; /* 0: Not confirmed. 1: Confirmed */
@@ -209,8 +213,8 @@
   uint32_t secondary_tei[PDP_MAXNSAPI]; 
 
   /* IP address used for Create and Update PDP Context Requests */
-  struct in_addr hisaddr0;       /* Server address */
-  struct in_addr hisaddr1;       /* Server address */
+  struct sockaddr_storage hisaddr0;       /* Server address */
+  struct sockaddr_storage hisaddr1;       /* Server address */
 
   /* to be used by libgtp callers/users (to attach their own private state) */
   void *priv;
@@ -243,8 +247,12 @@
 */
 
 int pdp_ntoeua(struct in_addr *src, struct ul66_t *eua);
+int pdp_ntoeua6(struct in6_addr *src, struct ul66_t *eua);
 int pdp_euaton(struct ul66_t *eua, struct in_addr *dst);
+int pdp_euaton6(struct ul66_t *eua, struct in6_addr *dst);
 uint64_t pdp_gettid(uint64_t imsi, uint8_t nsapi);
 int ulcpy(void* dst, void* src, size_t size);
 
+int pdp_testip(struct ul66_t* eua);
+
 #endif	/* !_PDP_H */
diff --git a/gtp/queue.c b/gtp/queue.c
index 7fdf9df..23804fb 100644
--- a/gtp/queue.c
+++ b/gtp/queue.c
@@ -57,7 +57,7 @@
 }
 
 int queue_seqset(struct queue_t *queue, struct qmsg_t *qmsg,
-		 struct sockaddr_in *peer, uint16_t seq) {
+		 struct sockaddr *peer, uint16_t seq) {
   int hash = queue_seqhash(peer, seq);
   struct qmsg_t *qmsg2;
   struct qmsg_t *qmsg_prev = NULL;
@@ -66,7 +66,7 @@
   if (QUEUE_DEBUG) printf("SIZEOF PEER %d, *PEER %d\n", sizeof(peer), sizeof(*peer));
 
   qmsg->seq = seq;
-  memcpy(&qmsg->peer, peer, sizeof(*peer));
+  memcpy(&qmsg->peer, peer, sizeof(struct sockaddr_storage));
 
   for (qmsg2 = queue->hashseq[hash]; qmsg2; qmsg2 = qmsg2->seqnext)
     qmsg_prev = qmsg2;
@@ -123,7 +123,7 @@
 }
 
 int queue_newmsg(struct queue_t *queue, struct qmsg_t **qmsg,
-		 struct sockaddr_in *peer, uint16_t seq) {
+		 struct sockaddr *peer, uint16_t seq) {
   if (QUEUE_DEBUG) printf("queue_newmsg %d\n", (int) seq);
   if (queue->qmsga[queue->next].state == 1) {
     return EOF; /* Queue is full */
@@ -204,13 +204,13 @@
 }
 
 int queue_getseqx(struct queue_t *queue, struct qmsg_t **qmsg,
-		 struct sockaddr_in *peer, uint16_t seq) {
+		 struct sockaddr *peer, uint16_t seq) {
   int n;
   if (QUEUE_DEBUG) printf("queue_getseq, %d\n", (int) seq);
   if (QUEUE_DEBUG) queue_print(queue);
   for (n=0; n<QUEUE_SIZE; n++) {
     if ((queue->qmsga[n].seq == seq) &&
-	(!memcmp(&queue->qmsga[n].peer, peer, sizeof(*peer)))) {
+	(!memcmp(&queue->qmsga[n].peer, peer, peer->sa_family==AF_INET?sizeof(struct sockaddr_in):sizeof(struct sockaddr_in6)))) {
       *qmsg = &queue->qmsga[n];
       return 0;
     }
@@ -219,13 +219,13 @@
 }
 
 int queue_seqget(struct queue_t *queue, struct qmsg_t **qmsg,
-		 struct sockaddr_in *peer, uint16_t seq) {
+		 struct sockaddr *peer, uint16_t seq) {
   int hash = queue_seqhash(peer, seq);
   struct qmsg_t *qmsg2;
   if (QUEUE_DEBUG) printf("Begin queue_seqget seq = %d\n", (int) seq);
   for (qmsg2 = queue->hashseq[hash]; qmsg2; qmsg2 = qmsg2->seqnext) {
     if ((qmsg2->seq == seq) && 
-	(!memcmp(&qmsg2->peer, peer, sizeof(*peer)))) {
+	(!memcmp(&qmsg2->peer, peer, peer->sa_family==AF_INET?sizeof(struct sockaddr_in):sizeof(struct sockaddr_in6)))) {
       *qmsg = qmsg2;
       if (QUEUE_DEBUG) printf("End queue_seqget. Found\n");
       return 0;
@@ -235,7 +235,7 @@
   return EOF; /* End of linked list and not found */
 }
 
-int queue_freemsg_seq(struct queue_t *queue, struct sockaddr_in *peer, 
+int queue_freemsg_seq(struct queue_t *queue, struct sockaddr *peer,
 		      uint16_t seq, uint8_t *type, void **cbp) {
   struct qmsg_t *qmsg;
   if (queue_seqget(queue, &qmsg, peer, seq)) {
diff --git a/gtp/queue.h b/gtp/queue.h
index 46fa22d..11978c1 100644
--- a/gtp/queue.h
+++ b/gtp/queue.h
@@ -30,7 +30,7 @@
   union gtp_packet p;     /* The packet stored */
   int l;                  /* Length of the packet */
   int fd;                 /* Socket packet was sent to / received from */
-  struct sockaddr_in peer;/* Address packet was sent to / received from */
+  struct sockaddr_storage peer;/* Address packet was sent to / received from */
   struct qmsg_t *seqnext; /* Pointer to next in sequence hash list */
   int next;               /* Pointer to the next in queue. -1: Last */
   int prev;               /* Pointer to the previous in queue. -1: First */
@@ -54,7 +54,7 @@
 int queue_free(struct queue_t *queue);
 /* Find a new queue element. Return EOF if allready full */
 int queue_newmsg(struct queue_t *queue, struct qmsg_t **qmsg,
-		 struct sockaddr_in *peer, uint16_t seq);
+		 struct sockaddr *peer, uint16_t seq);
 /* Remove an element from the queue. */
 int queue_freemsg(struct queue_t *queue, struct qmsg_t *qmsg);
 /* Move an element to the back of the queue */
@@ -63,9 +63,9 @@
 int queue_getfirst(struct queue_t *queue, struct qmsg_t **qmsg);
 /* Get the element with a particular sequence number */
 int queue_seqget(struct queue_t *queue, struct qmsg_t **qmsg,
-		 struct sockaddr_in  *peer, uint16_t seq);
+		 struct sockaddr  *peer, uint16_t seq);
 /* Free message based on sequence number */
-int queue_freemsg_seq(struct queue_t *queue, struct sockaddr_in *peer,
+int queue_freemsg_seq(struct queue_t *queue, struct sockaddr *peer,
 		      uint16_t seq, uint8_t *type, void **cbp);
 
 
diff --git a/sgsnemu/ippool.c b/sgsnemu/ippool.c
index fa3d8af..9408d47 100644
--- a/sgsnemu/ippool.c
+++ b/sgsnemu/ippool.c
@@ -16,6 +16,7 @@
 #include <syslog.h>
 #include <string.h>
 #include <sys/socket.h>
+#include <netdb.h>
 #include <arpa/inet.h>
 #include "syserr.h"
 #include "ippool.h"
@@ -32,13 +33,32 @@
   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 [1024];
+    char s2 [1024];
+    struct sockaddr_storage ss;
+
+    size_t addrlen = this->member[n].addrlen;
+    size_t sockaddrlen;
+
+    memcpy(((struct sockaddr*)&ss)->sa_data, &this->member[n].addr, addrlen);
+
+    if (addrlen == 4) {
+      ss.ss_family = AF_INET;
+      sockaddrlen = sizeof(struct sockaddr_in);
+    }
+    else {
+      ss.ss_family = AF_INET6;
+      sockaddrlen = sizeof(struct sockaddr_in6);
+    }
+
+    getnameinfo((struct sockaddr*)&ss, sockaddrlen, s, sizeof(s), s2, sizeof(s2), NI_NUMERICHOST);
+
+    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;
@@ -50,12 +70,15 @@
   struct ippoolm_t *p_prev = NULL; 
 
   /* Insert into hash table */
-  hash = ippool_hash4(&member->addr) & this->hashmask;
+  if (member->addrlen == 4)
+	hash = ippool_hash4((struct in_addr*)&member->addr) & this->hashmask;
+  else
+	hash = ippool_hash6(&member->addr) & this->hashmask;
   for (p = this->hash[hash]; p; p = p->nexthash)
     p_prev = p;
   if (!p_prev)
     this->hash[hash] = member;
-  else 
+  else
     p_prev->nexthash = member;
   return 0; /* Always OK to insert */
 }
@@ -66,7 +89,10 @@
   struct ippoolm_t *p_prev = NULL; 
 
   /* Find in hash table */
-  hash = ippool_hash4(&member->addr) & this->hashmask;
+  if (member->addrlen == 4)
+	hash = ippool_hash4((struct in_addr*)&member->addr) & this->hashmask;
+  else
+	hash = ippool_hash6(&member->addr) & this->hashmask;
   for (p = this->hash[hash]; p; p = p->nexthash) {
     if (p == member) {
       break;
@@ -93,66 +119,80 @@
   return lookup((unsigned char*) &addr->s_addr, sizeof(addr->s_addr), 0);
 }
 
-#ifndef IPPOOL_NOIP6
 unsigned long int ippool_hash6(struct in6_addr *addr) {
-  return lookup((unsigned char*) addr->u6_addr8, sizeof(addr->u6_addr8), 0);
+ // TODO: review hash spread for ipv6
+  return lookup((unsigned char*) addr->s6_addr32, sizeof(addr->s6_addr32), 0);
 }
-#endif
-
 
 /* Get IP address and mask */
-int ippool_aton(struct in_addr *addr, struct in_addr *mask,
-		char *pool, int number) {
+int ippool_aton(struct in_addr *addr, size_t *addrlen, size_t* prefixlen, char *pool, int number) {
+	struct addrinfo* ai;
 
-  /* Parse only first instance of network for now */
-  /* Eventually "number" will indicate the token which we want to parse */
+	struct addrinfo hints;
+	hints.ai_family = AF_UNSPEC;
+	hints.ai_socktype = SOCK_DGRAM;
+	hints.ai_flags = 0;
+	hints.ai_protocol = 0;
 
-  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(LOG_ERR, __FILE__, __LINE__, 0, "Invalid mask");
-      return -1; /* Invalid mask */
-    }
-    mask->s_addr = htonl(0xffffffff << (32 - m1));
-    break;
-  case 8:
-    if (m1 >= 256 ||  m2 >= 256 || m3 >= 256 || m4 >= 256) {
-      sys_err(LOG_ERR, __FILE__, __LINE__, 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(LOG_ERR, __FILE__, __LINE__, 0, "Invalid mask");
-      return -1; /* Wrong mask format (not all ones followed by all zeros)*/
-    }
-    mask->s_addr = htonl(m);
-    break;
-  default:
-    sys_err(LOG_ERR, __FILE__, __LINE__, 0, "Invalid mask");
-    return -1; /* Invalid mask */
-  }
+	// Find '/'
+	char* prefixlen_str = strchr(pool, '/');
+	if (prefixlen_str) {
+		*prefixlen_str = '\0';
+		prefixlen_str++;
+		if (*prefixlen_str == '\0') {
+			sys_err(LOG_ERR, __FILE__, __LINE__, 0, "Empty prefix length specified");
+			return -1;
+		}
+	}
 
-  if (a1 >= 256 ||  a2 >= 256 || a3 >= 256 || a4 >= 256) {
-    sys_err(LOG_ERR, __FILE__, __LINE__, 0, "Wrong IP address format");
-    return -1;
-  }
-  else
-    addr->s_addr = htonl(a1 * 0x1000000 + a2 * 0x10000 + a3 * 0x100 + a4);
+	// Convert address
+	if ((err = getaddrinfo(pool, NULL, &hints, &ai))) {
+		sys_err(LOG_ERR, __FILE__, __LINE__, 0, "Bad address");
+		return -1;
+	}
 
-  return 0;
+	// Copy address, set lengths
+	if (ai->ai_family == AF_INET) {
+		*prefixlen = 32;
+		*addrlen = sizeof(struct in_addr);
+		memcpy(addr, &((struct sockaddr_in*)ai->ai_addr)->sin_addr, sizeof(struct in_addr));
+	}
+	else {
+		*prefixlen = 128;
+		*addrlen = sizeof(struct in6_addr);
+		memcpy(addr, &((struct sockaddr_in6*)ai->ai_addr)->sin6_addr, sizeof(struct in6_addr));
+	}
+
+	freeaddrinfo(ai);
+
+	// Parse prefixlen
+	if (prefixlen_str) {
+		char* e;
+		*prefixlen = strtol(prefixlen_str, &e, 10);
+		if (*e != '\0') {
+			sys_err(LOG_ERR, __FILE__, __LINE__, 0, "Prefixlen is not an int");
+			return -1;
+		}
+	}
+
+	if (*prefixlen > (*addrlen * 8)) {
+		sys_err(LOG_ERR, __FILE__, __LINE__, 0, "Prefixlen too big");
+		return -1;
+	}
+
+	return 0;
+}
+
+/* Increases ip4/6 address by 1 */
+void inc_addr(struct in_addr* addr, size_t addrlen)
+{
+	uint8_t* a = (uint8_t*)&addr->s_addr;
+	for (; addrlen > 0; addrlen--) {
+		if (++a[addrlen-1])
+			break;
+	}
 }
 
 /* Create new address pool */
@@ -162,10 +202,12 @@
   /* 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;
+  struct in6_addr addr;
+  size_t addrlen;
+  size_t addrprefixlen;
+  struct in6_addr stataddr;
+  size_t stataddrlen;
+  size_t stataddrprefixlen;
   unsigned int m;
   int listsize;
   int dynsize;
@@ -175,7 +217,7 @@
     dynsize = 0;
   }
   else {
-    if (ippool_aton(&addr, &mask, dyn, 0)) {
+    if (ippool_aton((struct in_addr*)&addr, &addrlen, &addrprefixlen, dyn, 0)) {
       sys_err(LOG_ERR, __FILE__, __LINE__, 0, 
 	      "Failed to parse dynamic pool");
       return -1;
@@ -185,9 +227,11 @@
     if (flags & IPPOOL_NOGATEWAY) {   
       flags |= IPPOOL_NONETWORK;
     }
-    
-    m = ntohl(mask.s_addr);
-    dynsize = ((~m)+1);
+
+    dynsize = (1 << (addrlen - addrprefixlen + 1)) - 1;
+
+    //m = ntohl(mask.s_addr);
+    //dynsize = ((~m)+1);
     if (flags & IPPOOL_NONETWORK)   /* Exclude network address from pool */
       dynsize--;
     if (flags & IPPOOL_NOGATEWAY)   /* Exclude gateway address from pool */
@@ -198,18 +242,17 @@
 
   if (!allowstat) {
     statsize = 0;
-    stataddr.s_addr = 0;
-    statmask.s_addr = 0;
+    stataddrlen = 0;
+    stataddrprefixlen = 0;
   }
   else {
-    if (ippool_aton(&stataddr, &statmask, stat, 0)) {
+    if (ippool_aton((struct in_addr*)&stataddr, &stataddrlen, &stataddrprefixlen, stat, 0)) {
       sys_err(LOG_ERR, __FILE__, __LINE__, 0,
 	      "Failed to parse static range");
       return -1;
     }
 
-    m = ntohl(statmask.s_addr);
-    statsize = ((~m)+1);
+    statsize = (1 << (addrlen - addrprefixlen + 1)) - 1;
     if (statsize > IPPOOL_STATSIZE) statsize = IPPOOL_STATSIZE;
   }
 
@@ -223,8 +266,10 @@
   
   (*this)->allowdyn  = allowdyn;
   (*this)->allowstat = allowstat;
-  (*this)->stataddr  = stataddr;
-  (*this)->statmask  = statmask;
+  if (stataddrlen > 0)
+	memcpy(&(*this)->stataddr, &stataddr, stataddrlen);
+  (*this)->stataddrlen  = stataddrlen;
+  (*this)->stataddrprefixlen  = stataddrprefixlen;
 
   (*this)->listsize += listsize;
   if (!((*this)->member = calloc(sizeof(struct ippoolm_t), listsize))){
@@ -252,15 +297,20 @@
   
   (*this)->firstdyn = NULL;
   (*this)->lastdyn = NULL;
+
+  if (flags & IPPOOL_NOGATEWAY) {
+	inc_addr((struct in_addr*)&addr, addrlen);
+	inc_addr((struct in_addr*)&addr, addrlen);
+  }
+  else if (flags & IPPOOL_NONETWORK)
+	inc_addr((struct in_addr*)&addr, addrlen);
+
   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);
-    
+    memcpy(&(*this)->member[i].addr, &addr, addrlen);
+    (*this)->member[i].addrlen = addrlen;
+    inc_addr((struct in_addr*)&addr, addrlen);
+
     (*this)->member[i].inuse = 0;
 
     /* Insert into list of unused */
@@ -280,8 +330,10 @@
   (*this)->firststat = NULL;
   (*this)->laststat = NULL;
   for (i = dynsize; i<listsize; i++) {
+    // TODO: set addrlen to 0?
+    memset(&(*this)->member[i].addr, 0, addrlen);
+    (*this)->member[i].addrlen = addrlen;
 
-    (*this)->member[i].addr.s_addr = 0;
     (*this)->member[i].inuse = 0;
 
     /* Insert into list of unused */
@@ -312,14 +364,19 @@
 
 /* Find an IP address in the pool */
 int ippool_getip(struct ippool_t *this, struct ippoolm_t **member,
-		 struct in_addr *addr) {
+		 struct in_addr *addr, size_t addrlen) {
   struct ippoolm_t *p;
   uint32_t hash;
 
   /* Find in hash table */
-  hash = ippool_hash4(addr) & this->hashmask;
+  if (addrlen == 4)
+    hash = ippool_hash4(addr) & this->hashmask;
+  else
+    hash = ippool_hash6(addr) & this->hashmask;
   for (p = this->hash[hash]; p; p = p->nexthash) {
-    if ((p->addr.s_addr == addr->s_addr) && (p->inuse)) {
+    if (p->addrlen == addrlen
+        && memcmp(&p->addr, addr, addrlen) == 0
+        && (p->inuse)) {
       if (member) *member = p;
       return 0;
     }
@@ -337,7 +394,7 @@
  * address space.
 **/
 int ippool_newip(struct ippool_t *this, struct ippoolm_t **member,
-		 struct in_addr *addr, int statip) {
+		 struct in_addr *addr, size_t addrlen, int statip) {
   struct ippoolm_t *p;
   struct ippoolm_t *p2 = NULL;
   uint32_t hash;
@@ -357,15 +414,25 @@
   if (0) (void)ippool_printaddr(this);
 
   /* First check to see if this type of address is allowed */
-  if ((addr) && (addr->s_addr) && statip) { /* IP address given */
+  int specified = 0;
+  if (addr) {
+    if (addrlen == 4 && ((struct in_addr*)&addr)->s_addr)
+      specified = 1;
+    if (addrlen == 16 && !IN6_IS_ADDR_UNSPECIFIED(addr))
+      specified = 1;
+  }
+  if (specified && statip) { /* IP address given */
     if (!this->allowstat) {
       sys_err(LOG_ERR, __FILE__, __LINE__, 0, "Static IP address not allowed");
       return -1;
     }
+    // TODO: ipv6 mask check
+/*
     if ((addr->s_addr & this->statmask.s_addr) != this->stataddr.s_addr) {
       sys_err(LOG_ERR, __FILE__, __LINE__, 0, "Static out of range");
       return -1;
     }
+*/
   }
   else {
     if (!this->allowdyn) {
@@ -376,11 +443,14 @@
   }
 
   /* 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;
+    if (addrlen == 4)
+      hash = ippool_hash4(addr) & this->hashmask;
+    else
+      hash = ippool_hash6(addr) & this->hashmask;
     for (p = this->hash[hash]; p; p = p->nexthash) {
-      if ((p->addr.s_addr == addr->s_addr)) {
+      if (p->addrlen == addrlen && !memcmp(&p->addr, &addr, addrlen)) {
 	p2 = p;
 	break;
       }
@@ -431,7 +501,7 @@
   /* 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(LOG_ERR, __FILE__, __LINE__, 0, 
 	      "No more IP addresses available");
@@ -452,7 +522,7 @@
     p2->next = NULL;
     p2->prev = NULL;
     p2->inuse = 2; /* Static address in use */
-    memcpy(&p2->addr, addr, sizeof(addr));
+    memcpy(&p2->addr, addr, addrlen);
     *member = p2;
     (void)ippool_hashadd(this, *member);
     if (0) (void)ippool_printaddr(this);
@@ -507,7 +577,8 @@
     this->laststat = member;
     
     member->inuse = 0;
-    member->addr.s_addr = 0;
+    memset(&member->addr, 0, sizeof(member->addr));
+    member->addrlen = 0;
     member->peer = NULL;
     member->nexthash = NULL;
     if (0) (void)ippool_printaddr(this);
@@ -519,8 +590,3 @@
 }
 
 
-#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/sgsnemu/ippool.h b/sgsnemu/ippool.h
index 02691a6..432fa8d 100644
--- a/sgsnemu/ippool.h
+++ b/sgsnemu/ippool.h
@@ -26,8 +26,6 @@
    in RFC2373.
 */
 
-#define IPPOOL_NOIP6
-
 #define IPPOOL_NONETWORK   0x01
 #define IPPOOL_NOBROADCAST 0x02
 #define IPPOOL_NOGATEWAY   0x04
@@ -40,7 +38,9 @@
   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 in6_addr stataddr;       /* Static address range network address */
+  size_t stataddrlen;
+  size_t stataddrprefixlen;
   struct in_addr statmask;       /* Static address range network mask */
   struct ippoolm_t *member;      /* Listsize array of members */
   unsigned int hashsize;                  /* Size of hash table */
@@ -54,11 +54,8 @@
 };
 
 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
+  size_t addrlen;
   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 */
@@ -81,25 +78,22 @@
 
 /* Find an IP address in the pool */
 extern int ippool_getip(struct ippool_t *this, struct ippoolm_t **member,
-		 struct in_addr *addr);
+		 struct in_addr *addr, size_t addrlen);
 
 /* 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 in_addr *addr, size_t addrlen, 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,
+extern int ippool_aton(struct in_addr *addr, size_t *addrlen, size_t *prefixlen,
 		       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
+
+void inc_addr(struct in_addr* addr, size_t addrlen);
 
 #endif	/* !_IPPOOL_H */
diff --git a/sgsnemu/sgsnemu.c b/sgsnemu/sgsnemu.c
index 9b5c2e2..42350e5 100644
--- a/sgsnemu/sgsnemu.c
+++ b/sgsnemu/sgsnemu.c
@@ -59,7 +59,8 @@
   uint8_t     inuse;    /* 0=free. 1=used by somebody */
   struct iphash_t *ipnext;
   struct pdp_t *pdp;
-  struct in_addr addr;
+  struct in6_addr addr;
+  size_t addrlen;
 };
 struct iphash_t iparr[MAXCONTEXTS];
 struct iphash_t *iphash[MAXCONTEXTS];
@@ -82,17 +83,20 @@
 struct {
   int debug;                      /* Print debug messages */
   int createif;                   /* Create local network interface */
-  struct in_addr netaddr, destaddr, net, mask;   /* Network interface  */
+  struct sockaddr_storage netaddr;
+  struct sockaddr_storage destaddr;
+  struct sockaddr_storage net;
+  size_t net_prefixlen;
   char *ipup, *ipdown;            /* Filename of scripts */
   int defaultroute;               /* Set up default route */
-  struct in_addr pinghost;        /* Remote ping host    */
+  struct sockaddr_storage pinghost;        /* Remote ping host    */
   int pingrate;
   int pingsize;
   int pingcount;
   int pingquiet;
-  struct in_addr listen;
-  struct in_addr remote;
-  struct in_addr dns;
+  struct sockaddr_storage listen;
+  struct sockaddr_storage remote;
+  struct sockaddr_storage dns;
   int contexts;                   /* Number of contexts to create */
   int timelimit;                  /* Number of seconds to be connected */
   char *statedir;
@@ -145,12 +149,17 @@
 int pingseq = 0;              /* Ping sequence counter */
 struct timeval firstping;
 
-int ipset(struct iphash_t *ipaddr, struct in_addr *addr) {
-  int hash = ippool_hash4(addr) % MAXCONTEXTS;
+int ipset(struct iphash_t *ipaddr, struct in_addr *addr, size_t addrlen) {
+  int hash;
+  if (addrlen == 4)
+    hash = ippool_hash4(addr) % MAXCONTEXTS;
+  else
+    hash = ippool_hash6((struct in6_addr*)addr) % MAXCONTEXTS;
   struct iphash_t *h;
   struct iphash_t *prev = NULL;
   ipaddr->ipnext = NULL;
-  ipaddr->addr.s_addr = addr->s_addr;
+  memcpy(&ipaddr->addr, addr, addrlen);
+  ipaddr->addrlen = addrlen;
   for (h = iphash[hash]; h; h = h->ipnext)
     prev = h;
   if (!prev) 
@@ -161,7 +170,11 @@
 }
 
 int ipdel(struct iphash_t *ipaddr) {
-  int hash = ippool_hash4(&ipaddr->addr) % MAXCONTEXTS;
+  int hash;
+  if (ipaddr->addrlen == 4)
+    hash = ippool_hash4((struct in_addr*)&ipaddr->addr) % MAXCONTEXTS;
+  else
+    hash = ippool_hash6(&ipaddr->addr) % MAXCONTEXTS;
   struct iphash_t *h;
   struct iphash_t *prev = NULL;
   for (h = iphash[hash]; h; h = h->ipnext) {
@@ -177,18 +190,21 @@
   return EOF; /* End of linked list and not found */
 }
 
-int ipget(struct iphash_t **ipaddr, struct in_addr *addr) {
-  int hash = ippool_hash4(addr) % MAXCONTEXTS;
+int ipget(struct iphash_t **ipaddr, struct in_addr *addr, size_t addrlen) {
+  int hash;
+  if (addrlen == 4)
+    hash = ippool_hash4(addr) % MAXCONTEXTS;
+  else
+    hash = ippool_hash6((struct in6_addr*)addr) % MAXCONTEXTS;
   struct iphash_t *h;
   for (h = iphash[hash]; h; h = h->ipnext) {
-    if ((h->addr.s_addr == addr->s_addr)) {
+    if (h->addrlen == addrlen && memcmp(&h->addr, addr, addrlen) == 0) {
       *ipaddr = h;
       return 0;
     }
   }
   return EOF; /* End of linked list and not found */
 }
-
 
 /* Used to write process ID to file. Assume someone else will delete */
 void log_pid(char *pidfile) {
@@ -209,8 +225,16 @@
   /* gengeopt declarations */
   struct gengetopt_args_info args_info;
 
-  struct hostent *host;
+  struct addrinfo* ai;
   unsigned int n;
+
+  struct addrinfo hints;
+  hints.ai_family = AF_UNSPEC;
+  hints.ai_socktype = SOCK_DGRAM;
+  hints.ai_flags = 0;
+  hints.ai_protocol = 0;
+
+  int err;
 
   if (cmdline_parser (argc, argv, &args_info) != 0)
     return -1;
@@ -306,21 +330,26 @@
   /* Do hostname lookup to translate hostname to IP address       */
   printf("\n");
   if (args_info.dns_arg) {
-    if (!(host = gethostbyname(args_info.dns_arg))) {
+    if ((err = getaddrinfo(args_info.dns_arg, NULL, &hints, &ai))) {
       sys_err(LOG_ERR, __FILE__, __LINE__, 0,
-	      "Invalid DNS address: %s!", args_info.dns_arg);
+	      "Invalid DNS address: %s! %s %i", args_info.dns_arg, gai_strerror(err), err);
       return -1;
     }
     else {
-      memcpy(&options.dns.s_addr, host->h_addr, host->h_length);
-      _res.nscount = 1;
-      _res.nsaddr_list[0].sin_addr = options.dns;
-      printf("Using DNS server:      %s (%s)\n", 
-	     args_info.dns_arg, inet_ntoa(options.dns));
+      memcpy(&options.dns, ai->ai_addr, ai->ai_addrlen);
+      freeaddrinfo(ai);
+
+      // TODO: ipv6
+      if (options.dns.ss_family == AF_INET) {
+        _res.nscount = 1;
+        _res.nsaddr_list[0].sin_addr = ((struct sockaddr_in*)&options.dns)->sin_addr;
+        //printf("Using DNS server:      %s (%s)\n",
+        //args_info.dns_arg, inet_ntoa(options.dns));
+      }
     }
   }
   else {
-    options.dns.s_addr= 0;
+    options.dns.ss_family = 0; // Indicates not specified
     printf("Using default DNS server\n");
   }
 
@@ -328,15 +357,17 @@
   /* If no listen option is specified listen to any local port    */
   /* Do hostname lookup to translate hostname to IP address       */
   if (args_info.listen_arg) {
-    if (!(host = gethostbyname(args_info.listen_arg))) {
+    if ((err = getaddrinfo(args_info.listen_arg, NULL, &hints, &ai))) {
       sys_err(LOG_ERR, __FILE__, __LINE__, 0,
-	      "Invalid listening address: %s!", args_info.listen_arg);
+	      "Invalid listening address: %s! %s %i", args_info.listen_arg, gai_strerror(err), err);
       return -1;
     }
     else {
-      memcpy(&options.listen.s_addr, host->h_addr, host->h_length);
-      printf("Local IP address is:   %s (%s)\n", 
-	     args_info.listen_arg, inet_ntoa(options.listen));
+     memcpy(&options.listen, ai->ai_addr, ai->ai_addrlen);
+     freeaddrinfo(ai);
+     // TODO: ipv6
+     //printf("Local IP address is:   %s (%s)\n",
+     //args_info.listen_arg, inet_ntoa(options.listen));
     }
   }
   else {
@@ -350,15 +381,17 @@
   /* If no remote option is specified terminate                   */
   /* Do hostname lookup to translate hostname to IP address       */
   if (args_info.remote_arg) {
-    if (!(host = gethostbyname(args_info.remote_arg))) {
+    if ((err = getaddrinfo(args_info.remote_arg, NULL, &hints, &ai))) {
       sys_err(LOG_ERR, __FILE__, __LINE__, 0,
-	      "Invalid remote address: %s!", args_info.remote_arg);
+	      "Invalid remote address: %s! %s %i", args_info.remote_arg, gai_strerror(err), err);
       return -1;
     }
     else {
-      memcpy(&options.remote.s_addr, host->h_addr, host->h_length);
-      printf("Remote IP address is:  %s (%s)\n", 
-	     args_info.remote_arg, inet_ntoa(options.remote));
+       memcpy(&options.remote, ai->ai_addr, ai->ai_addrlen);
+       freeaddrinfo(ai);
+       // TODO: ipv6
+       //printf("Remote IP address is:  %s (%s)\n",
+       //args_info.remote_arg, inet_ntoa(options.remote));
     }
   }
   else {
@@ -498,26 +531,31 @@
   /* net                                                          */
   /* Store net as in_addr net and mask                            */
   if (args_info.net_arg) {
-    if(ippool_aton(&options.net, &options.mask, args_info.net_arg, 0)) {
+    size_t addrlen;
+    if(ippool_aton(&(((struct sockaddr_in*)&options.net)->sin_addr), &addrlen, &options.net_prefixlen, args_info.net_arg, 0)) {
       sys_err(LOG_ERR, __FILE__, __LINE__, 0,
 	      "Invalid network address: %s!", args_info.net_arg);
       exit(1);
     }
+    if (addrlen == 4)
+      options.net.ss_family = AF_INET;
+    else
+      options.net.ss_family = AF_INET6;
 
 #if defined (__sun__)
     options.netaddr.s_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;
-    options.destaddr.s_addr = options.net.s_addr;
+    memcpy(&options.netaddr, &options.net, sizeof(options.net));
+    memcpy(&options.destaddr, &options.net, sizeof(options.net));
 #endif
 
   }
   else {
-    options.net.s_addr = 0;
-    options.mask.s_addr = 0;
-    options.netaddr.s_addr = 0;
-    options.destaddr.s_addr = 0;
+    options.net.ss_family = 0;
+    options.net_prefixlen = 0;
+    options.netaddr.ss_family = 0;
+    options.destaddr.ss_family = 0;
   }
 
   /* ipup */
@@ -536,16 +574,21 @@
   /* pinghost                                                     */
   /* Store ping host as in_addr                                   */
   if (args_info.pinghost_arg) {
-    if (!(host = gethostbyname(args_info.pinghost_arg))) {
+    if ((err = getaddrinfo(args_info.pinghost_arg, NULL, &hints, &ai))) {
+    //if (!(host = gethostbyname(args_info.pinghost_arg))) {
       sys_err(LOG_ERR, __FILE__, __LINE__, 0,
-	      "Invalid ping host: %s!", args_info.pinghost_arg);
+	      "Invalid ping host: %s! %s %i", args_info.pinghost_arg, gai_strerror(err), err);
       return -1;
     }
     else {
-      memcpy(&options.pinghost.s_addr, host->h_addr, host->h_length);
-      printf("Using ping host:       %s (%s)\n", 
-	     args_info.pinghost_arg, inet_ntoa(options.pinghost));
+       memcpy(&options.pinghost, ai->ai_addr, ai->ai_addrlen);
+       freeaddrinfo(ai);
+      //printf("Using ping host:       %s (%s)\n", 
+//	     args_info.pinghost_arg, inet_ntoa(options.pinghost));
     }
+  }
+  else {
+    options.pinghost.ss_family = 0; // Indicates not specified
   }
 
   /* Other ping parameters                                        */
@@ -687,7 +730,7 @@
   struct timezone tz;
   struct timeval tv;
   int diff;
-  if ((options.pinghost.s_addr) && (2 == state) && 
+  if ((options.pinghost.ss_family) && (2 == state) &&
       ((pingseq < options.pingcount) || (options.pingcount == 0))) {
     gettimeofday(&tv, &tz);
     diff = 1000000 / options.pingrate * pingseq -
@@ -715,7 +758,8 @@
   elapsed = 1000000 * (tv.tv_sec - firstping.tv_sec) + 
     (tv.tv_usec - firstping.tv_usec); /* Microseconds */
   printf("\n");
-  printf("\n----%s PING Statistics----\n", inet_ntoa(options.pinghost));
+  //TODO: ipv6
+  //printf("\n----%s PING Statistics----\n", inet_ntoa(options.pinghost));
   printf("%d packets transmitted in %.3f seconds, ", ntransmitted,
 	 elapsed / 1000000.0);
   printf("%d packets received, ", nreceived );
@@ -899,12 +943,21 @@
 /* Callback for receiving messages from tun */
 int cb_tun_ind(struct tun_t *tun, void *pack, unsigned len) {
   struct iphash_t *ipm;
-  struct in_addr src;
+  struct in6_addr src;
+  size_t srclen;
   struct tun_packet_t *iph = (struct tun_packet_t*) pack;
+  struct tun_packet6_t *ip6h = (struct tun_packet6_t*) pack;
 
-  src.s_addr = iph->src;
+  if (iph->ver == 4) {
+    memcpy(&src, &iph->src, sizeof(struct in_addr));
+    srclen = 4;
+  }
+  else {
+    memcpy(&src, ip6h->src, sizeof(struct in6_addr));
+    srclen = 16;
+  }
 
-  if (ipget(&ipm, &src)) {
+  if (ipget(&ipm, (struct in_addr*)&src, srclen)) {
     printf("Received packet without a valid source address!!!\n");
     return 0;
   }
@@ -916,6 +969,8 @@
 
 int create_pdp_conf(struct pdp_t *pdp, void *cbp, int cause) {
   struct in_addr addr;
+  struct in6_addr addr6;
+  int ip6 = 0;
 
   struct iphash_t *iph = (struct iphash_t*) cbp;
 
@@ -944,17 +999,20 @@
   }
 
   if (pdp_euaton(&pdp->eua, &addr)) {
-    printf("Received create PDP context response. Cause value: %d\n", cause);
-    pdp_freepdp(iph->pdp);
-    iph->pdp = NULL;
-    state = 0;
-    return EOF; /* Not a valid IP address */
+    if (pdp_euaton6(&pdp->eua, &addr6)) {
+      printf("Received create PDP context response. Cause value: %d\n", cause);
+      pdp_freepdp(iph->pdp);
+      iph->pdp = NULL;
+      state = 0;
+      return EOF; /* Not a valid IP address */
+    }
+    ip6 = 1;
   }
 
   printf("Received create PDP context response. IP address: %s\n", 
 	 inet_ntoa(addr));
 
-  if ((options.createif) && (!options.net.s_addr)) {
+  if ((options.createif) && (!options.net.ss_family)) {
     struct in_addr m;
 #ifdef HAVE_INET_ATON
     inet_aton("255.255.255.255", &m);
@@ -962,7 +1020,10 @@
     m.s_addr = -1;
 #endif
     /* printf("Setting up interface and routing\n");*/
-    tun_addaddr(tun, &addr,  &addr, &m);
+    if (ip6)
+      tun_addaddr6(tun, &addr6,  &addr6, 128);
+    else
+      tun_addaddr(tun, &addr,  &addr, &m);
     if (options.defaultroute) {
       struct in_addr rm;
       rm.s_addr = 0;
@@ -971,7 +1032,10 @@
     if (options.ipup) tun_runscript(tun, options.ipup);
   }
     
-  ipset((struct iphash_t*) pdp->peer, &addr);
+  if (ip6)
+    ipset((struct iphash_t*) pdp->peer, (struct in_addr*)&addr6, sizeof(addr6));
+  else
+    ipset((struct iphash_t*) pdp->peer, &addr, sizeof(addr));
   
   state = 2;                      /* Connected */
 
@@ -990,7 +1054,7 @@
     if (echoversion == 1) {
       printf("Retrying with version 0\n");
       echoversion = 0;
-      gtp_echo_req(gsn, echoversion, NULL, &options.remote);
+      gtp_echo_req(gsn, echoversion, NULL, (struct sockaddr*)&options.remote);
       return 0;
     }
     else {
@@ -1056,7 +1120,7 @@
     exit(1);
 
   printf("\nInitialising GTP library\n");
-  if (gtp_new(&gsn, options.statedir,  &options.listen, GTP_MODE_SGSN)) {
+  if (gtp_new(&gsn, options.statedir,  (struct sockaddr*)&options.listen, GTP_MODE_SGSN)) {
     sys_err(LOG_ERR, __FILE__, __LINE__, 0,
 	    "Failed to create gtp");
     exit(1);
@@ -1084,13 +1148,23 @@
     if (tun->fd > maxfd) maxfd = tun->fd;
   }
 
-  if ((options.createif) && (options.net.s_addr)) {
+  if ((options.createif) && (options.net.ss_family)) {
     /* printf("Setting up interface and routing\n");*/
-    tun_addaddr(tun, &options.netaddr,  &options.destaddr, &options.mask);
+    if (options.net.ss_family == AF_INET) {
+      struct in_addr mask;
+      mask.s_addr = 0xFFFFFFFF << (32-options.net_prefixlen);
+      tun_addaddr(tun, &((struct sockaddr_in*)&options.netaddr)->sin_addr,  &((struct sockaddr_in*)&options.destaddr)->sin_addr, &mask);
+    }
+    else {
+      tun_addaddr6(tun, &((struct sockaddr_in6*)&options.netaddr)->sin6_addr,  &((struct sockaddr_in6*)&options.destaddr)->sin6_addr, options.net_prefixlen);
+    }
     if (options.defaultroute) {
-      struct in_addr rm;
-      rm.s_addr = 0;
-      tun_addroute(tun, &rm, &options.destaddr, &rm);
+      if (options.destaddr.ss_family == AF_INET) {
+        struct in_addr rm;
+        rm.s_addr = 0;
+        tun_addroute(tun, &rm, &((struct sockaddr_in*)&options.destaddr)->sin_addr, &rm);
+      }
+      // TODO: IPv6 default route
     }
     if (options.ipup) tun_runscript(tun, options.ipup);
   }
@@ -1105,7 +1179,7 @@
   /* See if anybody is there */
   printf("Sending off echo request\n");
   echoversion = options.gtpversion;
-  gtp_echo_req(gsn, echoversion, NULL, &options.remote); /* Is remote alive? */
+  gtp_echo_req(gsn, echoversion, NULL, (struct sockaddr*)&options.remote); /* Is remote alive? */
 
   for(n=0; n<options.contexts; n++) {
     uint64_t myimsi;
@@ -1147,10 +1221,20 @@
       memcpy(pdp->apn_use.v, options.apn.v, options.apn.l);
     }
     
-    pdp->gsnlc.l = sizeof(options.listen);
-    memcpy(pdp->gsnlc.v, &options.listen, sizeof(options.listen));
-    pdp->gsnlu.l = sizeof(options.listen);
-    memcpy(pdp->gsnlu.v, &options.listen, sizeof(options.listen));
+    if (options.listen.ss_family == AF_INET) {
+	struct sockaddr_in* s = (struct sockaddr_in*)&options.listen;
+	memcpy(pdp->gsnlc.v, &s->sin_addr,  sizeof(s->sin_addr));
+	memcpy(pdp->gsnlu.v, &s->sin_addr,  sizeof(s->sin_addr));
+	pdp->gsnlc.l = sizeof(s->sin_addr);
+	pdp->gsnlu.l = sizeof(s->sin_addr);
+    }
+    else {
+	struct sockaddr_in6* s = (struct sockaddr_in6*)&options.listen;
+	memcpy(pdp->gsnlc.v, &s->sin6_addr,  sizeof(s->sin6_addr));
+	memcpy(pdp->gsnlu.v, &s->sin6_addr,  sizeof(s->sin6_addr));
+	pdp->gsnlc.l = sizeof(s->sin6_addr);
+	pdp->gsnlu.l = sizeof(s->sin6_addr);
+    }
     
     if (options.msisdn.l > sizeof(pdp->msisdn.v)) {
       sys_err(LOG_ERR, __FILE__, __LINE__, 0, "MSISDN length too big");
@@ -1173,8 +1257,9 @@
     
     pdp->version = options.gtpversion;
 
-    pdp->hisaddr0 = options.remote;
-    pdp->hisaddr1 = options.remote;
+    memcpy(&pdp->hisaddr0, &options.remote, options.remote.ss_family == AF_INET?sizeof(struct sockaddr_in):sizeof(struct sockaddr_in6));
+    memcpy(&pdp->hisaddr1, &options.remote, options.remote.ss_family == AF_INET?sizeof(struct sockaddr_in):sizeof(struct sockaddr_in6));
+
 
     pdp->cch_pdp = options.cch; /* 2048 = Normal, 1024 = Prepaid, 
 				   512 = Flat rate, 256 = Hot billing */
@@ -1231,7 +1316,7 @@
 	/* Delete context */
 	printf("Disconnecting PDP context #%d\n", n);
 	gtp_delete_context_req(gsn, iparr[n].pdp, NULL, 1);
-	if ((options.pinghost.s_addr !=0) && ntransmitted) ping_finish();
+	if ((options.pinghost.ss_family !=0) && ntransmitted) ping_finish();
       }
     }
 
@@ -1239,7 +1324,7 @@
     diff = 0;
     while (( diff<=0 ) && 
 	   /* Send off an ICMP ping packet */
-	   /*if (*/(options.pinghost.s_addr) && (2 == state) && 
+	   /*if (*/(options.pinghost.ss_family) && (2 == state) &&
 	   ((pingseq < options.pingcount) || (options.pingcount == 0))) {
       if (!pingseq) gettimeofday(&firstping, &tz); /* Set time of first ping */
       gettimeofday(&tv, &tz);
@@ -1249,7 +1334,7 @@
       if (diff <=0) {
 	if (options.debug) printf("Create_ping %d\n", diff);
 	create_ping(gsn, iparr[pingseq % options.contexts].pdp,
-		    &options.pinghost, pingseq, options.pingsize);
+		    &((struct sockaddr_in*)&options.pinghost)->sin_addr, pingseq, options.pingsize);
 	pingseq++;
       }
     }
diff --git a/sgsnemu/tun.c b/sgsnemu/tun.c
index 1cc706b..2bb852f 100644
--- a/sgsnemu/tun.c
+++ b/sgsnemu/tun.c
@@ -71,6 +71,13 @@
 
 #if defined(__linux__)
 
+// From linux/ipv6.h
+struct in6_ifreq {
+	struct in6_addr	ifr6_addr;
+	__u32		ifr6_prefixlen;
+	int		ifr6_ifindex;
+};
+
 int tun_nlattr(struct nlmsghdr *n, int nsize, int type, void *d, int dlen)
 {
   int len = RTA_LENGTH(dlen);
@@ -397,6 +404,174 @@
   
 }
 
+int tun_addaddr6(struct tun_t *this,
+		struct in6_addr *addr,
+		struct in6_addr *dstaddr,
+		size_t prefixlen) {
+
+#if defined(__linux__)
+  struct {
+    struct nlmsghdr 	n;
+    struct ifaddrmsg 	i;
+    char buf[TUN_NLBUFSIZE];
+  } req;
+
+  struct sockaddr_nl local;
+  socklen_t addr_len;
+  int fd;
+  int status;
+
+  struct sockaddr_nl nladdr;
+  struct iovec iov;
+  struct msghdr msg;
+
+  if (!this->addrs) /* Use ioctl for first addr to make ping work */
+    return tun_setaddr6(this, addr, dstaddr, prefixlen);
+
+  memset(&req, 0, sizeof(req));
+  req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
+  req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
+  req.n.nlmsg_type = RTM_NEWADDR;
+  req.i.ifa_family = AF_INET6;
+  req.i.ifa_prefixlen = 128; /* 128 FOR IPv6 */
+  req.i.ifa_flags = 0;
+  req.i.ifa_scope = RT_SCOPE_HOST; /* TODO or 0 */
+  if (tun_gifindex(this, &req.i.ifa_index)) {
+    return -1;
+  }
+
+  tun_nlattr(&req.n, sizeof(req), IFA_ADDRESS, addr, sizeof(addr));
+  tun_nlattr(&req.n, sizeof(req), IFA_LOCAL, dstaddr, sizeof(dstaddr));
+
+  if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0) {
+    sys_err(LOG_ERR, __FILE__, __LINE__, errno,
+	    "socket() failed");
+    return -1;
+  }
+
+  memset(&local, 0, sizeof(local));
+  local.nl_family = AF_NETLINK;
+  local.nl_groups = 0;
+
+  if (bind(fd, (struct sockaddr*)&local, sizeof(local)) < 0) {
+    sys_err(LOG_ERR, __FILE__, __LINE__, errno,
+	    "bind() failed");
+    close(fd);
+    return -1;
+  }
+
+  addr_len = sizeof(local);
+  if (getsockname(fd, (struct sockaddr*)&local, &addr_len) < 0) {
+    sys_err(LOG_ERR, __FILE__, __LINE__, errno,
+	    "getsockname() failed");
+    close(fd);
+    return -1;
+  }
+
+  if (addr_len != sizeof(local)) {
+    sys_err(LOG_ERR, __FILE__, __LINE__, 0,
+	    "Wrong address length %d", addr_len);
+    close(fd);
+    return -1;
+  }
+
+  if (local.nl_family != AF_NETLINK) {
+    sys_err(LOG_ERR, __FILE__, __LINE__, 0,
+	    "Wrong address family %d", local.nl_family);
+    close(fd);
+    return -1;
+  }
+
+  iov.iov_base = (void*)&req.n;
+  iov.iov_len = req.n.nlmsg_len;
+
+  msg.msg_name = (void*)&nladdr;
+  msg.msg_namelen = sizeof(nladdr),
+  msg.msg_iov = &iov;
+  msg.msg_iovlen = 1;
+  msg.msg_control = NULL;
+  msg.msg_controllen = 0;
+  msg.msg_flags = 0;
+
+  memset(&nladdr, 0, sizeof(nladdr));
+  nladdr.nl_family = AF_NETLINK;
+  nladdr.nl_pid = 0;
+  nladdr.nl_groups = 0;
+
+  req.n.nlmsg_seq = 0;
+  req.n.nlmsg_flags |= NLM_F_ACK;
+
+  status = sendmsg(fd, &msg, 0); /* TODO Error check */
+
+  tun_sifflags(this, IFF_UP | IFF_RUNNING); /* TODO */
+  close(fd);
+  this->addrs++;
+  return 0;
+
+
+
+#elif defined (__FreeBSD__) || defined (__APPLE__)
+
+  int fd;
+  struct ifaliasreq      areq;
+
+  /* TODO: Is this needed on FreeBSD? */
+  if (!this->addrs) /* Use ioctl for first addr to make ping work */
+    return tun_setaddr(this, addr, dstaddr, netmask); /* TODO dstaddr */
+
+  memset(&areq, 0, sizeof(areq));
+
+  /* Set up interface name */
+  strncpy(areq.ifra_name, this->devname, IFNAMSIZ);
+  areq.ifra_name[IFNAMSIZ-1] = 0; /* Make sure to terminate */
+
+  ((struct sockaddr_in*) &areq.ifra_addr)->sin_family = AF_INET;
+  ((struct sockaddr_in*) &areq.ifra_addr)->sin_len = sizeof(areq.ifra_addr);
+  ((struct sockaddr_in*) &areq.ifra_addr)->sin_addr.s_addr = addr->s_addr;
+
+  ((struct sockaddr_in*) &areq.ifra_mask)->sin_family = AF_INET;
+  ((struct sockaddr_in*) &areq.ifra_mask)->sin_len    = sizeof(areq.ifra_mask);
+  ((struct sockaddr_in*) &areq.ifra_mask)->sin_addr.s_addr = netmask->s_addr;
+
+  /* For some reason FreeBSD uses ifra_broadcast for specifying dstaddr */
+  ((struct sockaddr_in*) &areq.ifra_broadaddr)->sin_family = AF_INET;
+  ((struct sockaddr_in*) &areq.ifra_broadaddr)->sin_len = 
+    sizeof(areq.ifra_broadaddr);
+  ((struct sockaddr_in*) &areq.ifra_broadaddr)->sin_addr.s_addr = 
+    dstaddr->s_addr;
+
+  /* Create a channel to the NET kernel. */
+  if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+    sys_err(LOG_ERR, __FILE__, __LINE__, errno,
+	    "socket() failed");
+    return -1;
+  }
+
+  if (ioctl(fd, SIOCAIFADDR, (void *) &areq) < 0) {
+    sys_err(LOG_ERR, __FILE__, __LINE__, errno,
+	    "ioctl(SIOCAIFADDR) failed");
+    close(fd);
+    return -1;
+  }
+
+  close(fd);
+  this->addrs++;
+  return 0;
+
+#elif defined (__sun__)
+
+  if (!this->addrs) /* Use ioctl for first addr to make ping work */
+    return tun_setaddr(this, addr, dstaddr, netmask);
+
+  sys_err(LOG_ERR, __FILE__, __LINE__, errno,
+	  "Setting multiple addresses not possible on Solaris");
+  return -1;
+
+#else
+#error  "Unknown platform!"
+#endif
+}
+
 
 int tun_setaddr(struct tun_t *this,
 		struct in_addr *addr,
@@ -431,7 +606,7 @@
   }
 
   if (addr) { /* Set the interface address */
-    this->addr.s_addr = addr->s_addr;
+    ((struct in_addr*)&this->addr)->s_addr = addr->s_addr;
     ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr.s_addr = addr->s_addr;
     if (ioctl(fd, SIOCSIFADDR, (void *) &ifr) < 0) {
       if (errno != EEXIST) {
@@ -448,7 +623,7 @@
   }
 
   if (dstaddr) { /* Set the destination address */
-    this->dstaddr.s_addr = dstaddr->s_addr;
+    ((struct in_addr*)&this->dstaddr)->s_addr = dstaddr->s_addr;
     ((struct sockaddr_in *) &ifr.ifr_dstaddr)->sin_addr.s_addr = 
       dstaddr->s_addr;
     if (ioctl(fd, SIOCSIFDSTADDR, (caddr_t) &ifr) < 0) {
@@ -487,6 +662,87 @@
   close(fd);
   this->addrs++;
   
+  /* On linux the route to the interface is set automatically
+     on FreeBSD we have to do this manually */
+
+  /* TODO: How does it work on Solaris? */
+
+  tun_sifflags(this, IFF_UP | IFF_RUNNING);
+
+#if defined(__FreeBSD__) || defined (__APPLE__)
+  tun_addroute(this, dstaddr, addr, netmask);
+  this->routes = 1;
+#endif
+
+  return 0;
+}
+
+int tun_setaddr6(struct tun_t *this,
+		struct in6_addr *addr,
+		struct in6_addr *dstaddr,
+		size_t prefixlen)
+{
+  struct in6_ifreq   ifr;
+  struct ifreq namereq;
+  int fd;
+
+  this->ip6 = 1;
+
+  memset (&ifr, '\0', sizeof (ifr));
+  memset (&namereq, '\0', sizeof (namereq));
+
+  /* Create a channel to the NET kernel. */
+  if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+    sys_err(LOG_ERR, __FILE__, __LINE__, errno,
+	    "socket() failed");
+    return -1;
+  }
+
+  // Get interface index
+  strncpy(namereq.ifr_name, this->devname, IFNAMSIZ);
+  namereq.ifr_name[IFNAMSIZ-1] = 0; /* Make sure to terminate */
+  if (ioctl(fd, SIOGIFINDEX, &namereq) < 0) {
+    sys_err(LOG_ERR, __FILE__, __LINE__, errno, "Could not get ifindex");
+    return -1;
+  }
+  ifr.ifr6_ifindex = namereq.ifr_ifindex;
+
+  ifr.ifr6_prefixlen = prefixlen;
+
+  if (addr) { /* Set the interface address */
+    memcpy(&this->addr, addr, sizeof(struct in6_addr));
+    memcpy(&ifr.ifr6_addr, addr, sizeof(struct in6_addr));
+    if (ioctl(fd, SIOCSIFADDR, (void *) &ifr) < 0) {
+      if (errno != EEXIST) {
+	sys_err(LOG_ERR, __FILE__, __LINE__, errno,
+		"ioctl(SIOCSIFADDR) failed");
+      }
+      else {
+	sys_err(LOG_WARNING, __FILE__, __LINE__, errno,
+		"ioctl(SIOCSIFADDR): Address already exists");
+      }
+      close(fd);
+      return -1;
+    }
+  }
+
+#ifdef xxx
+  // Looks like this is not possible/necessary for ipv6?
+  if (dstaddr) { /* Set the destination address */
+    memcpy(&this->dstaddr, dstaddr, sizeof(struct in6_addr));
+    memcpy(&ifr.ifr6_addr, dstaddr, sizeof(struct in6_addr));
+    if (ioctl(fd, SIOCSIFDSTADDR, (caddr_t) &ifr) < 0) {
+      sys_err(LOG_ERR, __FILE__, __LINE__, errno,
+	      "ioctl(SIOCSIFDSTADDR) failed");
+      close(fd);
+      return -1;
+    }
+  }
+#endif //xxx
+
+  close(fd);
+  this->addrs++;
+
   /* On linux the route to the interface is set automatically
      on FreeBSD we have to do this manually */
 
@@ -883,9 +1139,10 @@
   char snet[TUN_ADDRSIZE];
   char smask[TUN_ADDRSIZE];
 
-  strncpy(snet, inet_ntoa(tun->addr), sizeof(snet));
+  //TODO: fix for IPv6
+  //strncpy(snet, inet_ntoa(tun->addr), sizeof(snet));
   snet[sizeof(snet)-1] = 0;
-  strncpy(smask, inet_ntoa(tun->netmask), sizeof(smask));
+  //strncpy(smask, inet_ntoa(tun->netmask), sizeof(smask));
   smask[sizeof(smask)-1] = 0;
   
   /* system("ipup /dev/tun0 192.168.0.10 255.255.255.0"); */
diff --git a/sgsnemu/tun.h b/sgsnemu/tun.h
index 7972c53..a75cc43 100644
--- a/sgsnemu/tun.h
+++ b/sgsnemu/tun.h
@@ -17,9 +17,18 @@
 #define TUN_ADDRSIZE     128
 #define TUN_NLBUFSIZE   1024
 
+/* This should not be re-declared!  Why not just use netinet/ip(6).h?
+ * It is not completely functional as it is here.  Just the minimum
+ * amount of hacking has been done to make it work. */
 struct tun_packet_t {
+#if BYTE_ORDER == LITTLE_ENDIAN
+  unsigned int ihl:4;
+  unsigned int ver:4;
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
   unsigned int ver:4;
   unsigned int ihl:4;
+#endif
   unsigned int dscp:6;
   unsigned int ecn:2;
   unsigned int length:16;
@@ -29,8 +38,19 @@
   unsigned int ttl:8;
   unsigned int protocol:8;
   unsigned int check:16;
-  unsigned int src:32;
-  unsigned int dst:32;
+  uint32_t src;
+  uint32_t dst;
+};
+
+struct tun_packet6_t {
+  unsigned int ver:4;
+  unsigned int tclass:8;
+  unsigned int flowlabel:20;
+  unsigned int payloadlen:16;
+  unsigned int nexthdr:8;
+  unsigned int hoplim:8;
+  unsigned char src[16];
+  unsigned char dst[16];
 };
 
 
@@ -39,10 +59,12 @@
  *************************************************************/
 
 struct tun_t {
+  int ip6;
   int fd;                /* File descriptor to tun interface */
-  struct in_addr addr;
-  struct in_addr dstaddr;
-  struct in_addr netmask;
+  struct in6_addr addr;
+  struct in6_addr dstaddr;
+  struct in_addr netmask; //ipv4
+  size_t prefixlen;
   int addrs;             /* Number of allocated IP addresses */
   int routes;            /* One if we allocated an automatic route */
   char devname[IFNAMSIZ];/* Name of the tun device */
@@ -57,10 +79,13 @@
 
 extern int tun_addaddr(struct tun_t *this, struct in_addr *addr,
 		       struct in_addr *dstaddr, struct in_addr *netmask);
-
+extern int tun_addaddr6(struct tun_t *this, struct in6_addr *addr,
+		       struct in6_addr *dstaddr, size_t prefixlen);
 
 extern int tun_setaddr(struct tun_t *this, struct in_addr *our_adr, 
 		       struct in_addr *his_adr, struct in_addr *net_mask);
+extern int tun_setaddr6(struct tun_t *this, struct in6_addr *our_adr,
+		       struct in6_addr *his_adr, size_t prefixlen);
 
 int tun_addroute(struct tun_t *this, struct in_addr *dst, 
 		 struct in_addr *gateway, struct in_addr *mask);

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

Gerrit-MessageType: newchange
Gerrit-Change-Id: If8ce8b4b8cd2ba97f7ba122de4703983111046e4
Gerrit-PatchSet: 1
Gerrit-Project: openggsn
Gerrit-Branch: master
Gerrit-Owner: groos at xiplink.com <groos at xiplink.com>



More information about the gerrit-log mailing list