added ippool.h and ippool.c
diff --git a/ggsn/Makefile.am b/ggsn/Makefile.am
index 8b84aea..7483f70 100644
--- a/ggsn/Makefile.am
+++ b/ggsn/Makefile.am
@@ -2,9 +2,9 @@
 
 LDFLAGS = -Wl,--rpath -Wl,/usr/local/lib
 
-CFLAGS = -O2 -D_GNU_SOURCE -fno-builtin -Wall -ansi -DSBINDIR='"$(sbindir)"' -lgtp -L../gtp
+CFLAGS = -O2 -D_GNU_SOURCE -fno-builtin -Wall -ansi -DSBINDIR='"$(sbindir)"' -ggdb -lgtp -L../gtp
 
-ggsn_SOURCES = ggsn.c tun.c tun.h cmdline.c cmdline.h
+ggsn_SOURCES = ggsn.c tun.c tun.h cmdline.c cmdline.h ippool.h ippool.c syserr.h syserr.c
 
 
 
diff --git a/ggsn/Makefile.in b/ggsn/Makefile.in
index 9233cfd..5a227ef 100644
--- a/ggsn/Makefile.in
+++ b/ggsn/Makefile.in
@@ -89,9 +89,9 @@
 
 LDFLAGS = -Wl,--rpath -Wl,/usr/local/lib
 
-CFLAGS = -O2 -D_GNU_SOURCE -fno-builtin -Wall -ansi -DSBINDIR='"$(sbindir)"' -lgtp -L../gtp
+CFLAGS = -O2 -D_GNU_SOURCE -fno-builtin -Wall -ansi -DSBINDIR='"$(sbindir)"' -ggdb -lgtp -L../gtp
 
-ggsn_SOURCES = ggsn.c tun.c tun.h cmdline.c cmdline.h
+ggsn_SOURCES = ggsn.c tun.c tun.h cmdline.c cmdline.h ippool.h ippool.c syserr.h syserr.c
 subdir = ggsn
 mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
 CONFIG_HEADER = $(top_builddir)/config.h
@@ -99,7 +99,8 @@
 bin_PROGRAMS = ggsn$(EXEEXT)
 PROGRAMS = $(bin_PROGRAMS)
 
-am_ggsn_OBJECTS = ggsn.$(OBJEXT) tun.$(OBJEXT) cmdline.$(OBJEXT)
+am_ggsn_OBJECTS = ggsn.$(OBJEXT) tun.$(OBJEXT) cmdline.$(OBJEXT) \
+	ippool.$(OBJEXT) syserr.$(OBJEXT)
 ggsn_OBJECTS = $(am_ggsn_OBJECTS)
 ggsn_LDADD = $(LDADD)
 ggsn_DEPENDENCIES =
@@ -112,6 +113,7 @@
 depcomp = $(SHELL) $(top_srcdir)/depcomp
 am__depfiles_maybe = depfiles
 @AMDEP_TRUE@DEP_FILES = ./$(DEPDIR)/cmdline.Po ./$(DEPDIR)/ggsn.Po \
+@AMDEP_TRUE@	./$(DEPDIR)/ippool.Po ./$(DEPDIR)/syserr.Po \
 @AMDEP_TRUE@	./$(DEPDIR)/tun.Po
 COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
 	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
@@ -174,6 +176,8 @@
 
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmdline.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ggsn.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ippool.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/syserr.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tun.Po@am__quote@
 
 distclean-depend:
diff --git a/ggsn/cmdline.c b/ggsn/cmdline.c
index 3c5d7e2..bb5c3f1 100644
--- a/ggsn/cmdline.c
+++ b/ggsn/cmdline.c
@@ -1,7 +1,7 @@
 /*
-  File autogenerated by gengetopt version 2.8rc
+  File autogenerated by gengetopt version 2.8
   generated with the following command:
-  ../../gengetopt-2.8rc/src/gengetopt --conf-parser 
+  gengetopt --conf-parser 
 
   The developers of gengetopt consider the fixed text that goes in all
   gengetopt output files to be in the public domain:
@@ -50,13 +50,16 @@
   printf("              --pidfile=STRING   Filename of process id file (default='/var/run/ggsn.pid')\n");
   printf("              --statedir=STRING  Directory of nonvolatile data (default='/var/lib/ggsn/')\n");
   printf("   -lSTRING   --listen=STRING    Local interface\n");
-  printf("   -nSTRING   --net=STRING       Network (default='192.168.0.0')\n");
-  printf("              --mask=STRING      Network mask (default='255.255.255.0')\n");
+  printf("   -nSTRING   --net=STRING       Network (default='192.168.0.0/24')\n");
+  printf("              --ipup=STRING      Script to run after link-up\n");
+  printf("              --ipdown=STRING    Script to run after link-down\n");
+  printf("              --dynip=STRING     Dynamic IP address pool (default='192.168.0.0/24')\n");
+  printf("              --statip=STRING    Static IP address pool (default='192.168.1.0/24')\n");
+  printf("              --pcodns1=STRING   PCO DNS Server 1 (default='0.0.0.0')\n");
+  printf("              --pcodns2=STRING   PCO DNS Server 2 (default='0.0.0.0')\n");
   printf("              --timelimit=INT    Exit after timelimit seconds (default='0')\n");
   printf("   -aSTRING   --apn=STRING       Access point name (default='internet')\n");
   printf("   -qINT      --qos=INT          Requested quality of service (default='0x0b921f')\n");
-  printf("              --ipup=STRING      Script to run after link-up\n");
-  printf("              --ipdown=STRING    Script to run after link-down\n");
 }
 
 
@@ -89,12 +92,15 @@
   args_info->statedir_given = 0 ;
   args_info->listen_given = 0 ;
   args_info->net_given = 0 ;
-  args_info->mask_given = 0 ;
+  args_info->ipup_given = 0 ;
+  args_info->ipdown_given = 0 ;
+  args_info->dynip_given = 0 ;
+  args_info->statip_given = 0 ;
+  args_info->pcodns1_given = 0 ;
+  args_info->pcodns2_given = 0 ;
   args_info->timelimit_given = 0 ;
   args_info->apn_given = 0 ;
   args_info->qos_given = 0 ;
-  args_info->ipup_given = 0 ;
-  args_info->ipdown_given = 0 ;
 #define clear_args() { \
   args_info->fg_flag = 0;\
   args_info->debug_flag = 0;\
@@ -102,13 +108,16 @@
   args_info->pidfile_arg = strdup("/var/run/ggsn.pid") ;\
   args_info->statedir_arg = strdup("/var/lib/ggsn/") ;\
   args_info->listen_arg = NULL; \
-  args_info->net_arg = strdup("192.168.0.0") ;\
-  args_info->mask_arg = strdup("255.255.255.0") ;\
+  args_info->net_arg = strdup("192.168.0.0/24") ;\
+  args_info->ipup_arg = NULL; \
+  args_info->ipdown_arg = NULL; \
+  args_info->dynip_arg = strdup("192.168.0.0/24") ;\
+  args_info->statip_arg = strdup("192.168.1.0/24") ;\
+  args_info->pcodns1_arg = strdup("0.0.0.0") ;\
+  args_info->pcodns2_arg = strdup("0.0.0.0") ;\
   args_info->timelimit_arg = 0 ;\
   args_info->apn_arg = strdup("internet") ;\
   args_info->qos_arg = 0x0b921f ;\
-  args_info->ipup_arg = NULL; \
-  args_info->ipdown_arg = NULL; \
 }
 
   clear_args();
@@ -132,12 +141,15 @@
         { "statedir",	1, NULL, 0 },
         { "listen",	1, NULL, 'l' },
         { "net",	1, NULL, 'n' },
-        { "mask",	1, NULL, 0 },
+        { "ipup",	1, NULL, 0 },
+        { "ipdown",	1, NULL, 0 },
+        { "dynip",	1, NULL, 0 },
+        { "statip",	1, NULL, 0 },
+        { "pcodns1",	1, NULL, 0 },
+        { "pcodns2",	1, NULL, 0 },
         { "timelimit",	1, NULL, 0 },
         { "apn",	1, NULL, 'a' },
         { "qos",	1, NULL, 'q' },
-        { "ipup",	1, NULL, 0 },
-        { "ipdown",	1, NULL, 0 },
         { NULL,	0, NULL, 0 }
       };
 
@@ -262,32 +274,6 @@
             args_info->statedir_arg = strdup (optarg);
             break;
           }
-          /* Network mask.  */
-          else if (strcmp (long_options[option_index].name, "mask") == 0)
-          {
-            if (args_info->mask_given)
-              {
-                fprintf (stderr, "%s: `--mask' option given more than once\n", PACKAGE);
-                clear_args ();
-                exit (EXIT_FAILURE);
-              }
-            args_info->mask_given = 1;
-            args_info->mask_arg = strdup (optarg);
-            break;
-          }
-          /* Exit after timelimit seconds.  */
-          else if (strcmp (long_options[option_index].name, "timelimit") == 0)
-          {
-            if (args_info->timelimit_given)
-              {
-                fprintf (stderr, "%s: `--timelimit' option given more than once\n", PACKAGE);
-                clear_args ();
-                exit (EXIT_FAILURE);
-              }
-            args_info->timelimit_given = 1;
-            args_info->timelimit_arg = strtol (optarg,&stop_char,0);
-            break;
-          }
           /* Script to run after link-up.  */
           else if (strcmp (long_options[option_index].name, "ipup") == 0)
           {
@@ -314,6 +300,71 @@
             args_info->ipdown_arg = strdup (optarg);
             break;
           }
