added ippool.h and ippool.c
diff --git a/examples/ggsn.conf b/examples/ggsn.conf
index c2d3678..5550525 100644
--- a/examples/ggsn.conf
+++ b/examples/ggsn.conf
@@ -36,13 +36,8 @@
 
 # TAG: net
 # IP network address of external packet data network
-# Used to allocate dynamic IP addresses and set up routing.
-#net 192.168.0.0
-
-# TAG: mask
-# IP network mask of external packet data network
-# Used to allocate dynamic IP addresses and set up routing.
-#mask 255.255.255.0
+# Used to set up network interface.
+#net 192.168.0.0/24
 
 # TAG: ipup
 # Script executed after network interface has been brought up.
@@ -54,12 +49,36 @@
 # Executed with the following parameters: <devicename> <ip address>
 #ipdown /etc/ggsn/ip-down
 
+# TAG: dynip
+# Dynamic IP address pool.
+# Used for allocation of dynamic IP address when address is not given
+# by HLR or radius server.
+#dynip 192.168.0.0/24
+
+# TAG: statip
+# Use of this tag is currently UNSUPPORTED
+# Static IP address pool.
+# Used for allocation of static IP address by means of either HLR or
+# radius server.
+#statip 192.168.1.0/24
+
+# TAG: pcodns1
+# Protocol configuration option domain name system server 1.
+#pcodns1 0.0.0.0
+
+# TAG: pcodns2
+# Protocol configuration option domain name system server 2.
+#pcodns2 0.0.0.0
 
 # TAG: timelimit
 # Exit after timelimit seconds.
 # Setting timelimit to zero will cause the program not to exit.
 #timelimit 0
 
+# TAG: apn
+# Use of this tag is EXPERIMENTAL
+# Access point name to connect to when run in client mode.
+#apn internet
 
 # TAG: qos
 # Use of this tag is EXPERIMENTAL
@@ -67,12 +86,6 @@
 # 3 bytes corresponding to ????
 #qos 0x0b921f
 
-# TAG: apn
-# Use of this tag is EXPERIMENTAL
-# Access point name to connect to when run in client mode.
-#apn internet
-
-
 
 
 
diff --git a/examples/sgsnemu.conf b/examples/sgsnemu.conf
index 343f106..f2c97d7 100644
--- a/examples/sgsnemu.conf
+++ b/examples/sgsnemu.conf
@@ -27,13 +27,13 @@
 # TAG: statedir
 # Directory to use for nonvolatile storage. 
 # The program must have write access to this directory.
-#pidfile ./sgsnemu.pid
+#statedir ./
 
 
 # TAG: dns
 # DNS server to use for ns lookups.
 # If this tag is not set the system default DNS will be used.
-#pidfile ./sgsnemu.pid
+#dns 10.1.2.3
 
 # TAG: listen
 # Specifies the local IP address to listen to
@@ -46,17 +46,10 @@
 remote 10.0.0.240
 
 # TAG: contexts
-# Use of this tag is EXPERIMENTAL
 # Number of contexts to establish from the emulator to the ggsn.
 # Set this tag to zero to not establish any contexts.
 #contexts 1
 
-# TAG: static
-# Use of this tag is EXPERIMENTAL
-# Use this flag if you do not want to set dynamic tun interfaces.
-# If this flag is set a single network interface is established.
-#static
-
 # TAG: timelimit
 # Disconnect contexts after timelimit seconds, and exit the program.
 # Setting timelimit to zero will cause the program not to disconnect.
@@ -88,11 +81,17 @@
 # Password used when run in client mode.
 #pwd hemlig
 
+
 # TAG: createif
 # Use this flag if you want to set up a local network interface after
 # a PDP context has been established.
 #createif
 
+# TAG: defaultroute
+# Use this flag if you want to add a default route after a network interface
+# had been established.
+#defaultroute
+
 # TAG: ipup
 # Script executed after network interface has been brought up.
 # Executed with the following parameters: <devicename> <ip address>
@@ -103,16 +102,6 @@
 # Executed with the following parameters: <devicename> <ip address>
 #ipdown /etc/sgsnemu/ip-down
 
-# TAG: net
-# IP network address of external packet data network.
-# Only used if the defaultroute flag is set.
-#net 0.0.0.0
-
-# TAG: mask
-# IP network mask of external packet data network.
-# Only used if the defaultroute flag is set.
-#mask 0.0.0.0
-
 # TAG: pinghost
 # Ping a remote host through a PDP context by using ICMP echo messages.
 # If more than one PDP context has been established the ICMP messages will
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 */
diff --git a/gtp/Makefile.am b/gtp/Makefile.am
index 88131cc..871a446 100644
--- a/gtp/Makefile.am
+++ b/gtp/Makefile.am
@@ -1,6 +1,6 @@
 lib_LTLIBRARIES = libgtp.la
 
-CFLAGS = -O2 -fno-builtin -Wall -ansi -DSBINDIR='"$(sbindir)"'
+CFLAGS = -O2 -fno-builtin -Wall -ansi -DSBINDIR='"$(sbindir)"' -ggdb
 
 libgtp_la_SOURCES = gtp.c gtp.h gtpie.c gtpie.h pdp.c pdp.h lookupa.c lookupa.h queue.c queue.h
 
diff --git a/gtp/Makefile.in b/gtp/Makefile.in
index 9093710..661c7dc 100644
--- a/gtp/Makefile.in
+++ b/gtp/Makefile.in
@@ -87,7 +87,7 @@
 install_sh = @install_sh@
 lib_LTLIBRARIES = libgtp.la
 
-CFLAGS = -O2 -fno-builtin -Wall -ansi -DSBINDIR='"$(sbindir)"'
+CFLAGS = -O2 -fno-builtin -Wall -ansi -DSBINDIR='"$(sbindir)"' -ggdb
 
 libgtp_la_SOURCES = gtp.c gtp.h gtpie.c gtpie.h pdp.c pdp.h lookupa.c lookupa.h queue.c queue.h
 subdir = gtp
diff --git a/gtp/gtp.c b/gtp/gtp.c
index a6aebb9..fb98be1 100644
--- a/gtp/gtp.c
+++ b/gtp/gtp.c
@@ -54,10 +54,6 @@
 #include "gtpie.h"
 #include "queue.h"
 
-
-struct gtp0_header gtp0_default;
-struct gtp1_header_long gtp1_default;
-
 /* API Functions */
 
 const char* gtp_version()
@@ -162,18 +158,29 @@
 }
 
 
-
 void get_default_gtp(int version, void *packet) {
+  struct gtp0_header *gtp0_default = (struct gtp0_header*) packet;
+  struct gtp1_header_long *gtp1_default = (struct gtp1_header_long*) packet;
   switch (version) {
   case 0:
-    memcpy(packet, &gtp0_default, sizeof(gtp0_default));
+    /* Initialise "standard" GTP0 header */
+    memset(gtp0_default, 0, sizeof(gtp0_default));
+    gtp0_default->flags=0x1e;
+    gtp0_default->spare1=0xff;
+    gtp0_default->spare2=0xff;
+    gtp0_default->spare3=0xff;
+    gtp0_default->number=0xff;
   break;
   case 1:
-    memcpy(packet, &gtp1_default, sizeof(gtp1_default));
+    /* Initialise "standard" GTP1 header */
+    memset(gtp1_default, 0, sizeof(gtp1_default));
+    gtp0_default->flags=0x1e;
     break;
   }
 }
 
+
+
 int print_packet(void *packet, unsigned len)
 {
   int i;
@@ -448,6 +455,11 @@
 	 ntohs(peer->sin_port));
   print_packet(packet, len); 
   */
+
+  if (fcntl(gsn->fd, F_SETFL, 0)) {
+    gtp_err(LOG_ERR, __FILE__, __LINE__, "fnctl()");
+    return -1;
+  }
   
   if (sendto(gsn->fd, packet, len, 0,
 	     (struct sockaddr *) peer, sizeof(struct sockaddr_in)) < 0) {
@@ -487,6 +499,12 @@
 	   ntohs(peer->sin_port));
     print_packet(&qmsg->p, qmsg->l);
     */
+
+    if (fcntl(gsn->fd, F_SETFL, 0)) {
+      gtp_err(LOG_ERR, __FILE__, __LINE__, "fnctl()");
+      return -1;
+    }
+
     if (sendto(gsn->fd, &qmsg->p, qmsg->l, 0,
 	       (struct sockaddr *) peer, sizeof(struct sockaddr_in)) < 0) {
       gsn->err_sendto++;
@@ -553,6 +571,9 @@
 
   (*gsn)->statedir = statedir;
   log_restart(*gsn);
+
+  /* Initialise sequence number */
+  (*gsn)->seq_next = (*gsn)->restart_counter * 1024;
   
   /* Initialise request retransmit queue */
   queue_new(&(*gsn)->queue_req);
@@ -573,8 +594,6 @@
     return -1;
   }
   (*gsn)->fd = gtp_fd;
-  
-  /* syslog(LOG_ERR, "GTP: gtp_init() after socket");*/
 
   (*gsn)->gsnc = *listen;
   (*gsn)->gsnu = *listen;
@@ -592,18 +611,6 @@
     return -1;
   }
 
-  /* Initialise "standard" GTP0 header */
-  memset(&gtp0_default, 0, sizeof(gtp0_default));
-  gtp0_default.flags=0x1e;
-  gtp0_default.spare1=0xff;
-  gtp0_default.spare2=0xff;
-  gtp0_default.spare3=0xff;
-  gtp0_default.number=0xff;
-  
-  /* Initialise "standard" GTP1 header */
-  memset(&gtp1_default, 0, sizeof(gtp1_default));
-  gtp0_default.flags=0x1e;
-  
   return 0;
 }
 
@@ -1736,90 +1743,111 @@
   struct gtp0_header *pheader;
   int version = 0; /* GTP version should be determined from header!*/
 
-  peerlen = sizeof(peer);
-  if ((status = 
-       recvfrom(gsn->fd, buffer, sizeof(buffer), 0,
-		(struct sockaddr *) &peer, &peerlen)) < 0 ) {
-    gsn->err_readfrom++;
-    gtp_err(LOG_ERR, __FILE__, __LINE__, "recvfrom(fd=%d, buffer=%lx, len=%d) failed: status = %d error = %s", gsn->fd, (unsigned long) buffer, sizeof(buffer), status, status ? strerror(errno) : "No error");
-    return -1;
-  }
-  
-  /* Strip off IP header, if present: TODO Is this nessesary? */
-  if ((buffer[0] & 0xF0) == 0x40) {
-    ip_len = (buffer[0] & 0xF) * 4;
-    gtp_errpack(LOG_ERR, __FILE__, __LINE__, &peer, buffer, status,
-		"IP header found in return from read");
-    return -1;
-  }
-  
-  /* Need at least 1 byte in order to check version */
-  if (status < (1)) {
-    gsn->empty++;
-    gtp_errpack(LOG_ERR, __FILE__, __LINE__, &peer, buffer, status,
-		"Discarding packet - too small");
-    return -1;
-  }
-  
-  /* TODO: Remove these ERROR MESSAGES 
-  gtp_err(LOG_ERR, __FILE__, __LINE__, "Discarding packet - too small");
-  gtp_errpack(LOG_ERR, __FILE__, __LINE__, &peer, buffer, status,
-	      "Discarding packet - too small"); */
+  /* TODO: Need strategy of userspace buffering and blocking */
+  /* Currently read is non-blocking and send is blocking. */
+  /* This means that the program have to wait for busy send calls...*/
 
