Initial revision
diff --git a/gtp/.deps/gtp.P b/gtp/.deps/gtp.P
new file mode 100644
index 0000000..bb8ee3c
--- /dev/null
+++ b/gtp/.deps/gtp.P
@@ -0,0 +1,111 @@
+gtp.lo gtp.o : gtp.c /usr/include/syslog.h /usr/include/sys/syslog.h \
+  /usr/include/features.h /usr/include/sys/cdefs.h \
+  /usr/include/gnu/stubs.h \
+  /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stdarg.h \
+  /usr/include/stdio.h \
+  /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stddef.h \
+  /usr/include/bits/types.h /usr/include/bits/pthreadtypes.h \
+  /usr/include/bits/sched.h /usr/include/libio.h /usr/include/_G_config.h \
+  /usr/include/wchar.h /usr/include/bits/wchar.h /usr/include/gconv.h \
+  /usr/include/bits/stdio_lim.h /usr/include/bits/stdio.h \
+  /usr/include/stdlib.h /usr/include/bits/waitflags.h \
+  /usr/include/bits/waitstatus.h /usr/include/endian.h \
+  /usr/include/bits/endian.h /usr/include/xlocale.h \
+  /usr/include/sys/types.h /usr/include/time.h /usr/include/sys/select.h \
+  /usr/include/bits/select.h /usr/include/bits/sigset.h \
+  /usr/include/bits/time.h /usr/include/sys/sysmacros.h \
+  /usr/include/alloca.h /usr/include/sys/time.h /usr/include/sys/socket.h \
+  /usr/include/sys/uio.h /usr/include/bits/uio.h \
+  /usr/include/bits/socket.h \
+  /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/limits.h \
+  /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/syslimits.h \
+  /usr/include/limits.h /usr/include/bits/posix1_lim.h \
+  /usr/include/bits/local_lim.h /usr/include/linux/limits.h \
+  /usr/include/bits/posix2_lim.h /usr/include/bits/xopen_lim.h \
+  /usr/include/bits/wordsize.h /usr/include/bits/sockaddr.h \
+  /usr/include/asm/socket.h /usr/include/asm/sockios.h \
+  /usr/include/netinet/in.h /usr/include/stdint.h /usr/include/bits/in.h \
+  /usr/include/bits/byteswap.h /usr/include/arpa/inet.h \
+  /usr/include/sys/stat.h /usr/include/bits/stat.h /usr/include/unistd.h \
+  /usr/include/bits/posix_opt.h /usr/include/bits/environments.h \
+  /usr/include/bits/confname.h /usr/include/getopt.h \
+  /usr/include/string.h /usr/include/bits/string.h \
+  /usr/include/bits/string2.h /usr/include/errno.h \
+  /usr/include/bits/errno.h /usr/include/linux/errno.h \
+  /usr/include/asm/errno.h /usr/include/fcntl.h /usr/include/bits/fcntl.h \
+  pdp.h gtp.h gtpie.h queue.h
+gtp.c :
+/usr/include/syslog.h :
+/usr/include/sys/syslog.h :
+/usr/include/features.h :
+/usr/include/sys/cdefs.h :
+/usr/include/gnu/stubs.h :
+/usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stdarg.h :
+/usr/include/stdio.h :
+/usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stddef.h :
+/usr/include/bits/types.h :
+/usr/include/bits/pthreadtypes.h :
+/usr/include/bits/sched.h :
+/usr/include/libio.h :
+/usr/include/_G_config.h :
+/usr/include/wchar.h :
+/usr/include/bits/wchar.h :
+/usr/include/gconv.h :
+/usr/include/bits/stdio_lim.h :
+/usr/include/bits/stdio.h :
+/usr/include/stdlib.h :
+/usr/include/bits/waitflags.h :
+/usr/include/bits/waitstatus.h :
+/usr/include/endian.h :
+/usr/include/bits/endian.h :
+/usr/include/xlocale.h :
+/usr/include/sys/types.h :
+/usr/include/time.h :
+/usr/include/sys/select.h :
+/usr/include/bits/select.h :
+/usr/include/bits/sigset.h :
+/usr/include/bits/time.h :
+/usr/include/sys/sysmacros.h :
+/usr/include/alloca.h :
+/usr/include/sys/time.h :
+/usr/include/sys/socket.h :
+/usr/include/sys/uio.h :
+/usr/include/bits/uio.h :
+/usr/include/bits/socket.h :
+/usr/lib/gcc-lib/i386-redhat-linux/2.96/include/limits.h :
+/usr/lib/gcc-lib/i386-redhat-linux/2.96/include/syslimits.h :
+/usr/include/limits.h :
+/usr/include/bits/posix1_lim.h :
+/usr/include/bits/local_lim.h :
+/usr/include/linux/limits.h :
+/usr/include/bits/posix2_lim.h :
+/usr/include/bits/xopen_lim.h :
+/usr/include/bits/wordsize.h :
+/usr/include/bits/sockaddr.h :
+/usr/include/asm/socket.h :
+/usr/include/asm/sockios.h :
+/usr/include/netinet/in.h :
+/usr/include/stdint.h :
+/usr/include/bits/in.h :
+/usr/include/bits/byteswap.h :
+/usr/include/arpa/inet.h :
+/usr/include/sys/stat.h :
+/usr/include/bits/stat.h :
+/usr/include/unistd.h :
+/usr/include/bits/posix_opt.h :
+/usr/include/bits/environments.h :
+/usr/include/bits/confname.h :
+/usr/include/getopt.h :
+/usr/include/string.h :
+/usr/include/bits/string.h :
+/usr/include/bits/string2.h :
+/usr/include/errno.h :
+/usr/include/bits/errno.h :
+/usr/include/linux/errno.h :
+/usr/include/asm/errno.h :
+/usr/include/fcntl.h :
+/usr/include/bits/fcntl.h :
+pdp.h :
+gtp.h :
+gtpie.h :
+queue.h :
diff --git a/gtp/.deps/gtpie.P b/gtp/.deps/gtpie.P
new file mode 100644
index 0000000..646efea
--- /dev/null
+++ b/gtp/.deps/gtpie.P
@@ -0,0 +1,52 @@
+gtpie.lo gtpie.o : gtpie.c /usr/include/stdio.h /usr/include/features.h \
+  /usr/include/sys/cdefs.h /usr/include/gnu/stubs.h \
+  /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stddef.h \
+  /usr/include/bits/types.h /usr/include/libio.h /usr/include/_G_config.h \
+  /usr/include/wchar.h /usr/include/bits/wchar.h /usr/include/gconv.h \
+  /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stdarg.h \
+  /usr/include/bits/stdio_lim.h /usr/include/bits/stdio.h \
+  /usr/include/sys/types.h /usr/include/time.h /usr/include/netinet/in.h \
+  /usr/include/stdint.h /usr/include/bits/wordsize.h \
+  /usr/include/bits/socket.h \
+  /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/limits.h \
+  /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/syslimits.h \
+  /usr/include/limits.h /usr/include/bits/sockaddr.h \
+  /usr/include/asm/socket.h /usr/include/asm/sockios.h \
+  /usr/include/bits/in.h /usr/include/endian.h /usr/include/bits/endian.h \
+  /usr/include/bits/byteswap.h /usr/include/string.h \
+  /usr/include/bits/string.h /usr/include/bits/string2.h gtpie.h
+gtpie.c :
+/usr/include/stdio.h :
+/usr/include/features.h :
+/usr/include/sys/cdefs.h :
+/usr/include/gnu/stubs.h :
+/usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stddef.h :
+/usr/include/bits/types.h :
+/usr/include/libio.h :
+/usr/include/_G_config.h :
+/usr/include/wchar.h :
+/usr/include/bits/wchar.h :
+/usr/include/gconv.h :
+/usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stdarg.h :
+/usr/include/bits/stdio_lim.h :
+/usr/include/bits/stdio.h :
+/usr/include/sys/types.h :
+/usr/include/time.h :
+/usr/include/netinet/in.h :
+/usr/include/stdint.h :
+/usr/include/bits/wordsize.h :
+/usr/include/bits/socket.h :
+/usr/lib/gcc-lib/i386-redhat-linux/2.96/include/limits.h :
+/usr/lib/gcc-lib/i386-redhat-linux/2.96/include/syslimits.h :
+/usr/include/limits.h :
+/usr/include/bits/sockaddr.h :
+/usr/include/asm/socket.h :
+/usr/include/asm/sockios.h :
+/usr/include/bits/in.h :
+/usr/include/endian.h :
+/usr/include/bits/endian.h :
+/usr/include/bits/byteswap.h :
+/usr/include/string.h :
+/usr/include/bits/string.h :
+/usr/include/bits/string2.h :
+gtpie.h :
diff --git a/gtp/.deps/lookupa.P b/gtp/.deps/lookupa.P
new file mode 100644
index 0000000..046263b
--- /dev/null
+++ b/gtp/.deps/lookupa.P
@@ -0,0 +1,3 @@
+lookupa.lo lookupa.o : lookupa.c lookupa.h
+lookupa.c :
+lookupa.h :
diff --git a/gtp/.deps/pdp.P b/gtp/.deps/pdp.P
new file mode 100644
index 0000000..1231e52
--- /dev/null
+++ b/gtp/.deps/pdp.P
@@ -0,0 +1,53 @@
+pdp.lo pdp.o : pdp.c /usr/include/stdio.h /usr/include/features.h \
+  /usr/include/sys/cdefs.h /usr/include/gnu/stubs.h \
+  /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stddef.h \
+  /usr/include/bits/types.h /usr/include/libio.h /usr/include/_G_config.h \
+  /usr/include/wchar.h /usr/include/bits/wchar.h /usr/include/gconv.h \
+  /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stdarg.h \
+  /usr/include/bits/stdio_lim.h /usr/include/bits/stdio.h \
+  /usr/include/sys/types.h /usr/include/time.h /usr/include/netinet/in.h \
+  /usr/include/stdint.h /usr/include/bits/wordsize.h \
+  /usr/include/bits/socket.h \
+  /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/limits.h \
+  /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/syslimits.h \
+  /usr/include/limits.h /usr/include/bits/sockaddr.h \
+  /usr/include/asm/socket.h /usr/include/asm/sockios.h \
+  /usr/include/bits/in.h /usr/include/endian.h /usr/include/bits/endian.h \
+  /usr/include/bits/byteswap.h /usr/include/string.h \
+  /usr/include/bits/string.h /usr/include/bits/string2.h pdp.h lookupa.h
+pdp.c :
+/usr/include/stdio.h :
+/usr/include/features.h :
+/usr/include/sys/cdefs.h :
+/usr/include/gnu/stubs.h :
+/usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stddef.h :
+/usr/include/bits/types.h :
+/usr/include/libio.h :
+/usr/include/_G_config.h :
+/usr/include/wchar.h :
+/usr/include/bits/wchar.h :
+/usr/include/gconv.h :
+/usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stdarg.h :
+/usr/include/bits/stdio_lim.h :
+/usr/include/bits/stdio.h :
+/usr/include/sys/types.h :
+/usr/include/time.h :
+/usr/include/netinet/in.h :
+/usr/include/stdint.h :
+/usr/include/bits/wordsize.h :
+/usr/include/bits/socket.h :
+/usr/lib/gcc-lib/i386-redhat-linux/2.96/include/limits.h :
+/usr/lib/gcc-lib/i386-redhat-linux/2.96/include/syslimits.h :
+/usr/include/limits.h :
+/usr/include/bits/sockaddr.h :
+/usr/include/asm/socket.h :
+/usr/include/asm/sockios.h :
+/usr/include/bits/in.h :
+/usr/include/endian.h :
+/usr/include/bits/endian.h :
+/usr/include/bits/byteswap.h :
+/usr/include/string.h :
+/usr/include/bits/string.h :
+/usr/include/bits/string2.h :
+pdp.h :
+lookupa.h :
diff --git a/gtp/.deps/queue.P b/gtp/.deps/queue.P
new file mode 100644
index 0000000..04bbd67
--- /dev/null
+++ b/gtp/.deps/queue.P
@@ -0,0 +1,64 @@
+queue.lo queue.o : queue.c /usr/include/stdlib.h /usr/include/features.h \
+  /usr/include/sys/cdefs.h /usr/include/gnu/stubs.h \
+  /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stddef.h \
+  /usr/include/stdio.h /usr/include/bits/types.h /usr/include/libio.h \
+  /usr/include/_G_config.h /usr/include/wchar.h /usr/include/bits/wchar.h \
+  /usr/include/gconv.h \
+  /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stdarg.h \
+  /usr/include/bits/stdio_lim.h /usr/include/bits/stdio.h \
+  /usr/include/sys/types.h /usr/include/time.h /usr/include/sys/time.h \
+  /usr/include/bits/time.h /usr/include/sys/select.h \
+  /usr/include/bits/select.h /usr/include/bits/sigset.h \
+  /usr/include/netinet/in.h /usr/include/stdint.h \
+  /usr/include/bits/wordsize.h /usr/include/bits/socket.h \
+  /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/limits.h \
+  /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/syslimits.h \
+  /usr/include/limits.h /usr/include/bits/sockaddr.h \
+  /usr/include/asm/socket.h /usr/include/asm/sockios.h \
+  /usr/include/bits/in.h /usr/include/endian.h /usr/include/bits/endian.h \
+  /usr/include/bits/byteswap.h /usr/include/string.h \
+  /usr/include/bits/string.h /usr/include/bits/string2.h pdp.h gtp.h \
+  queue.h
+queue.c :
+/usr/include/stdlib.h :
+/usr/include/features.h :
+/usr/include/sys/cdefs.h :
+/usr/include/gnu/stubs.h :
+/usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stddef.h :
+/usr/include/stdio.h :
+/usr/include/bits/types.h :
+/usr/include/libio.h :
+/usr/include/_G_config.h :
+/usr/include/wchar.h :
+/usr/include/bits/wchar.h :
+/usr/include/gconv.h :
+/usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stdarg.h :
+/usr/include/bits/stdio_lim.h :
+/usr/include/bits/stdio.h :
+/usr/include/sys/types.h :
+/usr/include/time.h :
+/usr/include/sys/time.h :
+/usr/include/bits/time.h :
+/usr/include/sys/select.h :
+/usr/include/bits/select.h :
+/usr/include/bits/sigset.h :
+/usr/include/netinet/in.h :
+/usr/include/stdint.h :
+/usr/include/bits/wordsize.h :
+/usr/include/bits/socket.h :
+/usr/lib/gcc-lib/i386-redhat-linux/2.96/include/limits.h :
+/usr/lib/gcc-lib/i386-redhat-linux/2.96/include/syslimits.h :
+/usr/include/limits.h :
+/usr/include/bits/sockaddr.h :
+/usr/include/asm/socket.h :
+/usr/include/asm/sockios.h :
+/usr/include/bits/in.h :
+/usr/include/endian.h :
+/usr/include/bits/endian.h :
+/usr/include/bits/byteswap.h :
+/usr/include/string.h :
+/usr/include/bits/string.h :
+/usr/include/bits/string2.h :
+pdp.h :
+gtp.h :
+queue.h :
diff --git a/gtp/Makefile b/gtp/Makefile
new file mode 100644
index 0000000..535c07d
--- /dev/null
+++ b/gtp/Makefile
@@ -0,0 +1,339 @@
+# Generated automatically from Makefile.in by configure.
+# Makefile.in generated automatically by automake 1.4 from Makefile.am
+
+# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+
+SHELL = /bin/sh
+
+srcdir = .
+top_srcdir = ..
+prefix = /usr/local
+exec_prefix = ${prefix}
+
+bindir = ${exec_prefix}/bin
+sbindir = ${exec_prefix}/sbin
+libexecdir = ${exec_prefix}/libexec
+datadir = ${prefix}/share
+sysconfdir = ${prefix}/etc
+sharedstatedir = ${prefix}/com
+localstatedir = ${prefix}/var
+libdir = ${exec_prefix}/lib
+infodir = ${prefix}/info
+mandir = ${prefix}/man
+includedir = ${prefix}/include
+oldincludedir = /usr/include
+
+DESTDIR =
+
+pkgdatadir = $(datadir)/OpenGGSN
+pkglibdir = $(libdir)/OpenGGSN
+pkgincludedir = $(includedir)/OpenGGSN
+
+top_builddir = ..
+
+ACLOCAL = aclocal
+AUTOCONF = autoconf
+AUTOMAKE = automake
+AUTOHEADER = autoheader
+
+INSTALL = /usr/bin/install -c
+INSTALL_PROGRAM = ${INSTALL} $(AM_INSTALL_PROGRAM_FLAGS)
+INSTALL_DATA = ${INSTALL} -m 644
+INSTALL_SCRIPT = ${INSTALL_PROGRAM}
+transform = s,x,x,
+
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+host_alias = i686-pc-linux
+host_triplet = i686-pc-linux-gnu
+AS = @AS@
+AWK = gawk
+CC = gcc
+DLLTOOL = @DLLTOOL@
+LIBTOOL = $(SHELL) $(top_builddir)/libtool
+LN_S = ln -s
+MAKEINFO = makeinfo
+OBJDUMP = @OBJDUMP@
+PACKAGE = OpenGGSN
+RANLIB = ranlib
+VERSION = 0.5
+
+lib_LTLIBRARIES = libgtp.la
+
+CFLAGS = -O2 -fno-builtin -Wall -ansi -DSBINDIR='"$(sbindir)"'
+
+libgtp_la_SOURCES = gtp.c gtp.h gtpie.c gtpie.h pdp.c pdp.h lookupa.c lookupa.h queue.c queue.h
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_CLEAN_FILES = 
+LTLIBRARIES =  $(lib_LTLIBRARIES)
+
+
+DEFS =  -DSTDC_HEADERS=1 -DHAVE_SYS_WAIT_H=1 -DHAVE_FCNTL_H=1 -DHAVE_STRINGS_H=1 -DHAVE_SYS_IOCTL_H=1 -DHAVE_SYS_TIME_H=1 -DHAVE_SYSLOG_H=1 -DHAVE_UNISTD_H=1 -DTIME_WITH_SYS_TIME=1 -DHAVE_SELECT=1 -DHAVE_SOCKET=1 -DHAVE_STRDUP=1 -DHAVE_STRERROR=1 -DHAVE_STRTOUL=1 -DPACKAGE=\"OpenGGSN\" -DVERSION=\"0.5\"  -I. -I$(srcdir) 
+CPPFLAGS = 
+LDFLAGS = 
+LIBS = 
+libgtp_la_LDFLAGS = 
+libgtp_la_LIBADD = 
+libgtp_la_OBJECTS =  gtp.lo gtpie.lo pdp.lo lookupa.lo queue.lo
+COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@
+DIST_COMMON =  Makefile.am Makefile.in
+
+
+DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST)
+
+TAR = gtar
+GZIP_ENV = --best
+DEP_FILES =  .deps/gtp.P .deps/gtpie.P .deps/lookupa.P .deps/pdp.P \
+.deps/queue.P
+SOURCES = $(libgtp_la_SOURCES)
+OBJECTS = $(libgtp_la_OBJECTS)
+
+all: all-redirect
+.SUFFIXES:
+.SUFFIXES: .S .c .lo .o .s
+$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) 
+	cd $(top_srcdir) && $(AUTOMAKE) --gnu gtp/Makefile
+
+Makefile: $(srcdir)/Makefile.in  $(top_builddir)/config.status $(BUILT_SOURCES)
+	cd $(top_builddir) \
+	  && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status
+
+
+mostlyclean-libLTLIBRARIES:
+
+clean-libLTLIBRARIES:
+	-test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+
+distclean-libLTLIBRARIES:
+
+maintainer-clean-libLTLIBRARIES:
+
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+	@$(NORMAL_INSTALL)
+	$(mkinstalldirs) $(DESTDIR)$(libdir)
+	@list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+	  if test -f $$p; then \
+	    echo "$(LIBTOOL)  --mode=install $(INSTALL) $$p $(DESTDIR)$(libdir)/$$p"; \
+	    $(LIBTOOL)  --mode=install $(INSTALL) $$p $(DESTDIR)$(libdir)/$$p; \
+	  else :; fi; \
+	done
+
+uninstall-libLTLIBRARIES:
+	@$(NORMAL_UNINSTALL)
+	list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+	  $(LIBTOOL)  --mode=uninstall rm -f $(DESTDIR)$(libdir)/$$p; \
+	done
+
+.s.o:
+	$(COMPILE) -c $<
+
+.S.o:
+	$(COMPILE) -c $<
+
+mostlyclean-compile:
+	-rm -f *.o core *.core
+
+clean-compile:
+
+distclean-compile:
+	-rm -f *.tab.c
+
+maintainer-clean-compile:
+
+.s.lo:
+	$(LIBTOOL) --mode=compile $(COMPILE) -c $<
+
+.S.lo:
+	$(LIBTOOL) --mode=compile $(COMPILE) -c $<
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+distclean-libtool:
+
+maintainer-clean-libtool:
+
+libgtp.la: $(libgtp_la_OBJECTS) $(libgtp_la_DEPENDENCIES)
+	$(LINK) -rpath $(libdir) $(libgtp_la_LDFLAGS) $(libgtp_la_OBJECTS) $(libgtp_la_LIBADD) $(LIBS)
+
+tags: TAGS
+
+ID: $(HEADERS) $(SOURCES) $(LISP)
+	list='$(SOURCES) $(HEADERS)'; \
+	unique=`for i in $$list; do echo $$i; done | \
+	  awk '    { files[$$0] = 1; } \
+	       END { for (i in files) print i; }'`; \
+	here=`pwd` && cd $(srcdir) \
+	  && mkid -f$$here/ID $$unique $(LISP)
+
+TAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) $(LISP)
+	tags=; \
+	here=`pwd`; \
+	list='$(SOURCES) $(HEADERS)'; \
+	unique=`for i in $$list; do echo $$i; done | \
+	  awk '    { files[$$0] = 1; } \
+	       END { for (i in files) print i; }'`; \
+	test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \
+	  || (cd $(srcdir) && etags $(ETAGS_ARGS) $$tags  $$unique $(LISP) -o $$here/TAGS)
+
+mostlyclean-tags:
+
+clean-tags:
+
+distclean-tags:
+	-rm -f TAGS ID
+
+maintainer-clean-tags:
+
+distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir)
+
+subdir = gtp
+
+distdir: $(DISTFILES)
+	here=`cd $(top_builddir) && pwd`; \
+	top_distdir=`cd $(top_distdir) && pwd`; \
+	distdir=`cd $(distdir) && pwd`; \
+	cd $(top_srcdir) \
+	  && $(AUTOMAKE) --include-deps --build-dir=$$here --srcdir-name=$(top_srcdir) --output-dir=$$top_distdir --gnu gtp/Makefile
+	@for file in $(DISTFILES); do \
+	  d=$(srcdir); \
+	  if test -d $$d/$$file; then \
+	    cp -pr $$d/$$file $(distdir)/$$file; \
+	  else \
+	    test -f $(distdir)/$$file \
+	    || ln $$d/$$file $(distdir)/$$file 2> /dev/null \
+	    || cp -p $$d/$$file $(distdir)/$$file || :; \
+	  fi; \
+	done
+
+DEPS_MAGIC := $(shell mkdir .deps > /dev/null 2>&1 || :)
+
+-include $(DEP_FILES)
+
+mostlyclean-depend:
+
+clean-depend:
+
+distclean-depend:
+	-rm -rf .deps
+
+maintainer-clean-depend:
+
+%.o: %.c
+	@echo '$(COMPILE) -c $<'; \
+	$(COMPILE) -Wp,-MD,.deps/$(*F).pp -c $<
+	@-cp .deps/$(*F).pp .deps/$(*F).P; \
+	tr ' ' '\012' < .deps/$(*F).pp \
+	  | sed -e 's/^\\$$//' -e '/^$$/ d' -e '/:$$/ d' -e 's/$$/ :/' \
+	    >> .deps/$(*F).P; \
+	rm .deps/$(*F).pp
+
+%.lo: %.c
+	@echo '$(LTCOMPILE) -c $<'; \
+	$(LTCOMPILE) -Wp,-MD,.deps/$(*F).pp -c $<
+	@-sed -e 's/^\([^:]*\)\.o[ 	]*:/\1.lo \1.o :/' \
+	  < .deps/$(*F).pp > .deps/$(*F).P; \
+	tr ' ' '\012' < .deps/$(*F).pp \
+	  | sed -e 's/^\\$$//' -e '/^$$/ d' -e '/:$$/ d' -e 's/$$/ :/' \
+	    >> .deps/$(*F).P; \
+	rm -f .deps/$(*F).pp
+info-am:
+info: info-am
+dvi-am:
+dvi: dvi-am
+check-am: all-am
+check: check-am
+installcheck-am:
+installcheck: installcheck-am
+install-exec-am: install-libLTLIBRARIES
+install-exec: install-exec-am
+
+install-data-am:
+install-data: install-data-am
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+install: install-am
+uninstall-am: uninstall-libLTLIBRARIES
+uninstall: uninstall-am
+all-am: Makefile $(LTLIBRARIES)
+all-redirect: all-am
+install-strip:
+	$(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install
+installdirs:
+	$(mkinstalldirs)  $(DESTDIR)$(libdir)
+
+
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+	-rm -f Makefile $(CONFIG_CLEAN_FILES)
+	-rm -f config.cache config.log stamp-h stamp-h[0-9]*
+
+maintainer-clean-generic:
+mostlyclean-am:  mostlyclean-libLTLIBRARIES mostlyclean-compile \
+		mostlyclean-libtool mostlyclean-tags mostlyclean-depend \
+		mostlyclean-generic
+
+mostlyclean: mostlyclean-am
+
+clean-am:  clean-libLTLIBRARIES clean-compile clean-libtool clean-tags \
+		clean-depend clean-generic mostlyclean-am
+
+clean: clean-am
+
+distclean-am:  distclean-libLTLIBRARIES distclean-compile \
+		distclean-libtool distclean-tags distclean-depend \
+		distclean-generic clean-am
+	-rm -f libtool
+
+distclean: distclean-am
+
+maintainer-clean-am:  maintainer-clean-libLTLIBRARIES \
+		maintainer-clean-compile maintainer-clean-libtool \
+		maintainer-clean-tags maintainer-clean-depend \
+		maintainer-clean-generic distclean-am
+	@echo "This command is intended for maintainers to use;"
+	@echo "it deletes files that may require special tools to rebuild."
+
+maintainer-clean: maintainer-clean-am
+
+.PHONY: mostlyclean-libLTLIBRARIES distclean-libLTLIBRARIES \
+clean-libLTLIBRARIES maintainer-clean-libLTLIBRARIES \
+uninstall-libLTLIBRARIES install-libLTLIBRARIES mostlyclean-compile \
+distclean-compile clean-compile maintainer-clean-compile \
+mostlyclean-libtool distclean-libtool clean-libtool \
+maintainer-clean-libtool tags mostlyclean-tags distclean-tags \
+clean-tags maintainer-clean-tags distdir mostlyclean-depend \
+distclean-depend clean-depend maintainer-clean-depend info-am info \
+dvi-am dvi check check-am installcheck-am installcheck install-exec-am \
+install-exec install-data-am install-data install-am install \
+uninstall-am uninstall all-redirect all-am all installdirs \
+mostlyclean-generic distclean-generic clean-generic \
+maintainer-clean-generic clean mostlyclean distclean maintainer-clean
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/gtp/Makefile.am b/gtp/Makefile.am
new file mode 100644
index 0000000..88131cc
--- /dev/null
+++ b/gtp/Makefile.am
@@ -0,0 +1,9 @@
+lib_LTLIBRARIES = libgtp.la
+
+CFLAGS = -O2 -fno-builtin -Wall -ansi -DSBINDIR='"$(sbindir)"'
+
+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
new file mode 100644
index 0000000..0dbace6
--- /dev/null
+++ b/gtp/Makefile.in
@@ -0,0 +1,339 @@
+# Makefile.in generated automatically by automake 1.4 from Makefile.am
+
+# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+
+SHELL = @SHELL@
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+
+bindir = @bindir@
+sbindir = @sbindir@
+libexecdir = @libexecdir@
+datadir = @datadir@
+sysconfdir = @sysconfdir@
+sharedstatedir = @sharedstatedir@
+localstatedir = @localstatedir@
+libdir = @libdir@
+infodir = @infodir@
+mandir = @mandir@
+includedir = @includedir@
+oldincludedir = /usr/include
+
+DESTDIR =
+
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+
+top_builddir = ..
+
+ACLOCAL = @ACLOCAL@
+AUTOCONF = @AUTOCONF@
+AUTOMAKE = @AUTOMAKE@
+AUTOHEADER = @AUTOHEADER@
+
+INSTALL = @INSTALL@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS)
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+transform = @program_transform_name@
+
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+host_alias = @host_alias@
+host_triplet = @host@
+AS = @AS@
+AWK = @AWK@
+CC = @CC@
+DLLTOOL = @DLLTOOL@
+LIBTOOL = @LIBTOOL@
+LN_S = @LN_S@
+MAKEINFO = @MAKEINFO@
+OBJDUMP = @OBJDUMP@
+PACKAGE = @PACKAGE@
+RANLIB = @RANLIB@
+VERSION = @VERSION@
+
+lib_LTLIBRARIES = libgtp.la
+
+CFLAGS = -O2 -fno-builtin -Wall -ansi -DSBINDIR='"$(sbindir)"'
+
+libgtp_la_SOURCES = gtp.c gtp.h gtpie.c gtpie.h pdp.c pdp.h lookupa.c lookupa.h queue.c queue.h
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_CLEAN_FILES = 
+LTLIBRARIES =  $(lib_LTLIBRARIES)
+
+
+DEFS = @DEFS@ -I. -I$(srcdir) 
+CPPFLAGS = @CPPFLAGS@
+LDFLAGS = @LDFLAGS@
+LIBS = @LIBS@
+libgtp_la_LDFLAGS = 
+libgtp_la_LIBADD = 
+libgtp_la_OBJECTS =  gtp.lo gtpie.lo pdp.lo lookupa.lo queue.lo
+COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@
+DIST_COMMON =  Makefile.am Makefile.in
+
+
+DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST)
+
+TAR = gtar
+GZIP_ENV = --best
+DEP_FILES =  .deps/gtp.P .deps/gtpie.P .deps/lookupa.P .deps/pdp.P \
+.deps/queue.P
+SOURCES = $(libgtp_la_SOURCES)
+OBJECTS = $(libgtp_la_OBJECTS)
+
+all: all-redirect
+.SUFFIXES:
+.SUFFIXES: .S .c .lo .o .s
+$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) 
+	cd $(top_srcdir) && $(AUTOMAKE) --gnu gtp/Makefile
+
+Makefile: $(srcdir)/Makefile.in  $(top_builddir)/config.status $(BUILT_SOURCES)
+	cd $(top_builddir) \
+	  && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status
+
+
+mostlyclean-libLTLIBRARIES:
+
+clean-libLTLIBRARIES:
+	-test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+
+distclean-libLTLIBRARIES:
+
+maintainer-clean-libLTLIBRARIES:
+
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+	@$(NORMAL_INSTALL)
+	$(mkinstalldirs) $(DESTDIR)$(libdir)
+	@list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+	  if test -f $$p; then \
+	    echo "$(LIBTOOL)  --mode=install $(INSTALL) $$p $(DESTDIR)$(libdir)/$$p"; \
+	    $(LIBTOOL)  --mode=install $(INSTALL) $$p $(DESTDIR)$(libdir)/$$p; \
+	  else :; fi; \
+	done
+
+uninstall-libLTLIBRARIES:
+	@$(NORMAL_UNINSTALL)
+	list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+	  $(LIBTOOL)  --mode=uninstall rm -f $(DESTDIR)$(libdir)/$$p; \
+	done
+
+.s.o:
+	$(COMPILE) -c $<
+
+.S.o:
+	$(COMPILE) -c $<
+
+mostlyclean-compile:
+	-rm -f *.o core *.core
+
+clean-compile:
+
+distclean-compile:
+	-rm -f *.tab.c
+
+maintainer-clean-compile:
+
+.s.lo:
+	$(LIBTOOL) --mode=compile $(COMPILE) -c $<
+
+.S.lo:
+	$(LIBTOOL) --mode=compile $(COMPILE) -c $<
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+distclean-libtool:
+
+maintainer-clean-libtool:
+
+libgtp.la: $(libgtp_la_OBJECTS) $(libgtp_la_DEPENDENCIES)
+	$(LINK) -rpath $(libdir) $(libgtp_la_LDFLAGS) $(libgtp_la_OBJECTS) $(libgtp_la_LIBADD) $(LIBS)
+
+tags: TAGS
+
+ID: $(HEADERS) $(SOURCES) $(LISP)
+	list='$(SOURCES) $(HEADERS)'; \
+	unique=`for i in $$list; do echo $$i; done | \
+	  awk '    { files[$$0] = 1; } \
+	       END { for (i in files) print i; }'`; \
+	here=`pwd` && cd $(srcdir) \
+	  && mkid -f$$here/ID $$unique $(LISP)
+
+TAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) $(LISP)
+	tags=; \
+	here=`pwd`; \
+	list='$(SOURCES) $(HEADERS)'; \
+	unique=`for i in $$list; do echo $$i; done | \
+	  awk '    { files[$$0] = 1; } \
+	       END { for (i in files) print i; }'`; \
+	test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \
+	  || (cd $(srcdir) && etags $(ETAGS_ARGS) $$tags  $$unique $(LISP) -o $$here/TAGS)
+
+mostlyclean-tags:
+
+clean-tags:
+
+distclean-tags:
+	-rm -f TAGS ID
+
+maintainer-clean-tags:
+
+distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir)
+
+subdir = gtp
+
+distdir: $(DISTFILES)
+	here=`cd $(top_builddir) && pwd`; \
+	top_distdir=`cd $(top_distdir) && pwd`; \
+	distdir=`cd $(distdir) && pwd`; \
+	cd $(top_srcdir) \
+	  && $(AUTOMAKE) --include-deps --build-dir=$$here --srcdir-name=$(top_srcdir) --output-dir=$$top_distdir --gnu gtp/Makefile
+	@for file in $(DISTFILES); do \
+	  d=$(srcdir); \
+	  if test -d $$d/$$file; then \
+	    cp -pr $$d/$$file $(distdir)/$$file; \
+	  else \
+	    test -f $(distdir)/$$file \
+	    || ln $$d/$$file $(distdir)/$$file 2> /dev/null \
+	    || cp -p $$d/$$file $(distdir)/$$file || :; \
+	  fi; \
+	done
+
+DEPS_MAGIC := $(shell mkdir .deps > /dev/null 2>&1 || :)
+
+-include $(DEP_FILES)
+
+mostlyclean-depend:
+
+clean-depend:
+
+distclean-depend:
+	-rm -rf .deps
+
+maintainer-clean-depend:
+
+%.o: %.c
+	@echo '$(COMPILE) -c $<'; \
+	$(COMPILE) -Wp,-MD,.deps/$(*F).pp -c $<
+	@-cp .deps/$(*F).pp .deps/$(*F).P; \
+	tr ' ' '\012' < .deps/$(*F).pp \
+	  | sed -e 's/^\\$$//' -e '/^$$/ d' -e '/:$$/ d' -e 's/$$/ :/' \
+	    >> .deps/$(*F).P; \
+	rm .deps/$(*F).pp
+
+%.lo: %.c
+	@echo '$(LTCOMPILE) -c $<'; \
+	$(LTCOMPILE) -Wp,-MD,.deps/$(*F).pp -c $<
+	@-sed -e 's/^\([^:]*\)\.o[ 	]*:/\1.lo \1.o :/' \
+	  < .deps/$(*F).pp > .deps/$(*F).P; \
+	tr ' ' '\012' < .deps/$(*F).pp \
+	  | sed -e 's/^\\$$//' -e '/^$$/ d' -e '/:$$/ d' -e 's/$$/ :/' \
+	    >> .deps/$(*F).P; \
+	rm -f .deps/$(*F).pp
+info-am:
+info: info-am
+dvi-am:
+dvi: dvi-am
+check-am: all-am
+check: check-am
+installcheck-am:
+installcheck: installcheck-am
+install-exec-am: install-libLTLIBRARIES
+install-exec: install-exec-am
+
+install-data-am:
+install-data: install-data-am
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+install: install-am
+uninstall-am: uninstall-libLTLIBRARIES
+uninstall: uninstall-am
+all-am: Makefile $(LTLIBRARIES)
+all-redirect: all-am
+install-strip:
+	$(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install
+installdirs:
+	$(mkinstalldirs)  $(DESTDIR)$(libdir)
+
+
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+	-rm -f Makefile $(CONFIG_CLEAN_FILES)
+	-rm -f config.cache config.log stamp-h stamp-h[0-9]*
+
+maintainer-clean-generic:
+mostlyclean-am:  mostlyclean-libLTLIBRARIES mostlyclean-compile \
+		mostlyclean-libtool mostlyclean-tags mostlyclean-depend \
+		mostlyclean-generic
+
+mostlyclean: mostlyclean-am
+
+clean-am:  clean-libLTLIBRARIES clean-compile clean-libtool clean-tags \
+		clean-depend clean-generic mostlyclean-am
+
+clean: clean-am
+
+distclean-am:  distclean-libLTLIBRARIES distclean-compile \
+		distclean-libtool distclean-tags distclean-depend \
+		distclean-generic clean-am
+	-rm -f libtool
+
+distclean: distclean-am
+
+maintainer-clean-am:  maintainer-clean-libLTLIBRARIES \
+		maintainer-clean-compile maintainer-clean-libtool \
+		maintainer-clean-tags maintainer-clean-depend \
+		maintainer-clean-generic distclean-am
+	@echo "This command is intended for maintainers to use;"
+	@echo "it deletes files that may require special tools to rebuild."
+
+maintainer-clean: maintainer-clean-am
+
+.PHONY: mostlyclean-libLTLIBRARIES distclean-libLTLIBRARIES \
+clean-libLTLIBRARIES maintainer-clean-libLTLIBRARIES \
+uninstall-libLTLIBRARIES install-libLTLIBRARIES mostlyclean-compile \
+distclean-compile clean-compile maintainer-clean-compile \
+mostlyclean-libtool distclean-libtool clean-libtool \
+maintainer-clean-libtool tags mostlyclean-tags distclean-tags \
+clean-tags maintainer-clean-tags distdir mostlyclean-depend \
+distclean-depend clean-depend maintainer-clean-depend info-am info \
+dvi-am dvi check check-am installcheck-am installcheck install-exec-am \
+install-exec install-data-am install-data install-am install \
+uninstall-am uninstall all-redirect all-am all installdirs \
+mostlyclean-generic distclean-generic clean-generic \
+maintainer-clean-generic clean mostlyclean distclean maintainer-clean
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/gtp/gtp.c b/gtp/gtp.c
new file mode 100644
index 0000000..e00168c
--- /dev/null
+++ b/gtp/gtp.c
@@ -0,0 +1,1917 @@
+/* 
+ *  OpenGGSN - Gateway GPRS Support Node
+ *  Copyright (C) 2002 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):
+ * 
+ */
+
+/*
+ * gtp.c: Contains all GTP functionality. Should be able to handle multiple
+ * tunnels in the same program. 
+ *
+ * TODO:
+ *  - Do we need to handle fragmentation?
+ */
+
+
+#ifdef __linux__
+#define _GNU_SOURCE 1
+#endif
+
+
+#include <syslog.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include <arpa/inet.h>
+
+#include <stdint.h> /* ISO C99 types */
+
+#include "pdp.h"
+#include "gtp.h"
+#include "gtpie.h"
+#include "queue.h"
+
+
+struct gtp0_header gtp0_default;
+struct gtp1_header_long gtp1_default;
+
+/* API Functions */
+
+const char* gtp_version()
+{
+  return VERSION;
+}
+
+/* gtp_new */
+/* gtp_free */
+
+int gtp_newpdp(struct gsn_t* gsn, struct pdp_t **pdp, 
+	       uint64_t imsi, uint8_t nsapi) {
+  return pdp_newpdp(pdp, imsi, nsapi, NULL);
+}
+
+int gtp_freepdp(struct gsn_t* gsn, struct pdp_t *pdp) {
+  return pdp_freepdp(pdp);
+}
+
+int gtp_create_context(struct gsn_t *gsn, struct pdp_t *pdp, void *aid,
+		       struct in_addr* inetaddr) {
+  int version = 0;
+
+  return gtp_create_pdp_req(gsn, version, aid, inetaddr, pdp);
+}
+
+int gtp_update_context(struct gsn_t *gsn, struct pdp_t *pdp, void *aid,
+		       struct in_addr* inetaddr) {
+  int version = 0;
+  
+  return gtp_update_pdp_req(gsn, version, aid, inetaddr, pdp);
+}
+
+int gtp_delete_context(struct gsn_t *gsn, struct pdp_t *pdp, void *aid) {
+  int version = 0;
+  return gtp_delete_pdp_req(gsn, version, aid, pdp);
+}
+
+/* gtp_gpdu */
+
+extern int gtp_fd(struct gsn_t *gsn) {
+  return gsn->fd;
+}
+
+/* gtp_decaps */
+/* gtp_retrans */
+/* gtp_retranstimeout */
+
+int gtp_set_cb_delete_context(struct gsn_t *gsn,
+			      int (*cb_delete_context) (struct pdp_t* pdp)) 
+{
+  gsn->cb_delete_context = cb_delete_context;
+  return 0;
+}
+
+int gtp_set_cb_create_context(struct gsn_t *gsn,
+			      int (*cb_create_context) (struct pdp_t* pdp)) 
+{
+  gsn->cb_create_context = cb_create_context;
+  return 0;
+}
+
+/*
+
+  int gtp_set_cb_create_pdp_conf(struct gsn_t *gsn, 
+  int (*cb) (struct pdp_t*, int)) 
+  {
+   gsn->cb_create_pdp_conf = cb;
+  return 0;
+  }
+
+ int gtp_set_cb_update_pdp_conf(struct gsn_t *gsn, 
+			       int (*cb) (struct pdp_t*, int, int)) 
+ {
+   gsn->cb_update_pdp_conf = cb;
+   return 0;
+} 
+
+in t gtp_set_cb_delete_pdp_conf(struct gsn_t *gsn, 
+int (*cb) (struct pdp_t*, int)) 
+ { 
+gsn->cb_delete_pdp_conf = cb;
+return 0;
+}
+
+*/
+
+int gtp_set_cb_conf(struct gsn_t *gsn,
+		    int (*cb) (int type, int cause, 
+			       struct pdp_t* pdp, void *aid)) {
+  gsn->cb_conf = cb;
+  return 0;
+}
+
+extern int gtp_set_cb_gpdu(struct gsn_t *gsn,
+			   int (*cb_gpdu) (struct pdp_t* pdp,
+					   void* pack,
+					   unsigned len)) 
+{
+  gsn->cb_gpdu = cb_gpdu;
+  return 0;
+}
+
+
+
+void get_default_gtp(int version, void *packet) {
+  switch (version) {
+  case 0:
+    memcpy(packet, &gtp0_default, sizeof(gtp0_default));
+  break;
+  case 1:
+    memcpy(packet, &gtp1_default, sizeof(gtp1_default));
+    break;
+  }
+}
+
+int print_packet(void *packet, unsigned len)
+{
+  int i;
+  printf("The packet looks like this (%d bytes):\n", len);
+  for( i=0; i<len; i++) {
+    printf("%02x ", (unsigned char)*(char *)(packet+i));
+    if (!((i+1)%16)) printf("\n");
+  };
+  printf("\n"); 
+  return 0;
+}
+
+char* snprint_packet(struct gsn_t *gsn, struct sockaddr_in *peer,
+		     void *pack, unsigned len, char *buf, int size) {
+  int n;
+  int pos;
+  snprintf(buf, size, "Packet from %s:%u, length: %d, content:",
+	   inet_ntoa(peer->sin_addr),
+	   ntohs(peer->sin_port),
+	   len);
+  pos = strlen(buf);
+  for(n=0; n<len; n++) {
+    if ((pos+4)<size) {
+      sprintf((buf+pos), " %02hhx", ((unsigned char*)pack)[n]);
+      pos += 3;
+    }
+  }
+  buf[pos] = 0;
+  return buf;
+}
+
+void gtp_err(int priority, char *filename, int linenum, char *fmt, ...) {
+  va_list args;
+  char buf[ERRMSG_SIZE];
+
+  va_start(args, fmt);
+  vsnprintf(buf, ERRMSG_SIZE, fmt, args);
+  va_end(args);
+
+  syslog(priority, "%s: %d: %s", filename, linenum, buf); 
+}
+
+void gtp_errpack(int pri, char *fn, int ln, struct sockaddr_in *peer,
+		 void *pack, unsigned len, char *fmt, ...) {
+  
+  va_list args;
+  char buf[ERRMSG_SIZE];
+  char buf2[ERRMSG_SIZE];
+  int n;
+  int pos;
+  
+  va_start(args, fmt);
+  vsnprintf(buf, ERRMSG_SIZE, fmt, args);
+  va_end(args);
+
+  snprintf(buf2, ERRMSG_SIZE, "Packet from %s:%u, length: %d, content:",
+	   inet_ntoa(peer->sin_addr),
+	   ntohs(peer->sin_port),
+	   len);
+  pos = strlen(buf2);
+  for(n=0; n<len; n++) {
+    if ((pos+4)<ERRMSG_SIZE) {
+      sprintf((buf2+pos), " %02hhx", ((unsigned char*)pack)[n]);
+      pos += 3;
+    }
+  }
+  buf2[pos] = 0;
+  
+  syslog(pri, "%s: %d: %s. %s", fn, ln, buf, buf2);
+
+}
+
+
+/* ***********************************************************
+ * Reliable delivery of signalling messages
+ * 
+ * Sequence numbers are used for both signalling messages and
+ * data messages.
+ *
+ * For data messages each tunnel maintains a sequence counter,
+ * which is incremented by one each time a new data message
+ * is sent. The sequence number starts at (0) zero at tunnel
+ * establishment, and wraps around at 65535 (29.060 9.3.1.1 
+ * and 09.60 8.1.1.1). The sequence numbers are either ignored,
+ * or can be used to check the validity of the message in the
+ * receiver, or for reordering af packets.
+ *
+ * For signalling messages the sequence number is used by 
+ * signalling messages for which a response is defined. A response
+ * message should copy the sequence from the corresponding request
+ * message. The sequence number "unambiguously" identifies a request
+ * message within a given path, with a path being defined as a set of
+ * two endpoints (29.060 8.2, 29.060 7.6, 09.60 7.8). "All request
+ * messages shall be responded to, and all response messages associated
+ * with a certain request shall always include the same information"
+ *
+ * We take this to mean that the GSN transmitting a request is free to
+ * choose the sequence number, as long as it is unique within a given path.
+ * It means that we are allowed to count backwards, or roll over at 17
+ * if we prefer that. It also means that we can use the same counter for
+ * all paths. This has the advantage that the transmitted request sequence
+ * numbers are unique within each GSN, and also we dont have to mess around
+ * with path setup and teardown.
+ *
+ * If a response message is lost, the request will be retransmitted, and
+ * the receiving GSN will receive a "duplicated" request. The standard 
+ * requires the receiving GSN to send a response, with the same information
+ * as in the original response. For most messages this happens automatically:
+ *
+ * Echo: Automatically dublicates the original response
+ * Create pdp context: The SGSN may send create context request even if
+ *   a context allready exist (imsi+nsapi?). This means that the reply will
+     automatically dublicate the original response. It might however have
+ *   sideeffects in the application which is asked twice to allocate
+ *   validate the login.
+ * Update pdp context: Automatically dublicates the original response???
+ * Delete pdp context. Automatically in gtp0, but in gtp1 will generate
+ *   a nonexist reply message.
+ *
+ * The correct solution will be to make a queue containing response messages.
+ * This queue should be checked whenever a request is received. If the 
+ * response is allready in the queue that response should be transmitted.
+ * It should be possible to find messages in this queue on the basis of
+ * the sequence number and peer GSN IP address (The sequense number is unique
+ * within each path). This need to be implemented by a hash table. Furthermore
+ * it should be possibly to delete messages based on a timeout. This can be
+ * achieved by means of a linked list. The timeout value need to be larger
+ * than T3-RESPONSE * N3-REQUESTS (recommended value 5). These timers are 
+ * set in the peer GSN, so there is no way to know these parameters. On the
+ * other hand the timeout value need to be so small that we do not receive
+ * wraparound sequence numbere before the message is deleted. 60 seconds is
+ * probably not a bad choise.
+ * 
+ * This queue however is first really needed from gtp1.
+ *
+ * gtp_req: 
+ *   Send off a signalling message with appropiate sequence
+ *   number. Store packet in queue.
+ * gtp_conf:
+ *   Remove an incoming confirmation from the queue
+ * gtp_resp:
+ *   Send off a responce to a request. Use the same sequence
+ *   number in the response as in the request.
+ * gtp_retrans:
+ *   Retransmit any outstanding packets which have exceeded
+ *   a predefined timeout.
+ *************************************************************/
+
+int gtp_req(struct gsn_t *gsn, int version, union gtp_packet *packet, 
+	    int len, struct in_addr *inetaddr, void *aid) {
+  struct sockaddr_in addr;
+  struct qmsg_t *qmsg;
+  memset(&addr, 0, sizeof(addr));
+  addr.sin_family = AF_INET;
+  addr.sin_addr = *inetaddr;
+  addr.sin_port = htons(GTP0_PORT);
+
+  packet->gtp0.h.seq = hton16(gsn->seq_next);
+  
+  if (sendto(gsn->fd, packet, len, 0,
+	     (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+    gsn->err_sendto++;
+    gtp_err(LOG_ERR, __FILE__, __LINE__, "Sendto(fd=%d, msg=%lx, len=%d) failed: Error = %s", gsn->fd, (unsigned long) &packet, len, strerror(errno));
+    return -1;
+  }
+
+  /* Use new queue structure */
+  if (queue_newmsg(gsn->queue_req, &qmsg, &addr, gsn->seq_next)) {
+    gsn->err_queuefull++;
+    gtp_err(LOG_ERR, __FILE__, __LINE__, "Retransmit queue is full");
+  }
+  else {
+    memcpy(&qmsg->p, packet, sizeof(union gtp_packet));
+    qmsg->l = len;
+    qmsg->timeout = time(NULL) + 3; /* When to timeout */
+    qmsg->retrans = 0;   /* No retransmissions so far */
+    qmsg->aid = aid;
+    qmsg->type = ntoh8(packet->gtp0.h.type);
+  }
+  gsn->seq_next++; /* Count up this time */
+  return 0;
+}
+
+/* gtp_conf
+ * Remove signalling packet from retransmission queue.
+ * return 0 on success, EOF if packet was not found */
+
+int gtp_conf(struct gsn_t *gsn, int version, struct sockaddr_in *peer,
+	     union gtp_packet *packet, int len, uint8_t *type, void **aid) {
+  int seq = ntoh16(packet->gtp0.h.seq);
+
+  if (queue_freemsg_seq(gsn->queue_req, peer, seq, type, aid)) {
+    gsn->err_seq++;
+    gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, packet, len,
+		"Confirmation packet not found in queue");
+    return EOF;
+  }
+
+  return 0;
+}
+
+int gtp_retrans(struct gsn_t *gsn) {
+  /* Retransmit any outstanding packets */
+  /* Remove from queue if maxretrans exceeded */
+  time_t now;
+  struct qmsg_t *qmsg;
+  now = time(NULL);
+  /*printf("Retrans: New beginning %d\n", (int) now);*/
+
+  while ((!queue_getfirst(gsn->queue_req, &qmsg)) &&
+	 (qmsg->timeout <= now)) {
+    /*printf("Retrans timeout found: %d\n", (int) time(NULL));*/
+    if (qmsg->retrans > 3) { /* To many retrans */
+      if (gsn->cb_conf) gsn->cb_conf(qmsg->type, EOF, NULL, qmsg->aid);
+      queue_freemsg(gsn->queue_req, qmsg);
+    }
+    else {
+      if (sendto(gsn->fd, &qmsg->p, qmsg->l, 0,
+		 (struct sockaddr *) &qmsg->peer, sizeof(struct sockaddr_in)) < 0) {
+	gsn->err_sendto++;
+	gtp_err(LOG_ERR, __FILE__, __LINE__, "Sendto(fd=%d, msg=%lx, len=%d) failed: Error = %s", gsn->fd, (unsigned long) &qmsg->p, qmsg->l, strerror(errno));
+      }
+      queue_back(gsn->queue_req, qmsg);
+      qmsg->timeout = now + 3;
+      qmsg->retrans++;
+    }
+  } 
+
+  /* Also clean up reply timeouts */
+  while ((!queue_getfirst(gsn->queue_resp, &qmsg)) &&
+	 (qmsg->timeout < now)) {
+    /*printf("Retrans (reply) timeout found: %d\n", (int) time(NULL));*/
+    queue_freemsg(gsn->queue_resp, qmsg);
+  }
+
+  return 0;
+}
+
+int gtp_retranstimeout(struct gsn_t *gsn, struct timeval *timeout) {
+  time_t now, later;
+  struct qmsg_t *qmsg;
+
+  if (queue_getfirst(gsn->queue_req, &qmsg)) {
+    timeout->tv_sec = 10;
+    timeout->tv_usec = 0;
+  }
+  else {
+    now = time(NULL);
+    later = qmsg->timeout;
+    timeout->tv_sec = later - now;
+    timeout->tv_usec = 0;
+    if (timeout->tv_sec < 0) timeout->tv_sec = 0; /* No negative allowed */
+    if (timeout->tv_sec > 10) timeout->tv_sec = 10; /* Max sleep for 10 sec*/
+  }
+  return 0;
+}
+
+int gtp_resp(int version, struct gsn_t *gsn, union gtp_packet *packet,
+	     int len, struct sockaddr_in *peer) {
+  struct qmsg_t *qmsg;
+  uint16_t seq;
+
+  seq = ntoh16(packet->gtp0.h.seq);
+  
+  /* print message */
+  /*
+  printf("gtp_resp: to %s:UDP%u\n",
+	 inet_ntoa(peer->sin_addr),
+	 ntohs(peer->sin_port));
+  print_packet(packet, len); 
+  */
+  
+  if (sendto(gsn->fd, packet, len, 0,
+	     (struct sockaddr *) peer, sizeof(struct sockaddr_in)) < 0) {
+    gsn->err_sendto++;
+    gtp_err(LOG_ERR, __FILE__, __LINE__, "Sendto(fd=%d, msg=%lx, len=%d) failed: Error = %s", gsn->fd, (unsigned long) &packet, len, strerror(errno));
+    return -1;
+  }
+
+  /* Use new queue structure */
+  if (queue_newmsg(gsn->queue_resp, &qmsg, peer, seq)) {
+    gsn->err_queuefull++;
+    gtp_err(LOG_ERR, __FILE__, __LINE__, "Retransmit queue is full");
+  }
+  else {
+    memcpy(&qmsg->p, packet, sizeof(union gtp_packet));
+    qmsg->l = len;
+    qmsg->timeout = time(NULL) + 60; /* When to timeout */
+    qmsg->retrans = 0;   /* No retransmissions so far */
+    qmsg->aid = NULL;
+    qmsg->type = 0;
+  }
+  return 0;
+}
+
+int gtp_dublicate(struct gsn_t *gsn, int version,  
+		  struct sockaddr_in *peer, uint16_t seq) {
+  struct qmsg_t *qmsg;
+
+  if(queue_seqget(gsn->queue_resp, &qmsg, peer, seq)) {
+    return EOF; /* Notfound */
+  }
+  else {
+    /* print message */
+    
+    /*printf("gtp_dublicate: to %s:UDP%u\n",
+	   inet_ntoa(peer->sin_addr),
+	   ntohs(peer->sin_port));
+    print_packet(&qmsg->p, qmsg->l);
+    */
+    if (sendto(gsn->fd, &qmsg->p, qmsg->l, 0,
+	       (struct sockaddr *) peer, sizeof(struct sockaddr_in)) < 0) {
+      gsn->err_sendto++;
+      gtp_err(LOG_ERR, __FILE__, __LINE__, "Sendto(fd=%d, msg=%lx, len=%d) failed: Error = %s", gsn->fd, (unsigned long) &qmsg->p, qmsg->l, strerror(errno));
+    }
+    return 0;
+  }
+}
+
+
+
+/* Perform restoration and recovery error handling as described in 29.060 */
+static void log_restart(struct gsn_t *gsn) {
+	FILE *f;
+	int i;
+	int counter = 0;
+	char filename[NAMESIZE];
+
+	filename[NAMESIZE-1] = 0; /* No null term. guarantee by strncpy */ 
+	strncpy(filename, gsn->statedir, NAMESIZE-1);
+	strncat(filename, RESTART_FILE, 
+		NAMESIZE-1-sizeof(RESTART_FILE));
+
+	i = umask(022);
+
+	/* We try to open file. On failure we will later try to create file */
+	if (!(f = fopen(filename, "r"))) {
+	  gtp_err(LOG_ERR, __FILE__, __LINE__, "fopen(path=%s, mode=%s) failed: Error = %s", filename, "r", strerror(errno));
+	}
+	else {
+	  umask(i);
+	  fscanf(f, "%d", &counter);
+	  if (fclose(f)) {
+	    gtp_err(LOG_ERR, __FILE__, __LINE__, "fclose failed: Error = %s", strerror(errno));
+	  }
+	}
+	
+	gsn->restart_counter = (unsigned char) counter;
+	gsn->restart_counter++;
+	
+	if (!(f = fopen(filename, "w"))) {
+	  gtp_err(LOG_ERR, __FILE__, __LINE__, "fopen(path=%s, mode=%s) failed: Error = %s", filename, "w", strerror(errno));
+	  return;
+	}
+
+	umask(i);
+	fprintf(f, "%d\n", gsn->restart_counter);
+	if (fclose(f)) {
+	  gtp_err(LOG_ERR, __FILE__, __LINE__, "fclose failed: Error = %s", strerror(errno));
+	  return;
+	}
+}
+
+
+
+int gtp_new(struct gsn_t **gsn, char *statedir, struct in_addr *listen) 
+{
+  struct sockaddr_in addr;
+  int gtp_fd;
+  
+  syslog(LOG_ERR, "GTP: gtp_newgsn() started");
+
+  *gsn = calloc(sizeof(struct gsn_t), 1); /* TODO */
+
+  (*gsn)->statedir = statedir;
+  log_restart(*gsn);
+  
+  /* Initialise request retransmit queue */
+  queue_new(&(*gsn)->queue_req);
+  queue_new(&(*gsn)->queue_resp);
+  
+  /* Initialise pdp table */
+  pdp_init();
+
+  /* Initialise call back functions */
+  (*gsn)->cb_create_context = 0;
+  (*gsn)->cb_delete_context = 0;
+  (*gsn)->cb_conf = 0;
+  (*gsn)->cb_gpdu = 0;
+
+  if ((gtp_fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {
+    (*gsn)->err_socket++;
+    gtp_err(LOG_ERR, __FILE__, __LINE__, "socket(domain=%d, type=%d, protocol=%d) failed: Error = %s", AF_INET, SOCK_DGRAM, 0, strerror(errno));
+    return -1;
+  }
+  (*gsn)->fd = gtp_fd;
+  
+  /* syslog(LOG_ERR, "GTP: gtp_init() after socket");*/
+
+  (*gsn)->gsnc = *listen;
+  (*gsn)->gsnu = *listen;
+    
+  memset(&addr, 0, sizeof(addr));
+  
+  addr.sin_family = AF_INET;
+  /*  addr.sin_addr = *inetaddr; */
+  addr.sin_addr = *listen;  /* Same IP for user traffic and signalling*/
+  addr.sin_port = htons(GTP0_PORT);
+  
+  if (bind(gtp_fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+    (*gsn)->err_socket++;
+    gtp_err(LOG_ERR, __FILE__, __LINE__, "bind(fd=%d, addr=%lx, len=%d) failed: Error = %s", gtp_fd, (unsigned long) &addr, sizeof(addr), strerror(errno));
+    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;
+}
+
+int gtp_free(struct gsn_t *gsn) {
+
+  /* Clean up retransmit queues */
+  queue_free(gsn->queue_req);
+  queue_free(gsn->queue_resp);
+
+  free(gsn);
+  return 0;
+}
+
+/* ***********************************************************
+ * Path management messages
+ * Messages: echo and version not supported.
+ * A path is connection between two UDP/IP endpoints
+ *
+ * A path is either using GTP0 or GTP1. A path can be
+ * established by any kind of GTP message??
+
+ * Which source port to use?
+ * GTP-C request destination port is 2123/3386
+ * GTP-U request destination port is 2152/3386
+ * T-PDU destination port is 2152/3386.
+ * For the above messages the source port is locally allocated.
+ * For response messages src=rx-dst and dst=rx-src.
+ * For simplicity we should probably use 2123+2152/3386 as
+ * src port even for the cases where src can be locally
+ * allocated. This also means that we have to listen only to
+ * the same ports.
+ * For response messages we need to be able to respond to
+ * the relevant src port even if it is locally allocated by
+ * the peer.
+ * 
+ * The need for path management!
+ * We might need to keep a list of active paths. This might
+ * be in the form of remote IP address + UDP port numbers.
+ * (We will consider a path astablished if we have a context
+ * with the node in question)
+ *************************************************************/
+
+/* Send off an echo request */
+int gtp_echo_req(struct gsn_t *gsn, struct in_addr *inetaddr)
+{
+  union gtp_packet packet;
+
+  get_default_gtp(0, &packet);
+  packet.gtp0.h.type = hton8(GTP_ECHO_REQ);
+  packet.gtp0.h.length = hton16(0);
+
+  return gtp_req(gsn, 0, &packet, GTP0_HEADER_SIZE, inetaddr, NULL);
+}
+
+/* Send of an echo reply */
+int gtp_echo_resp(struct gsn_t *gsn, struct sockaddr_in *peer,
+		  void *pack, unsigned len)
+{
+  union gtp_packet packet;
+  int length = 0;
+  
+  get_default_gtp(0, &packet);
+
+  gtpie_tv1(packet.gtp0.p, &length, GTP_MAX, GTPIE_RECOVERY, 
+	    gsn->restart_counter);
+  
+  packet.gtp0.h.type = hton8(GTP_ECHO_RSP);
+  packet.gtp0.h.length = hton16(length);
+  packet.gtp0.h.seq = ((union gtp_packet*)pack)->gtp0.h.seq;
+
+  return gtp_resp(0, gsn, &packet, GTP0_HEADER_SIZE+length, peer);
+}
+
+
+/* Handle a received echo request */
+int gtp_echo_ind(struct gsn_t *gsn, struct sockaddr_in *peer,
+		 void *pack, unsigned len) {
+
+  uint16_t seq = ntoh16(((union gtp_packet*)pack)->gtp0.h.seq);
+
+  if(!gtp_dublicate(gsn, 0, peer, seq)) {
+    return 0; /* We allready send of response once */
+  }
+
+
+  /* Now send off a reply to the peer */
+  return gtp_echo_resp(gsn, peer, pack, len);
+}
+
+/* Handle a received echo reply */
+int gtp_echo_conf(struct gsn_t *gsn, struct sockaddr_in *peer,
+		  void *pack, unsigned len) {
+  union gtpie_member *ie[GTPIE_SIZE];
+  unsigned char recovery;
+  void *aid = NULL;
+  uint8_t type = 0;
+
+  /* Remove packet from queue */
+  if (gtp_conf(gsn, 0, peer, pack, len, &type, &aid)) return EOF;
+
+  if (gtpie_decaps(ie, pack+sizeof(struct gtp0_header), len-sizeof(struct gtp0_header))) {
+    gsn->invalid++;
+    gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+		"Invalid message format");
+    return EOF;
+  }
+  
+  if (gtpie_gettv1(ie, GTPIE_RECOVERY, 0, &recovery)) {
+    gsn->missing++;
+    gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+		"Missing mandatory field");
+    return EOF;
+  }
+
+  if (gsn->cb_conf) gsn->cb_conf(type, 0, NULL, aid); /* TODO: Should return recovery in callback */
+
+  return 0;
+}
+
+/* Send off a Version Not Supported message */
+/* This message is somewhat special in that it actually is a
+ * response to some other message with unsupported GTP version
+ * For this reason it has parameters like a response, and does
+ * its own message transmission. No signalling queue is used 
+ * The reply is sent to the peer IP and peer UDP. This means that
+ * the peer will be receiving a GTP0 message on a GTP1 port! 
+ * In practice however this will never happen as a GTP0 GSN will
+ * only listen to the GTP0 port, and therefore will never receive
+ * anything else than GTP0 */
+
+int gtp_unsup_resp(struct gsn_t *gsn, struct sockaddr_in *peer,
+		   void *pack, unsigned len)
+{
+  union gtp_packet packet;
+  int length = 0;
+
+  get_default_gtp(0, &packet);
+  packet.gtp0.h.type = hton8(GTP_NOT_SUPPORTED);
+  packet.gtp0.h.length = hton16(0);
+  
+  return gtp_resp(0, gsn, &packet, GTP0_HEADER_SIZE+length, peer);
+}
+
+/* Handle a Version Not Supported message */
+int gtp_unsup_conf(struct gsn_t *gsn, struct sockaddr_in *peer, void *pack, unsigned len) {
+
+  /* TODO: Need to check the validity of header and information elements */
+  /* TODO: Implement callback to application */
+  /* As long as we only support GTP0 we should never receive this message */
+  /* Should be implemented as part of GTP1 support */
+  
+  /* print received message */
+  /*
+  printf("gtp_unsup_ind: from %s:UDP%u\n",
+	 inet_ntoa(peer->sin_addr),
+	 ntohs(peer->sin_port));
+  print_packet(pack, len);
+  */
+  return 0;
+}
+
+/* ***********************************************************
+ * Session management messages
+ * Messages: create, update and delete PDP context
+ *
+ * Information storage
+ * Information storage for each PDP context is defined in 
+ * 23.060 section 13.3. Includes IMSI, MSISDN, APN, PDP-type,
+ * PDP-address (IP address), sequence numbers, charging ID.
+ * For the SGSN it also includes radio related mobility
+ * information.
+ *************************************************************/
+
+/* Send Create PDP Context Request */
+extern int gtp_create_pdp_req(struct gsn_t *gsn, int version, void *aid,
+			      struct in_addr* inetaddr, struct pdp_t *pdp) {
+  union gtp_packet packet;
+  int length = 0;
+
+  get_default_gtp(0, &packet);
+  
+  if (0==0) { /* Always GTP0 */
+
+    gtpie_tv0(packet.gtp0.p, &length, GTP_MAX, GTPIE_QOS_PROFILE0, 
+	      sizeof(pdp->qos_req0), pdp->qos_req0);
+    gtpie_tv1(packet.gtp0.p, &length, GTP_MAX, GTPIE_RECOVERY, 
+	      gsn->restart_counter);
+    gtpie_tv1(packet.gtp0.p, &length, GTP_MAX, GTPIE_SELECTION_MODE,
+	      pdp->selmode);
+    gtpie_tv2(packet.gtp0.p, &length, GTP_MAX, GTPIE_FL_DI, 
+	      pdp->fllu);
+    gtpie_tv2(packet.gtp0.p, &length, GTP_MAX, GTPIE_FL_C,
+	      pdp->fllc);
+    gtpie_tlv(packet.gtp0.p, &length, GTP_MAX, GTPIE_EUA, 
+	      pdp->eua.l, pdp->eua.v);
+    gtpie_tlv(packet.gtp0.p, &length, GTP_MAX, GTPIE_APN, 
+	      pdp->apn_use.l, pdp->apn_use.v);
+
+    if (pdp->pco_req.l) { /* Optional PCO */
+      gtpie_tlv(packet.gtp0.p, &length, GTP_MAX, GTPIE_PCO, 
+		pdp->pco_req.l, pdp->pco_req.v);
+    }
+
+    gtpie_tlv(packet.gtp0.p, &length, GTP_MAX, GTPIE_GSN_ADDR, 
+	      pdp->gsnlc.l, pdp->gsnlc.v);
+    gtpie_tlv(packet.gtp0.p, &length, GTP_MAX, GTPIE_GSN_ADDR, 
+	      pdp->gsnlu.l, pdp->gsnlu.v);
+    gtpie_tlv(packet.gtp0.p, &length, GTP_MAX, GTPIE_MSISDN,
+	      pdp->msisdn.l, pdp->msisdn.v);
+    
+
+
+  } else { /* GTP1 */
+    gtpie_tv0(packet.gtp1s.p, &length, GTP_MAX, GTPIE_IMSI, 
+	      sizeof(pdp->imsi), (uint8_t*) &pdp->imsi);
+    gtpie_tv1(packet.gtp1s.p, &length, GTP_MAX, GTPIE_RECOVERY, 
+	      gsn->restart_counter);
+    gtpie_tv1(packet.gtp1s.p, &length, GTP_MAX, GTPIE_SELECTION_MODE,
+	      pdp->selmode);
+    gtpie_tv4(packet.gtp1s.p, &length, GTP_MAX, GTPIE_TEI_DI, 
+	      pdp->teid_own);
+    gtpie_tv4(packet.gtp1s.p, &length, GTP_MAX, GTPIE_TEI_C,
+	      pdp->teic_own);
+    gtpie_tv1(packet.gtp1s.p, &length, GTP_MAX, GTPIE_NSAPI, 
+	      pdp->nsapi);
+    /*gtpie_tv1(packet.gtp1s.p, &length, GTP_MAX, GTPIE_NSAPI, 
+      pdp->nsapil); For use by several QoS profiles for the same address */
+    gtpie_tv2(packet.gtp1s.p, &length, GTP_MAX, GTPIE_CHARGING_C,
+	      pdp->cch_pdp);
+    gtpie_tv2(packet.gtp1s.p, &length, GTP_MAX, GTPIE_TRACE_REF,
+	      pdp->traceref);
+    gtpie_tv2(packet.gtp1s.p, &length, GTP_MAX, GTPIE_TRACE_TYPE,
+	      pdp->tracetype);
+    gtpie_tlv(packet.gtp1s.p, &length, GTP_MAX, GTPIE_EUA, 
+	      pdp->eua.l, pdp->eua.v);
+    gtpie_tlv(packet.gtp1s.p, &length, GTP_MAX, GTPIE_APN, 
+	      pdp->apn_use.l, pdp->apn_use.v);
+    gtpie_tlv(packet.gtp1s.p, &length, GTP_MAX, GTPIE_PCO, 
+	      pdp->pco_req.l, pdp->pco_req.v);
+    gtpie_tlv(packet.gtp1s.p, &length, GTP_MAX, GTPIE_GSN_ADDR, 
+	      pdp->gsnlc.l, pdp->gsnlc.v);
+    gtpie_tlv(packet.gtp1s.p, &length, GTP_MAX, GTPIE_GSN_ADDR, 
+	      pdp->gsnlu.l, pdp->gsnlu.v);
+    gtpie_tlv(packet.gtp1s.p, &length, GTP_MAX, GTPIE_MSISDN,
+	      pdp->msisdn.l, pdp->msisdn.v);
+    gtpie_tlv(packet.gtp1s.p, &length, GTP_MAX, GTPIE_QOS_PROFILE,
+	      pdp->qos_req.l, pdp->qos_req.v);
+    gtpie_tlv(packet.gtp1s.p, &length, GTP_MAX, GTPIE_TFT,
+	      pdp->tft.l, pdp->tft.v);
+    gtpie_tlv(packet.gtp1s.p, &length, GTP_MAX, GTPIE_TRIGGER_ID,
+	      pdp->triggerid.l, pdp->triggerid.v);
+    gtpie_tlv(packet.gtp1s.p, &length, GTP_MAX, GTPIE_OMC_ID,
+	      pdp->omcid.l, pdp->omcid.v);
+  }
+  packet.gtp0.h.type = hton8(GTP_CREATE_PDP_REQ);
+  packet.gtp0.h.length = hton16(length);
+  packet.gtp0.h.flow = 0;
+  packet.gtp0.h.tid = pdp->tid;
+
+  gtp_req(gsn, 0, &packet, GTP0_HEADER_SIZE+length, inetaddr, aid);
+
+  return 0;
+}
+
+/* Send Create PDP Context Response */
+int gtp_create_pdp_resp(struct gsn_t *gsn, int version,
+			struct sockaddr_in *peer, 
+			void *pack, unsigned len, 
+			struct pdp_t *pdp, uint8_t cause)
+{
+  union gtp_packet packet;
+  int length = 0;
+
+  get_default_gtp(0, &packet);
+
+  gtpie_tv1(packet.gtp0.p, &length, GTP_MAX, GTPIE_CAUSE, cause);
+
+  if (cause == GTPCAUSE_ACC_REQ) {
+    gtpie_tv0(packet.gtp0.p, &length, GTP_MAX, GTPIE_QOS_PROFILE0, 
+	      sizeof(pdp->qos_neg0), pdp->qos_neg0);
+    gtpie_tv1(packet.gtp0.p, &length, GTP_MAX, GTPIE_REORDER,
+	      pdp->reorder);
+    gtpie_tv1(packet.gtp0.p, &length, GTP_MAX, GTPIE_RECOVERY, 
+	      gsn->restart_counter);
+    gtpie_tv2(packet.gtp0.p, &length, GTP_MAX, GTPIE_FL_DI, 
+	      pdp->fllu);
+    gtpie_tv2(packet.gtp0.p, &length, GTP_MAX, GTPIE_FL_C,
+	      pdp->fllc);
+    gtpie_tv4(packet.gtp0.p, &length, GTP_MAX, GTPIE_CHARGING_ID,
+	      0x12345678);
+    gtpie_tlv(packet.gtp0.p, &length, GTP_MAX, GTPIE_EUA, 
+	      pdp->eua.l, pdp->eua.v);
+
+    if (pdp->pco_neg.l) { /* Optional PCO */
+      gtpie_tlv(packet.gtp0.p, &length, GTP_MAX, GTPIE_PCO,
+		pdp->pco_neg.l, pdp->pco_neg.v);
+    }
+
+    gtpie_tlv(packet.gtp0.p, &length, GTP_MAX, GTPIE_GSN_ADDR, 
+	      pdp->gsnlc.l, pdp->gsnlc.v);
+    gtpie_tlv(packet.gtp0.p, &length, GTP_MAX, GTPIE_GSN_ADDR, 
+	      pdp->gsnlu.l, pdp->gsnlu.v);
+  }
+
+  packet.gtp0.h.type = hton8(GTP_CREATE_PDP_RSP);
+  packet.gtp0.h.length = hton16(length);
+  packet.gtp0.h.flow = hton16(pdp->flrc);
+  packet.gtp0.h.seq = ((union gtp_packet*)pack)->gtp0.h.seq;
+  packet.gtp0.h.tid = ((union gtp_packet*)pack)->gtp0.h.tid;
+
+  return gtp_resp(0, gsn, &packet, GTP0_HEADER_SIZE+length, peer);
+}
+
+/* Handle Create PDP Context Request */
+int gtp_create_pdp_ind(struct gsn_t *gsn, int version,
+		       struct sockaddr_in *peer, void *pack, unsigned len) {
+  struct pdp_t *pdp, *pdp_old; 
+  struct pdp_t pdp_buf;
+  union gtpie_member* ie[GTPIE_SIZE];
+  uint8_t recovery;
+  uint64_t imsi;
+  uint8_t nsapi;
+  int auth = 0; /* Allow access if no callback is defined */
+
+  uint16_t seq = ntoh16(((union gtp_packet*)pack)->gtp0.h.seq);
+
+  if(!gtp_dublicate(gsn, 0, peer, seq)) {
+    return 0; /* We allready send of response once */
+  }
+
+  /* Decode information elements */
+  if (gtpie_decaps(ie, pack+GTP0_HEADER_SIZE, len-GTP0_HEADER_SIZE)) {
+    gsn->invalid++;
+    gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+		"Invalid message format");
+    if (0 == version)
+      return EOF;
+    else
+      return gtp_create_pdp_resp(gsn, version, peer, pack, len, pdp, 
+				 GTPCAUSE_INVALID_MESSAGE);
+  }
+
+  pdp = &pdp_buf;
+  memset(pdp, 0, sizeof(struct pdp_t));
+
+  /* Extract IMSI and NSAPI from header */
+  imsi = ((union gtp_packet*)pack)->gtp0.h.tid & 0x0fffffffffffffff;
+  nsapi = (((union gtp_packet*)pack)->gtp0.h.tid & 0xf000000000000000) >> 60;
+
+  /* pdp_newpdp(&pdp, imsi, nsapi); TODO: Need to remove again */
+
+  if (gtpie_gettv0(ie, GTPIE_QOS_PROFILE0, 0,
+		   pdp->qos_req0, sizeof(pdp->qos_req0))) {
+    gsn->missing++;
+    gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+		"Missing mandatory information field");
+    return gtp_create_pdp_resp(gsn, version, peer, pack, len, pdp, 
+			       GTPCAUSE_MAN_IE_MISSING);
+  }
+
+  /* Extract recovery (optional) */
+  if (!gtpie_gettv1(ie, GTPIE_RECOVERY, 0, &recovery)) {
+    /* TODO: Handle received recovery IE */
+  }
+
+  if (gtpie_gettv0(ie, GTPIE_SELECTION_MODE, 0,
+		   &pdp->selmode, sizeof(pdp->selmode))) {
+    gsn->missing++;
+    gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+		"Missing mandatory information field");
+    return gtp_create_pdp_resp(gsn, version, peer, pack, len, pdp, 
+			       GTPCAUSE_MAN_IE_MISSING);
+  }
+
+  if (gtpie_gettv2(ie, GTPIE_FL_DI, 0, &pdp->flru)) {
+    gsn->missing++;
+    gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+		"Missing mandatory information field");
+    return gtp_create_pdp_resp(gsn, version, peer, pack, len, pdp, 
+			       GTPCAUSE_MAN_IE_MISSING);
+  }
+
+  if (gtpie_gettv2(ie, GTPIE_FL_C, 0, &pdp->flrc)) {
+    gsn->missing++;
+    gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+		"Missing mandatory information field");
+    return gtp_create_pdp_resp(gsn, version, peer, pack, len, pdp, 
+			       GTPCAUSE_MAN_IE_MISSING);
+  }
+
+  if (gtpie_gettlv(ie, GTPIE_EUA, 0, &pdp->eua.l,
+		     &pdp->eua.v, sizeof(pdp->eua.v))) {
+    gsn->missing++;
+    gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+		"Missing mandatory information field");
+    return gtp_create_pdp_resp(gsn, version, peer, pack, len, pdp, 
+			       GTPCAUSE_MAN_IE_MISSING);
+  }
+
+  if (gtpie_gettlv(ie, GTPIE_APN, 0, &pdp->apn_req.l,
+		     &pdp->apn_req.v, sizeof(pdp->apn_req.v))) {
+    gsn->missing++;
+    gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+		"Missing mandatory information field");
+    return gtp_create_pdp_resp(gsn, version, peer, pack, len, pdp, 
+			       GTPCAUSE_MAN_IE_MISSING);
+  }
+
+  /* Extract protocol configuration options (optional) */
+  if (!gtpie_gettlv(ie, GTPIE_PCO, 0, &pdp->pco_req.l,
+		    &pdp->pco_req.v, sizeof(pdp->pco_req.v))) {
+    /* TODO: Handle PCO IE */
+  }
+
+  if (gtpie_gettlv(ie, GTPIE_GSN_ADDR, 0, &pdp->gsnrc.l,
+		     &pdp->gsnrc.v, sizeof(pdp->gsnrc.v))) {
+    gsn->missing++;
+    gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+		"Missing mandatory information field");
+    return gtp_create_pdp_resp(gsn, version, peer, pack, len, pdp, 
+			       GTPCAUSE_MAN_IE_MISSING);
+  }
+
+  if (gtpie_gettlv(ie, GTPIE_GSN_ADDR, 1, &pdp->gsnru.l,
+		     &pdp->gsnru.v, sizeof(pdp->gsnru.v))) {
+    gsn->missing++;
+    gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+		"Missing mandatory information field");
+    return gtp_create_pdp_resp(gsn, version, peer, pack, len, pdp, 
+			       GTPCAUSE_MAN_IE_MISSING);
+  }
+  
+  if (gtpie_gettlv(ie, GTPIE_MSISDN, 0, &pdp->msisdn.l,
+		   &pdp->msisdn.v, sizeof(pdp->msisdn.v))) {
+    gsn->missing++;
+    gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+		"Missing mandatory information field");
+    return gtp_create_pdp_resp(gsn, version, peer, pack, len, pdp, 
+			       GTPCAUSE_MAN_IE_MISSING);
+  }
+
+  in_addr2gsna(&pdp->gsnlc, &gsn->gsnc);
+  in_addr2gsna(&pdp->gsnlu, &gsn->gsnu);
+
+  if (!pdp_tidget(&pdp_old, ((union gtp_packet*)pack)->gtp0.h.tid)) {
+    /* Found old pdp with same tid. Now the voodoo begins! */
+    /* We check that the APN, selection mode and MSISDN is the same */
+    if (   (pdp->apn_req.l == pdp_old->apn_req.l) 
+	&& (!memcmp(pdp->apn_req.v, pdp_old->apn_req.v, pdp->apn_req.l)) 
+	&& (pdp->selmode == pdp_old->selmode)
+	&& (pdp->msisdn.l == pdp_old->msisdn.l) 
+	&& (!memcmp(pdp->msisdn.v, pdp_old->msisdn.v, pdp->msisdn.l))) {
+      /* OK! We are dealing with the same APN. We will copy new
+       * parameters to the old pdp and send off confirmation 
+       * We ignore the following information elements:
+       * QoS: MS will get originally negotiated QoS.
+       * End user address (EUA). MS will get old EUA anyway.
+       * Protocol configuration option (PCO): Only application can verify */
+      
+      /* Copy remote flow label */
+      pdp_old->flru = pdp->flru;
+      pdp_old->flrc = pdp->flrc;
+
+      /* Copy peer GSN address */
+      pdp_old->gsnrc.l = pdp->gsnrc.l;
+      memcpy(&pdp_old->gsnrc.v, &pdp->gsnrc.v, pdp->gsnrc.l);
+      pdp_old->gsnru.l = pdp->gsnru.l;
+      memcpy(&pdp_old->gsnru.v, &pdp->gsnru.v, pdp->gsnru.l);
+      
+      /* pdp_freepdp(pdp); not nessasary anymore since never allocated */
+      pdp = pdp_old;
+      
+      /* Confirm to peer that things were "successful" */
+      return gtp_create_pdp_resp(gsn, version, peer, pack, len, pdp, 
+				 GTPCAUSE_ACC_REQ);
+    }
+    else { /* This is not the same PDP context. Delete the old one. */
+      
+      if (gsn->cb_delete_context) gsn->cb_delete_context(pdp_old);
+      pdp_freepdp(pdp_old);
+      
+    }
+  }
+
+  pdp_newpdp(&pdp, imsi, nsapi, pdp);
+
+  /* Callback function to validata login */
+  if (gsn->cb_create_context !=0) 
+    auth = gsn->cb_create_context(pdp);
+
+  /* Now send off a reply to the peer */
+  if (!auth) {
+    return gtp_create_pdp_resp(gsn, version, peer, pack, len, pdp, 
+			       GTPCAUSE_ACC_REQ);
+  }
+  else {
+    gtp_create_pdp_resp(gsn, version, peer, pack, len, pdp, 
+			GTPCAUSE_USER_AUTH_FAIL);
+    pdp_freepdp(pdp);
+    return 0;
+  }
+}
+
+
+/* Handle Create PDP Context Response */
+int gtp_create_pdp_conf(struct gsn_t *gsn, int version,
+			struct sockaddr_in *peer, 
+			void *pack, unsigned len) {
+  struct pdp_t *pdp; 
+  union gtpie_member *ie[GTPIE_SIZE];
+  uint8_t cause, recovery;
+  void *aid = NULL;
+  uint8_t type = 0;
+
+  /* Remove packet from queue */
+  if (gtp_conf(gsn, 0, peer, pack, len, &type, &aid)) return EOF;
+  
+  /* Find the context in question */
+  if (pdp_getgtp0(&pdp, ntoh16(((union gtp_packet*)pack)->gtp0.h.flow))) {
+    gsn->err_unknownpdp++;
+    gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+		"Unknown PDP context");
+    if (gsn->cb_conf) gsn->cb_conf(type, EOF, NULL, aid);
+    return EOF;
+  }
+
+  /* Decode information elements */
+  if (gtpie_decaps(ie, pack+GTP0_HEADER_SIZE, len-GTP0_HEADER_SIZE)) {
+    gsn->invalid++;
+    gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+		"Invalid message format");
+    if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, aid);
+    return EOF;
+  }
+
+  /* Extract cause value (mandatory) */
+  if (gtpie_gettv1(ie, GTPIE_CAUSE, 0, &cause)) {
+    gsn->missing++;
+    gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+		"Missing mandatory information field");
+    if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, aid);
+    return EOF;
+  }
+
+  /* Extract recovery (optional) */
+  if (!gtpie_gettv1(ie, GTPIE_RECOVERY, 0, &recovery)) {
+    /* TODO: Handle received recovery IE */
+  }
+
+  /* Extract protocol configuration options (optional) */
+  if (!gtpie_gettlv(ie, GTPIE_PCO, 0, &pdp->pco_req.l,
+		    &pdp->pco_req.v, sizeof(pdp->pco_req.v))) {
+    /* TODO: Handle PCO IE */
+  }
+
+  /* Check all conditional information elements */
+  if (GTPCAUSE_ACC_REQ == cause) {
+
+    if (gtpie_gettv0(ie, GTPIE_QOS_PROFILE0, 0,   /* TODO: HACK only gtp0 */
+		     &pdp->qos_neg0, sizeof(pdp->qos_neg0))) {
+      gsn->missing++;
+      gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+		  "Missing conditional information field");
+      if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, aid);
+      return EOF;
+    }
+    /* pdp->qos_neg.l = 3;  * TODO: HACK only gtp0 */
+    
+    if (gtpie_gettv1(ie, GTPIE_REORDER, 0, &pdp->reorder)) {
+      gsn->missing++;
+      gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+		  "Missing conditional information field");
+      if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, aid);
+      return EOF;
+    }
+
+    if (gtpie_gettv2(ie, GTPIE_FL_DI, 0, &pdp->flru)) {
+      gsn->missing++;
+      gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+		  "Missing conditional information field");
+      if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, aid);
+      return EOF;
+    }
+    
+    if (gtpie_gettv2(ie, GTPIE_FL_C, 0, &pdp->flrc)) {
+      gsn->missing++;
+      gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+		  "Missing conditional information field");
+      if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, aid);
+      return EOF;
+    }
+    
+    if (gtpie_gettv4(ie, GTPIE_CHARGING_ID, 0, &pdp->cid)) {
+      gsn->missing++;
+      gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+		  "Missing conditional information field");
+      if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, aid);
+      return gtp_create_pdp_resp(gsn, version, peer, pack, len, pdp, 
+				 GTPCAUSE_MAN_IE_MISSING);
+    }
+    
+    if (gtpie_gettlv(ie, GTPIE_EUA, 0, &pdp->eua.l,
+		     &pdp->eua.v, sizeof(pdp->eua.v))) {
+      gsn->missing++;
+      gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+		  "Missing conditional information field");
+      if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, aid);
+      return EOF;
+    }
+    
+    if (gtpie_gettlv(ie, GTPIE_GSN_ADDR, 0, &pdp->gsnrc.l,
+		     &pdp->gsnrc.v, sizeof(pdp->gsnrc.v))) {
+      gsn->missing++;
+      gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+		  "Missing conditional information field");
+      if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, aid);
+      return EOF;
+    }
+    
+    if (gtpie_gettlv(ie, GTPIE_GSN_ADDR, 1, &pdp->gsnru.l,
+		     &pdp->gsnru.v, sizeof(pdp->gsnru.v))) {
+      gsn->missing++;
+      gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+		  "Missing conditional information field");
+      if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, aid);
+      return EOF;
+    }
+  }
+
+  if (gsn->cb_conf) gsn->cb_conf(type, cause, pdp, aid);
+
+  return 0;
+}
+
+/* Send Update PDP Context Request */
+extern int gtp_update_pdp_req(struct gsn_t *gsn, int version, void *aid,
+			      struct in_addr* inetaddr, struct pdp_t *pdp) {
+  union gtp_packet packet;
+  int length = 0;
+
+  get_default_gtp(0, &packet);
+  
+  gtpie_tv0(packet.gtp0.p, &length, GTP_MAX, GTPIE_QOS_PROFILE0, 
+	    sizeof(pdp->qos_req0), pdp->qos_req0);
+  gtpie_tv1(packet.gtp0.p, &length, GTP_MAX, GTPIE_RECOVERY, 
+	    gsn->restart_counter);
+  gtpie_tv2(packet.gtp0.p, &length, GTP_MAX, GTPIE_FL_DI, 
+	    pdp->fllu);
+  gtpie_tv2(packet.gtp0.p, &length, GTP_MAX, GTPIE_FL_C,
+	    pdp->fllc);
+  gtpie_tlv(packet.gtp0.p, &length, GTP_MAX, GTPIE_GSN_ADDR, 
+	    pdp->gsnlc.l, pdp->gsnlc.v);
+  gtpie_tlv(packet.gtp0.p, &length, GTP_MAX, GTPIE_GSN_ADDR, 
+	    pdp->gsnlu.l, pdp->gsnlu.v);
+  
+  packet.gtp0.h.type = hton8(GTP_UPDATE_PDP_REQ);
+  packet.gtp0.h.length = hton16(length);
+  packet.gtp0.h.flow = 0;
+  packet.gtp0.h.tid = (pdp->imsi & 0x0fffffffffffffff) + ((uint64_t)pdp->nsapi << 60);
+
+  return gtp_req(gsn, 0, &packet, GTP0_HEADER_SIZE+length, inetaddr, aid);
+}
+
+/* Send Update PDP Context Response */
+int gtp_update_pdp_resp(struct gsn_t *gsn, int version,
+			struct sockaddr_in *peer, 
+			void *pack, unsigned len, 
+			struct pdp_t *pdp, uint8_t cause)
+{
+  union gtp_packet packet;
+  int length = 0;
+
+  get_default_gtp(0, &packet);
+
+  gtpie_tv1(packet.gtp0.p, &length, GTP_MAX, GTPIE_CAUSE, cause);
+
+  if (cause == GTPCAUSE_ACC_REQ) {
+    gtpie_tv0(packet.gtp0.p, &length, GTP_MAX, GTPIE_QOS_PROFILE0, 
+	      sizeof(pdp->qos_sub0), pdp->qos_sub0);
+    gtpie_tv1(packet.gtp0.p, &length, GTP_MAX, GTPIE_RECOVERY, 
+	      gsn->restart_counter);
+    gtpie_tv2(packet.gtp0.p, &length, GTP_MAX, GTPIE_FL_DI, 
+	      pdp->fllu);
+    gtpie_tv2(packet.gtp0.p, &length, GTP_MAX, GTPIE_FL_C,
+	      pdp->fllc);
+    gtpie_tv4(packet.gtp0.p, &length, GTP_MAX, GTPIE_CHARGING_ID,
+	      0x12345678);
+    gtpie_tlv(packet.gtp0.p, &length, GTP_MAX, GTPIE_GSN_ADDR, 
+	      pdp->gsnlc.l, pdp->gsnlc.v);
+    gtpie_tlv(packet.gtp0.p, &length, GTP_MAX, GTPIE_GSN_ADDR, 
+	      pdp->gsnlu.l, pdp->gsnlu.v);
+  }
+
+  packet.gtp0.h.type = hton8(GTP_UPDATE_PDP_RSP);
+  packet.gtp0.h.length = hton16(length);
+  packet.gtp0.h.flow = hton16(pdp->flrc);
+  packet.gtp0.h.seq = ((union gtp_packet*)pack)->gtp0.h.seq;
+  packet.gtp0.h.tid = (pdp->imsi & 0x0fffffffffffffff) + ((uint64_t)pdp->nsapi << 60);
+
+  return gtp_resp(0, gsn, &packet, GTP0_HEADER_SIZE+length, peer);
+}
+
+/* Handle Update PDP Context Request */
+int gtp_update_pdp_ind(struct gsn_t *gsn, int version,
+		       struct sockaddr_in *peer, void *pack, unsigned len) {
+  struct pdp_t *pdp, *pdp2; 
+  struct pdp_t pdp_buf;
+  union gtpie_member* ie[GTPIE_SIZE];
+  uint8_t recovery;
+
+  uint16_t seq = ntoh16(((union gtp_packet*)pack)->gtp0.h.seq);
+
+  /* Is this a dublicate ? */
+  if(!gtp_dublicate(gsn, 0, peer, seq)) {
+    return 0; /* We allready send of response once */
+  }
+
+  /* Find the pdp context in question */
+  if (pdp_getgtp0(&pdp, ntoh16(((union gtp_packet*)pack)->gtp0.h.flow))) {
+    gsn->err_unknownpdp++;
+    gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+		"Unknown PDP context");
+    return gtp_update_pdp_resp(gsn, version, peer, pack, len, NULL,
+			       GTPCAUSE_NON_EXIST);
+  }
+  
+  /* Decode information elements */
+  if (gtpie_decaps(ie, pack+GTP0_HEADER_SIZE, len-GTP0_HEADER_SIZE)) {
+    gsn->invalid++;
+    gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+		"Invalid message format");
+    if (0 == version)
+      return EOF;
+    else
+      return gtp_update_pdp_resp(gsn, version, peer, pack, len, pdp, 
+				 GTPCAUSE_INVALID_MESSAGE);
+  }
+
+  pdp2 = &pdp_buf;
+  memcpy(pdp2, pdp, sizeof (struct pdp_t)); /* Generate local copy */
+
+  if (gtpie_gettv0(ie, GTPIE_QOS_PROFILE0, 0,   /* TODO: HACK only gtp0 */
+		     &pdp2->qos_req0, sizeof(pdp2->qos_req0))) {
+    gsn->missing++;
+    gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+		"Missing mandatory information field");
+    return gtp_update_pdp_resp(gsn, version, peer, pack, len, pdp2,
+			       GTPCAUSE_MAN_IE_MISSING);
+  }
+  /* pdp2->qos_req.l = 3;  * TODO: HACK only gtp0 */
+
+  /* Extract recovery (optional) */
+  if (!gtpie_gettv1(ie, GTPIE_RECOVERY, 0, &recovery)) {
+    /* TODO: Handle received recovery IE */
+  }
+
+  if (gtpie_gettv2(ie, GTPIE_FL_DI, 0, &pdp2->flru)) {
+    gsn->missing++;
+    gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+		"Missing mandatory information field");
+    return gtp_update_pdp_resp(gsn, version, peer, pack, len, pdp2, 
+			       GTPCAUSE_MAN_IE_MISSING);
+  }
+
+  if (gtpie_gettv2(ie, GTPIE_FL_C, 0, &pdp2->flrc)) {
+    gsn->missing++;
+    gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+		"Missing mandatory information field");
+    return gtp_update_pdp_resp(gsn, version, peer, pack, len, pdp2, 
+			       GTPCAUSE_MAN_IE_MISSING);
+  }
+
+  if (gtpie_gettlv(ie, GTPIE_GSN_ADDR, 0, &pdp2->gsnrc.l,
+		     &pdp2->gsnrc.v, sizeof(pdp2->gsnrc.v))) {
+    gsn->missing++;
+    gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+		"Missing mandatory information field");
+    return gtp_update_pdp_resp(gsn, version, peer, pack, len, pdp2, 
+			       GTPCAUSE_MAN_IE_MISSING);
+  }
+
+  if (gtpie_gettlv(ie, GTPIE_GSN_ADDR, 1, &pdp2->gsnru.l,
+		     &pdp2->gsnru.v, sizeof(pdp2->gsnru.v))) {
+    gsn->missing++;
+    gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+		"Missing mandatory information field");
+    return gtp_update_pdp_resp(gsn, version, peer, pack, len, pdp2, 
+			       GTPCAUSE_MAN_IE_MISSING);
+  }
+
+  /* OK! It seames as if we received a valid message */
+
+  memcpy(pdp, pdp2, sizeof (struct pdp_t)); /* Update original pdp */
+
+  /* Confirm to peer that things were "successful" */
+  return gtp_update_pdp_resp(gsn, version, peer, pack, len, pdp, 
+			     GTPCAUSE_ACC_REQ);
+}
+
+
+/* Handle Update PDP Context Response */
+int gtp_update_pdp_conf(struct gsn_t *gsn, int version,
+			struct sockaddr_in *peer, 
+			void *pack, unsigned len) {
+  struct pdp_t *pdp; 
+  union gtpie_member *ie[GTPIE_SIZE];
+  uint8_t cause, recovery;
+  void *aid = NULL;
+  uint8_t type = 0;
+  
+  /* Remove packet from queue */
+  if (gtp_conf(gsn, 0, peer, pack, len, &type, &aid)) return EOF;
+
+  /* Find the context in question */
+  if (pdp_getgtp0(&pdp, ntoh16(((union gtp_packet*)pack)->gtp0.h.flow))) {
+    gsn->err_unknownpdp++;
+    gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+		"Unknown PDP context");
+    if (gsn->cb_conf) gsn->cb_conf(type, cause, NULL, aid);
+    return EOF;
+  }
+
+  /* Decode information elements */
+  if (gtpie_decaps(ie, pack+GTP0_HEADER_SIZE, len-GTP0_HEADER_SIZE)) {
+    gsn->invalid++;
+    gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+		"Invalid message format");
+    if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, aid);
+    if (gsn->cb_delete_context) gsn->cb_delete_context(pdp);
+    pdp_freepdp(pdp);
+    return EOF;
+  }
+      
+  /* Extract cause value (mandatory) */
+  if (gtpie_gettv1(ie, GTPIE_CAUSE, 0, &cause)) {
+    gsn->missing++;
+    gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+		"Missing mandatory information field");
+    if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, aid);
+    if (gsn->cb_delete_context) gsn->cb_delete_context(pdp);
+    pdp_freepdp(pdp);
+    return EOF;
+  }
+
+  /* Extract recovery (optional) */
+  if (!gtpie_gettv1(ie, GTPIE_RECOVERY, 0, &recovery)) {
+    /* TODO: Handle received recovery IE */
+  }
+
+  /* Check all conditional information elements */
+  if (GTPCAUSE_ACC_REQ != cause) {
+    if (gsn->cb_conf) gsn->cb_conf(type, cause, pdp, aid);
+    if (gsn->cb_delete_context) gsn->cb_delete_context(pdp);
+    pdp_freepdp(pdp);
+    return 0;
+  }
+  else {
+    /* Check for missing conditionary information elements */
+    if (!(gtpie_exist(ie, GTPIE_QOS_PROFILE0, 0) &&
+	  gtpie_exist(ie, GTPIE_REORDER, 0) &&
+	  gtpie_exist(ie, GTPIE_FL_DI, 0) &&
+	  gtpie_exist(ie, GTPIE_FL_C, 0) &&
+	  gtpie_exist(ie, GTPIE_CHARGING_ID, 0) &&
+	  gtpie_exist(ie, GTPIE_EUA, 0) &&
+	  gtpie_exist(ie, GTPIE_GSN_ADDR, 0) &&
+	  gtpie_exist(ie, GTPIE_GSN_ADDR, 1))) {
+      gsn->missing++;
+      gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+		  "Missing conditional information field");
+      if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, aid);
+      if (gsn->cb_delete_context) gsn->cb_delete_context(pdp);
+      pdp_freepdp(pdp);
+      return EOF;
+    }
+
+    /* Update pdp with new values */
+    gtpie_gettv0(ie, GTPIE_QOS_PROFILE0, 0,
+		 pdp->qos_neg0, sizeof(pdp->qos_neg0));
+    gtpie_gettv1(ie, GTPIE_REORDER, 0, &pdp->reorder);
+    gtpie_gettv2(ie, GTPIE_FL_DI, 0, &pdp->flru);
+    gtpie_gettv2(ie, GTPIE_FL_C, 0, &pdp->flrc);
+    gtpie_gettv4(ie, GTPIE_CHARGING_ID, 0, &pdp->cid);
+    gtpie_gettlv(ie, GTPIE_EUA, 0, &pdp->eua.l,
+		 &pdp->eua.v, sizeof(pdp->eua.v));
+    gtpie_gettlv(ie, GTPIE_GSN_ADDR, 0, &pdp->gsnrc.l,
+		 &pdp->gsnrc.v, sizeof(pdp->gsnrc.v));
+    gtpie_gettlv(ie, GTPIE_GSN_ADDR, 1, &pdp->gsnru.l,
+		 &pdp->gsnru.v, sizeof(pdp->gsnru.v));
+    
+    if (gsn->cb_conf) gsn->cb_conf(type, cause, pdp, aid);
+    return 0; /* Succes */
+  }
+}
+
+/* Send Delete PDP Context Request */
+extern int gtp_delete_pdp_req(struct gsn_t *gsn, int version, void *aid,
+			      struct pdp_t *pdp) {
+  union gtp_packet packet;
+  int length = 0;
+  struct in_addr addr;
+
+  if (gsna2in_addr(&addr, &pdp->gsnrc)) {
+    gsn->err_address++;
+    gtp_err(LOG_ERR, __FILE__, __LINE__, "GSN address conversion failed");
+    return EOF;
+  }
+
+  get_default_gtp(0, &packet);
+
+  packet.gtp0.h.type = hton8(GTP_DELETE_PDP_REQ);
+  packet.gtp0.h.length = hton16(length);
+  packet.gtp0.h.flow = hton16(pdp->flrc);
+  packet.gtp0.h.tid = (pdp->imsi & 0x0fffffffffffffff) + ((uint64_t)pdp->nsapi << 60);
+
+  return gtp_req(gsn, 0, &packet, GTP0_HEADER_SIZE+length, &addr, aid);
+}
+
+/* Send Delete PDP Context Response */
+int gtp_delete_pdp_resp(struct gsn_t *gsn, int version,
+			struct sockaddr_in *peer, 
+			void *pack, unsigned len, 
+			struct pdp_t *pdp, uint8_t cause)
+{
+  union gtp_packet packet;
+  int length = 0;
+  uint16_t flow = 0;
+  
+  if (pdp) flow = hton16(pdp->flrc);
+  
+  get_default_gtp(0, &packet);
+
+  gtpie_tv1(packet.gtp0.p, &length, GTP_MAX, GTPIE_CAUSE, cause);
+
+  packet.gtp0.h.type = hton8(GTP_DELETE_PDP_RSP);
+  packet.gtp0.h.length = hton16(length);
+  packet.gtp0.h.flow = flow;
+  packet.gtp0.h.seq = ((union gtp_packet*)pack)->gtp0.h.seq;
+  packet.gtp0.h.tid = ((union gtp_packet*)pack)->gtp0.h.tid;
+
+  if (pdp) {
+    /* Callback function to allow application to clean up */
+    if (gsn->cb_delete_context) gsn->cb_delete_context(pdp);
+    pdp_freepdp(pdp); /* Clean up PDP context */
+  }
+  
+  return gtp_resp(0, gsn, &packet, GTP0_HEADER_SIZE+length, peer);
+}
+
+/* Handle Delete PDP Context Request */
+int gtp_delete_pdp_ind(struct gsn_t *gsn, int version,
+		       struct sockaddr_in *peer, void *pack, unsigned len) {
+  struct pdp_t *pdp; 
+  union gtpie_member* ie[GTPIE_SIZE];
+  uint16_t seq = ntoh16(((union gtp_packet*)pack)->gtp0.h.seq);
+
+  /* Is this a dublicate ? */
+  if(!gtp_dublicate(gsn, 0, peer, seq)) {
+    return 0;
+  }
+
+  /* Find the pdp context in question */
+  if (pdp_getgtp0(&pdp, ntoh16(((union gtp_packet*)pack)->gtp0.h.flow))) {
+    gsn->err_unknownpdp++;
+    gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+		"Unknown PDP context");
+    if (0 == version)
+      return gtp_delete_pdp_resp(gsn, version, peer, pack, len, NULL,
+				 GTPCAUSE_ACC_REQ);
+    else
+      return gtp_delete_pdp_resp(gsn, version, peer, pack, len, NULL,
+				 GTPCAUSE_NON_EXIST);
+  }
+  
+  /* Decode information elements */
+  if (gtpie_decaps(ie, pack+GTP0_HEADER_SIZE, len-GTP0_HEADER_SIZE)) {
+    gsn->invalid++;
+    gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+		"Invalid message format");
+    if (0 == version)
+      return EOF;
+    else
+      return gtp_delete_pdp_resp(gsn, version, peer, pack, len, pdp, 
+				 GTPCAUSE_INVALID_MESSAGE);
+  }
+
+  return gtp_delete_pdp_resp(gsn, version, peer, pack, len, pdp,
+			     GTPCAUSE_ACC_REQ);
+}
+
+
+/* Handle Delete PDP Context Response */
+int gtp_delete_pdp_conf(struct gsn_t *gsn, int version,
+			struct sockaddr_in *peer, 
+			void *pack, unsigned len) {
+  struct pdp_t *pdp; 
+  union gtpie_member *ie[GTPIE_SIZE];
+  uint8_t cause;
+  void *aid = NULL;
+  uint8_t type = 0;
+
+  /* Remove packet from queue */
+  if (gtp_conf(gsn, 0, peer, pack, len, &type, &aid)) return EOF;
+  
+  /* Find the context in question */
+  if (pdp_getgtp0(&pdp, ntoh16(((union gtp_packet*)pack)->gtp0.h.flow))) {
+    gsn->err_unknownpdp++;
+    gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+		"Unknown PDP context");
+    return EOF;
+  }
+
+  /* Decode information elements */
+  if (gtpie_decaps(ie, pack+GTP0_HEADER_SIZE, len-GTP0_HEADER_SIZE)) {
+    gsn->invalid++;
+    gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+		"Invalid message format");
+    return EOF;
+  }
+
+  /* Extract cause value */
+  if (gtpie_gettv1(ie, GTPIE_CAUSE, 0, &cause)) {
+    gsn->missing++;
+    gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+		"Missing mandatory information field");
+    return EOF;
+  }
+
+  /* Check the cause value */
+  if ((GTPCAUSE_ACC_REQ != cause) &&  (GTPCAUSE_NON_EXIST != cause)) {
+    gsn->err_cause++;
+    gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+		"Unexpected cause value received: %d", cause);
+    return EOF;
+  }
+
+  /* Callback function to allow application to clean up */
+  if (gsn->cb_conf) gsn->cb_conf(type, cause, pdp, aid);
+
+  if (gsn->cb_delete_context) gsn->cb_delete_context(pdp);
+  pdp_freepdp(pdp);  
+  
+  return 0;
+}
+
+/* Send Error Indication (response to a GPDU message */
+int gtp_error_ind_resp(struct gsn_t *gsn, int version,
+		       struct sockaddr_in *peer, 
+		       void *pack, unsigned len)
+{
+  union gtp_packet packet;
+  int length = 0;
+  
+  get_default_gtp(0, &packet);
+  
+  packet.gtp0.h.type = hton8(GTP_ERROR);
+  packet.gtp0.h.length = hton16(length);
+  packet.gtp0.h.flow = 0;
+  packet.gtp0.h.seq = ((union gtp_packet*)pack)->gtp0.h.seq;
+  packet.gtp0.h.tid = ((union gtp_packet*)pack)->gtp0.h.tid;
+  
+  return gtp_resp(0, gsn, &packet, GTP0_HEADER_SIZE+length, peer);
+}
+
+/* Handle Error Indication */
+int gtp_error_ind_conf(struct gsn_t *gsn, int version,
+		       struct sockaddr_in *peer, 
+		       void *pack, unsigned len) {
+  struct pdp_t *pdp; 
+  
+  /* Find the context in question */
+  if (pdp_tidget(&pdp, ((union gtp_packet*)pack)->gtp0.h.tid)) {
+    gsn->err_unknownpdp++;
+    gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+		"Unknown PDP context");
+    return EOF;
+  }
+
+  gsn->err_unknownpdp++; /* TODO: Change counter */
+  gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+	      "Received Error Indication");
+
+  if (gsn->cb_delete_context) gsn->cb_delete_context(pdp);
+  pdp_freepdp(pdp);
+  return 0;
+}
+
+int gtp_gpdu_ind(struct gsn_t *gsn, int version,
+		 struct sockaddr_in *peer,
+		 void *pack,
+		 unsigned len) {
+
+  /* Need to include code to verify packet src and dest addresses */
+  struct pdp_t *pdp; 
+  
+  if (pdp_getgtp0(&pdp, ntoh16(((union gtp_packet*)pack)->gtp0.h.flow))) {
+    gsn->err_unknownpdp++;
+    gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+		"Unknown PDP context");
+    return gtp_error_ind_resp(gsn, version, peer, pack, len);
+ 
+  }
+  
+  /* Callback function */
+  if (gsn->cb_gpdu !=0)
+    return gsn->cb_gpdu(pdp, pack+20, len-20); /* TODO ???? */
+
+  return 0;
+}
+
+
+/* Receives GTP packet and sends off for further processing 
+ * Function will check the validity of the header. If the header
+ * is not valid the packet is either dropped or a version not 
+ * supported is returned to the peer. 
+ * TODO: Need to decide on return values! */
+int gtp_decaps(struct gsn_t *gsn)
+{
+  unsigned char buffer[PACKET_MAX + 64 /*TODO: ip header */ ];
+  int status, ip_len = 0;
+  struct sockaddr_in peer;
+  int peerlen;
+  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"); */
+
+  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 */
+  }
+  
+  /* 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:
+    {
+      gsn->unknown++;
+      gtp_errpack(LOG_ERR, __FILE__, __LINE__, &peer, buffer, status,
+		  "Unknown GTP message type received");
+      return -1;
+    }
+  }
+}
+
+int gtp_gpdu(struct gsn_t *gsn, struct pdp_t* pdp, 
+	     void *pack, unsigned len)
+{
+  union gtp_packet packet;
+  struct sockaddr_in addr;
+
+  /*printf("gtp_encaps start\n");
+    print_packet(pack, len);*/
+
+  memset(&addr, 0, sizeof(addr));
+  addr.sin_family = AF_INET;
+
+  memcpy(&addr.sin_addr, pdp->gsnru.v,pdp->gsnru.l); /* TODO range check */
+  addr.sin_port = htons(GTP0_PORT);
+
+  get_default_gtp(0, &packet);
+  packet.gtp0.h.type = hton8(GTP_GPDU);
+  packet.gtp0.h.length = hton16(len);
+  packet.gtp0.h.seq = hton16(pdp->gtpsntx++);
+  packet.gtp0.h.flow = hton16(pdp->flru);
+  packet.gtp0.h.tid = (pdp->imsi & 0x0fffffffffffffff) + ((uint64_t)pdp->nsapi << 60);
+
+  if (len > sizeof (union gtp_packet) - sizeof(struct gtp0_header)) {
+    gsn->err_memcpy++;
+    gtp_err(LOG_ERR, __FILE__, __LINE__, 
+	    "Memcpy failed");
+    return EOF;
+    }
+
+  memcpy(packet.gtp0.p, pack, len); /* TODO Should be avoided! */
+  
+  if (sendto(gsn->fd, &packet, GTP0_HEADER_SIZE+len, 0,
+	     (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+    gsn->err_sendto++;
+    gtp_err(LOG_ERR, __FILE__, __LINE__, "Sendto(fd=%d, msg=%lx, len=%d) failed: Error = %s", gsn->fd, (unsigned long) &packet, GTP0_HEADER_SIZE+len, strerror(errno));
+    return EOF;
+  }
+  return 0;
+}
+
+
+/* ***********************************************************
+ * Conversion functions
+ *************************************************************/
+
+int char2ul_t(char* src, struct ul_t dst) {
+  dst.l = strlen(src)+1;
+  dst.v = malloc(dst.l);
+  dst.v[0] = dst.l - 1;
+  memcpy(&dst.v[1], src, dst.v[0]);
+  return 0;
+}
+
+/* ***********************************************************
+ * IP address conversion functions
+ * There exist several types of address representations:
+ * - eua: End User Address. (29.060, 7.7.27, message type 128) 
+ *   Used for signalling address to mobile station. Supports IPv4
+ *   IPv6 x.25 etc. etc.
+ * - gsna: GSN Address. (29.060, 7.7.32, message type 133): IP address
+ *   of GSN. If length is 4 it is IPv4. If length is 16 it is IPv6.
+ * - in_addr: IPv4 address struct.
+ * - sockaddr_in: Socket API representation of IP address and
+ *   port number.
+ *************************************************************/
+
+int ipv42eua(struct ul66_t *eua, struct in_addr *src) {
+  eua->v[0] = 0xf1; /* IETF */
+  eua->v[1] = 0x21; /* IPv4 */
+  if (src) {
+    eua->l = 6;
+    memcpy(&eua->v[2], src, 4);
+  }
+  else 
+    {
+      eua->l = 2;
+    }
+  return 0;
+}
+
+int eua2ipv4(struct in_addr *dst, struct ul66_t *eua) {
+  if ((eua->l != 6) || 
+      (eua->v[0] != 0xf1) || 
+      (eua->v[1] = 0x21)) 
+    return -1; /* Not IPv4 address*/
+  memcpy(dst, &eua->v[2], 4);
+  return 0;
+}
+
+int gsna2in_addr(struct in_addr *dst, struct ul16_t *gsna) {
+  memset(dst, 0, sizeof(struct in_addr));
+  if (gsna->l != 4) return EOF; /* Return if not IPv4 */
+  memcpy(dst, gsna->v, gsna->l);
+  return 0;
+}
+
+int in_addr2gsna(struct ul16_t *gsna, struct in_addr *src) {
+  memset(gsna, 0, sizeof(struct ul16_t));
+  gsna->l = 4;
+  memcpy(gsna->v, src, gsna->l);
+  return 0;
+}
+
diff --git a/gtp/gtp.h b/gtp/gtp.h
new file mode 100644
index 0000000..2a4e57a
--- /dev/null
+++ b/gtp/gtp.h
@@ -0,0 +1,339 @@
+/* 
+ *  OpenGGSN - Gateway GPRS Support Node
+ *  Copyright (C) 2002 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 _GTP_H
+#define _GTP_H
+
+#define GTP0_PORT	3386
+#define GTP1C_PORT	2123
+#define GTP1U_PORT	2152
+#define PACKET_MAX      8196
+
+#define GTP_MAX  0xffff         /* TODO: Choose right number */
+#define GTP0_HEADER_SIZE 20
+#define GTP1_HEADER_SIZE_SHORT  8
+#define GTP1_HEADER_SIZE_LONG  12
+
+#define SYSLOG_PRINTSIZE 255
+#define ERRMSG_SIZE 255
+
+#define RESTART_FILE "gsn_restart"
+#define NAMESIZE 1024
+
+/* GTP version 1 message type definitions. Also covers version 0 except *
+ * for anonymous PDP context which was superceded in version 1 */
+
+/* 0 For future use. */
+#define GTP_ECHO_REQ          1 /* Echo Request */
+#define GTP_ECHO_RSP          2 /* Echo Response */
+#define GTP_NOT_SUPPORTED     3 /* Version Not Supported */
+#define GTP_ALIVE_REQ         4 /* Node Alive Request */
+#define GTP_ALIVE_RSP         5 /* Node Alive Response */
+#define GTP_REDIR_REQ         6 /* Redirection Request */
+#define GTP_REDIR_RSP         7 /* Redirection Response */
+/* 8-15 For future use. */
+#define GTP_CREATE_PDP_REQ   16 /* Create PDP Context Request */
+#define GTP_CREATE_PDP_RSP   17 /* Create PDP Context Response */
+#define GTP_UPDATE_PDP_REQ   18 /* Update PDP Context Request */
+#define GTP_UPDATE_PDP_RSP   19 /* Update PDP Context Response */
+#define GTP_DELETE_PDP_REQ   20 /* Delete PDP Context Request */
+#define GTP_DELETE_PDP_RSP   21 /* Delete PDP Context Response */
+/* 22-25 For future use. */ /* In version GTP 1 anonomous PDP context */
+#define GTP_ERROR            26 /* Error Indication */
+#define GTP_PDU_NOT_REQ      27 /* PDU Notification Request */
+#define GTP_PDU_NOT_RSP      28 /* PDU Notification Response */
+#define GTP_PDU_NOT_REJ_REQ  29 /* PDU Notification Reject Request */
+#define GTP_PDU_NOT_REJ_RSP  30 /* PDU Notification Reject Response */
+#define GTP_SUPP_EXT_HEADER  31 /* Supported Extension Headers Notification */
+#define GTP_SND_ROUTE_REQ    32 /* Send Routeing Information for GPRS Request */
+#define GTP_SND_ROUTE_RSP    33 /* Send Routeing Information for GPRS Response */
+#define GTP_FAILURE_REQ      34 /* Failure Report Request */
+#define GTP_FAILURE_RSP      35 /* Failure Report Response */
+#define GTP_MS_PRESENT_REQ   36 /* Note MS GPRS Present Request */
+#define GTP_MS_PRESENT_RSP   37 /* Note MS GPRS Present Response */
+/* 38-47 For future use. */ 
+#define GTP_IDEN_REQ         48 /* Identification Request */
+#define GTP_IDEN_RSP         49 /* Identification Response */
+#define GTP_SGSN_CONTEXT_REQ 50 /* SGSN Context Request */
+#define GTP_SGSN_CONTEXT_RSP 51 /* SGSN Context Response */
+#define GTP_SGSN_CONTEXT_ACK 52 /* SGSN Context Acknowledge */
+#define GTP_FWD_RELOC_REQ    53 /* Forward Relocation Request */
+#define GTP_FWD_RELOC_RSP    54 /* Forward Relocation Response */
+#define GTP_FWD_RELOC_COMPL  55 /* Forward Relocation Complete */
+#define GTP_RELOC_CANCEL_REQ 56 /* Relocation Cancel Request */
+#define GTP_RELOC_CANCEL_RSP 57 /* Relocation Cancel Response */
+#define GTP_FWD_SRNS         58 /* Forward SRNS Context */
+#define GTP_FWD_RELOC_ACK    59 /* Forward Relocation Complete Acknowledge */
+#define GTP_FWD_SRNS_ACK     60 /* Forward SRNS Context Acknowledge */
+/* 61-239 For future use. */
+#define GTP_DATA_TRAN_REQ   240 /* Data Record Transfer Request */
+#define GTP_DATA_TRAN_RSP   241 /* Data Record Transfer Response */
+/* 242-254 For future use. */
+#define GTP_GPDU            255 /* G-PDU */
+
+/* GTP 0 header. 
+ * Explanation to some of the fields:
+ * SNDCP NPDU Number flag = 0 except for inter SGSN handover situations
+ * SNDCP N-PDU LCC Number 0 = 0xff except for inter SGSN handover situations
+ * Sequence number. Used for reliable delivery of signalling messages, and
+ *   to discard "illegal" data messages.
+ * Flow label. Is used to point a particular PDP context. Is used in data
+ *   messages as well as signalling messages related to a particular context.
+ * Tunnel ID is IMSI+NSAPI. Unique identifier of PDP context. Is somewhat
+ *   redundant because the header also includes flow. */
+
+struct gtp0_header {            /*    Descriptions from 3GPP 09.60 */
+	u_int8_t  flags;        /* 01 bitfield, with typical values */
+                                /*    000..... Version: 1 (0) */
+                                /*    ...1111. Spare (7) */
+                                /*    .......0 SNDCP N-PDU Number flag (0) */
+	u_int8_t  type;	        /* 02 Message type. T-PDU = 0xff */
+	u_int16_t length;	/* 03 Length (of G-PDU excluding header) */
+	u_int16_t seq;	        /* 05 Sequence Number */
+	u_int16_t flow;	        /* 07 Flow Label ( = 0 for signalling) */
+	u_int8_t  number;	/* 09 SNDCP N-PDU LCC Number ( 0 = 0xff) */
+	u_int8_t  spare1;	/* 10 Spare */
+	u_int8_t  spare2;	/* 11 Spare */
+	u_int8_t  spare3;	/* 12 Spare */
+	u_int64_t tid;		/* 13 Tunnel ID */
+};                              /* 20 */
+
+struct gtp1_header_short {      /*     Descriptions from 3GPP 29060 */
+	u_int8_t  flags;        /* 01 bitfield, with typical values */
+                                /*    000..... Version: 1 */
+                                /*    ...1.... Protocol Type: GTP=1, GTP'=0 */
+                                /*    ....1... Spare = 1 */
+                                /*    .....1.. Extension header flag: 1 */
+                                /*    ......1. Sequence number flag: 1 */
+                                /*    .......0 PN: N-PDU Number flag */
+	u_int8_t  type;		/* 02 Message type. T-PDU = 0xff */
+	u_int16_t length;	/* 03 Length (of IP packet or signalling) */
+	u_int64_t tid;		/* 05 - 08 Tunnel ID */
+};
+
+struct gtp1_header_long {       /*     Descriptions from 3GPP 29060 */
+	u_int8_t  flags;        /* 01 bitfield, with typical values */
+                                /*    000..... Version: 1 */
+                                /*    ...1.... Protocol Type: GTP=1, GTP'=0 */
+                                /*    ....1... Spare = 1 */
+                                /*    .....1.. Extension header flag: 1 */
+                                /*    ......1. Sequence number flag: 1 */
+                                /*    .......0 PN: N-PDU Number flag */
+	u_int8_t  type;		/* 02 Message type. T-PDU = 0xff */
+	u_int16_t length;	/* 03 Length (of IP packet or signalling) */
+	u_int64_t tid;		/* 05 Tunnel ID */
+	u_int16_t seq;	        /* 10 Sequence Number */
+	u_int8_t  npdu;		/* 11 N-PDU Number */
+	u_int8_t  next;		/* 12 Next extension header type. Empty = 0 */
+};
+
+struct gtp0_packet {
+  struct gtp0_header h;
+  u_int8_t p[GTP_MAX];
+} __attribute__((packed));
+
+struct gtp1_packet_short {
+  struct gtp1_header_short h;
+  u_int8_t p[GTP_MAX];
+} __attribute__((packed));
+
+struct gtp1_packet_long {
+  struct gtp1_header_long h;
+  u_int8_t p[GTP_MAX];
+} __attribute__((packed));
+
+union gtp_packet {
+  u_int8_t flags;
+  struct gtp0_packet       gtp0;
+  struct gtp1_packet_short gtp1s;
+  struct gtp1_packet_long  gtp1l;
+} __attribute__((packed)) h;
+
+
+
+
+/* ***********************************************************
+ * Information storage for each gsn instance
+ *
+ * Normally each instance of the application corresponds to
+ * one instance of a gsn. 
+ * 
+ * In order to avoid global variables in the application, and
+ * also in order to allow several instances of a gsn in the same
+ * application this struct is provided in order to store all
+ * relevant information related to the gsn.
+ * 
+ * Note that this does not include information storage for '
+ * each pdp context. This is stored in another struct.
+ *************************************************************/
+
+struct gsn_t {
+  /* Parameters related to the network interface */
+
+  int         fd;       /* File descriptor to network interface */
+  struct in_addr gsnc;  /* IP address of this gsn for signalling */
+  struct in_addr gsnu;  /* IP address of this gsn for user traffic */
+
+  /* Parameters related to signalling messages */
+  uint16_t seq_next;    /* Next sequence number to use */
+  int seq_first;        /* First packet in queue (oldest timeout) */
+  int seq_last;         /* Last packet in queue (youngest timeout) */
+
+  unsigned char restart_counter; /* Increment on restart. Stored on disk */
+  char *statedir;       /* Disk location for permanent storage */
+
+  struct queue_t *queue_req;  /* Request queue */
+  struct queue_t *queue_resp; /* Response queue */
+
+  /* Call back functions */
+  int (*cb_delete_context) (struct pdp_t*);
+  int (*cb_create_context) (struct pdp_t*);
+  int (*cb_conf) (int type, int cause, struct pdp_t *pdp, void* aid);
+  int (*cb_gpdu) (struct pdp_t* pdp, void* pack, unsigned len);
+
+  /* Counters */
+  
+  uint64_t err_socket;      /* Number of socket errors */
+  uint64_t err_readfrom;    /* Number of readfrom errors */
+  uint64_t err_sendto;      /* Number of sendto errors */
+  uint64_t err_memcpy;      /* Number of memcpy */
+  uint64_t err_queuefull;   /* Number of times queue was full */
+  uint64_t err_seq;         /* Number of seq out of range */
+  uint64_t err_address;     /* GSN address conversion failed */
+  uint64_t err_unknownpdp;  /* GSN address conversion failed */
+  uint64_t err_unknowntid;  /* Application supplied unknown imsi+nsapi */
+  uint64_t err_cause;       /* Unexpected cause value received */
+  uint64_t err_outofpdp;    /* Out of storage for PDP contexts */
+
+  uint64_t empty;       /* Number of empty packets */
+  uint64_t unsup;       /* Number of unsupported version 29.60 11.1.1 */
+  uint64_t tooshort;    /* Number of too short headers 29.60 11.1.2 */
+  uint64_t unknown;     /* Number of unknown messages 29.60 11.1.3 */
+  uint64_t unexpect;    /* Number of unexpected messages 29.60 11.1.4 */
+  uint64_t dublicate;   /* Number of dublicate or unsolicited replies */
+  uint64_t missing;     /* Number of missing mandatory field messages */
+  uint64_t invalid;     /* Number of invalid message format messages */
+};
+
+
+/* External API functions */
+
+extern const char* gtp_version();
+extern int gtp_new(struct gsn_t **gsn, char *statedir, struct in_addr *listen);
+extern int gtp_free(struct gsn_t *gsn);
+
+extern int gtp_newpdp(struct gsn_t *gsn, struct pdp_t **pdp,
+		      uint64_t imsi, uint8_t nsapi);
+extern int gtp_freepdp(struct gsn_t *gsn, struct pdp_t *pdp);
+
+extern int gtp_create_context(struct gsn_t *gsn, struct pdp_t *pdp, void *aid,
+			      struct in_addr* inetaddr);
+extern int gtp_update_context(struct gsn_t *gsn, struct pdp_t *pdp, void *aid,
+			      struct in_addr* inetaddr);
+extern int gtp_delete_context(struct gsn_t *gsn, struct pdp_t *pdp, void *aid);
+
+extern int gtp_gpdu(struct gsn_t *gsn, struct pdp_t *pdp,
+		    void *pack, unsigned len);
+
+extern int gtp_fd(struct gsn_t *gsn);
+extern int gtp_decaps(struct gsn_t *gsn);
+extern int gtp_retrans(struct gsn_t *gsn);
+extern int gtp_retranstimeout(struct gsn_t *gsn, struct timeval *timeout);
+
+/*
+extern int gtp_set_cb_newpdp(struct gsn_t *gsn, 
+			     int (*cb) (struct pdp_t*));
+extern int gtp_set_cb_freepdp(struct gsn_t *gsn, 
+			      int (*cb) (struct pdp_t*));
+extern int gtp_set_cb_create_pdp_ind(struct gsn_t *gsn, 
+				     int (*cb) (struct pdp_t*));
+extern int gtp_set_cb_create_pdp_conf(struct gsn_t *gsn, 
+				      int (*cb) (struct pdp_t*, int));
+extern int gtp_set_cb_update_pdp_conf(struct gsn_t *gsn, 
+				      int (*cb) (struct pdp_t*, int, int));
+extern int gtp_set_cb_delete_pdp_ind(struct gsn_t *gsn, 
+				     int (*cb) (struct pdp_t*));
+extern int gtp_set_cb_delete_pdp_conf(struct gsn_t *gsn, 
+				      int (*cb) (struct pdp_t*, int));
+*/
+
+extern int gtp_set_cb_delete_context(struct gsn_t *gsn, 
+	     int (*cb_delete_context) (struct pdp_t* pdp));
+extern int gtp_set_cb_create_context(struct gsn_t *gsn,
+	     int (*cb_create_context) (struct pdp_t* pdp));
+extern int gtp_set_cb_conf(struct gsn_t *gsn,
+             int (*cb) (int type, int cause, struct pdp_t* pdp, void *aid));
+extern int gtp_set_cb_gpdu(struct gsn_t *gsn,
+	     int (*cb_gpdu) (struct pdp_t* pdp, void* pack, unsigned len));
+
+
+/* Internal functions (not part of the API */
+
+extern int gtp_echo_req(struct gsn_t *gsn, struct in_addr *inetaddrs);
+extern int gtp_echo_resp(struct gsn_t *gsn, struct sockaddr_in *peer,
+			 void *pack, unsigned len);
+extern int gtp_echo_ind(struct gsn_t *gsn, struct sockaddr_in *peer,
+			void *pack, unsigned len);
+extern int gtp_echo_conf(struct gsn_t *gsn, struct sockaddr_in *peer,
+			 void *pack, unsigned len);
+
+extern int gtp_unsup_resp(struct gsn_t *gsn, struct sockaddr_in *peer,
+			  void *pack, unsigned len);
+extern int gtp_unsup_conf(struct gsn_t *gsn, struct sockaddr_in *peer,
+			  void *pack, unsigned len);
+
+extern int gtp_create_pdp_req(struct gsn_t *gsn, int version, void *aid,
+			      struct in_addr* inetaddr, struct pdp_t *pdp);
+
+extern int gtp_create_pdp_resp(struct gsn_t *gsn, int version,
+			       struct sockaddr_in *peer, 
+			       void *pack, unsigned len, 
+			       struct pdp_t *pdp, uint8_t cause);
+
+extern int gtp_create_pdp_ind(struct gsn_t *gsn, int version,
+			      struct sockaddr_in *peer, 
+			      void *pack, unsigned len);
+
+extern int gtp_create_pdp_conf(struct gsn_t *gsn, int version,
+			       struct sockaddr_in *peer,
+			       void *pack, unsigned len);
+
+extern int gtp_update_pdp_req(struct gsn_t *gsn, int version, void *aid,
+			      struct in_addr* inetaddr, struct pdp_t *pdp);
+
+extern int gtp_delete_pdp_req(struct gsn_t *gsn, int version, void *aid,
+			      struct pdp_t *pdp);
+
+extern int gtp_delete_pdp_resp(struct gsn_t *gsn, int version,
+			       struct sockaddr_in *peer, 
+			       void *pack, unsigned len, 
+			       struct pdp_t *pdp, uint8_t cause);
+
+extern int gtp_delete_pdp_ind(struct gsn_t *gsn, int version,
+			      struct sockaddr_in *peer, 
+			      void *pack, unsigned len);
+
+extern int gtp_delete_pdp_conf(struct gsn_t *gsn, int version,
+			       struct sockaddr_in *peer,
+			       void *pack, unsigned len);
+
+
+extern int ipv42eua(struct ul66_t *eua, struct in_addr *src);
+extern int eua2ipv4(struct in_addr *dst, struct ul66_t *eua);
+extern int gsna2in_addr(struct in_addr *dst, struct ul16_t *gsna);
+extern int in_addr2gsna(struct ul16_t *gsna, struct in_addr *src);
+
+#endif	/* !_GTP_H */
diff --git a/gtp/gtpie.c b/gtp/gtpie.c
new file mode 100644
index 0000000..8fd4a20
--- /dev/null
+++ b/gtp/gtpie.c
@@ -0,0 +1,513 @@
+/* 
+ *  OpenGGSN - Gateway GPRS Support Node
+ *  Copyright (C) 2002 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):
+ * 
+ */
+
+/*
+ * gtpie.c: Contains functions to encapsulate and decapsulate GTP 
+ * information elements 
+ *
+ *
+ * Encapsulation
+ * - gtpie_tlv, gtpie_tv0, gtpie_tv1, gtpie_tv2 ... Adds information
+ * elements to a buffer.
+ *
+ * Decapsulation
+ *  - gtpie_decaps: Returns array with pointers to information elements.
+ *  - getie_getie: Returns the pointer of a particular element.
+ *  - gtpie_gettlv: Copies tlv information element. Return 0 on success.
+ *  - gtpie_gettv: Copies tv information element. Return 0 on success.
+ *
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <string.h>
+
+#include "gtpie.h"
+
+int gtpie_tlv(void *p, int *length, int size, u_int8_t t, int l, void *v) {
+  if ((*length + 3 + l) >= size) return 1;
+  ((union gtpie_member*) (p + *length))->tlv.t = hton8(t);
+  ((union gtpie_member*) (p + *length))->tlv.l = hton16(l);
+  memcpy((void*) (p + *length +3), v, l);
+  *length += 3 + l;
+  return 0;
+}
+
+int gtpie_tv0(void *p, int *length, int size, u_int8_t t, int l, u_int8_t *v) {
+  if ((*length + 1 + l) >= size) return 1;
+  ((union gtpie_member*) (p + *length))->tv0.t = hton8(t);
+  memcpy((void*) (p + *length +1), v, l);
+  *length += 1 + l;
+  return 0;
+}
+
+int gtpie_tv1(void *p, int *length, int size, u_int8_t t, u_int8_t v) {
+  if ((*length + 2) >= size) return 1;
+  ((union gtpie_member*) (p + *length))->tv1.t = hton8(t);
+  ((union gtpie_member*) (p + *length))->tv1.v = hton8(v);
+  *length += 2;
+  return 0;
+}
+
+int gtpie_tv2(void *p, int *length, int size, u_int8_t t, u_int16_t v) {
+  if ((*length + 3) >= size) return 1;
+  ((union gtpie_member*) (p + *length))->tv2.t = hton8(t);
+  ((union gtpie_member*) (p + *length))->tv2.v = hton16(v);
+  *length += 3;
+  return 0;
+}
+
+int gtpie_tv4(void *p, int *length, int size, u_int8_t t, u_int32_t v) {
+  if ((*length + 5) >= size) return 1;
+  ((union gtpie_member*) (p + *length))->tv4.t = hton8(t);
+  ((union gtpie_member*) (p + *length))->tv4.v = hton32(v);
+  *length += 5;
+  return 0;
+}
+
+int gtpie_getie(union gtpie_member* ie[], int type, int instance) {
+  int j;
+  for (j=0; j< GTPIE_SIZE; j++) {
+    if ((ie[j] != 0) && (ie[j]->t == type)) {
+      if (instance-- == 0) return j;
+    }
+  }
+  return -1;
+}
+
+int gtpie_exist(union gtpie_member* ie[], int type, int instance) {
+  int j;
+  for (j=0; j< GTPIE_SIZE; j++) {
+    if ((ie[j] != 0) && (ie[j]->t == type)) {
+      if (instance-- == 0) return 1;
+    }
+  }
+  return 0;
+}
+
+int gtpie_gettlv(union gtpie_member* ie[], int type, int instance,
+		 int *length, void *dst, int size){
+  int ien;
+  ien = gtpie_getie(ie, type, instance);
+  if (ien>=0) {
+    *length = ntoh16(ie[ien]->tlv.l);
+    if (*length <= size)
+      memcpy(dst, ie[ien]->tlv.v, *length);
+    else
+      return EOF;
+  }
+  return 0;
+}
+
+int gtpie_gettv0(union gtpie_member* ie[], int type, int instance,
+		void *dst, int size){
+  int ien;
+  ien = gtpie_getie(ie, type, instance);
+  if (ien>=0)
+    memcpy(dst, ie[ien]->tv0.v, size);
+  else
+    return EOF;
+  return 0;
+}
+
+int gtpie_gettv1(union gtpie_member* ie[], int type, int instance, 
+		 uint8_t *dst){
+  int ien;
+  ien = gtpie_getie(ie, type, instance);
+  if (ien>=0)
+    *dst = ntoh8(ie[ien]->tv1.v);
+  else
+    return EOF;
+  return 0;
+}
+
+int gtpie_gettv2(union gtpie_member* ie[], int type, int instance, 
+		 uint16_t *dst){
+  int ien;
+  ien = gtpie_getie(ie, type, instance);
+  if (ien>=0)
+    *dst = ntoh16(ie[ien]->tv2.v);
+  else
+    return EOF;
+  return 0;
+}
+
+int gtpie_gettv4(union gtpie_member* ie[], int type, int instance, 
+		 uint32_t *dst){
+  int ien;
+  ien = gtpie_getie(ie, type, instance);
+  if (ien>=0)
+    *dst = ntoh32(ie[ien]->tv4.v);
+  else
+    return EOF;
+  return 0;
+}
+
+int gtpie_decaps(union gtpie_member* ie[], void *pack, unsigned len) {
+  int i;
+  int j = 0;
+  unsigned char *p;
+  unsigned char *end;
+
+  end = (unsigned char*) pack + len;
+  p = pack;
+
+  memset(ie, 0, 4 * GTPIE_SIZE);
+
+  while (p<end) {
+    if (GTPIE_DEBUG) {
+      printf("The packet looks like this:\n");
+      for( i=0; i<(end-p); i++) {
+	printf("%02x ", (unsigned char)*(char *)(p+i));
+	if (!((i+1)%16)) printf("\n");
+      };
+    printf("\n");
+    }
+
+    switch (*p) {
+    case GTPIE_CAUSE:             /* TV GTPIE types with value length 1 */
+    case GTPIE_REORDER:
+    case GTPIE_MAP_CAUSE:
+    case GTPIE_MS_VALIDATED:
+    case GTPIE_RECOVERY:
+    case GTPIE_SELECTION_MODE:
+    case GTPIE_TEARDOWN:
+    case GTPIE_NSAPI:
+    case GTPIE_RANAP_CAUSE:
+    case GTPIE_RP_SMS:
+    case GTPIE_RP:
+    case GTPIE_MS_NOT_REACH:
+      if (j<GTPIE_SIZE) {
+	ie[j] = (union gtpie_member*) p;
+	if (GTPIE_DEBUG) printf("GTPIE TV1 found. Type %d, value %d\n", 
+	       ie[j]->tv1.t, ie[j]->tv1.v);
+	p+= 1 + 1;
+	j++;
+      }
+      break;
+    case GTPIE_FL_DI:           /* TV GTPIE types with value length 2 */
+    case GTPIE_FL_C:
+    case GTPIE_PFI:  
+    case GTPIE_CHARGING_C:
+    case GTPIE_TRACE_REF:
+    case GTPIE_TRACE_TYPE:
+      if (j<GTPIE_SIZE) {
+	ie[j] = (union gtpie_member*) p;
+	if (GTPIE_DEBUG) printf("GTPIE TV2 found. Type %d, value %d\n", 
+	       ie[j]->tv2.t, ie[j]->tv2.v);
+	p+= 1 + 2;
+	j++;
+      }
+      break;
+    case GTPIE_QOS_PROFILE0:    /* TV GTPIE types with value length 3 */
+    case GTPIE_P_TMSI_S:
+      if (j<GTPIE_SIZE) {
+	ie[j] = (union gtpie_member*) p;
+	if (GTPIE_DEBUG) printf("GTPIE TV 3 found. Type %d, value %d, %d, %d\n", 
+				ie[j]->tv0.t, ie[j]->tv0.v[0], 
+				ie[j]->tv0.v[1], ie[j]->tv0.v[2]);
+	p+= 1 + 3;
+	j++;
+      }
+      break;
+    case GTPIE_TLLI:            /* TV GTPIE types with value length 4 */
+    case GTPIE_P_TMSI:
+    case GTPIE_CHARGING_ID:
+      if (j<GTPIE_SIZE) {
+	/*    case GTPIE_TEI_DI: gtp1 */
+	/*    case GTPIE_TEI_C: gtp1 */
+	ie[j] = (union gtpie_member*) p;
+	if (GTPIE_DEBUG) printf("GTPIE TV 4 found. Type %d, value %d\n",
+				ie[j]->tv4.t, ie[j]->tv4.v);
+	p+= 1 + 4;
+	j++;
+      }
+      break;
+    case GTPIE_TEI_DII:         /* TV GTPIE types with value length 5 */
+      if (j<GTPIE_SIZE) {
+	ie[j] = (union gtpie_member*) p;
+	if (GTPIE_DEBUG) printf("GTPIE TV 5 found. Type %d\n", ie[j]->tv0.t);
+	p+= 1 + 5;
+	j++;
+      }
+      break;
+    case GTPIE_RAB_CONTEXT:     /* TV GTPIE types with value length 7 */
+      if (j<GTPIE_SIZE) {
+	ie[j] = (union gtpie_member*) p;
+	if (GTPIE_DEBUG) printf("GTPIE TV 7 found. Type %d\n", ie[j]->tv0.t);
+	p+= 1 + 7;
+	j++;
+      }
+      break;
+    case GTPIE_IMSI:            /* TV GTPIE types with value length 8 */
+    case GTPIE_RAI:
+      if (j<GTPIE_SIZE) {
+	ie[j] = (union gtpie_member*) p;
+	if (GTPIE_DEBUG) printf("GTPIE TV 8 found. Type %d, value 0x%llx\n",
+				ie[j]->tv0.t, ie[j]->tv8.v);
+	p+= 1 + 8;
+	j++;
+      }
+      break;
+    case GTPIE_AUTH_TRIPLET:    /* TV GTPIE types with value length 28 */
+      if (j<GTPIE_SIZE) {
+	ie[j] = (union gtpie_member*) p;
+	if (GTPIE_DEBUG) printf("GTPIE TV 28 found. Type %d\n", ie[j]->tv0.t);
+	p+= 1 + 28;
+	j++;
+      }
+      break;
+    case GTPIE_EXT_HEADER_T:   /* GTP extension header */
+      if (j<GTPIE_SIZE) {
+	ie[j] = (union gtpie_member*) p;
+	if (GTPIE_DEBUG) printf("GTPIE GTP extension header found. Type %d\n",
+				ie[j]->ext.t);
+	p+= 2 + ntoh8(ie[j]->ext.l);
+	j++;
+      }
+      break;
+    case GTPIE_EUA:            /* TLV GTPIE types with variable length */
+    case GTPIE_MM_CONTEXT:
+    case GTPIE_PDP_CONTEXT:
+    case GTPIE_APN:
+    case GTPIE_PCO:
+    case GTPIE_GSN_ADDR:
+    case GTPIE_MSISDN:
+    case GTPIE_QOS_PROFILE:
+    case GTPIE_AUTH_QUINTUP:
+    case GTPIE_TFT:
+    case GTPIE_TARGET_INF:
+    case GTPIE_UTRAN_TRANS:
+    case GTPIE_RAB_SETUP:
+    case GTPIE_TRIGGER_ID:
+    case GTPIE_OMC_ID:
+    case GTPIE_CHARGING_ADDR:
+    case GTPIE_PRIVATE:
+      if (j<GTPIE_SIZE) {
+	ie[j] = (union gtpie_member*) p;
+	if (GTPIE_DEBUG) printf("GTPIE TLV found. Type %d\n", ie[j]->tlv.t);
+	p+= 3 + ntoh16(ie[j]->tlv.l);
+	j++;
+      }
+      break;
+    default:
+      if (GTPIE_DEBUG) printf("GTPIE something unknown. Type %d\n", *p);
+      return EOF; /* We received something unknown */
+    }	
+  }
+  if (p==end) {
+    if (GTPIE_DEBUG) printf("GTPIE normal return. %lx %lx\n",
+			    (unsigned long) p, (unsigned long) end);
+    return 0; /* We landed at the end of the packet: OK */
+  }
+  else {
+    if (GTPIE_DEBUG) printf("GTPIE exceeded end of packet. %lx %lx\n",
+			    (unsigned long) p, (unsigned long) end);
+    return EOF; /* We exceeded the end of the packet: Error */
+  }
+}
+
+int gtpie_encaps(union gtpie_member *ie[], void *pack, unsigned *len) {
+  int i;
+  unsigned char *p;
+  unsigned char *end;
+  union gtpie_member *m;
+  int iesize;
+  
+  p = pack;
+
+  memset(pack, 0, GTPIE_MAX);
+  end = p + GTPIE_MAX;
+  for (i=1; i<GTPIE_SIZE; i++) if (ie[i] != 0) {
+    if (GTPIE_DEBUG) printf("gtpie_encaps. Type %d\n", i); 
+    m=(union gtpie_member *)p;
+    switch (i) {
+    case GTPIE_CAUSE:             /* TV GTPIE types with value length 1 */
+    case GTPIE_REORDER:
+    case GTPIE_MAP_CAUSE:
+    case GTPIE_MS_VALIDATED:
+    case GTPIE_RECOVERY:
+    case GTPIE_SELECTION_MODE:
+    case GTPIE_TEARDOWN:
+    case GTPIE_NSAPI:
+    case GTPIE_RANAP_CAUSE:
+    case GTPIE_RP_SMS:
+    case GTPIE_RP:
+    case GTPIE_MS_NOT_REACH:
+      iesize = 2;
+      break;
+    case GTPIE_FL_DI:           /* TV GTPIE types with value length 2 */
+    case GTPIE_FL_C:
+    case GTPIE_PFI:
+    case GTPIE_CHARGING_C:
+    case GTPIE_TRACE_REF:
+    case GTPIE_TRACE_TYPE:
+      iesize = 3;
+      break;
+    case GTPIE_QOS_PROFILE0:    /* TV GTPIE types with value length 3 */
+    case GTPIE_P_TMSI_S:
+      iesize = 4;
+      break;
+    case GTPIE_TLLI:            /* TV GTPIE types with value length 4 */
+    case GTPIE_P_TMSI:
+      /* case GTPIE_TEI_DI: only in gtp1*/
+      /* case GTPIE_TEI_C: only in gtp1*/
+    case GTPIE_CHARGING_ID:
+      iesize = 5;
+      break;
+    case GTPIE_TEI_DII:         /* TV GTPIE types with value length 5 */
+      iesize = 6;
+      break;
+    case GTPIE_RAB_CONTEXT:     /* TV GTPIE types with value length 7 */
+      iesize = 8;
+      break;
+    case GTPIE_IMSI:            /* TV GTPIE types with value length 8 */
+    case GTPIE_RAI:
+      iesize = 9;
+      break;
+    case GTPIE_AUTH_TRIPLET:    /* TV GTPIE types with value length 28 */
+      iesize = 29;
+      break;
+    case GTPIE_EXT_HEADER_T:   /* GTP extension header */
+      iesize = 2 + hton8(ie[i]->ext.l);
+      break;
+    case GTPIE_EUA:            /* TLV GTPIE types with length length 2 */
+    case GTPIE_MM_CONTEXT:
+    case GTPIE_PDP_CONTEXT:
+    case GTPIE_APN:
+    case GTPIE_PCO:
+    case GTPIE_GSN_ADDR:
+    case GTPIE_MSISDN:
+    case GTPIE_QOS_PROFILE:
+    case GTPIE_AUTH_QUINTUP:
+    case GTPIE_TFT:
+    case GTPIE_TARGET_INF:
+    case GTPIE_UTRAN_TRANS:
+    case GTPIE_RAB_SETUP:
+    case GTPIE_TRIGGER_ID:
+    case GTPIE_OMC_ID:
+    case GTPIE_CHARGING_ADDR:
+    case GTPIE_PRIVATE:
+      iesize = 3 + hton16(ie[i]->tlv.l);
+      break;
+    default:
+      return 2; /* We received something unknown */
+    }
+    if (p+iesize < end) {
+      memcpy(p, ie[i], iesize);
+      p += iesize;
+      *len += iesize;
+    }
+    else return 2;              /* Out of space */
+  }
+  return 0;
+}
+
+int gtpie_encaps2(union gtpie_member ie[], int size,
+		  void *pack, unsigned *len) {
+  int i, j;
+  unsigned char *p;
+  unsigned char *end;
+  union gtpie_member *m;
+  int iesize;
+  
+  p = pack;
+
+  memset(pack, 0, GTPIE_MAX);
+  end = p + GTPIE_MAX;
+  for (j=0; j<GTPIE_SIZE; j++) for (i=0; i<size; i++) if (ie[i].t == j) {
+    if (GTPIE_DEBUG) printf("gtpie_encaps. Number %d, Type %d\n", i, ie[i].t); 
+    m=(union gtpie_member *)p;
+    switch (ie[i].t) {
+    case GTPIE_CAUSE:             /* TV GTPIE types with value length 1 */
+    case GTPIE_REORDER:
+    case GTPIE_MAP_CAUSE:
+    case GTPIE_MS_VALIDATED:
+    case GTPIE_RECOVERY:
+    case GTPIE_SELECTION_MODE:
+    case GTPIE_TEARDOWN:
+    case GTPIE_NSAPI:
+    case GTPIE_RANAP_CAUSE:
+    case GTPIE_RP_SMS:
+    case GTPIE_RP:
+    case GTPIE_MS_NOT_REACH:
+      iesize = 2;
+      break;
+    case GTPIE_PFI:             /* TV GTPIE types with value length 2 */
+    case GTPIE_CHARGING_C:
+    case GTPIE_TRACE_REF:
+    case GTPIE_TRACE_TYPE:
+      iesize = 3;
+      break;
+    case GTPIE_QOS_PROFILE0:    /* TV GTPIE types with value length 3 */
+    case GTPIE_P_TMSI_S:
+      iesize = 4;
+      break;
+    case GTPIE_TLLI:            /* TV GTPIE types with value length 4 */
+    case GTPIE_P_TMSI:
+    case GTPIE_TEI_DI:
+    case GTPIE_TEI_C:
+      iesize = 5;
+      break;
+    case GTPIE_TEI_DII:         /* TV GTPIE types with value length 5 */
+      iesize = 6;
+      break;
+    case GTPIE_RAB_CONTEXT:     /* TV GTPIE types with value length 7 */
+      iesize = 8;
+      break;
+    case GTPIE_IMSI:            /* TV GTPIE types with value length 8 */
+    case GTPIE_RAI:
+      iesize = 9;
+      break;
+    case GTPIE_AUTH_TRIPLET:    /* TV GTPIE types with value length 28 */
+      iesize = 29;
+      break;
+    case GTPIE_EXT_HEADER_T:   /* GTP extension header */
+      iesize = 2 + hton8(ie[i].ext.l);
+      break;
+    case GTPIE_CHARGING_ID:    /* TLV GTPIE types with length length 2 */
+    case GTPIE_EUA:
+    case GTPIE_MM_CONTEXT:
+    case GTPIE_PDP_CONTEXT:
+    case GTPIE_APN:
+    case GTPIE_PCO:
+    case GTPIE_GSN_ADDR:
+    case GTPIE_MSISDN:
+    case GTPIE_QOS_PROFILE:
+    case GTPIE_AUTH_QUINTUP:
+    case GTPIE_TFT:
+    case GTPIE_TARGET_INF:
+    case GTPIE_UTRAN_TRANS:
+    case GTPIE_RAB_SETUP:
+    case GTPIE_TRIGGER_ID:
+    case GTPIE_OMC_ID:
+    case GTPIE_CHARGING_ADDR:
+    case GTPIE_PRIVATE:
+      iesize = 3 + hton16(ie[i].tlv.l);
+      break;
+    default:
+      return 2; /* We received something unknown */
+    }
+    if (p+iesize < end) {
+      memcpy(p, &ie[i], iesize);
+      p += iesize;
+      *len += iesize;
+    }
+    else return 2;              /* Out of space */
+  }
+  return 0;
+}
diff --git a/gtp/gtpie.h b/gtp/gtpie.h
new file mode 100644
index 0000000..3a798ae
--- /dev/null
+++ b/gtp/gtpie.h
@@ -0,0 +1,279 @@
+/* 
+ *  OpenGGSN - Gateway GPRS Support Node
+ *  Copyright (C) 2002 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 _GTPIE_H
+#define _GTPIE_H
+
+/* Macroes for conversion between host and network byte order */
+#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 GTPIE_SIZE 256          /* Max number of information elements */
+#define GTPIE_MAX  0xffff       /* Max length of information elements */
+#define GTPIE_MAX_TV 28         /* Max length of type value pair */
+#define GTPIE_MAX_TLV 0xffff-3  /* Max length of TLV (GTP length is 16 bit) */
+
+#define GTPIE_DEBUG 0           /* Print debug information */
+
+/* GTP Information elements from 29.060 v3.9.0 7.7 Information Elements */
+/* Also covers version 0. Note that version 0 6: QOS Profile was superceded *
+ * by 135: QOS Profile in version 1 */
+
+#define GTPIE_CAUSE           1 /* Cause 1 */
+#define GTPIE_IMSI            2 /* International Mobile Subscriber Identity 8 */
+#define GTPIE_RAI             3 /* Routing Area Identity (RAI) 8 */
+#define GTPIE_TLLI            4 /* Temporary Logical Link Identity (TLLI) 4 */
+#define GTPIE_P_TMSI          5 /* Packet TMSI (P-TMSI) 4 */
+#define GTPIE_QOS_PROFILE0    6 /* Quality of Service Profile GTP version 0 3*/
+                                /* 6-7 SPARE */ /* 6 is QoS Profile vers 0 */ 
+#define GTPIE_REORDER         8 /* Reordering Required 1 */
+#define GTPIE_AUTH_TRIPLET    9 /* Authentication Triplet 28 */
+                                /* 10 SPARE */
+#define GTPIE_MAP_CAUSE      11 /* MAP Cause 1 */
+#define GTPIE_P_TMSI_S       12 /* P-TMSI Signature 3 */
+#define GTPIE_MS_VALIDATED   13 /* MS Validated 1 */
+#define GTPIE_RECOVERY       14 /* Recovery 1 */
+#define GTPIE_SELECTION_MODE 15 /* Selection Mode 1 */
+#define GTPIE_FL_DI          16 /* Flow Label Data I 2 */
+#define GTPIE_TEI_DI         16 /* Tunnel Endpoint Identifier Data I 4 */
+#define GTPIE_TEI_C          17 /* Tunnel Endpoint Identifier Control Plane 4 */
+#define GTPIE_FL_C           17 /* Flow Label Signalling 2 */
+#define GTPIE_TEI_DII        18 /* Tunnel Endpoint Identifier Data II 5 */
+#define GTPIE_TEARDOWN       19 /* Teardown Ind 1 */
+#define GTPIE_NSAPI          20 /* NSAPI 1 */
+#define GTPIE_RANAP_CAUSE    21 /* RANAP Cause 1 */
+#define GTPIE_RAB_CONTEXT    22 /* RAB Context 7 */
+#define GTPIE_RP_SMS         23 /* Radio Priority SMS 1 */
+#define GTPIE_RP             24 /* Radio Priority 1 */
+#define GTPIE_PFI            25 /* Packet Flow Id 2 */
+#define GTPIE_CHARGING_C     26 /* Charging Characteristics 2 */
+#define GTPIE_TRACE_REF      27 /* Trace Reference 2 */
+#define GTPIE_TRACE_TYPE     28 /* Trace Type 2 */
+#define GTPIE_MS_NOT_REACH   29 /* MS Not Reachable Reason 1 */
+                                /* 30-116 UNUSED */
+/* 117-126 Reserved for the GPRS charging protocol (see GTP' in GSM 12.15) */
+#define GTPIE_CHARGING_ID   127 /* Charging ID 4 */
+#define GTPIE_EUA           128 /* End User Address */
+#define GTPIE_MM_CONTEXT    129 /* MM Context */
+#define GTPIE_PDP_CONTEXT   130 /* PDP Context */
+#define GTPIE_APN           131 /* Access Point Name */
+#define GTPIE_PCO           132 /* Protocol Configuration Options */
+#define GTPIE_GSN_ADDR      133 /* GSN Address */
+#define GTPIE_MSISDN        134 /* MS International PSTN/ISDN Number */
+#define GTPIE_QOS_PROFILE   135 /* Quality of Service Profile */
+#define GTPIE_AUTH_QUINTUP  136 /* Authentication Quintuplet */
+#define GTPIE_TFT           137 /* Traffic Flow Template */
+#define GTPIE_TARGET_INF    138 /* Target Identification */
+#define GTPIE_UTRAN_TRANS   139 /* UTRAN Transparent Container */
+#define GTPIE_RAB_SETUP     140 /* RAB Setup Information */
+#define GTPIE_EXT_HEADER_T  141 /* Extension Header Type List */
+#define GTPIE_TRIGGER_ID    142 /* Trigger Id */
+#define GTPIE_OMC_ID        143 /* OMC Identity */
+/* 239-250 Reserved for the GPRS charging protocol (see GTP' in GSM 12.15) */
+#define GTPIE_CHARGING_ADDR 251 /* Charging Gateway Address */
+/* 252-254 Reserved for the GPRS charging protocol (see GTP' in GSM 12.15) */
+#define GTPIE_PRIVATE       255 /* Private Extension */
+
+/* GTP information element cause codes from 29.060 v3.9.0 7.7 */
+/*                                                            */
+#define GTPCAUSE_REQ_IMSI                   0 /* Request IMSI */
+#define GTPCAUSE_REQ_IMEI                   1 /* Request IMEI */
+#define GTPCAUSE_REQ_IMSI_IMEI              2 /* Request IMSI and IMEI */
+#define GTPCAUSE_NO_ID_NEEDED               3 /* No identity needed */
+#define GTPCAUSE_MS_REFUSES                 4 /* MS refuses */
+#define GTPCAUSE_MS_NOT_RESP                5 /* MS is not GPRS responding */
+#define GTPCAUSE_006                        6 /* For future use 6-48 */
+#define GTPCAUSE_049                       49 /* Cause values reserved for GPRS charging protocol use (See GTP' in GSM 12.15) 49-63 */
+#define GTPCAUSE_064                       64 /* For future use 64-127 */
+#define GTPCAUSE_ACC_REQ                  128 /* Request accepted */
+#define GTPCAUSE_129                      129 /* For future use 129-176 */
+#define GTPCAUSE_177                      177 /* Cause values reserved for GPRS charging protocol use (See GTP' In GSM 12.15) 177-191 */
+#define GTPCAUSE_NON_EXIST                192 /* Non-existent */
+#define GTPCAUSE_INVALID_MESSAGE          193 /* Invalid message format */
+#define GTPCAUSE_IMSI_NOT_KNOWN           194 /* IMSI not known */
+#define GTPCAUSE_MS_DETACHED              195 /* MS is GPRS detached */
+#define GTPCAUSE_MS_NOT_RESP              196 /* MS is not GPRS responding */
+#define GTPCAUSE_MS_REFUSES               197 /* MS refuses */
+#define GTPCAUSE_198                      198 /* For future use */
+#define GTPCAUSE_NO_RESOURCES             199 /* No resources available */
+#define GTPCAUSE_NOT_SUPPORTED            200 /* Service not supported */
+#define GTPCAUSE_MAN_IE_INCORRECT         201 /* Mandatory IE incorrect */
+#define GTPCAUSE_MAN_IE_MISSING           202 /* Mandatory IE missing */
+#define GTPCAUSE_OPT_IE_INCORRECT         203 /* Optional IE incorrect */
+#define GTPCAUSE_SYS_FAIL                 204 /* System failure */
+#define GTPCAUSE_ROAMING_REST             205 /* Roaming Restriction */
+#define GTPCAUSE_PTIMSI_MISMATCH          206 /* P-TMSI signature mismatch */
+#define GTPCAUSE_CONN_SUSP                207 /* GPRS connection suspended */
+#define GTPCAUSE_AUTH_FAIL                208 /* Authentication failure */
+#define GTPCAUSE_USER_AUTH_FAIL           209 /* User authentication failed */
+#define GTPCAUSE_CONTEXT_NOT_FOUND        210 /* Context not found */
+#define GTPCAUSE_ADDR_OCCUPIED            211 /* All dynamic PDP addresses are occupied */
+#define GTPCAUSE_NO_MEMORY                212 /* No memory is available */
+#define GTPCAUSE_RELOC_FAIL               213 /* Relocation failure */
+#define GTPCAUSE_UNKNOWN_MAN_EXTHEADER    214 /* Unknown mandatory extension header */
+#define GTPCAUSE_SEM_ERR_TFT              215 /* Semantic error in the TFT operation */
+#define GTPCAUSE_SYN_ERR_TFT              216 /* Syntactic error in the TFT operation */
+#define GTPCAUSE_SEM_ERR_FILTER           217 /* Semantic errors in packet filter(s) */
+#define GTPCAUSE_SYN_ERR_FILTER           218 /* Syntactic errors in packet filter(s) */
+#define GTPCAUSE_MISSING_APN              219 /* Missing or unknown APN*/
+#define GTPCAUSE_UNKNOWN_PDP              220 /* Unknown PDP address or PDP type */
+#define GTPCAUSE_221                      221 /* For Future Use 221-240 */
+#define GTPCAUSE_241                      241 /* Cause Values Reserved For Gprs Charging Protocol Use (See Gtp' In Gsm 12.15) 241-255 */
+
+
+/* GTP information element structs in network order */
+struct gtpie_ext {              /* Extension header */
+  u_int8_t t;                   /* Type */
+  u_int8_t l;                   /* Length */
+  u_int8_t *p;                  /* Value */
+} __attribute__((packed));
+
+struct gtpie_tlv {              /* Type length value pair */
+  u_int8_t t;                   /* Type */
+  u_int16_t l;                  /* Length */
+  u_int8_t v[GTPIE_MAX_TLV];    /* Value */
+} __attribute__((packed));
+
+struct gtpie_tv0 {              /* 1 byte type value pair */
+  u_int8_t t;                   /* Type */
+  u_int8_t v[GTPIE_MAX_TV];     /* Pointer to value */
+}__attribute__((packed));
+
+struct gtpie_tv1 {              /* 1 byte type value pair */
+  u_int8_t t;                   /* Type */
+  u_int8_t v;                   /* Value */
+}__attribute__((packed));
+
+struct gtpie_tv2 {              /* 2 byte type value pair */
+  u_int8_t t;                   /* Type */
+  u_int16_t v;                  /* Value */
+}__attribute__((packed));
+
+struct gtpie_tv4 {              /* 4 byte type value pair */
+  u_int8_t t;                   /* Type */
+  u_int32_t v;                  /* Value */
+}__attribute__((packed));
+
+struct gtpie_tv8 {              /* 8 byte type value pair */
+  u_int8_t t;                   /* Type */
+  u_int64_t v;                  /* Value */
+}__attribute__((packed));
+
+
+union gtpie_member {
+  u_int8_t t;
+  struct gtpie_ext ext;
+  struct gtpie_tlv tlv;
+  struct gtpie_tv0 tv0;
+  struct gtpie_tv1 tv1;
+  struct gtpie_tv2 tv2;
+  struct gtpie_tv4 tv4;
+  struct gtpie_tv8 tv8;
+}__attribute__((packed));
+
+/*
+cause
+imsi
+rai
+tlli
+p_tmsi
+qos_profile0
+reorder
+auth
+map_cause
+p_tmsi_s
+ms_validated
+recovery
+selection_mode
+tei_di
+tei_c
+tei_dii
+teardown
+nsapi
+ranap_cause
+rab_context
+rp_sms
+rp
+pfi
+charging_c
+trace_ref
+trace_type
+ms_not_reach
+charging_id
+eua
+mm_context
+pdp_context
+apn
+pco
+gsn_addr
+msisdn
+qos_profile
+auth
+tft
+target_inf
+utran_trans
+rab_setup
+ext_header_t
+trigger_id
+omc_id
+charging_addr
+private
+*/
+
+struct tlv1 {
+  u_int8_t  type;
+  u_int8_t  length;
+}__attribute__((packed));
+
+struct tlv2 {
+  u_int8_t  type;
+  u_int16_t length;
+}__attribute__((packed));
+
+extern int gtpie_tlv(void *p, int *length, int size,
+		     u_int8_t t, int l, void *v);
+extern int gtpie_tv0(void *p, int *length, int size, 
+		     u_int8_t t, int l, u_int8_t *v);
+extern int gtpie_tv1(void *p, int *length, int size, u_int8_t t, u_int8_t v);
+extern int gtpie_tv2(void *p, int *length, int size, u_int8_t t, u_int16_t v);
+extern int gtpie_tv4(void *p, int *length, int size, u_int8_t t, u_int32_t v);
+extern int gtpie_tv8(void *p, int *length, int size, u_int8_t t, u_int64_t v);
+extern int gtpie_getie(union gtpie_member* ie[], int type, int instance);
+extern int gtpie_exist(union gtpie_member* ie[], int type, int instance);
+extern int gtpie_gettlv(union gtpie_member* ie[], int type, int instance,
+			int *length, void *dst, int size);
+extern int gtpie_gettv0(union gtpie_member* ie[], int type, int instance,
+			void *dst, int size);
+extern int gtpie_gettv1(union gtpie_member* ie[], int type, int instance, 
+			uint8_t *dst);
+extern int gtpie_gettv2(union gtpie_member* ie[], int type, int instance, 
+			uint16_t *dst);
+extern int gtpie_gettv4(union gtpie_member* ie[], int type, int instance, 
+			uint32_t *dst);
+
+extern int gtpie_decaps(union gtpie_member* ie[], void *pack, unsigned len);
+extern int gtpie_encaps(union gtpie_member* ie[], void *pack, unsigned *len);
+extern int gtpie_encaps2(union gtpie_member ie[], int size,
+		  void *pack, unsigned *len);
+
+
+#endif	/* !_GTPIE_H */
+
+
diff --git a/gtp/lookupa.c b/gtp/lookupa.c
new file mode 100644
index 0000000..8ff114b
--- /dev/null
+++ b/gtp/lookupa.c
@@ -0,0 +1,246 @@
+/*
+--------------------------------------------------------------------
+lookupa.c, by Bob Jenkins, December 1996.  Same as lookup2.c
+Use this code however you wish.  Public Domain.  No warranty.
+Source is http://burtleburtle.net/bob/c/lookupa.c
+--------------------------------------------------------------------
+*/
+#ifndef STANDARD
+/*
+#include "standard.h"
+*/
+#endif
+#ifndef LOOKUPA
+#include "lookupa.h"
+#endif
+
+/*
+--------------------------------------------------------------------
+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.
+--------------------------------------------------------------------
+*/
+
+ub4 lookup( k, length, level)
+register ub1 *k;        /* the key */
+register ub4  length;   /* the length of the key */
+register ub4  level;    /* the previous hash, or an arbitrary value */
+{
+   register ub4 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;
+}
+
+
+/*
+--------------------------------------------------------------------
+mixc -- mixc 8 4-bit values as quickly and thoroughly as possible.
+Repeating mix() three times achieves avalanche.
+Repeating mix() four times eliminates all funnels and all
+  characteristics stronger than 2^{-11}.
+--------------------------------------------------------------------
+*/
+#define mixc(a,b,c,d,e,f,g,h) \
+{ \
+   a^=b<<11; d+=a; b+=c; \
+   b^=c>>2;  e+=b; c+=d; \
+   c^=d<<8;  f+=c; d+=e; \
+   d^=e>>16; g+=d; e+=f; \
+   e^=f<<10; h+=e; f+=g; \
+   f^=g>>4;  a+=f; g+=h; \
+   g^=h<<8;  b+=g; h+=a; \
+   h^=a>>9;  c+=h; a+=b; \
+}
+
+/*
+--------------------------------------------------------------------
+checksum() -- hash a variable-length key into a 256-bit value
+  k     : the key (the unaligned variable-length array of bytes)
+  len   : the length of the key, counting by bytes
+  state : an array of CHECKSTATE 4-byte values (256 bits)
+The state is the checksum.  Every bit of the key affects every bit of
+the state.  There are no funnels.  About 112+6.875len instructions.
+
+If you are hashing n strings (ub1 **)k, do it like this:
+  for (i=0; i<8; ++i) state[i] = 0x9e3779b9;
+  for (i=0, h=0; i<n; ++i) checksum( k[i], len[i], state);
+
+(c) Bob Jenkins, 1996.  bob_jenkins@burtleburtle.net.  You may use this
+code any way you wish, private, educational, or commercial, as long
+as this whole comment accompanies it.
+
+See http://burtleburtle.net/bob/hash/evahash.html
+Use to detect changes between revisions of documents, assuming nobody
+is trying to cause collisions.  Do NOT use for cryptography.
+--------------------------------------------------------------------
+*/
+void  checksum( k, len, state)
+register ub1 *k;
+register ub4  len;
+register ub4 *state;
+{
+   register ub4 a,b,c,d,e,f,g,h,length;
+
+   /* Use the length and level; add in the golden ratio. */
+   length = len;
+   a=state[0]; b=state[1]; c=state[2]; d=state[3];
+   e=state[4]; f=state[5]; g=state[6]; h=state[7];
+
+   /*---------------------------------------- handle most of the key */
+   while (len >= 32)
+   {
+      a += (k[0] +(k[1]<<8) +(k[2]<<16) +(k[3]<<24));
+      b += (k[4] +(k[5]<<8) +(k[6]<<16) +(k[7]<<24));
+      c += (k[8] +(k[9]<<8) +(k[10]<<16)+(k[11]<<24));
+      d += (k[12]+(k[13]<<8)+(k[14]<<16)+(k[15]<<24));
+      e += (k[16]+(k[17]<<8)+(k[18]<<16)+(k[19]<<24));
+      f += (k[20]+(k[21]<<8)+(k[22]<<16)+(k[23]<<24));
+      g += (k[24]+(k[25]<<8)+(k[26]<<16)+(k[27]<<24));
+      h += (k[28]+(k[29]<<8)+(k[30]<<16)+(k[31]<<24));
+      mixc(a,b,c,d,e,f,g,h);
+      mixc(a,b,c,d,e,f,g,h);
+      mixc(a,b,c,d,e,f,g,h);
+      mixc(a,b,c,d,e,f,g,h);
+      k += 32; len -= 32;
+   }
+
+   /*------------------------------------- handle the last 31 bytes */
+   h += length;
+   switch(len)
+   {
+   case 31: h+=(k[30]<<24);
+   case 30: h+=(k[29]<<16);
+   case 29: h+=(k[28]<<8);
+   case 28: g+=(k[27]<<24);
+   case 27: g+=(k[26]<<16);
+   case 26: g+=(k[25]<<8);
+   case 25: g+=k[24];
+   case 24: f+=(k[23]<<24);
+   case 23: f+=(k[22]<<16);
+   case 22: f+=(k[21]<<8);
+   case 21: f+=k[20];
+   case 20: e+=(k[19]<<24);
+   case 19: e+=(k[18]<<16);
+   case 18: e+=(k[17]<<8);
+   case 17: e+=k[16];
+   case 16: d+=(k[15]<<24);
+   case 15: d+=(k[14]<<16);
+   case 14: d+=(k[13]<<8);
+   case 13: d+=k[12];
+   case 12: c+=(k[11]<<24);
+   case 11: c+=(k[10]<<16);
+   case 10: c+=(k[9]<<8);
+   case 9 : c+=k[8];
+   case 8 : b+=(k[7]<<24);
+   case 7 : b+=(k[6]<<16);
+   case 6 : b+=(k[5]<<8);
+   case 5 : b+=k[4];
+   case 4 : a+=(k[3]<<24);
+   case 3 : a+=(k[2]<<16);
+   case 2 : a+=(k[1]<<8);
+   case 1 : a+=k[0];
+   }
+   mixc(a,b,c,d,e,f,g,h);
+   mixc(a,b,c,d,e,f,g,h);
+   mixc(a,b,c,d,e,f,g,h);
+   mixc(a,b,c,d,e,f,g,h);
+
+   /*-------------------------------------------- report the result */
+   state[0]=a; state[1]=b; state[2]=c; state[3]=d;
+   state[4]=e; state[5]=f; state[6]=g; state[7]=h;
+}
diff --git a/gtp/lookupa.h b/gtp/lookupa.h
new file mode 100644
index 0000000..16784a9
--- /dev/null
+++ b/gtp/lookupa.h
@@ -0,0 +1,29 @@
+/*
+------------------------------------------------------------------------------
+By Bob Jenkins, September 1996.
+lookupa.h, a hash function for table lookup, same function as lookup.c.
+Use this code in any way you wish.  Public Domain.  It has no warranty.
+Source is http://burtleburtle.net/bob/c/lookupa.h
+------------------------------------------------------------------------------
+*/
+
+/* Uncommented by Jens Jakobsen 20020717 
+#ifndef STANDARD
+#include "standard.h"
+#endif
+*/
+
+#ifndef LOOKUPA
+#define LOOKUPA
+
+typedef  unsigned long  int  ub4;   /* unsigned 4-byte quantities */
+typedef  unsigned       char ub1;
+
+#define CHECKSTATE 8
+#define hashsize(n) ((ub4)1<<(n))
+#define hashmask(n) (hashsize(n)-1)
+
+ub4 lookup(/*_ ub1 *k, ub4 length, ub4 level _*/);
+void checksum(/*_ ub1 *k, ub4 length, ub4 *state _*/);
+
+#endif /* LOOKUPA */
diff --git a/gtp/pdp.c b/gtp/pdp.c
new file mode 100644
index 0000000..3e5951e
--- /dev/null
+++ b/gtp/pdp.c
@@ -0,0 +1,320 @@
+/* 
+ *  OpenGGSN - Gateway GPRS Support Node
+ *  Copyright (C) 2002 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):
+ * 
+ */
+
+/*
+ * pdp.c: 
+ *
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <string.h>
+#include "pdp.h"
+#include "lookupa.h"
+
+/* ***********************************************************
+ * Global variables TODO: most should be moved to gsn_t
+ *************************************************************/
+
+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 */
+
+/* ***********************************************************
+ * Functions related to PDP storage
+ *
+ * Lifecycle
+ * For a GGSN pdp context life begins with the reception of a 
+ * create pdp context request. It normally ends with the reception
+ * of a delete pdp context request, but will also end with the
+ * reception of an error indication message. 
+ * Provisions should probably be made for terminating pdp contexts
+ * based on either idle timeout, or by sending downlink probe 
+ * messages (ping?) to see if the MS is still responding.
+ * 
+ * For an SGSN pdp context life begins with the application just
+ * before sending off a create pdp context request. It normally
+ * ends when a delete pdp context response message is received 
+ * from the GGSN, but should also end when with the reception of
+ * an error indication message.
+ *
+ *
+ * HASH Tables
+ *
+ * Downlink packets received in the GGSN are identified only by their
+ * network interface together with their destination IP address (Two
+ * network interfaces can use the same private IP address). Each IMSI
+ * (mobile station) can have several PDP contexts using the same IP 
+ * address. In this case the traffic flow template (TFT) is used to
+ * determine the correct PDP context for a particular IMSI. Also it 
+ * should be possible for each PDP context to use several IP adresses
+ * For fixed wireless access a mobile station might need a full class
+ * C network. Even in the case of several IP adresses the PDP context
+ * should be determined on the basis of the network IP address.
+ * Thus we need a hash table based on network interface + IP address.
+ * 
+ * Uplink packets are for GTP0 identified by their IMSI and NSAPI, which
+ * is collectively called the tunnel identifier. There is also a 16 bit
+ * flow label that can be used for identification of uplink packets. This
+ * however is quite useless as it limits the number of contexts to 65536.
+ * For GTP1 uplink packets are identified by a Tunnel Endpoint Identifier
+ * (32 bit), or in some cases by the combination of IMSI and NSAPI.
+ * For GTP1 delete context requests there is a need to find the PDP
+ * contexts with the same IP address. This however can be done by using
+ * the IP hash table.
+ * Thus we need a hash table based on TID (IMSI and NSAPI). The TEID will
+ * be used for directly addressing the PDP context.
+
+ * pdp_newpdp 
+ * Gives you a pdp context with no hash references In some way
+ * this should have a limited lifetime.
+ *
+ * pdp_freepdp
+ * Frees a context that was previously allocated with
+ * pdp_newpdp
+ *
+ *
+ * pdp_getpdpIP
+ * An incoming IP packet is uniquely identified by a pointer
+ * to a network connection (void *) and an IP address
+ * (struct in_addr)
+ *
+ * pdp_getpdpGTP
+ * An incoming GTP packet is uniquely identified by a the
+ * TID (imsi + nsapi (8 octets)) in or by the Flow Label
+ * (2 octets) in gtp0 or by the Tunnel Endpoint Identifier
+ * (4 octets) in gtp1.
+ *
+ * This leads to an architecture where the receiving GSN
+ * chooses a Flow Label or a Tunnel Endpoint Identifier
+ * when the connection is setup.
+ * Thus no hash table is needed for GTP lookups.
+ *
+ *************************************************************/
+
+int pdp_init() {
+  memset(&pdpa, 0, sizeof(pdpa));
+  memset(&hashtid, 0, sizeof(hashtid));
+  memset(&haship, 0, sizeof(haship));
+
+  return 0;
+}
+
+int pdp_newpdp(struct pdp_t **pdp, uint64_t imsi, uint8_t nsapi, 
+	       struct pdp_t *pdp_old){
+  int n;
+  for (n=0; n<PDP_MAX; n++) { /* TODO: Need to do better than linear search */
+    if (pdpa[n].inuse == 0) {
+      *pdp = &pdpa[n];
+      if (NULL != pdp_old) memcpy(*pdp, pdp_old, sizeof(struct pdp_t));
+      else memset(*pdp, 0, sizeof(struct pdp_t));
+      (*pdp)->inuse    = 1;
+      (*pdp)->imsi     = imsi;
+      (*pdp)->nsapi    = nsapi;
+      (*pdp)->fllc     = (uint16_t) n;
+      (*pdp)->fllu     = (uint16_t) n;
+      (*pdp)->teic_own = (uint32_t) n;
+      (*pdp)->teic_own = (uint32_t) n;
+      pdp_tidset(*pdp, pdp_gettid(imsi, nsapi));
+      return 0;
+    }
+  }
+  return EOF; /* No more available */
+}
+
+int pdp_freepdp(struct pdp_t *pdp){
+  pdp_tiddel(pdp);
+  memset(pdp, 0, sizeof(struct pdp_t));
+  /* Also need to clean up IP hash tables */
+  return 0;
+}
+
+int pdp_getpdp(struct pdp_t **pdp){
+  *pdp = &pdpa[0];
+  return 0;
+}
+
+int pdp_getgtp0(struct pdp_t **pdp, uint16_t fl){
+  if (fl>=PDP_MAX) {
+    return EOF;  /* Not found */
+  }
+  else {
+    *pdp = &pdpa[fl];
+    if ((*pdp)->inuse) return 0;
+    else return EOF; 
+    /* Context exists. We do no further validity checking. */
+  }
+}
+
+int pdp_getgtp1(struct pdp_t **pdp, uint32_t teid){
+  if (teid>=PDP_MAX) {
+    return -1;  /* Not found */
+  }
+  else {
+    *pdp = &pdpa[teid];
+    return 0; /* We do no validity checking. */
+  }
+}
+
+
+int pdp_tidhash(uint64_t tid) {
+  return (lookup(&tid, sizeof(tid), 0) % PDP_MAX);
+}
+
+int pdp_tidset(struct pdp_t *pdp, uint64_t tid) {
+  int hash = pdp_tidhash(tid);
+  struct pdp_t *pdp2;
+  struct pdp_t *pdp_prev = NULL;
+  if (PDP_DEBUG) printf("Begin pdp_tidset tid = %llx\n", tid);
+  pdp->tid = tid;
+  for (pdp2 = hashtid[hash]; pdp2; pdp2 = pdp2->tidnext)
+    pdp_prev = pdp2;
+  if (!pdp_prev) 
+    hashtid[hash] = pdp;
+  else 
+    pdp_prev->tidnext = pdp;
+  if (PDP_DEBUG) printf("End pdp_tidset\n");
+  return 0;
+}
+
+int pdp_tiddel(struct pdp_t *pdp) {
+  int hash = pdp_tidhash(pdp->tid);
+  struct pdp_t *pdp2;
+  struct pdp_t *pdp_prev = NULL;
+  if (PDP_DEBUG) printf("Begin pdp_tiddel tid = %llx\n", pdp->tid);
+  for (pdp2 = hashtid[hash]; pdp2; pdp2 = pdp2->tidnext) {
+    if (pdp2 == pdp) {
+      if (!pdp_prev) 
+	hashtid[hash] = pdp2->tidnext;
+      else 
+	pdp_prev->tidnext = pdp2->tidnext;
+      if (PDP_DEBUG) printf("End pdp_tidset: PDP found\n");
+      return 0;
+    }
+    pdp_prev = pdp2;
+  }
+  if (PDP_DEBUG) printf("End pdp_tidset: PDP not found\n");
+  return EOF; /* End of linked list and not found */
+}
+
+int pdp_tidget(struct pdp_t **pdp, uint64_t tid) {
+  int hash = pdp_tidhash(tid);
+  struct pdp_t *pdp2;
+  if (PDP_DEBUG) printf("Begin pdp_tidget tid = %llx\n", tid);
+  for (pdp2 = hashtid[hash]; pdp2; pdp2 = pdp2->tidnext) {
+    if (pdp2->tid == tid) {
+      *pdp = pdp2;
+      if (PDP_DEBUG) printf("Begin pdp_tidget. Found\n");
+      return 0;
+    }
+  }
+  if (PDP_DEBUG) printf("Begin pdp_tidget. Not found\n");
+  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);*/
+  return (lookup(eua->v, eua->l, ipif) % PDP_MAX);
+}
+    
+int pdp_ipset(struct pdp_t *pdp, void* ipif, struct ul66_t *eua) {
+  int hash;
+  struct pdp_t *pdp2;
+  struct pdp_t *pdp_prev = NULL;
+
+  if (PDP_DEBUG) printf("Begin pdp_ipset %d %d %2x%2x%2x%2x\n",
+			(unsigned) ipif, eua->l,
+			eua->v[2], eua->v[3], 
+			eua->v[4], eua->v[5]);
+
+  pdp->ipif = ipif;
+  pdp->eua.l = eua->l;
+  memcpy(pdp->eua.v, eua->v, eua->l);
+
+  hash = pdp_iphash(pdp->ipif, &pdp->eua);
+
+  for (pdp2 = haship[hash]; pdp2; pdp2 = pdp2->ipnext)
+    pdp_prev = pdp2;
+  if (!pdp_prev) 
+    haship[hash] = pdp;
+  else 
+    pdp_prev->ipnext = pdp;
+  if (PDP_DEBUG) printf("End pdp_ipset\n");
+  return 0;
+}
+
+int pdp_ipdel(struct pdp_t *pdp) {
+  int hash = pdp_iphash(pdp->ipif, &pdp->eua);
+  struct pdp_t *pdp2;
+  struct pdp_t *pdp_prev = NULL;
+  if (PDP_DEBUG) printf("Begin pdp_ipdel\n");
+  for (pdp2 = haship[hash]; pdp2; pdp2 = pdp2->ipnext) {
+    if (pdp2 == pdp) {
+      if (!pdp_prev) 
+	haship[hash] = pdp2->ipnext;
+      else 
+	pdp_prev->ipnext = pdp2->ipnext;
+      if (PDP_DEBUG) printf("End pdp_ipdel: PDP found\n");
+      return 0;
+    }
+    pdp_prev = pdp2;
+  }
+  if (PDP_DEBUG) printf("End pdp_ipdel: PDP not found\n");
+  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]);*/
+  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");*/
+      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 */
+}
+
+/* Various conversion functions */
+
+int pdp_ntoeua(struct in_addr *src, struct ul66_t *eua) {
+  eua->l=6;
+  eua->v[0]=0xf1; /* IETF */
+  eua->v[1]=0x21; /* IPv4 */
+  memcpy(&eua->v[2], src, 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);
+}
+
+int ulcpy(void* dst, void* src, size_t size) {
+  if (((struct ul255_t*)src)->l <= size) {
+    ((struct ul255_t*)dst)->l = ((struct ul255_t*)src)->l;
+    memcpy(((struct ul255_t*)dst)->v, ((struct ul255_t*)src)->v, 
+	   ((struct ul255_t*)dst)->l);
+    return 0;
+  }
+  else return EOF;
+}
diff --git a/gtp/pdp.h b/gtp/pdp.h
new file mode 100644
index 0000000..b9ca62a
--- /dev/null
+++ b/gtp/pdp.h
@@ -0,0 +1,203 @@
+/* 
+ *  OpenGGSN - Gateway GPRS Support Node
+ *  Copyright (C) 2002 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 _PDP_H
+#define _PDP_H
+
+#define PDP_MAX 1024             /* Max number of PDP contexts */
+
+#define PDP_DEBUG 0              /* Print debug information */
+
+/* GTP Information elements from 29.060 v3.9.0 7.7 Information Elements */
+/* Also covers version 0. Note that version 0 6: QOS Profile was superceded *
+ * by 135: QOS Profile in version 1 */
+
+
+struct sl_t {
+int l;
+char *v;
+};
+
+struct ul_t {
+int l;
+unsigned char *v;
+};
+
+struct ul16_t {
+int l;
+unsigned char v[16];
+};
+
+struct ul66_t {
+int l;
+unsigned char v[66];
+};
+
+struct ul255_t {
+int l;
+unsigned char v[255];
+};
+
+
+/* ***********************************************************
+ * Information storage for each PDP context
+ *
+ * Information storage for each PDP context is defined in 
+ * 23.060 section 13.3 and 03.60. Includes IMSI, MSISDN, APN, 
+ * PDP-type, PDP-address (IP address), sequence numbers, charging ID.
+ * For the SGSN it also includes radio related mobility
+ * information.
+ * The following structure is a combination of the storage 
+ * requirements for each PDP context for the GGSN and SGSN.
+ * It contains both 23.060 as well as 03.60 parameters.
+ * Information is stored in the format for information elements
+ * described in 29.060 and 09.60.
+ * 31 * 4 + 15 structs +  = 120 + 15 structs ~ 2k / context
+ * Structs: IP address 16+4 bytes (6), APN 255 bytes (2)
+ * QOS: 255 bytes (3), msisdn 16 bytes (1), 
+ *
+ * TODO: We need to consider who manages the pdp_t hash tables
+ * Is it gtp_lib, or is it the application?
+ * I suppose that it will be gtp_lib. 
+ * SGSN will ask gtplib for new pdp_t. Fill out the fields,
+ * and pass it on to gtp_create_pdp_req.
+ * GGSN will receive gtp_create_pdp_ind, create new pdp_t and
+ * send responce to SGSN.
+ * SGSN will receive response and gtplib will find the 
+ * original pdp_t corresponding to the request. This will be
+ * passed on to the application.
+ * Eventually the SGSN will close the connection, and the
+ * pdp_t will be freed by gtplib in SGSN and GGSN
+ * This means that gtplib need to have functions to
+ * allocate, free, sort and find pdp_t
+ * (newpdp, freepdp, getpdp)
+ * Hash tables: TID, IMSI, IP etc.) 
+ *************************************************************/
+
+struct pdp_t {
+  /* Parameter determining if this PDP is in use. */
+  uint8_t     inuse;    /* 0=free. 1=used by somebody */
+
+  /* Pointers related to hash tables */
+  struct pdp_t *tidnext;
+  struct pdp_t *ipnext;
+
+  /* Parameters shared by all PDP context belonging to the same MS */
+
+  void *ipif;           /* IP network interface */
+  void *asap;           /* Application specific service access point */
+
+  uint64_t    imsi;     /* International Mobile Subscriber Identity.*/
+  struct ul16_t msisdn; /* The basic MSISDN of the MS. */
+  uint8_t     mnrg;     /* Indicates whether the MS is marked as not reachable for PS at the HLR. (1 bit, not transmitted) */
+  uint8_t     cch_sub;  /* The charging characteristics for the MS, e.g. normal, prepaid, flat-rate, and/or hot billing subscription. (not transmitted) */
+  uint16_t    traceref; /* Identifies a record or a collection of records for a particular trace. */
+  uint16_t    tracetype;/* Indicates the type of trace. */
+  struct ul_t triggerid;/* Identifies the entity that initiated the trace. */
+  struct ul_t omcid;    /* Identifies the OMC that shall receive the trace record(s). */
+  uint8_t     rec_hlr;  /* Indicates if HLR or VLR is performing database recovery. (1 bit, not transmitted) */
+
+  /* Parameters specific to each individual PDP context */
+
+  uint8_t     pdp_id;   /* Index of the PDP context. (PDP context identifier) */
+  uint8_t     pdp_state;/* PDP State Packet data protocol state, INACTIVE or ACTIVE. (1 bit, not transmitted) */
+  /* struct ul_t pdp_type; * PDP type; e.g. PPP or IP. */
+  /* struct ul_t pdp_addr; * PDP address; e.g. an IP address. */
+  struct ul66_t eua;    /* End user address. PDP type and address combined */
+  uint8_t     pdp_dyn;  /* Indicates whether PDP Address is static or dynamic. (1 bit, not transmitted) */
+  struct ul255_t apn_req;/* The APN requested. */
+  struct ul255_t apn_sub;/* The APN received from the HLR. */
+  struct ul255_t apn_use;/* The APN Network Identifier currently used. */
+  uint8_t     nsapi;    /* Network layer Service Access Point Identifier. (4 bit) */
+  uint16_t    ti;       /* Transaction Identifier. (4 or 12 bit) */
+
+  uint32_t    teic_own; /* (Own Tunnel Endpoint Identifier Control) */
+  uint32_t    teid_own; /* (Own Tunnel Endpoint Identifier Data I) */
+  uint32_t    teic_gn;  /* Tunnel Endpoint Identifier for the Gn and Gp interfaces. (Control plane) */
+  uint32_t    teid_gn;  /* Tunnel Endpoint Identifier for the Gn and Gp interfaces. (Data I) */
+  uint32_t    tei_iu;   /* Tunnel Endpoint Identifier for the Iu interface. */
+
+  uint16_t    fllc;     /* (Local Flow Label Control, gtp0) */
+  uint16_t    fllu;     /* (Local Flow Label Data I, gtp0) */
+  uint16_t    flrc;     /* (Remote gn/gp Flow Label Control, gtp0) */
+  uint16_t    flru;     /* (Remote gn/gp Flow Label Data I, gtp0) */
+
+  struct ul_t tft;      /* Traffic flow template. */
+  /*struct ul16_t sgsnc;  * The IP address of the SGSN currently serving this MS. (Control plane) */
+  /*struct ul16_t sgsnu;  * The IP address of the SGSN currently serving this MS. (User plane) */
+  /*struct ul16_t ggsnc;  * The IP address of the GGSN currently used. (Control plane) */
+  /*struct ul16_t ggsnu;  * The IP address of the GGSN currently used. (User plane) */
+
+  struct ul16_t gsnlc;  /* The IP address of the local GSN. (Control plane) */
+  struct ul16_t gsnlu;  /* The IP address of the local GSN. (User plane) */
+  struct ul16_t gsnrc;  /* The IP address of the remote GSN. (Control plane) */
+  struct ul16_t gsnru;  /* The IP address of the remote GSN. (User plane) */
+
+  uint8_t  vplmn_allow; /* Specifies whether the MS is allowed to use the APN in the domain of the HPLMN only, or additionally the APN in the domain of the VPLMN. (1 bit) */
+  uint8_t qos_sub0[3];  /* The quality of service profile subscribed. */
+  uint8_t qos_req0[3];  /* The quality of service profile requested. */
+  uint8_t qos_neg0[3];  /* The quality of service profile negotiated. */
+  struct ul255_t qos_sub;  /* The quality of service profile subscribed. */
+  struct ul255_t qos_req;  /* The quality of service profile requested. */
+  struct ul255_t qos_neg;  /* The quality of service profile negotiated. */
+  uint8_t     radio_pri;/* The RLC/MAC radio priority level for uplink user data transmission. (4 bit) */
+  uint16_t    flow_id;  /*  Packet flow identifier. */
+  /* struct ul_t bssqos_neg; * The aggregate BSS quality of service profile negotiated for the packet flow that this PDP context belongs to. (NOT GTP)*/
+  uint8_t     sndcpd;   /* SNDCP sequence number of the next downlink N-PDU to be sent to the MS. */
+  uint8_t     sndcpu;   /* SNDCP sequence number of the next uplink N-PDU expected from the MS. */
+  uint8_t     rec_sgsn; /* Indicates if the SGSN is performing database recovery. (1 bit, not transmitted) */
+/*  uint16_t    gtpsnd;    GTP-U sequence number of the next downlink N-PDU to be sent to the SGSN / received from the GGSN. */
+/*  uint16_t    gtpsnu;    GTP-U sequence number of the next uplink N-PDU to be received from the SGSN / sent to the GGSN */
+  uint16_t    gtpsntx;  /* GTP-U sequence number of the next downlink N-PDU to be sent (09.60 section 8.1.1.1) */
+  uint16_t    gtpsnrx;  /* GTP-U sequence number of the next uplink N-PDU to be received (09.60 section 8.1.1.1) */
+  uint8_t     pdcpsndd; /* Sequence number of the next downlink in-sequence PDCP-PDU to be sent to the MS. */
+  uint8_t     pdcpsndu; /* Sequence number of the next uplink in-sequence PDCP-PDU expected from the MS. */
+  uint32_t    cid;      /* Charging identifier, identifies charging records generated by SGSN and GGSN. */
+  uint16_t    cch_pdp;  /* The charging characteristics for this PDP context, e.g. normal, prepaid, flat-rate, and/or hot billing. */
+  struct ul16_t rnc_addr;/* The IP address of the RNC currently used. */
+  uint8_t     reorder;  /* Specifies whether the GGSN shall reorder N-PDUs received from the SGSN / Specifies whether the SGSN shall reorder N-PDUs before delivering the N-PSUs to the MS. (1 bit) */
+  struct ul255_t pco_req;  /* Requested packet control options. */
+  struct ul255_t pco_neg;  /* Negotiated packet control options. */
+  uint32_t    selmode;  /* Selection mode. */
+  uint64_t    tid;      /* (Combination of imsi and nsapi) */
+};
+
+
+/* functions related to pdp_t management */
+int pdp_init();
+int pdp_newpdp(struct pdp_t **pdp, uint64_t imsi, uint8_t nsapi,
+	       struct pdp_t *pdp_old);
+int pdp_freepdp(struct pdp_t *pdp);
+int pdp_getpdp(struct pdp_t **pdp);
+
+int pdp_getgtp0(struct pdp_t **pdp, uint16_t fl);
+int pdp_getgtp1(struct pdp_t **pdp, uint32_t teid);
+
+int pdp_tidhash(uint64_t tid);
+int pdp_tidset(struct pdp_t *pdp, uint64_t tid);
+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);
+uint64_t pdp_gettid(uint64_t imsi, uint8_t nsapi);
+int ulcpy(void* dst, void* src, size_t size);
+
+#endif	/* !_PDP_H */
diff --git a/gtp/queue.c b/gtp/queue.c
new file mode 100644
index 0000000..0080e43
--- /dev/null
+++ b/gtp/queue.c
@@ -0,0 +1,249 @@
+/* 
+ *  OpenGGSN - Gateway GPRS Support Node
+ *  Copyright (C) 2002 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):
+ * 
+ */
+
+/*
+ * Queue.c
+ * Reliable delivery of signalling messages
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <string.h>
+#include "pdp.h"
+#include "gtp.h"
+#include "queue.h"
+
+int queue_print(struct queue_t *queue) {
+  int n;
+  printf("Queue: %x Next: %d First: %d Last: %d\n", (int) queue, queue->next, queue->first, queue->last);
+  printf("# State seq next prev timeout retrans\n");
+  for (n=0; n<QUEUE_SIZE; n++) {
+    printf("%d %d %d %d %d %d %d\n",
+	   n,
+	   queue->qmsga[n].state,
+	   queue->qmsga[n].seq,
+	   queue->qmsga[n].next,
+	   queue->qmsga[n].prev,
+	   (int) queue->qmsga[n].timeout,
+	   queue->qmsga[n].retrans);
+  }
+  return 0;
+}
+
+int queue_seqhash(struct sockaddr_in *peer, uint16_t seq) {
+  /* With QUEUE_HASH_SIZE = 2^16 this describes all possible
+     seq values. Thus we have perfect hash for the request queue.
+     For the response queue we might have collisions, but not very
+     often.
+     For performance optimisation we should remove the modulus
+     operator, but this is only valid for QUEUE_HASH_SIZE = 2^16 */
+  return seq % QUEUE_HASH_SIZE;
+}
+
+int queue_seqset(struct queue_t *queue, struct qmsg_t *qmsg,
+		 struct sockaddr_in *peer, uint16_t seq) {
+  int hash = queue_seqhash(peer, seq);
+  struct qmsg_t *qmsg2;
+  struct qmsg_t *qmsg_prev = NULL;
+
+  if (QUEUE_DEBUG) printf("Begin queue_seqset seq = %d\n", (int) seq);
+  if (QUEUE_DEBUG) printf("SIZEOF PEER %d, *PEER %d\n", sizeof(peer), sizeof(*peer));
+
+  qmsg->seq = seq;
+  memcpy(&qmsg->peer, peer, sizeof(*peer));
+
+  for (qmsg2 = queue->hashseq[hash]; qmsg2; qmsg2 = qmsg2->seqnext)
+    qmsg_prev = qmsg2;
+  if (!qmsg_prev) 
+    queue->hashseq[hash] = qmsg;
+  else
+    qmsg_prev->seqnext = qmsg;
+  if (QUEUE_DEBUG) printf("End queue_seqset\n");
+  return 0;
+}
+int queue_seqdel(struct queue_t *queue, struct qmsg_t *qmsg) {
+  int hash = queue_seqhash(&qmsg->peer, qmsg->seq);
+  struct qmsg_t *qmsg2;
+  struct qmsg_t *qmsg_prev = NULL;
+  if (QUEUE_DEBUG) printf("Begin queue_seqdel seq = %d\n", (int) qmsg->seq);
+
+  for (qmsg2 = queue->hashseq[hash]; qmsg2; qmsg2 = qmsg2->seqnext) {
+    if (qmsg == qmsg) {
+      if (!qmsg_prev) 
+	queue->hashseq[hash] = qmsg2->seqnext;
+      else 
+	qmsg_prev->seqnext = qmsg2->seqnext;
+      if (QUEUE_DEBUG) printf("End queue_seqset: SEQ found\n");
+      return 0;
+    }
+    qmsg_prev = qmsg2;
+  }
+  printf("End queue_seqset: SEQ not found\n");
+  return EOF; /* End of linked list and not found */
+}
+
+
+/*  Allocates and initialises new queue structure */
+int queue_new(struct queue_t **queue) {
+  if (QUEUE_DEBUG) printf("queue_new\n");
+  *queue = calloc(1, sizeof(struct queue_t));
+  (*queue)->next = 0;
+  (*queue)->first = -1;
+  (*queue)->last = -1;
+
+  if (QUEUE_DEBUG) queue_print(*queue);
+  if (*queue) return 0;
+  else return EOF;
+}
+
+/*  Deallocates queue structure */
+int queue_free(struct queue_t *queue) {
+  if (QUEUE_DEBUG) printf("queue_free\n");
+  if (QUEUE_DEBUG) queue_print(queue);
+  free(queue);
+  return 0;
+}
+
+int queue_newmsg(struct queue_t *queue, struct qmsg_t **qmsg,
+		 struct sockaddr_in *peer, uint16_t seq) {
+  if (QUEUE_DEBUG) printf("queue_newmsg %d\n", (int) seq);
+  if (queue->qmsga[queue->next].state == 1) {
+    return EOF; /* Queue is full */
+  }
+  else {
+    *qmsg = &queue->qmsga[queue->next];
+    queue_seqset(queue, *qmsg, peer, seq);
+    (*qmsg)->state = 1;    /* Space taken */
+    (*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 */
+    queue->last = queue->next;                  /* End of queue */
+    if (queue->first == -1) queue->first = queue->next;
+    queue->next = (queue->next+1) % QUEUE_SIZE;   /* Increment */
+    if (QUEUE_DEBUG) queue_print(queue);
+    return 0;
+  }
+}
+
+int queue_freemsg(struct queue_t *queue, struct qmsg_t *qmsg) {
+  if (QUEUE_DEBUG) printf("queue_freemsg\n");
+  if (qmsg->state != 1) { 
+    return EOF; /* Not in queue */
+  }
+
+  queue_seqdel(queue, qmsg);
+
+  if (qmsg->next == -1) /* Are we the last in queue? */
+    queue->last = qmsg->prev;
+  else
+    queue->qmsga[qmsg->next].prev = qmsg->prev;
+    
+  if (qmsg->prev == -1) /* Are we the first in queue? */
+    queue->first = qmsg->next;
+  else
+    queue->qmsga[qmsg->prev].next = qmsg->next;
+
+  memset(qmsg, 0, sizeof(struct qmsg_t)); /* Just to be safe */
+
+  if (QUEUE_DEBUG) queue_print(queue);
+
+  return 0;
+}
+
+int queue_back(struct queue_t *queue, struct qmsg_t *qmsg) {
+  if (QUEUE_DEBUG) printf("queue_back\n");
+  if (qmsg->state != 1) { 
+    return EOF; /* Not in queue */
+  }
+
+  /* Insert stuff to maintain hash table */
+
+  if (qmsg->next != -1) {/* Only swop if there are others */
+    queue->qmsga[qmsg->next].prev = qmsg->prev;
+    queue->first = qmsg->next;
+    
+    qmsg->next = -1;
+    qmsg->prev = queue->last;
+    if (queue->last != -1) queue->qmsga[queue->last].next = qmsg->this; 
+    queue->last = qmsg->this;
+  }
+  if (QUEUE_DEBUG) queue_print(queue);
+  return 0;
+}
+
+/* Get the element with a particular sequence number */
+int queue_getfirst(struct queue_t *queue, struct qmsg_t **qmsg) {
+  /*printf("queue_getfirst\n");*/
+  if (queue->first == -1) {
+    *qmsg = NULL;
+    return EOF; /* End of queue = queue is empty. */
+  }
+  *qmsg = &queue->qmsga[queue->first];
+  if (QUEUE_DEBUG) queue_print(queue);
+  return 0;
+}
+
+int queue_getseqx(struct queue_t *queue, struct qmsg_t **qmsg,
+		 struct sockaddr_in *peer, uint16_t seq) {
+  int n;
+  if (QUEUE_DEBUG) printf("queue_getseq, %d\n", (int) seq);
+  if (QUEUE_DEBUG) queue_print(queue);
+  for (n=0; n<QUEUE_SIZE; n++) {
+    if ((queue->qmsga[n].seq == seq) &&
+	(!memcmp(&queue->qmsga[n].peer, peer, sizeof(*peer)))) {
+      *qmsg = &queue->qmsga[n];
+      return 0;
+    }
+  }
+  return EOF; /* Not found */
+}
+
+int queue_seqget(struct queue_t *queue, struct qmsg_t **qmsg,
+		 struct sockaddr_in *peer, uint16_t seq) {
+  int hash = queue_seqhash(peer, seq);
+  struct qmsg_t *qmsg2;
+  if (QUEUE_DEBUG) printf("Begin queue_seqget seq = %d\n", (int) seq);
+  for (qmsg2 = queue->hashseq[hash]; qmsg2; qmsg2 = qmsg2->seqnext) {
+    if ((qmsg2->seq == seq) && 
+	(!memcmp(&qmsg2->peer, peer, sizeof(*peer)))) {
+      *qmsg = qmsg2;
+      if (QUEUE_DEBUG) printf("End queue_seqget. Found\n");
+      return 0;
+    }
+  }
+  if (QUEUE_DEBUG) printf("End queue_seqget. Not found\n");
+  return EOF; /* End of linked list and not found */
+}
+
+int queue_freemsg_seq(struct queue_t *queue, struct sockaddr_in *peer, 
+		      uint16_t seq, uint8_t *type, void **aid) {
+  struct qmsg_t *qmsg;
+  if (queue_seqget(queue, &qmsg, peer, seq)) {
+    *aid = NULL;
+    *type = 0;
+    return EOF;
+  }
+  *aid = qmsg->aid;
+  *type = qmsg->type;
+  if (queue_freemsg(queue, qmsg)) {
+    return EOF;
+  }
+  return 0;
+}
diff --git a/gtp/queue.h b/gtp/queue.h
new file mode 100644
index 0000000..076a3ef
--- /dev/null
+++ b/gtp/queue.h
@@ -0,0 +1,77 @@
+/* 
+ *  OpenGGSN - Gateway GPRS Support Node
+ *  Copyright (C) 2002 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):
+ * 
+ */
+
+/*
+ * Queue.c
+ * Reliable delivery of signalling messages
+ */
+
+#ifndef _QUEUE_H
+#define _QUEUE_H
+
+#define QUEUE_DEBUG 0    /* Print debug information */
+
+#define QUEUE_SIZE 256 /* Size of retransmission queue */
+#define QUEUE_HASH_SIZE 65536 /* Size of hash table (2^16) */
+
+struct qmsg_t {           /* Holder for queued packets */
+  int state;              /* 0=empty, 1=full */
+  uint16_t seq;           /* The sequence number */
+  u_int8_t type;         /* The type of packet */
+  void *aid;              /* Application specific pointer */
+  union gtp_packet p;     /* The packet stored */
+  int l;                  /* Length of the packet */
+  struct sockaddr_in peer;/* Address packet was sent to / received from */
+  struct qmsg_t *seqnext; /* Pointer to next in sequence hash list */
+  int next;               /* Pointer to the next in queue. -1: Last */
+  int prev;               /* Pointer to the previous in queue. -1: First */
+  int this;               /* Pointer to myself */
+  time_t timeout;         /* When do we retransmit this packet? */
+  int retrans;            /* How many times did we retransmit this? */
+};
+
+struct queue_t {
+  struct qmsg_t qmsga[QUEUE_SIZE]; /* Array holding signalling messages */
+  void *hashseq[QUEUE_HASH_SIZE];    /* Hash array */
+  int next;               /* Next location in queue to use */
+  int first;              /* First packet in queue (oldest timeout) */
+  int last;               /* Last packet in queue (youngest timeout) */
+};
+
+
+/*  Allocates and initialises new queue structure */
+int queue_new(struct queue_t **queue);
+/*  Deallocates queue structure */
+int queue_free(struct queue_t *queue);
+/* Find a new queue element. Return EOF if allready full */
+int queue_newmsg(struct queue_t *queue, struct qmsg_t **qmsg,
+		 struct sockaddr_in *peer, uint16_t seq);
+/* Remove an element from the queue. */
+int queue_freemsg(struct queue_t *queue, struct qmsg_t *qmsg);
+/* Move an element to the back of the queue */
+int queue_back(struct queue_t *queue, struct qmsg_t *qmsg);
+/* Get the first element in the queue (oldest) */
+int queue_getfirst(struct queue_t *queue, struct qmsg_t **qmsg);
+/* Get the element with a particular sequence number */
+int queue_seqget(struct queue_t *queue, struct qmsg_t **qmsg,
+		 struct sockaddr_in  *peer, uint16_t seq);
+/* Free message based on sequence number */
+int queue_freemsg_seq(struct queue_t *queue, struct sockaddr_in *peer,
+		      uint16_t seq, uint8_t *type, void **aid);
+
+
+#endif	/* !_QUEUE_H */
+