+          /* Dynamic IP address pool.  */
+          else if (strcmp (long_options[option_index].name, "dynip") == 0)
+          {
+            if (args_info->dynip_given)
+              {
+                fprintf (stderr, "%s: `--dynip' option given more than once\n", PACKAGE);
+                clear_args ();
+                exit (EXIT_FAILURE);
+              }
+            args_info->dynip_given = 1;
+            args_info->dynip_arg = strdup (optarg);
+            break;
+          }
+          /* Static IP address pool.  */
+          else if (strcmp (long_options[option_index].name, "statip") == 0)
+          {
+            if (args_info->statip_given)
+              {
+                fprintf (stderr, "%s: `--statip' option given more than once\n", PACKAGE);
+                clear_args ();
+                exit (EXIT_FAILURE);
+              }
+            args_info->statip_given = 1;
+            args_info->statip_arg = strdup (optarg);
+            break;
+          }
+          /* PCO DNS Server 1.  */
+          else if (strcmp (long_options[option_index].name, "pcodns1") == 0)
+          {
+            if (args_info->pcodns1_given)
+              {
+                fprintf (stderr, "%s: `--pcodns1' option given more than once\n", PACKAGE);
+                clear_args ();
+                exit (EXIT_FAILURE);
+              }
+            args_info->pcodns1_given = 1;
+            args_info->pcodns1_arg = strdup (optarg);
+            break;
+          }
+          /* PCO DNS Server 2.  */
+          else if (strcmp (long_options[option_index].name, "pcodns2") == 0)
+          {
+            if (args_info->pcodns2_given)
+              {
+                fprintf (stderr, "%s: `--pcodns2' option given more than once\n", PACKAGE);
+                clear_args ();
+                exit (EXIT_FAILURE);
+              }
+            args_info->pcodns2_given = 1;
+            args_info->pcodns2_arg = strdup (optarg);
+            break;
+          }
+          /* Exit after timelimit seconds.  */
+          else if (strcmp (long_options[option_index].name, "timelimit") == 0)
+          {
+            if (args_info->timelimit_given)
+              {
+                fprintf (stderr, "%s: `--timelimit' option given more than once\n", PACKAGE);
+                clear_args ();
+                exit (EXIT_FAILURE);
+              }
+            args_info->timelimit_given = 1;
+            args_info->timelimit_arg = strtol (optarg,&stop_char,0);
+            break;
+          }
 
         case '?':	/* Invalid option.  */
           /* `getopt_long' already printed an error message.  */
@@ -485,13 +536,93 @@
                 }
               continue;
             }
-          if (!strcmp(fopt, "mask"))
+          if (!strcmp(fopt, "ipup"))
             {
-              if (override || !args_info->mask_given)
+              if (override || !args_info->ipup_given)
                 {
-                  args_info->mask_given = 1;
+                  args_info->ipup_given = 1;
                   if (fnum == 2)
-                    args_info->mask_arg = strdup (farg);
+                    args_info->ipup_arg = strdup (farg);
+                  else
+                    {
+                      fprintf (stderr, "%s:%d: required <option_name> <option_val>\n",
+                               filename, line_num);
+                      exit (EXIT_FAILURE);
+                    }
+                }
+              continue;
+            }
+          if (!strcmp(fopt, "ipdown"))
+            {
+              if (override || !args_info->ipdown_given)
+                {
+                  args_info->ipdown_given = 1;
+                  if (fnum == 2)
+                    args_info->ipdown_arg = strdup (farg);
+                  else
+                    {
+                      fprintf (stderr, "%s:%d: required <option_name> <option_val>\n",
+                               filename, line_num);
+                      exit (EXIT_FAILURE);
+                    }
+                }
+              continue;
+            }
+          if (!strcmp(fopt, "dynip"))
+            {
+              if (override || !args_info->dynip_given)
+                {
+                  args_info->dynip_given = 1;
+                  if (fnum == 2)
+                    args_info->dynip_arg = strdup (farg);
+                  else
+                    {
+                      fprintf (stderr, "%s:%d: required <option_name> <option_val>\n",
+                               filename, line_num);
+                      exit (EXIT_FAILURE);
+                    }
+                }
+              continue;
+            }
+          if (!strcmp(fopt, "statip"))
+            {
+              if (override || !args_info->statip_given)
+                {
+                  args_info->statip_given = 1;
+                  if (fnum == 2)
+                    args_info->statip_arg = strdup (farg);
+                  else
+                    {
+                      fprintf (stderr, "%s:%d: required <option_name> <option_val>\n",
+                               filename, line_num);
+                      exit (EXIT_FAILURE);
+                    }
+                }
+              continue;
+            }
+          if (!strcmp(fopt, "pcodns1"))
+            {
+              if (override || !args_info->pcodns1_given)
+                {
+                  args_info->pcodns1_given = 1;
+                  if (fnum == 2)
+                    args_info->pcodns1_arg = strdup (farg);
+                  else
+                    {
+                      fprintf (stderr, "%s:%d: required <option_name> <option_val>\n",
+                               filename, line_num);
+                      exit (EXIT_FAILURE);
+                    }
+                }
+              continue;
+            }
+          if (!strcmp(fopt, "pcodns2"))
+            {
+              if (override || !args_info->pcodns2_given)
+                {
+                  args_info->pcodns2_given = 1;
+                  if (fnum == 2)
+                    args_info->pcodns2_arg = strdup (farg);
                   else
                     {
                       fprintf (stderr, "%s:%d: required <option_name> <option_val>\n",
@@ -549,38 +680,6 @@
                 }
               continue;
             }
-          if (!strcmp(fopt, "ipup"))
-            {
-              if (override || !args_info->ipup_given)
-                {
-                  args_info->ipup_given = 1;
-                  if (fnum == 2)
-                    args_info->ipup_arg = strdup (farg);
-                  else
-                    {
-                      fprintf (stderr, "%s:%d: required <option_name> <option_val>\n",
-                               filename, line_num);
-                      exit (EXIT_FAILURE);
-                    }
-                }
-              continue;
-            }
-          if (!strcmp(fopt, "ipdown"))
-            {
-              if (override || !args_info->ipdown_given)
-                {
-                  args_info->ipdown_given = 1;
-                  if (fnum == 2)
-                    args_info->ipdown_arg = strdup (farg);
-                  else
-                    {
-                      fprintf (stderr, "%s:%d: required <option_name> <option_val>\n",
-                               filename, line_num);
-                      exit (EXIT_FAILURE);
-                    }
-                }
-              continue;
-            }
           
 
           /* Tried all known options. This one is unknown! */
diff --git a/ggsn/cmdline.ggo b/ggsn/cmdline.ggo
index c4d281d..0e3c548 100644
--- a/ggsn/cmdline.ggo
+++ b/ggsn/cmdline.ggo
@@ -1,15 +1,15 @@
-#  OpenGGSN - Gateway GPRS Support Node
-#  Copyright (C) 2002 Mondru AB.
+# OpenGGSN - Gateway GPRS Support Node
+# Copyright (C) 2002, 2003 Mondru AB.
 #  
-#  The contents of this file may be used under the terms of the GNU
-#  General Public License Version 2, provided that the above copyright
-#  notice and this permission notice is included in all copies or
-#  substantial portions of the software.
+# The contents of this file may be used under the terms of the GNU
+# General Public License Version 2, provided that the above copyright
+# notice and this permission notice is included in all copies or
+# substantial portions of the software.
 #  
-#  The initial developer of the original code is
-#  Jens Jakobsen <jj@openggsn.org>
+# The initial developer of the original code is
+# Jens Jakobsen <jj@openggsn.org>
 #  
-#  Contributor(s):
+# Contributor(s):
 
 
 option  "fg"          f "Run in foreground"             flag   off
@@ -20,14 +20,19 @@
 option  "statedir"    - "Directory of nonvolatile data" string default="/var/lib/ggsn/" no
 
 option  "listen"      l "Local interface"               string no
-option  "net"         n "Network"                       string default="192.168.0.0" no
-option  "mask"        - "Network mask"                  string default="255.255.255.0" no
+option  "net"         n "Network"                       string default="192.168.0.0/24" no
+option  "ipup"        - "Script to run after link-up"    string no
+option  "ipdown"      - "Script to run after link-down"  string no
+
+option  "dynip"       - "Dynamic IP address pool"       string default="192.168.0.0/24" no
+option  "statip"      - "Static IP address pool"        string default="192.168.1.0/24" no
+
+option  "pcodns1"     - "PCO DNS Server 1"              string default="0.0.0.0" no
+option  "pcodns2"     - "PCO DNS Server 2"              string default="0.0.0.0" no
 
 option  "timelimit"   - "Exit after timelimit seconds"  int default="0" no
 
 option  "apn"         a "Access point name"             string default="internet" no
 option  "qos"         q "Requested quality of service"  int    default="0x0b921f" no
 
-option  "ipup"        - "Script to run after link-up"    string no
-option  "ipdown"      - "Script to run after link-down"  string no
 
diff --git a/ggsn/cmdline.h b/ggsn/cmdline.h
index 22c723f..0cbf483 100644
--- a/ggsn/cmdline.h
+++ b/ggsn/cmdline.h
@@ -1,6 +1,6 @@
 /* cmdline.h */
 
-/* File autogenerated by gengetopt version 2.8rc  */
+/* File autogenerated by gengetopt version 2.8  */
 
 #ifndef _cmdline_h
 #define _cmdline_h
@@ -26,13 +26,16 @@
   char * pidfile_arg;	/* Filename of process id file (default='/var/run/ggsn.pid').  */
   char * statedir_arg;	/* Directory of nonvolatile data (default='/var/lib/ggsn/').  */
   char * listen_arg;	/* Local interface.  */
-  char * net_arg;	/* Network (default='192.168.0.0').  */
-  char * mask_arg;	/* Network mask (default='255.255.255.0').  */
+  char * net_arg;	/* Network (default='192.168.0.0/24').  */
+  char * ipup_arg;	/* Script to run after link-up.  */
+  char * ipdown_arg;	/* Script to run after link-down.  */
+  char * dynip_arg;	/* Dynamic IP address pool (default='192.168.0.0/24').  */
+  char * statip_arg;	/* Static IP address pool (default='192.168.1.0/24').  */
+  char * pcodns1_arg;	/* PCO DNS Server 1 (default='0.0.0.0').  */
+  char * pcodns2_arg;	/* PCO DNS Server 2 (default='0.0.0.0').  */
   int timelimit_arg;	/* Exit after timelimit seconds (default='0').  */
   char * apn_arg;	/* Access point name (default='internet').  */
   int qos_arg;	/* Requested quality of service (default='0x0b921f').  */
-  char * ipup_arg;	/* Script to run after link-up.  */
-  char * ipdown_arg;	/* Script to run after link-down.  */
 
   int help_given ;	/* Whether help was given.  */
   int version_given ;	/* Whether version was given.  */
@@ -43,12 +46,15 @@
   int statedir_given ;	/* Whether statedir was given.  */
   int listen_given ;	/* Whether listen was given.  */
   int net_given ;	/* Whether net was given.  */
-  int mask_given ;	/* Whether mask was given.  */
+  int ipup_given ;	/* Whether ipup was given.  */
+  int ipdown_given ;	/* Whether ipdown was given.  */
+  int dynip_given ;	/* Whether dynip was given.  */
+  int statip_given ;	/* Whether statip was given.  */
+  int pcodns1_given ;	/* Whether pcodns1 was given.  */
+  int pcodns2_given ;	/* Whether pcodns2 was given.  */
   int timelimit_given ;	/* Whether timelimit was given.  */
   int apn_given ;	/* Whether apn was given.  */
   int qos_given ;	/* Whether qos was given.  */
-  int ipup_given ;	/* Whether ipup was given.  */
-  int ipdown_given ;	/* Whether ipdown was given.  */
 
 } ;
 
diff --git a/ggsn/ggsn.c b/ggsn/ggsn.c
index 3f9767b..322cc9d 100644
--- a/ggsn/ggsn.c
+++ b/ggsn/ggsn.c
@@ -1,16 +1,16 @@
 /* 
- *  OpenGGSN - Gateway GPRS Support Node
- *  Copyright (C) 2002 Mondru AB.
+ * OpenGGSN - Gateway GPRS Support Node
+ * Copyright (C) 2002, 2003 Mondru AB.
  * 
- *  The contents of this file may be used under the terms of the GNU
- *  General Public License Version 2, provided that the above copyright
- *  notice and this permission notice is included in all copies or
- *  substantial portions of the software.
+ * The contents of this file may be used under the terms of the GNU
+ * General Public License Version 2, provided that the above copyright
+ * notice and this permission notice is included in all copies or
+ * substantial portions of the software.
  * 
- *  The initial developer of the original code is
- *  Jens Jakobsen <jj@openggsn.org>
+ * The initial developer of the original code is
+ * Jens Jakobsen <jj@openggsn.org>
  * 
- *  Contributor(s):
+ * Contributor(s):
  * 
  */
 
@@ -52,17 +52,29 @@
 #include <time.h>
 
 #include "tun.h"
+#include "ippool.h"
+#include "syserr.h"
 #include "../gtp/pdp.h"
 #include "../gtp/gtp.h"
 #include "cmdline.h"
 
 
-int maxfd = 0;	                /* For select() */
-int tun_fd = -1;		/* Network file descriptor */
+int maxfd = 0;	                /* For select()            */
 struct tun_t *tun;              /* TUN instance            */
+
+struct in_addr listen_;
 struct in_addr net, mask;       /* Network interface       */
-char *ipup, *ipdown;            /* Filename of scripts */
-int debug;                      /* Print debug output */
+struct in_addr dns1, dns2;      /* PCO DNS address         */
+char *ipup, *ipdown;            /* Filename of scripts     */
+int debug;                      /* Print debug output      */
+struct ul255_t pco;
+struct ul255_t qos;
+struct ul255_t apn;
+
+struct tun_t *tun;              /* TUN instance            */
+struct ippool_t *ippool;        /* Pool of IP addresses    */
+struct gsn_t *gsn;              /* GSN instance            */
+
 
 
 /* Used to write process ID to file. Assume someone else will delete */
@@ -94,60 +106,16 @@
   return 0;
 }
 