-  pheader = (struct gtp0_header *) (buffer + ip_len);
-
-  /* Version should be gtp0 (or earlier in theory) */
-  if (((pheader->flags & 0xe0) > 0x00)) {
-    gsn->unsup++;
-    gtp_errpack(LOG_ERR, __FILE__, __LINE__, &peer, buffer, status,
-		"Unsupported GTP version");
-    return gtp_unsup_resp(gsn, &peer, buffer, status); /* 29.60: 11.1.1 */
-  }
+  while (1) { /* Loop until no more to read */
+    if (fcntl(gsn->fd, F_SETFL, O_NONBLOCK)) {
+      gtp_err(LOG_ERR, __FILE__, __LINE__, "fnctl()");
+      return -1;
+    }
+    peerlen = sizeof(peer);
+    if ((status = 
+	 recvfrom(gsn->fd, buffer, sizeof(buffer), 0,
+		  (struct sockaddr *) &peer, &peerlen)) < 0 ) {
+      if (errno == EAGAIN) return -1;
+      gsn->err_readfrom++;
+      gtp_err(LOG_ERR, __FILE__, __LINE__, "recvfrom(fd=%d, buffer=%lx, len=%d) failed: status = %d error = %s", gsn->fd, (unsigned long) buffer, sizeof(buffer), status, status ? strerror(errno) : "No error");
+      return -1;
+    }
   
-  /* Check length of gtp0 packet */
-  if (((pheader->flags & 0xe0) == 0x00) && (status < GTP0_HEADER_SIZE)) {
-    gsn->tooshort++;
-    gtp_errpack(LOG_ERR, __FILE__, __LINE__, &peer, buffer, status,
-		"GTP0 packet too short");
-    return -1; /* Silently discard 29.60: 11.1.2 */
-  }
-
-  switch (pheader->type) {
-  case GTP_ECHO_REQ:
-    return gtp_echo_ind(gsn, &peer, buffer+ip_len, status - ip_len);
-  case GTP_ECHO_RSP:
-    return gtp_echo_conf(gsn, &peer, buffer+ip_len, status - ip_len);
-  case GTP_NOT_SUPPORTED:
-    return gtp_unsup_conf(gsn, &peer, buffer+ip_len, status - ip_len);
-  case GTP_CREATE_PDP_REQ:
-    return gtp_create_pdp_ind(gsn, version, &peer, buffer+ip_len, 
-			      status - ip_len);
-  case GTP_CREATE_PDP_RSP:
-    return gtp_create_pdp_conf(gsn, version, &peer, buffer+ip_len, 
-			       status - ip_len);
-  case GTP_UPDATE_PDP_REQ:
-    return gtp_update_pdp_ind(gsn, version, &peer, buffer+ip_len, 
-			      status - ip_len);
-  case GTP_UPDATE_PDP_RSP:
-    return gtp_update_pdp_conf(gsn, version, &peer, buffer+ip_len, 
-			       status - ip_len);
-  case GTP_DELETE_PDP_REQ:
-    return gtp_delete_pdp_ind(gsn, version, &peer, buffer+ip_len, 
-			      status - ip_len);
-  case GTP_DELETE_PDP_RSP:
-    return gtp_delete_pdp_conf(gsn, version, &peer, buffer+ip_len, 
-			       status - ip_len);
-  case GTP_ERROR:
-    return gtp_error_ind_conf(gsn, version, &peer, buffer+ip_len, 
-			      status - ip_len);
-  case GTP_GPDU:
-    return gtp_gpdu_ind(gsn, version, &peer, buffer+ip_len, status - ip_len);
-  default:
-    {
+    /* Strip off IP header, if present: TODO Is this nessesary? */
+    if ((buffer[0] & 0xF0) == 0x40) {
+      ip_len = (buffer[0] & 0xF) * 4;
+      gtp_errpack(LOG_ERR, __FILE__, __LINE__, &peer, buffer, status,
+		  "IP header found in return from read");
+      continue;
+    }
+    
+    /* Need at least 1 byte in order to check version */
+    if (status < (1)) {
+      gsn->empty++;
+      gtp_errpack(LOG_ERR, __FILE__, __LINE__, &peer, buffer, status,
+		  "Discarding packet - too small");
+      continue;
+    }
+    
+    /* TODO: Remove these ERROR MESSAGES 
+       gtp_err(LOG_ERR, __FILE__, __LINE__, "Discarding packet - too small");
+       gtp_errpack(LOG_ERR, __FILE__, __LINE__, &peer, buffer, status,
+       "Discarding packet - too small"); */
+    
+    pheader = (struct gtp0_header *) (buffer + ip_len);
+    
+    /* Version should be gtp0 (or earlier in theory) */
+    if (((pheader->flags & 0xe0) > 0x00)) {
+      gsn->unsup++;
+      gtp_errpack(LOG_ERR, __FILE__, __LINE__, &peer, buffer, status,
+		  "Unsupported GTP version");
+      gtp_unsup_resp(gsn, &peer, buffer, status); /* 29.60: 11.1.1 */
+      continue;
+    }
+    
+    /* Check length of gtp0 packet */
+    if (((pheader->flags & 0xe0) == 0x00) && (status < GTP0_HEADER_SIZE)) {
+      gsn->tooshort++;
+      gtp_errpack(LOG_ERR, __FILE__, __LINE__, &peer, buffer, status,
+		  "GTP0 packet too short");
+      continue;  /* Silently discard 29.60: 11.1.2 */
+    }
+    
+    switch (pheader->type) {
+    case GTP_ECHO_REQ:
+      gtp_echo_ind(gsn, &peer, buffer+ip_len, status - ip_len);
+      break;
+    case GTP_ECHO_RSP:
+      gtp_echo_conf(gsn, &peer, buffer+ip_len, status - ip_len);
+      break;
+    case GTP_NOT_SUPPORTED:
+      gtp_unsup_conf(gsn, &peer, buffer+ip_len, status - ip_len);
+      break;
+    case GTP_CREATE_PDP_REQ:
+      gtp_create_pdp_ind(gsn, version, &peer, buffer+ip_len, 
+			 status - ip_len);
+      break;
+    case GTP_CREATE_PDP_RSP:
+      gtp_create_pdp_conf(gsn, version, &peer, buffer+ip_len, 
+			  status - ip_len);
+      break;
+    case GTP_UPDATE_PDP_REQ:
+      gtp_update_pdp_ind(gsn, version, &peer, buffer+ip_len, 
+			 status - ip_len);
+      break;
+    case GTP_UPDATE_PDP_RSP:
+      gtp_update_pdp_conf(gsn, version, &peer, buffer+ip_len, 
+			  status - ip_len);
+      break;
+    case GTP_DELETE_PDP_REQ:
+      gtp_delete_pdp_ind(gsn, version, &peer, buffer+ip_len, 
+			 status - ip_len);
+      break;
+    case GTP_DELETE_PDP_RSP:
+      gtp_delete_pdp_conf(gsn, version, &peer, buffer+ip_len, 
+			  status - ip_len);
+      break;
+    case GTP_ERROR:
+      gtp_error_ind_conf(gsn, version, &peer, buffer+ip_len, 
+			 status - ip_len);
+      break;
+    case GTP_GPDU:
+      gtp_gpdu_ind(gsn, version, &peer, buffer+ip_len, status - ip_len);
+      break;
+    default:
       gsn->unknown++;
       gtp_errpack(LOG_ERR, __FILE__, __LINE__, &peer, buffer, status,
 		  "Unknown GTP message type received");
-      return -1;
+      break;
     }
   }
 }
@@ -1853,6 +1881,11 @@
     return EOF;
     }
 
+  if (fcntl(gsn->fd, F_SETFL, 0)) {
+    gtp_err(LOG_ERR, __FILE__, __LINE__, "fnctl()");
+    return -1;
+  }
+
   memcpy(packet.gtp0.p, pack, len); /* TODO Should be avoided! */
   
   if (sendto(gsn->fd, &packet, GTP0_HEADER_SIZE+len, 0,
diff --git a/gtp/pdp.c b/gtp/pdp.c
index 276c8ac..2c05bdb 100644
--- a/gtp/pdp.c
+++ b/gtp/pdp.c
@@ -1,6 +1,6 @@
 /* 
  *  OpenGGSN - Gateway GPRS Support Node
- *  Copyright (C) 2002 Mondru AB.
+ *  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
@@ -32,7 +32,7 @@
 
 struct pdp_t pdpa[PDP_MAX];    /* PDP storage */
 struct pdp_t* hashtid[PDP_MAX];/* Hash table for IMSI + NSAPI */
-struct pdp_t* haship[PDP_MAX]; /* Hash table for IP and network interface */
+/* struct pdp_t* haship[PDP_MAX];  Hash table for IP and network interface */
 
 /* ***********************************************************
  * Functions related to PDP storage
@@ -109,7 +109,7 @@
 int pdp_init() {
   memset(&pdpa, 0, sizeof(pdpa));
   memset(&hashtid, 0, sizeof(hashtid));
-  memset(&haship, 0, sizeof(haship));
+  /*  memset(&haship, 0, sizeof(haship)); */
 
   return 0;
 }
@@ -227,8 +227,9 @@
   return EOF; /* End of linked list and not found */
 }
 
+/*
 int pdp_iphash(void* ipif, struct ul66_t *eua) {
-  /*printf("IPhash %ld\n", lookup(eua->v, eua->l, ipif) % PDP_MAX);*/
+  /#printf("IPhash %ld\n", lookup(eua->v, eua->l, ipif) % PDP_MAX);#/
   return (lookup(eua->v, eua->l, ipif) % PDP_MAX);
 }
     
@@ -276,27 +277,27 @@
     pdp_prev = pdp2;
   }
   if (PDP_DEBUG) printf("End pdp_ipdel: PDP not found\n");
-  return EOF; /* End of linked list and not found */
+  return EOF; /# End of linked list and not found #/
 }
 
 int pdp_ipget(struct pdp_t **pdp, void* ipif, struct ul66_t *eua) {
   int hash = pdp_iphash(ipif, eua);
   struct pdp_t *pdp2;
-  /*printf("Begin pdp_ipget %d %d %2x%2x%2x%2x\n", (unsigned)ipif, eua->l, 
-    eua->v[2],eua->v[3],eua->v[4],eua->v[5]);*/
+  /#printf("Begin pdp_ipget %d %d %2x%2x%2x%2x\n", (unsigned)ipif, eua->l, 
+    eua->v[2],eua->v[3],eua->v[4],eua->v[5]);#/
   for (pdp2 = haship[hash]; pdp2; pdp2 = pdp2->ipnext) {
     if ((pdp2->ipif == ipif) && (pdp2->eua.l == eua->l) && 
 	(memcmp(&pdp2->eua.v, &eua->v, eua->l) == 0)) {
       *pdp = pdp2;
-      /*printf("End pdp_ipget. Found\n");*/
+      /#printf("End pdp_ipget. Found\n");#/
       return 0;
     }
   }
   if (PDP_DEBUG) printf("End pdp_ipget Notfound %d %d %2x%2x%2x%2x\n", 
 	 (unsigned)ipif, eua->l, eua->v[2],eua->v[3],eua->v[4],eua->v[5]);
-  return EOF; /* End of linked list and not found */
+  return EOF; /# End of linked list and not found #/
 }
-
+*/
 /* Various conversion functions */
 
 int pdp_ntoeua(struct in_addr *src, struct ul66_t *eua) {
@@ -307,6 +308,14 @@
   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;
+  }
+  memcpy(dst, &eua->v[2], 4); /* Copy a 4 byte address */
+  return 0;
+}
+
 uint64_t pdp_gettid(uint64_t imsi, uint8_t nsapi) {
   return (imsi & 0x0fffffffffffffff) + ((uint64_t)nsapi << 60);
 }
diff --git a/gtp/pdp.h b/gtp/pdp.h
index b9ca62a..b4f9986 100644
--- a/gtp/pdp.h
+++ b/gtp/pdp.h
@@ -1,6 +1,6 @@
 /* 
  *  OpenGGSN - Gateway GPRS Support Node
- *  Copyright (C) 2002 Mondru AB.
+ *  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
@@ -98,6 +98,7 @@
   /* Parameters shared by all PDP context belonging to the same MS */
 
   void *ipif;           /* IP network interface */
