diff options
author | jjako <jjako> | 2002-12-16 13:33:51 +0000 |
---|---|---|
committer | jjako <jjako> | 2002-12-16 13:33:51 +0000 |
commit | 52c2414f6cabefb0427475756e8ac4856180bc59 (patch) | |
tree | 5ecb31a74c392c36a7d7c802f18d37349973bf00 /gtp |
Initial revision
Diffstat (limited to 'gtp')
-rw-r--r-- | gtp/.deps/gtp.P | 111 | ||||
-rw-r--r-- | gtp/.deps/gtpie.P | 52 | ||||
-rw-r--r-- | gtp/.deps/lookupa.P | 3 | ||||
-rw-r--r-- | gtp/.deps/pdp.P | 53 | ||||
-rw-r--r-- | gtp/.deps/queue.P | 64 | ||||
-rw-r--r-- | gtp/Makefile | 339 | ||||
-rw-r--r-- | gtp/Makefile.am | 9 | ||||
-rw-r--r-- | gtp/Makefile.in | 339 | ||||
-rw-r--r-- | gtp/gtp.c | 1917 | ||||
-rw-r--r-- | gtp/gtp.h | 339 | ||||
-rw-r--r-- | gtp/gtpie.c | 513 | ||||
-rw-r--r-- | gtp/gtpie.h | 279 | ||||
-rw-r--r-- | gtp/lookupa.c | 246 | ||||
-rw-r--r-- | gtp/lookupa.h | 29 | ||||
-rw-r--r-- | gtp/pdp.c | 320 | ||||
-rw-r--r-- | gtp/pdp.h | 203 | ||||
-rw-r--r-- | gtp/queue.c | 249 | ||||
-rw-r--r-- | gtp/queue.h | 77 |
18 files changed, 5142 insertions, 0 deletions
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, >p0_default, sizeof(gtp0_default)); + break; + case 1: + memcpy(packet, >p1_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(>p0_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(>p1_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 */ + |