-int getip(struct pdp_t *pdp, void* ipif, struct ul66_t *eua,
-	  struct in_addr *net, struct in_addr *mask) {
-  struct in_addr addr;
-  uint32_t ip_start, ip_end, ip_cur;
-  struct pdp_t *pdp_;
-  struct ul66_t eua_;
-
-  if (debug) {
-  printf("Begin getip %d %d %2x%2x%2x%2x\n", (unsigned)ipif, eua->l, 
-	 eua->v[2],eua->v[3],eua->v[4],eua->v[5]);
-  }
-
-  ip_start = ntoh32(net->s_addr & mask->s_addr);
-  ip_end   = ntoh32(hton32(ip_start) | ~mask->s_addr);
-
-  /* By convention the first address is the network address, and the last */
-  /* address is the broadcast address. This way two IP addresses are "lost" */
-  ip_start++; 
-  
-  if (eua->l == 0) { /* No address supplied. Find one that is available! */
-    /* This routine does linear search. In order to support millions of 
-     * addresses we should instead keep a linked list of available adresses */
-    for (ip_cur = ip_start; ip_cur < ip_end; ip_cur++) {
-      addr.s_addr = hton32(ip_cur);
-      pdp_ntoeua(&addr, &eua_);
-      if (pdp_ipget(&pdp_, ipif, &eua_) == -1) {
-	pdp_ntoeua(&addr, &pdp->eua);
-	pdp->ipif = ipif;
-	return 0;
-      };
-    }
-    return EOF; /* No addresses available */
-  }
-  else { /* Address supplied */
-    if (pdp_ipget(&pdp_, ipif, eua) == -1) {
-      pdp->ipif = ipif;
-      pdp->eua.l = eua->l;
-      memcpy(pdp->eua.v, eua->v, eua->l);
-      return 0;
-    }
-    else return EOF; /* Specified address not available */
-  }
-}
-
-
 int delete_context(struct pdp_t *pdp) {
   if (debug) printf("Deleting PDP context\n");
-  pdp_ipdel(pdp);
+  ippool_freeip((struct ippoolm_t *) pdp->peer);
   return 0;
 }
 
 
-
 int create_context(struct pdp_t *pdp) {
+  struct in_addr addr;
+  struct ippoolm_t *member;
 
   if (debug) printf("Received create PDP context request\n");
 
@@ -155,69 +123,43 @@
 
   /* ulcpy(&pdp->qos_neg, &pdp->qos_req, sizeof(pdp->qos_req.v)); */
   memcpy(pdp->qos_neg0, pdp->qos_req0, sizeof(pdp->qos_neg));
+  memcpy(&pdp->pco_neg, &pco, sizeof(pdp->pco_neg));
 
-  getip(pdp, tun, &pdp->eua, &net, &mask);
-  pdp_ipset(pdp, pdp->ipif, &pdp->eua);
+  if (pdp_euaton(&pdp->eua, &addr)) {
+    addr.s_addr = 0; /* Request dynamic */
+  }
+
+  if (ippool_newip(ippool, &member, &addr)) {
+    return EOF; /* Allready in use, or no more available */
+  }
+
+  pdp_ntoeua(&member->addr, &pdp->eua);
+  pdp->peer = &member;
+  pdp->ipif = tun; /* TODO */
+  member->peer = pdp;
 
   return 0; /* Success */
 }
 
 