+  void *peer;           /* Pointer to peer protocol */
   void *asap;           /* Application specific service access point */
 
   uint64_t    imsi;     /* International Mobile Subscriber Identity.*/
@@ -191,12 +192,15 @@
 int pdp_tiddel(struct pdp_t *pdp);
 int pdp_tidget(struct pdp_t **pdp, uint64_t tid);
 
+/*
 int pdp_iphash(void* ipif, struct ul66_t *eua);
 int pdp_ipset(struct pdp_t *pdp, void* ipif, struct ul66_t *eua);
 int pdp_ipdel(struct pdp_t *pdp);
 int pdp_ipget(struct pdp_t **pdp, void* ipif, struct ul66_t *eua);
+*/
 
 int pdp_ntoeua(struct in_addr *src, struct ul66_t *eua);
+int pdp_euaton(struct ul66_t *eua, struct in_addr *dst);
 uint64_t pdp_gettid(uint64_t imsi, uint8_t nsapi);
 int ulcpy(void* dst, void* src, size_t size);
 
diff --git a/gtp/queue.c b/gtp/queue.c
index 0080e43..900f240 100644
--- a/gtp/queue.c
+++ b/gtp/queue.c
@@ -133,7 +133,8 @@
     (*qmsg)->this = queue->next;
     (*qmsg)->next=-1;       /* End of the queue */
     (*qmsg)->prev=queue->last; /* Link to the previous */
-    queue->qmsga[queue->last].next=queue->next; /* Link previous to us */
+    if (queue->last != -1)
+      queue->qmsga[queue->last].next=queue->next; /* Link previous to us */
     queue->last = queue->next;                  /* End of queue */
     if (queue->first == -1) queue->first = queue->next;
     queue->next = (queue->next+1) % QUEUE_SIZE;   /* Increment */
diff --git a/sgsnemu/Makefile.am b/sgsnemu/Makefile.am
index 49b4588..b72604e 100644
--- a/sgsnemu/Makefile.am
+++ b/sgsnemu/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
 
-sgsnemu_SOURCES = sgsnemu.c tun.c tun.h cmdline.c cmdline.h
+sgsnemu_SOURCES = sgsnemu.c tun.c tun.h cmdline.c cmdline.h ippool.h ippool.c syserr.h syserr.c
 
 
 
diff --git a/sgsnemu/Makefile.in b/sgsnemu/Makefile.in
index 9407ca0..9a6d74e 100644
--- a/sgsnemu/Makefile.in
+++ b/sgsnemu/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
 
-sgsnemu_SOURCES = sgsnemu.c tun.c tun.h cmdline.c cmdline.h
+sgsnemu_SOURCES = sgsnemu.c tun.c tun.h cmdline.c cmdline.h ippool.h ippool.c syserr.h syserr.c
 subdir = sgsnemu
 mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
 CONFIG_HEADER = $(top_builddir)/config.h
@@ -99,7 +99,8 @@
 bin_PROGRAMS = sgsnemu$(EXEEXT)
 PROGRAMS = $(bin_PROGRAMS)
 
-am_sgsnemu_OBJECTS = sgsnemu.$(OBJEXT) tun.$(OBJEXT) cmdline.$(OBJEXT)
+am_sgsnemu_OBJECTS = sgsnemu.$(OBJEXT) tun.$(OBJEXT) cmdline.$(OBJEXT) \
+	ippool.$(OBJEXT) syserr.$(OBJEXT)
 sgsnemu_OBJECTS = $(am_sgsnemu_OBJECTS)
 sgsnemu_LDADD = $(LDADD)
 sgsnemu_DEPENDENCIES =
@@ -111,7 +112,8 @@
 LIBS = @LIBS@
 depcomp = $(SHELL) $(top_srcdir)/depcomp
 am__depfiles_maybe = depfiles
-@AMDEP_TRUE@DEP_FILES = ./$(DEPDIR)/cmdline.Po ./$(DEPDIR)/sgsnemu.Po \
+@AMDEP_TRUE@DEP_FILES = ./$(DEPDIR)/cmdline.Po ./$(DEPDIR)/ippool.Po \
+@AMDEP_TRUE@	./$(DEPDIR)/sgsnemu.Po ./$(DEPDIR)/syserr.Po \
 @AMDEP_TRUE@	./$(DEPDIR)/tun.Po
 COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
 	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
@@ -173,7 +175,9 @@
 	-rm -f *.tab.c
 
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmdline.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ippool.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sgsnemu.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/sgsnemu/cmdline.c b/sgsnemu/cmdline.c
index a781b18..2240c56 100644
--- a/sgsnemu/cmdline.c
+++ b/sgsnemu/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:
@@ -61,10 +61,9 @@
   printf("   -uSTRING   --uid=STRING       Login user ID (default='mig')\n");
   printf("   -pSTRING   --pwd=STRING       Login password (default='hemmelig')\n");
   printf("              --createif         Create local network interface (default=off)\n");
+  printf("              --defaultroute     Create default route (default=off)\n");
   printf("              --ipup=STRING      Script to run after link-up\n");
   printf("              --ipdown=STRING    Script to run after link-down\n");
-  printf("              --net=STRING       Network (default='0.0.0.0')\n");
-  printf("              --mask=STRING      Network mask (default='0.0.0.0')\n");
   printf("              --pinghost=STRING  Ping remote host\n");
   printf("              --pingrate=INT     Number of ping req per second (default='1')\n");
   printf("              --pingsize=INT     Number of ping data bytes (default='56')\n");
@@ -112,10 +111,9 @@
   args_info->uid_given = 0 ;
   args_info->pwd_given = 0 ;
   args_info->createif_given = 0 ;
+  args_info->defaultroute_given = 0 ;
   args_info->ipup_given = 0 ;
   args_info->ipdown_given = 0 ;
-  args_info->net_given = 0 ;
-  args_info->mask_given = 0 ;
   args_info->pinghost_given = 0 ;
   args_info->pingrate_given = 0 ;
   args_info->pingsize_given = 0 ;
@@ -139,10 +137,9 @@
   args_info->uid_arg = strdup("mig") ;\
   args_info->pwd_arg = strdup("hemmelig") ;\
   args_info->createif_flag = 0;\
+  args_info->defaultroute_flag = 0;\
   args_info->ipup_arg = NULL; \
   args_info->ipdown_arg = NULL; \
-  args_info->net_arg = strdup("0.0.0.0") ;\
-  args_info->mask_arg = strdup("0.0.0.0") ;\
   args_info->pinghost_arg = NULL; \
   args_info->pingrate_arg = 1 ;\
   args_info->pingsize_arg = 56 ;\
@@ -181,10 +178,9 @@
         { "uid",	1, NULL, 'u' },
         { "pwd",	1, NULL, 'p' },
         { "createif",	0, NULL, 0 },
+        { "defaultroute",	0, NULL, 0 },
         { "ipup",	1, NULL, 0 },
         { "ipdown",	1, NULL, 0 },
-        { "net",	1, NULL, 0 },
-        { "mask",	1, NULL, 0 },
         { "pinghost",	1, NULL, 0 },
         { "pingrate",	1, NULL, 0 },
         { "pingsize",	1, NULL, 0 },
@@ -410,6 +406,19 @@
             args_info->createif_flag = !(args_info->createif_flag);
             break;
           }
+          /* Create default route.  */
+          else if (strcmp (long_options[option_index].name, "defaultroute") == 0)
+          {
+            if (args_info->defaultroute_given)
+              {
+                fprintf (stderr, "%s: `--defaultroute' option given more than once\n", PACKAGE);
+                clear_args ();
+                exit (EXIT_FAILURE);
+              }
+            args_info->defaultroute_given = 1;
+            args_info->defaultroute_flag = !(args_info->defaultroute_flag);
+            break;
+          }
           /* Script to run after link-up.  */
           else if (strcmp (long_options[option_index].name, "ipup") == 0)
           {
@@ -436,32 +445,6 @@
             args_info->ipdown_arg = strdup (optarg);
             break;
           }
-          /* Network.  */
-          else if (strcmp (long_options[option_index].name, "net") == 0)
-          {
-            if (args_info->net_given)
-              {
-                fprintf (stderr, "%s: `--net' option given more than once\n", PACKAGE);
-                clear_args ();
-                exit (EXIT_FAILURE);
-              }
-            args_info->net_given = 1;
-            args_info->net_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;
-          }
           /* Ping remote host.  */
           else if (strcmp (long_options[option_index].name, "pinghost") == 0)
           {
@@ -851,6 +834,15 @@
                 }
               continue;
             }
+          if (!strcmp(fopt, "defaultroute"))
+            {
+              if (override || !args_info->defaultroute_given)
+                {
+                  args_info->defaultroute_given = 1;
+                  args_info->defaultroute_flag = !(args_info->defaultroute_flag);
+                }
+              continue;
+            }
           if (!strcmp(fopt, "ipup"))
             {
               if (override || !args_info->ipup_given)
@@ -883,38 +875,6 @@
                 }
               continue;
             }
-          if (!strcmp(fopt, "net"))
-            {
-              if (override || !args_info->net_given)
-                {
-                  args_info->net_given = 1;
-                  if (fnum == 2)
-                    args_info->net_arg = strdup (farg);
-                  else
-                    {
-                      fprintf (stderr, "%s:%d: required <option_name> <option_val>\n",
-                               filename, line_num);
-                      exit (EXIT_FAILURE);
-                    }
-                }
-              continue;
-            }
-          if (!strcmp(fopt, "mask"))
-            {
-              if (override || !args_info->mask_given)
-                {
-                  args_info->mask_given = 1;
-                  if (fnum == 2)
-                    args_info->mask_arg = strdup (farg);
-                  else
-                    {
-                      fprintf (stderr, "%s:%d: required <option_name> <option_val>\n",
-                               filename, line_num);
-                      exit (EXIT_FAILURE);
-                    }
-                }
-              continue;
-            }
           if (!strcmp(fopt, "pinghost"))
             {
               if (override || !args_info->pinghost_given)
diff --git a/sgsnemu/cmdline.ggo b/sgsnemu/cmdline.ggo
index 8a7bf4e..0b562ec 100644
--- a/sgsnemu/cmdline.ggo
+++ b/sgsnemu/cmdline.ggo
@@ -1,46 +1,49 @@
-#  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):
+#
+#
+# Use "gengetopt --conf-parser < cmdline.ggo" 
+# to generate cmdline.c and cmdline.h
 
-option  "fg"          f "Run in foreground"              flag   off
-option  "debug"       d "Run in debug mode"              flag   off
+option  "fg"           f "Run in foreground"              flag   off
+option  "debug"        d "Run in debug mode"              flag   off
 
-option  "conf"        c "Read configuration file"        string no
-option  "pidfile"     - "Filename of process id file"    string default="./sgsnemu.pid" no
-option  "statedir"    - "Directory of nonvolatile data"  string default="./" no
+option  "conf"         c "Read configuration file"        string no
+option  "pidfile"      - "Filename of process id file"    string default="./sgsnemu.pid" no
+option  "statedir"     - "Directory of nonvolatile data"  string default="./" no
 
-option  "dns"         - "DNS Server to use"              string no
-option  "listen"      l "Local interface"                string no
-option  "remote"      r "Remote host"                    string no
+option  "dns"          - "DNS Server to use"              string no
+option  "listen"       l "Local interface"                string no
+option  "remote"       r "Remote host"                    string no
 
-option  "contexts"    - "Number of contexts"             int    default="1" no
-option  "timelimit"   - "Exit after timelimit seconds"   int default="0" no
+option  "contexts"     - "Number of contexts"             int    default="1" no
+option  "timelimit"    - "Exit after timelimit seconds"   int default="0" no
 
-option  "apn"         a "Access point name"              string default="internet" no
-option  "imsi"        i "IMSI"                           string default="240010123456789" no
-option  "msisdn"      m "Mobile Station ISDN number"     string default="46702123456" no
-option  "qos"         q "Requested quality of service"   int    default="0x0b921f" no
-option  "uid"         u "Login user ID"                  string default="mig" no
-option  "pwd"         p "Login password"                 string default="hemmelig" no
+option  "apn"          a "Access point name"              string default="internet" no
+option  "imsi"         i "IMSI"                           string default="240010123456789" no
+option  "msisdn"       m "Mobile Station ISDN number"     string default="46702123456" no
+option  "qos"          q "Requested quality of service"   int    default="0x0b921f" no
+option  "uid"          u "Login user ID"                  string default="mig" no
+option  "pwd"          p "Login password"                 string default="hemmelig" no
 
-option  "createif"    - "Create local network interface" flag   off
-option  "ipup"        - "Script to run after link-up"    string no
-option  "ipdown"      - "Script to run after link-down"  string no
-option  "net"         - "Network"                        string default="0.0.0.0" no
-option  "mask"        - "Network mask"                   string default="0.0.0.0" no
+option  "createif"     - "Create local network interface" flag   off
+option  "defaultroute" - "Create default route"           flag   off
+option  "ipup"         - "Script to run after link-up"    string no
+option  "ipdown"       - "Script to run after link-down"  string no
 
-option  "pinghost"    - "Ping remote host"               string no
-option  "pingrate"    - "Number of ping req per second"  int default="1" no
-option  "pingsize"    - "Number of ping data bytes"      int default="56" no
-option  "pingcount"   - "Number of ping req to send"     int default="0" no
-option  "pingquiet"   - "Do not print ping packet info"  flag off
+option  "pinghost"     - "Ping remote host"               string no
+option  "pingrate"     - "Number of ping req per second"  int default="1" no
+option  "pingsize"     - "Number of ping data bytes"      int default="56" no
+option  "pingcount"    - "Number of ping req to send"     int default="0" no
+option  "pingquiet"    - "Do not print ping packet info"  flag off
 
diff --git a/sgsnemu/cmdline.h b/sgsnemu/cmdline.h
index bf4e78d..0c03629 100644
--- a/sgsnemu/cmdline.h
+++ b/sgsnemu/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
@@ -37,10 +37,9 @@
   char * uid_arg;	/* Login user ID (default='mig').  */
   char * pwd_arg;	/* Login password (default='hemmelig').  */
   int createif_flag;	/* Create local network interface (default=off).  */
+  int defaultroute_flag;	/* Create default route (default=off).  */
   char * ipup_arg;	/* Script to run after link-up.  */
   char * ipdown_arg;	/* Script to run after link-down.  */
-  char * net_arg;	/* Network (default='0.0.0.0').  */
-  char * mask_arg;	/* Network mask (default='0.0.0.0').  */
   char * pinghost_arg;	/* Ping remote host.  */
   int pingrate_arg;	/* Number of ping req per second (default='1').  */
   int pingsize_arg;	/* Number of ping data bytes (default='56').  */
@@ -66,10 +65,9 @@
   int uid_given ;	/* Whether uid was given.  */
   int pwd_given ;	/* Whether pwd was given.  */
   int createif_given ;	/* Whether createif was given.  */
+  int defaultroute_given ;	/* Whether defaultroute was given.  */
   int ipup_given ;	/* Whether ipup was given.  */
   int ipdown_given ;	/* Whether ipdown was given.  */
-  int net_given ;	/* Whether net was given.  */
-  int mask_given ;	/* Whether mask was given.  */
   int pinghost_given ;	/* Whether pinghost was given.  */
   int pingrate_given ;	/* Whether pingrate was given.  */
   int pingsize_given ;	/* Whether pingsize was given.  */
diff --git a/sgsnemu/ippool.c b/sgsnemu/ippool.c
new file mode 100644
index 0000000..3ad5c04
--- /dev/null
+++ b/sgsnemu/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/sgsnemu/ippool.h b/sgsnemu/ippool.h
new file mode 100644
index 0000000..2d3f575
--- /dev/null
+++ b/sgsnemu/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/sgsnemu/sgsnemu.c b/sgsnemu/sgsnemu.c
index 1cdfcac..99f0366 100644
--- a/sgsnemu/sgsnemu.c
+++ b/sgsnemu/sgsnemu.c
@@ -1,6 +1,6 @@
 /* 
  *  OpenGGSN - Gateway GPRS Support Node
- *  Copyright (C) 2002 Mondru AB.
+ *  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
@@ -21,7 +21,7 @@
 
 
 #ifdef __linux__
-#define _GNU_SOURCE 1		/* strdup() prototype, broken arpa/inet.h */
+#define _GNU_SOURCE 1 /* strdup() prototype, broken arpa/inet.h */
 #endif
 
 
@@ -52,38 +52,59 @@
 #include <time.h>
 
 #include "tun.h"
+#include "ippool.h"
+#include "syserr.h"
 #include "../gtp/pdp.h"
 #include "../gtp/gtp.h"
 #include "cmdline.h"
 
-#define SGSNEMU_BUFSIZE 1024
+#define IPADDRLEN 256      /* Character length of addresses */ 
+#define MAXCONTEXTS 16     /* Max number of allowed contexts */ 
 
-/* State variable      */
-/* 0: Idle             */
-/* 1: Wait_connect     */
-/* 2: Connected        */
-/* 3: Wait_disconnect  */
+/* HASH tables for IP address allocation */
+struct iphash_t {
+  uint8_t     inuse;    /* 0=free. 1=used by somebody */
+  struct iphash_t *ipnext;
+  struct pdp_t *pdp;
+  struct in_addr addr;
+};
+struct iphash_t iparr[MAXCONTEXTS];
+struct iphash_t *iphash[MAXCONTEXTS];
+
+/* State variable used for ping  */
+/* 0: Idle                       */
+/* 1: Wait_connect               */
+/* 2: Connected                  */
+/* 3: Wait_disconnect            */
 int state = 0;                  
 
+struct gsn_t *gsn = NULL;       /* GSN instance */
+struct tun_t *tun = NULL;       /* TUN instance */
 int maxfd = 0;	                /* For select() */
-int tun_fd = -1;		/* Network file descriptor */
-struct tun_t *tun;              /* TUN instance            */
-struct tun_t *tun1, *tun2;      /* TUN instance for client */
-int tun_fd1 = -1;		/* Network file descriptor */
-int tun_fd2 = -1;		/* Network file descriptor */
 
-/* Variables matching program configuration parameters */
-int debug;                      /* Print debug messages */
-struct in_addr net, mask;       /* Network interface       */
-int createif;                   /* Create local network interface */
-char *ipup, *ipdown;            /* Filename of scripts */
-/* int defaultroute;               Set up default route    */
-struct in_addr pinghost;        /* Remote ping host    */
-int pingrate, pingsize, pingcount, pingquiet;
-struct in_addr listen_, remote;
-struct in_addr dns;
-int contexts;                   /* Number of contexts to create */
-int timelimit;                  /* Number of seconds to be connected */
+/* Struct with local versions of gengetopt options */
+struct {
+  int debug;                      /* Print debug messages */
+  int createif;                   /* Create local network interface */
+  char *ipup, *ipdown;            /* Filename of scripts */
+  int defaultroute;               /* Set up default route */
+  struct in_addr pinghost;        /* Remote ping host    */
+  int pingrate;
+  int pingsize;
+  int pingcount;
+  int pingquiet;
+  struct in_addr listen;
+  struct in_addr remote;
+  struct in_addr dns;
+  int contexts;                   /* Number of contexts to create */
+  int timelimit;                  /* Number of seconds to be connected */
+  char *statedir;
+  uint64_t imsi;
+  struct ul255_t pco;
+  struct ul255_t qos;
+  struct ul255_t apn;
+  struct ul16_t msisdn;
+} options;
 
 
 /* Definitions to use for PING. Most of the ping code was derived from */
@@ -123,6 +144,359 @@
 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;
+  struct iphash_t *h;
+  struct iphash_t *prev = NULL;
+  ipaddr->ipnext = NULL;
+  ipaddr->addr.s_addr = addr->s_addr;
+  for (h = iphash[hash]; h; h = h->ipnext)
+    prev = h;
+  if (!prev) 
+    iphash[hash] = ipaddr;
+  else 
+    prev->ipnext = ipaddr;
+  return 0;
+}
+
+int ipdel(struct iphash_t *ipaddr) {
+  int hash = ippool_hash4(&ipaddr->addr) % MAXCONTEXTS;
+  struct iphash_t *h;
+  struct iphash_t *prev = NULL;
+  for (h = iphash[hash]; h; h = h->ipnext) {
+    if (h == ipaddr) {
+      if (!prev) 
+	iphash[hash] = h->ipnext;
+      else 
+	prev->ipnext = h->ipnext;
+      return 0;
+    }
+    prev = h;
+  }
+  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;
+  struct iphash_t *h;
+  for (h = iphash[hash]; h; h = h->ipnext) {
+    if ((h->addr.s_addr == addr->s_addr)) {
+      *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) {
+  FILE *file;
+  mode_t oldmask;
+  
+  oldmask = umask(022);
+  file = fopen(pidfile, "w");
+  umask(oldmask);
+  if(!file)
+    return;
+  fprintf(file, "%d\n", getpid());
+  fclose(file);
+}
+
+
+int process_options(int argc, char **argv) {
+  /* gengeopt declarations */
+  struct gengetopt_args_info args_info;
+
+  struct hostent *host;
+  int n;
+
+  if (cmdline_parser (argc, argv, &args_info) != 0)
+    return -1;
+  if (args_info.debug_flag) {
+    printf("remote: %s\n", args_info.remote_arg);
+    printf("listen: %s\n", args_info.listen_arg);
+    printf("conf: %s\n", args_info.conf_arg);
+    printf("fg: %d\n", args_info.fg_flag);
+    printf("debug: %d\n", args_info.debug_flag);
+    printf("imsi: %s\n", args_info.imsi_arg);
+    printf("qos: %#08x\n", args_info.qos_arg);
+    printf("apn: %s\n", args_info.apn_arg);
+    printf("msisdn: %s\n", args_info.msisdn_arg);
+    printf("uid: %s\n", args_info.uid_arg);
+    printf("pwd: %s\n", args_info.pwd_arg);
+    printf("pidfile: %s\n", args_info.pidfile_arg);
+    printf("statedir: %s\n", args_info.statedir_arg);
+    printf("dns: %s\n", args_info.dns_arg);
+    printf("contexts: %d\n", args_info.contexts_arg);
+    printf("timelimit: %d\n", args_info.timelimit_arg);
+    printf("createif: %d\n", args_info.createif_flag);
+    printf("ipup: %s\n", args_info.ipup_arg);
+    printf("ipdown: %s\n", args_info.ipdown_arg);
+    printf("defaultroute: %d\n", args_info.defaultroute_flag);
+    printf("pinghost: %s\n", args_info.pinghost_arg);
+    printf("pingrate: %d\n", args_info.pingrate_arg);
+    printf("pingsize: %d\n", args_info.pingsize_arg);
+    printf("pingcount: %d\n", args_info.pingcount_arg);
+    printf("pingquiet: %d\n", args_info.pingquiet_flag);
+  }
+
+  /* Try out our new parser */
+  
+  if (args_info.conf_arg) {
+    if (cmdline_parser_configfile (args_info.conf_arg, &args_info, 0) != 0)
+      return -1;
+    if (args_info.debug_flag) {
+      printf("cmdline_parser_configfile\n");
+      printf("remote: %s\n", args_info.remote_arg);
+      printf("listen: %s\n", args_info.listen_arg);
+      printf("conf: %s\n", args_info.conf_arg);
+      printf("fg: %d\n", args_info.fg_flag);
+      printf("debug: %d\n", args_info.debug_flag);
+      printf("imsi: %s\n", args_info.imsi_arg);
+      printf("qos: %#08x\n", args_info.qos_arg);
+      printf("apn: %s\n", args_info.apn_arg);
+      printf("msisdn: %s\n", args_info.msisdn_arg);
+      printf("uid: %s\n", args_info.uid_arg);
+      printf("pwd: %s\n", args_info.pwd_arg);
+      printf("pidfile: %s\n", args_info.pidfile_arg);
+      printf("statedir: %s\n", args_info.statedir_arg);
+      printf("dns: %s\n", args_info.dns_arg);
+      printf("contexts: %d\n", args_info.contexts_arg);
+      printf("timelimit: %d\n", args_info.timelimit_arg);
+      printf("createif: %d\n", args_info.createif_flag);
+      printf("ipup: %s\n", args_info.ipup_arg);
+      printf("ipdown: %s\n", args_info.ipdown_arg);
+      printf("defaultroute: %d\n", args_info.defaultroute_flag);
+      printf("pinghost: %s\n", args_info.pinghost_arg);
+      printf("pingrate: %d\n", args_info.pingrate_arg);
+      printf("pingsize: %d\n", args_info.pingsize_arg);
+      printf("pingcount: %d\n", args_info.pingcount_arg);
+      printf("pingquiet: %d\n", args_info.pingquiet_flag);
+    }
+  }
+
+  /* Handle each option */
+
+  /* foreground                                                   */
+  /* If fg flag not given run as a daemon                            */
+  if (!args_info.fg_flag)
+    {
+      closelog(); 
+      /* Close the standard file descriptors. Why? */
+      freopen("/dev/null", "w", stdout);
+      freopen("/dev/null", "w", stderr);
+      freopen("/dev/null", "r", stdin);
+      daemon(0, 0);
+      /* Open log again. This time with new pid */
+      openlog(PACKAGE, LOG_PID, LOG_DAEMON);
+    }
+
+  /* debug                                                        */
+  options.debug = args_info.debug_flag;
+
+  /* pidfile */
+  /* This has to be done after we have our final pid */
+  if (args_info.pidfile_arg) {
+    log_pid(args_info.pidfile_arg);
+  }
+
+  /* dns                                                          */
+  /* If no dns option is given use system default                 */
+  /* Do hostname lookup to translate hostname to IP address       */
+  printf("\n");
+  if (args_info.dns_arg) {
+    if (!(host = gethostbyname(args_info.dns_arg))) {
+      sys_err(LOG_ERR, __FILE__, __LINE__, 0,
+	      "Invalid DNS address: %s!", args_info.dns_arg);
+      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));
+    }
+  }
+  else {
+    options.dns.s_addr= 0;
+    printf("Using default DNS server\n");
+  }
+
+  /* listen                                                       */
+  /* 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))) {
+      sys_err(LOG_ERR, __FILE__, __LINE__, 0,
+	      "Invalid listening address: %s!", args_info.listen_arg);
+      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));
+    }
+  }
+  else {
+    sys_err(LOG_ERR, __FILE__, __LINE__, 0,
+	    "Listening address must be specified: %s!", args_info.listen_arg);
+    return -1;
+  }
+  
+  
+  /* remote                                                       */
+  /* 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))) {
+      sys_err(LOG_ERR, __FILE__, __LINE__, 0,
+	      "Invalid remote address: %s!", args_info.remote_arg);
+      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));
+    }
+  }
+  else {
+    sys_err(LOG_ERR, __FILE__, __LINE__, 0,
+	    "No remote address given!");
+    return -1;
+  }
+
+
+  /* imsi                                                            */
+  if (strlen(args_info.imsi_arg)!=15) {
+    printf("Invalid IMSI\n");
+    return -1;
+  }
+  options.imsi  = ((uint64_t) (args_info.imsi_arg[ 0]-48));
+  options.imsi |= ((uint64_t) (args_info.imsi_arg[ 1]-48)) <<  4;
+  options.imsi |= ((uint64_t) (args_info.imsi_arg[ 2]-48)) <<  8;
+  options.imsi |= ((uint64_t) (args_info.imsi_arg[ 3]-48)) << 12;
+  options.imsi |= ((uint64_t) (args_info.imsi_arg[ 4]-48)) << 16;
+  options.imsi |= ((uint64_t) (args_info.imsi_arg[ 5]-48)) << 20;
+  options.imsi |= ((uint64_t) (args_info.imsi_arg[ 6]-48)) << 24;
+  options.imsi |= ((uint64_t) (args_info.imsi_arg[ 7]-48)) << 28;
+  options.imsi |= ((uint64_t) (args_info.imsi_arg[ 8]-48)) << 32;
+  options.imsi |= ((uint64_t) (args_info.imsi_arg[ 9]-48)) << 36;
+  options.imsi |= ((uint64_t) (args_info.imsi_arg[10]-48)) << 40;
+  options.imsi |= ((uint64_t) (args_info.imsi_arg[11]-48)) << 44;
+  options.imsi |= ((uint64_t) (args_info.imsi_arg[12]-48)) << 48;
+  options.imsi |= ((uint64_t) (args_info.imsi_arg[13]-48)) << 52;
+  options.imsi |= ((uint64_t) (args_info.imsi_arg[14]-48)) << 56;
+
+    printf("IMSI is:               %s (%#08llx)\n", 
+	   args_info.imsi_arg, options.imsi);
+
+
+  /* qos                                                             */
+  options.qos.l = 3;
+  options.qos.v[2] = (args_info.qos_arg) & 0xff;
+  options.qos.v[1] = ((args_info.qos_arg) >> 8) & 0xff;
+  options.qos.v[0] = ((args_info.qos_arg) >> 16) & 0xff;
+  
+  /* contexts                                                        */
+  if (args_info.contexts_arg > MAXCONTEXTS) {
+    printf("Contexts has to be less than %d\n", MAXCONTEXTS);
+    return -1;
+  }
+  options.contexts = args_info.contexts_arg;
+
+  /* Timelimit                                                       */
+  options.timelimit = args_info.timelimit_arg;
+  
+  /* apn                                                             */
+  if (strlen(args_info.apn_arg) > (sizeof(options.apn.v)-1)) {
+    printf("Invalid APN\n");
+    return -1;
+  }
+  options.apn.l = strlen(args_info.apn_arg) + 1;
+  options.apn.v[0] = (char) strlen(args_info.apn_arg);
+  strncpy(&options.apn.v[1], args_info.apn_arg, sizeof(options.apn.v)-1);
+  printf("Using APN:             %s\n", args_info.apn_arg);
+  
+  /* msisdn                                                          */
+  if (strlen(args_info.msisdn_arg)>(sizeof(options.msisdn.v)-1)) {
+    printf("Invalid MSISDN\n");
+    return -1;
+  }
+  options.msisdn.l = 1;
+  options.msisdn.v[0] = 0x91; /* International format */
+  for(n=0; n<strlen(args_info.msisdn_arg); n++) {
+    if ((n%2) == 0) {
+      options.msisdn.v[((int)n/2)+1] = args_info.msisdn_arg[n] - 48 + 0xf0;
+      options.msisdn.l += 1;
+    }
+    else {
+      options.msisdn.v[((int)n/2)+1] = 
+	(options.msisdn.v[((int)n/2)+1] & 0x0f) +
+	(args_info.msisdn_arg[n] - 48) * 16;
+    }
+  }
+  printf("Using MSISDN:          %s\n", args_info.msisdn_arg);
+
+  /* UID and PWD */
+  /* Might need to also insert stuff like DNS etc. */
+  if ((strlen(args_info.uid_arg) + strlen(args_info.pwd_arg) + 10)>
+      (sizeof(options.pco.v)-1)) {
+    printf("invalid UID and PWD\n");
+    return -1;
+  }
+  options.pco.l = strlen(args_info.uid_arg) + strlen(args_info.pwd_arg) + 10;
+  options.pco.v[0] = 0x80; /* PPP */
+  options.pco.v[1] = 0xc0; /* PAP */
+  options.pco.v[2] = 0x23; 
+  options.pco.v[3] = 0x12; /* Length of protocol contents */
+  options.pco.v[4] = 0x01; /* Authenticate request */
+  options.pco.v[5] = 0x01;
+  options.pco.v[6] = 0x00; /* MSB of length */
+  options.pco.v[7] = strlen(args_info.uid_arg) + strlen(args_info.pwd_arg) + 6;
+  options.pco.v[8] = strlen(args_info.uid_arg);
+  memcpy(&options.pco.v[9], args_info.uid_arg, strlen(args_info.uid_arg));
+  options.pco.v[9+strlen(args_info.uid_arg)] = strlen(args_info.pwd_arg);
+  memcpy(&options.pco.v[10+strlen(args_info.uid_arg)], 
+	 args_info.pwd_arg, strlen(args_info.pwd_arg));
+  
+  /* createif */
+  options.createif = args_info.createif_flag;
+
+  /* ipup */
+  options.ipup = args_info.ipup_arg;
+
+  /* ipdown */
+  options.ipdown = args_info.ipdown_arg;
+
+  /* statedir */
+  options.statedir = args_info.statedir_arg;
+
+  /* defaultroute */
+  options.defaultroute = args_info.defaultroute_flag;
+
+
+  /* pinghost                                                         */
+  /* Store ping host as in_addr                                   */
+  if (args_info.pinghost_arg) {
+    if (!inet_aton(args_info.pinghost_arg, &options.pinghost)) {
+      sys_err(LOG_ERR, __FILE__, __LINE__, 0,
+	      "Invalid ping host: %s!", args_info.pinghost_arg);
+      return -1;
+    }
+  }
+
+  /* Other ping parameters                                        */
+  options.pingrate = args_info.pingrate_arg;
+  options.pingsize = args_info.pingsize_arg;
+  options.pingcount = args_info.pingcount_arg;
+  options.pingquiet = args_info.pingquiet_flag;
+
+  return 0;
+
+}
+
 
 int encaps_printf(struct pdp_t *pdp, void *pack, unsigned len) {
   int i;
@@ -175,18 +549,20 @@
   struct timezone tz;
   struct timeval tv;
   int diff;
-  if ((pinghost.s_addr) && (2 == state) && 
-      ((pingseq < pingcount) || (pingcount == 0))) {
+  if ((options.pinghost.s_addr) && (2 == state) && 
+      ((pingseq < options.pingcount) || (options.pingcount == 0))) {
     gettimeofday(&tv, &tz);
-    diff = 1000000 / pingrate * pingseq -
+    diff = 1000000 / options.pingrate * pingseq -
       1000000 * (tv.tv_sec - firstping.tv_sec) -
       (tv.tv_usec - firstping.tv_usec); /* Microseconds safe up to 500 sec */
     tp->tv_sec = 0;
     if (diff > 0) 
       tp->tv_usec = diff;
-    else
+    else {
       /* For some reason we get packet loss if set to zero */
-      tp->tv_usec = 100000 / pingrate; /* 10 times pingrate */
+      tp->tv_usec = 100000 / options.pingrate; /* 10 times pingrate */
+	 tp->tv_usec = 0; 
+    }
   }
   return 0;
 }
@@ -201,7 +577,7 @@
   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(pinghost));