-
-int create_tun() {
-  char buf[1024];
-  char snet[100], smask[100];
-
-  if ((tun_fd = tun_newtun((struct tun_t**) &tun)) > maxfd)
-    maxfd = tun_fd;
-
-  if (tun_fd == -1) {
-    printf("Failed to open tun\n");
-    exit(1);
-  }
-
-  strncpy(snet, inet_ntoa(net), sizeof(snet)); 
-  snet[sizeof(snet)-1] = 0;
-  strncpy(smask, inet_ntoa(mask), sizeof(smask));
-  smask[sizeof(smask)-1] = 0;
-
-  snprintf(buf, sizeof(buf), "/sbin/ifconfig %s %s mtu 1450 netmask %s",
-	  tun->devname, snet, smask);
-  buf[sizeof(buf)-1] = 0;
-  if (debug) printf("%s\n", buf);
-  system(buf);
-
-  if (ipup) {
-    /* system("ipup /dev/tun0 192.168.0.10"); */
-    snprintf(buf, sizeof(buf), "%s %s %s %s",
-	     ipup, tun->devname, snet, smask);
-    buf[sizeof(buf)-1] = 0;
-    if (debug) printf("%s\n", buf);
-    system(buf);
-  }
-
-  return 0;
-}
-
-
-int encaps_gtp(void *gsn, struct tun_t *tun, void *pack, unsigned len) {
-  struct pdp_t *pdp;
-  struct in_addr addr;
-  struct ul66_t eua;
-  /*printf("encaps_gtp. Packet received: forwarding to gtp.\n");*/
-  /* First we need to extract the IP destination address */
-  memcpy(&addr.s_addr, pack+16, 4); /* This ought to be dest addr */
-  pdp_ntoeua(&addr, &eua);
-  if (pdp_ipget(&pdp, tun, &eua) == 0) {
-    return gtp_gpdu((struct gsn_t*) gsn, pdp, pack, len);
-  }
-  else {
+/* 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 tun_packet_t *iph = (struct tun_packet_t*) pack;
+  
+  dst.s_addr = iph->dst;
+  
+  if (ippool_getip(ippool, &ipm, &dst)) {
     if (debug) printf("Received packet with no destination!!!\n");
     return 0;
   }
+  
+  if (ipm->peer) /* Check if a peer protocol is defined */
+    gtp_gpdu(gsn, (struct pdp_t*) ipm->peer, pack, len);
+  return 0;
 }
 
-
 int encaps_tun(struct pdp_t *pdp, void *pack, unsigned len) {
   /*  printf("encaps_tun. Packet received: forwarding to tun\n");*/
   return tun_encaps((struct tun_t*) pdp->ipif, pack, len);
@@ -231,16 +173,10 @@
 
   struct hostent *host;
 
-  struct in_addr listen;
 	
-  int gtpfd = -1;		/* Network file descriptor */
-  struct gsn_t *gsn;            /* GSN instance            */
-
   fd_set fds;			/* For select() */
   struct timeval idleTime;	/* How long to select() */
 
-  struct ul_t qos, apn;
-  unsigned char qosh[3], apnh[256];
 
   int timelimit; /* Number of seconds to be connected */
   int starttime; /* Time program was started */
@@ -259,7 +195,8 @@
     printf("qos: %#08x\n", args_info.qos_arg);
     printf("apn: %s\n", args_info.apn_arg);
     printf("net: %s\n", args_info.net_arg);
-    printf("mask: %s\n", args_info.mask_arg);
+    printf("dynip: %s\n", args_info.dynip_arg);
+    printf("statip: %s\n", args_info.statip_arg);
     printf("ipup: %s\n", args_info.ipup_arg);
     printf("ipdown: %s\n", args_info.ipdown_arg);
     printf("pidfile: %s\n", args_info.pidfile_arg);
@@ -280,7 +217,8 @@
     printf("qos: %#08x\n", args_info.qos_arg);
     printf("apn: %s\n", args_info.apn_arg);
     printf("net: %s\n", args_info.net_arg);
-    printf("mask: %s\n", args_info.mask_arg);
+    printf("dynip: %s\n", args_info.dynip_arg);
+    printf("statip: %s\n", args_info.statip_arg);
     printf("ipup: %s\n", args_info.ipup_arg);
     printf("ipdown: %s\n", args_info.ipdown_arg);
     printf("pidfile: %s\n", args_info.pidfile_arg);
@@ -326,37 +264,62 @@
       return 1;
     }
     else {
-      memcpy(&listen.s_addr, host->h_addr, host->h_length);
+      memcpy(&listen_.s_addr, host->h_addr, host->h_length);
     }
   }
   else {
-    listen.s_addr = htonl(INADDR_ANY);
+    listen_.s_addr = htonl(INADDR_ANY);
   }
   
   /* net                                                          */
-  /* Store net as in_addr                                         */
+  /* Store net as in_addr net and mask                            */
   if (args_info.net_arg) {
-    if (!inet_aton(args_info.net_arg, &net)) {
-      fprintf(stderr, "%s: Invalid network address: %s!\n", 
-	      PACKAGE, args_info.net_arg);
-      syslog(LOG_ERR, "Invalid network address: %s!", 
-	     args_info.net_arg);
-      return 1;
+    if(ippool_aton(&net, &mask, args_info.net_arg, 0)) {
+      sys_err(LOG_ERR, __FILE__, __LINE__, 0,
+	      "Invalid network address: %s!", args_info.net_arg);
+      return -1;
     }
   }
 
-  /* mask                                                         */
-  /* Store mask as in_addr                                        */
-  if (args_info.mask_arg) {
-    if (!inet_aton(args_info.mask_arg, &mask)) {
-      fprintf(stderr, "%s: Invalid network mask: %s!\n", 
-	      PACKAGE, args_info.mask_arg);
-      syslog(LOG_ERR, "Invalid network mask: %s!", 
-	     args_info.mask_arg);
-      return 1;
+  /* dynip                                                        */
+  if (!args_info.dynip_arg) {
+    sys_err(LOG_ERR, __FILE__, __LINE__, 0,
+	    "No dynamic address pool given!");
+    return -1;
+  }
+  else {
+    if (ippool_new(&ippool, args_info.dynip_arg, 
+		   IPPOOL_NONETWORK | IPPOOL_NOBROADCAST)) {
+      sys_err(LOG_ERR, __FILE__, __LINE__, 0,
+	      "Failed to allocate IP pool!");
     }
   }
 
+  /* DNS1 and DNS2 */
+  dns1.s_addr = 0;
+  if (args_info.pcodns1_arg)
+    inet_aton(args_info.pcodns1_arg, &dns1);
+
+  dns2.s_addr = 0;
+  if (args_info.pcodns2_arg)
+    inet_aton(args_info.pcodns2_arg, &dns2);
+
+  pco.l = 20;
+  pco.v[0] = 0x80; /* x0000yyy x=1, yyy=000: PPP */
+  pco.v[1] = 0x80; /* IPCP */
+  pco.v[2] = 0x21; 
+  pco.v[3] = 0x10; /* Length of contents */
+  pco.v[4] = 0x02; /* ACK */
+  pco.v[5] = 0x00; /* ID: Need to match request */
+  pco.v[6] = 0x00; /* Length */
+  pco.v[7] = 0x10;
+  pco.v[8] = 0x81; /* DNS 1 */
+  pco.v[9] = 0x06;
+  memcpy(&pco.v[10], &dns1, sizeof(dns1));
+  pco.v[14] = 0x83;
+  pco.v[15] = 0x06; /* DNS 2 */
+  memcpy(&pco.v[16], &dns2, sizeof(dns2));
+
   /* ipup */
   ipup = args_info.ipup_arg;
 
@@ -369,35 +332,64 @@
   
   /* qos                                                             */
   qos.l = 3;
-  qos.v = qosh;
   qos.v[2] = (args_info.qos_arg) & 0xff;
   qos.v[1] = ((args_info.qos_arg) >> 8) & 0xff;
   qos.v[0] = ((args_info.qos_arg) >> 16) & 0xff;
-  
+
   /* apn                                                             */
-  if (strlen(args_info.apn_arg)>(sizeof(apnh)-1)) {
-    printf("invalid APN\n");
-    exit(1);
+  if (strlen(args_info.apn_arg) > (sizeof(apn.v)-1)) {
+    printf("Invalid APN\n");
+    return -1;
   }
   apn.l = strlen(args_info.apn_arg) + 1;
-  apn.v = apnh;
   apn.v[0] = (char) strlen(args_info.apn_arg);
-  strncpy(&apn.v[1], args_info.apn_arg, (sizeof(apnh)-1));
+  strncpy(&apn.v[1], args_info.apn_arg, sizeof(apn.v)-1);
+  
+  
 
   if (debug) printf("gtpclient: Initialising GTP tunnel\n");
   
-  if ((gtpfd = gtp_new(&gsn, args_info.statedir_arg, &listen)) > maxfd)
-    maxfd = gtpfd;
-
-  if ((gtpfd = gtp_fd(gsn)) > maxfd)
-    maxfd = gtpfd;
+  if (gtp_new(&gsn, args_info.statedir_arg,  &listen_)) {
+    sys_err(LOG_ERR, __FILE__, __LINE__, 0,
+	    "Failed to create gtp");
+    exit(1);
+  }
+  if (gsn->fd > maxfd) maxfd = gsn->fd;
     
 
   gtp_set_cb_gpdu(gsn, encaps_tun);
   gtp_set_cb_delete_context(gsn, delete_context);
-  
   gtp_set_cb_create_context(gsn, create_context);
-  create_tun();
+
+
+  /* Create a tunnel interface */
+  if (tun_new((struct tun_t**) &tun)) {
+    sys_err(LOG_ERR, __FILE__, __LINE__, 0,
+	    "Failed to create tun");
+    exit(1);
+  }
+
+  tun_setaddr(tun, &net,  &net, &mask);
+  tun_set_cb_ind(tun, cb_tun_ind);
+  if (tun->fd > maxfd) maxfd = tun->fd;
+
+  if (ipup) {
+    char buf[1024];
+    char snet[100];
+    char smask[100];
+
+    strncpy(snet, inet_ntoa(net), sizeof(snet));
+    snet[sizeof(snet)-1] = 0;
+    strncpy(smask, inet_ntoa(mask), sizeof(smask));
+    smask[sizeof(smask)-1] = 0;
+    
+    /* system("ipup /dev/tun0 192.168.0.10"); */
+    snprintf(buf, sizeof(buf), "%s %s %s %s",
+	     ipup, tun->devname, snet, smask);
+    buf[sizeof(buf)-1] = 0;
+    if (debug) printf("%s\n", buf);
+    system(buf);
+  }
 
   /******************************************************************/
   /* Main select loop                                               */
@@ -406,8 +398,8 @@
   while (((starttime + timelimit) > time(NULL)) || (0 == timelimit)) {
 	
     FD_ZERO(&fds);
-    if (tun_fd != -1) FD_SET(tun_fd, &fds);
-    if (gtpfd != -1) FD_SET(gtpfd, &fds);
+    if (tun) FD_SET(tun->fd, &fds);
+    FD_SET(gsn->fd, &fds);
     
     gtp_retranstimeout(gsn, &idleTime);
     switch (select(maxfd + 1, &fds, NULL, NULL, &idleTime)) {
@@ -418,26 +410,25 @@
       syslog(LOG_ERR, "GGSN: select = -1");
       break;  
     case 0:
+      /* printf("Select returned 0\n"); */
       gtp_retrans(gsn); /* Only retransmit if nothing else */
       break; 
     default:
       break;
     }
 
-    if (tun_fd != -1 && FD_ISSET(tun_fd, &fds) && 
-	tun_decaps(tun, encaps_gtp, gsn) < 0) {
-      syslog(LOG_ERR, "TUN read failed (fd)=(%d)", tun_fd);
+    if (tun->fd != -1 && FD_ISSET(tun->fd, &fds) && 
+	tun_decaps(tun) < 0) {
+      syslog(LOG_ERR, "TUN read failed (fd)=(%d)", tun->fd);
     }
 
-    if (gtpfd != -1 && FD_ISSET(gtpfd, &fds) && 
-	gtp_decaps(gsn) < 0) {
-      syslog(LOG_ERR, "GTP read failed (gtpfd)=(%d)", gtpfd);
-    }
-    
+    if (FD_ISSET(gsn->fd, &fds))
+      gtp_decaps(gsn);
     
   }
 
   gtp_free(gsn);
+  tun_free(tun);
   
   return 1;
   
diff --git a/ggsn/ippool.c b/ggsn/ippool.c
new file mode 100644
index 0000000..3ad5c04
--- /dev/null
+++ b/ggsn/ippool.c
@@ -0,0 +1,416 @@
+/* 
+ * IP address pool functions.
+ * Copyright (C) 2003 Mondru AB.
+ * 
+ * The contents of this file may be used under the terms of the GNU
+ * General Public License Version 2, provided that the above copyright
+ * notice and this permission notice is included in all copies or
+ * substantial portions of the software.
+ * 
+ * The initial developer of the original code is
+ * Jens Jakobsen <jj@openggsn.org>
+ * 
+ * Contributor(s):
+ * 
+ */
+
+#include <netinet/in.h> /* in_addr */
+#include <stdlib.h>     /* calloc */
+#include <stdio.h>      /* sscanf */
+
+#include "ippool.h"
+
+
+/*
+--------------------------------------------------------------------
+Public domain by From Bob Jenkins, December 1996.
+mix -- mix 3 32-bit values reversibly.
+For every delta with one or two bit set, and the deltas of all three
+  high bits or all three low bits, whether the original value of a,b,c
+  is almost all zero or is uniformly distributed,
+* If mix() is run forward or backward, at least 32 bits in a,b,c
+  have at least 1/4 probability of changing.
+* If mix() is run forward, every bit of c will change between 1/3 and
+  2/3 of the time.  (Well, 22/100 and 78/100 for some 2-bit deltas.)
+mix() was built out of 36 single-cycle latency instructions in a 
+  structure that could supported 2x parallelism, like so:
+      a -= b; 
+      a -= c; x = (c>>13);
+      b -= c; a ^= x;
+      b -= a; x = (a<<8);
+      c -= a; b ^= x;
+      c -= b; x = (b>>13);
+      ...
+  Unfortunately, superscalar Pentiums and Sparcs can't take advantage 
+  of that parallelism.  They've also turned some of those single-cycle
+  latency instructions into multi-cycle latency instructions.  Still,
+  this is the fastest good hash I could find.  There were about 2^^68
+  to choose from.  I only looked at a billion or so.
+--------------------------------------------------------------------
+*/
+#define mix(a,b,c) \
+{ \
+  a -= b; a -= c; a ^= (c>>13); \
+  b -= c; b -= a; b ^= (a<<8); \
+  c -= a; c -= b; c ^= (b>>13); \
+  a -= b; a -= c; a ^= (c>>12);  \
+  b -= c; b -= a; b ^= (a<<16); \
+  c -= a; c -= b; c ^= (b>>5); \
+  a -= b; a -= c; a ^= (c>>3);  \
+  b -= c; b -= a; b ^= (a<<10); \
+  c -= a; c -= b; c ^= (b>>15); \
+}
+/*
+--------------------------------------------------------------------
+lookup() -- hash a variable-length key into a 32-bit value
+  k     : the key (the unaligned variable-length array of bytes)
+  len   : the length of the key, counting by bytes
+  level : can be any 4-byte value
+Returns a 32-bit value.  Every bit of the key affects every bit of
+the return value.  Every 1-bit and 2-bit delta achieves avalanche.
+About 6len+35 instructions.
+
+The best hash table sizes are powers of 2.  There is no need to do
+mod a prime (mod is sooo slow!).  If you need less than 32 bits,
+use a bitmask.  For example, if you need only 10 bits, do
+  h = (h & hashmask(10));
+In which case, the hash table should have hashsize(10) elements.
+
+If you are hashing n strings (ub1 **)k, do it like this:
+  for (i=0, h=0; i<n; ++i) h = lookup( k[i], len[i], h);
+
+By Bob Jenkins, 1996.  bob_jenkins@burtleburtle.net.  You may use this
+code any way you wish, private, educational, or commercial.
+
+See http://burtleburtle.net/bob/hash/evahash.html
+Use for hash table lookup, or anything where one collision in 2^32 is
+acceptable.  Do NOT use for cryptographic purposes.
+--------------------------------------------------------------------
+*/
+
+unsigned long int lookup( k, length, level)
+register unsigned char *k;           /* the key */
+register unsigned long int length;   /* the length of the key */
+register unsigned long int level;    /* the previous hash, or an arbitrary value */
+{
+   register unsigned long int a,b,c,len;
+
+   /* Set up the internal state */
+   len = length;
+   a = b = 0x9e3779b9;  /* the golden ratio; an arbitrary value */
+   c = level;           /* the previous hash value */
+
+   /*---------------------------------------- handle most of the key */
+   while (len >= 12)
+   {
+      a += (k[0] +((ub4)k[1]<<8) +((ub4)k[2]<<16) +((ub4)k[3]<<24));
+      b += (k[4] +((ub4)k[5]<<8) +((ub4)k[6]<<16) +((ub4)k[7]<<24));
+      c += (k[8] +((ub4)k[9]<<8) +((ub4)k[10]<<16)+((ub4)k[11]<<24));
+      mix(a,b,c);
+      k += 12; len -= 12;
+   }
+
+   /*------------------------------------- handle the last 11 bytes */
+   c += length;
+   switch(len)              /* all the case statements fall through */
+   {
+   case 11: c+=((ub4)k[10]<<24);
+   case 10: c+=((ub4)k[9]<<16);
+   case 9 : c+=((ub4)k[8]<<8);
+      /* the first byte of c is reserved for the length */
+   case 8 : b+=((ub4)k[7]<<24);
+   case 7 : b+=((ub4)k[6]<<16);
+   case 6 : b+=((ub4)k[5]<<8);
+   case 5 : b+=k[4];
+   case 4 : a+=((ub4)k[3]<<24);
+   case 3 : a+=((ub4)k[2]<<16);
+   case 2 : a+=((ub4)k[1]<<8);
+   case 1 : a+=k[0];
+     /* case 0: nothing left to add */
+   }
+   mix(a,b,c);
+   /*-------------------------------------------- report the result */
+   return c;
+}
+
+/*
+End of public domain code by From Bob Jenkins, December 1996.
+--------------------------------------------------------------------
+*/
+
+int ippool_printaddr(struct ippool_t *this) {
+  int n;
+  printf("ippool_printaddr\n");
+  printf("First %d\n", this->first - this->member);
+  printf("Last %d\n",  this->last - this->member);
+  printf("Listsize %d\n",  this->listsize);
+
+  for (n=0; n<this->listsize; n++) {
+    printf("Unit %d inuse %d prev %d next %d addr %x\n", 
+	   n,
+	   this->member[n].inuse,
+	   this->member[n].prev - this->member,
+	   this->member[n].next - this->member,
+	   this->member[n].addr.s_addr
+	   );
+  }
+  return 0;
+}
+
+
+unsigned long int ippool_hash4(struct in_addr *addr) {
+  return lookup(&addr->s_addr, sizeof(addr->s_addr), 0);
+}
+
+#ifndef IPPOOL_NOIP6
+unsigned long int ippool_hash6(struct in6_addr *addr) {
+  return lookup(addr->u6_addr8, sizeof(addr->u6_addr8), 0);
+}
+#endif
+
+
+/* Get IP address and mask */
+int ippool_aton(struct in_addr *addr, struct in_addr *mask,
+		char *pool, int number) {
+
+  /* Parse only first instance of network for now */
+  /* Eventually "number" will indicate the token which we want to parse */
+
+  unsigned int a1, a2, a3, a4;
+  unsigned int m1, m2, m3, m4;
+  int c;
+  unsigned int m;
+  int masklog;
+
+  c = sscanf(pool, "%u.%u.%u.%u/%u.%u.%u.%u",
+	     &a1, &a2, &a3, &a4,
+	     &m1, &m2, &m3, &m4);
+  switch (c) {
+  case 4:
+    if (a1 == 0 && a2 == 0 && a3 == 0 && a4 == 0) /* Full Internet */
+      mask->s_addr = 0x00000000;
+    else if (a2 == 0 && a3 == 0 && a4 == 0)       /* class A */
+      mask->s_addr = htonl(0xff000000);
+    else if (a3 == 0 && a4 == 0)	          /* class B */
+      mask->s_addr = htonl(0xffff0000);
+    else if (a4 == 0)	                          /* class C */
+      mask->s_addr = htonl(0xffffff00);
+    else
+      mask->s_addr = 0xffffffff;
+    break;
+  case 5:
+    if (m1 < 0 || m1 > 32) {
+      return -1; /* Invalid mask */
+    }
+    mask->s_addr = htonl(0xffffffff << (32 - m1));
+    break;
+  case 8:
+    if (m1 >= 256 ||  m2 >= 256 || m3 >= 256 || m4 >= 256)
+      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))
+      return -1; /* Wrong mask format (not all ones followed by all zeros)*/
+    mask->s_addr = htonl(m);
+    break;
+  default:
+    return -1; /* Invalid mask */
+  }
+
+  if (a1 >= 256 ||  a2 >= 256 || a3 >= 256 || a4 >= 256)
+    return -1; /* Wrong IP address format */
+  else
+    addr->s_addr = htonl(a1 * 0x1000000 + a2 * 0x10000 + a3 * 0x100 + a4);
+
+  return 0;
+}
+
+/* Create new address pool */
+int ippool_new(struct ippool_t **this, char *pool, int flags) {
+
+  /* Parse only first instance of network for now */
+
+  int i;
+  struct ippoolm_t *p;
+  struct ippoolm_t *p_prev = NULL; 
+  uint32_t hash;
+  struct in_addr addr;
+  struct in_addr mask;
+  unsigned int m;
+  unsigned int listsize;
+
+  if (ippool_aton(&addr, &mask, pool, 0))
+    return 0; /* Failed to parse pool */
+
+  m = ntohl(mask.s_addr);
+  listsize = ((~m)+1);
+  if (flags & IPPOOL_NONETWORK)   /* Exclude network address from pool */
+    listsize--;
+  if (flags & IPPOOL_NOBROADCAST) /* Exclude broadcast address from pool */
+    listsize--;
+
+  if (!(*this = calloc(sizeof(struct ippool_t), 1))) {
+    /* Failed to allocate memory for ippool */
+    return -1;
+  }
+  
+  (*this)->listsize += listsize;
+  if (!((*this)->member = calloc(sizeof(struct ippoolm_t), (*this)->listsize))){
+    /* Failed to allocate memory for members in ippool */
+    return -1;
+  }
+  
+  for ((*this)->hashlog = 0; 
+       ((1 << (*this)->hashlog) < listsize);
+       (*this)->hashlog++);
+
+  /*   printf ("Hashlog %d %d %d\n", (*this)->hashlog, listsize, (1 << (*this)->hashlog)); */
+
+  /* Determine hashsize */
+  (*this)->hashsize = 1 << (*this)->hashlog; /* Fails if mask=0: All Internet*/
+  (*this)->hashmask = (*this)->hashsize -1;
+  
+  /* Allocate hash table */
+  if (!((*this)->hash = calloc(sizeof(struct ippoolm_t), (*this)->hashsize))){
+    /* Failed to allocate memory for hash members in ippool */
+    return -1;
+  }
+  
+  (*this)->first = NULL;
+  (*this)->last = NULL;
+  for (i = 0; i<(*this)->listsize; i++) {
+
+    if (flags & IPPOOL_NONETWORK)
+      (*this)->member[i].addr.s_addr = htonl(ntohl(addr.s_addr) + i + 1);
+    else
+      (*this)->member[i].addr.s_addr = htonl(ntohl(addr.s_addr) + i);
+
+    (*this)->member[i].inuse = 0;
+    (*this)->member[i].parent = *this;
+
+    /* Insert into list of unused */
+    (*this)->member[i].prev = (*this)->last;
+    if ((*this)->last) {
+      (*this)->last->next = &((*this)->member[i]);
+    }
+    else {
+      (*this)->first = &((*this)->member[i]);
+    }
+    (*this)->last = &((*this)->member[i]);
+    (*this)->member[i].next = NULL; /* Redundant */
+
+    /* Insert into hash table */
+    hash = ippool_hash4(&(*this)->member[i].addr) & (*this)->hashmask;
+    for (p = (*this)->hash[hash]; p; p = p->nexthash)
+      p_prev = p;
+    if (!p_prev)
+      (*this)->hash[hash] = &((*this)->member[i]);
+    else 
+      p_prev->nexthash = &((*this)->member[i]);
+  }
+  /*ippool_printaddr(*this);*/
+  return 0;
+}
+
+/* Delete existing address pool */
+int ippool_free(struct ippool_t *this) {
+  free(this->hash);
+  free(this->member);
+  free(this);
+  return 0; /* Always OK */
+}
+
+/* Find an IP address in the pool */
+int ippool_getip(struct ippool_t *this, struct ippoolm_t **member,
+		 struct in_addr *addr) {
+  struct ippoolm_t *p;
+  uint32_t hash;
+
+  /* Find in hash table */
+  hash = ippool_hash4(addr) & this->hashmask;
+  for (p = this->hash[hash]; p; p = p->nexthash) {
+    if ((p->addr.s_addr == addr->s_addr) && (p->inuse)) {
+      *member = p;
+      return 0;
+    }
+  }
+  *member = NULL;
+  return -1; /* Address could not be found */
+}
+
+
+/* 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 */
+int ippool_newip(struct ippool_t *this, struct ippoolm_t **member,
+		 struct in_addr *addr) {
+  struct ippoolm_t *p;
+  struct ippoolm_t *p2 = NULL;
+  uint32_t hash;
+
+  /*ippool_printaddr(this);*/
+
+  if ((addr) && (addr->s_addr)) { /* IP address given */
+    /* Find in hash table */
+    hash = ippool_hash4(addr) & this->hashmask;
+    for (p = this->hash[hash]; p; p = p->nexthash) {
+      if ((p->addr.s_addr == addr->s_addr)) {
+	p2 = p;
+	break;
+      }
+    }
+  }
+  else { /* No ip address given */
+    p2 = this -> first;
+  }
+
+  if (!p2) return -1; /* Not found */
+  if (p2->inuse) return -1; /* Allready in use / Should not happen */
+  
+  /* Found new address. Remove from queue */
+  if (p2->prev) 
+    p2->prev->next = p2->next;
+  else
+    this->first = p2->next;
+  if (p2->next) 
+    p2->next->prev = p2->prev;
+  else
+    this->last = p2->prev;
+  p2->next = NULL;
+  p2->prev = NULL;
+  p2->inuse = 1;
+  
+  *member = p2;
+  /*ippool_printaddr(this);*/
+  return 0; /* Success */
+}
+
+
+int ippool_freeip(struct ippoolm_t *member) {
+  struct ippool_t *this = member->parent;
+  
+  /*ippool_printaddr(this);*/
+
+  if (!member->inuse) return -1; /* Not in use: Should not happen */
+
+  /* Insert into list of unused */
+  member->prev = this->last;
+  if (this->last) {
+    this->last->next = member;
+  }
+  else {
+    this->first = member;
+  }
+  this->last = member;
+
+  member->inuse = 0;
+  /*ippool_printaddr(this);*/
+  
+  return 0; /* Success */
+}
+
+
+#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
new file mode 100644
index 0000000..2d3f575
--- /dev/null
+++ b/ggsn/ippool.h
@@ -0,0 +1,105 @@
+/* 
+ * IP address pool functions.
+ * Copyright (C) 2003 Mondru AB.
+ * 
+ * The contents of this file may be used under the terms of the GNU
+ * General Public License Version 2, provided that the above copyright
+ * notice and this permission notice is included in all copies or
+ * substantial portions of the software.
+ * 
+ * The initial developer of the original code is
+ * Jens Jakobsen <jj@openggsn.org>
+ * 
+ * Contributor(s):
+ * 
+ */
+
+#ifndef _IPPOOL_H
+#define _IPPOOL_H
+
+/* Assuming that the address space is fragmented we need a hash table
+   in order to return the addresses.
+
+   The list pool should provide for both IPv4 and IPv6 addresses.
+
+   When initialising a new address pool it should be possible to pass
+   a string of CIDR format networks: "10.0.0.0/24 10.15.0.0/20" would
+   translate to 256 addresses starting at 10.0.0.0 and 1024 addresses
+   starting at 10.15.0.0. 
+
+   The above also applies to IPv6 which can be specified as described
+   in RFC2373.
+*/
+
+typedef  unsigned long  int  ub4;   /* unsigned 4-byte quantities */
+typedef  unsigned       char ub1;
+
+#define IPPOOL_NOIP6
+
+#define IPPOOL_NONETWORK   0x01
+#define IPPOOL_NOBROADCAST 0x02
+
+struct ippoolm_t;                /* Forward declaration */
+
+struct ippool_t {
+  int listsize;                  /* Total number of addresses */
+  struct ippoolm_t *member;      /* Listsize array of members */
+  int hashsize;                  /* Size of hash table */
+  int hashlog;                   /* Log2 size of hash table */
+  int hashmask;                  /* Bitmask for calculating hash */
+  struct ippoolm_t **hash;       /* Hashsize array of pointer to member */
+  struct ippoolm_t *first;       /* Pointer to first available member */
+  struct ippoolm_t *last;        /* Pointer to last available member */
+};
+
+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
+  int inuse;                     /* 0=available; 1= inuse */
+  struct ippoolm_t *nexthash;    /* Linked list part of hash table */
+  struct ippoolm_t *prev, *next; /* Double linked list of available members */
+  struct ippool_t *parent;       /* Pointer to parent */
+  void *peer;                    /* Pointer to peer protocol handler */
+};
+
+
+/* The above structures requires approximately 20+4 = 24 bytes for
+   each address (IPv4). For IPv6 the corresponding value is 32+4 = 36
+   bytes for each address. */
+
+/* Hash an IP address using code based on Bob Jenkins lookupa */
+extern unsigned long int ippool_hash4(struct in_addr *addr);
+
+/* Create new address pool */
+extern int ippool_new(struct ippool_t **this, char *pool, int flags);
+
+/* Delete existing address pool */
+extern int ippool_free(struct ippool_t *this);
+
+/* Find an IP address in the pool */
+extern int ippool_getip(struct ippool_t *this, struct ippoolm_t **member,
+		 struct in_addr *addr);
+
+/* Get an IP address. If addr = 0.0.0.0 get a dynamic IP address. Otherwise
+   check to see if the given address is available */
+extern int ippool_newip(struct ippool_t *this, struct ippoolm_t **member,
+			struct in_addr *addr);
+
+/* Return a previously allocated IP address */
+extern int ippool_freeip(struct ippoolm_t *member);
+
+/* Get net and mask based on ascii string */
+extern int ippool_aton(struct in_addr *addr, struct in_addr *mask,
+		       char *pool, int number);
+
+
+#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
+
+#endif	/* !_IPPOOL_H */
diff --git a/ggsn/tun.c b/ggsn/tun.c
index 72ea264..5b8cc95 100644
--- a/ggsn/tun.c
+++ b/ggsn/tun.c
@@ -1,32 +1,24 @@
 /* 
- *  OpenGGSN - Gateway GPRS Support Node
- *  Copyright (C) 2002 Mondru AB.
+ * TUN interface functions.
+ * Copyright (C) 2002, 2003 Mondru AB.
  * 
- *  The contents of this file may be used under the terms of the GNU
- *  General Public License Version 2, provided that the above copyright
- *  notice and this permission notice is included in all copies or
- *  substantial portions of the software.
+ * The contents of this file may be used under the terms of the GNU
+ * General Public License Version 2, provided that the above copyright
+ * notice and this permission notice is included in all copies or
+ * substantial portions of the software.
  * 
- *  The initial developer of the original code is
- *  Jens Jakobsen <jj@openggsn.org>
+ * The initial developer of the original code is
+ * Jens Jakobsen <jj@openggsn.org>
  * 
- *  Contributor(s):
+ * Contributor(s):
  * 
  */
 
 /*
- * tun.c: Contains all TUN functionality. Should be able to handle multiple
- * tunnels in the same program. Each tunnel is identified by the socket. 
- * I suppose that no other state information than the socket is needed.
+ * tun.c: Contains all TUN functionality. Is able to handle multiple
+ * tunnels in the same program. Each tunnel is identified by the struct,
+ * which is passed to functions.
  *
- *  - tun_newtun: Initialise TUN tunnel.
- *  - tun_freetun: Free a device previously created with tun_newtun.
- *  - tun_encaps: Encapsulate packet in TUN tunnel and send off
- *  - tun_decaps: Extract packet from TUN tunnel and call function to
- *    ship it off as GTP encapsulated packet. 
- *
- * TODO:
- *  - Do we need to handle fragmentation?
  */
 
 
@@ -54,75 +46,471 @@
 #include <linux/if.h>
 #include <errno.h>
 #include <linux/if_tun.h>
+#include <net/route.h>
 
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
 
 #include "tun.h"
+#include "syserr.h"
 
 
-int tun_newtun(struct tun_t **tun)
+
+int tun_nlattr(struct nlmsghdr *n, int nsize, int type, void *d, int dlen)
 {
+  int len = RTA_LENGTH(dlen);
+  int alen = NLMSG_ALIGN(n->nlmsg_len);
+  struct rtattr *rta = (struct rtattr*) (((void*)n) + alen);
+  if (alen + len > nsize)
+    return -1;
+  rta->rta_len = len;
+  rta->rta_type = type;
+  memcpy(RTA_DATA(rta), d, dlen);
+  n->nlmsg_len = alen + len;
+  return 0;
+}
+
+int tun_gifindex(struct tun_t *this, int *index) {
   struct ifreq ifr;
+  int fd;
 
-  if (!(*tun = calloc(1, sizeof(struct tun_t)))) {
-    syslog(LOG_ERR, "%s %d. calloc(nmemb=%d, size=%d) failed: Error = %s(%d)",
-	   __FILE__, __LINE__, 1, sizeof(struct tun_t), 
-	   strerror(errno), errno);
-    return EOF;
+  memset (&ifr, '\0', sizeof (ifr));
+  ifr.ifr_addr.sa_family = AF_INET;
+  ifr.ifr_dstaddr.sa_family = AF_INET;
+  ifr.ifr_netmask.sa_family = AF_INET;
+  strncpy(ifr.ifr_name, this->devname, IFNAMSIZ);
+  ifr.ifr_name[IFNAMSIZ-1] = 0; /* Make sure to terminate */
+  if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+    sys_err(LOG_ERR, __FILE__, __LINE__, errno,
+	    "socket() failed");
   }
+  if (ioctl(fd, SIOCGIFINDEX, &ifr)) {
+    sys_err(LOG_ERR, __FILE__, __LINE__, errno,
+	    "ioctl() failed");
+    close(fd);
+    return -1;
+  }
+  close(fd);
+  *index = ifr.ifr_ifindex;
+  return 0;
+}
 
-  if (((*tun)->fd  = open("/dev/net/tun", O_RDWR)) < 0) {
-    syslog(LOG_ERR, "TUN: open() failed");
+int tun_sifflags(struct tun_t *this, int flags) {
+  struct ifreq ifr;
+  int fd;
+
+  memset (&ifr, '\0', sizeof (ifr));
+  ifr.ifr_flags = flags;
+  strncpy(ifr.ifr_name, this->devname, IFNAMSIZ);
+  ifr.ifr_name[IFNAMSIZ-1] = 0; /* Make sure to terminate */
+  if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+    sys_err(LOG_ERR, __FILE__, __LINE__, errno,
+	    "socket() failed");
+  }
+  if (ioctl(fd, SIOCSIFFLAGS, &ifr)) {
+    sys_err(LOG_ERR, __FILE__, __LINE__, errno,
+	    "ioctl(SIOCSIFFLAGS) failed");
+    close(fd);
+    return -1;
+  }
+  close(fd);
+  return 0;
+}
+
+
+/* Currently unused */
+int tun_addroute2(struct tun_t *this,
+		  struct in_addr *dst,
+		  struct in_addr *gateway,
+		  struct in_addr *mask) {
+  
+  struct {
+    struct nlmsghdr 	n;
+    struct rtmsg 	r;
+    char buf[TUN_NLBUFSIZE];
+  } req;
+  
+  struct sockaddr_nl local;
+  int addr_len;
+  int fd;
+  int status;
+  struct sockaddr_nl nladdr;
+  struct iovec iov;
+  struct msghdr msg;
+
+  memset(&req, 0, sizeof(req));
+  req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
+  req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
+  req.n.nlmsg_type = RTM_NEWROUTE;
+  req.r.rtm_family = AF_INET;
+  req.r.rtm_table  = RT_TABLE_MAIN;
+  req.r.rtm_protocol = RTPROT_BOOT;
+  req.r.rtm_scope  = RT_SCOPE_UNIVERSE;
+  req.r.rtm_type  = RTN_UNICAST;
+  tun_nlattr(&req.n, sizeof(req), RTA_DST, dst, 4);
+  tun_nlattr(&req.n, sizeof(req), RTA_GATEWAY, gateway, 4);
+  
+  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 */
+  close(fd);
+  return 0;
+}
+
+
+int tun_addaddr(struct tun_t *this,
+		struct in_addr *addr,
+		struct in_addr *dstaddr,
+		struct in_addr *netmask) {
+  struct {
+    struct nlmsghdr 	n;
+    struct ifaddrmsg 	i;
+    char buf[TUN_NLBUFSIZE];
+  } req;
+  
+  struct sockaddr_nl local;
+  int 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_setaddr(this, addr, dstaddr, netmask);
+
+  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_INET;
+  req.i.ifa_prefixlen = 32; /* 32 FOR IPv4 */
+  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);
+  close(fd);
+  this->addrs++;
+  return 0;
+}
+
+
+int tun_setaddr(struct tun_t *this,
+		struct in_addr *addr,
+		struct in_addr *dstaddr,
+		struct in_addr *netmask)
+{
+  struct ifreq   ifr;
+  int fd;
+
+  memset (&ifr, '\0', sizeof (ifr));
+  ifr.ifr_addr.sa_family = AF_INET;
+  ifr.ifr_dstaddr.sa_family = AF_INET;
+  ifr.ifr_netmask.sa_family = AF_INET;
+  strncpy(ifr.ifr_name, this->devname, IFNAMSIZ);
+  ifr.ifr_name[IFNAMSIZ-1] = 0; /* Make sure to terminate */
+
+  /* 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 (addr) { /* Set the interface address */
+    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) {
+	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;
+    }
+  }
+
+  if (dstaddr) { /* Set the destination address */
+    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) {
+      sys_err(LOG_ERR, __FILE__, __LINE__, errno,
+	      "ioctl(SIOCSIFDSTADDR) failed");
+      close(fd);
+      return -1; 
+    }
+  }
+
+  if (netmask) { /* Set the netmask */
+    this->netmask.s_addr = netmask->s_addr;
+    ((struct sockaddr_in *) &ifr.ifr_netmask)->sin_addr.s_addr = 
+      netmask->s_addr;
+    if (ioctl(fd, SIOCSIFNETMASK, (void *) &ifr) < 0) {
+      sys_err(LOG_ERR, __FILE__, __LINE__, errno,
+	      "ioctl(SIOCSIFNETMASK) failed");
+      close(fd);
+      return -1;
+    }
+  }
+
+  close(fd);
+  this->addrs++;
+  return tun_sifflags(this, IFF_UP | IFF_RUNNING);
+}
+
+int tun_addroute(struct tun_t *this,
+		 struct in_addr *dst,
+		 struct in_addr *gateway,
+		 struct in_addr *mask)
+{
+  struct rtentry r;
+  int fd;
+
+  memset (&r, '\0', sizeof (r));
+  r.rt_flags = RTF_UP | RTF_GATEWAY; /* RTF_HOST not set */
+
+  /* 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;
+  }
+
+  r.rt_dst.sa_family     = AF_INET;
+  r.rt_gateway.sa_family = AF_INET;
+  r.rt_genmask.sa_family = AF_INET;
+  ((struct sockaddr_in *) &r.rt_dst)->sin_addr.s_addr = dst->s_addr;
+  ((struct sockaddr_in *) &r.rt_gateway)->sin_addr.s_addr = gateway->s_addr;
+  ((struct sockaddr_in *) &r.rt_genmask)->sin_addr.s_addr = mask->s_addr;
+
+  if (ioctl(fd, SIOCADDRT, (void *) &r) < 0) {   /* SIOCDELRT */
+    sys_err(LOG_ERR, __FILE__, __LINE__, errno,
+	    "ioctl(SIOCADDRT) failed");
+    close(fd);
+    return -1;
+  }
+  close(fd);
+  return 0;
+}
+
+
+int tun_new(struct tun_t **tun)
+{
+  struct ifreq ifr;
+  
+  if (!(*tun = calloc(1, sizeof(struct tun_t)))) {
+    sys_err(LOG_ERR, __FILE__, __LINE__, errno, "calloc() failed");
+    return EOF;
+  }
+  
+  (*tun)->cb_ind = NULL;
+  (*tun)->addrs = 0;
+  
+  if (((*tun)->fd  = open("/dev/net/tun", O_RDWR)) < 0) {
+    sys_err(LOG_ERR, __FILE__, __LINE__, errno, "open() failed");
+    return -1;
+  }
+  
   memset(&ifr, 0, sizeof(ifr));
   ifr.ifr_flags = IFF_TUN | IFF_NO_PI; /* Tun device, no packet info */
   strncpy(ifr.ifr_name, (*tun)->devname, IFNAMSIZ);
-
+  
   if (ioctl((*tun)->fd, TUNSETIFF, (void *) &ifr) < 0) {
-    syslog(LOG_ERR, "TUN: ioctl() failed");
+    sys_err(LOG_ERR, __FILE__, __LINE__, errno, "ioctl() failed");
     close((*tun)->fd);
     return -1;
   } 
-
+  
   ioctl((*tun)->fd, TUNSETNOCSUM, 1); /* Disable checksums */
-
+  
   strncpy((*tun)->devname, ifr.ifr_name, IFNAMSIZ);
-
-  return (*tun)->fd;
+  (*tun)->devname[IFNAMSIZ] = 0;
+  
+  return 0;
 }
 