+  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 );
@@ -214,7 +590,7 @@
 		    ntransmitted));
   }
   printf("\n");
-  if (debug) printf("%d packets received in total\n", ntreceived );
+  if (options.debug) printf("%d packets received in total\n", ntreceived );
   if (nreceived  && tsum)
     printf("round-trip (ms)  min/avg/max = %.3f/%.3f/%.3f\n\n",
 	   tmin/1000.0,
@@ -238,7 +614,7 @@
   src.s_addr = pingpack->src;
 
   gettimeofday(&tv, &tz);
-  if (debug) printf("%d.%6d ", (int) tv.tv_sec, (int) tv.tv_usec);
+  if (options.debug) printf("%d.%6d ", (int) tv.tv_sec, (int) tv.tv_usec);
 
   if (len < CREATEPING_IP + CREATEPING_ICMP) {
     printf("packet too short (%d bytes) from %s\n", len,
@@ -248,21 +624,21 @@
 
   ntreceived++;
   if (pingpack->protocol != 1) {
-    if (!pingquiet) printf("%d bytes from %s: ip_protocol=%d (%s)\n",
+    if (!options.pingquiet) printf("%d bytes from %s: ip_protocol=%d (%s)\n",
 	   len, inet_ntoa(src), pingpack->protocol, 
 	   print_ipprot(pingpack->protocol));
     return 0;
   }
 
   if (pingpack->type != 0) {
-    if (!pingquiet) printf("%d bytes from %s: icmp_type=%d (%s) icmp_code=%d\n",
+    if (!options.pingquiet) printf("%d bytes from %s: icmp_type=%d (%s) icmp_code=%d\n",
 	   len, inet_ntoa(src), pingpack->type, 
 	   print_icmptype(pingpack->type), pingpack->code);
     return 0;
   }
 
   nreceived++;
-  if (!pingquiet) printf("%d bytes from %s: icmp_seq=%d", len,
+  if (!options.pingquiet) printf("%d bytes from %s: icmp_seq=%d", len,
 	 inet_ntoa(src), ntohs(pingpack->seq));
 
   if (len >= sizeof(struct timeval) + CREATEPING_IP + CREATEPING_ICMP) {
@@ -281,11 +657,11 @@
     if( triptime > tmax )
       tmax = triptime;
 
-    if (!pingquiet) printf(" time=%.3f ms\n", triptime/1000.0);
+    if (!options.pingquiet) printf(" time=%.3f ms\n", triptime/1000.0);
 
   } 
   else
-    if (!pingquiet) printf("\n");
+    if (!options.pingquiet) printf("\n");
   return 0;
 }
 
@@ -305,11 +681,9 @@
   struct timeval *tp = (struct timeval *) &p8[CREATEPING_IP + CREATEPING_ICMP];
 
   if (datasize > CREATEPING_MAX) {
-    fprintf(stderr, "%s: Ping size to large: %d!\n", 
-	    PACKAGE, datasize);
-    syslog(LOG_ERR, "Ping size to large: %d!", 
-	   datasize);
-    exit(1);
+    sys_err(LOG_ERR, __FILE__, __LINE__, 0,
+	    "Ping size to large: %d!", datasize);
+    return -1;
   }
 
   memcpy(&src, &(pdp->eua.v[2]), 4); /* Copy a 4 byte address */
@@ -366,130 +740,72 @@
   pack.checksum = ~sum;
 
   ntransmitted++;
-
   return gtp_gpdu(gsn, pdp, &pack, 28 + datasize);
 }
 		
 
-/* Used to write process ID to file. Assume someone else will delete */
-void log_pid(char *pidfile) {
-  FILE *file;
-  mode_t oldmask;
-  
-  oldmask = umask(022);
-  file = fopen(pidfile, "w");
-  umask(oldmask);
-  if(!file)
-    return;
-  fprintf(file, "%d\n", getpid());
-  fclose(file);
+int delete_context(struct pdp_t *pdp) {
+
+  if (tun && options.ipdown) tun_runscript(tun, options.ipdown);
+
+  ipdel((struct iphash_t*) pdp->peer);
+  memset(pdp->peer, 0, sizeof(struct iphash_t)); /* To be sure */
+  return 0;
 }
 
-int delete_context(struct pdp_t *pdp) {
-  char buf[SGSNEMU_BUFSIZE];
-  if ((createif) && (pdp->ipif!=0)) {
-    tun_freetun((struct tun_t*) pdp->ipif);
-    
-    /* Clean up locally */
-    if (pdp->ipif == tun1) {
-      printf("Deleting tun interface\n");
-      tun_fd1=-1;
-    }
-    if (pdp->ipif == tun2) {
-      printf("Deleting tun interface\n");
-      tun_fd2=-1;
-    }
+
+/* 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 tun_packet_t *iph = (struct tun_packet_t*) pack;
+
+  src.s_addr = iph->src;
+
+  if (ipget(&ipm, &src)) {
+    printf("Received packet without a valid source address!!!\n");
+    return 0;
   }
   
-  if (ipdown) {
-    /* system("ipdown /dev/tun0 192.168.0.10"); */
-    snprintf(buf, sizeof(buf), "%s %s %hu.%hu.%hu.%hu",
-	     ipdown,
-	     ((struct tun_t*) pdp->ipif)->devname,
-	     pdp->eua.v[2], pdp->eua.v[3], pdp->eua.v[4], pdp->eua.v[5]);
-    if (debug) printf("%s\n", buf);
-    system(buf);
-  }
-
-  pdp_ipdel(pdp);
+  if (ipm->pdp) /* Check if a peer protocol is defined */
+    gtp_gpdu(gsn, ipm->pdp, pack, len);
   return 0;
 }
 
 int create_pdp_conf(struct pdp_t *pdp, int cause) {
-  char buf[SGSNEMU_BUFSIZE];
-  char snet[SGSNEMU_BUFSIZE];
-  char smask[SGSNEMU_BUFSIZE];
+  struct in_addr addr;
 
-  printf("Received create PDP context response. Cause value: %d\n", cause);
-  if ((cause == 128) && (pdp->eua.l == 6)) {
-    
-    if (!createif) {
-      pdp->ipif = tun1;
-    }
-    else {
-      printf("Setting up interface and routing\n");
-      if ((tun_fd = tun_newtun((struct tun_t**) &pdp->ipif)) > maxfd)
-	maxfd = tun_fd;
-      
-      /* HACK: Only support select of up to two tun interfaces */
-      if (NULL == tun1) {
-	tun1 = pdp->ipif;
-	tun_fd1 = tun1->fd;
-      }
-      else {
-	tun2 = pdp->ipif;
-	tun_fd2 = tun2->fd;
-      }
-      
-      /*system("/sbin/ifconfig tun0 192.168.0.10");*/
-      snprintf(buf, sizeof(buf), "/sbin/ifconfig %s %hu.%hu.%hu.%hu", 
-	      ((struct tun_t*) pdp->ipif)->devname,
-	      pdp->eua.v[2], pdp->eua.v[3], pdp->eua.v[4], pdp->eua.v[5]);
-      /* if (debug) */ printf("%s\n", buf);
-      system(buf);
-
-      /* system("route add -host 192.168.0.10 dev tun0"); */
-      /* It seams as if we do not need to set up a route to a p-t-p interface
-	 snprintf(buf, sizeof(buf), 
-	       "/sbin/route add -host %hu.%hu.%hu.%hu dev %s",
-	      pdp->eua.v[2], pdp->eua.v[3], pdp->eua.v[4], pdp->eua.v[5],
-	      ((struct tun_t*) pdp->ipif)->devname);
-	 if (debug) printf("%s\n", buf);
-      system(buf);*/
-
-      /*if (defaultroute) {*/
-	strncpy(snet, inet_ntoa(net), sizeof(snet));
-	strncpy(smask, inet_ntoa(mask), sizeof(smask));
-	/* system("route add -net 0.0.0.0 netmask 0.0.0.0 gw 192.168.0.1"); */
-	snprintf(buf, sizeof(buf), 
-		 "/sbin/route add -net %s netmask %s gw %hu.%hu.%hu.%hu", 
-		snet, smask,
-		pdp->eua.v[2], pdp->eua.v[3], pdp->eua.v[4], pdp->eua.v[5]);
-	/* if (debug) */ printf("%s\n", buf);
-	system(buf);
-	/*}*/
-
-      if (ipup) {
-	/* system("ipup /dev/tun0 192.168.0.10"); */
-	snprintf(buf, sizeof(buf), "%s %s %hu.%hu.%hu.%hu",
-		ipup,
-		((struct tun_t*) pdp->ipif)->devname,
-		pdp->eua.v[2], pdp->eua.v[3], pdp->eua.v[4], pdp->eua.v[5]);
-	if (debug) printf("%s\n", buf);
-	system(buf);
-      }
-      
-    }
-    
-    pdp_ipset(pdp, pdp->ipif, &pdp->eua);
-
-    state = 2;                      /* Connected */
-  }
-  else {
+  if (cause != 128) {
+    printf("Received create PDP context response. Cause value: %d\n", cause);
     state = 0;
+    return EOF; /* Not what we expected */
   }
 
-  printf("\n");
+  if (pdp_euaton(&pdp->eua, &addr)) {
+    printf("Received create PDP context response. Cause value: %d\n", cause);
+    state = 0;
+    return EOF; /* Not a valid IP address */
+  }
+
+  printf("Received create PDP context response. IP address: %s\n", 
+	 inet_ntoa(addr));
+
+  if (options.createif) {
+    struct in_addr m;
+    inet_aton("255.255.255.255", &m);
+    /* printf("Setting up interface and routing\n");*/
+    tun_addaddr(tun, &addr,  &addr, &m);
+    if (options.defaultroute) {
+      struct in_addr rm;
+      rm.s_addr = 0;
+      tun_addroute(tun, &rm,  &addr, &rm);
+    }
+    if (options.ipup) tun_runscript(tun, options.ipup);
+  }
+    
+  ipset((struct iphash_t*) pdp->peer, &addr);
+  
+  state = 2;                      /* Connected */
 
   return 0;
 }
@@ -524,23 +840,6 @@
   }
 }
 
-int encaps_gtp_client(void *gsn, struct tun_t *tun, void *pack, unsigned len) {
-  /* Special client version which checks for source address instead */
-  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+12, 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 {
-    printf("Received packet without a valid source address!!!\n");
-    return 0;
-  }
-}
 
 int encaps_tun(struct pdp_t *pdp, void *pack, unsigned len) {
   /*  printf("encaps_tun. Packet received: forwarding to tun\n");*/
@@ -549,420 +848,119 @@
 
 int main(int argc, char **argv)
 {
-  /* gengeopt declarations */
-  struct gengetopt_args_info args_info;
-
-
-  struct hostent *host;
-  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 pdp_t *pdp[50];
-  int n; /* For counter */
-  int starttime;                /* Time program was started */
+  struct pdp_t *pdp;
+  int n;
+  int starttime = time(NULL);   /* Time program was started */
 
   struct timezone tz;           /* Used for calculating ping times */
   struct timeval tv;
   int diff;
 
-  /* function-local options */
-  struct ul_t imsi, qos, apn, msisdn;
-  unsigned char qosh[3], imsih[8], apnh[256], msisdnh[256];
-  struct ul255_t pco;
-  uint64_t imsi3;
-
   /* open a connection to the syslog daemon */
   /*openlog(PACKAGE, LOG_PID, LOG_DAEMON);*/
   openlog(PACKAGE, (LOG_PID | LOG_PERROR), LOG_DAEMON);
 
-  if (cmdline_parser (argc, argv, &args_info) != 0)
+  /* Process options given in configuration file and command line */
+  if (process_options(argc, argv)) 
     exit(1);
-  if (args_info.debug_flag) {
-    printf("remote: %s\n", args_info.remote_arg);
-    printf("listen: %s\n", args_info.listen_arg);
-    printf("conf: %s\n", args_info.conf_arg);
-    printf("fg: %d\n", args_info.fg_flag);
-    printf("debug: %d\n", args_info.debug_flag);
-    printf("imsi: %s\n", args_info.imsi_arg);
-    printf("qos: %#08x\n", args_info.qos_arg);
-    printf("apn: %s\n", args_info.apn_arg);
-    printf("msisdn: %s\n", args_info.msisdn_arg);
-    printf("uid: %s\n", args_info.uid_arg);
-    printf("pwd: %s\n", args_info.pwd_arg);
-    printf("pidfile: %s\n", args_info.pidfile_arg);
-    printf("statedir: %s\n", args_info.statedir_arg);
-    printf("dns: %s\n", args_info.dns_arg);
-    printf("contexts: %d\n", args_info.contexts_arg);
-    printf("timelimit: %d\n", args_info.timelimit_arg);
-    printf("createif: %d\n", args_info.createif_flag);
-    printf("ipup: %s\n", args_info.ipup_arg);
-    printf("ipdown: %s\n", args_info.ipdown_arg);
-    /*printf("defaultroute: %d\n", args_info.defaultroute_flag);*/
-    printf("net: %s\n", args_info.net_arg);
-    printf("mask: %s\n", args_info.mask_arg);
-    printf("pinghost: %s\n", args_info.pinghost_arg);
-    printf("pingrate: %d\n", args_info.pingrate_arg);
-    printf("pingsize: %d\n", args_info.pingsize_arg);
-    printf("pingcount: %d\n", args_info.pingcount_arg);
-    printf("pingquiet: %d\n", args_info.pingquiet_flag);
-  }
-
-  /* Try out our new parser */
-  
-  if (args_info.conf_arg) {
-    if (cmdline_parser_configfile (args_info.conf_arg, &args_info, 0) != 0)
-      exit(1);
-    if (args_info.debug_flag) {
-      printf("cmdline_parser_configfile\n");
-      printf("remote: %s\n", args_info.remote_arg);
-      printf("listen: %s\n", args_info.listen_arg);
-      printf("conf: %s\n", args_info.conf_arg);
-      printf("fg: %d\n", args_info.fg_flag);
-      printf("debug: %d\n", args_info.debug_flag);
-      printf("imsi: %s\n", args_info.imsi_arg);
-      printf("qos: %#08x\n", args_info.qos_arg);
-      printf("apn: %s\n", args_info.apn_arg);
-      printf("msisdn: %s\n", args_info.msisdn_arg);
-      printf("uid: %s\n", args_info.uid_arg);
-      printf("pwd: %s\n", args_info.pwd_arg);
-      printf("pidfile: %s\n", args_info.pidfile_arg);
-      printf("statedir: %s\n", args_info.statedir_arg);
-      printf("dns: %s\n", args_info.dns_arg);
-      printf("contexts: %d\n", args_info.contexts_arg);
-      printf("timelimit: %d\n", args_info.timelimit_arg);
-      printf("createif: %d\n", args_info.createif_flag);
-      printf("ipup: %s\n", args_info.ipup_arg);
-      printf("ipdown: %s\n", args_info.ipdown_arg);
-      /*printf("defaultroute: %d\n", args_info.defaultroute_flag);*/
-      printf("net: %s\n", args_info.net_arg);
-      printf("mask: %s\n", args_info.mask_arg);
-      printf("pinghost: %s\n", args_info.pinghost_arg);
-      printf("pingrate: %d\n", args_info.pingrate_arg);
-      printf("pingsize: %d\n", args_info.pingsize_arg);
-      printf("pingcount: %d\n", args_info.pingcount_arg);
-      printf("pingquiet: %d\n", args_info.pingquiet_flag);
-    }
-  }
-
-  /* Handle each option */
-
-  /* foreground                                                   */
-  /* If fg flag not given run as a daemon                            */
-  if (!args_info.fg_flag)
-    {
-      closelog(); 
-      /* Close the standard file descriptors. Why? */
-      freopen("/dev/null", "w", stdout);
-      freopen("/dev/null", "w", stderr);
-      freopen("/dev/null", "r", stdin);
-      daemon(0, 0);
-      /* Open log again. This time with new pid */
-      openlog(PACKAGE, LOG_PID, LOG_DAEMON);
-    }
-
-  /* debug                                                        */
-  debug = args_info.debug_flag;
-
-  /* pidfile */
-  /* This has to be done after we have our final pid */
-  if (args_info.pidfile_arg) {
-    log_pid(args_info.pidfile_arg);
-  }
-
-  /* dns                                                          */
-  /* If no dns option is given use system default                 */
-  /* Do hostname lookup to translate hostname to IP address       */
-  printf("\n");
-  if (args_info.dns_arg) {
-    if (!(host = gethostbyname(args_info.dns_arg))) {
-      fprintf(stderr, "%s: Invalid dns address: %s!\n", 
-	      PACKAGE, args_info.dns_arg);
-      syslog(LOG_ERR, "Invalid dns address: %s!", 
-	     args_info.dns_arg);
-      exit(1);
-    }
-    else {
-      memcpy(&dns.s_addr, host->h_addr, host->h_length);
-      _res.nscount = 1;
-      _res.nsaddr_list[0].sin_addr = dns;
-      printf("Using DNS server:      %s (%s)\n", args_info.dns_arg, inet_ntoa(dns));
-    }
-  }
-  else {
-    dns.s_addr= 0;
-    printf("Using default DNS server\n");
-  }
-
-  /* listen                                                       */
-  /* 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))) {
-      fprintf(stderr, "%s: Invalid listening address: %s!\n", 
-	      PACKAGE, args_info.listen_arg);
-      syslog(LOG_ERR, "Invalid listening address: %s!", 
-	     args_info.listen_arg);
-      exit(1);
-    }
-    else {
-      memcpy(&listen_.s_addr, host->h_addr, host->h_length);
-      printf("Local IP address is:   %s (%s)\n", args_info.listen_arg, inet_ntoa(listen_));
-    }
-  }
-  else {
-    fprintf(stderr, "%s: Listening address must be specified: %s!\n", 
-	    PACKAGE, args_info.listen_arg);
-    syslog(LOG_ERR, "Listening address must be specified: %s!", 
-	   args_info.listen_arg);
-    exit(1);
-  }
-  
-  
-  /* remote                                                       */
-  /* 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))) {
-      fprintf(stderr, "%s: Invalid remote address: %s!\n", 
-	      PACKAGE, args_info.remote_arg);
-      syslog(LOG_ERR, "Invalid remote address: %s!", 
-	     args_info.remote_arg);
-      exit(1);
-    }
-    else {
-      memcpy(&remote.s_addr, host->h_addr, host->h_length);
-      printf("Remote IP address is:  %s (%s)\n", args_info.remote_arg, inet_ntoa(remote));
-    }
-  }
-  else {
-    fprintf(stderr, "%s: No remote address given!\n", 
-	    PACKAGE);
-    syslog(LOG_ERR, "No remote address given!");
-    exit(1);
-  }
-
-
-  /* imsi                                                            */
-  if (strlen(args_info.imsi_arg)!=15) {
-    printf("Invalid IMSI\n");
-    exit(1);
-  }
-  imsi.l = 8;
-  imsi.v = imsih;
-  imsi.v[0] = args_info.imsi_arg[0]-48 + (args_info.imsi_arg[1]-48)*16;
-  imsi.v[1] = args_info.imsi_arg[2]-48 + (args_info.imsi_arg[3]-48)*16;
-  imsi.v[2] = args_info.imsi_arg[4]-48 + (args_info.imsi_arg[5]-48)*16;
-  imsi.v[3] = args_info.imsi_arg[6]-48 + (args_info.imsi_arg[7]-48)*16;
-  imsi.v[4] = args_info.imsi_arg[8]-48 + (args_info.imsi_arg[9]-48)*16;
-  imsi.v[5] = args_info.imsi_arg[10]-48 + (args_info.imsi_arg[11]-48)*16;
-  imsi.v[6] = args_info.imsi_arg[12]-48 + (args_info.imsi_arg[13]-48)*16;
-  imsi.v[7] = args_info.imsi_arg[14]-48 + 0*16;
-
-  if (imsi.l > sizeof(imsi3)) {
-    printf("Invalid IMSI\n");
-    exit(1);
-  }
-  else {
-    memcpy(&imsi3, imsi.v, imsi.l);
-    printf("IMSI is:               %s (%#08llx)\n", args_info.imsi_arg, imsi3);
-  }
-
-  /* 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;
-  
-  /* contexts                                                        */
-  if (args_info.contexts_arg>16) {
-    printf("Contexts has to be less than 16\n");
-    exit(1);
-  }
-  contexts = args_info.contexts_arg;
-
-  /* Timelimit                                                       */
-  timelimit = args_info.timelimit_arg;
-  starttime = time(NULL);
-  
-  /* apn                                                             */
-  if (strlen(args_info.apn_arg)>255) {
-    printf("Invalid APN\n");
-    exit(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, 255);
-  printf("Using APN:             %s\n", args_info.apn_arg);
-  
-  /* msisdn                                                          */
-  if (strlen(args_info.msisdn_arg)>255) {
-    printf("Invalid MSISDN\n");
-    exit(1);
-  }
-  msisdn.l = 1;
-  msisdn.v = msisdnh;
-  msisdn.v[0] = 0x91; /* International format */
-  for(n=0; n<strlen(args_info.msisdn_arg); n++) {
-    if ((n%2) == 0) {
-      msisdn.v[((int)n/2)+1] = args_info.msisdn_arg[n] - 48 + 0xf0;
-      msisdn.l += 1;
-    }
-    else {
-      msisdn.v[((int)n/2)+1] = (msisdn.v[((int)n/2)+1] & 0x0f) + (args_info.msisdn_arg[n] - 48) * 16;
-    }
-  }
-  printf("Using MSISDN:          %s\n", args_info.msisdn_arg);
-
-  /* UID and PWD */
-  /* Might need to also insert stuff like DNS etc. */
-  if ((strlen(args_info.uid_arg) + strlen(args_info.pwd_arg) + 10)>255) {
-    printf("invalid UID and PWD\n");
-    exit(1);
-  }
-  pco.l = strlen(args_info.uid_arg) + strlen(args_info.pwd_arg) + 10;
-  pco.v[0] = 0x80; /* PPP */
-  pco.v[1] = 0xc0;
-  pco.v[2] = 0x23; /* PAP */
-  pco.v[3] = 0x12;
-  pco.v[4] = 0x01; /* Authenticate request */
-  pco.v[5] = 0x01;
-  pco.v[6] = 0x00; /* MSB of length */
-  pco.v[7] = strlen(args_info.uid_arg) + strlen(args_info.pwd_arg) + 6;
-  pco.v[8] = strlen(args_info.uid_arg);
-  memcpy(&pco.v[9], args_info.uid_arg, strlen(args_info.uid_arg));
-  pco.v[9+strlen(args_info.uid_arg)] = strlen(args_info.pwd_arg);
-  memcpy(&pco.v[10+strlen(args_info.uid_arg)], args_info.pwd_arg, strlen(args_info.pwd_arg));
-  
-  /* createif */
-  createif = args_info.createif_flag;
-
-  /* ipup */
-  ipup = args_info.ipup_arg;
-
-  /* ipdown */
-  ipdown = args_info.ipdown_arg;
-
-  /* defaultroute 
-  defaultroute = args_info.defaultroute_flag;*/
-
-  /* net                                                          */
-  /* Store net as in_addr                                         */
-  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);
-      exit(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);
-      exit(1);
-    }
-  }
-
-  /* pinghost                                                         */
-  /* Store ping host as in_addr                                   */
-  if (args_info.pinghost_arg) {
-    if (!inet_aton(args_info.pinghost_arg, &pinghost)) {
-      fprintf(stderr, "%s: Invalid ping host: %s!\n", 
-	      PACKAGE, args_info.pinghost_arg);
-      syslog(LOG_ERR, "Invalid ping host: %s!", 
-	     args_info.pinghost_arg);
-      exit(1);
-    }
-  }
-
-  /* Other ping parameters                                        */
-  pingrate = args_info.pingrate_arg;
-  pingsize = args_info.pingsize_arg;
-  pingcount = args_info.pingcount_arg;
-  pingquiet = args_info.pingquiet_flag;
 
   printf("\nInitialising GTP library\n");
-  if ((gtpfd = gtp_new(&gsn, args_info.statedir_arg,  &listen_)) > maxfd)
-    maxfd = gtpfd;
+  if (gtp_new(&gsn, options.statedir,  &options.listen)) {
+    sys_err(LOG_ERR, __FILE__, __LINE__, 0,
+	    "Failed to create gtp");
+    exit(1);
+  }
+  if (gsn->fd > maxfd) maxfd = gsn->fd;
 
-  if ((gtpfd = gtp_fd(gsn)) > maxfd)
-    maxfd = gtpfd;
-    
-  if (createif) 
+  gtp_set_cb_delete_context(gsn, delete_context);
+  gtp_set_cb_conf(gsn, conf);
+  if (options.createif) 
     gtp_set_cb_gpdu(gsn, encaps_tun);
   else
     gtp_set_cb_gpdu(gsn, encaps_ping);
-	
-  gtp_set_cb_delete_context(gsn, delete_context);
-  
-  gtp_set_cb_conf(gsn, conf);
+
+  if (options.createif) {
+    printf("Setting up interface\n");
+    /* 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_set_cb_ind(tun, cb_tun_ind);
+    if (tun->fd > maxfd) maxfd = tun->fd;
+  }
+
+  /* Initialise hash tables */
+  memset(&iphash, 0, sizeof(iphash));  
+  memset(&iparr, 0, sizeof(iparr));  
+
   printf("Done initialising GTP library\n\n");
 
   /* See if anybody is there */
   printf("Sending off echo request\n");
-  if (gtpfd != -1) gtp_echo_req(gsn, &remote); /* See if remote is alive ? */
+  gtp_echo_req(gsn, &options.remote); /* See if remote is alive ? */
 
-  for(n=0; n<contexts; n++) {
+  for(n=0; n<options.contexts; n++) {
     printf("Setting up PDP context #%d\n", n);
+    iparr[n].inuse = 1; /* TODO */
 
-    pdp_newpdp(&pdp[n], imsi3, n, NULL); /* Allocated here. Cleaned up in gtp.c: TODO Should be statically allocated! */
-    
-    /*    
-	  if (qos.l > sizeof(pdp[n]->qos_req.v)) {
-	  exit(1);
-	  }
-	  else {
-	  pdp[n]->qos_req.l = qos.l;
-	  memcpy(pdp[n]->qos_req.v, qos.v, qos.l);
-	  }
-    */
-    memcpy(pdp[n]->qos_req0, qos.v, qos.l); /* TODO range check */
-    
-    pdp[n]->selmode = 0x01; /* MS provided APN, subscription not verified */
-    
-    if (apn.l > sizeof(pdp[n]->apn_use.v)) {
+    /* Allocated here. Cleaned up in gtp.c:*/
+    pdp_newpdp(&pdp, options.imsi, n, NULL); 
+
+    pdp->peer = &iparr[n];
+    pdp->ipif = tun; /* TODO */
+    iparr[n].pdp = pdp;
+
+    if (options.qos.l > sizeof(pdp->qos_req0)) {
+      sys_err(LOG_ERR, __FILE__, __LINE__, 0, "QoS length too big");
       exit(1);
     }
     else {
-      pdp[n]->apn_use.l = apn.l;
-      memcpy(pdp[n]->apn_use.v, apn.v, apn.l);
+      memcpy(pdp->qos_req0, options.qos.v, options.qos.l);
     }
     
-    pdp[n]->gsnlc.l = 4;
-    memcpy(pdp[n]->gsnlc.v, &listen_, 4);
-    pdp[n]->gsnlu.l = 4;
-    memcpy(pdp[n]->gsnlu.v, &listen_, 4);
+    pdp->selmode = 0x01; /* MS provided APN, subscription not verified */
     
-    if (msisdn.l > sizeof(pdp[n]->msisdn.v)) {
+    if (options.apn.l > sizeof(pdp->apn_use.v)) {
+      sys_err(LOG_ERR, __FILE__, __LINE__, 0, "APN length too big");
       exit(1);
     }
     else {
-      pdp[n]->msisdn.l = msisdn.l;
-      memcpy(pdp[n]->msisdn.v, msisdn.v, msisdn.l);
+      pdp->apn_use.l = options.apn.l;
+      memcpy(pdp->apn_use.v, options.apn.v, options.apn.l);
     }
     
-    ipv42eua(&pdp[n]->eua, NULL); /* Request dynamic IP address */
+    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 (pco.l > sizeof(pdp[n]->pco_req.v)) {
+    if (options.msisdn.l > sizeof(pdp->msisdn.v)) {
+      sys_err(LOG_ERR, __FILE__, __LINE__, 0, "MSISDN length too big");
       exit(1);
     }
     else {
-      pdp[n]->pco_req.l = pco.l;
-      memcpy(pdp[n]->pco_req.v, pco.v, pco.l);
+      pdp->msisdn.l = options.msisdn.l;
+      memcpy(pdp->msisdn.v, options.msisdn.v, options.msisdn.l);
+    }
+    
+    ipv42eua(&pdp->eua, NULL); /* Request dynamic IP address */
+    
+    if (options.pco.l > sizeof(pdp->pco_req.v)) {
+      sys_err(LOG_ERR, __FILE__, __LINE__, 0, "PCO length too big");
+      exit(1);
+    }
+    else {
+      pdp->pco_req.l = options.pco.l;
+      memcpy(pdp->pco_req.v, options.pco.v, options.pco.l);
     }
     
     /* Create context */
     /* We send this of once. Retransmissions are handled by gtplib */
-    if (gtpfd != -1) gtp_create_context(gsn, pdp[n], NULL, &remote);
+    gtp_create_context(gsn, pdp, NULL, &options.remote);
   }    
 
   state = 1;  /* Enter wait_connection state */
@@ -974,55 +972,58 @@
   /* Main select loop                                               */
   /******************************************************************/
 
-  while ((((starttime + timelimit + 10) > time(NULL)) 
-	 || (0 == timelimit)) && (state!=0)) {
+  while ((((starttime + options.timelimit + 10) > time(NULL)) 
+	 || (0 == options.timelimit)) && (state!=0)) {
 
     /* Take down client connections at some stage */
-    if (((starttime + timelimit) <= time(NULL)) && (0 != timelimit) && (2 == state)) {
+    if (((starttime + options.timelimit) <= time(NULL)) &&
+	(0 != options.timelimit) && (2 == state)) {
       state = 3;
-      for(n=0; n<contexts; n++) {
+      for(n=0; n<options.contexts; n++) {
 	/* Delete context */
 	printf("Disconnecting PDP context #%d\n", n);
-	if (gtpfd != -1) gtp_delete_context(gsn, pdp[n], NULL);
-	if ((pinghost.s_addr !=0) && ntransmitted) ping_finish();
+	gtp_delete_context(gsn, iparr[n].pdp, NULL);
+	if ((options.pinghost.s_addr !=0) && ntransmitted) ping_finish();
       }
     }
-
-
+    
+    diff = 0;
+    while (( diff<=0 ) && 
     /* Send off an ICMP ping packet */
-    if ((pinghost.s_addr) && (2 == state) && 
-	((pingseq < pingcount) || (pingcount == 0))) {
+	   /*if (*/(options.pinghost.s_addr) && (2 == state) && 
+	((pingseq < options.pingcount) || (options.pingcount == 0))) {
       if (!pingseq) gettimeofday(&firstping, &tz); /* Set time of first ping */
       gettimeofday(&tv, &tz);
-      diff = 1000000 / pingrate * pingseq -
+      diff = 1000000 / options.pingrate * pingseq -
 	1000000 * (tv.tv_sec - firstping.tv_sec) -
 	(tv.tv_usec - firstping.tv_usec); /* Microseconds safe up to 500 sec */
       if (diff <=0) {
-	if (debug) printf("Create_ping %d\n", diff);
-	create_ping(gsn, pdp[pingseq % contexts],
-		    &pinghost, pingseq, pingsize);
+	if (options.debug) printf("Create_ping %d\n", diff);
+	create_ping(gsn, iparr[pingseq % options.contexts].pdp,
+		    &options.pinghost, pingseq, options.pingsize);
 	pingseq++;
       }
     }
+    
 
-    if (ntransmitted && pingcount && nreceived >= pingcount)
+    if (ntransmitted && options.pingcount && nreceived >= options.pingcount)
       ping_finish();
-
+    
 
     FD_ZERO(&fds);
-    if (tun_fd1 != -1) FD_SET(tun_fd1, &fds);
-    if (tun_fd2 != -1) FD_SET(tun_fd2, &fds);
-    if (gtpfd != -1) FD_SET(gtpfd, &fds);
+    if (tun) FD_SET(tun->fd, &fds);
+    FD_SET(gsn->fd, &fds);
     
     gtp_retranstimeout(gsn, &idleTime);
     ping_timeout(&idleTime);
-
-    if (debug) printf("idletime.tv_sec %d, idleTime.tv_usec %d\n",
-		      (int) idleTime.tv_sec, (int) idleTime.tv_usec);
-
+    
+    if (options.debug) printf("idletime.tv_sec %d, idleTime.tv_usec %d\n",
+			      (int) idleTime.tv_sec, (int) idleTime.tv_usec);
+    
     switch (select(maxfd + 1, &fds, NULL, NULL, &idleTime)) {
     case -1:
-      syslog(LOG_ERR, "sgsnemu: select = -1");
+      sys_err(LOG_ERR, __FILE__, __LINE__, 0,
+	      "Select returned -1");
       break;  
     case 0:
       gtp_retrans(gsn); /* Only retransmit if nothing else */
@@ -1030,30 +1031,22 @@
     default:
       break;
     }
-
-    if (tun_fd1 != -1 && 
-	FD_ISSET(tun_fd1, &fds) && 
-	tun_decaps(tun1, encaps_gtp_client, gsn) < 0) {
-      syslog(LOG_ERR, "TUN read failed (fd)=(%d)", tun_fd1);
-    }
-
-    if (tun_fd2 != -1 && 
-	FD_ISSET(tun_fd2, &fds) && 
-	tun_decaps(tun2, encaps_gtp_client, gsn) < 0) {
-      syslog(LOG_ERR, "TUN read failed (fd)=(%d)", tun_fd2);
-    }
-
-    if (gtpfd != -1 && FD_ISSET(gtpfd, &fds) && 
-	gtp_decaps(gsn) < 0) {
-      syslog(LOG_ERR, "GTP read failed (gtpfd)=(%d)", gtpfd);
+    
+    if ((tun) && FD_ISSET(tun->fd, &fds) && tun_decaps(tun) < 0) {
+      sys_err(LOG_ERR, __FILE__, __LINE__, 0,
+	      "TUN decaps failed");
     }
     
+    if (FD_ISSET(gsn->fd, &fds))
+      gtp_decaps(gsn);
     
   }
-
+  
   gtp_free(gsn); /* Clean up the gsn instance */
   
-  return 0;
+  if (options.createif)
+    tun_free(tun);
   
+  return 0; 
 }
 
diff --git a/sgsnemu/tun.c b/sgsnemu/tun.c
index 72ea264..5b8cc95 100644
--- a/sgsnemu/tun.c
+++ b/sgsnemu/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/sgsnemu/tun.h b/sgsnemu/tun.h
index 03dc7df..8d4618d 100644
--- a/sgsnemu/tun.h
+++ b/sgsnemu/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 */