-int tun_freetun(struct tun_t *tun)
+int tun_free(struct tun_t *tun)
 {
   if (close(tun->fd)) {
-    syslog(LOG_ERR, "%s %d. close(fd=%d) failed: Error = %s", 
-	   __FILE__, __LINE__, tun->fd, strerror(errno));
-    return EOF;
+    sys_err(LOG_ERR, __FILE__, __LINE__, errno, "close() failed");
   }
+
   free(tun);
   return 0;
 }
 
 
-int tun_decaps(struct tun_t *tun, 
-	       int (*cb) (void *cl, struct tun_t*, void *pack, unsigned len),
-	       void *cl)
+int tun_set_cb_ind(struct tun_t *this, 
+  int (*cb_ind) (struct tun_t *tun, void *pack, unsigned len)) {
+  this->cb_ind = cb_ind;
+  return 0;
+}
+
+
+int tun_decaps(struct tun_t *this)
 {
-	unsigned char buffer[PACKET_MAX + 64 /*TODO: ip header */ ];
-	int status;
+  unsigned char buffer[PACKET_MAX];
+  int status;
+  
+  if ((status = read(this->fd, buffer, sizeof(buffer))) <= 0) {
+    sys_err(LOG_ERR, __FILE__, __LINE__, errno, "read() failed");
+    return -1;
+  }
+  
+  if (this->cb_ind)
+    return this->cb_ind(this, buffer, status);
 
-
-	if ((status = read(tun->fd, buffer, sizeof(buffer))) <= 0) {
-		syslog(LOG_ERR, "TUN: read(fd=%d,buffer=%lx,len=%d) from network failed: status = %d error = %s",
-		       tun->fd, (unsigned long) buffer, sizeof(buffer), status, status ? strerror(errno) : "No error");
-		return -1;
-	}
-
-	/* Need to include code to verify packet src and dest addresses */
-	return cb(cl, tun, buffer, status);
+  return 0;
 }
 
 int tun_encaps(struct tun_t *tun, void *pack, unsigned len)
 {
-	return write(tun->fd, pack, len);
+  return write(tun->fd, pack, len);
+}
+
+int tun_runscript(struct tun_t *tun, char* script) {
+  
+  char buf[TUN_SCRIPTSIZE];
+  char snet[TUN_ADDRSIZE];
+  char smask[TUN_ADDRSIZE];
+
+  strncpy(snet, inet_ntoa(tun->addr), sizeof(snet));
+  snet[sizeof(snet)-1] = 0;
+  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"); */
+  snprintf(buf, sizeof(buf), "%s %s %s %s",
+	   script, tun->devname, snet, smask);
+  buf[sizeof(buf)-1] = 0;
+  system(buf);
+  return 0;
 }
diff --git a/ggsn/tun.h b/ggsn/tun.h
index 03dc7df..8d4618d 100644
--- a/ggsn/tun.h
+++ b/ggsn/tun.h
@@ -1,48 +1,78 @@
 /* 
- *  OpenGGSN - Gateway GPRS Support Node
- *  Copyright (C) 2002 Mondru AB.
+ * TUN interface functions.
+ * Copyright (C) 2002, 2003 Mondru AB.
  * 
- *  The contents of this file may be used under the terms of the GNU
- *  General Public License Version 2, provided that the above copyright
- *  notice and this permission notice is included in all copies or
- *  substantial portions of the software.
+ * The contents of this file may be used under the terms of the GNU
+ * General Public License Version 2, provided that the above copyright
+ * notice and this permission notice is included in all copies or
+ * substantial portions of the software.
  * 
- *  The initial developer of the original code is
- *  Jens Jakobsen <jj@openggsn.org>
+ * The initial developer of the original code is
+ * Jens Jakobsen <jj@openggsn.org>
  * 
- *  Contributor(s):
+ * Contributor(s):
  * 
  */
 
 #ifndef _TUN_H
 #define _TUN_H
 
-#define hton8(x)  (x)
-#define ntoh8(x)  (x)
-#define hton16(x) htons(x)
-#define ntoh16(x) ntohs(x)
-#define hton32(x) htonl(x)
-#define ntoh32(x) ntohl(x)
+#define PACKET_MAX      8196 /* Maximum packet size we receive */
+#define TUN_SCRIPTSIZE   256
+#define TUN_ADDRSIZE     128
+#define TUN_NLBUFSIZE   1024
 
-#define PACKET_MAX      8196 /* TODO */
+struct tun_packet_t {
+  unsigned int ver:4;
+  unsigned int ihl:4;
+  unsigned int dscp:6;
+  unsigned int ecn:2;
+  unsigned int length:16;
+  unsigned int id:16;
+  unsigned int flags:3;
+  unsigned int fragment:13;
+  unsigned int ttl:8;
+  unsigned int protocol:8;
+  unsigned int check:16;
+  unsigned int src:32;
+  unsigned int dst:32;
+};
+
 
 /* ***********************************************************
  * Information storage for each tun instance
  *************************************************************/
 
 struct tun_t {
-  int fd;                /* File descriptor to network interface */
-  struct in_addr addr;   /* IP address of tun interface */
+  int fd;                /* File descriptor to tun interface */
+  struct in_addr addr;
+  struct in_addr dstaddr;
+  struct in_addr netmask;
+  int addrs;             /* Number of allocated IP addresses */
   char devname[IFNAMSIZ];/* Name of the tun device */
+  int (*cb_ind) (struct tun_t *tun, void *pack, unsigned len);
 };
 
 
-extern int tun_newtun(struct tun_t **tun);
-extern int tun_freetun(struct tun_t *tun);
-extern int tun_decaps(struct tun_t *tun, 
-     int (*cb) (void *cl, struct tun_t*, void *pack, unsigned len),
-		      void *cl);
+extern int tun_new(struct tun_t **tun);
+extern int tun_free(struct tun_t *tun);
+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_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);
 
 #endif	/* !_TUN_H */