diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/AUTHORS b/openair-cn/OPENAIRHSS/OPENAIRHSS/AUTHORS new file mode 100644 index 0000000000..e69de29bb2 diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/COPYING b/openair-cn/OPENAIRHSS/OPENAIRHSS/COPYING new file mode 120000 index 0000000000..6168a397eb --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/COPYING @@ -0,0 +1 @@ +/usr/share/automake-1.11/COPYING \ No newline at end of file diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/ChangeLog b/openair-cn/OPENAIRHSS/OPENAIRHSS/ChangeLog new file mode 100644 index 0000000000..e69de29bb2 diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/INSTALL b/openair-cn/OPENAIRHSS/OPENAIRHSS/INSTALL new file mode 100644 index 0000000000..a1e89e18ad --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/INSTALL @@ -0,0 +1,370 @@ +Installation Instructions +************************* + +Copyright (C) 1994-1996, 1999-2002, 2004-2011 Free Software Foundation, +Inc. + + Copying and distribution of this file, with or without modification, +are permitted in any medium without royalty provided the copyright +notice and this notice are preserved. This file is offered as-is, +without warranty of any kind. + +Basic Installation +================== + + Briefly, the shell commands `./configure; make; make install' should +configure, build, and install this package. The following +more-detailed instructions are generic; see the `README' file for +instructions specific to this package. Some packages provide this +`INSTALL' file but do not implement all of the features documented +below. The lack of an optional feature in a given package is not +necessarily a bug. More recommendations for GNU packages can be found +in *note Makefile Conventions: (standards)Makefile Conventions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, and a +file `config.log' containing compiler output (useful mainly for +debugging `configure'). + + It can also use an optional file (typically called `config.cache' +and enabled with `--cache-file=config.cache' or simply `-C') that saves +the results of its tests to speed up reconfiguring. Caching is +disabled by default to prevent problems with accidental use of stale +cache files. + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If you are using the cache, and at +some point `config.cache' contains results you don't want to keep, you +may remove or edit it. + + The file `configure.ac' (or `configure.in') is used to create +`configure' by a program called `autoconf'. You need `configure.ac' if +you want to change it or regenerate `configure' using a newer version +of `autoconf'. + + The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. + + Running `configure' might take a while. While running, it prints + some messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package, generally using the just-built uninstalled binaries. + + 4. Type `make install' to install the programs and any data files and + documentation. When installing into a prefix owned by root, it is + recommended that the package be configured and built as a regular + user, and only the `make install' phase executed with root + privileges. + + 5. Optionally, type `make installcheck' to repeat any self-tests, but + this time using the binaries in their final installed location. + This target does not install anything. Running this target as a + regular user, particularly if the prior `make install' required + root privileges, verifies that the installation completed + correctly. + + 6. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + + 7. Often, you can also type `make uninstall' to remove the installed + files again. In practice, not all packages have tested that + uninstallation works correctly, even though it is required by the + GNU Coding Standards. + + 8. Some packages, particularly those that use Automake, provide `make + distcheck', which can by used by developers to test that all other + targets like `make install' and `make uninstall' work correctly. + This target is generally not run by end users. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. Run `./configure --help' +for details on some of the pertinent environment variables. + + You can give `configure' initial values for configuration parameters +by setting variables in the command line or in the environment. Here +is an example: + + ./configure CC=c99 CFLAGS=-g LIBS=-lposix + + *Note Defining Variables::, for more details. + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you can use GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. This +is known as a "VPATH" build. + + With a non-GNU `make', it is safer to compile the package for one +architecture at a time in the source code directory. After you have +installed the package for one architecture, use `make distclean' before +reconfiguring for another architecture. + + On MacOS X 10.5 and later systems, you can create libraries and +executables that work on multiple system types--known as "fat" or +"universal" binaries--by specifying multiple `-arch' options to the +compiler but only a single `-arch' option to the preprocessor. Like +this: + + ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ + CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ + CPP="gcc -E" CXXCPP="g++ -E" + + This is not guaranteed to produce working output in all cases, you +may have to build one architecture at a time and combine the results +using the `lipo' tool if you have problems. + +Installation Names +================== + + By default, `make install' installs the package's commands under +`/usr/local/bin', include files under `/usr/local/include', etc. You +can specify an installation prefix other than `/usr/local' by giving +`configure' the option `--prefix=PREFIX', where PREFIX must be an +absolute file name. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +pass the option `--exec-prefix=PREFIX' to `configure', the package uses +PREFIX as the prefix for installing programs and libraries. +Documentation and other data files still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=DIR' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. In general, the +default for these options is expressed in terms of `${prefix}', so that +specifying just `--prefix' will affect all of the other directory +specifications that were not explicitly provided. + + The most portable way to affect installation locations is to pass the +correct locations to `configure'; however, many packages provide one or +both of the following shortcuts of passing variable assignments to the +`make install' command line to change installation locations without +having to reconfigure or recompile. + + The first method involves providing an override variable for each +affected directory. For example, `make install +prefix=/alternate/directory' will choose an alternate location for all +directory configuration variables that were expressed in terms of +`${prefix}'. Any directories that were specified during `configure', +but not in terms of `${prefix}', must each be overridden at install +time for the entire installation to be relocated. The approach of +makefile variable overrides for each directory variable is required by +the GNU Coding Standards, and ideally causes no recompilation. +However, some platforms have known limitations with the semantics of +shared libraries that end up requiring recompilation when using this +method, particularly noticeable in packages that use GNU Libtool. + + The second method involves providing the `DESTDIR' variable. For +example, `make install DESTDIR=/alternate/directory' will prepend +`/alternate/directory' before all installation names. The approach of +`DESTDIR' overrides is not required by the GNU Coding Standards, and +does not work on platforms that have drive letters. On the other hand, +it does better at avoiding recompilation issues, and works well even +when some directory options were not specified in terms of `${prefix}' +at `configure' time. + +Optional Features +================= + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + + Some packages offer the ability to configure how verbose the +execution of `make' will be. For these packages, running `./configure +--enable-silent-rules' sets the default to minimal output, which can be +overridden with `make V=1'; while running `./configure +--disable-silent-rules' sets the default to verbose, which can be +overridden with `make V=0'. + +Particular systems +================== + + On HP-UX, the default C compiler is not ANSI C compatible. If GNU +CC is not installed, it is recommended to use the following options in +order to use an ANSI C compiler: + + ./configure CC="cc -Ae -D_XOPEN_SOURCE=500" + +and if that doesn't work, install pre-built binaries of GCC for HP-UX. + + HP-UX `make' updates targets which have the same time stamps as +their prerequisites, which makes it generally unusable when shipped +generated files such as `configure' are involved. Use GNU `make' +instead. + + On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot +parse its `' header file. The option `-nodtk' can be used as +a workaround. If GNU CC is not installed, it is therefore recommended +to try + + ./configure CC="cc" + +and if that doesn't work, try + + ./configure CC="cc -nodtk" + + On Solaris, don't put `/usr/ucb' early in your `PATH'. This +directory contains several dysfunctional programs; working variants of +these programs are available in `/usr/bin'. So, if you need `/usr/ucb' +in your `PATH', put it _after_ `/usr/bin'. + + On Haiku, software installed for all users goes in `/boot/common', +not `/usr/local'. It is recommended to use the following options: + + ./configure --prefix=/boot/common + +Specifying the System Type +========================== + + There may be some features `configure' cannot figure out +automatically, but needs to determine by the type of machine the package +will run on. Usually, assuming the package is built to be run on the +_same_ architectures, `configure' can figure that out, but if it prints +a message saying it cannot guess the machine type, give it the +`--build=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name which has the form: + + CPU-COMPANY-SYSTEM + +where SYSTEM can have one of these forms: + + OS + KERNEL-OS + + See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the machine type. + + If you are _building_ compiler tools for cross-compiling, you should +use the option `--target=TYPE' to select the type of system they will +produce code for. + + If you want to _use_ a cross compiler, that generates code for a +platform different from the build platform, you should specify the +"host" platform (i.e., that on which the generated programs will +eventually be run) with `--host=TYPE'. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Defining Variables +================== + + Variables not defined in a site shell script can be set in the +environment passed to `configure'. However, some packages may run +configure again during the build, and the customized values of these +variables may be lost. In order to avoid this problem, you should set +them in the `configure' command line, using `VAR=value'. For example: + + ./configure CC=/usr/local2/bin/gcc + +causes the specified `gcc' to be used as the C compiler (unless it is +overridden in the site shell script). + +Unfortunately, this technique does not work for `CONFIG_SHELL' due to +an Autoconf bug. Until the bug is fixed you can use this workaround: + + CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash + +`configure' Invocation +====================== + + `configure' recognizes the following options to control how it +operates. + +`--help' +`-h' + Print a summary of all of the options to `configure', and exit. + +`--help=short' +`--help=recursive' + Print a summary of the options unique to this package's + `configure', and exit. The `short' variant lists options used + only in the top level, while the `recursive' variant lists options + also present in any nested packages. + +`--version' +`-V' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`--cache-file=FILE' + Enable the cache: use and save the results of the tests in FILE, + traditionally `config.cache'. FILE defaults to `/dev/null' to + disable caching. + +`--config-cache' +`-C' + Alias for `--cache-file=config.cache'. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--prefix=DIR' + Use DIR as the installation prefix. *note Installation Names:: + for more details, including other options available for fine-tuning + the installation locations. + +`--no-create' +`-n' + Run the configure checks, but stop before creating any output + files. + +`configure' also accepts some other, not widely useful, options. Run +`configure --help' for more details. + diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/Makefile.am b/openair-cn/OPENAIRHSS/OPENAIRHSS/Makefile.am new file mode 100644 index 0000000000..7ded68e6c9 --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/Makefile.am @@ -0,0 +1,26 @@ +ACLOCAL_AMFLAGS = -I m4 + +SUBDIRS = access_restriction auc db s6a utils tests . + +AM_CFLAGS = @ADD_CFLAGS@ \ + -I$(top_srcdir)/access_restriction \ + -I$(top_srcdir)/auc \ + -I$(top_srcdir)/db \ + -I$(top_srcdir)/s6a \ + -I$(top_srcdir)/utils + +bin_PROGRAMS = openair-hss + +openair_hssdir=$(sysconfdir)/openair-hss +openair_hss_DATA = $(top_builddir)/conf/hss.conf $(top_srcdir)/conf/hss_fd.conf + +openair_hss_LDADD = \ + $(top_builddir)/s6a/libs6a.la \ + $(top_builddir)/db/libdb.la \ + $(top_builddir)/auc/libauc.la \ + $(top_builddir)/access_restriction/libaccess_restriction.la \ + $(top_builddir)/utils/libutils.la + +openair_hss_SOURCES = \ + hss_config.h \ + hss_main.c diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/NEWS b/openair-cn/OPENAIRHSS/OPENAIRHSS/NEWS new file mode 100644 index 0000000000..e69de29bb2 diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/README b/openair-cn/OPENAIRHSS/OPENAIRHSS/README new file mode 100644 index 0000000000..e69de29bb2 diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/access_restriction/Makefile.am b/openair-cn/OPENAIRHSS/OPENAIRHSS/access_restriction/Makefile.am new file mode 100644 index 0000000000..e7db61c5b8 --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/access_restriction/Makefile.am @@ -0,0 +1,9 @@ +AM_CFLAGS = @ADD_CFLAGS@ \ + -I$(top_srcdir) \ + -I$(top_srcdir)/utils + +noinst_LTLIBRARIES = libaccess_restriction.la + +libaccess_restriction_la_LDFLAGS = -all-static +libaccess_restriction_la_SOURCES = \ + access_restriction.c access_restriction.h \ No newline at end of file diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/access_restriction/access_restriction.c b/openair-cn/OPENAIRHSS/OPENAIRHSS/access_restriction/access_restriction.c new file mode 100644 index 0000000000..c8a697c6e9 --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/access_restriction/access_restriction.c @@ -0,0 +1,100 @@ +#include +#include +#include +#include + +#include "conversion.h" +#include "access_restriction.h" + +/* TODO: identification of MCC and MNC within an IMSI should be done according + * to a table that maps MCC to 2 or 3 digits expected... + * By default we will assume there is only 2 digits for our use. + */ + +/* Split a PLMN formed of to mcc and mnc. + * In case MNC is formed of only two digits a 0 is inserted at the most significant + * digit. + * When PLMN is represented using european convention it contains only two digits, + * while three digits are used in North American Standard. + * Encoding of PLMN is defined in ITU E.212. + * @param plmn string either 5 or 6 digits resulting of the concatenation of + * MCC and MNC. + */ +int split_plmn(uint8_t plmn[3], uint8_t mcc[3], uint8_t mnc[3]) +{ + if (plmn == NULL) { + return -1; + } + + mcc[0] = plmn[0] & 0x0F; + mcc[1] = (plmn[0] & 0xF0) >> 4; + mcc[2] = plmn[1] & 0x0F; + + if ((plmn[1] & 0xF0) == 0xF0) { + /* Only 2 digits case */ + mnc[1] = plmn[2] & 0x0F; + mnc[2] = (plmn[2] & 0xF0) >> 4; + mnc[0] = 0; + } else { + mnc[0] = plmn[2] & 0x0F; + mnc[1] = (plmn[2] & 0xF0) >> 4; + mnc[2] = ((plmn[1] & 0xF0) >> 4); + } + + return 0; +} + +/* Apply restriction (if any) to current 'visited' PLMN for this user. + * Criterias are based on ODB (operator determined barring), visited PLMN + * and user PLMN (obtain from IMSI). + * @param imsi is the user identity formed of MCC.MNC.MSIN (14 or 15 digits long) + * @param vplmn is the plmn of the cell the UE is currently attached to + */ +#define FORMAT_MCC(mCC) (mCC[0] * 100 + mCC[1] * 10 + mCC[2]) +#define FORMAT_MNC(mNC) (mNC[0] * 100 + mNC[1] * 10 + mNC[2]) + +int apply_access_restriction(char *imsi, uint8_t *vplmn) +{ + uint8_t vmcc[3], vmnc[3]; + uint8_t hmcc[3], hmnc[3]; + + uint8_t imsi_hex[15]; + + if (bcd_to_hex(imsi_hex, imsi, strlen(imsi)) != 0) { + fprintf(stderr, "Failed to convert imsi %s to hex representation\n", + imsi); + return -1; + } + + /* There is a problem while converting the PLMN... */ + if (split_plmn(vplmn, vmcc, vmnc) != 0) { + fprintf(stderr, "Fail to convert vplmn %02x%02x%02x to mcc/mnc for imsi %s\n", + vplmn[0], vplmn[1], vplmn[2], imsi); + return -1; + } + + fprintf(stderr, "Converted %02x%02x%02x to plmn %u.%u\n", vplmn[0], vplmn[1], vplmn[2], + FORMAT_MCC(vmcc), FORMAT_MNC(vmnc)); + + /* MCC is always 3 digits */ + memcpy(hmcc, &imsi_hex[0], 3); + if (memcmp(vmcc, hmcc, 3) != 0) { + fprintf(stderr, "Only France MCC is handled for now, got imsi plmn %u.%u for a visited plmn %u.%u\n", + FORMAT_MCC(hmcc), FORMAT_MNC(hmnc), FORMAT_MCC(vmcc), FORMAT_MNC(vmnc)); + /* Reject the association */ + return -1; + } + + /* In France MNC is composed of 2 digits and thus imsi by 14 digit */ + hmnc[0] = 0; + memcpy(&hmnc[1], &imsi_hex[3], 2); + + if ((memcmp(vmcc, hmcc, 3) != 0) && (memcmp(vmnc, hmnc, 3) != 0)) { + fprintf(stderr, "UE is roaming from %u.%u to %u.%u which is not allowed" + " by the ODB\n", FORMAT_MCC(hmcc), FORMAT_MNC(hmnc), FORMAT_MCC(vmcc), FORMAT_MNC(vmnc)); + return -1; + } + + /* User has successfully passed all the checking -> accept the association */ + return 0; +} diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/access_restriction/access_restriction.h b/openair-cn/OPENAIRHSS/OPENAIRHSS/access_restriction/access_restriction.h new file mode 100644 index 0000000000..04c9d55bf9 --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/access_restriction/access_restriction.h @@ -0,0 +1,8 @@ +#ifndef ACCESS_RESTRICTION_H_ +#define ACCESS_RESTRICTION_H_ + +int split_plmn(uint8_t *plmn, uint8_t mcc[3], uint8_t mnc[3]); + +int apply_access_restriction(char *imsi, uint8_t *vplmn); + +#endif /* ACCESS_RESTRICTION_H_ */ diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/auc/Makefile.am b/openair-cn/OPENAIRHSS/OPENAIRHSS/auc/Makefile.am new file mode 100644 index 0000000000..41774d4615 --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/auc/Makefile.am @@ -0,0 +1,14 @@ +AM_CFLAGS = @ADD_CFLAGS@ \ + -I$(top_srcdir) \ + -I$(top_srcdir)/utils + +noinst_LTLIBRARIES = libauc.la + +libauc_la_LDFLAGS = -all-static +libauc_la_SOURCES = \ + auc.h \ + fx.c \ + kdf.c \ + random.c \ + rijndael.c \ + sequence_number.c \ No newline at end of file diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/auc/auc.h b/openair-cn/OPENAIRHSS/OPENAIRHSS/auc/auc.h new file mode 100644 index 0000000000..6d47d49527 --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/auc/auc.h @@ -0,0 +1,87 @@ +#include +#include +#include + +#ifndef AUC_H_ +#define AUC_H_ + +#define SQN_LENGTH_BITS (48) +#define SQN_LENGTH_OCTEST (SQN_LENGTH_BITS/8) +#define IK_LENGTH_BITS (128) +#define IK_LENGTH_OCTETS (IK_LENGTH_BITS/8) +#define RAND_LENGTH_OCTETS (16) +#define RAND_LENGTH_BITS (RAND_LENGTH_OCTETS*8) +#define XRES_LENGTH_OCTETS (8) +#define AUTN_LENGTH_OCTETS (16) +#define KASME_LENGTH_OCTETS (32) +#define MAC_S_LENGTH (8) + +extern uint8_t opc[16]; + +typedef mpz_t random_t; +typedef mpz_t sqn_t; + +typedef uint8_t u8; + +typedef struct { + uint8_t rand[16]; + uint8_t rand_new; + uint8_t xres[8]; + uint8_t autn[16]; + uint8_t kasme[32]; +} auc_vector_t; + +void RijndaelKeySchedule(u8 key[16]); +void RijndaelEncrypt(u8 in[16], u8 out[16]); + +/* Sequence number functions */ +struct sqn_ue_s; +struct sqn_ue_s *sqn_exists(uint64_t imsi); +void sqn_insert(struct sqn_ue_s *item); +void sqn_init(struct sqn_ue_s *item); +struct sqn_ue_s *sqn_new(uint64_t imsi); +void sqn_list_init(void); +void sqn_get(uint64_t imsi, uint8_t sqn[6]); + +/* Random number functions */ +struct random_state_s; +void random_init(void); +void generate_random(uint8_t *random, ssize_t length); + +void SetOPc(u8 op_c[16]); + +void f1 ( u8 k[16], u8 rand[16], u8 sqn[6], u8 amf[2], + u8 mac_a[8] ); +void f1star( u8 k[16], u8 rand[16], u8 sqn[6], u8 amf[2], + u8 mac_s[8] ); +void f2345 ( u8 k[16], u8 rand[16], + u8 res[8], u8 ck[16], u8 ik[16], u8 ak[6] ); +void f5star( u8 k[16], u8 rand[16], + u8 ak[6] ); + +void generate_autn(u8 sqn[6], u8 ak[6], u8 amf[2], u8 mac_a[8], u8 autn[16]); +int generate_vector(uint64_t imsi, uint8_t key[16], uint8_t plmn[3], + uint8_t sqn[6], auc_vector_t *vector); + +inline +void kdf(uint8_t *key, uint16_t key_len, uint8_t *s, uint16_t s_len, uint8_t *out, + uint16_t out_len); + +inline +void derive_kasme(uint8_t ck[16], uint8_t ik[16], uint8_t plmn[3], uint8_t sqn[6], + uint8_t ak[6], uint8_t kasme[32]); + +uint8_t *sqn_ms_derive(uint8_t *key, uint8_t *auts, uint8_t *rand); + +static inline void print_buffer(const char *prefix, uint8_t *buffer, int length) +{ + int i; + + fprintf(stdout, "%s", prefix); + for (i = 0; i < length; i++) { + fprintf(stdout, "%02x.", buffer[i]); + } + fprintf(stdout, "\n"); +} + +#endif /* AUC_H_ */ diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/auc/fx.c b/openair-cn/OPENAIRHSS/OPENAIRHSS/auc/fx.c new file mode 100644 index 0000000000..92299d54ea --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/auc/fx.c @@ -0,0 +1,249 @@ +/*------------------------------------------------------------------- + * Example algorithms f1, f1*, f2, f3, f4, f5, f5* + *------------------------------------------------------------------- + * + * A sample implementation of the example 3GPP authentication and + * key agreement functions f1, f1*, f2, f3, f4, f5 and f5*. This is + * a byte-oriented implementation of the functions, and of the block + * cipher kernel function Rijndael. + * + * This has been coded for clarity, not necessarily for efficiency. + * + * The functions f2, f3, f4 and f5 share the same inputs and have + * been coded together as a single function. f1, f1* and f5* are + * all coded separately. + * + *-----------------------------------------------------------------*/ + +#include +#include +#include +#include + +#include "auc.h" + +/*--------- Operator Variant Algorithm Configuration Field --------*/ +/*------- Insert your value of OP here -------*/ +u8 OP[16]; + +/*--------------------------- prototypes --------------------------*/ +void ComputeOPc( u8 op_c[16] ); + +void SetOPc(u8 op_c[16]) +{ + memcpy(OP, op_c, 16); +} + +void generate_autn(u8 sqn[6], u8 ak[6], u8 amf[2], u8 mac_a[8], u8 autn[16]) +{ + int i; + for (i = 0; i < 6; i++) { + autn[i] = sqn[i] ^ ak[i]; + } + memcpy(&autn[6], amf, 2); + memcpy(&autn[8], mac_a, 8); +} + +/*------------------------------------------------------------------- + * Algorithm f1 + *------------------------------------------------------------------- + * + * Computes network authentication code MAC-A from key K, random + * challenge RAND, sequence number SQN and authentication management + * field AMF. + * + *-----------------------------------------------------------------*/ +void f1 ( u8 k[16], u8 _rand[16], u8 sqn[6], u8 amf[2], + u8 mac_a[8] ) +{ + u8 op_c[16]; + u8 temp[16]; + u8 in1[16]; + u8 out1[16]; + u8 rijndaelInput[16]; + u8 i; + RijndaelKeySchedule( k ); + ComputeOPc( op_c ); + for (i=0; i<16; i++) + rijndaelInput[i] = _rand[i] ^ op_c[i]; + RijndaelEncrypt( rijndaelInput, temp ); + for (i=0; i<6; i++) + { + in1[i] = sqn[i]; + in1[i+8] = sqn[i]; + } + for (i=0; i<2; i++) + { + in1[i+6] = amf[i]; + in1[i+14] = amf[i]; + } + /* XOR op_c and in1, rotate by r1=64, and XOR * + * on the constant c1 (which is all zeroes) */ + for (i=0; i<16; i++) + rijndaelInput[(i+8) % 16] = in1[i] ^ op_c[i]; + /* XOR on the value temp computed before */ + for (i=0; i<16; i++) + rijndaelInput[i] ^= temp[i]; + RijndaelEncrypt( rijndaelInput, out1 ); + for (i=0; i<16; i++) + out1[i] ^= op_c[i]; + for (i=0; i<8; i++) + mac_a[i] = out1[i]; + return; +} /* end of function f1 */ + +/*------------------------------------------------------------------- + * Algorithms f2-f5 + *------------------------------------------------------------------- + * + * Takes key K and random challenge RAND, and returns response RES, + * confidentiality key CK, integrity key IK and anonymity key AK. + * + *-----------------------------------------------------------------*/ +void f2345 ( u8 k[16], u8 _rand[16], + u8 res[8], u8 ck[16], u8 ik[16], u8 ak[6] ) +{ + u8 op_c[16]; + u8 temp[16]; + u8 out[16]; + u8 rijndaelInput[16]; + u8 i; + RijndaelKeySchedule( k ); + ComputeOPc( op_c ); + for (i=0; i<16; i++) + rijndaelInput[i] = _rand[i] ^ op_c[i]; + RijndaelEncrypt( rijndaelInput, temp ); + /* To obtain output block OUT2: XOR OPc and TEMP, * + * rotate by r2=0, and XOR on the constant c2 (which * + * is all zeroes except that the last bit is 1). */ + for (i=0; i<16; i++) + rijndaelInput[i] = temp[i] ^ op_c[i]; + rijndaelInput[15] ^= 1; + RijndaelEncrypt( rijndaelInput, out ); + for (i=0; i<16; i++) + out[i] ^= op_c[i]; + for (i=0; i<8; i++) + res[i] = out[i+8]; + for (i=0; i<6; i++) + ak[i] = out[i]; + /* To obtain output block OUT3: XOR OPc and TEMP, * + * rotate by r3=32, and XOR on the constant c3 (which * + * is all zeroes except that the next to last bit is 1). */ + + for (i=0; i<16; i++) + rijndaelInput[(i+12) % 16] = temp[i] ^ op_c[i]; + rijndaelInput[15] ^= 2; + RijndaelEncrypt( rijndaelInput, out ); + for (i=0; i<16; i++) + out[i] ^= op_c[i]; + for (i=0; i<16; i++) + ck[i] = out[i]; + /* To obtain output block OUT4: XOR OPc and TEMP, * + * rotate by r4=64, and XOR on the constant c4 (which * + * is all zeroes except that the 2nd from last bit is 1). */ + for (i=0; i<16; i++) + rijndaelInput[(i+8) % 16] = temp[i] ^ op_c[i]; + rijndaelInput[15] ^= 4; + RijndaelEncrypt( rijndaelInput, out ); + for (i=0; i<16; i++) + out[i] ^= op_c[i]; + for (i=0; i<16; i++) + ik[i] = out[i]; + return; +} /* end of function f2345 */ + +/*------------------------------------------------------------------- + * Algorithm f1* + *------------------------------------------------------------------- + * + * Computes resynch authentication code MAC-S from key K, random + * challenge RAND, sequence number SQN and authentication management + * field AMF. + * + *-----------------------------------------------------------------*/ +void f1star( u8 k[16], u8 _rand[16], u8 sqn[6], u8 amf[2], + u8 mac_s[8] ) +{ + u8 op_c[16]; + u8 temp[16]; + u8 in1[16]; + u8 out1[16]; + u8 rijndaelInput[16]; + u8 i; + RijndaelKeySchedule( k ); + ComputeOPc( op_c ); + for (i=0; i<16; i++) + rijndaelInput[i] = _rand[i] ^ op_c[i]; + RijndaelEncrypt( rijndaelInput, temp ); + for (i=0; i<6; i++) + { + in1[i] = sqn[i]; + in1[i+8] = sqn[i]; + } + for (i=0; i<2; i++) + { + in1[i+6] = amf[i]; + in1[i+14] = amf[i]; + } + /* XOR op_c and in1, rotate by r1=64, and XOR * + * on the constant c1 (which is all zeroes) */ + for (i=0; i<16; i++) + rijndaelInput[(i+8) % 16] = in1[i] ^ op_c[i]; + /* XOR on the value temp computed before */ + for (i=0; i<16; i++) + rijndaelInput[i] ^= temp[i]; + RijndaelEncrypt( rijndaelInput, out1 ); + for (i=0; i<16; i++) + out1[i] ^= op_c[i]; + for (i=0; i<8; i++) + mac_s[i] = out1[i+8]; + return; +} /* end of function f1star */ + +/*------------------------------------------------------------------- + * Algorithm f5* + *------------------------------------------------------------------- + * + * Takes key K and random challenge RAND, and returns resynch + * anonymity key AK. + * + *-----------------------------------------------------------------*/ +void f5star( u8 k[16], u8 _rand[16], + u8 ak[6] ) +{ + u8 op_c[16]; + u8 temp[16]; + u8 out[16]; + u8 rijndaelInput[16]; + u8 i; + RijndaelKeySchedule( k ); + ComputeOPc( op_c ); + for (i=0; i<16; i++) + rijndaelInput[i] = _rand[i] ^ op_c[i]; + RijndaelEncrypt( rijndaelInput, temp ); + /* To obtain output block OUT5: XOR OPc and TEMP, * + * rotate by r5=96, and XOR on the constant c5 (which * + * is all zeroes except that the 3rd from last bit is 1). */ + for (i=0; i<16; i++) + rijndaelInput[(i+4) % 16] = temp[i] ^ op_c[i]; + rijndaelInput[15] ^= 8; + RijndaelEncrypt( rijndaelInput, out ); + for (i=0; i<16; i++) + out[i] ^= op_c[i]; + for (i=0; i<6; i++) + ak[i] = out[i]; + return; +} /* end of function f5star */ + +/*------------------------------------------------------------------- + * Function to compute OPc from OP and K. Assumes key schedule has + a lready been performed. * + *-----------------------------------------------------------------*/ +void ComputeOPc( u8 op_c[16] ) +{ + u8 i; + RijndaelEncrypt( OP, op_c ); + for (i=0; i<16; i++) + op_c[i] ^= OP[i]; + return; +} /* end of function ComputeOPc */ diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/auc/kdf.c b/openair-cn/OPENAIRHSS/OPENAIRHSS/auc/kdf.c new file mode 100644 index 0000000000..ef3024c301 --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/auc/kdf.c @@ -0,0 +1,139 @@ +#include +#include +#include +#include +#include + +#include +#include + +#include "auc.h" + +uint8_t opc[16] = { + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11 +}; + +/* + * @param key the input key + * @param key_len length of the key + * @param s string for key derivation as defined in 3GPP TS.33401 Annex A + * @param s_len length of s + * @param out buffer to place the output of kdf + * @param ou_len expected length for the output key + */ +inline +void kdf(uint8_t *key, uint16_t key_len, uint8_t *s, uint16_t s_len, uint8_t *out, + uint16_t out_len) +{ + struct hmac_sha256_ctx ctx; + + memset(&ctx, 0, sizeof(ctx)); + + hmac_sha256_set_key(&ctx, key_len, key); + hmac_sha256_update(&ctx, s_len, s); + hmac_sha256_digest(&ctx, out_len, out); +} + +/* + * Derive the Kasme using the KDF (key derive function). + * See 3GPP TS.33401 Annex A.2 + * The input String S to the KDF is composed of 14 bytes: + * FC = 0x10 + * P0 = SN id = PLMN + * L0 = length(SN id) = 0x00 0x03 + * P1 = SQN xor AK + * L1 = length(P1) = 0x00 0x06 + */ +inline +void derive_kasme(uint8_t ck[16], uint8_t ik[16], uint8_t plmn[3], uint8_t sqn[6], + uint8_t ak[6], uint8_t *kasme) +{ + uint8_t s[14]; + int i; + uint8_t key[32]; + + /* The input key is equal to the concatenation of CK and IK */ + memcpy(&key[0], ck, 16); + memcpy(&key[16], ik, 16); + + SetOPc(opc); + + /* FC */ + s[0] = 0x10; + + /* SN id is composed of MCC and MNC + * Octets: + * 1 MCC digit 2 | MCC digit 1 + * 2 MNC digit 3 | MCC digit 3 + * 3 MNC digit 2 | MNC digit 1 + */ + memcpy(&s[1], plmn, 3); + + /* L0 */ + s[4] = 0x00; + s[5] = 0x03; + + /* P1 */ + for (i = 0; i < 6; i++) { + s[6 + i] = sqn[i] ^ ak[i]; + } + + /* L1 */ + s[12] = 0x00; + s[13] = 0x06; + +#if defined(DEBUG_AUC_KDF) + for (i = 0; i < 32; i++) + printf("0x%02x ", key[i]); + printf("\n"); + for (i = 0; i < 14; i++) + printf("0x%02x ", s[i]); + printf("\n"); +#endif + + kdf(key, 32, s, 14, kasme, 32); +} + +int generate_vector(uint64_t imsi, uint8_t key[16], uint8_t plmn[3], + uint8_t sqn[6], auc_vector_t *vector) +{ + /* in E-UTRAN an authentication vector is composed of: + * - RAND + * - XRES + * - AUTN + * - KASME + */ + + uint8_t amf[] = { 0x80, 0x00 }; + uint8_t mac_a[8]; + uint8_t ck[16]; + uint8_t ik[16]; + uint8_t ak[6]; + int i; + + if (vector == NULL) { + return EINVAL; + } + + /* Compute MAC */ + f1(key, vector->rand, sqn, amf, mac_a); + + print_buffer("MAC_A : ", mac_a, 8); + print_buffer("SQN : ", sqn, 6); + print_buffer("AK : ", ak, 6); + print_buffer("RAND : ", vector->rand, 16); + + /* Compute XRES, CK, IK, AK */ + f2345(key, vector->rand, vector->xres, ck, ik, ak); + + /* AUTN = SQN ^ AK || AMF || MAC */ + generate_autn(sqn, ak, amf, mac_a, vector->autn); + + print_buffer("XRES : ", vector->xres, 8); + + derive_kasme(ck, ik, plmn, sqn, ak, vector->kasme); + print_buffer("KASME : ", vector->kasme, 32); + + return 0; +} diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/auc/random.c b/openair-cn/OPENAIRHSS/OPENAIRHSS/auc/random.c new file mode 100644 index 0000000000..c34fec5f48 --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/auc/random.c @@ -0,0 +1,54 @@ +#include +#include +#include +#include + +#include +#include + +#include "auc.h" + +typedef struct random_state_s { + pthread_mutex_t lock; + gmp_randstate_t state; +} random_state_t; + +random_state_t random_state; + +void random_init(void) +{ +// mpz_t number; +// pthread_mutex_init(&random_state.lock, NULL); +// mpz_init(number); +// gmp_randinit_default(random_state.state); +// srand(time(NULL)); + struct timeval t1; + gettimeofday(&t1, NULL); + srand(t1.tv_usec * t1.tv_sec); +} + +/* Generate a random number between 0 and 2^length - 1 where length is expressed + * in bits. + */ +void generate_random(uint8_t *random_p, ssize_t length) +{ +// random_t random_nb; + +// mpz_init_set_ui(random_nb, 0); + +// pthread_mutex_lock(&random_state.lock); +// mpz_urandomb(random_nb, random_state.state, 8 * length); +// pthread_mutex_unlock(&random_state.lock); + +// mpz_export(random_p, NULL, 1, length, 0, 0, random_nb); + int r = 0, i, mask = 0, shift; + + for (i = 0; i < length; i ++) { +// if ((i % sizeof(i)) == 0) +// r = rand(); +// shift = 8 * (i % sizeof(i)); +// mask = 0xFF << shift; +// random_p[i] = (r & mask) >> shift; + random_p[i] = rand(); + } +} diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/auc/rijndael.c b/openair-cn/OPENAIRHSS/OPENAIRHSS/auc/rijndael.c new file mode 100644 index 0000000000..c1a14f7bfa --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/auc/rijndael.c @@ -0,0 +1,181 @@ +#include +#include +#include + +#include "auc.h" + +typedef uint8_t u8; +typedef uint32_t u32; + +/*-------------------- Rijndael round subkeys ---------------------*/ +u8 roundKeys[11][4][4]; +/*--------------------- Rijndael S box table ----------------------*/ +u8 S[256] = { + 99,124,119,123,242,107,111,197, 48, 1,103, 43,254,215,171,118, + 202,130,201,125,250, 89, 71,240,173,212,162,175,156,164,114,192, + 183,253,147, 38, 54, 63,247,204, 52,165,229,241,113,216, 49, 21, + 4,199, 35,195, 24,150, 5,154, 7, 18,128,226,235, 39,178,117, + 9,131, 44, 26, 27,110, 90,160, 82, 59,214,179, 41,227, 47,132, + 83,209, 0,237, 32,252,177, 91,106,203,190, 57, 74, 76, 88,207, + 208,239,170,251, 67, 77, 51,133, 69,249, 2,127, 80, 60,159,168, + 81,163, 64,143,146,157, 56,245,188,182,218, 33, 16,255,243,210, + 205, 12, 19,236, 95,151, 68, 23,196,167,126, 61,100, 93, 25,115, + 96,129, 79,220, 34, 42,144,136, 70,238,184, 20,222, 94, 11,219, + 224, 50, 58, 10, 73, 6, 36, 92,194,211,172, 98,145,149,228,121, + 231,200, 55,109,141,213, 78,169,108, 86,244,234,101,122,174, 8, + 186,120, 37, 46, 28,166,180,198,232,221,116, 31, 75,189,139,138, + 112, 62,181,102, 72, 3,246, 14, 97, 53, 87,185,134,193, 29,158, + 225,248,152, 17,105,217,142,148,155, 30,135,233,206, 85, 40,223, + 140,161,137, 13,191,230, 66,104, 65,153, 45, 15,176, 84,187, 22, +}; +/*------- This array does the multiplication by x in GF(2^8) ------*/ +u8 Xtime[256] = { + 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, + 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, + 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, + 96, 98,100,102,104,106,108,110,112,114,116,118,120,122,124,126, + 128,130,132,134,136,138,140,142,144,146,148,150,152,154,156,158, + 160,162,164,166,168,170,172,174,176,178,180,182,184,186,188,190, + 192,194,196,198,200,202,204,206,208,210,212,214,216,218,220,222, + 224,226,228,230,232,234,236,238,240,242,244,246,248,250,252,254, + 27, 25, 31, 29, 19, 17, 23, 21, 11, 9, 15, 13, 3, 1, 7, 5, + 59, 57, 63, 61, 51, 49, 55, 53, 43, 41, 47, 45, 35, 33, 39, 37, + 91, 89, 95, 93, 83, 81, 87, 85, 75, 73, 79, 77, 67, 65, 71, 69, + 123,121,127,125,115,113,119,117,107,105,111,109, 99, 97,103,101, + 155,153,159,157,147,145,151,149,139,137,143,141,131,129,135,133, + 187,185,191,189,179,177,183,181,171,169,175,173,163,161,167,165, + 219,217,223,221,211,209,215,213,203,201,207,205,195,193,199,197, + 251,249,255,253,243,241,247,245,235,233,239,237,227,225,231,229 +}; +/*------------------------------------------------------------------- + * Rijndael key schedule function. Takes 16-byte key and creates + * all Rijndael's internal subkeys ready for encryption. + *-----------------------------------------------------------------*/ +void RijndaelKeySchedule( u8 key[16] ) +{ + u8 roundConst; + int i, j; + /* first round key equals key */ + for (i=0; i<16; i++) + roundKeys[0][i & 0x03][i>>2] = key[i]; + roundConst = 1; + /* now calculate round keys */ + for (i=1; i<11; i++) + { + roundKeys[i][0][0] = S[roundKeys[i-1][1][3]] + ^ roundKeys[i-1][0][0] ^ roundConst; + roundKeys[i][1][0] = S[roundKeys[i-1][2][3]] + ^ roundKeys[i-1][1][0]; + roundKeys[i][2][0] = S[roundKeys[i-1][3][3]] + ^ roundKeys[i-1][2][0]; + roundKeys[i][3][0] = S[roundKeys[i-1][0][3]] + ^ roundKeys[i-1][3][0]; + for (j=0; j<4; j++) + { + roundKeys[i][j][1] = roundKeys[i-1][j][1] ^ roundKeys[i][j][0]; + roundKeys[i][j][2] = roundKeys[i-1][j][2] ^ roundKeys[i][j][1]; + roundKeys[i][j][3] = roundKeys[i-1][j][3] ^ roundKeys[i][j][2]; + } + /* update round constant */ + roundConst = Xtime[roundConst]; + } + return; +} /* end of function RijndaelKeySchedule */ +/* Round key addition function */ +void KeyAdd(u8 state[4][4], u8 roundKeys[11][4][4], int round) +{ + int i, j; + for (i=0; i<4; i++) + for (j=0; j<4; j++) + state[i][j] ^= roundKeys[round][i][j]; + return; +} +/* Byte substitution transformation */ +int ByteSub(u8 state[4][4]) +{ + int i, j; + for (i=0; i<4; i++) + for (j=0; j<4; j++) + state[i][j] = S[state[i][j]]; + return 0; +} +/* Row shift transformation */ +void ShiftRow(u8 state[4][4]) +{ + u8 temp; + /* left rotate row 1 by 1 */ + temp = state[1][0]; + state[1][0] = state[1][1]; + state[1][1] = state[1][2]; + state[1][2] = state[1][3]; + state[1][3] = temp; + /* left rotate row 2 by 2 */ + temp = state[2][0]; + state[2][0] = state[2][2]; + state[2][2] = temp; + temp = state[2][1]; + state[2][1] = state[2][3]; + state[2][3] = temp; + /* left rotate row 3 by 3 */ + temp = state[3][0]; + state[3][0] = state[3][3]; + state[3][3] = state[3][2]; + state[3][2] = state[3][1]; + state[3][1] = temp; + return; +} +/* MixColumn transformation*/ +void MixColumn(u8 state[4][4]) +{ + u8 temp, tmp, tmp0; + int i; + /* do one column at a time */ + for (i=0; i<4;i++) + { + temp = state[0][i] ^ state[1][i] ^ state[2][i] ^ state[3][i]; + tmp0 = state[0][i]; + /* Xtime array does multiply by x in GF2^8 */ + tmp = Xtime[state[0][i] ^ state[1][i]]; + state[0][i] ^= temp ^ tmp; + tmp = Xtime[state[1][i] ^ state[2][i]]; + state[1][i] ^= temp ^ tmp; + tmp = Xtime[state[2][i] ^ state[3][i]]; + state[2][i] ^= temp ^ tmp; + tmp = Xtime[state[3][i] ^ tmp0]; + state[3][i] ^= temp ^ tmp; + } + return; +} +/*------------------------------------------------------------------- + * Rijndael encryption function. Takes 16-byte input and creates + * 16-byte output (using round keys already derived from 16-byte + * key). + *-----------------------------------------------------------------*/ +void RijndaelEncrypt( u8 input[16], u8 output[16] ) +{ + u8 state[4][4]; + int i, r; + /* initialise state array from input byte string */ + for (i=0; i<16; i++) + state[i & 0x3][i>>2] = input[i]; + /* add first round_key */ + KeyAdd(state, roundKeys, 0); + /* do lots of full rounds */ + for (r=1; r<=9; r++) + { + ByteSub(state); + ShiftRow(state); + MixColumn(state); + KeyAdd(state, roundKeys, r); + } + /* final round */ + ByteSub(state); + ShiftRow(state); + KeyAdd(state, roundKeys, r); + /* produce output byte string from state array */ + for (i=0; i<16; i++) + { + output[i] = state[i & 0x3][i>>2]; + } + return; +} /* end of function RijndaelEncrypt */ diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/auc/sequence_number.c b/openair-cn/OPENAIRHSS/OPENAIRHSS/auc/sequence_number.c new file mode 100644 index 0000000000..e10a538538 --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/auc/sequence_number.c @@ -0,0 +1,54 @@ +#include +#include +#include +#include + +#include "auc.h" + +uint8_t *sqn_ms_derive(uint8_t *key, uint8_t *auts, uint8_t *rand_p) +{ + /* AUTS = Conc(SQN MS ) || MAC-S + * Conc(SQN MS ) = SQN MS ^ f5* (RAND) + * MAC-S = f1* (SQN MS || RAND || AMF) + */ + uint8_t ak[6]; + uint8_t *conc_sqn_ms; + uint8_t *mac_s; + uint8_t mac_s_computed[MAC_S_LENGTH]; + uint8_t *sqn_ms; + uint8_t amf[2] = { 0, 0 }; + int i; + + conc_sqn_ms = auts; + mac_s = &auts[6]; + + sqn_ms = malloc(SQN_LENGTH_OCTEST); + + SetOPc(opc); + + /* Derive AK from key and rand */ + f5star(key, rand_p, ak); + + for (i = 0; i < 6; i++) { + sqn_ms[i] = ak[i] ^ conc_sqn_ms[i]; + } + + print_buffer("KEY : ", key, 16); + print_buffer("RAND : ", rand_p, 16); + print_buffer("AUTS : ", auts, 14); + print_buffer("AK : ", ak, 6); + print_buffer("SQN_MS : ", sqn_ms, 6); + print_buffer("MAC_S : ", mac_s, 8); + + f1star(key, rand_p, sqn_ms, amf, mac_s_computed); + + print_buffer("MAC_S +: ", mac_s_computed, 8); + + if (memcmp(mac_s_computed, mac_s, 8) != 0) { + fprintf(stderr, "Failed to verify computed SQN_MS\n"); + free(sqn_ms); + return NULL; + } + + return sqn_ms; +} diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/autogen.sh b/openair-cn/OPENAIRHSS/OPENAIRHSS/autogen.sh new file mode 100755 index 0000000000..fccf26a61e --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/autogen.sh @@ -0,0 +1,2 @@ +#!/bin/sh +autoreconf --force --install -I m4 \ No newline at end of file diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/conf/hss.conf b/openair-cn/OPENAIRHSS/OPENAIRHSS/conf/hss.conf new file mode 100644 index 0000000000..c718de1aa4 --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/conf/hss.conf @@ -0,0 +1,8 @@ +## MySQL mandatory options +MYSQL_server = "127.0.0.1"; +MYSQL_user = "hssadmin"; +MYSQL_pass = "admin"; +MYSQL_db = "oai_db"; + +## Freediameter options +FD_conf = "/etc/openair-hss/hss_fd.conf"; diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/conf/hss.conf.in b/openair-cn/OPENAIRHSS/OPENAIRHSS/conf/hss.conf.in new file mode 100644 index 0000000000..fd4a1ef3c7 --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/conf/hss.conf.in @@ -0,0 +1,8 @@ +## MySQL mandatory options +MYSQL_server = "127.0.0.1"; +MYSQL_user = "hssadmin"; +MYSQL_pass = "admin"; +MYSQL_db = "oai_db"; + +## Freediameter options +FD_conf = "@AM_CONF_DIR@/hss_fd.conf"; diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/conf/hss_fd.conf b/openair-cn/OPENAIRHSS/OPENAIRHSS/conf/hss_fd.conf new file mode 100644 index 0000000000..b1e088553c --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/conf/hss_fd.conf @@ -0,0 +1,66 @@ +# -------- Local --------- + +# Uncomment if the framework cannot resolv it. +Identity = "hss.eur"; + +# TLS configuration (see previous section) +TLS_Cred = "/homes/roux/lte-epc/openair3/OPENAIRHSS/conf/hss.cert.pem", +"/homes/roux/lte-epc/openair3/OPENAIRHSS/conf/hss.key.pem"; +TLS_CA = "/homes/roux/lte-epc/openair3/OPENAIRHSS/conf/cacert.pem"; + +# Disable use of TCP protocol (only listen and connect in SCTP) +# Default : TCP enabled +No_TCP; +#No_SCTP; + +# Disable use of IPv6 addresses (only IP) +# Default : IPv6 enabled +# No_IPv6; + +# Limit the number of SCTP streams +SCTP_streams = 15; + +NoRelay; +TLS_old_method; + +# Core 2 DUO +AppServThreads = 4; + +# -------- Extensions --------- + +# Uncomment (and create rtd.conf) to specify routing table for this peer. +#LoadExtension = "rt_default.fdx" : "rtd.conf"; + +# Uncomment (and create acl.conf) to allow incoming connections from other peers. +#LoadExtension = "acl_wl.fdx" : "/homes/roux/lte-epc/openair3/OPENAIRHSS/conf/acl.conf"; + +# Uncomment to display periodic state information +#LoadExtension = "dbg_monitor.fdx"; + +# Uncomment to enable an interactive Python interpreter session. +# (see doc/dbg_interactive.py.sample for more information) +#LoadExtension = "dbg_interactive.fdx"; + +# Load the RFC4005 dictionary objects +#LoadExtension = "dict_nasreq.fdx"; + +LoadExtension = "dict_nas_mipv6.fdx"; +LoadExtension = "dict_s6a.fdx"; + +# Load RFC4072 dictionary objects +#LoadExtension = "dict_eap.fdx"; + +# Load the Diameter EAP server extension (requires diameap.conf) +#LoadExtension = "app_diameap.fdx" : "diameap.conf"; + +# Load the Accounting Server extension (requires app_acct.conf) +#LoadExtension = "app_acct.fdx" : "app_acct.conf"; + +# -------- Peers --------- + +# The framework will actively attempt to establish and maintain a connection +# with the peers listed here. +# For only accepting incoming connections, see the acl_wl.fx extension. + +#ConnectPeer = "ubuntu.localdomain" { ConnectTo = "127.0.0.1"; No_TLS; }; +#ConnectPeer = "roux.test.fr" { No_TLS; }; diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/conf/make_certs.sh b/openair-cn/OPENAIRHSS/OPENAIRHSS/conf/make_certs.sh new file mode 100755 index 0000000000..ef3a6e0348 --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/conf/make_certs.sh @@ -0,0 +1,24 @@ +rm -rf demoCA +mkdir demoCA +echo 01 > demoCA/serial +touch demoCA/index.txt + +echo "Creating certificate for HSS" +# +# # CA self certificate +# openssl req -new -batch -x509 -days 3650 -nodes -newkey rsa:1024 -out cacert.pem -keyout cakey.pem -subj /CN=test.fr/C=FR/ST=Biot/L=Aix/O=test.fr/OU=mobiles +# +# openssl genrsa -out hss.key.pem 1024 +# openssl req -new -batch -out hss.csr.pem -key hss.key.pem -subj /CN=hss.test.fr/C=FR/ST=Biot/L=Aix/O=test.fr/OU=mobiles +# openssl ca -cert cacert.pem -keyfile cakey.pem -in hss.csr.pem -out hss.cert.pem -outdir . -batch + +# CA self certificate +openssl req -new -batch -x509 -days 3650 -nodes -newkey rsa:1024 -out cacert.pem -keyout cakey.pem -subj /CN=eur/C=FR/ST=PACA/L=Aix/O=Eurecom/OU=CM + +openssl genrsa -out hss.key.pem 1024 +openssl req -new -batch -out hss.csr.pem -key hss.key.pem -subj /CN=hss.eur/C=FR/ST=PACA/L=Aix/O=Eurecom/OU=CM +openssl ca -cert cacert.pem -keyfile cakey.pem -in hss.csr.pem -out hss.cert.pem -outdir . -batch + +# openssl genrsa -out $hss.key.pem 1024 +# openssl req -new -batch -out $hss.csr.pem -key $hss.key.pem -subj /CN=$hss.test.fr/C=FR/ST=Biot/L=Aix/O=test.fr/OU=mobiles +# openssl ca -cert cacert.pem -keyfile cakey.pem -in $hss.csr.pem -out $hss.cert.pem -outdir . -batch diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/configure.ac b/openair-cn/OPENAIRHSS/OPENAIRHSS/configure.ac new file mode 100644 index 0000000000..8277a87b3a --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/configure.ac @@ -0,0 +1,172 @@ +AC_PREREQ([2.65]) + +define([svnversion], esyscmd([sh -c "svnversion ..|tr -d '\n'"])) + +AC_DEFINE(SVN_REVISION, "svnversion", [SVN Revision]) + +AC_INIT([openair-hss], [1.0.0.svnversion], [openair_admin@eurecom.fr]) +AC_CANONICAL_BUILD +AC_CANONICAL_TARGET +AM_INIT_AUTOMAKE([1.11 silent-rules]) + +AC_CONFIG_MACRO_DIR([m4]) + +AC_CONFIG_HEADERS([config.h]) + +AM_MAINTAINER_MODE + +AM_SILENT_RULES([yes]) + +PKG_CHECK_MODULES(CHECK, [check >= 0.9.4], [HAVE_CHECK=true], [HAVE_CHECK=false]) +AM_CONDITIONAL(HAVE_CHECK, test x$HAVE_CHECK = xtrue) + +AM_PROG_LEX +AM_PROG_LIBTOOL + +AC_PROG_YACC +AC_PROG_CXX +AC_PROG_RANLIB + +AC_TYPE_INT8_T +AC_TYPE_INT16_T +AC_TYPE_INT32_T +AC_TYPE_UINT8_T +AC_TYPE_UINT16_T +AC_TYPE_UINT32_T +AC_TYPE_UINT64_T +AC_TYPE_SIZE_T +AC_TYPE_SSIZE_T + +AC_CHECK_HEADERS([libintl.h malloc.h netinet/in.h stddef.h]) + +AC_CHECK_FUNCS([memset strdup strerror]) +AC_C_INLINE + +AC_FUNC_ALLOCA +AC_FUNC_MALLOC +AC_FUNC_REALLOC + +dnl *** Autoconf support *** +AC_ARG_ENABLE(autoconf, + [ --disable-autoconf disable automatic generation of configure script ], + enable_autoconf=$enableval, enable_autoconf=yes +) +AC_PATH_PROG(AUTOCONF, autoconf, @echo autoconf not available) +AC_PATH_PROG(AUTOHEADER, autoheader, @echo autoheader not available) +if test -z "$AUTOCONF"; then enable_autoconf=no ; fi +if test -z "$AUTOHEADER"; then enable_autoconf=no ; fi +if test x$enable_autoconf = xyes; then + CONFIGURE_DEPENDS="configure.in" +fi +AC_SUBST(CONFIGURE_DEPENDS) + +AC_CHECK_LIB([mysqlclient], + [mysql_init], + [], + []) + +dnl ***freediameter support*** +AC_CHECK_LIB([fdcore], [fd_core_initialize], [], + [AC_MSG_ERROR(Free diameter lib not installed)]) +AC_CHECK_LIB([fdproto], [fd_core_initialize], [], + [AC_MSG_ERROR(Free diameter lib not installed)]) + +dnl *** Freediameter requirements *** +AC_CHECK_HEADERS([signalent.h]) +AC_CHECK_FUNCS([ntohll strndup]) +AC_DEFINE([HAVE_AI_ADDRCONFIG], [], + [Define to 1 if you have AI_ADDRCONFIG defined in netdb.h]) +AC_DEFINE([HAVE_CLOCK_GETTIME], [], + [Define to 1 if you have clock_gettime in librt]) +AC_DEFINE([HAVE_PTHREAD_BAR], [], + [Define to 1 if you have pthread_barrier_wait in libpthread]) +AC_DEFINE([SCTP_CONNECTX_4_ARGS], [], + [Define to 1 if sctp_connectx function accepts 4 arguments]) +AC_CHECK_LIB([rt], [clock_gettime], [AC_DEFINE(HAVE_CLOCK_GETTIME, 1)], []) +AC_CHECK_LIB([pthread], [pthread_barrier_wait], + [AC_DEFINE(HAVE_PTHREAD_BAR, 1)], []) +AC_CHECK_DECL([AI_ADDRCONFIG], + [AC_DEFINE(HAVE_AI_ADDRCONFIG, 1)], + [], [[#include ]]) +AC_MSG_CHECKING(if sctp_connectx accepts 4 arguments) +AC_LINK_IFELSE([ + AC_LANG_SOURCE( + [[int main() { return sctp_connectx(0, NULL, 0, NULL); }]]) +], [AC_DEFINE(SCTP_CONNECTX_4_ARGS, 1)]) +AC_CHECK_LIB([gnutls], + [gnutls_hash], + [AC_DEFINE(GNUTLS_VERSION_210, 1, + [Define to 1 if you have gnutls 2.10 installed])], + []) +AC_CHECK_LIB([gnutls], + [gnutls_x509_trust_list_verify_crt], + [AC_DEFINE(GNUTLS_VERSION_300, 1, + [Define to 1 if you have gnutls 3.0 installed])], + []) +AC_CHECK_LIB([gnutls], + [gnutls_handshake_set_timeout], + [AC_DEFINE(GNUTLS_VERSION_310, 1, + [Define to 1 if you have gnutls 3.1 installed])], + []) + +AC_DEFINE(FREE_DIAMETER_MINIMUM_VERSION, "1.1.5", [freeDiameter minimum version]) + +AC_CHECK_LIB(gmp, __gmpz_init, , + [AC_MSG_ERROR([GNU MP not found, see http://gmplib.org/])]) +AC_CHECK_LIB([nettle], + [nettle_hmac_sha256_set_key], + [], + [AC_MSG_ERROR(nettle is not installed)]) +AC_CHECK_LIB([pthread], + [pthread_getspecific], + [], + [AC_MSG_ERROR(lib pthread is not installed)]) + +AC_SUBST(ADD_CFLAGS) + +dnl Add these flags +CFLAGS="$CFLAGS -Wall" +CFLAGS="$CFLAGS -Wshadow" +CFLAGS="$CFLAGS -Wcast-align" +CFLAGS="$CFLAGS -Wchar-subscripts" +CFLAGS="$CFLAGS -Wmissing-prototypes" +CFLAGS="$CFLAGS -Wmissing-declarations" +CFLAGS="$CFLAGS -Werror=implicit-function-declaration" + +AC_C_BIGENDIAN +if test "x$ac_cv_c_bigendian" = "xyes"; then + CFLAGS="$CFLAGS -DBYTE_ORDER=BIG_ENDIAN" +else + CFLAGS="$CFLAGS -DBYTE_ORDER=LITTLE_ENDIAN" +fi + +AC_SUBST([AM_CFLAGS]) + +AC_ARG_ENABLE(dh_install, AS_HELP_STRING([--enable-dh-install], [Replace some variables for installation]), + [], + []) + +if test x$enable_dh_install == xyes; then + AC_SUBST(AM_CONF_DIR, $sysconfdir/openair-hss) +else + AC_SUBST(AM_CONF_DIR, $srcdir/conf) +fi + +AC_OUTPUT( \ + conf/hss.conf \ + access_restriction/Makefile \ + auc/Makefile \ + db/Makefile \ + s6a/Makefile \ + utils/Makefile \ + tests/Makefile \ + Makefile \ +) + +echo " +($PACKAGE_NAME) version $PACKAGE_VERSION +Prefix.........: $prefix +C Compiler.....: $CC $CFLAGS +Linker.........: $LD $LDFLAGS $LIBS +Tests..........: ${HAVE_CHECK} +" \ No newline at end of file diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/db/Makefile.am b/openair-cn/OPENAIRHSS/OPENAIRHSS/db/Makefile.am new file mode 100644 index 0000000000..02744e8a19 --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/db/Makefile.am @@ -0,0 +1,12 @@ +AM_CFLAGS = @ADD_CFLAGS@ \ + -I$(top_srcdir) \ + -I$(top_srcdir)/s6a \ + -I$(top_srcdir)/utils + +noinst_LTLIBRARIES = libdb.la + +libdb_la_LDFLAGS = -all-static +libdb_la_SOURCES = \ + db_proto.h db_connector.c \ + db_subscription_data.c \ + db_epc_equipment.c \ No newline at end of file diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/db/db_connector.c b/openair-cn/OPENAIRHSS/OPENAIRHSS/db/db_connector.c new file mode 100644 index 0000000000..25ff508437 --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/db/db_connector.c @@ -0,0 +1,585 @@ +/******************************************************************************* + + Eurecom OpenAirInterface + Copyright(c) 1999 - 2013 Eurecom + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information + Openair Admin: openair_admin@eurecom.fr + Openair Tech : openair_tech@eurecom.fr + Forums : http://forums.eurecom.fr/openairinterface + Address : EURECOM, Campus SophiaTech, 450 Route des Chappes + 06410 Biot FRANCE + +*******************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "hss_config.h" +#include "db_proto.h" + +database_t *db_desc; + +static void print_buffer(const char *prefix, uint8_t *buffer, int length) +{ + int i; + + fprintf(stdout, "%s", prefix); + for (i = 0; i < length; i++) { + fprintf(stdout, "%02x.", buffer[i]); + } + fprintf(stdout, "\n"); +} + +int hss_mysql_connect(const hss_config_t *hss_config_p) +{ + const int mysql_reconnect_val = 1; + + if ((hss_config_p->mysql_server == NULL) || + (hss_config_p->mysql_user == NULL) || + (hss_config_p->mysql_password == NULL) || + (hss_config_p->mysql_database == NULL)) + { + DB_ERROR("An empty name is not allowed\n"); + return EINVAL; + } + + DB_DEBUG("Initializing db layer\n"); + + db_desc = malloc(sizeof(database_t)); + if (db_desc == NULL) { + DB_DEBUG("An error occured on MALLOC\n"); + return errno; + } + + pthread_mutex_init(&db_desc->db_cs_mutex, NULL); + + /* Copy database configuration from static hss config */ + db_desc->server = strdup(hss_config_p->mysql_server); + db_desc->user = strdup(hss_config_p->mysql_user); + db_desc->password = strdup(hss_config_p->mysql_password); + db_desc->database = strdup(hss_config_p->mysql_database); + + /* Init mySQL client */ + db_desc->db_conn = mysql_init(NULL); + + mysql_options(db_desc->db_conn, MYSQL_OPT_RECONNECT, &mysql_reconnect_val); + + /* Try to connect to database */ + if (!mysql_real_connect(db_desc->db_conn, db_desc->server, db_desc->user, + db_desc->password, db_desc->database, 0, NULL, 0)) { + DB_ERROR("An error occured while connecting to db: %s\n", + mysql_error(db_desc->db_conn)); + return -1; + } + + /* Set the multi statement ON */ + mysql_set_server_option(db_desc->db_conn, MYSQL_OPTION_MULTI_STATEMENTS_ON); + + DB_DEBUG("Initializing db layer: DONE\n"); + + return 0; +} + +void hss_mysql_disconnect(void) +{ + mysql_close(db_desc->db_conn); +} + +int hss_mysql_update_loc(const char *imsi, mysql_ul_ans_t *mysql_ul_ans) +{ + MYSQL_RES *res; + MYSQL_ROW row; + char query[255]; + int ret = 0; + + if ((db_desc->db_conn == NULL) || (mysql_ul_ans == NULL)) { + return EINVAL; + } + + if (strlen(imsi) > 15) { + return EINVAL; + } + + sprintf(query, "SELECT `access_restriction`,`mmeidentity_idmmeidentity`," + "`msisdn`,`ue_ambr_ul`,`ue_ambr_dl`,`rau_tau_timer` " + "FROM `users` WHERE `users`.`imsi`='%s' ", imsi); + + memcpy(mysql_ul_ans->imsi, imsi, strlen(imsi) + 1); + + DB_DEBUG("Query: %s\n", query); + + pthread_mutex_lock(&db_desc->db_cs_mutex); + + if (mysql_query(db_desc->db_conn, query)) + { + pthread_mutex_unlock(&db_desc->db_cs_mutex); + DB_ERROR("Query execution failed: %s\n", + mysql_error(db_desc->db_conn)); + mysql_thread_end(); + return EINVAL; + } + + res = mysql_store_result(db_desc->db_conn); + + pthread_mutex_unlock(&db_desc->db_cs_mutex); + + if ((row = mysql_fetch_row(res)) != NULL) { + int mme_id; + /* MSISDN may be NULL */ + mysql_ul_ans->access_restriction = atoi(row[0]); + if ((mme_id = atoi(row[1])) > 0) { + ret = hss_mysql_query_mmeidentity(mme_id, &mysql_ul_ans->mme_identity); + } else { + mysql_ul_ans->mme_identity.mme_host[0] = '\0'; + mysql_ul_ans->mme_identity.mme_realm[0] = '\0'; + } + if (row[2] != 0) { + memcpy(mysql_ul_ans->msisdn, row[2], strlen(row[2])); + } + mysql_ul_ans->aggr_ul = atoi(row[3]); + mysql_ul_ans->aggr_dl = atoi(row[4]); + mysql_ul_ans->rau_tau = atoi(row[5]); + + mysql_free_result(res); + mysql_thread_end(); + return ret; + } + + mysql_free_result(res); + mysql_thread_end(); + return EINVAL; +} + +int hss_mysql_purge_ue(mysql_pu_req_t *mysql_pu_req, + mysql_pu_ans_t *mysql_pu_ans) +{ + MYSQL_RES *res; + MYSQL_ROW row; + char query[255]; + int ret = 0; + + if ((db_desc->db_conn == NULL) || + (mysql_pu_req == NULL) || + (mysql_pu_ans == NULL)) + { + return EINVAL; + } + + if (strlen(mysql_pu_req->imsi) > 15) { + return EINVAL; + } + + sprintf(query, "UPDATE `users` SET `users`.`ms_ps_status`=\"PURGED\" " + "WHERE `users`.`imsi`='%s'; " + "SELECT `users`.`mmeidentity_idmmeidentity` FROM `users` " + "WHERE `users`.`imsi`=%s ", + mysql_pu_req->imsi, mysql_pu_req->imsi); + + DB_DEBUG("Query: %s\n", query); + + pthread_mutex_lock(&db_desc->db_cs_mutex); + + if (mysql_query(db_desc->db_conn, query)) + { + pthread_mutex_unlock(&db_desc->db_cs_mutex); + DB_ERROR("Query execution failed: %s\n", + mysql_error(db_desc->db_conn)); + mysql_thread_end(); + return EINVAL; + } + + res = mysql_store_result(db_desc->db_conn); + + pthread_mutex_unlock(&db_desc->db_cs_mutex); + + if ((row = mysql_fetch_row(res)) != NULL) { + int mme_id; + if ((mme_id = atoi(row[0])) > 0) { + ret = hss_mysql_query_mmeidentity(mme_id, mysql_pu_ans); + } else { + mysql_pu_ans->mme_host[0] = '\0'; + mysql_pu_ans->mme_realm[0] = '\0'; + } + + mysql_free_result(res); + mysql_thread_end(); + return ret; + } + + mysql_free_result(res); + mysql_thread_end(); + return EINVAL; +} + +int hss_mysql_get_user(const char *imsi) +{ + MYSQL_RES *res; + MYSQL_ROW row; + char query[255]; + + if (db_desc->db_conn == NULL) { + return EINVAL; + } + + sprintf(query, "SELECT `imsi` FROM `users` WHERE `users`.`imsi`=%s ", imsi); + + DB_DEBUG("Query: %s\n", query); + + pthread_mutex_lock(&db_desc->db_cs_mutex); + + if (mysql_query(db_desc->db_conn, query)) + { + pthread_mutex_unlock(&db_desc->db_cs_mutex); + DB_ERROR("Query execution failed: %s\n", + mysql_error(db_desc->db_conn)); + mysql_thread_end(); + return EINVAL; + } + + res = mysql_store_result(db_desc->db_conn); + + pthread_mutex_unlock(&db_desc->db_cs_mutex); + + if ((row = mysql_fetch_row(res)) != NULL) { + mysql_free_result(res); + mysql_thread_end(); + return 0; + } + + mysql_free_result(res); + mysql_thread_end(); + + return EINVAL; +} + +int mysql_push_up_loc(mysql_ul_push_t *ul_push_p) +{ + MYSQL_RES *res; + char query[500]; + int query_length = 0; + int status; + + if ((db_desc->db_conn == NULL) || (ul_push_p == NULL)) { + return EINVAL; + } + + // TODO: multi-statement check results + + if (ul_push_p->mme_identity_present == MME_IDENTITY_PRESENT) { + query_length += sprintf(&query[query_length], "INSERT INTO `mmeidentity`" + " (`mmehost`,`mmerealm`) SELECT '%s','%s' FROM `mmeidentity` WHERE NOT" + " EXISTS (SELECT * FROM `mmeidentity` WHERE `mmehost`='%s'" + " AND `mmerealm`='%s') LIMIT 1;", ul_push_p->mme_identity.mme_host, + ul_push_p->mme_identity.mme_realm, ul_push_p->mme_identity.mme_host, + ul_push_p->mme_identity.mme_realm); + } + query_length += sprintf(&query[query_length], "UPDATE `users`%s SET ", + ul_push_p->mme_identity_present == MME_IDENTITY_PRESENT ? + ",`mmeidentity`" : ""); + + if (ul_push_p->imei_present == IMEI_PRESENT) { + query_length += sprintf(&query[query_length], "`imei`='%s',", + ul_push_p->imei); + } + if (ul_push_p->sv_present == SV_PRESENT) { + query_length += sprintf(&query[query_length], "`imeisv`='%*s',", 2, + ul_push_p->software_version); + } + if (ul_push_p->mme_identity_present == MME_IDENTITY_PRESENT) { + query_length += sprintf(&query[query_length], + "`users`.`mmeidentity_idmmeidentity`=`mmeidentity`.`idmmeidentity`, " + "`users`.`ms_ps_status`=\"NOT_PURGED\""); + } + + query_length += sprintf(&query[query_length], " WHERE `users`.`imsi`='%s'", + ul_push_p->imsi); + if (ul_push_p->mme_identity_present == MME_IDENTITY_PRESENT) { + query_length += sprintf(&query[query_length], + " AND `mmeidentity`.`mmehost`='%s'" + " AND `mmeidentity`.`mmerealm`='%s'", + ul_push_p->mme_identity.mme_host, ul_push_p->mme_identity.mme_realm); + } + + DB_DEBUG("Query: %s\n", query); + + pthread_mutex_lock(&db_desc->db_cs_mutex); + + if (mysql_query(db_desc->db_conn, query)) + { + pthread_mutex_unlock(&db_desc->db_cs_mutex); + fprintf(stderr, "Query execution failed: %s\n", + mysql_error(db_desc->db_conn)); + mysql_thread_end(); + return EINVAL; + } + + /* process each statement result */ + do { + /* did current statement return data? */ + res = mysql_store_result(db_desc->db_conn); + if (res) + { + /* yes; process rows and free the result set */ + mysql_free_result(res); + } + else /* no result set or error */ + { + if (mysql_field_count(db_desc->db_conn) == 0) + { + DB_ERROR("%lld rows affected\n", + mysql_affected_rows(db_desc->db_conn)); + } + else /* some error occurred */ + { + DB_ERROR("Could not retrieve result set\n"); + break; + } + } + /* more results? -1 = no, >0 = error, 0 = yes (keep looping) */ + if ((status = mysql_next_result(db_desc->db_conn)) > 0) + DB_ERROR("Could not execute statement\n"); + } while (status == 0); + + pthread_mutex_unlock(&db_desc->db_cs_mutex); + + mysql_free_result(res); + mysql_thread_end(); + + return 0; +} + +int hss_mysql_push_rand_sqn(const char *imsi, uint8_t *rand_p, uint8_t *sqn) +{ + int status = 0, i; + MYSQL_RES *res; + MYSQL_ROW row; + char query[255]; + int query_length = 0; + uint64_t sqn_decimal = 0; + + if (db_desc->db_conn == NULL) { + return EINVAL; + } + + if (rand_p == NULL || sqn == NULL) { + return EINVAL; + } + + sqn_decimal = ((uint64_t)sqn[0] << 40) | ((uint64_t)sqn[1] << 32) | + (sqn[2] << 24) | (sqn[3] << 16) | (sqn[4] << 8) | sqn[5]; + + query_length = sprintf(query, "UPDATE `users` SET `rand`=UNHEX('"); + for (i = 0; i < RAND_LENGTH; i ++) { + query_length += sprintf(&query[query_length], "%02x", rand_p[i]); + } + query_length += sprintf(&query[query_length], "'),`sqn`=%"PRIu64, sqn_decimal); + query_length += sprintf(&query[query_length], " WHERE `users`.`imsi`='%s'", imsi); + + DB_DEBUG("Query: %s\n", query); + + pthread_mutex_lock(&db_desc->db_cs_mutex); + + if (mysql_query(db_desc->db_conn, query)) + { + pthread_mutex_unlock(&db_desc->db_cs_mutex); + DB_ERROR("Query execution failed: %s\n", + mysql_error(db_desc->db_conn)); + mysql_thread_end(); + return EINVAL; + } + + /* process each statement result */ + do { + /* did current statement return data? */ + res = mysql_store_result(db_desc->db_conn); + if (res) + { + /* yes; process rows and free the result set */ + mysql_free_result(res); + } + else /* no result set or error */ + { + if (mysql_field_count(db_desc->db_conn) == 0) + { + DB_ERROR("%lld rows affected\n", + mysql_affected_rows(db_desc->db_conn)); + } + else /* some error occurred */ + { + DB_ERROR("Could not retrieve result set\n"); + break; + } + } + /* more results? -1 = no, >0 = error, 0 = yes (keep looping) */ + if ((status = mysql_next_result(db_desc->db_conn)) > 0) + DB_ERROR("Could not execute statement\n"); + } while (status == 0); + + pthread_mutex_unlock(&db_desc->db_cs_mutex); + + mysql_free_result(res); + mysql_thread_end(); + + return 0; +} + +int hss_mysql_increment_sqn(const char *imsi) +{ + int status; + MYSQL_RES *res; + char query[255]; + + if (db_desc->db_conn == NULL) { + return EINVAL; + } + if (imsi == NULL) { + return EINVAL; + } + + /* + 32 = 2 ^ sizeof(IND) (see 3GPP TS. 33.102) */ + sprintf(query, "UPDATE `users` SET `sqn` = `sqn` + 32 WHERE `users`.`imsi`=%s", imsi); + + DB_DEBUG("Query: %s\n", query); + + if (mysql_query(db_desc->db_conn, query)) + { + pthread_mutex_unlock(&db_desc->db_cs_mutex); + DB_ERROR("Query execution failed: %s\n", + mysql_error(db_desc->db_conn)); + mysql_thread_end(); + return EINVAL; + } + + /* process each statement result */ + do { + /* did current statement return data? */ + res = mysql_store_result(db_desc->db_conn); + if (res) + { + /* yes; process rows and free the result set */ + mysql_free_result(res); + } + else /* no result set or error */ + { + if (mysql_field_count(db_desc->db_conn) == 0) + { + DB_ERROR("%lld rows affected\n", + mysql_affected_rows(db_desc->db_conn)); + } + else /* some error occurred */ + { + DB_ERROR("Could not retrieve result set\n"); + break; + } + } + /* more results? -1 = no, >0 = error, 0 = yes (keep looping) */ + if ((status = mysql_next_result(db_desc->db_conn)) > 0) + DB_ERROR("Could not execute statement\n"); + } while (status == 0); + + pthread_mutex_unlock(&db_desc->db_cs_mutex); + + mysql_free_result(res); + mysql_thread_end(); + + return 0; +} + +int hss_mysql_auth_info(mysql_auth_info_req_t *auth_info_req, + mysql_auth_info_resp_t *auth_info_resp) +{ + int ret = 0; + MYSQL_RES *res; + MYSQL_ROW row; + char query[255]; + + if (db_desc->db_conn == NULL) { + return EINVAL; + } + if ((auth_info_req == NULL) || (auth_info_resp == NULL)) { + return EINVAL; + } + + sprintf(query, "SELECT `key`,`sqn`,`rand` FROM `users` WHERE `users`.`imsi`=%s ", + auth_info_req->imsi); + + DB_DEBUG("Query: %s\n", query); + + pthread_mutex_lock(&db_desc->db_cs_mutex); + + if (mysql_query(db_desc->db_conn, query)) + { + pthread_mutex_unlock(&db_desc->db_cs_mutex); + DB_ERROR("Query execution failed: %s\n", + mysql_error(db_desc->db_conn)); + mysql_thread_end(); + return EINVAL; + } + + res = mysql_store_result(db_desc->db_conn); + + pthread_mutex_unlock(&db_desc->db_cs_mutex); + + if ((row = mysql_fetch_row(res)) != NULL) { + if (row[0] == NULL || row[1] == NULL || row[2] == NULL) { + ret = EINVAL; + } + if (row[0] != NULL) { + print_buffer("Key: ", (uint8_t*)row[0], KEY_LENGTH); + memcpy(auth_info_resp->key, row[0], KEY_LENGTH); + } + if (row[1] != NULL) { + uint64_t sqn = 0; + + sqn = atoll(row[1]); + printf("Received SQN %s converted to %"PRIu64"\n", row[1], sqn); + + auth_info_resp->sqn[0] = (sqn & (255UL << 40)) >> 40; + auth_info_resp->sqn[1] = (sqn & (255UL << 32)) >> 32; + auth_info_resp->sqn[2] = (sqn & (255UL << 24)) >> 24; + auth_info_resp->sqn[3] = (sqn & (255UL << 16)) >> 16; + auth_info_resp->sqn[4] = (sqn & (255UL << 8)) >> 8; + auth_info_resp->sqn[5] = (sqn & 0xFF); + + print_buffer("SQN: ", auth_info_resp->sqn, SQN_LENGTH); + } + if (row[2] != NULL) { + print_buffer("RAND: ", (uint8_t*)row[2], RAND_LENGTH); + memcpy(auth_info_resp->rand, row[2], RAND_LENGTH); + } + mysql_free_result(res); + mysql_thread_end(); + return ret; + } + + mysql_free_result(res); + mysql_thread_end(); + + return EINVAL; +} diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/db/db_epc_equipment.c b/openair-cn/OPENAIRHSS/OPENAIRHSS/db/db_epc_equipment.c new file mode 100644 index 0000000000..cd32106408 --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/db/db_epc_equipment.c @@ -0,0 +1,137 @@ +/******************************************************************************* + + Eurecom OpenAirInterface + Copyright(c) 1999 - 2013 Eurecom + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information + Openair Admin: openair_admin@eurecom.fr + Openair Tech : openair_tech@eurecom.fr + Forums : http://forums.eurecom.fr/openairinterface + Address : EURECOM, Campus SophiaTech, 450 Route des Chappes + 06410 Biot FRANCE + +*******************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "hss_config.h" +#include "db_proto.h" + +int hss_mysql_query_mmeidentity(const int id_mme_identity, + mysql_mme_identity_t *mme_identity_p) +{ + MYSQL_RES *res; + MYSQL_ROW row; + char query[255]; + + if ((db_desc->db_conn == NULL) || (mme_identity_p == NULL)) { + return EINVAL; + } + + memset(mme_identity_p, 0, sizeof(mysql_mme_identity_t)); + + sprintf(query, "SELECT mmehost,mmerealm FROM mmeidentity WHERE " + "mmeidentity.idmmeidentity='%d' ", id_mme_identity); + + DB_DEBUG("Query: %s\n", query); + + pthread_mutex_lock(&db_desc->db_cs_mutex); + + if (mysql_query(db_desc->db_conn, query)) + { + pthread_mutex_unlock(&db_desc->db_cs_mutex); + DB_ERROR("Query execution failed: %s\n", + mysql_error(db_desc->db_conn)); + mysql_thread_end(); + return EINVAL; + } + + res = mysql_store_result(db_desc->db_conn); + + pthread_mutex_unlock(&db_desc->db_cs_mutex); + if ((row = mysql_fetch_row(res)) != NULL) { + if (row[0] != NULL) { + memcpy(mme_identity_p->mme_host, row[0], strlen(row[0])); + } else { + mme_identity_p->mme_host[0] = '\0'; + } + + if (row[1] != NULL) { + memcpy(mme_identity_p->mme_realm, row[1], strlen(row[1])); + } else { + mme_identity_p->mme_realm[0] = '\0'; + } + + mysql_free_result(res); + mysql_thread_end(); + return 0; + } + + mysql_free_result(res); + mysql_thread_end(); + return EINVAL; +} + +int hss_mysql_check_epc_equipment(mysql_mme_identity_t *mme_identity_p) +{ + MYSQL_RES *res; + MYSQL_ROW row; + char query[255]; + + if ((db_desc->db_conn == NULL) || (mme_identity_p == NULL)) { + return EINVAL; + } + + sprintf(query, "SELECT idmmeidentity FROM mmeidentity WHERE mmeidentity.mmehost='%s' ", + mme_identity_p->mme_host); + + DB_DEBUG("Query: %s\n", query); + + pthread_mutex_lock(&db_desc->db_cs_mutex); + + if (mysql_query(db_desc->db_conn, query)) + { + pthread_mutex_unlock(&db_desc->db_cs_mutex); + DB_ERROR("Query execution failed: %s\n", + mysql_error(db_desc->db_conn)); + mysql_thread_end(); + return EINVAL; + } + + res = mysql_store_result(db_desc->db_conn); + + pthread_mutex_unlock(&db_desc->db_cs_mutex); + if ((row = mysql_fetch_row(res)) != NULL) { + mysql_free_result(res); + mysql_thread_end(); + return 0; + } + + mysql_free_result(res); + mysql_thread_end(); + return EINVAL; +} diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/db/db_proto.h b/openair-cn/OPENAIRHSS/OPENAIRHSS/db/db_proto.h new file mode 100644 index 0000000000..7842b4ec96 --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/db/db_proto.h @@ -0,0 +1,196 @@ +/******************************************************************************* + + Eurecom OpenAirInterface + Copyright(c) 1999 - 2013 Eurecom + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information + Openair Admin: openair_admin@eurecom.fr + Openair Tech : openair_tech@eurecom.fr + Forums : http://forums.eurecom.fr/openairinterface + Address : EURECOM, Campus SophiaTech, 450 Route des Chappes + 06410 Biot FRANCE + +*******************************************************************************/ + +#include +#include +#include + +#include /* To provide internet addresses strings helpers */ + +#ifndef DB_PROTO_H_ +#define DB_PROTO_H_ + +#define DB_DEBUG(fmt, args...) fprintf(stdout, "%s:%d: "fmt, __FILE__, __LINE__, ##args) +#define DB_ERROR(fmt, args...) fprintf(stderr, "%s:%d: "fmt, __FILE__, __LINE__, ##args) + +typedef struct { + /* The mysql reference connector object */ + MYSQL *db_conn; + char *server; + char *user; + char *password; + char *database; + + pthread_mutex_t db_cs_mutex; +} database_t; + +extern database_t *db_desc; + +typedef uint32_t pre_emp_vul_t; +typedef uint32_t pre_emp_cap_t; +typedef uint8_t access_restriction_t; +typedef uint32_t ambr_t; +typedef uint8_t qci_t; +typedef uint8_t prio_level_t; +typedef uint32_t rau_tau_t; + +#define IMSI_LENGTH_MAX (15) + +typedef struct { + char imsi[IMSI_LENGTH_MAX + 1]; +} mysql_auth_info_req_t; + +/* Expressed in bytes */ +#define KEY_LENGTH (16) +#define SQN_LENGTH (6) +#define RAND_LENGTH (16) + +typedef struct { + uint8_t key[KEY_LENGTH]; + uint8_t sqn[SQN_LENGTH]; + /* RAND should not be here... */ + uint8_t rand[RAND_LENGTH]; +} mysql_auth_info_resp_t; + +typedef struct { + char imsi[IMSI_LENGTH_MAX + 1]; + /* New computed SQN that will be used on next auth info req */ + uint8_t sqn[SQN_LENGTH]; +} mysql_sqn_push_t; + +typedef struct { + /* An MME may have already been registered as serving the UE. */ + char mme_host[255]; + char mme_realm[200]; +} mysql_mme_identity_t; + +typedef struct { + char imsi[16]; + /* MSISDN this parameter may be NULL */ + char msisdn[16]; + + /* Maximum aggregated bitrates for the user */ + ambr_t aggr_ul; + ambr_t aggr_dl; + + /* Subscribed RAU-TAU timer */ + rau_tau_t rau_tau; + + access_restriction_t access_restriction; + mysql_mme_identity_t mme_identity; +} mysql_ul_ans_t; + +typedef struct { + /* Bit masks indicating presence of optional fields */ +#define MME_IDENTITY_PRESENT (0x1) +#define MME_SUPPORTED_FEATURES_PRESENT (0x1) +#define IMEI_PRESENT (0x1) +#define SV_PRESENT (0x1) +#define UE_SRVCC_PRESENT (0x1) + + unsigned mme_identity_present:1; + unsigned mme_supported_features_present:1; + unsigned imei_present:1; + unsigned sv_present:1; + unsigned ue_srvcc_present:1; + + /* IMSI */ + char imsi[16]; + /* Origin host and realm */ + mysql_mme_identity_t mme_identity; + /* IMEISV */ + char imei[16]; + char software_version[2]; + + uint32_t ue_srvcc; + uint32_t mme_supported_features; +} mysql_ul_push_t; + +typedef enum { + IPV4 = 0, + IPV6 = 1, + IPV4V6 = 2, + IPV4_OR_IPV6 = 3, +} pdn_type_t; + +typedef struct { + char ipv4_address[INET_ADDRSTRLEN]; + char ipv6_address[INET6_ADDRSTRLEN]; +} pdn_address_t; + +typedef struct { + char apn[61]; + pdn_type_t pdn_type; + pdn_address_t pdn_address; + ambr_t aggr_ul; + ambr_t aggr_dl; + qci_t qci; + prio_level_t priority_level; + pre_emp_cap_t pre_emp_cap; + pre_emp_vul_t pre_emp_vul; +} mysql_pdn_t; + +typedef struct { + /* IMSI */ + char imsi[16]; +} mysql_pu_req_t; + +typedef mysql_mme_identity_t mysql_pu_ans_t; + +int hss_mysql_connect(const hss_config_t *hss_config_p); + +void hss_mysql_disconnect(void); + +int hss_mysql_get_user(const char *imsi); + +int hss_mysql_update_loc(const char *imsi, mysql_ul_ans_t *mysql_ul_ans); + +int hss_mysql_query_mmeidentity(const int id_mme_identity, + mysql_mme_identity_t *mme_identity_p); + +int hss_mysql_check_epc_equipment(mysql_mme_identity_t *mme_identity_p); + +int mysql_push_up_loc(mysql_ul_push_t *ul_push_p); + +int hss_mysql_purge_ue(mysql_pu_req_t *mysql_pu_req, + mysql_pu_ans_t *mysql_pu_ans); + +int hss_mysql_query_pdns(const char *imsi, + mysql_pdn_t **pdns_p, + uint8_t *nb_pdns); + +int hss_mysql_auth_info(mysql_auth_info_req_t *auth_info_req, + mysql_auth_info_resp_t *auth_info_resp); + +int hss_mysql_push_rand_sqn(const char *imsi, uint8_t *rand_p, uint8_t *sqn); + +int hss_mysql_increment_sqn(const char *imsi); + +#endif /* DB_PROTO_H_ */ diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/db/db_subscription_data.c b/openair-cn/OPENAIRHSS/OPENAIRHSS/db/db_subscription_data.c new file mode 100644 index 0000000000..6c91152ee7 --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/db/db_subscription_data.c @@ -0,0 +1,135 @@ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "hss_config.h" +#include "db_proto.h" + +int hss_mysql_query_pdns(const char *imsi, + mysql_pdn_t **pdns_p, + uint8_t *nb_pdns) +{ + int ret; + MYSQL_RES *res; + MYSQL_ROW row; + char query[255]; + mysql_pdn_t *pdn_array = NULL; + + if (db_desc->db_conn == NULL) { + return EINVAL; + } + if (nb_pdns == NULL || pdns_p == NULL) { + return EINVAL; + } + + sprintf(query, "SELECT * FROM `pdn` WHERE " + "`pdn`.`users_imsi`=%s LIMIT 10; ", imsi); + + printf("Query: %s\n", query); + + pthread_mutex_lock(&db_desc->db_cs_mutex); + + if (mysql_query(db_desc->db_conn, query)) + { + pthread_mutex_unlock(&db_desc->db_cs_mutex); + fprintf(stderr, "Query execution failed: %s\n", + mysql_error(db_desc->db_conn)); + mysql_thread_end(); + return EINVAL; + } + + res = mysql_store_result(db_desc->db_conn); + + pthread_mutex_unlock(&db_desc->db_cs_mutex); + + *nb_pdns = 0; + + while ((row = mysql_fetch_row(res)) != NULL) + { + mysql_pdn_t *pdn_elm; /* Local PDN element in array */ + unsigned long *lengths; + + lengths = mysql_fetch_lengths(res); + + *nb_pdns += 1; + if (*nb_pdns == 1) { + /* First row, must malloc */ + pdn_array = malloc(sizeof(mysql_pdn_t)); + } else { + pdn_array = realloc(pdn_array, *nb_pdns * sizeof(mysql_pdn_t)); + } + if (pdn_array == NULL) { + /* Error on malloc */ + ret = ENOMEM; + goto err; + } + pdn_elm = &pdn_array[*nb_pdns - 1]; + /* Copying the APN */ + memset(pdn_elm, 0, sizeof(mysql_pdn_t)); + memcpy(pdn_elm->apn, row[1], lengths[1]); + /* PDN Type + PDN address */ + if (strcmp(row[2], "IPv6") == 0) { + pdn_elm->pdn_type = IPV6; + memcpy(pdn_elm->pdn_address.ipv6_address, row[4], lengths[4]); + pdn_elm->pdn_address.ipv6_address[lengths[4]] = '\0'; + } else if (strcmp(row[2], "IPv4v6") == 0) { + pdn_elm->pdn_type = IPV4V6; + memcpy(pdn_elm->pdn_address.ipv4_address, row[3], lengths[3]); + pdn_elm->pdn_address.ipv4_address[lengths[3]] = '\0'; + memcpy(pdn_elm->pdn_address.ipv6_address, row[4], lengths[4]); + pdn_elm->pdn_address.ipv6_address[lengths[4]] = '\0'; + } else if (strcmp(row[2], "IPv4_or_IPv6") == 0) { + pdn_elm->pdn_type = IPV4_OR_IPV6; + memcpy(pdn_elm->pdn_address.ipv4_address, row[3], lengths[3]); + pdn_elm->pdn_address.ipv4_address[lengths[3]] = '\0'; + memcpy(pdn_elm->pdn_address.ipv6_address, row[4], lengths[4]); + pdn_elm->pdn_address.ipv6_address[lengths[4]] = '\0'; + } else { + pdn_elm->pdn_type = IPV4; + memcpy(pdn_elm->pdn_address.ipv4_address, row[3], lengths[3]); + pdn_elm->pdn_address.ipv4_address[lengths[3]] = '\0'; + } + pdn_elm->aggr_ul = atoi(row[5]); + pdn_elm->aggr_dl = atoi(row[6]); + pdn_elm->qci = atoi(row[9]); + pdn_elm->priority_level = atoi(row[10]); + if (strcmp(row[11], "ENABLED") == 0) { + pdn_elm->pre_emp_cap = 0; + } else { + pdn_elm->pre_emp_cap = 1; + } + if (strcmp(row[12], "DISABLED") == 0) { + pdn_elm->pre_emp_vul = 1; + } else { + pdn_elm->pre_emp_vul = 0; + } + } + + mysql_free_result(res); + mysql_thread_end(); + + /* We did not find any APN for the requested IMSI */ + if (*nb_pdns == 0) { + return EINVAL; + } else { + *pdns_p = pdn_array; + return 0; + } + +err: + if (pdn_array) { + free(pdn_array); + } + pdn_array = NULL; + *pdns_p = pdn_array; + *nb_pdns = 0; + mysql_free_result(res); + mysql_thread_end(); + return ret; +} diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/db/oai_db.sql b/openair-cn/OPENAIRHSS/OPENAIRHSS/db/oai_db.sql new file mode 100644 index 0000000000..aa5383fa1e --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/db/oai_db.sql @@ -0,0 +1,175 @@ +-- phpMyAdmin SQL Dump +-- version 3.4.10.1deb1 +-- http://www.phpmyadmin.net +-- +-- Client: localhost +-- Généré le : Mer 18 Décembre 2013 à 18:01 +-- Version du serveur: 5.5.34 +-- Version de PHP: 5.3.10-1ubuntu3.9 + +SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO"; +SET time_zone = "+00:00"; + + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; + +-- +-- Base de données: `oai_db` +-- + +-- -------------------------------------------------------- + +-- +-- Structure de la table `apn` +-- + +CREATE TABLE IF NOT EXISTS `apn` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `apn-name` varchar(60) NOT NULL, + `pdn-type` enum('IPv4','IPv6','IPv4v6','IPv4_or_IPv6') NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `apn-name` (`apn-name`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ; + +-- -------------------------------------------------------- + +-- +-- Structure de la table `mmeidentity` +-- + +CREATE TABLE IF NOT EXISTS `mmeidentity` ( + `idmmeidentity` int(11) NOT NULL AUTO_INCREMENT, + `mmehost` varchar(255) DEFAULT NULL, + `mmerealm` varchar(200) DEFAULT NULL, + `UE-Reachability` tinyint(1) NOT NULL COMMENT 'Indicates whether the MME supports UE Reachability Notifcation', + PRIMARY KEY (`idmmeidentity`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=39 ; + +-- +-- Contenu de la table `mmeidentity` +-- + +INSERT INTO `mmeidentity` (`idmmeidentity`, `mmehost`, `mmerealm`, `UE-Reachability`) VALUES +(29, 'yang.eur', 'eur', 0), +(28, 'nord.eur', 'eur', 0), +(30, 'hades.eur', 'eur', 0), +(35, 'olympie-Latitude-E6520.eur', 'eur', 0), +(33, 'orcus.eur', 'eur', 0), +(36, 'caviar.eur', 'eur', 0), +(37, 'sud.eur', 'eur', 0), +(38, 'tapenade.eur', 'eur', 1); + +-- -------------------------------------------------------- + +-- +-- Structure de la table `pdn` +-- + +CREATE TABLE IF NOT EXISTS `pdn` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `apn` varchar(60) NOT NULL, + `pdn_type` enum('IPv4','IPv6','IPv4v6','IPv4_or_IPv6') NOT NULL DEFAULT 'IPv4', + `pdn_ipv4` varchar(15) DEFAULT '0.0.0.0', + `pdn_ipv6` varchar(45) CHARACTER SET latin1 COLLATE latin1_general_ci DEFAULT '0:0:0:0:0:0:0:0', + `aggregate_ambr_ul` int(10) unsigned DEFAULT '50000000', + `aggregate_ambr_dl` int(10) unsigned DEFAULT '100000000', + `pgw_id` int(11) NOT NULL, + `users_imsi` varchar(15) NOT NULL, + `qci` tinyint(3) unsigned NOT NULL DEFAULT '9', + `priority_level` tinyint(3) unsigned NOT NULL DEFAULT '15', + `pre_emp_cap` enum('ENABLED','DISABLED') DEFAULT 'DISABLED', + `pre_emp_vul` enum('ENABLED','DISABLED') DEFAULT 'DISABLED', + `LIPA-Permissions` enum('LIPA-prohibited','LIPA-only','LIPA-conditional') NOT NULL DEFAULT 'LIPA-only', + PRIMARY KEY (`id`,`pgw_id`,`users_imsi`), + KEY `fk_pdn_pgw1_idx` (`pgw_id`), + KEY `fk_pdn_users1_idx` (`users_imsi`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=8 ; + +-- +-- Contenu de la table `pdn` +-- + +INSERT INTO `pdn` (`id`, `apn`, `pdn_type`, `pdn_ipv4`, `pdn_ipv6`, `aggregate_ambr_ul`, `aggregate_ambr_dl`, `pgw_id`, `users_imsi`, `qci`, `priority_level`, `pre_emp_cap`, `pre_emp_vul`, `LIPA-Permissions`) VALUES +(2, 'wap.test.fr', 'IPv4v6', '10.0.0.5', '0:0:0:0:0:0:0:1', 1048576, 14400000, 2, '20834123456789', 8, 2, 'ENABLED', 'DISABLED', 'LIPA-only'), +(3, 'toto.eurecom.fr', 'IPv4v6', '0.0.0.0', '2001:0db8:85a3:0042:1000:8a2e:0370:7334', 50000, 100000, 2, '20834123456710', 1, 3, 'DISABLED', 'DISABLED', 'LIPA-only'), +(4, 'edge.eurecom.fr', 'IPv4_or_IPv6', '0.0.0.0', '2001:0db8:85a3:0042:1000:8a2e:0370:7356', 50000000, 100000000, 1, '20834123456789', 3, 4, 'ENABLED', 'DISABLED', 'LIPA-only'), +(7, 'internet.v6.eur', 'IPv6', '0.0.0.0', '0:0:0:0:0:0:0:0', 50000000, 100000000, 4, '20834123456789', 9, 15, 'DISABLED', 'ENABLED', 'LIPA-only'), +(1, 'internet.v4.eur', 'IPv4', '0.0.0.0', '0:0:0:0:0:0:0:0', 50000000, 100000000, 3, '20834123456789', 9, 15, 'DISABLED', 'ENABLED', 'LIPA-only'); + +-- -------------------------------------------------------- + +-- +-- Structure de la table `pgw` +-- + +CREATE TABLE IF NOT EXISTS `pgw` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `ipv4` varchar(15) NOT NULL, + `ipv6` varchar(39) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `ipv4` (`ipv4`), + UNIQUE KEY `ipv6` (`ipv6`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=4 ; + +-- +-- Contenu de la table `pgw` +-- + +INSERT INTO `pgw` (`id`, `ipv4`, `ipv6`) VALUES +(1, '127.0.0.1', '0:0:0:0:0:0:0:1'), +(2, '192.168.56.101', ''), +(3, '10.0.0.2', '0'); + +-- -------------------------------------------------------- + +-- +-- Structure de la table `terminal-info` +-- + +CREATE TABLE IF NOT EXISTS `terminal-info` ( + `imei` varchar(15) NOT NULL, + `sv` varchar(2) NOT NULL, + UNIQUE KEY `imei` (`imei`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +-- -------------------------------------------------------- + +-- +-- Structure de la table `users` +-- + +CREATE TABLE IF NOT EXISTS `users` ( + `imsi` varchar(15) NOT NULL, + `msisdn` varchar(46) DEFAULT NULL, + `imei` varchar(15) DEFAULT NULL, + `imei_sv` varchar(2) DEFAULT NULL, + `ms_ps_status` enum('PURGED','NOT_PURGED') DEFAULT 'PURGED' COMMENT 'Indicates that ESM and EMM status are purged from MME', + `rau_tau_timer` int(10) unsigned DEFAULT '120', + `ue_ambr_ul` bigint(20) unsigned DEFAULT '50000000' COMMENT 'Subscribed UE AMBR in uplink', + `ue_ambr_dl` bigint(20) unsigned DEFAULT '100000000' COMMENT 'Subscribed UE AMBR in downlink', + `access_restriction` int(10) unsigned DEFAULT '60' COMMENT '3GPP TS.29272 #7.3.31', + `mme_cap` int(10) unsigned zerofill DEFAULT NULL, + `mmeidentity_idmmeidentity` int(11) NOT NULL DEFAULT '0', + `key` varbinary(16) NOT NULL DEFAULT '0' COMMENT 'UE security key', + `RFSP-Index` smallint(5) unsigned NOT NULL DEFAULT '1' COMMENT 'Index to RRM configuration. Possible values from 1 to 256', + `urrp_mme` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'UE reachability request requested by HSS', + `sqn` int(10) unsigned zerofill NOT NULL, + `rand` varbinary(16) NOT NULL, + PRIMARY KEY (`imsi`,`mmeidentity_idmmeidentity`), + KEY `fk_users_mmeidentity_idx1` (`mmeidentity_idmmeidentity`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +-- +-- Contenu de la table `users` +-- + +INSERT INTO `users` (`imsi`, `msisdn`, `imei`, `imei_sv`, `ms_ps_status`, `rau_tau_timer`, `ue_ambr_ul`, `ue_ambr_dl`, `access_restriction`, `mme_cap`, `mmeidentity_idmmeidentity`, `key`, `RFSP-Index`, `urrp_mme`, `sqn`, `rand`) VALUES +('20834123456789', '380561234567', '12345678', '23', 'NOT_PURGED', 50, 50000000, 100000000, 47, 0000000000, 36, '+ÖEŸ‚ų\0•,IH�ÿH', 0, 0, 0000000096, 'Px¼X \Z1¡Éx™ß'), +('208920000000008', NULL, NULL, NULL, 'PURGED', 120, 50000000, 100000000, 60, 0000000000, 0, '‹¯G?/�Д‡ÌË× |hb', 1, 0, 0000027276, '''©$Ìy2¨dËêmù'); + +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/debian/changelog b/openair-cn/OPENAIRHSS/OPENAIRHSS/debian/changelog new file mode 100644 index 0000000000..5ce3ffbc17 --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/debian/changelog @@ -0,0 +1,5 @@ +openair-hss (1.0.0-1) UNRELEASED; urgency=low + + * Initial release. (Closes: #XXXXXX) + + -- Sebastien Roux Wed, 10 Apr 2013 09:54:55 +0200 diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/debian/compat b/openair-cn/OPENAIRHSS/OPENAIRHSS/debian/compat new file mode 100644 index 0000000000..c7930257df --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/debian/compat @@ -0,0 +1 @@ +7 \ No newline at end of file diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/debian/control b/openair-cn/OPENAIRHSS/OPENAIRHSS/debian/control new file mode 100644 index 0000000000..ff727b65fa --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/debian/control @@ -0,0 +1,15 @@ +Source: openair-hss +Maintainer: Sebastien ROUX +Section: misc +Priority: optional +Standards-Version: 3.9.2 +Build-Depends: debhelper (>= 8), + make, gcc, bison, flex, autotools, + libmysqlclient-dev, libsctp-dev, + freediameter-dev, freediameter-dictionary-s6a, freediameter-dictionary-mip6, + libgmp-dev, libnettle-dev + +Package: openair-hss +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends} +Description: Implementation of R10 HSS for 3GPP LTE core network \ No newline at end of file diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/debian/copyright b/openair-cn/OPENAIRHSS/OPENAIRHSS/debian/copyright new file mode 100644 index 0000000000..e69de29bb2 diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/debian/dirs b/openair-cn/OPENAIRHSS/OPENAIRHSS/debian/dirs new file mode 100644 index 0000000000..b01f7e5c00 --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/debian/dirs @@ -0,0 +1 @@ +etc/openair-hss/ \ No newline at end of file diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/debian/files b/openair-cn/OPENAIRHSS/OPENAIRHSS/debian/files new file mode 100644 index 0000000000..94dcd01645 --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/debian/files @@ -0,0 +1 @@ +openair-hss_1.0.0-1_i386.deb misc optional diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/debian/openair-hss.install b/openair-cn/OPENAIRHSS/OPENAIRHSS/debian/openair-hss.install new file mode 100644 index 0000000000..66a9d5fcb7 --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/debian/openair-hss.install @@ -0,0 +1,3 @@ +conf/hss.conf +conf/hss_fd.conf +db/oai_db.sql \ No newline at end of file diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/debian/rules b/openair-cn/OPENAIRHSS/OPENAIRHSS/debian/rules new file mode 100755 index 0000000000..b6e44cb04d --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/debian/rules @@ -0,0 +1,12 @@ +#!/usr/bin/make -f + +export DH_OPTIONS + +%: + dh $@ + +override_dh_auto_configure: + dh_auto_configure -- --enable-dh-install LDFLAGS='-L/usr/local/lib' + +# Don't run tests: +override_dh_auto_test: \ No newline at end of file diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/hss_main.c b/openair-cn/OPENAIRHSS/OPENAIRHSS/hss_main.c new file mode 100644 index 0000000000..7e17f9cd72 --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/hss_main.c @@ -0,0 +1,63 @@ +/******************************************************************************* + + Eurecom OpenAirInterface + Copyright(c) 1999 - 2013 Eurecom + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information + Openair Admin: openair_admin@eurecom.fr + Openair Tech : openair_tech@eurecom.fr + Forums : http://forums.eurecom.fr/openairinterface + Address : EURECOM, Campus SophiaTech, 450 Route des Chappes + 06410 Biot FRANCE + +*******************************************************************************/ + +#include +#include + +#include "hss_config.h" +#include "db_proto.h" +#include "s6a_proto.h" +#include "auc.h" + +int main(int argc, char *argv[]) +{ + hss_config_t hss_config; + + memset(&hss_config, 0, sizeof(hss_config_t)); + + if (config_init(argc, argv, &hss_config) != 0) { + return -1; + } + + if (hss_mysql_connect(&hss_config) != 0) { + return -1; + } + + random_init(); + + s6a_init(&hss_config); + + while(1) { + /* TODO: handle signals here */ + sleep(1); + } + + return 0; +} diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/s6a/Makefile.am b/openair-cn/OPENAIRHSS/OPENAIRHSS/s6a/Makefile.am new file mode 100644 index 0000000000..2ddf549b36 --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/s6a/Makefile.am @@ -0,0 +1,21 @@ +AM_CFLAGS = @ADD_CFLAGS@ \ + -I$(top_srcdir) \ + -I$(top_srcdir)/access_restriction \ + -I$(top_srcdir)/auc \ + -I$(top_srcdir)/db \ + -I$(top_srcdir)/utils + +noinst_LTLIBRARIES = libs6a.la + +libs6a_la_LDFLAGS = -all-static +libs6a_la_SOURCES = \ + s6a_common.c \ + s6a_fd.c s6a_proto.h \ + s6a_auth_info.c \ + s6a_up_loc.c \ + s6a_purge_ue.c \ + s6a_error.c \ + s6a_subscription_data.c \ + s6a_in_addr.c \ + s6a_peers.c \ + s6a_supported_features.h s6a_supported_features.c \ No newline at end of file diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/s6a/s6a_auth_info.c b/openair-cn/OPENAIRHSS/OPENAIRHSS/s6a/s6a_auth_info.c new file mode 100644 index 0000000000..bf5b36b160 --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/s6a/s6a_auth_info.c @@ -0,0 +1,280 @@ +/******************************************************************************* + + Eurecom OpenAirInterface + Copyright(c) 1999 - 2013 Eurecom + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information + Openair Admin: openair_admin@eurecom.fr + Openair Tech : openair_tech@eurecom.fr + Forums : http://forums.eurecom.fr/openairinterface + Address : EURECOM, Campus SophiaTech, 450 Route des Chappes + 06410 Biot FRANCE + +*******************************************************************************/ + +/*! \file s6a_auth_info.c + * \brief Handle an authentication information request procedure and generate the answer + * \author Sebastien ROUX + * \date 2013 + * \version 0.1 + */ + +#include +#include +#include + +#include "hss_config.h" +#include "db_proto.h" +#include "s6a_proto.h" +#include "auc.h" +#include "access_restriction.h" + +int s6a_auth_info_cb(struct msg **msg, struct avp *paramavp, + struct session *sess, void *opaque, + enum disp_action *act) +{ + struct msg *ans, *qry; + struct avp *avp, *failed_avp = NULL; + + struct avp_hdr *hdr; + union avp_value value; + + /* Database queries */ + mysql_auth_info_req_t auth_info_req; + mysql_auth_info_resp_t auth_info_resp; + + /* Authentication vector */ + auc_vector_t vector; + + int ret = 0; + int result_code = ER_DIAMETER_SUCCESS; + int experimental = 0; + uint64_t imsi; + uint8_t *sqn = NULL, *auts = NULL; + + if (msg == NULL) { + return EINVAL; + } + + /* Create answer header */ + qry = *msg; + CHECK_FCT(fd_msg_new_answer_from_req(fd_g_config->cnf_dict, msg, 0)); + ans = *msg; + + /* Retrieving IMSI AVP: User-Name */ + CHECK_FCT(fd_msg_search_avp(qry, s6a_cnf.dataobj_s6a_imsi, &avp)); + if (avp) { + CHECK_FCT(fd_msg_avp_hdr(avp, &hdr)); + if (hdr->avp_value->os.len > IMSI_LENGTH_MAX) { + result_code = ER_DIAMETER_INVALID_AVP_VALUE; + goto out; + } + sprintf(auth_info_req.imsi, "%*s", (int)hdr->avp_value->os.len, + hdr->avp_value->os.data); + sscanf(auth_info_req.imsi, "%"SCNu64, &imsi); + } else { + result_code = ER_DIAMETER_MISSING_AVP; + goto out; + } + + /* Retrieving Supported Features AVP. This is an optional AVP. */ + CHECK_FCT(fd_msg_search_avp(qry, s6a_cnf.dataobj_s6a_supported_features, &avp)); + if (avp) { + CHECK_FCT(fd_msg_avp_hdr(avp, &hdr)); + } + + /* Retrieving the Requested-EUTRAN-Authentication-Info. + * If this AVP is not present, we have to check for + * Requested-GERAN-Authentication-Info AVP which will mean that the request + * comes from RAT other than E-UTRAN, case not handled by this HSS + * implementation. + */ + CHECK_FCT(fd_msg_search_avp(qry, s6a_cnf.dataobj_s6a_req_e_utran_auth_info, &avp)); + if (avp) { + struct avp *child_avp; + + /* Walk through childs avp */ + CHECK_FCT(fd_msg_browse(avp, MSG_BRW_FIRST_CHILD, &child_avp, NULL)); + while (child_avp) { + /* Retrieve the header of the child avp */ + CHECK_FCT(fd_msg_avp_hdr(child_avp, &hdr)); + switch(hdr->avp_code) { + case AVP_CODE_NUMBER_OF_REQ_VECTORS: { + /* We allow only one vector request */ + if (hdr->avp_value->u32 != 1) { + result_code = ER_DIAMETER_INVALID_AVP_VALUE; + failed_avp = child_avp; + goto out; + } + } break; + case AVP_CODE_IMMEDIATE_RESP_PREF: + /* We always respond immediately to the request */ + break; + case AVP_CODE_RE_SYNCHRONIZATION_INFO: + /* The resynchronization-info AVP is present. + * AUTS = Conc(SQN MS ) || MAC-S + */ + if (avp) { + auts = hdr->avp_value->os.data; + } + break; + default: { + /* This AVP is not expected on s6a interface */ + result_code = ER_DIAMETER_AVP_UNSUPPORTED; + failed_avp = child_avp; + goto out; + } + } + /* Go to next AVP in the grouped AVP */ + CHECK_FCT(fd_msg_browse(child_avp, MSG_BRW_NEXT, &child_avp, NULL)); + } + } else { + CHECK_FCT(fd_msg_search_avp(qry, s6a_cnf.dataobj_s6a_req_geran_auth_info, &avp)); + if (avp) { + result_code = DIAMETER_ERROR_RAT_NOT_ALLOWED; + experimental = 1; + goto out; + } else { + result_code = ER_DIAMETER_INVALID_AVP_VALUE; + failed_avp = avp; + goto out; + } + } + + /* Retrieving the Visited-PLMN-Id AVP */ + CHECK_FCT(fd_msg_search_avp(qry, s6a_cnf.dataobj_s6a_visited_plmn_id, &avp)); + if (avp) { + /* TODO: check PLMN and allow/reject connectivity depending on roaming */ + CHECK_FCT(fd_msg_avp_hdr(avp, &hdr)); + if (hdr->avp_value->os.len == 3) { + if (apply_access_restriction(auth_info_req.imsi, hdr->avp_value->os.data) != 0) { + /* We found that user is roaming and has no right to do it -> + * reject the connection + */ + result_code = DIAMETER_ERROR_ROAMING_NOT_ALLOWED; + experimental = 1; + goto out; + } + } else { + result_code = ER_DIAMETER_INVALID_AVP_VALUE; + goto out; + } + } else { + /* Mandatory AVP, raise an error if not present */ + result_code = ER_DIAMETER_MISSING_AVP; + goto out; + } + + /* Fetch User data */ + if (hss_mysql_auth_info(&auth_info_req, &auth_info_resp) != 0) { + /* Database query failed... */ + result_code = DIAMETER_AUTHENTICATION_DATA_UNAVAILABLE; + experimental = 1; + goto out; + } + + if (auts != NULL) { + /* Try to derive SQN_MS from previous RAND */ + sqn = sqn_ms_derive(auth_info_resp.key, auts, auth_info_resp.rand); + if (sqn != NULL) { + /* We succeeded to verify SQN_MS... */ + + /* Pick a new RAND and store SQN_MS + RAND in the HSS */ + generate_random(vector.rand, RAND_LENGTH); + hss_mysql_push_rand_sqn(auth_info_req.imsi, auth_info_resp.rand, sqn); + +// hss_mysql_increment_sqn(auth_info_req.imsi); + + free(sqn); + } + + /* Fetch new user data */ + if (hss_mysql_auth_info(&auth_info_req, &auth_info_resp) != 0) { + /* Database query failed... */ + result_code = DIAMETER_AUTHENTICATION_DATA_UNAVAILABLE; + experimental = 1; + goto out; + } + sqn = auth_info_resp.sqn; + + memcpy(vector.rand, auth_info_resp.rand, RAND_LENGTH); + } else { + /* Pick a new RAND and store SQN_MS + RAND in the HSS */ + generate_random(vector.rand, RAND_LENGTH); + sqn = auth_info_resp.sqn; + hss_mysql_push_rand_sqn(auth_info_req.imsi, vector.rand, sqn); + } + + hss_mysql_increment_sqn(auth_info_req.imsi); + + /* Generate authentication vector */ + generate_vector(imsi, auth_info_resp.key, + hdr->avp_value->os.data, sqn, &vector); + + /* We add the vector */ + { + struct avp *e_utran_vector, *child_avp; + CHECK_FCT(fd_msg_avp_new(s6a_cnf.dataobj_s6a_authentication_info, 0, &avp)); + + CHECK_FCT(fd_msg_avp_new(s6a_cnf.dataobj_s6a_e_utran_vector, 0, &e_utran_vector)); + + CHECK_FCT(fd_msg_avp_new(s6a_cnf.dataobj_s6a_rand, 0, &child_avp)); + value.os.data = vector.rand; + value.os.len = RAND_LENGTH_OCTETS; + CHECK_FCT(fd_msg_avp_setvalue(child_avp, &value)); + CHECK_FCT(fd_msg_avp_add(e_utran_vector, MSG_BRW_LAST_CHILD, child_avp)); + + CHECK_FCT(fd_msg_avp_new(s6a_cnf.dataobj_s6a_xres, 0, &child_avp)); + value.os.data = vector.xres; + value.os.len = XRES_LENGTH_OCTETS; + CHECK_FCT(fd_msg_avp_setvalue(child_avp, &value)); + CHECK_FCT(fd_msg_avp_add(e_utran_vector, MSG_BRW_LAST_CHILD, child_avp)); + + CHECK_FCT(fd_msg_avp_new(s6a_cnf.dataobj_s6a_autn, 0, &child_avp)); + value.os.data = vector.autn; + value.os.len = AUTN_LENGTH_OCTETS; + CHECK_FCT(fd_msg_avp_setvalue(child_avp, &value)); + CHECK_FCT(fd_msg_avp_add(e_utran_vector, MSG_BRW_LAST_CHILD, child_avp)); + + CHECK_FCT(fd_msg_avp_new(s6a_cnf.dataobj_s6a_kasme, 0, &child_avp)); + value.os.data = vector.kasme; + value.os.len = KASME_LENGTH_OCTETS; + CHECK_FCT(fd_msg_avp_setvalue(child_avp, &value)); + CHECK_FCT(fd_msg_avp_add(e_utran_vector, MSG_BRW_LAST_CHILD, child_avp)); + + CHECK_FCT(fd_msg_avp_add(avp, MSG_BRW_LAST_CHILD, e_utran_vector)); + + CHECK_FCT(fd_msg_avp_add(ans, MSG_BRW_LAST_CHILD, avp)); + } + +out: + /* Add the Auth-Session-State AVP */ + CHECK_FCT(fd_msg_search_avp(qry, s6a_cnf.dataobj_s6a_auth_session_state, &avp)); + CHECK_FCT(fd_msg_avp_hdr(avp, &hdr)); + + CHECK_FCT(fd_msg_avp_new(s6a_cnf.dataobj_s6a_auth_session_state, 0, &avp)); + CHECK_FCT(fd_msg_avp_setvalue(avp, hdr->avp_value)); + CHECK_FCT(fd_msg_avp_add(ans, MSG_BRW_LAST_CHILD, avp)); + + /* Append the result code to the answer */ + CHECK_FCT(s6a_add_result_code(ans, failed_avp, result_code, experimental)); + + CHECK_FCT(fd_msg_send(msg, NULL, NULL )); + return ret; +} diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/s6a/s6a_common.c b/openair-cn/OPENAIRHSS/OPENAIRHSS/s6a/s6a_common.c new file mode 100644 index 0000000000..d6e90c9d85 --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/s6a/s6a_common.c @@ -0,0 +1,66 @@ +/******************************************************************************* + + Eurecom OpenAirInterface + Copyright(c) 1999 - 2013 Eurecom + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information + Openair Admin: openair_admin@eurecom.fr + Openair Tech : openair_tech@eurecom.fr + Forums : http://forums.eurecom.fr/openairinterface + Address : EURECOM, Campus SophiaTech, 450 Route des Chappes + 06410 Biot FRANCE + +*******************************************************************************/ + +#include "hss_config.h" +#include "db_proto.h" +#include "s6a_proto.h" + +int s6a_add_result_code(struct msg *ans, struct avp *failed_avp, int result_code, int experimental) +{ + struct avp *avp; + union avp_value value; + + if (DIAMETER_ERROR_IS_VENDOR(result_code) && experimental != 0) { + struct avp *experimental_result; + CHECK_FCT(fd_msg_avp_new(s6a_cnf.dataobj_s6a_experimental_result, + 0, &experimental_result)); + CHECK_FCT(fd_msg_avp_new(s6a_cnf.dataobj_s6a_vendor_id, + 0, &avp)); + value.u32 = VENDOR_3GPP; + CHECK_FCT(fd_msg_avp_setvalue(avp, &value)); + CHECK_FCT(fd_msg_avp_add(experimental_result, MSG_BRW_LAST_CHILD, avp)); + CHECK_FCT(fd_msg_avp_new(s6a_cnf.dataobj_s6a_experimental_result_code, + 0, &avp)); + value.u32 = result_code; + CHECK_FCT(fd_msg_avp_setvalue(avp, &value)); + CHECK_FCT(fd_msg_avp_add(experimental_result, MSG_BRW_LAST_CHILD, avp)); + CHECK_FCT(fd_msg_avp_add(ans, MSG_BRW_LAST_CHILD, experimental_result)); + + /* Add Origin_Host & Origin_Realm AVPs */ + CHECK_FCT(fd_msg_add_origin(ans, 0)); + } else { + /* This is a code defined in the base protocol: result-code AVP should + * be used. + */ + CHECK_FCT(fd_msg_rescode_set(ans, retcode_2_string(result_code), NULL, + failed_avp, 1)); + } + return 0; +} diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/s6a/s6a_error.c b/openair-cn/OPENAIRHSS/OPENAIRHSS/s6a/s6a_error.c new file mode 100644 index 0000000000..4c2efeca99 --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/s6a/s6a_error.c @@ -0,0 +1,73 @@ +/******************************************************************************* + + Eurecom OpenAirInterface + Copyright(c) 1999 - 2013 Eurecom + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information + Openair Admin: openair_admin@eurecom.fr + Openair Tech : openair_tech@eurecom.fr + Forums : http://forums.eurecom.fr/openairinterface + Address : EURECOM, Campus SophiaTech, 450 Route des Chappes + 06410 Biot FRANCE + +*******************************************************************************/ + +#include "hss_config.h" +#include "db_proto.h" +#include "s6a_proto.h" + +inline char *experimental_retcode_2_string(int ret_code) +{ + switch(ret_code) { + /* Experimental-Result-Codes */ + case DIAMETER_ERROR_USER_UNKNOWN: + return "DIAMETER_ERROR_USER_UNKNOWN"; + case DIAMETER_ERROR_ROAMING_NOT_ALLOWED: + return "DIAMETER_ERROR_ROAMING_NOT_ALLOWED"; + case DIAMETER_ERROR_UNKNOWN_EPS_SUBSCRIPTION: + return "DIAMETER_ERROR_UNKNOWN_EPS_SUBSCRIPTION"; + case DIAMETER_ERROR_RAT_NOT_ALLOWED: + return "DIAMETER_ERROR_RAT_NOT_ALLOWED"; + case DIAMETER_ERROR_EQUIPMENT_UNKNOWN: + return "DIAMETER_ERROR_EQUIPMENT_UNKNOWN"; + case DIAMETER_ERROR_UNKOWN_SERVING_NODE: + return "DIAMETER_ERROR_UNKOWN_SERVING_NODE"; + case DIAMETER_AUTHENTICATION_DATA_UNAVAILABLE: + return "DIAMETER_AUTHENTICATION_DATA_UNAVAILABLE"; + default: + break; + } + return "DIAMETER_AVP_UNSUPPORTED"; +} + +inline char *retcode_2_string(int ret_code) +{ + switch(ret_code) { + case ER_DIAMETER_SUCCESS: + return "DIAMETER_SUCCESS"; + case ER_DIAMETER_MISSING_AVP: + return "DIAMETER_MISSING_AVP"; + case ER_DIAMETER_INVALID_AVP_VALUE: + return "DIAMETER_INVALID_AVP_VALUE"; + default: + break; + } + return "DIAMETER_AVP_UNSUPPORTED"; +} + diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/s6a/s6a_fd.c b/openair-cn/OPENAIRHSS/OPENAIRHSS/s6a/s6a_fd.c new file mode 100644 index 0000000000..76875a7572 --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/s6a/s6a_fd.c @@ -0,0 +1,350 @@ +/******************************************************************************* + + Eurecom OpenAirInterface + Copyright(c) 1999 - 2013 Eurecom + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information + Openair Admin: openair_admin@eurecom.fr + Openair Tech : openair_tech@eurecom.fr + Forums : http://forums.eurecom.fr/openairinterface + Address : EURECOM, Campus SophiaTech, 450 Route des Chappes + 06410 Biot FRANCE + +*******************************************************************************/ + +#include +#include +#include +#include +#include + +#if HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#include "hss_config.h" +#include "db_proto.h" +#include "s6a_proto.h" + +/* session handler for s6a sessions state machine */ +static struct session_handler *s6a_reg = NULL; + +/* handler for s6a server callback */ +static struct disp_hdl *handle; +s6a_cnf_t s6a_cnf; + +void s6a_cli_sess_cleanup(void * arg, char * sid, void * opaque); + +void s6a_cli_sess_cleanup(void * arg, char * sid, void * opaque) +{ + +} + +static int s6a_init_objs(void) +{ + vendor_id_t vendor_3gpp = VENDOR_3GPP; + application_id_t app_s6a = APP_S6A; + + memset(&s6a_cnf, 0, sizeof(s6a_cnf)); + + /* Pre-loading vendor object */ + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_VENDOR, VENDOR_BY_ID, + (void*)&vendor_3gpp, &s6a_cnf.dataobj_s6a_vendor, ENOENT)); + /* Pre-loading application object */ + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_APPLICATION, + APPLICATION_BY_ID, (void*)&app_s6a, + &s6a_cnf.dataobj_s6a_app, ENOENT)); + + /* Pre-loading commands objects */ + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_COMMAND, + CMD_BY_NAME, "Authentication-Information-Request", + &s6a_cnf.dataobj_s6a_auth_cmd, ENOENT)); + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_COMMAND, + CMD_BY_NAME, "Update-Location-Request", + &s6a_cnf.dataobj_s6a_loc_up, ENOENT)); + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_COMMAND, + CMD_BY_NAME, "Purge-UE-Request", + &s6a_cnf.dataobj_s6a_purge_ue, ENOENT)); + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_COMMAND, + CMD_BY_NAME, "Cancel-Location-Request", + &s6a_cnf.dataobj_s6a_cancel_loc_req, ENOENT)); + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_COMMAND, + CMD_BY_NAME, "Cancel-Location-Answer", + &s6a_cnf.dataobj_s6a_cancel_loc_ans, ENOENT)); + + /* Pre-loading AVPs objects */ + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, + AVP_BY_NAME, "Origin-Host", + &s6a_cnf.dataobj_s6a_origin_host, ENOENT)); + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, + AVP_BY_NAME, "Origin-Realm", + &s6a_cnf.dataobj_s6a_origin_realm, ENOENT)); + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, + AVP_BY_NAME_ALL_VENDORS, "User-Name", + &s6a_cnf.dataobj_s6a_imsi, ENOENT)); + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, + AVP_BY_NAME_ALL_VENDORS, "IMEI", + &s6a_cnf.dataobj_s6a_imei, ENOENT)); + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, + AVP_BY_NAME_ALL_VENDORS, "Software-Version", + &s6a_cnf.dataobj_s6a_software_version, ENOENT)); + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, + AVP_BY_NAME_ALL_VENDORS, "Supported-Features", + &s6a_cnf.dataobj_s6a_supported_features, ENOENT)); + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, + AVP_BY_NAME_ALL_VENDORS, "Requested-EUTRAN-Authentication-Info", + &s6a_cnf.dataobj_s6a_req_e_utran_auth_info, ENOENT)); + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, + AVP_BY_NAME_ALL_VENDORS, "Number-Of-Requested-Vectors", + &s6a_cnf.dataobj_s6a_req_nb_of_req_vectors, ENOENT)); + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, + AVP_BY_NAME_ALL_VENDORS, "Re-Synchronization-Info", + &s6a_cnf.dataobj_s6a_req_resync_info, ENOENT)); + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, + AVP_BY_NAME_ALL_VENDORS, "Requested-UTRAN-GERAN-Authentication-Info", + &s6a_cnf.dataobj_s6a_req_geran_auth_info, ENOENT)); + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, + AVP_BY_NAME_ALL_VENDORS, "Immediate-Response-Preferred", + &s6a_cnf.dataobj_s6a_immediate_response_pref, ENOENT)); + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, + AVP_BY_NAME_ALL_VENDORS, "Visited-PLMN-Id", + &s6a_cnf.dataobj_s6a_visited_plmn_id, ENOENT)); + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, + AVP_BY_NAME, "Result-Code", + &s6a_cnf.dataobj_s6a_result_code, ENOENT)); + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, + AVP_BY_NAME, "Experimental-Result", + &s6a_cnf.dataobj_s6a_experimental_result, ENOENT)); + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, + AVP_BY_NAME, "Vendor-Id", + &s6a_cnf.dataobj_s6a_vendor_id, ENOENT)); + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, + AVP_BY_NAME, "Experimental-Result-Code", + &s6a_cnf.dataobj_s6a_experimental_result_code, ENOENT)); + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, + AVP_BY_NAME_ALL_VENDORS, "Auth-Session-State", + &s6a_cnf.dataobj_s6a_auth_session_state, ENOENT)); + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, + AVP_BY_NAME_ALL_VENDORS, "Authentication-Info", + &s6a_cnf.dataobj_s6a_authentication_info, ENOENT)); + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, + AVP_BY_NAME_ALL_VENDORS, "E-UTRAN-Vector", + &s6a_cnf.dataobj_s6a_e_utran_vector, ENOENT)); + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, + AVP_BY_NAME_ALL_VENDORS, "RAND", + &s6a_cnf.dataobj_s6a_rand, ENOENT)); + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, + AVP_BY_NAME_ALL_VENDORS, "XRES", + &s6a_cnf.dataobj_s6a_xres, ENOENT)); + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, + AVP_BY_NAME_ALL_VENDORS, "AUTN", + &s6a_cnf.dataobj_s6a_autn, ENOENT)); + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, + AVP_BY_NAME_ALL_VENDORS, "KASME", + &s6a_cnf.dataobj_s6a_kasme, ENOENT)); + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, + AVP_BY_NAME_ALL_VENDORS, "ULR-Flags", + &s6a_cnf.dataobj_s6a_ulr_flags, ENOENT)); + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, + AVP_BY_NAME_ALL_VENDORS, "ULA-Flags", + &s6a_cnf.dataobj_s6a_ula_flags, ENOENT)); + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, + AVP_BY_NAME_ALL_VENDORS, "PUR-Flags", + &s6a_cnf.dataobj_s6a_pur_flags, ENOENT)); + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, + AVP_BY_NAME_ALL_VENDORS, "PUA-Flags", + &s6a_cnf.dataobj_s6a_pua_flags, ENOENT)); + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, + AVP_BY_NAME_ALL_VENDORS, "RAT-Type", + &s6a_cnf.dataobj_s6a_rat_type, ENOENT)); + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, + AVP_BY_NAME_ALL_VENDORS, "Terminal-Information", + &s6a_cnf.dataobj_s6a_terminal_info, ENOENT)); + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, + AVP_BY_NAME_ALL_VENDORS, "UE-SRVCC-Capability", + &s6a_cnf.dataobj_s6a_ue_srvcc_cap, ENOENT)); + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, + AVP_BY_NAME_ALL_VENDORS, "GMLC-Address", + &s6a_cnf.dataobj_s6a_gmlc_addr, ENOENT)); + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, + AVP_BY_NAME_ALL_VENDORS, "Subscription-Data", + &s6a_cnf.dataobj_s6a_subscription_data, ENOENT)); + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, + AVP_BY_NAME_ALL_VENDORS, "Subscriber-Status", + &s6a_cnf.dataobj_s6a_subscriber_status, ENOENT)); + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, + AVP_BY_NAME_ALL_VENDORS, "MSISDN", + &s6a_cnf.dataobj_s6a_msisdn, ENOENT)); + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, + AVP_BY_NAME_ALL_VENDORS, "AMBR", + &s6a_cnf.dataobj_s6a_ambr, ENOENT)); + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, + AVP_BY_NAME_ALL_VENDORS, "Network-Access-Mode", + &s6a_cnf.dataobj_s6a_network_access_mode, ENOENT)); + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, + AVP_BY_NAME_ALL_VENDORS, "Access-Restriction-Data", + &s6a_cnf.dataobj_s6a_access_restriction_data, ENOENT)); + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, + AVP_BY_NAME_ALL_VENDORS, "APN-Configuration-Profile", + &s6a_cnf.dataobj_s6a_apn_configuration_profile, ENOENT)); + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, + AVP_BY_NAME_ALL_VENDORS, "Subscribed-Periodic-RAU-TAU-Timer", + &s6a_cnf.dataobj_s6a_subscribed_rau_tau_timer, ENOENT)); + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, + AVP_BY_NAME_ALL_VENDORS, "Context-Identifier", + &s6a_cnf.dataobj_s6a_context_identifier, ENOENT)); + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, + AVP_BY_NAME_ALL_VENDORS, "All-APN-Configurations-Included-Indicator", + &s6a_cnf.dataobj_s6a_all_apn_conf_inc_ind, ENOENT)); + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, + AVP_BY_NAME_ALL_VENDORS, "APN-Configuration", + &s6a_cnf.dataobj_s6a_apn_configuration, ENOENT)); + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, + AVP_BY_NAME_ALL_VENDORS, "Max-Requested-Bandwidth-UL", + &s6a_cnf.dataobj_s6a_max_bandwidth_ul, ENOENT)); + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, + AVP_BY_NAME_ALL_VENDORS, "Max-Requested-Bandwidth-DL", + &s6a_cnf.dataobj_s6a_max_bandwidth_dl, ENOENT)); + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, + AVP_BY_NAME_ALL_VENDORS, "PDN-Type", + &s6a_cnf.dataobj_s6a_pdn_type, ENOENT)); + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, + AVP_BY_NAME_ALL_VENDORS, "Service-Selection", + &s6a_cnf.dataobj_s6a_service_selection, ENOENT)); + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, + AVP_BY_NAME_ALL_VENDORS, "EPS-Subscribed-QoS-Profile", + &s6a_cnf.dataobj_s6a_eps_subscribed_qos_profile, ENOENT)); + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, + AVP_BY_NAME_ALL_VENDORS, "QoS-Class-Identifier", + &s6a_cnf.dataobj_s6a_qos_class_identifier, ENOENT)); + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, + AVP_BY_NAME_ALL_VENDORS, "Allocation-Retention-Priority", + &s6a_cnf.dataobj_s6a_allocation_retention_priority, ENOENT)); + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, + AVP_BY_NAME_ALL_VENDORS, "Priority-Level", + &s6a_cnf.dataobj_s6a_priority_level, ENOENT)); + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, + AVP_BY_NAME_ALL_VENDORS, "Pre-emption-Capability", + &s6a_cnf.dataobj_s6a_pre_emption_capability, ENOENT)); + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, + AVP_BY_NAME_ALL_VENDORS, "Pre-emption-Vulnerability", + &s6a_cnf.dataobj_s6a_pre_emption_vulnerability, ENOENT)); + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, + AVP_BY_NAME_ALL_VENDORS, "Served-Party-IP-Address", + &s6a_cnf.dataobj_s6a_served_party_ip_addr, ENOENT)); + + /* Advertise the support for the test application in the peer */ + CHECK_FCT(fd_disp_app_support(s6a_cnf.dataobj_s6a_app, + s6a_cnf.dataobj_s6a_vendor, 1, 0)); + + return 0; +} + +int s6a_init(hss_config_t *hss_config_p) +{ + int ret = 0; + struct disp_when when; + char why[100]; + + fprintf(stdout, "Initializing s6a layer\n"); + + ret = fd_core_initialize(); + if (ret != 0) { + strcpy(why, "fd_core_initialize"); + goto err; + } + + ret = fd_core_start(); + if (ret != 0) { + strcpy(why, "fd_core_start"); + goto err; + } + + /* Parse the external configuration file */ + ret = fd_core_parseconf(hss_config_p->freediameter_config); + if (ret != 0) { + strcpy(why, "fd_core_parseconf"); + goto err; + } + + /* We wait till freediameter has completed loading extensions */ + fd_core_waitstartcomplete(); + + /* Register the peer acceptor/rejector */ + fd_peer_validate_register(s6a_peer_validate); + + /* Initialize useful objects */ + ret = s6a_init_objs(); + if (ret != 0) { + strcpy(why, "s6a_init_objs"); + goto err; + } + + /* Create handler for sessions */ + CHECK_FCT(fd_sess_handler_create(&s6a_reg, s6a_cli_sess_cleanup, NULL)); + + /* Register the callback */ + memset(&when, 0, sizeof(when)); + when.command = s6a_cnf.dataobj_s6a_auth_cmd; + when.app = s6a_cnf.dataobj_s6a_app; + + /* Register the callbacks for S6A Application */ + CHECK_FCT(fd_disp_register(s6a_auth_info_cb, DISP_HOW_CC, &when, NULL, + &handle)); + + if (handle == NULL) { + strcpy(why, "cannot register authentication info req cb"); + goto err; + } + + when.command = s6a_cnf.dataobj_s6a_loc_up; + when.app = s6a_cnf.dataobj_s6a_app; + + /* Register the callbacks for S6A Application */ + CHECK_FCT(fd_disp_register(s6a_up_loc_cb, DISP_HOW_CC, &when, NULL, + &handle)); + + if (handle == NULL) { + strcpy(why, "cannot register update location req cb"); + goto err; + } + + when.command = s6a_cnf.dataobj_s6a_purge_ue; + when.app = s6a_cnf.dataobj_s6a_app; + + /* Register the callbacks for S6A Application */ + CHECK_FCT(fd_disp_register(s6a_purge_ue_cb, DISP_HOW_CC, &when, NULL, + &handle)); + + if (handle == NULL) { + strcpy(why, "cannot register purge ue req cb"); + goto err; + } + + fprintf(stdout, "Initializing s6a layer: DONE\n"); + + return 0; + +err: + fprintf(stdout, "Initializing s6a layer: FAILED (%s)\n", why); + return -1; +} diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/s6a/s6a_in_addr.c b/openair-cn/OPENAIRHSS/OPENAIRHSS/s6a/s6a_in_addr.c new file mode 100644 index 0000000000..241b45db3c --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/s6a/s6a_in_addr.c @@ -0,0 +1,107 @@ +/******************************************************************************* + + Eurecom OpenAirInterface + Copyright(c) 1999 - 2013 Eurecom + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information + Openair Admin: openair_admin@eurecom.fr + Openair Tech : openair_tech@eurecom.fr + Forums : http://forums.eurecom.fr/openairinterface + Address : EURECOM, Campus SophiaTech, 450 Route des Chappes + 06410 Biot FRANCE + +*******************************************************************************/ + +#include "hss_config.h" +#include "db_proto.h" +#include "s6a_proto.h" + +/* http://www.iana.org/assignments/address-family-numbers/address-family-numbers.xml*/ + +/* Perform a conversion between ipv4 in BCD to AVP served-party-ip-address */ +int s6a_add_ipv4_address(struct avp *avp, const char *ipv4_addr) +{ + struct avp *child_avp; + union avp_value value; + + uint8_t ipv4[6]; /* Converted IPv4 address with family */ + in_addr_t sin; + + if (ipv4_addr == NULL) { + return -1; + } + + /* This is an IPv4 family -> ipv4 buffer should start with 0x0001 */ + ipv4[0] = 0x00; + ipv4[1] = 0x01; + + sin = inet_addr(ipv4_addr); + /* No need to add the address if it is an any address */ + if (sin != INADDR_ANY) { + memcpy(&ipv4[2], &sin, 4); + CHECK_FCT(fd_msg_avp_new(s6a_cnf.dataobj_s6a_served_party_ip_addr, 0, &child_avp)); + value.os.data = ipv4; + value.os.len = 6; + CHECK_FCT(fd_msg_avp_setvalue(child_avp, &value)); + CHECK_FCT(fd_msg_avp_add(avp, MSG_BRW_LAST_CHILD, child_avp)); + return 0; + } + /* No IP address added to AVP */ + return -1; +} + +/* Perform a conversion between ipv6 in BCD to AVP served-party-ip-address */ +int s6a_add_ipv6_address(struct avp *avp, const char *ipv6_addr) +{ + struct avp *child_avp; + union avp_value value; + + uint8_t ipv6[18]; + struct in6_addr sin6; + + if (ipv6_addr == NULL) { + return -1; + } + + memset(&sin6, 0, sizeof(struct in6_addr)); + + /* This is an IPv6 family -> ipv6 buffer should start with 0x0002 */ + ipv6[0] = 0x00; + ipv6[1] = 0x02; + + if (inet_pton(AF_INET6, ipv6_addr, &sin6) == -1) { + fprintf(stderr, "INET6 address conversion has failed\n"); + return -1; + } + + /* If the IPV6 address is 0:0:0:0:0:0:0:0 then we don't add it to the + * served-party ip address and consider the ip address can be dynamically + * allocated. + */ + if (!IN6_IS_ADDR_UNSPECIFIED(sin6.s6_addr32)) { + memcpy(&ipv6[2], &sin6.s6_addr, 16); + CHECK_FCT(fd_msg_avp_new(s6a_cnf.dataobj_s6a_served_party_ip_addr, 0, &child_avp)); + value.os.data = ipv6; + value.os.len = 18; + CHECK_FCT(fd_msg_avp_setvalue(child_avp, &value)); + CHECK_FCT(fd_msg_avp_add(avp, MSG_BRW_LAST_CHILD, child_avp)); + return 0; + } + return -1; +} diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/s6a/s6a_peers.c b/openair-cn/OPENAIRHSS/OPENAIRHSS/s6a/s6a_peers.c new file mode 100644 index 0000000000..4759049521 --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/s6a/s6a_peers.c @@ -0,0 +1,76 @@ +/******************************************************************************* + + Eurecom OpenAirInterface + Copyright(c) 1999 - 2013 Eurecom + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information + Openair Admin: openair_admin@eurecom.fr + Openair Tech : openair_tech@eurecom.fr + Forums : http://forums.eurecom.fr/openairinterface + Address : EURECOM, Campus SophiaTech, 450 Route des Chappes + 06410 Biot FRANCE + +*******************************************************************************/ + +/*! \file s6a_peers.c + * \brief Authenticate a new peer connecting to the HSS by checking the database + * \author Sebastien ROUX + * \date 2013 + * \version 0.1 + */ + +#include +#include + +#include "hss_config.h" +#include "db_proto.h" +#include "s6a_proto.h" + +int s6a_peer_validate(struct peer_info *info, int *auth, int (**cb2)(struct peer_info *)) +{ + mysql_mme_identity_t mme_identity; + + if (info == NULL) { + return EINVAL; + } + + memset(&mme_identity, 0, sizeof(mysql_mme_identity_t)); + + /* We received a new connection. Check the database for allowed equipments + * on EPC + */ + + memcpy(mme_identity.mme_host, info->pi_diamid, info->pi_diamidlen); + + if (hss_mysql_check_epc_equipment(&mme_identity) != 0) { + /* The MME has not been found in list of known peers -> reject it */ + *auth = -1; + fprintf(stdout, "Rejecting %s: either db has no knowledge of this peer " + "or sql query failed\n", info->pi_diamid); + } else { + *auth = 1; + + /* For now we don't use security */ + info->config.pic_flags.sec = PI_SEC_NONE; + info->config.pic_flags.persist = PI_PRST_NONE; + fprintf(stdout, "Accepting %s peer\n", info->pi_diamid); + } + + return 0; +} diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/s6a/s6a_proto.h b/openair-cn/OPENAIRHSS/OPENAIRHSS/s6a/s6a_proto.h new file mode 100644 index 0000000000..39049c5f5f --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/s6a/s6a_proto.h @@ -0,0 +1,239 @@ +/******************************************************************************* + + Eurecom OpenAirInterface + Copyright(c) 1999 - 2013 Eurecom + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information + Openair Admin: openair_admin@eurecom.fr + Openair Tech : openair_tech@eurecom.fr + Forums : http://forums.eurecom.fr/openairinterface + Address : EURECOM, Campus SophiaTech, 450 Route des Chappes + 06410 Biot FRANCE + +*******************************************************************************/ + +#include +#include + +#include "hss_config.h" + +#ifndef S6A_PROTO_H_ +#define S6A_PROTO_H_ + +#define VENDOR_3GPP (10415) +#define APP_S6A (16777251) + +/* Errors that fall within the Permanent Failures category shall be used to + * inform the peer that the request has failed, and should not be attempted + * again. The Result-Code AVP values defined in Diameter Base Protocol RFC 3588 + * shall be applied. When one of the result codes defined here is included in a + * response, it shall be inside an Experimental-Result AVP and the Result-Code + * AVP shall be absent. + */ +#define DIAMETER_ERROR_USER_UNKNOWN (5001) +#define DIAMETER_ERROR_ROAMING_NOT_ALLOWED (5004) +#define DIAMETER_ERROR_UNKNOWN_EPS_SUBSCRIPTION (5420) +#define DIAMETER_ERROR_RAT_NOT_ALLOWED (5421) +#define DIAMETER_ERROR_EQUIPMENT_UNKNOWN (5422) +#define DIAMETER_ERROR_UNKOWN_SERVING_NODE (5423) + +/* Result codes that fall within the transient failures category shall be used + * to inform a peer that the request could not be satisfied at the time it was + * received, but may be able to satisfy the request in the future. The + * Result-Code AVP values defined in Diameter Base Protocol RFC 3588 shall be + * applied. When one of the result codes defined here is included in a response, + * it shall be inside an Experimental-Result AVP and the Result-Code AVP shall + * be absent. + */ +#define DIAMETER_AUTHENTICATION_DATA_UNAVAILABLE (4181) + +#define DIAMETER_ERROR_IS_VENDOR(x) \ + ((x == DIAMETER_ERROR_USER_UNKNOWN) || \ + (x == DIAMETER_ERROR_ROAMING_NOT_ALLOWED) || \ + (x == DIAMETER_ERROR_UNKNOWN_EPS_SUBSCRIPTION) || \ + (x == DIAMETER_ERROR_RAT_NOT_ALLOWED) || \ + (x == DIAMETER_ERROR_EQUIPMENT_UNKNOWN) || \ + (x == DIAMETER_AUTHENTICATION_DATA_UNAVAILABLE) || \ + (x == DIAMETER_ERROR_UNKOWN_SERVING_NODE)) + +#define AVP_CODE_IMEI (1402) +#define AVP_CODE_SOFTWARE_VERSION (1403) +#define AVP_CODE_NUMBER_OF_REQ_VECTORS (1410) +#define AVP_CODE_RE_SYNCHRONIZATION_INFO (1411) +#define AVP_CODE_IMMEDIATE_RESP_PREF (1412) +#define AVP_CODE_3GPP2_MEID (1471) +#define AVP_CODE_FEATURE_LIST (629) +#define AVP_CODE_VENDOR_ID (266) + +typedef struct { + struct dict_object *dataobj_s6a_vendor; /* s6a vendor object */ + struct dict_object *dataobj_s6a_app; /* s6a application object */ + /* Commands */ + struct dict_object *dataobj_s6a_auth_cmd; /* s6a authentication command */ + struct dict_object *dataobj_s6a_loc_up; /* s6a update location */ + struct dict_object *dataobj_s6a_purge_ue; /* s6a purge ue req */ + struct dict_object *dataobj_s6a_cancel_loc_req; /* s6a Cancel Location req */ + struct dict_object *dataobj_s6a_cancel_loc_ans; /* s6a Cancel Location ans */ + /* AVPs */ + struct dict_object *dataobj_s6a_origin_host; + struct dict_object *dataobj_s6a_origin_realm; + struct dict_object *dataobj_s6a_imsi; /* s6a imsi avp */ + struct dict_object *dataobj_s6a_imei; + struct dict_object *dataobj_s6a_software_version; + struct dict_object *dataobj_s6a_supported_features; + struct dict_object *dataobj_s6a_req_e_utran_auth_info; + struct dict_object *dataobj_s6a_req_resync_info; + struct dict_object *dataobj_s6a_req_nb_of_req_vectors; + struct dict_object *dataobj_s6a_req_geran_auth_info; + struct dict_object *dataobj_s6a_immediate_response_pref; + struct dict_object *dataobj_s6a_visited_plmn_id; + struct dict_object *dataobj_s6a_result_code; + struct dict_object *dataobj_s6a_experimental_result; + struct dict_object *dataobj_s6a_vendor_id; + struct dict_object *dataobj_s6a_experimental_result_code; + struct dict_object *dataobj_s6a_auth_session_state; + struct dict_object *dataobj_s6a_authentication_info; + struct dict_object *dataobj_s6a_e_utran_vector; + struct dict_object *dataobj_s6a_rand; + struct dict_object *dataobj_s6a_xres; + struct dict_object *dataobj_s6a_autn; + struct dict_object *dataobj_s6a_kasme; + struct dict_object *dataobj_s6a_ulr_flags; + struct dict_object *dataobj_s6a_ula_flags; + struct dict_object *dataobj_s6a_pur_flags; + struct dict_object *dataobj_s6a_pua_flags; + struct dict_object *dataobj_s6a_rat_type; + struct dict_object *dataobj_s6a_terminal_info; + struct dict_object *dataobj_s6a_ue_srvcc_cap; + struct dict_object *dataobj_s6a_gmlc_addr; + struct dict_object *dataobj_s6a_subscription_data; + struct dict_object *dataobj_s6a_subscriber_status; + struct dict_object *dataobj_s6a_msisdn; + struct dict_object *dataobj_s6a_ambr; + struct dict_object *dataobj_s6a_network_access_mode; + struct dict_object *dataobj_s6a_access_restriction_data; + struct dict_object *dataobj_s6a_apn_configuration_profile; + struct dict_object *dataobj_s6a_subscribed_rau_tau_timer; + struct dict_object *dataobj_s6a_context_identifier; + /* All-APN-Configurations-Included-Indicator */ + struct dict_object *dataobj_s6a_all_apn_conf_inc_ind; + struct dict_object *dataobj_s6a_apn_configuration; + /* Max-Requested-Bandwidth-UL */ + struct dict_object *dataobj_s6a_max_bandwidth_ul; + /* Max-Requested-Bandwidth-DL */ + struct dict_object *dataobj_s6a_max_bandwidth_dl; + struct dict_object *dataobj_s6a_pdn_type; + struct dict_object *dataobj_s6a_service_selection; + struct dict_object *dataobj_s6a_eps_subscribed_qos_profile; + struct dict_object *dataobj_s6a_qos_class_identifier; + struct dict_object *dataobj_s6a_allocation_retention_priority; + struct dict_object *dataobj_s6a_priority_level; + struct dict_object *dataobj_s6a_pre_emption_capability; + struct dict_object *dataobj_s6a_pre_emption_vulnerability; + struct dict_object *dataobj_s6a_served_party_ip_addr; +} s6a_cnf_t; + +/* External definition of the S6A decriptor */ +extern s6a_cnf_t s6a_cnf; + +/* Length of IMSI should not exceed 15 digits */ +#define IMSI_LENGTH (15) +#define IMEI_LENGTH (15) +#define SV_LENGTH (2) + +/* ULR-Flags meaning: */ +#define ULR_SINGLE_REGISTRATION_IND (1U) +#define ULR_S6A_S6D_INDICATOR (1U << 1) +#define ULR_SKIP_SUBSCRIBER_DATA (1U << 2) +#define ULR_GPRS_SUBSCRIPTION_DATA_IND (1U << 3) +#define ULR_NODE_TYPE_IND (1U << 4) +#define ULR_INITIAL_ATTACH_IND (1U << 5) +#define ULR_PS_LCS_SUPPORTED_BY_UE (1U << 6) +#define ULR_PAD_VALID(x) ((x & ~0x7f) == 0) + +/* ULA-Flags */ +#define ULA_SEPARATION_IND (1U) + +/* PUR-Flags */ +#define PUR_UE_PURGED_IN_MME (1U) +#define PUR_UE_PURGED_IN_SGSN (1U << 1) +#define PUR_PAD_VALID(x) ((x & ~0x3) == 0) + +/* PUA-FLAGS */ +#define PUA_FREEZE_M_TMSI (1U) +#define PUA_FREEZE_P_TMSI (1U << 1) + +/* Access-restriction-data bits */ +#define UTRAN_NOT_ALLOWED (1U) +#define GERAN_NOT_ALLOWED (1U << 1) +#define GAN_NOT_ALLOWED (1U << 2) +#define I_HSDPA_EVO_NOT_ALLOWED (1U << 3) +#define E_UTRAN_NOT_ALLOWED (1U << 4) +#define HO_TO_NON_3GPP_NOT_ALLOWED (1U << 5) +#define ARD_CHECK_PAD(x) ((x & ~0x3f) == 0) + +#define FLAG_IS_SET(x, flag) ((x) & (flag)) + +#define FLAGS_SET(x, flags) ((x) |= (flags)) + +#define FLAGS_CLEAR(x, flags) ((x) = (x) & ~(flags)) + +/** \brief Initialize the s6a layer using freeDiameter + * \param hss_config_p pointer the global HSS configuration + * @returns 0 if the init was successfull, != 0 in case of failure + */ +int s6a_init(hss_config_t *hss_config_p); + +/** \brief Callback function for new equipements connections to the HSS. See + * libfdproto from freeDiameter for more informations on the peer validate + * callback + * \param info Peer freeDiameter info + * \param auth Result of the peer authentication, possible values: + * * 1 = accept + * * 0 = unknown + * * -1 = reject + * \param cb2 possible second callback to validate the TLS authentication + * @return 0 if the init was successfull, != 0 in case of failure + */ +int s6a_peer_validate(struct peer_info *info, int *auth, int (**cb2)(struct peer_info *)); + +/* Callback called when corresponding request/answer is received */ +int s6a_auth_info_cb(struct msg **msg, struct avp *paramavp, + struct session *sess, void *opaque, + enum disp_action *act); + +int s6a_up_loc_cb(struct msg **msg, struct avp *paramavp, + struct session *sess, void *opaque, + enum disp_action *act); + +int s6a_purge_ue_cb(struct msg **msg, struct avp *paramavp, + struct session *sess, void *opaque, + enum disp_action *act); + +int s6a_add_subscription_data_avp(struct msg *message, mysql_ul_ans_t *msql_ans); + +int s6a_add_result_code(struct msg *ans, struct avp *failed_avp, + int result_code, int experimental); + +int s6a_add_ipv4_address(struct avp *avp, const char *ipv4_addr); +int s6a_add_ipv6_address(struct avp *avp, const char *ipv6_addr); +inline char *experimental_retcode_2_string(int ret_code); +inline char *retcode_2_string(int ret_code); + +#endif /* S6A_PROTO_H_ */ diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/s6a/s6a_purge_ue.c b/openair-cn/OPENAIRHSS/OPENAIRHSS/s6a/s6a_purge_ue.c new file mode 100644 index 0000000000..0feba40c76 --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/s6a/s6a_purge_ue.c @@ -0,0 +1,140 @@ +/******************************************************************************* + + Eurecom OpenAirInterface + Copyright(c) 1999 - 2013 Eurecom + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information + Openair Admin: openair_admin@eurecom.fr + Openair Tech : openair_tech@eurecom.fr + Forums : http://forums.eurecom.fr/openairinterface + Address : EURECOM, Campus SophiaTech, 450 Route des Chappes + 06410 Biot FRANCE + +*******************************************************************************/ + +/*! \file s6a_purge_ue.c + * \brief Handle a purge UE request and generate the corresponding answer + * \author Sebastien ROUX + * \date 2013 + * \version 0.1 + */ + +#include "hss_config.h" +#include "db_proto.h" +#include "s6a_proto.h" + +int s6a_purge_ue_cb(struct msg **msg, struct avp *paramavp, + struct session *sess, void *opaque, + enum disp_action *act) +{ + struct msg *ans, *qry; + struct avp *avp, *failed_avp = NULL; + struct avp_hdr *hdr; + + int ret = 0; + int result_code = ER_DIAMETER_SUCCESS; + int experimental = 0; + uint32_t pur_flags = 0; + + /* MySQL requests and asnwer data */ + mysql_pu_req_t pu_req; + mysql_pu_ans_t pu_ans; + + if (msg == NULL) { + return EINVAL; + } + + memset(&pu_req, 0, sizeof(mysql_pu_req_t)); + + qry = *msg; + /* Create the answer */ + CHECK_FCT(fd_msg_new_answer_from_req(fd_g_config->cnf_dict, msg, 0)); + ans = *msg; + + /* Retrieving IMSI AVP */ + CHECK_FCT(fd_msg_search_avp(qry, s6a_cnf.dataobj_s6a_imsi, &avp)); + if (avp) { + CHECK_FCT(fd_msg_avp_hdr(avp, &hdr)); + if (hdr->avp_value->os.len > IMSI_LENGTH) { + result_code = ER_DIAMETER_INVALID_AVP_VALUE; + goto out; + } + sprintf(pu_req.imsi, "%*s", (int)hdr->avp_value->os.len, hdr->avp_value->os.data); + } else { + result_code = ER_DIAMETER_MISSING_AVP; + goto out; + } + + /* Retrieving the PUR-Flags if present */ + CHECK_FCT(fd_msg_search_avp(qry, s6a_cnf.dataobj_s6a_pur_flags, &avp)); + if (avp) { + CHECK_FCT(fd_msg_avp_hdr(avp, &hdr)); + pur_flags = hdr->avp_value->u32; + if (FLAG_IS_SET(pur_flags, PUR_UE_PURGED_IN_SGSN)) { + /* This bit shall not be set by a standalone MME. */ + result_code = ER_DIAMETER_INVALID_AVP_VALUE; + goto out; + } + } + + if ((ret = hss_mysql_purge_ue(&pu_req, &pu_ans)) != 0) { + /* We failed to find the IMSI in the database. Replying to the request + * with the user unknown cause. + */ + experimental = 1; + result_code = DIAMETER_ERROR_USER_UNKNOWN; + goto out; + } + + /* Retrieving Origin host AVP */ + CHECK_FCT(fd_msg_search_avp(qry, s6a_cnf.dataobj_s6a_origin_host, &avp)); + if (!avp) { + result_code = ER_DIAMETER_MISSING_AVP; + goto out; + } + /* Retrieve the header from origin host and realm avps */ + CHECK_FCT(fd_msg_avp_hdr(avp, &hdr)); + if (strncmp(pu_ans.mme_host, (char *)hdr->avp_value->os.data, hdr->avp_value->os.len) != 0) + { + result_code = DIAMETER_ERROR_UNKOWN_SERVING_NODE; + experimental = 1; + goto out; + } + + /* Retrieving Origin realm AVP */ + CHECK_FCT(fd_msg_search_avp(qry, s6a_cnf.dataobj_s6a_origin_realm, &avp)); + if (!avp) { + result_code = ER_DIAMETER_MISSING_AVP; + goto out; + } + CHECK_FCT(fd_msg_avp_hdr(avp, &hdr)); + if (strncmp(pu_ans.mme_realm, (char *)hdr->avp_value->os.data, hdr->avp_value->os.len) != 0) + { + result_code = DIAMETER_ERROR_UNKOWN_SERVING_NODE; + experimental = 1; + goto out; + } + +out: + /* Append the result code to the answer */ + CHECK_FCT(s6a_add_result_code(ans, failed_avp, result_code, experimental)); + + CHECK_FCT(fd_msg_send(msg, NULL, NULL )); + return 0; +} diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/s6a/s6a_subscription_data.c b/openair-cn/OPENAIRHSS/OPENAIRHSS/s6a/s6a_subscription_data.c new file mode 100644 index 0000000000..a830748a72 --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/s6a/s6a_subscription_data.c @@ -0,0 +1,276 @@ +/******************************************************************************* + + Eurecom OpenAirInterface + Copyright(c) 1999 - 2013 Eurecom + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information + Openair Admin: openair_admin@eurecom.fr + Openair Tech : openair_tech@eurecom.fr + Forums : http://forums.eurecom.fr/openairinterface + Address : EURECOM, Campus SophiaTech, 450 Route des Chappes + 06410 Biot FRANCE + +*******************************************************************************/ + +#include "hss_config.h" +#include "db_proto.h" +#include "s6a_proto.h" + +/*! \file s6a_subscription_data.c + * \brief Add the subscription data to a message. Data are retrieved from database + * \author Sebastien ROUX + * \date 2013 + * \version 0.1 + */ + +int s6a_add_subscription_data_avp(struct msg *message, mysql_ul_ans_t *mysql_ans) +{ + int ret = -1, i; + mysql_pdn_t *pdns; + uint8_t nb_pdns; + struct avp *avp, *child_avp; + union avp_value value; + + if (mysql_ans == NULL) { + return -1; + } + + ret = hss_mysql_query_pdns(mysql_ans->imsi, &pdns, &nb_pdns); + + if (ret != 0) { + /* mysql query failed: + * - maybe no more memory + * - maybe user is not known (should have failed before) + * - maybe imsi has no EPS subscribed + */ + goto out; + } + + if (nb_pdns == 0) + { + /* No PDN for this user -> DIAMETER_ERROR_UNKNOWN_EPS_SUBSCRIPTION */ + return -1; + } + + /* Create the Subscription-Data AVP */ + CHECK_FCT(fd_msg_avp_new(s6a_cnf.dataobj_s6a_subscription_data, 0, &avp)); + + { + uint8_t msisdn_len = strlen(mysql_ans->msisdn); + /* The MSISDN is known in the HSS, add it to the subscription data */ + if (msisdn_len > 0) { + CHECK_FCT(fd_msg_avp_new(s6a_cnf.dataobj_s6a_msisdn, 0, &child_avp)); + value.os.data = (uint8_t *)mysql_ans->msisdn; + value.os.len = msisdn_len; + CHECK_FCT(fd_msg_avp_setvalue(child_avp, &value)); + CHECK_FCT(fd_msg_avp_add(avp, MSG_BRW_LAST_CHILD, child_avp)); + } + } + + /* We have to include the acess-restriction-data if the value stored in DB + * indicates that at least one restriction is applied to the USER. + */ + if (mysql_ans->access_restriction != 0) { + CHECK_FCT(fd_msg_avp_new(s6a_cnf.dataobj_s6a_access_restriction_data, 0, &child_avp)); + value.u32 = (uint32_t)mysql_ans->access_restriction; + CHECK_FCT(fd_msg_avp_setvalue(child_avp, &value)); + CHECK_FCT(fd_msg_avp_add(avp, MSG_BRW_LAST_CHILD, child_avp)); + } + + /* Add the Subscriber-Status to the list of AVP. + * It shall indicate if the service is barred or granted. + * TODO: normally this parameter comes from DB... + */ + CHECK_FCT(fd_msg_avp_new(s6a_cnf.dataobj_s6a_subscriber_status, 0, &child_avp)); + /* SERVICE_GRANTED */ + value.u32 = 0; + CHECK_FCT(fd_msg_avp_setvalue(child_avp, &value)); + CHECK_FCT(fd_msg_avp_add(avp, MSG_BRW_LAST_CHILD, child_avp)); + + /* Add the Network-Access-Mode to the list of AVP. + * LTE Standalone HSS/MME: ONLY_PACKET. + */ + CHECK_FCT(fd_msg_avp_new(s6a_cnf.dataobj_s6a_network_access_mode, 0, &child_avp)); + /* SERVICE_GRANTED */ + value.u32 = 2; + CHECK_FCT(fd_msg_avp_setvalue(child_avp, &value)); + CHECK_FCT(fd_msg_avp_add(avp, MSG_BRW_LAST_CHILD, child_avp)); + + /* Add the AMBR to list of AVPs */ + { + struct avp *bandwidth; + CHECK_FCT(fd_msg_avp_new(s6a_cnf.dataobj_s6a_ambr, 0, &child_avp)); + + /* Uplink bandwidth */ + CHECK_FCT(fd_msg_avp_new(s6a_cnf.dataobj_s6a_max_bandwidth_ul, 0, &bandwidth)); + value.u32 = mysql_ans->aggr_ul; + CHECK_FCT(fd_msg_avp_setvalue(bandwidth, &value)); + CHECK_FCT(fd_msg_avp_add(child_avp, MSG_BRW_LAST_CHILD, bandwidth)); + + /* Downlink bandwidth */ + CHECK_FCT(fd_msg_avp_new(s6a_cnf.dataobj_s6a_max_bandwidth_dl, 0, &bandwidth)); + value.u32 = mysql_ans->aggr_dl; + CHECK_FCT(fd_msg_avp_setvalue(bandwidth, &value)); + CHECK_FCT(fd_msg_avp_add(child_avp, MSG_BRW_LAST_CHILD, bandwidth)); + + CHECK_FCT(fd_msg_avp_add(avp, MSG_BRW_LAST_CHILD, child_avp)); + } + + /* Add the APN-Configuration-Profile only if at least one APN is subscribed */ + if (nb_pdns > 0) + { + struct avp *apn_profile; + CHECK_FCT(fd_msg_avp_new(s6a_cnf.dataobj_s6a_apn_configuration_profile, + 0, &apn_profile)); + + /* Context-Identifier */ + CHECK_FCT(fd_msg_avp_new(s6a_cnf.dataobj_s6a_context_identifier, 0, &child_avp)); + value.u32 = 0; + /* TODO: this is the reference to the default APN... */ + CHECK_FCT(fd_msg_avp_setvalue(child_avp, &value)); + CHECK_FCT(fd_msg_avp_add(apn_profile, MSG_BRW_LAST_CHILD, child_avp)); + + /* All-APN-Configurations-Included-Indicator */ + CHECK_FCT(fd_msg_avp_new(s6a_cnf.dataobj_s6a_all_apn_conf_inc_ind, 0, &child_avp)); + value.u32 = 0; + CHECK_FCT(fd_msg_avp_setvalue(child_avp, &value)); + CHECK_FCT(fd_msg_avp_add(apn_profile, MSG_BRW_LAST_CHILD, child_avp)); + + for (i = 0; i < nb_pdns; i++) + { + struct avp *apn_configuration; + mysql_pdn_t *pdn_elm; + + pdn_elm = &pdns[i]; + + /* APN-Configuration */ + CHECK_FCT(fd_msg_avp_new(s6a_cnf.dataobj_s6a_apn_configuration, 0, &apn_configuration)); + + /* Context-Identifier */ + CHECK_FCT(fd_msg_avp_new(s6a_cnf.dataobj_s6a_context_identifier, 0, &child_avp)); + value.u32 = i; + CHECK_FCT(fd_msg_avp_setvalue(child_avp, &value)); + CHECK_FCT(fd_msg_avp_add(apn_configuration, MSG_BRW_LAST_CHILD, child_avp)); + + /* PDN-Type */ + CHECK_FCT(fd_msg_avp_new(s6a_cnf.dataobj_s6a_pdn_type, 0, &child_avp)); + value.u32 = pdn_elm->pdn_type; + CHECK_FCT(fd_msg_avp_setvalue(child_avp, &value)); + CHECK_FCT(fd_msg_avp_add(apn_configuration, MSG_BRW_LAST_CHILD, child_avp)); + + if ((pdn_elm->pdn_type == IPV4) || + (pdn_elm->pdn_type == IPV4_OR_IPV6) || + (pdn_elm->pdn_type == IPV4V6)) + { + s6a_add_ipv4_address(apn_configuration, pdn_elm->pdn_address.ipv4_address); + } + if ((pdn_elm->pdn_type == IPV6) || + (pdn_elm->pdn_type == IPV4_OR_IPV6) || + (pdn_elm->pdn_type == IPV4V6)) + { + s6a_add_ipv6_address(apn_configuration, pdn_elm->pdn_address.ipv6_address); + } + + /* Service-Selection */ + CHECK_FCT(fd_msg_avp_new(s6a_cnf.dataobj_s6a_service_selection, 0, &child_avp)); + value.os.data = (uint8_t*)pdn_elm->apn; + value.os.len = strlen(pdn_elm->apn); + CHECK_FCT(fd_msg_avp_setvalue(child_avp, &value)); + CHECK_FCT(fd_msg_avp_add(apn_configuration, MSG_BRW_LAST_CHILD, child_avp)); + + /* Add the eps subscribed qos profile */ + { + struct avp *qos_profile, *allocation_priority; + CHECK_FCT(fd_msg_avp_new(s6a_cnf.dataobj_s6a_eps_subscribed_qos_profile, 0, &qos_profile)); + + CHECK_FCT(fd_msg_avp_new(s6a_cnf.dataobj_s6a_qos_class_identifier, 0, &child_avp)); + /* For a QCI_1 */ + value.u32 = (uint32_t)pdn_elm->qci; + CHECK_FCT(fd_msg_avp_setvalue(child_avp, &value)); + CHECK_FCT(fd_msg_avp_add(qos_profile, MSG_BRW_LAST_CHILD, child_avp)); + + /* Allocation retention priority */ + { + CHECK_FCT(fd_msg_avp_new(s6a_cnf.dataobj_s6a_allocation_retention_priority, 0, &allocation_priority)); + + /* Priority level */ + CHECK_FCT(fd_msg_avp_new(s6a_cnf.dataobj_s6a_priority_level, 0, &child_avp)); + value.u32 = (uint32_t)pdn_elm->priority_level; + CHECK_FCT(fd_msg_avp_setvalue(child_avp, &value)); + CHECK_FCT(fd_msg_avp_add(allocation_priority, MSG_BRW_LAST_CHILD, child_avp)); + + /* Pre-emption-capability */ + CHECK_FCT(fd_msg_avp_new(s6a_cnf.dataobj_s6a_pre_emption_capability, 0, &child_avp)); + value.u32 = (uint32_t)pdn_elm->pre_emp_cap; + CHECK_FCT(fd_msg_avp_setvalue(child_avp, &value)); + CHECK_FCT(fd_msg_avp_add(allocation_priority, MSG_BRW_LAST_CHILD, child_avp)); + + /* Pre-emption-vulnerability */ + CHECK_FCT(fd_msg_avp_new(s6a_cnf.dataobj_s6a_pre_emption_vulnerability, 0, &child_avp)); + value.u32 = (uint32_t)pdn_elm->pre_emp_vul; + CHECK_FCT(fd_msg_avp_setvalue(child_avp, &value)); + CHECK_FCT(fd_msg_avp_add(allocation_priority, MSG_BRW_LAST_CHILD, child_avp)); + + CHECK_FCT(fd_msg_avp_add(qos_profile, MSG_BRW_LAST_CHILD, allocation_priority)); + } + CHECK_FCT(fd_msg_avp_add(apn_configuration, MSG_BRW_LAST_CHILD, qos_profile)); + } + + /* Add the AMBR to list of AVPs */ + { + struct avp *bandwidth; + CHECK_FCT(fd_msg_avp_new(s6a_cnf.dataobj_s6a_ambr, 0, &bandwidth)); + + /* Uplink bandwidth */ + CHECK_FCT(fd_msg_avp_new(s6a_cnf.dataobj_s6a_max_bandwidth_ul, 0, &child_avp)); + value.u32 = (uint32_t)pdn_elm->aggr_ul; + CHECK_FCT(fd_msg_avp_setvalue(child_avp, &value)); + CHECK_FCT(fd_msg_avp_add(bandwidth, MSG_BRW_LAST_CHILD, child_avp)); + + /* Downlink bandwidth */ + CHECK_FCT(fd_msg_avp_new(s6a_cnf.dataobj_s6a_max_bandwidth_dl, 0, &child_avp)); + value.u32 = (uint32_t)pdn_elm->aggr_dl; + CHECK_FCT(fd_msg_avp_setvalue(child_avp, &value)); + CHECK_FCT(fd_msg_avp_add(bandwidth, MSG_BRW_LAST_CHILD, child_avp)); + + CHECK_FCT(fd_msg_avp_add(apn_configuration, MSG_BRW_LAST_CHILD, bandwidth)); + } + + CHECK_FCT(fd_msg_avp_add(apn_profile, MSG_BRW_LAST_CHILD, apn_configuration)); + } + + CHECK_FCT(fd_msg_avp_add(avp, MSG_BRW_LAST_CHILD, apn_profile)); + } + + /* Subscribed-Periodic-RAU-TAU-Timer */ + CHECK_FCT(fd_msg_avp_new(s6a_cnf.dataobj_s6a_subscribed_rau_tau_timer, 0, &child_avp)); + /* Request an RAU/TAU update every x seconds */ + value.u32 = (uint32_t)mysql_ans->rau_tau; + CHECK_FCT(fd_msg_avp_setvalue(child_avp, &value)); + CHECK_FCT(fd_msg_avp_add(avp, MSG_BRW_LAST_CHILD, child_avp)); + + /* Add the AVP to the message */ + CHECK_FCT(fd_msg_avp_add(message, MSG_BRW_LAST_CHILD, avp)); + +out: + if (pdns) { + free(pdns); + } + return ret; +} diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/s6a/s6a_supported_features.c b/openair-cn/OPENAIRHSS/OPENAIRHSS/s6a/s6a_supported_features.c new file mode 100644 index 0000000000..f12fa8359f --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/s6a/s6a_supported_features.c @@ -0,0 +1,41 @@ +/******************************************************************************* + + Eurecom OpenAirInterface + Copyright(c) 1999 - 2013 Eurecom + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information + Openair Admin: openair_admin@eurecom.fr + Openair Tech : openair_tech@eurecom.fr + Forums : http://forums.eurecom.fr/openairinterface + Address : EURECOM, Campus SophiaTech, 450 Route des Chappes + 06410 Biot FRANCE + +*******************************************************************************/ + +/*! \file s6a_supported_features.c + * \brief + * \author Sebastien ROUX + * \date 2013 + * \version 0.1 + */ + +#include "hss_config.h" +#include "db_proto.h" +#include "s6a_proto.h" + diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/s6a/s6a_supported_features.h b/openair-cn/OPENAIRHSS/OPENAIRHSS/s6a/s6a_supported_features.h new file mode 100644 index 0000000000..2b00bb913a --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/s6a/s6a_supported_features.h @@ -0,0 +1,142 @@ +/******************************************************************************* + + Eurecom OpenAirInterface + Copyright(c) 1999 - 2013 Eurecom + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information + Openair Admin: openair_admin@eurecom.fr + Openair Tech : openair_tech@eurecom.fr + Forums : http://forums.eurecom.fr/openairinterface + Address : EURECOM, Campus SophiaTech, 450 Route des Chappes + 06410 Biot FRANCE + +*******************************************************************************/ + +#include "hss_config.h" + +#ifndef S6A_SUPPORTED_FEATURES_H_ +#define S6A_SUPPORTED_FEATURES_H_ + +/* Operator Determined Barring of all Packet Oriented Services. */ +#define ODB_ALL_APN(x) (x & 0x1) + +/* Operator Determined Barring of Packet Oriented Services from access points + * that are within the HPLMN whilst the subscriber is roaming in a VPLMN. + */ +#define ODB_HPLMN_APN(x) ((x & 0x2) >> 1) + +/* Operator Determined Barring of Packet Oriented Services from access points + * that are within the roamed to VPLMN + */ +#define ODB_VPLMN_APN(x) ((x & 0x4) >> 2) + +/* Operator Determined Barring of all outgoing calls */ +#define ODB_ALL_OG(x) ((x & 0x8) >> 3) + +/* Operator Determined Barring of all outgoing international calls */ +#define ODB_ALL_INT_OG ((x & 0x10) >> 4) + +/* Operator Determined Barring of all outgoing international calls except those + * directed to the home PLMN country + */ +#define ODB_ALL_INT_NOT_TO_HPLMN(x) ((x & 0x20) >> 5) + +/* Operator Determined Barring of all outgoing inter-zonal calls */ +#define ODB_ALL_INT_ZONE_OG(x) ((x & 0x40) >> 6) + +/* Operator Determined Barring of all outgoing inter-zonal calls except those + * directed to the home PLMN country + */ +#define ODB_ALL_INT_ZONE_OG_NOT_TO_HPLMN(x) ((x & 0x80) >> 7) + +/* Operator Determined Barring of all outgoing international calls except those + * directed to the home PLMN country and Barring of all outgoing inter-zonal calls + */ +#define ODB_ALL_INT_ZONE_OG_AND_INT_OG_NOT_TO_HPLMN(x) ((x & 0x100) >> 8) + +/* Regional Subscription */ +#define REG_SUB(x) ((x & 0x200) >> 9) + +/* Trace Function */ +#define TRACE(x) ((x & 0x400) >> 10) + +/* All LCS Privacy Exception Classes */ +#define LCS_ALL_PRIV_EXCEP(x) ((x & 0x800) >> 11) + +/* Allow location by any LCS client */ +#define LCS_UNIVERSAL(x) ((x & 0x1000) >> 12) + +/* Allow location by any value added LCS client to which a call/session is + * established from the target UE + */ +#define LCS_CALL_SESS_RELATED(x) ((x & 0x2000) >> 13) + +/* Allow location by designated external value added LCS clients */ +#define LCS_CALL_SESS_UNRELATED(x) ((x & 0x4000) >> 14) + +/* Allow location by designated PLMN operator LCS clients */ +#define LCS_PLMN_OPERATOR(x) ((x & 0x8000) >> 15) + +/* Allow location by LCS clients of a designated LCS service type */ +#define LCS_SERVICE_TYPE(x) ((x & 0x10000) >> 16) + +/* All Mobile Originating Location Request Classes */ +#define LCS_ALL_MOLR_SS(x) ((x & 0x20000) >> 17) + +/* Allow an MS to request its own location */ +#define LCS_BASIC_SELF_LOCATION(x) ((x & 0x40000) >> 18) + +/* Allow an MS to perform self location without interaction with the PLMN */ +#define LCS_AUTO_SELF_LOCATION(x) ((x & 0x80000) >> 19) + +/* Allow an MS to request transfer of its location to another LCS client */ +#define LCS_TRANSFER_TO_THIRD_PARTY(x) ((x & 0x100000) >> 20) + +/* Short Message MO-PP */ +#define SM_MO_PP(x) ((x & 0x200000) >> 21) + +/* Barring of Outgoing Calls */ +#define BARRING_OUTGOING_CALLS(x) ((x & 0x400000) >> 22) + +/* Barring of all outgoing calls */ +#define BAOC(x) ((x & 0x800000) >> 23) + +/* Barring of outgoing international calls */ +#define BOIC(x) ((x & 0x1000000) >> 24) + +/* Barring of outgoing international calls except those directed to the home PLMN + * Country + */ +#define BOIC_EXCEPT_HC(x) ((x & 0x2000000) >> 25) + +/* UE Reachability Notifcation */ +#define UE_REACH_NOTIF(x) ((x & 0x4000000) >> 26) + +/* Terminating Access Domain Selection Data Retrieval */ +#define T_ADS_DATA_RETR(x) ((x & 0x8000000) >> 27) + +/* State/Location Information Retrieval */ +#define STATE_LOCATION_INFO_RETR(x) ((x & 0x10000000) >> 28) + +/* Partial Purge from a Combined MME/SGSN */ +#define PARTIAL_PURGE(x) ((x & 0x20000000) >> 29) + +#define SUPP_FEAT_PAD_VALID(x) ((x & 0xfc000000) == 0) + +#endif /* S6A_SUPPORTED_FEATURES_H_ */ diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/s6a/s6a_up_loc.c b/openair-cn/OPENAIRHSS/OPENAIRHSS/s6a/s6a_up_loc.c new file mode 100644 index 0000000000..537c9297db --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/s6a/s6a_up_loc.c @@ -0,0 +1,373 @@ +/******************************************************************************* + + Eurecom OpenAirInterface + Copyright(c) 1999 - 2013 Eurecom + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information + Openair Admin: openair_admin@eurecom.fr + Openair Tech : openair_tech@eurecom.fr + Forums : http://forums.eurecom.fr/openairinterface + Address : EURECOM, Campus SophiaTech, 450 Route des Chappes + 06410 Biot FRANCE + +*******************************************************************************/ + +/*! \file s6a_up_loc.c + * \brief Handle an update location message and create the answer. + * \author Sebastien ROUX + * \date 2013 + * \version 0.1 + */ + +#include "hss_config.h" +#include "db_proto.h" +#include "s6a_proto.h" +#include "access_restriction.h" + +int s6a_up_loc_cb(struct msg **msg, struct avp *paramavp, + struct session *sess, void *opaque, + enum disp_action *act) +{ + struct msg *ans, *qry; + struct avp *avp, *origin_host, *origin_realm; + struct avp *failed_avp = NULL; + + struct avp_hdr *origin_host_hdr, *origin_realm_hdr; + struct avp_hdr *hdr; + + union avp_value value; + + int ret = 0; + int result_code = ER_DIAMETER_SUCCESS; + int experimental = 0; + uint32_t ulr_flags = 0; + + mysql_ul_ans_t mysql_ans; + mysql_ul_push_t mysql_push; + + if (msg == NULL) { + return EINVAL; + } + + memset(&mysql_push, 0, sizeof(mysql_ul_push_t)); + memset(&mysql_ans, 0, sizeof(mysql_ul_ans_t)); + + fprintf(stdout, "Received new update location request\n"); + + qry = *msg; + /* Create the answer */ + CHECK_FCT(fd_msg_new_answer_from_req(fd_g_config->cnf_dict, msg, 0)); + ans = *msg; + + /* Retrieving IMSI AVP */ + CHECK_FCT(fd_msg_search_avp(qry, s6a_cnf.dataobj_s6a_imsi, &avp)); + if (avp) { + CHECK_FCT(fd_msg_avp_hdr(avp, &hdr)); + if (hdr->avp_value->os.len > IMSI_LENGTH) { + result_code = ER_DIAMETER_INVALID_AVP_VALUE; + goto out; + } + sprintf(mysql_push.imsi, "%*s", (int)hdr->avp_value->os.len, + (char*)hdr->avp_value->os.data); + if ((ret = hss_mysql_update_loc(mysql_push.imsi, &mysql_ans)) != 0) { + /* We failed to find the IMSI in the database. Replying to the request + * with the user unknown cause. + */ + experimental = 1; + result_code = DIAMETER_ERROR_USER_UNKNOWN; + goto out; + } + } else { + fprintf(stderr, "Cannot get IMSI AVP which is mandatory\n"); + result_code = ER_DIAMETER_MISSING_AVP; + goto out; + } + + /* Retrieving Origin host AVP */ + CHECK_FCT(fd_msg_search_avp(qry, s6a_cnf.dataobj_s6a_origin_host, &origin_host)); + if (!origin_host) { + result_code = ER_DIAMETER_MISSING_AVP; + goto out; + } + + /* Retrieving Origin realm AVP */ + CHECK_FCT(fd_msg_search_avp(qry, s6a_cnf.dataobj_s6a_origin_realm, &origin_realm)); + if (!origin_realm) { + result_code = ER_DIAMETER_MISSING_AVP; + goto out; + } + + /* Retrieve the header from origin host and realm avps */ + CHECK_FCT(fd_msg_avp_hdr(origin_host, &origin_host_hdr)); + CHECK_FCT(fd_msg_avp_hdr(origin_realm, &origin_realm_hdr)); + + sprintf(mysql_push.mme_identity.mme_host, "%*s", + (int)origin_host_hdr->avp_value->os.len, + (char*)origin_host_hdr->avp_value->os.data); + + sprintf(mysql_push.mme_identity.mme_realm, "%*s", + (int)origin_realm_hdr->avp_value->os.len, + (char*)origin_realm_hdr->avp_value->os.data); + + /* Retrieving RAT type AVP */ + CHECK_FCT(fd_msg_search_avp(qry, s6a_cnf.dataobj_s6a_rat_type, &avp)); + if (avp) { + CHECK_FCT(fd_msg_avp_hdr(avp, &hdr)); + /* As we are in E-UTRAN stand-alone HSS, we have to reject incoming + * location request with a RAT-Type != than E-UTRAN. + * The user may be disallowed to use the specified RAT, check the access + * restriction bit mask received from DB. + */ + if ((hdr->avp_value->u32 != 1004) || + (FLAG_IS_SET(mysql_ans.access_restriction, E_UTRAN_NOT_ALLOWED))) + { + experimental = 1; + result_code = DIAMETER_ERROR_RAT_NOT_ALLOWED; + goto out; + } + } else { + result_code = ER_DIAMETER_MISSING_AVP; + goto out; + } + + /* Retrieving ULR Flags AVP */ + CHECK_FCT(fd_msg_search_avp(qry, s6a_cnf.dataobj_s6a_ulr_flags, &avp)); + if (avp) { + CHECK_FCT(fd_msg_avp_hdr(avp, &hdr)); + ulr_flags = hdr->avp_value->u32; + /* Check the flags received */ + if (FLAG_IS_SET(ulr_flags, ULR_SINGLE_REGISTRATION_IND)) { + /* We don't handle cases where we have to inform SGSN */ + result_code = ER_DIAMETER_INVALID_AVP_VALUE; + + fprintf(stderr, "ULR single registration bit set (SGSN to MME): " + "not handled by standalone E-UTRAN HSS\n"); + goto out; + } + if (!FLAG_IS_SET(ulr_flags, ULR_S6A_S6D_INDICATOR)) { + /* The request is coming from s6d interface (SGSN). */ + result_code = ER_DIAMETER_INVALID_AVP_VALUE; + + fprintf(stderr, "ULR S6D bit set: " + "not handled by standalone E-UTRAN HSS\n"); + goto out; + } + if (FLAG_IS_SET(ulr_flags, ULR_NODE_TYPE_IND)) { + /* Request coming from combined SGSN/MME. */ + result_code = ER_DIAMETER_INVALID_AVP_VALUE; + fprintf(stderr, "ULR conbined SGSN/MME bit set: " + "not handled by standalone E-UTRAN HSS\n"); + goto out; + } + if (FLAG_IS_SET(ulr_flags, ULR_INITIAL_ATTACH_IND)) { + /* This bit, when set, indicates that the HSS shall send Cancel + * Location to the MME or SGSN if there is the MME or SGSN + * registration. + */ + // TODO: check if an MME is already registered, serving the UE. + // If so, it should be informed. + + /* The identity of the MME will be added to db */ + mysql_push.mme_identity_present = MME_IDENTITY_PRESENT; + } else { + /* The bit is not set, we are expecting that the mme contained in db + * matches the original MME. + */ + if ((mysql_ans.mme_identity.mme_host != NULL) && + (mysql_ans.mme_identity.mme_realm != NULL)) + { + /* Compare if values match expected */ + if (memcmp(mysql_ans.mme_identity.mme_host, origin_host_hdr->avp_value->os.data, + origin_host_hdr->avp_value->os.len > strlen(mysql_ans.mme_identity.mme_host) ? + strlen(mysql_ans.mme_identity.mme_host) : origin_host_hdr->avp_value->os.len) != 0) + { + experimental = 1; + result_code = DIAMETER_ERROR_UNKOWN_SERVING_NODE; + goto out; + } + if (memcmp(mysql_ans.mme_identity.mme_realm, origin_realm_hdr->avp_value->os.data, + origin_realm_hdr->avp_value->os.len > strlen(mysql_ans.mme_identity.mme_realm) ? + strlen(mysql_ans.mme_identity.mme_realm) : origin_realm_hdr->avp_value->os.len) != 0) + { + experimental = 1; + result_code = DIAMETER_ERROR_UNKOWN_SERVING_NODE; + goto out; + } + } else { + /* Failed to retrieve current serving MME and the ULR is not + * marked as an initial attach indication... + */ + experimental = 1; + result_code = DIAMETER_ERROR_UNKOWN_SERVING_NODE; + goto out; + } + } + if (!ULR_PAD_VALID(ulr_flags)) { + /* Padding is not zero'ed, may be the MME/SGSN supports newer + * release. Inform it. + */ + result_code = ER_DIAMETER_INVALID_AVP_VALUE; + goto out; + } + } else { + result_code = ER_DIAMETER_MISSING_AVP; + goto out; + } + + /* Retrieving Visited-PLMN-Id AVP */ + CHECK_FCT(fd_msg_search_avp(qry, s6a_cnf.dataobj_s6a_visited_plmn_id, &avp)); + if (avp) { + CHECK_FCT(fd_msg_avp_hdr(avp, &hdr)); + /* Roaming cases are not allowed for now. + * Reject the connectivity if PLMN visited and IMSI PLMN disagree. + */ + /* TODO */ + if (hdr->avp_value->os.len == 3) { +// if (apply_access_restriction(mysql_push.imsi, hdr->avp_value->os.data) != 0) { +// /* We found that user is roaming and has no right to do it -> +// * reject the connection +// */ +// result_code = DIAMETER_ERROR_ROAMING_NOT_ALLOWED; +// experimental = 1; +// goto out; +// } + } else { + result_code = ER_DIAMETER_INVALID_AVP_VALUE; + goto out; + } + } else { + result_code = ER_DIAMETER_MISSING_AVP; + goto out; + } + + /* Retrieving Terminal-Information AVP */ + CHECK_FCT(fd_msg_search_avp(qry, s6a_cnf.dataobj_s6a_terminal_info, &avp)); + if (avp) { + struct avp *child_avp; + + /* Walk through childs avp */ + CHECK_FCT(fd_msg_browse(avp, MSG_BRW_FIRST_CHILD, &child_avp, NULL)); + while (child_avp) { + /* Retrieve the header of the child avp */ + CHECK_FCT(fd_msg_avp_hdr(child_avp, &hdr)); + switch(hdr->avp_code) { + case AVP_CODE_IMEI: { + /* Check that we do not exceed the maximum size for IMEI */ + if (hdr->avp_value->os.len > IMEI_LENGTH) { + result_code = ER_DIAMETER_INVALID_AVP_VALUE; + failed_avp = child_avp; + goto out; + } + sprintf(mysql_push.imei, "%*s", (int)hdr->avp_value->os.len, + hdr->avp_value->os.data); + mysql_push.imei_present = IMEI_PRESENT; + } break; + case AVP_CODE_SOFTWARE_VERSION: { + /* Check the size for SV */ + if (hdr->avp_value->os.len != SV_LENGTH) { + result_code = ER_DIAMETER_INVALID_AVP_VALUE; + failed_avp = child_avp; + goto out; + } + mysql_push.software_version[0] = hdr->avp_value->os.data[0]; + mysql_push.software_version[1] = hdr->avp_value->os.data[1]; + mysql_push.sv_present = SV_PRESENT; + } break; + default: /* Fall through */ + case AVP_CODE_3GPP2_MEID: { + /* This AVP is not expected on s6a interface */ + result_code = ER_DIAMETER_AVP_UNSUPPORTED; + failed_avp = child_avp; + goto out; + } + } + /* Go to next AVP in the grouped AVP */ + CHECK_FCT(fd_msg_browse(child_avp, MSG_BRW_NEXT, &child_avp, NULL)); + } + } + + /* Retrieving UE-SRVCC AVP */ + CHECK_FCT(fd_msg_search_avp(qry, s6a_cnf.dataobj_s6a_ue_srvcc_cap, &avp)); + if (avp) { + CHECK_FCT(fd_msg_avp_hdr(avp, &hdr)); + mysql_push.ue_srvcc_present = UE_SRVCC_PRESENT; + mysql_push.ue_srvcc = hdr->avp_value->u32; + } + + /* Retrieving Supported features list AVP */ + CHECK_FCT(fd_msg_search_avp(qry, s6a_cnf.dataobj_s6a_supported_features, &avp)); + if (avp) { + struct avp *child_avp; + + /* Walk through childs avp */ + CHECK_FCT(fd_msg_browse(avp, MSG_BRW_FIRST_CHILD, &child_avp, NULL)); + while (child_avp) { + /* Retrieve the header of the child avp */ + CHECK_FCT(fd_msg_avp_hdr(child_avp, &hdr)); + switch(hdr->avp_code) { + case AVP_CODE_VENDOR_ID: { + if (hdr->avp_value->u32 != VENDOR_3GPP) { + /* features from a vendor other than 3GPP is not supported */ + fprintf(stderr, "Cannot interpret features list with vendor id " + "different than 3GPP(%d)\n", VENDOR_3GPP); + continue; + } + } break; + case AVP_CODE_FEATURE_LIST: { + mysql_push.mme_supported_features_present = MME_SUPPORTED_FEATURES_PRESENT; + mysql_push.mme_supported_features = hdr->avp_value->u32; + } break; + } + /* Go to next AVP in the grouped AVP */ + CHECK_FCT(fd_msg_browse(child_avp, MSG_BRW_NEXT, &child_avp, NULL)); + } + } + + mysql_push_up_loc(&mysql_push); + + /* ULA flags */ + CHECK_FCT(fd_msg_avp_new(s6a_cnf.dataobj_s6a_ula_flags, 0, &avp)); + value.u32 = 1; + CHECK_FCT(fd_msg_avp_setvalue(avp, &value)); + CHECK_FCT(fd_msg_avp_add(ans, MSG_BRW_LAST_CHILD, avp)); + + /* Only add the subscriber data if not marked as skipped by MME */ + if (!FLAG_IS_SET(ulr_flags, ULR_SKIP_SUBSCRIBER_DATA)) { + if (s6a_add_subscription_data_avp(ans, &mysql_ans) != 0) { + result_code = DIAMETER_ERROR_UNKNOWN_EPS_SUBSCRIPTION; + experimental = 1; + goto out; + } + } + +out: + /* Add the Auth-Session-State AVP */ + CHECK_FCT(fd_msg_search_avp(qry, s6a_cnf.dataobj_s6a_auth_session_state, &avp)); + CHECK_FCT(fd_msg_avp_hdr(avp, &hdr)); + + CHECK_FCT(fd_msg_avp_new(s6a_cnf.dataobj_s6a_auth_session_state, 0, &avp)); + CHECK_FCT(fd_msg_avp_setvalue(avp, hdr->avp_value)); + CHECK_FCT(fd_msg_avp_add(ans, MSG_BRW_LAST_CHILD, avp)); + + /* Append the result code to the answer */ + CHECK_FCT(s6a_add_result_code(ans, failed_avp, result_code, experimental)); + + CHECK_FCT(fd_msg_send(msg, NULL, NULL )); + return 0; +} diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/tests/Makefile.am b/openair-cn/OPENAIRHSS/OPENAIRHSS/tests/Makefile.am new file mode 100644 index 0000000000..a924164810 --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/tests/Makefile.am @@ -0,0 +1,60 @@ +# if HAVE_CHECK +# TESTS = up_loc_test +# else +# TESTS = +# endif +AM_CFLAGS = @CHECK_CFLAGS@ \ + -I$(top_srcdir)/auc + +if HAVE_CHECK +TESTS = up_loc_test test_security_f1 \ + test_security_f2_f3_f5 \ + test_security_f4_f5star \ + test_kdf \ + test_security_kasme +else +TESTS = +endif + +test_common = \ + $(top_builddir)/auc/libauc.la \ + libtest_utils.la \ + @CHECK_LIBS@ + +up_loc_test_LDADD = \ + libtest_utils.la \ + @CHECK_LIBS@ + +test_security_f1_LDADD = \ + $(test_common) + +test_security_f2_f3_f5_LDADD = \ + $(test_common) + +test_security_f4_f5star_LDADD = \ + $(test_common) + +test_security_kasme_LDADD = \ + $(test_common) + +test_kdf_LDADD = \ + $(test_common) + +noinst_LTLIBRARIES = libtest_utils.la +libtest_utils_la_SOURCES = \ + test_utils.h test_utils.c \ + test_fd.h test_fd.c + +check_PROGRAMS = \ + up_loc_test test_security_f1 \ + test_security_f2_f3_f5 \ + test_security_f4_f5star \ + test_security_kasme \ + test_kdf + +up_loc_test_SOURCES = up_loc_test.c +test_security_f1_SOURCE = test_security_f1.c +test_security_f2_f3_f5_SOURCE = test_security_f2_f3_f5.c +test_security_f4_f5star_SOURCE = test_security_f4_f5star.c +test_security_kasme_SOURCE = test_security_kasme.c +test_kdf_SOURCE = test_kdf.c \ No newline at end of file diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/tests/test_fd.c b/openair-cn/OPENAIRHSS/OPENAIRHSS/tests/test_fd.c new file mode 100644 index 0000000000..528849a759 --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/tests/test_fd.c @@ -0,0 +1,55 @@ + +#include "config.h" + +#include +#include + +#include "test_utils.h" +#include "test_fd.h" + +extern int fd_ext_add( char * filename, char * conffile ); + +void s6a_fd_init(void) +{ + struct peer_info peer; + + fd_g_debug_lvl = NONE; + + memset(&peer, 0, sizeof(struct peer_info)); + + peer.pi_diamid = "hss.test.fr"; + peer.pi_diamidlen = strlen(peer.pi_diamid); + + /* Only SCTP */ + peer.config.pic_flags.pro4 = PI_P4_SCTP; + peer.config.pic_flags.sec = PI_SEC_NONE; + peer.config.pic_flags.exp = PI_EXP_NONE; + + peer.config.pic_port = 18678; + + if (fd_core_initialize() != 0) { + fail("fd_core_initialize failed"); + } + if (fd_core_start() != 0) { + fail("fd_core_start failed"); + } + if (fd_core_parseconf("../../conf/hss_fd.conf") != 0) { + fail("fd_core_waitstartcomplete failed"); + } + if (fd_core_waitstartcomplete() != 0) { + fail("fd_core_waitstartcomplete failed"); + } +// if (fd_peer_add(&peer, NULL, NULL, NULL) != 0) { +// fail("fd_peer_add failed"); +// } +} + +void s6a_fd_stop(void) +{ + if (fd_core_shutdown() != 0) { + fail("fd_core_shutdown failed"); + } + if (fd_core_wait_shutdown_complete() != 0) { + fail("fd_core_shutdown failed"); + } +} diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/tests/test_fd.h b/openair-cn/OPENAIRHSS/OPENAIRHSS/tests/test_fd.h new file mode 100644 index 0000000000..006772a211 --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/tests/test_fd.h @@ -0,0 +1,8 @@ +#ifndef TEST_FD_H_ +#define TEST_FD_H_ + +void s6a_fd_init(void); + +void s6a_fd_stop(void); + +#endif /* TEST_FD_H_ */ diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/tests/test_kdf.c b/openair-cn/OPENAIRHSS/OPENAIRHSS/tests/test_kdf.c new file mode 100644 index 0000000000..b1be9f2e8a --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/tests/test_kdf.c @@ -0,0 +1,103 @@ +#include +#include +#include +#include + +#include "config.h" +#include "test_utils.h" +#include "test_fd.h" + +#include "auc.h" + +static +void do_kdf(uint8_t *key, unsigned key_length, uint8_t *data, unsigned data_length, + uint8_t *exp, unsigned exp_length) +{ + uint8_t result[32]; + + kdf(key, key_length, data, data_length, result, 32); + if (compare_buffer(result, exp_length, exp, exp_length) != 0) { + fail("Fail: kdf\n"); + } +} + +void +doit (void) +{ + /* RFC 4231 */ + + /* Test case 1 #4.2 */ + do_kdf(HL("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b" + "0b0b0b0b"), + HL("4869205468657265"), + HL("b0344c61d8db38535ca8afceaf0bf12b" + "881dc200c9833da726e9376c2e32cff7")); + /* Test case 2 #4.3 */ + do_kdf(HL("4a656665"), + HL("7768617420646f2079612077616e7420" + "666f72206e6f7468696e673f"), + HL("5bdcc146bf60754e6a042426089575c7" + "5a003f089d2739839dec58b964ec3843")); + /* Test case 3 #4.4 */ + do_kdf(HL("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaa"), + HL("dddddddddddddddddddddddddddddddd" + "dddddddddddddddddddddddddddddddd" + "dddddddddddddddddddddddddddddddd" + "dddd"), + HL("773ea91e36800e46854db8ebd09181a7" + "2959098b3ef8c122d9635514ced565fe")); + /* Test case 4 #4.5 */ + do_kdf(HL("0102030405060708090a0b0c0d0e0f10" + "111213141516171819"), + HL("cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd" + "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd" + "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd" + "cdcd"), + HL("82558a389a443c0ea4cc819899f2083a" + "85f0faa3e578f8077a2e3ff46729665b")); + /* Test case 5 #4.6 */ + do_kdf(HL("0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c" + "0c0c0c0c"), + HL("546573742057697468205472756e6361" + "74696f6e"), + HL("a3b6167473100ee06e0c796c2955552b")); + /* Test case 6 #4.7 */ + do_kdf(HL("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaa"), + HL("54657374205573696e67204c61726765" + "72205468616e20426c6f636b2d53697a" + "65204b6579202d2048617368204b6579" + "204669727374"), + HL("60e431591ee0b67f0d8a26aacbf5b77f" + "8e0bc6213728c5140546040f0ee37f54")); + /* Test case 6 #4.7 */ + do_kdf(HL("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaa"), + HL("54686973206973206120746573742075" + "73696e672061206c6172676572207468" + "616e20626c6f636b2d73697a65206b65" + "7920616e642061206c61726765722074" + "68616e20626c6f636b2d73697a652064" + "6174612e20546865206b6579206e6565" + "647320746f2062652068617368656420" + "6265666f7265206265696e6720757365" + "642062792074686520484d414320616c" + "676f726974686d2e"), + HL("9b09ffa71b942fcb27635fbcd5b0e944" + "bfdc63644f0713938a7f51535c3a35e2")); +} diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/tests/test_security.c b/openair-cn/OPENAIRHSS/OPENAIRHSS/tests/test_security.c new file mode 100644 index 0000000000..c6ae099015 --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/tests/test_security.c @@ -0,0 +1,89 @@ +#include +#include +#include +#include + +#include "config.h" +#include "test_utils.h" +#include "test_fd.h" + +#include "auc.h" + +typedef struct { + uint8_t key[16]; + uint8_t rand[16]; + uint8_t sqn[6]; + uint8_t amf[2]; + uint8_t op[16]; + uint8_t f1_exp[8]; + uint8_t f1_star_exp[8]; +} test_set_t; + +test_set_t test_set[] = { + /* 35.207 #4.3 */ + { + .key = { + 0x46, 0x5b, 0x5c, 0xe8, 0xb1, 0x99, 0xb4, 0x9f, 0xaa, 0x5f, 0x0a, + 0x2e, 0xe2, 0x38, 0xa6, 0xbc + }, + .rand = { + 0x23, 0x55, 0x3c, 0xbe, 0x96, 0x37, 0xa8, 0x9d, 0x21, 0x8a, 0xe6, + 0x4d, 0xae, 0x47, 0xbf, 0x35 + }, + .sqn = { 0xff, 0x9b, 0xb4, 0xd0, 0xb6, 0x07 }, + .amf = { 0xb9, 0xb9 }, + .op = { + 0xcd, 0xc2, 0x02, 0xd5, 0x12, 0x3e, 0x20, 0xf6, 0x2b, 0x6d, 0x67, + 0x6a, 0xc7, 0x2c, 0xb3, 0x18 + }, + .f1_exp = { 0x4a, 0x9f, 0xfa, 0xc3, 0x54, 0xdf, 0xaf, 0xb3 }, + .f1_star_exp = { 0x01, 0xcf, 0xaf, 0x9e, 0xc4, 0xe8, 0x71, 0xe9 }, + }, + { + .key = { + 0x03, 0x96, 0xeb, 0x31, 0x7b, 0x6d, 0x1c, 0x36, 0xf1, 0x9c, 0x1c, + 0x84, 0xcd, 0x6f, 0xfd, 0x16 + }, + .rand = { + 0xc0, 0x0d, 0x60, 0x31, 0x03, 0xdc, 0xee, 0x52, 0xc4, 0x47, 0x81, + 0x19, 0x49, 0x42, 0x02, 0xe8, + }, + .sqn = { 0xfd, 0x8e, 0xef, 0x40, 0xdf, 0x7d }, + .amf = { 0xaf, 0x17 }, + .op = { + 0xff, 0x53, 0xba, 0xde, 0x17, 0xdf, 0x5d, 0x4e, 0x79, 0x30, 0x73, + 0xce, 0x9d, 0x75, 0x79, 0xfa + }, + .f1_exp = { 0x5d, 0xf5, 0xb3, 0x18, 0x07, 0xe2, 0x58, 0xb0 }, + .f1_star_exp = { 0xa8, 0xc0, 0x16, 0xe5, 0x1e, 0xf4, 0xa3, 0x43 } + }, +}; + +void +doit (void) +{ + int i; + + for (i = 0; i < sizeof(test_set) / sizeof(test_set_t); i++) { + uint8_t res[8]; + + SetOPc(test_set[i].op); + f1(test_set[i].key, test_set[i].rand, test_set[i].sqn, test_set[i].amf, res); +// printf("%02x%02x%02x%02x%02x%02x%02x%02x\n", res[0], res[1], res[2], +// res[3], res[4], res[5], res[6], res[7]); + if (memcmp(res, test_set[i].f1_exp, 8) != 0) { + fail("Test set %d (f1) : failed\n", i); + } else { + success("Test set %d (f1) : success\n", i); + } + + f1star(test_set[i].key, test_set[i].rand, test_set[i].sqn, test_set[i].amf, res); +// printf("%02x%02x%02x%02x%02x%02x%02x%02x\n", res[0], res[1], res[2], +// res[3], res[4], res[5], res[6], res[7]); + if (memcmp(res, test_set[i].f1_star_exp, 8) != 0) { + fail("Test set %d (f1*): failed\n", i); + } else { + success("Test set %d (f1*): success\n", i); + } + } +} diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/tests/test_security_f1.c b/openair-cn/OPENAIRHSS/OPENAIRHSS/tests/test_security_f1.c new file mode 100644 index 0000000000..ca5da9c71f --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/tests/test_security_f1.c @@ -0,0 +1,90 @@ +#include +#include +#include +#include + +#include "config.h" +#include "test_utils.h" +#include "test_fd.h" + +#include "auc.h" + +static +void do_f1_f1star(uint8_t *key, uint8_t *rand, uint8_t *sqn, + uint8_t *amf, uint8_t *op, uint8_t *f1_exp, + uint8_t *f1star_exp) +{ + uint8_t res[8]; + + SetOPc(op); + + f1(key, rand, sqn, amf, res); + if (compare_buffer(res, 8, f1_exp, 8) != 0) { + fail("Fail: f1"); + } + f1star(key, rand, sqn, amf, res); + if (compare_buffer(res, 8, f1star_exp, 8) != 0) { + fail("Fail: f1*"); + } +} + +void +doit (void) +{ + /* Test suite taken from 3GPP TS 35.207 */ + + /* Test set 1 #4.3 */ + do_f1_f1star( + H("465b5ce8 b199b49f aa5f0a2e e238a6bc"), + H("23553cbe 9637a89d 218ae64d ae47bf35"), + H("ff9bb4d0 b607"), + H("b9b9"), + H("cdc202d5 123e20f6 2b6d676a c72cb318"), + H("4a9ffac3 54dfafb3"), + H("01cfaf9e c4e871e9")); + /* Test set 2 #4.4 */ + do_f1_f1star( + H("0396eb31 7b6d1c36 f19c1c84 cd6ffd16"), + H("c00d6031 03dcee52 c4478119 494202e8"), + H("fd8eef40 df7d"), + H("af17"), + H("ff53bade 17df5d4e 793073ce 9d7579fa"), + H("5df5b318 07e258b0"), + H("a8c016e5 1ef4a343")); + /* Test set 3 #4.5 */ + do_f1_f1star( + H("fec86ba6 eb707ed0 8905757b 1bb44b8f"), + H("9f7c8d02 1accf4db 213ccff0 c7f71a6a"), + H("9d027759 5ffc"), + H("725c"), + H("dbc59adc b6f9a0ef 735477b7 fadf8374"), + H("9cabc3e9 9baf7281"), + H("95814ba2 b3044324")); + /* Test set 4 #4.5 */ + do_f1_f1star( + H("9e5944ae a94b8116 5c82fbf9 f32db751"), + H("ce83dbc5 4ac0274a 157c17f8 0d017bd6"), + H("0b604a81 eca8"), + H("9e09"), + H("223014c5 806694c0 07ca1eee f57f004f"), + H("74a58220 cba84c49"), + H("ac2cc74a 96871837")); + /* Test set 5 #4.6 */ + do_f1_f1star( + H("4ab1deb0 5ca6ceb0 51fc98e7 7d026a84"), + H("74b0cd60 31a1c833 9b2b6ce2 b8c4a186"), + H("e880a1b5 80b6"), + H("9f07"), + H("2d16c5cd 1fdf6b22 383584e3 bef2a8d8"), + H("49e785dd 12626ef2"), + H("9e857903 36bb3fa2")); + /* Test set 6 #4.7 */ + do_f1_f1star( + H("6c38a116 ac280c45 4f59332e e35c8c4f"), + H("ee6466bc 96202c5a 557abbef f8babf63"), + H("414b9822 2181"), + H("4464"), + H("1ba00a1a 7c6700ac 8c3ff3e9 6ad08725"), + H("078adfb4 88241a57"), + H("80246b8d 0186bcf1")); +} diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/tests/test_security_f2_f3_f5.c b/openair-cn/OPENAIRHSS/OPENAIRHSS/tests/test_security_f2_f3_f5.c new file mode 100644 index 0000000000..8da3ada3c3 --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/tests/test_security_f2_f3_f5.c @@ -0,0 +1,79 @@ +#include +#include +#include +#include + +#include "config.h" +#include "test_utils.h" +#include "test_fd.h" + +#include "auc.h" + +static +void do_f2f3f5(uint8_t *key, uint8_t *rand, uint8_t *op, uint8_t *f2_exp, + uint8_t *f5_exp, uint8_t *f3_exp) +{ + uint8_t res_f2[8]; + uint8_t res_f5[6]; + uint8_t res_f3[16]; + uint8_t res_f4[16]; + + SetOPc(op); + + f2345(key, rand, res_f2, res_f3, res_f4, res_f5); + if (compare_buffer(res_f2, 8, f2_exp, 8) != 0) { + fail("Fail: f2"); + } + if (compare_buffer(res_f5, 6, f5_exp, 6) != 0) { + fail("Fail: f5"); + } + if (compare_buffer(res_f3, 16, f3_exp, 16) != 0) { + fail("Fail: f3"); + } +} + +void +doit (void) +{ + /* Test set 1 #5.3 */ + do_f2f3f5(H("465b5ce8 b199b49f aa5f0a2e e238a6bc"), + H("23553cbe 9637a89d 218ae64d ae47bf35"), + H("cdc202d5 123e20f6 2b6d676a c72cb318"), + H("a54211d5 e3ba50bf"), H("aa689c64 8370"), + H("b40ba9a3 c58b2a05 bbf0d987 b21bf8cb")); + + /* Test set 2 #5.4 */ + do_f2f3f5(H("0396eb31 7b6d1c36 f19c1c84 cd6ffd16"), + H("c00d6031 03dcee52 c4478119 494202e8"), + H("ff53bade 17df5d4e 793073ce 9d7579fa"), + H("d3a628ed 988620f0"), H("c4778399 5f72"), + H("58c433ff 7a7082ac d424220f 2b67c556")); + + /* Test set 3 #5.5 */ + do_f2f3f5(H("fec86ba6 eb707ed0 8905757b 1bb44b8f"), + H("9f7c8d02 1accf4db 213ccff0 c7f71a6a"), + H("dbc59adc b6f9a0ef 735477b7 fadf8374"), + H("8011c48c 0c214ed2"), H("33484dc2 136b"), + H("5dbdbb29 54e8f3cd e665b046 179a5098")); + + /* Test set 4 #5.6 */ + do_f2f3f5(H("9e5944ae a94b8116 5c82fbf9 f32db751"), + H("ce83dbc5 4ac0274a 157c17f8 0d017bd6"), + H("223014c5 806694c0 07ca1eee f57f004f"), + H("f365cd68 3cd92e96"), H("f0b9c08a d02e"), + H("e203edb3 971574f5 a94b0d61 b816345d")); + + /* Test set 5 #5.7 */ + do_f2f3f5(H("4ab1deb0 5ca6ceb0 51fc98e7 7d026a84"), + H("74b0cd60 31a1c833 9b2b6ce2 b8c4a186"), + H("2d16c5cd 1fdf6b22 383584e3 bef2a8d8"), + H("5860fc1b ce351e7e"), H("31e11a60 9118"), + H("7657766b 373d1c21 38f307e3 de9242f9")); + + /* Test set 6 #5.8 */ + do_f2f3f5(H("6c38a116 ac280c45 4f59332e e35c8c4f"), + H("ee6466bc 96202c5a 557abbef f8babf63"), + H("1ba00a1a 7c6700ac 8c3ff3e9 6ad08725"), + H("16c8233f 05a0ac28"), H("45b0f69a b06c"), + H("3f8c7587 fe8e4b23 3af676ae de30ba3b")); +} diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/tests/test_security_f4_f5star.c b/openair-cn/OPENAIRHSS/OPENAIRHSS/tests/test_security_f4_f5star.c new file mode 100644 index 0000000000..4422290201 --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/tests/test_security_f4_f5star.c @@ -0,0 +1,73 @@ +#include +#include +#include +#include + +#include "config.h" +#include "test_utils.h" +#include "test_fd.h" + +#include "auc.h" + +static +void do_f4f5star(uint8_t *key, uint8_t *rand, uint8_t *op, uint8_t *f4_exp, + uint8_t *f5star_exp) +{ + uint8_t res_f2[8]; + uint8_t res_f5[6]; + uint8_t res_f3[16]; + uint8_t res_f4[16]; + uint8_t res_f5star[6]; + + SetOPc(op); + + f2345(key, rand, res_f2, res_f3, res_f4, res_f5); + if (compare_buffer(res_f4, 16, f4_exp, 16) != 0) { + fail("Fail: f4"); + } + f5star(key, rand, res_f5star); + if (compare_buffer(res_f5star, 6, f5star_exp, 6) != 0) { + fail("Fail: f5star"); + } +} + +void +doit (void) +{ + /* Test set 1 #6.3 */ + do_f4f5star(H("465b5ce8 b199b49f aa5f0a2e e238a6bc"), + H("23553cbe 9637a89d 218ae64d ae47bf35"), + H("cdc202d5 123e20f6 2b6d676a c72cb318"), + H("f769bcd7 51044604 12767271 1c6d3441"), + H("451e8bec a43b")); + /* Test set 2 #6.4 */ + do_f4f5star(H("0396eb31 7b6d1c36 f19c1c84 cd6ffd16"), + H("c00d6031 03dcee52 c4478119 494202e8"), + H("ff53bade 17df5d4e 793073ce 9d7579fa"), + H("21a8c1f9 29702adb 3e738488 b9f5c5da"), + H("30f11970 61c1")); + /* Test set 3 #6.5 */ + do_f4f5star(H("fec86ba6 eb707ed0 8905757b 1bb44b8f"), + H("9f7c8d02 1accf4db 213ccff0 c7f71a6a"), + H("dbc59adc b6f9a0ef 735477b7 fadf8374"), + H("59a92d3b 476a0443 487055cf 88b2307b"), + H("deacdd84 8cc6")); + /* Test set 4 #6.5 */ + do_f4f5star(H("9e5944ae a94b8116 5c82fbf9 f32db751"), + H("ce83dbc5 4ac0274a 157c17f8 0d017bd6"), + H("223014c5 806694c0 07ca1eee f57f004f"), + H("0c4524ad eac041c4 dd830d20 854fc46b"), + H("6085a86c 6f63")); + /* Test set 5 #6.6 */ + do_f4f5star(H("4ab1deb0 5ca6ceb0 51fc98e7 7d026a84"), + H("74b0cd60 31a1c833 9b2b6ce2 b8c4a186"), + H("2d16c5cd 1fdf6b22 383584e3 bef2a8d8"), + H("1c42e960 d89b8fa9 9f2744e0 708ccb53"), + H("fe2555e5 4aa9")); + /* Test set 6 #6.7 */ + do_f4f5star(H("6c38a116 ac280c45 4f59332e e35c8c4f"), + H("ee6466bc 96202c5a 557abbef f8babf63"), + H("1ba00a1a 7c6700ac 8c3ff3e9 6ad08725"), + H("a7466cc1 e6b2a133 7d49d3b6 6e95d7b4"), + H("1f53cd2b 1113")); +} diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/tests/test_security_kasme.c b/openair-cn/OPENAIRHSS/OPENAIRHSS/tests/test_security_kasme.c new file mode 100644 index 0000000000..4a2f957b6a --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/tests/test_security_kasme.c @@ -0,0 +1,59 @@ +#include +#include +#include +#include +#include + +#include "config.h" +#include "test_utils.h" +#include "test_fd.h" + +#include "auc.h" + +static +void do_derive_kasme(uint8_t *sn_id, uint8_t *sqn, uint8_t *ak, + uint8_t *ck, uint8_t *ik, uint8_t *kasme_exp) +{ + uint8_t kasme[32]; + derive_kasme(ck, ik, sn_id, sqn, ak, kasme); + + if (compare_buffer(kasme, 32, kasme_exp, 32) != 0) { + fail("Fail: derive_kasme\n"); + } +} + +void +doit (void) +{ + /* 20834 -> 024830 SNid */ + do_derive_kasme(H("024830"), H("FD8EEF40DF7D"), H("AA689C648370"), + H("B40BA9A3C58B2A05BBF0D987B21BF8CB"), + H("F769BCD751044604127672711C6D3441"), + H("238E457E0F758BADBCA8D34BB2612C10" + "428D426757CB5553B2B184FA64BFC549")); + do_derive_kasme(H("02F843"), H("FD8EEF40DF7D"), H("AA689C648370"), + H("B40BA9A3C58B2A05BBF0D987B21BF8CB"), + H("F769BCD751044604127672711C6D3441"), + H("BD7A0903A7D0F68767EE2F5C90CB7D7D" + "835998D940AFDBF73173E63567C5B894")); + do_derive_kasme(H("21F354"), H("FD8EEF40DF7D"), H("AA689C648370"), + H("B40BA9A3C58B2A05BBF0D987B21BF8CB"), + H("F769BCD751044604127672711C6D3441"), + H("546A79BC6D1613A72A4D631EE0351D66" + "036B2A0C44A3831BE6D365E24F023013")); + do_derive_kasme(H("024830"), H("FF9BB4D0B607"), H("AA689C648370"), + H("B40BA9A3C58B2A05BBF0D987B21BF8CB"), + H("F769BCD751044604127672711C6D3441"), + H("564CB4D2007E4F293B67D9B29392A64A" + "DD4C776B133D895AF6499AA6882AAB62")); + do_derive_kasme(H("02F843"), H("FF9BB4D0B607"), H("AA689C648370"), + H("B40BA9A3C58B2A05BBF0D987B21BF8CB"), + H("F769BCD751044604127672711C6D3441"), + H("34865EB0DC9A6D788A905C0514529BF5" + "88485DA817FFBE92E9A9B4D033B8CC6F")); + do_derive_kasme(H("21F354"), H("FF9BB4D0B607"), H("AA689C648370"), + H("B40BA9A3C58B2A05BBF0D987B21BF8CB"), + H("F769BCD751044604127672711C6D3441"), + H("9EA141DA4B24CDEBC8F5FB3F61A05112" + "16681F121199B23EBCFACC75B358BE43")); +} diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/tests/test_utils.c b/openair-cn/OPENAIRHSS/OPENAIRHSS/tests/test_utils.c new file mode 100644 index 0000000000..1404bebd8b --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/tests/test_utils.c @@ -0,0 +1,274 @@ +/* + * Copyright (C) 2004-2012 Free Software Foundation, Inc. + * + * Author: Simon Josefsson + * + * This file is part of GnuTLS. + * + * GnuTLS is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GnuTLS is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GnuTLS; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include "test_utils.h" + +int debug = 0; +int error_count = 0; +int break_on_error = 0; + +/* -1 means invalid */ +static const signed char hex_digits[0x100] = +{ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1, + -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 +}; + +unsigned +decode_hex_length(const char *h) +{ + const unsigned char *hex = (const unsigned char *) h; + unsigned count; + unsigned i; + + for (count = i = 0; hex[i]; i++) + { + if (isspace(hex[i])) + continue; + if (hex_digits[hex[i]] < 0) + abort(); + count++; + } + + if (count % 2) + abort(); + return count / 2; +} + +int +decode_hex(uint8_t *dst, const char *h) +{ + const unsigned char *hex = (const unsigned char *) h; + unsigned i = 0; + + for (;;) + { + int high, low; + + while (*hex && isspace(*hex)) + hex++; + + if (!*hex) + return 1; + + high = hex_digits[*hex++]; + if (high < 0) + return 0; + + while (*hex && isspace(*hex)) + hex++; + + if (!*hex) + return 0; + + low = hex_digits[*hex++]; + if (low < 0) + return 0; + + dst[i++] = (high << 4) | low; + } +} + +uint8_t * +decode_hex_dup(const char *hex) +{ + uint8_t *p; + unsigned length = decode_hex_length(hex); + + p = malloc(length * sizeof(uint8_t)); + + if (decode_hex(p, hex)) + return p; + else + { + free(p); + return NULL; + } +} + +void +fail (const char *format, ...) +{ + char str[1024]; + va_list arg_ptr; + + va_start (arg_ptr, format); + vsnprintf ( str, sizeof(str), format, arg_ptr); + va_end (arg_ptr); + fputs(str, stderr); + error_count++; + if (break_on_error) + exit (1); +} + +void +success (const char *format, ...) +{ + char str[1024]; + va_list arg_ptr; + + va_start (arg_ptr, format); + vsnprintf ( str, sizeof(str), format, arg_ptr); + va_end (arg_ptr); + fputs(str, stderr); +} + +void +escapeprint (const char *str, size_t len) +{ + size_t i; + + printf (" (length %d bytes):\n\t", (int) len); + for (i = 0; i < len; i++) + { + if (((str[i] & 0xFF) >= 'A' && (str[i] & 0xFF) <= 'Z') || + ((str[i] & 0xFF) >= 'a' && (str[i] & 0xFF) <= 'z') || + ((str[i] & 0xFF) >= '0' && (str[i] & 0xFF) <= '9') + || (str[i] & 0xFF) == ' ' || (str[i] & 0xFF) == '.') + printf ("%c", (str[i] & 0xFF)); + else + printf ("\\x%02X", (str[i] & 0xFF)); + if ((i + 1) % 16 == 0 && (i + 1) < len) + printf ("'\n\t'"); + } + printf ("\n"); +} + +void +hexprint (const void *_str, size_t len) +{ + size_t i; + const char* str = _str; + + printf ("\t;; "); + for (i = 0; i < len; i++) + { + printf ("%02x ", (str[i] & 0xFF)); + if ((i + 1) % 8 == 0) + printf (" "); + if ((i + 1) % 16 == 0 && i + 1 < len) + printf ("\n\t;; "); + } + printf ("\n"); +} + +void +binprint (const void *_str, size_t len) +{ + size_t i; + const char* str = _str; + + printf ("\t;; "); + for (i = 0; i < len; i++) + { + printf ("%d%d%d%d%d%d%d%d ", + (str[i] & 0xFF) & 0x80 ? 1 : 0, + (str[i] & 0xFF) & 0x40 ? 1 : 0, + (str[i] & 0xFF) & 0x20 ? 1 : 0, + (str[i] & 0xFF) & 0x10 ? 1 : 0, + (str[i] & 0xFF) & 0x08 ? 1 : 0, + (str[i] & 0xFF) & 0x04 ? 1 : 0, + (str[i] & 0xFF) & 0x02 ? 1 : 0, (str[i] & 0xFF) & 0x01 ? 1 : 0); + if ((i + 1) % 3 == 0) + printf (" "); + if ((i + 1) % 6 == 0 && i + 1 < len) + printf ("\n\t;; "); + } + printf ("\n"); +} + +int +compare_buffer(uint8_t *buffer, uint32_t length_buffer, + uint8_t *pattern, uint32_t length_pattern) +{ + int i; + if (length_buffer != length_pattern) { + printf("Length mismatch, expecting %d bytes, got %d bytes\n", length_pattern, + length_buffer); + hexprint(buffer, length_buffer); + return -1; + } + for (i = 0; i < length_buffer; i++) { + if (pattern[i] != buffer[i]) { + printf("Expecting:\n"); + hexprint(pattern, length_pattern); + printf("Received:\n"); + hexprint(buffer, length_buffer); + printf("Mismatch fount in byte %d\nExpecting 0x%02x, got 0x%02x\n", + i, pattern[i], buffer[i]); + return -1; + } + } + return 0; +} + +int +main (int argc, char *argv[]) +{ + do + if (strcmp (argv[argc - 1], "-v") == 0 || + strcmp (argv[argc - 1], "--verbose") == 0) + debug = 1; + else if (strcmp (argv[argc - 1], "-b") == 0 || + strcmp (argv[argc - 1], "--break-on-error") == 0) + break_on_error = 1; + else if (strcmp (argv[argc - 1], "-h") == 0 || + strcmp (argv[argc - 1], "-?") == 0 || + strcmp (argv[argc - 1], "--help") == 0) + { + printf ("Usage: %s [-vbh?] [--verbose] [--break-on-error] [--help]\n", + argv[0]); + return 1; + } + while (argc-- > 1); + + doit (); + + if (debug || error_count > 0) + printf ("Self test `%s' finished with %d errors\n", argv[0], error_count); + + return error_count ? 1 : 0; +} diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/tests/test_utils.h b/openair-cn/OPENAIRHSS/OPENAIRHSS/tests/test_utils.h new file mode 100644 index 0000000000..e478f8a480 --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/tests/test_utils.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2004-2012 Free Software Foundation, Inc. + * + * Author: Simon Josefsson + * + * This file is part of GnuTLS. + * + * GnuTLS is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GnuTLS is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GnuTLS; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef UTILS_H +#define UTILS_H + +#include +#include + +#ifndef __attribute__ +#if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 5) +#define __attribute__(Spec) /* empty */ +#endif +#endif + +extern int debug; +extern int error_count; +extern int break_on_error; + +extern uint8_t * decode_hex_dup(const char *hex); +extern int decode_hex(uint8_t *dst, const char *h); +extern unsigned decode_hex_length(const char *h); + +#define H(x) decode_hex_dup(x) +#define HL(x) decode_hex_dup(x), decode_hex_length(x) + +extern void fail (const char *format, ...) + __attribute__ ((format (printf, 1, 2))); +extern void success (const char *format, ...) + __attribute__ ((format (printf, 1, 2))); + +extern void escapeprint (const char *str, size_t len); +extern void hexprint (const void *str, size_t len); +extern void binprint (const void *str, size_t len); + +extern int +compare_buffer(uint8_t *buffer, uint32_t length_buffer, + uint8_t *pattern, uint32_t length_pattern); + +/* This must be implemented elsewhere. */ +extern void doit (void); + +#endif /* UTILS_H */ diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/tests/up_loc_test.c b/openair-cn/OPENAIRHSS/OPENAIRHSS/tests/up_loc_test.c new file mode 100644 index 0000000000..62bd5fad06 --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/tests/up_loc_test.c @@ -0,0 +1,19 @@ + +#include +#include + +#include "config.h" +#include "test_utils.h" +#include "test_fd.h" + +void +doit (void) +{ + s6a_fd_init(); + + sleep(1); + + s6a_fd_stop(); + + success ("freediameter start/stop ok\n"); +} diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/utils/Makefile.am b/openair-cn/OPENAIRHSS/OPENAIRHSS/utils/Makefile.am new file mode 100644 index 0000000000..36c4dcd668 --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/utils/Makefile.am @@ -0,0 +1,12 @@ +AM_CFLAGS = @ADD_CFLAGS@ +AM_YFLAGS = -d +AM_LFLAGS = -o$(LEX_OUTPUT_ROOT).c + +BUILT_SOURCE = hss_parser.h + +noinst_LTLIBRARIES = libutils.la +libutils_la_LDFLAGS = -all-static +libutils_la_SOURCES = \ + hss_parser.y hss_scanner.l \ + hss_config.h hss_config.c \ + conversion.h conversion.c diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/utils/conversion.c b/openair-cn/OPENAIRHSS/OPENAIRHSS/utils/conversion.c new file mode 100644 index 0000000000..1925c7742a --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/utils/conversion.c @@ -0,0 +1,110 @@ +#include +#include +#include + +#include "conversion.h" + +static const char hex_to_ascii_table[16] = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', +}; + +static const signed char ascii_to_hex_table[0x100] = { + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 +}; + +static const signed char ascii_to_dec_table[0x100] = { + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 +}; + +void hexa_to_ascii(uint8_t *from, char *to, size_t length) +{ + int i; + for(i = 0; i < length; i++) { + uint8_t upper = (from[i] & 0xf0) >> 4; + uint8_t lower = from[i] & 0x0f; + to[2 * i] = hex_to_ascii_table[upper]; + to[2 * i + 1] = hex_to_ascii_table[lower]; + } +} + +int ascii_to_hex(uint8_t *dst, const char *h) +{ + const unsigned char *hex = (const unsigned char *) h; + unsigned i = 0; + + for (;;) { + int high, low; + + while (*hex && isspace(*hex)) { + hex++; + } + + if (!*hex) { + return 1; + } + + high = ascii_to_hex_table[*hex++]; + if (high < 0) { + return 0; + } + + while (*hex && isspace(*hex)) { + hex++; + } + + if (!*hex) { + return 0; + } + + low = ascii_to_hex_table[*hex++]; + if (low < 0) { + return 0; + } + + dst[i++] = (high << 4) | low; + } +} + +int bcd_to_hex(uint8_t *dst, const char *h, int h_length) +{ + const unsigned char *hex = (const unsigned char *) h; + unsigned i = 0; + + for (i = 0; i < h_length; i++) { + int value = ascii_to_dec_table[*hex++]; + if (value < 0) + return -1; + dst[i] = value; + } + return 0; +} diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/utils/conversion.h b/openair-cn/OPENAIRHSS/OPENAIRHSS/utils/conversion.h new file mode 100644 index 0000000000..60c036e6a2 --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/utils/conversion.h @@ -0,0 +1,8 @@ +#ifndef CONVERSION_H_ +#define CONVERSION_H_ + +void hexa_to_ascii(uint8_t *from, char *to, size_t length); +int ascii_to_hex(uint8_t *dst, const char *h); +int bcd_to_hex(uint8_t *dst, const char *h, int h_length); + +#endif /* CONVERSION_H_ */ diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/utils/hss_config.c b/openair-cn/OPENAIRHSS/OPENAIRHSS/utils/hss_config.c new file mode 100644 index 0000000000..5d62c1aa00 --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/utils/hss_config.c @@ -0,0 +1,187 @@ +/******************************************************************************* + + Eurecom OpenAirInterface + Copyright(c) 1999 - 2013 Eurecom + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information + Openair Admin: openair_admin@eurecom.fr + Openair Tech : openair_tech@eurecom.fr + Forums : http://forums.eurecom.fr/openairinterface + Address : EURECOM, Campus SophiaTech, 450 Route des Chappes + 06410 Biot FRANCE + +*******************************************************************************/ + +/*! \file hss_config.c + * \brief Base configuration for the HSS. Parse command line and configuration file + * \author Sebastien ROUX + * \date 2013 + * \version 0.1 + */ + +#if HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +#include "hss_config.h" +#include "hss_parser.h" + +/* YACC forward declarations */ +extern int yyparse (struct hss_config_s *hss_config_p); + +static int config_parse_command_line(int argc, char *argv[], + hss_config_t *hss_config_p); +static int config_parse_file(hss_config_t *hss_config_p); +static void display_banner(void); +static void config_display(hss_config_t *hss_config_p); + +static struct option long_options[] = { + { "config", 1, 0, 'c' }, + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'v' }, + { 0, 0, 0, 0 }, +}; + +static const char option_string[] = "c:vh"; + +int config_init(int argc, char *argv[], hss_config_t *hss_config_p) +{ + int ret = 0; + + if (hss_config_p == NULL) { + return EINVAL; + } + if ((ret = config_parse_command_line(argc, argv, hss_config_p)) != 0) { + return ret; + } + display_banner(); + if ((ret = config_parse_file(hss_config_p)) != 0) { + /* Parsing of the file failed. -> abort */ + abort(); + } + config_display(hss_config_p); + return ret; +} + +static void display_banner(void) +{ + fprintf(stdout, "==== EURECOM %s v%s ====\n", PACKAGE_NAME, PACKAGE_VERSION); + fprintf(stdout, "Please report any bug to: %s\n\n", PACKAGE_BUGREPORT); +} + +static void usage(void) +{ + display_banner(); + fprintf(stdout, "Usage: openair_hss [options]\n\n"); + fprintf(stdout, "Available options:\n"); + fprintf(stdout, "\t--help\n\t-h\n"); + fprintf(stdout, "\t\tPrint this help and return\n\n"); + fprintf(stdout, "\t--config=\n\t-c\n"); + fprintf(stdout, "\t\tSet the configuration file for hss\n"); + fprintf(stdout, "\t\tSee template in conf dir\n\n"); + fprintf(stdout, "\t--version\n\t-v\n"); + fprintf(stdout, "\t\tPrint %s version and return\n", PACKAGE_NAME); +} + +static void config_display(hss_config_t *hss_config_p) +{ + fprintf(stdout, "Configuration\n"); + fprintf(stdout, "* Global:\n"); + fprintf(stdout, "\t- File .............: %s\n", hss_config_p->config); + fprintf(stdout, "* MYSQL:\n"); + fprintf(stdout, "\t- Server ...........: %s\n", hss_config_p->mysql_server); + fprintf(stdout, "\t- Database .........: %s\n", hss_config_p->mysql_database); + fprintf(stdout, "\t- User .............: %s\n", hss_config_p->mysql_user); + fprintf(stdout, "\t- Password .........: %s\n", + (hss_config_p->mysql_password == NULL) ? "None" : "*****"); + fprintf(stdout, "* FreeDiameter:\n"); + fprintf(stdout, "\t- Conf file ........: %s\n", + hss_config_p->freediameter_config); +} + +static int config_parse_command_line(int argc, char *argv[], + hss_config_t *hss_config_p) +{ + int c; + int option_index = 0; + + while ((c = getopt_long (argc, argv, option_string, long_options, + &option_index)) != -1) { + switch(c) { + case 'c': { + hss_config_p->config = strdup(optarg); + } + break; + case 'v': { + /* We display version and return immediately */ + display_banner(); + exit(0); + } + break; + default: + case 'h': { + usage(); + exit(0); + } + break; + } + } + return 0; +} + +static int config_parse_file(hss_config_t *hss_config_p) +{ + extern FILE *yyin; + int ret = -1; + + if (hss_config_p == NULL) { + return ret; + } + if (hss_config_p->config == NULL) { + return ret; + } + + printf("Parsing configuration file: %s\n", hss_config_p->config); + + yyin = fopen(hss_config_p->config, "r"); + if (!yyin) { + /* We failed to open the file */ + fprintf(stderr, "Unable to open the configuration file: %s (%d:%s)\n", + hss_config_p->config, errno, strerror(errno)); + return errno; + } + + /* Call the yacc parser */ + ret = yyparse(hss_config_p); + + /* Close the file descriptor */ + if (fclose(yyin) != 0) { + fprintf(stderr, "Unable to close the configuration file: %s (%d:%s)\n", + hss_config_p->config, errno, strerror(errno)); + return errno; + } + return ret; +} diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/utils/hss_config.h b/openair-cn/OPENAIRHSS/OPENAIRHSS/utils/hss_config.h new file mode 100644 index 0000000000..d5d5cb5eae --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/utils/hss_config.h @@ -0,0 +1,49 @@ +/******************************************************************************* + + Eurecom OpenAirInterface + Copyright(c) 1999 - 2013 Eurecom + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information + Openair Admin: openair_admin@eurecom.fr + Openair Tech : openair_tech@eurecom.fr + Forums : http://forums.eurecom.fr/openairinterface + Address : EURECOM, Campus SophiaTech, 450 Route des Chappes + 06410 Biot FRANCE + +*******************************************************************************/ + +#ifndef HSS_CONFIG_H_ +#define HSS_CONFIG_H_ + +typedef struct hss_config_s { + char *mysql_server; + char *mysql_user; + char *mysql_password; + char *mysql_database; + + /* The freediameter configuration file */ + char *freediameter_config; + + /* THe HSS global configuration file */ + char *config; +} hss_config_t; + +int config_init(int argc, char *argv[], hss_config_t *hss_config_p); + +#endif /* HSS_CONFIG_H_ */ diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/utils/hss_parser.y b/openair-cn/OPENAIRHSS/OPENAIRHSS/utils/hss_parser.y new file mode 100644 index 0000000000..0610fa1654 --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/utils/hss_parser.y @@ -0,0 +1,129 @@ +/******************************************************************************* + + Eurecom OpenAirInterface + Copyright(c) 1999 - 2013 Eurecom + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information + Openair Admin: openair_admin@eurecom.fr + Openair Tech : openair_tech@eurecom.fr + Forums : http://forums.eurecom.fr/openairinterface + Address : EURECOM, Campus SophiaTech, 450 Route des Chappes + 06410 Biot FRANCE + +*******************************************************************************/ + +/* For development only : */ +%debug +%error-verbose + +%parse-param {struct hss_config_s *hss_config_p} + +/* Keep track of location */ +%locations +%defines +%pure-parser + +%{ +#include +#include + +#include "hss_config.h" +#include "hss_parser.h" + +void yyerror (YYLTYPE *llocp, struct hss_config_s *hss_config_p, const char *s); + +extern int yywrap(); +extern int yylex(); + +/* The Lex parser prototype */ +int fddlex(YYSTYPE *lvalp, YYLTYPE *llocp); +%} + +/* Values returned by lex for token */ +%union { + char *string; /* The string is allocated by strdup in lex.*/ + int integer; /* Store integer values */ +} + +%token QSTRING +%token INTEGER + +%token LEX_ERROR +%token FDCONF +%token MYSQL_USER +%token MYSQL_SERVER +%token MYSQL_PASS +%token MYSQL_DB + +%% +conffile: /* Empty is OK -- for simplicity here, we reject in daemon later */ + | conffile mysql_db + | conffile mysql_server + | conffile mysql_user + | conffile mysql_pass + | conffile fdconf + | conffile errors + { + yyerror(&yylloc, hss_config_p, "An error occurred while parsing the configuration file"); + return EINVAL; + } + ; + +mysql_db: MYSQL_DB '=' QSTRING ';' + { + hss_config_p->mysql_database = $3; + } + ; + +mysql_pass: MYSQL_PASS '=' QSTRING ';' + { + hss_config_p->mysql_password = $3; + } + ; + +mysql_user: MYSQL_USER '=' QSTRING ';' + { + hss_config_p->mysql_user = $3; + } + ; + +mysql_server: MYSQL_SERVER '=' QSTRING ';' + { + hss_config_p->mysql_server = $3; + } + ; + +fdconf: FDCONF '=' QSTRING ';' + { + hss_config_p->freediameter_config = $3; + } + ; + + /* Lexical or syntax error */ +errors: LEX_ERROR + | error + ; +%% + +void yyerror(YYLTYPE *llocp, struct hss_config_s *hss_config_p, const char *str) +{ + fprintf(stderr, "Error in %s ( on line %i column %i -> line %i column %i) : %s\n", + hss_config_p->freediameter_config, llocp->first_line, llocp->first_column, + llocp->last_line, llocp->last_column, str); +} diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/utils/hss_scanner.l b/openair-cn/OPENAIRHSS/OPENAIRHSS/utils/hss_scanner.l new file mode 100644 index 0000000000..b902af2a93 --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/utils/hss_scanner.l @@ -0,0 +1,129 @@ +/******************************************************************************* + + Eurecom OpenAirInterface + Copyright(c) 1999 - 2013 Eurecom + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information + Openair Admin: openair_admin@eurecom.fr + Openair Tech : openair_tech@eurecom.fr + Forums : http://forums.eurecom.fr/openairinterface + Address : EURECOM, Campus SophiaTech, 450 Route des Chappes + 06410 Biot FRANCE + +*******************************************************************************/ + +/* Lex configuration parser. + * + * This file defines the token for parsing the configuration file + * + * Note : This module is NOT thread-safe. All processing must be done from one thread only. + */ +%{ +#include + +#include "hss_config.h" +/* Include yacc tokens definitions */ +#include "hss_parser.h" + +/* Update the column information */ +#ifdef DEBUG_LEX +#define YY_USER_ACTION { \ + yylloc->first_column = yylloc->last_column + 1; \ + yylloc->last_column = yylloc->first_column + yyleng - 1; \ + TRACE_DEBUG(FULL, \ + "(%d:%d-%d:%d) matched rule %d, length=%d, txt='%s'\n", \ + yylloc->first_line, yylloc->first_column, \ + yylloc->last_line, yylloc->last_column, \ + yy_act, yyleng, yytext); \ +} +#else /* DEBUG_LEX */ +#define YY_USER_ACTION { \ + yylloc->first_column = yylloc->last_column + 1; \ + yylloc->last_column = yylloc->first_column + yyleng - 1; \ +} +#endif + +#define YY_NO_INPUT +%} + +%option bison-bridge +%option bison-locations +%option noyywrap +%option nounput + +/* Quoted string. Multilines do not match. */ +qstring \"[^\"\n]*\" + +%% + + /* List of patterns and actions */ + +<*>\n { + /* Update the line count */ + yylloc->first_line++; + yylloc->last_line++; + yylloc->last_column=0; +} + +<*>([[:space:]]{-}[\n])+ ; /* Eat all spaces, not new lines */ +<*>#.*$ ; /* Eat all comments */ + +{qstring} { + /* First copy the string without the quotes for use in the yacc parser */ + if ((yylval->string = strdup(yytext+1)) == NULL) { /* This allocates one useless tail char but... it's easier :D */ + return LEX_ERROR;/* on error, trig an error in yacc parser */ + } + yylval->string[yyleng-2] = '\0'; + + /* the yacc parser will check the string is valid */ + return QSTRING; +} + +[[:digit:]]+ { + /* Convert this to an integer value */ + int ret = sscanf(yytext, "%i", &yylval->integer); + if (ret != 1) { + /* No matching: an error occurred */ + fprintf(stderr, "Unable to convert the value '%s' to a valid number: %s\n", + yytext, strerror(errno)); + return LEX_ERROR; /* trig an error in yacc parser */ + /* Maybe we could REJECT instead of failing here? */ + } + return INTEGER; +} + + /* Full words tokens (keywords) */ +(?i:"FD_conf") { return FDCONF; } +(?i:"MYSQL_server") { return MYSQL_SERVER; } +(?i:"MYSQL_user") { return MYSQL_USER; } +(?i:"MYSQL_pass") { return MYSQL_PASS; } +(?i:"MYSQL_db") { return MYSQL_DB; } + + /* Valid single characters for yyparse */ +<*>[=,:;{}] { return yytext[0]; } + +<*>[[:alnum:]]+ | /* This rule is only useful to print a complete token in error messages */ + /* Unrecognized character */ +<*>. { + fprintf(stderr, "Unrecognized text on line %d col %d: '%s'.\n", + yylloc->first_line, yylloc->first_column, yytext); + return LEX_ERROR; +} + +%% diff --git a/openair-cn/OPENAIRHSS/OPENAIRHSS/utils/queue.h b/openair-cn/OPENAIRHSS/OPENAIRHSS/utils/queue.h new file mode 100644 index 0000000000..47f638b57c --- /dev/null +++ b/openair-cn/OPENAIRHSS/OPENAIRHSS/utils/queue.h @@ -0,0 +1,592 @@ +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + */ + +#ifndef _SYS_QUEUE_H_ +#define _SYS_QUEUE_H_ + +/* + * This file defines five types of data structures: singly-linked lists, + * lists, simple queues, tail queues, and circular queues. + * + * A singly-linked list is headed by a single forward pointer. The + * elements are singly linked for minimum space and pointer manipulation + * overhead at the expense of O(n) removal for arbitrary elements. New + * elements can be added to the list after an existing element or at the + * head of the list. Elements being removed from the head of the list + * should use the explicit macro for this purpose for optimum + * efficiency. A singly-linked list may only be traversed in the forward + * direction. Singly-linked lists are ideal for applications with large + * datasets and few or no removals or for implementing a LIFO queue. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before + * or after an existing element or at the head of the list. A list + * may only be traversed in the forward direction. + * + * A simple queue is headed by a pair of pointers, one the head of the + * list and the other to the tail of the list. The elements are singly + * linked to save space, so elements can only be removed from the + * head of the list. New elements can be added to the list after + * an existing element, at the head of the list, or at the end of the + * list. A simple queue may only be traversed in the forward direction. + * + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or + * after an existing element, at the head of the list, or at the end of + * the list. A tail queue may be traversed in either direction. + * + * A circle queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or after + * an existing element, at the head of the list, or at the end of the list. + * A circle queue may be traversed in either direction, but has a more + * complex end of list detection. + * + * For details on the use of these macros, see the queue(3) manual page. + * SLIST LIST STAILQ TAILQ CIRCLEQ + * _HEAD + + + + + + * _HEAD_INITIALIZER + + + + + + * _ENTRY + + + + + + * _INIT + + + + + + * _EMPTY + + + + + + * _FIRST + + + + + + * _NEXT + + + + + + * _PREV - - - + + + * _LAST - - + + + + * _FOREACH + + + + + + * _FOREACH_REVERSE - - - + + + * _INSERT_HEAD + + + + + + * _INSERT_BEFORE - + - + + + * _INSERT_AFTER + + + + + + * _INSERT_TAIL - - + + + + * _REMOVE_HEAD + - + - - + * _REMOVE + + + + + + */ + +/* + * List definitions. + */ +#define LIST_HEAD(name, type) \ + struct name { \ + struct type *lh_first; /* first element */ \ + } + +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LIST_ENTRY(type) \ + struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ + } + +/* + * List functions. + */ +#define LIST_INIT(head) do { \ + (head)->lh_first = NULL; \ + } while (/*CONSTCOND*/0) + +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ + (listelm)->field.le_next->field.le_prev = \ + &(elm)->field.le_next; \ + (listelm)->field.le_next = (elm); \ + (elm)->field.le_prev = &(listelm)->field.le_next; \ + } while (/*CONSTCOND*/0) + +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + (elm)->field.le_next = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &(elm)->field.le_next; \ + } while (/*CONSTCOND*/0) + +#define LIST_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.le_next = (head)->lh_first) != NULL) \ + (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ + (head)->lh_first = (elm); \ + (elm)->field.le_prev = &(head)->lh_first; \ + } while (/*CONSTCOND*/0) + +#define LIST_REMOVE(elm, field) do { \ + if ((elm)->field.le_next != NULL) \ + (elm)->field.le_next->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = (elm)->field.le_next; \ + } while (/*CONSTCOND*/0) + +#define LIST_FOREACH(var, head, field) \ + for ((var) = ((head)->lh_first); \ + (var); \ + (var) = ((var)->field.le_next)) + +/* + * List access methods. + */ +#define LIST_EMPTY(head) ((head)->lh_first == NULL) +#define LIST_FIRST(head) ((head)->lh_first) +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + + +/* + * Singly-linked List definitions. + */ +#define SLIST_HEAD(name, type) \ + struct name { \ + struct type *slh_first; /* first element */ \ + } + +#define SLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define SLIST_ENTRY(type) \ + struct { \ + struct type *sle_next; /* next element */ \ + } + +/* + * Singly-linked List functions. + */ +#define SLIST_INIT(head) do { \ + (head)->slh_first = NULL; \ + } while (/*CONSTCOND*/0) + +#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ + (elm)->field.sle_next = (slistelm)->field.sle_next; \ + (slistelm)->field.sle_next = (elm); \ + } while (/*CONSTCOND*/0) + +#define SLIST_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.sle_next = (head)->slh_first; \ + (head)->slh_first = (elm); \ + } while (/*CONSTCOND*/0) + +#define SLIST_REMOVE_HEAD(head, field) do { \ + (head)->slh_first = (head)->slh_first->field.sle_next; \ + } while (/*CONSTCOND*/0) + +#define SLIST_REMOVE(head, elm, type, field) do { \ + if ((head)->slh_first == (elm)) { \ + SLIST_REMOVE_HEAD((head), field); \ + } \ + else { \ + struct type *curelm = (head)->slh_first; \ + while(curelm->field.sle_next != (elm)) \ + curelm = curelm->field.sle_next; \ + curelm->field.sle_next = \ + curelm->field.sle_next->field.sle_next; \ + } \ + } while (/*CONSTCOND*/0) + +#define SLIST_FOREACH(var, head, field) \ + for((var) = (head)->slh_first; (var); (var) = (var)->field.sle_next) + +/* + * Singly-linked List access methods. + */ +#define SLIST_EMPTY(head) ((head)->slh_first == NULL) +#define SLIST_FIRST(head) ((head)->slh_first) +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + + +/* + * Singly-linked Tail queue declarations. + */ +#define STAILQ_HEAD(name, type) \ + struct name { \ + struct type *stqh_first; /* first element */ \ + struct type **stqh_last; /* addr of last next element */ \ + } + +#define STAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).stqh_first } + +#define STAILQ_ENTRY(type) \ + struct { \ + struct type *stqe_next; /* next element */ \ + } + +/* + * Singly-linked Tail queue functions. + */ +#define STAILQ_INIT(head) do { \ + (head)->stqh_first = NULL; \ + (head)->stqh_last = &(head)->stqh_first; \ + } while (/*CONSTCOND*/0) + +#define STAILQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.stqe_next = (head)->stqh_first) == NULL) \ + (head)->stqh_last = &(elm)->field.stqe_next; \ + (head)->stqh_first = (elm); \ + } while (/*CONSTCOND*/0) + +#define STAILQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.stqe_next = NULL; \ + *(head)->stqh_last = (elm); \ + (head)->stqh_last = &(elm)->field.stqe_next; \ + } while (/*CONSTCOND*/0) + +#define STAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.stqe_next = (listelm)->field.stqe_next) == NULL)\ + (head)->stqh_last = &(elm)->field.stqe_next; \ + (listelm)->field.stqe_next = (elm); \ + } while (/*CONSTCOND*/0) + +#define STAILQ_REMOVE_HEAD(head, field) do { \ + if (((head)->stqh_first = (head)->stqh_first->field.stqe_next) == NULL) \ + (head)->stqh_last = &(head)->stqh_first; \ + } while (/*CONSTCOND*/0) + +#define STAILQ_REMOVE(head, elm, type, field) do { \ + if ((head)->stqh_first == (elm)) { \ + STAILQ_REMOVE_HEAD((head), field); \ + } else { \ + struct type *curelm = (head)->stqh_first; \ + while (curelm->field.stqe_next != (elm)) \ + curelm = curelm->field.stqe_next; \ + if ((curelm->field.stqe_next = \ + curelm->field.stqe_next->field.stqe_next) == NULL) \ + (head)->stqh_last = &(curelm)->field.stqe_next; \ + } \ + } while (/*CONSTCOND*/0) + +#define STAILQ_FOREACH(var, head, field) \ + for ((var) = ((head)->stqh_first); \ + (var); \ + (var) = ((var)->field.stqe_next)) + +#define STAILQ_CONCAT(head1, head2) do { \ + if (!STAILQ_EMPTY((head2))) { \ + *(head1)->stqh_last = (head2)->stqh_first; \ + (head1)->stqh_last = (head2)->stqh_last; \ + STAILQ_INIT((head2)); \ + } \ + } while (/*CONSTCOND*/0) + +/* + * Singly-linked Tail queue access methods. + */ +#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL) +#define STAILQ_FIRST(head) ((head)->stqh_first) +#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) + + +/* + * Simple queue definitions. + */ +#define SIMPLEQ_HEAD(name, type) \ + struct name { \ + struct type *sqh_first; /* first element */ \ + struct type **sqh_last; /* addr of last next element */ \ + } + +#define SIMPLEQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).sqh_first } + +#define SIMPLEQ_ENTRY(type) \ + struct { \ + struct type *sqe_next; /* next element */ \ + } + +/* + * Simple queue functions. + */ +#define SIMPLEQ_INIT(head) do { \ + (head)->sqh_first = NULL; \ + (head)->sqh_last = &(head)->sqh_first; \ + } while (/*CONSTCOND*/0) + +#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (head)->sqh_first = (elm); \ + } while (/*CONSTCOND*/0) + +#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.sqe_next = NULL; \ + *(head)->sqh_last = (elm); \ + (head)->sqh_last = &(elm)->field.sqe_next; \ + } while (/*CONSTCOND*/0) + +#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (listelm)->field.sqe_next = (elm); \ + } while (/*CONSTCOND*/0) + +#define SIMPLEQ_REMOVE_HEAD(head, field) do { \ + if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ + (head)->sqh_last = &(head)->sqh_first; \ + } while (/*CONSTCOND*/0) + +#define SIMPLEQ_REMOVE(head, elm, type, field) do { \ + if ((head)->sqh_first == (elm)) { \ + SIMPLEQ_REMOVE_HEAD((head), field); \ + } else { \ + struct type *curelm = (head)->sqh_first; \ + while (curelm->field.sqe_next != (elm)) \ + curelm = curelm->field.sqe_next; \ + if ((curelm->field.sqe_next = \ + curelm->field.sqe_next->field.sqe_next) == NULL) \ + (head)->sqh_last = &(curelm)->field.sqe_next; \ + } \ + } while (/*CONSTCOND*/0) + +#define SIMPLEQ_FOREACH(var, head, field) \ + for ((var) = ((head)->sqh_first); \ + (var); \ + (var) = ((var)->field.sqe_next)) + +/* + * Simple queue access methods. + */ +#define SIMPLEQ_EMPTY(head) ((head)->sqh_first == NULL) +#define SIMPLEQ_FIRST(head) ((head)->sqh_first) +#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) + + +/* + * Tail queue definitions. + */ +#define _TAILQ_HEAD(name, type, qual) \ + struct name { \ + qual type *tqh_first; /* first element */ \ + qual type *qual *tqh_last; /* addr of last next element */ \ + } +#define TAILQ_HEAD(name, type) _TAILQ_HEAD(name, struct type,) + +#define TAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).tqh_first } + +#define _TAILQ_ENTRY(type, qual) \ + struct { \ + qual type *tqe_next; /* next element */ \ + qual type *qual *tqe_prev; /* address of previous next element */\ + } +#define TAILQ_ENTRY(type) _TAILQ_ENTRY(struct type,) + +/* + * Tail queue functions. + */ +#define TAILQ_INIT(head) do { \ + (head)->tqh_first = NULL; \ + (head)->tqh_last = &(head)->tqh_first; \ + } while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ + (head)->tqh_first->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (head)->tqh_first = (elm); \ + (elm)->field.tqe_prev = &(head)->tqh_first; \ + } while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.tqe_next = NULL; \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + } while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ + (elm)->field.tqe_next->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (listelm)->field.tqe_next = (elm); \ + (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ + } while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ + (elm)->field.tqe_next = (listelm); \ + *(listelm)->field.tqe_prev = (elm); \ + (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ + } while (/*CONSTCOND*/0) + +#define TAILQ_REMOVE(head, elm, field) do { \ + if (((elm)->field.tqe_next) != NULL) \ + (elm)->field.tqe_next->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ + } while (/*CONSTCOND*/0) + +#define TAILQ_FOREACH(var, head, field) \ + for ((var) = ((head)->tqh_first); \ + (var); \ + (var) = ((var)->field.tqe_next)) + +#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ + for ((var) = (*(((struct headname *)((head)->tqh_last))->tqh_last)); \ + (var); \ + (var) = (*(((struct headname *)((var)->field.tqe_prev))->tqh_last))) + +#define TAILQ_CONCAT(head1, head2, field) do { \ + if (!TAILQ_EMPTY(head2)) { \ + *(head1)->tqh_last = (head2)->tqh_first; \ + (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \ + (head1)->tqh_last = (head2)->tqh_last; \ + TAILQ_INIT((head2)); \ + } \ + } while (/*CONSTCOND*/0) + +/* + * Tail queue access methods. + */ +#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL) +#define TAILQ_FIRST(head) ((head)->tqh_first) +#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) + +#define TAILQ_LAST(head, headname) \ + (*(((struct headname *)((head)->tqh_last))->tqh_last)) +#define TAILQ_PREV(elm, headname, field) \ + (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) + + +/* + * Circular queue definitions. + */ +#define CIRCLEQ_HEAD(name, type) \ + struct name { \ + struct type *cqh_first; /* first element */ \ + struct type *cqh_last; /* last element */ \ + } + +#define CIRCLEQ_HEAD_INITIALIZER(head) \ + { (void *)&head, (void *)&head } + +#define CIRCLEQ_ENTRY(type) \ + struct { \ + struct type *cqe_next; /* next element */ \ + struct type *cqe_prev; /* previous element */ \ + } + +/* + * Circular queue functions. + */ +#define CIRCLEQ_INIT(head) do { \ + (head)->cqh_first = (void *)(head); \ + (head)->cqh_last = (void *)(head); \ + } while (/*CONSTCOND*/0) + +#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + (elm)->field.cqe_next = (listelm)->field.cqe_next; \ + (elm)->field.cqe_prev = (listelm); \ + if ((listelm)->field.cqe_next == (void *)(head)) \ + (head)->cqh_last = (elm); \ + else \ + (listelm)->field.cqe_next->field.cqe_prev = (elm); \ + (listelm)->field.cqe_next = (elm); \ + } while (/*CONSTCOND*/0) + +#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \ + (elm)->field.cqe_next = (listelm); \ + (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ + if ((listelm)->field.cqe_prev == (void *)(head)) \ + (head)->cqh_first = (elm); \ + else \ + (listelm)->field.cqe_prev->field.cqe_next = (elm); \ + (listelm)->field.cqe_prev = (elm); \ + } while (/*CONSTCOND*/0) + +#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.cqe_next = (head)->cqh_first; \ + (elm)->field.cqe_prev = (void *)(head); \ + if ((head)->cqh_last == (void *)(head)) \ + (head)->cqh_last = (elm); \ + else \ + (head)->cqh_first->field.cqe_prev = (elm); \ + (head)->cqh_first = (elm); \ + } while (/*CONSTCOND*/0) + +#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.cqe_next = (void *)(head); \ + (elm)->field.cqe_prev = (head)->cqh_last; \ + if ((head)->cqh_first == (void *)(head)) \ + (head)->cqh_first = (elm); \ + else \ + (head)->cqh_last->field.cqe_next = (elm); \ + (head)->cqh_last = (elm); \ + } while (/*CONSTCOND*/0) + +#define CIRCLEQ_REMOVE(head, elm, field) do { \ + if ((elm)->field.cqe_next == (void *)(head)) \ + (head)->cqh_last = (elm)->field.cqe_prev; \ + else \ + (elm)->field.cqe_next->field.cqe_prev = \ + (elm)->field.cqe_prev; \ + if ((elm)->field.cqe_prev == (void *)(head)) \ + (head)->cqh_first = (elm)->field.cqe_next; \ + else \ + (elm)->field.cqe_prev->field.cqe_next = \ + (elm)->field.cqe_next; \ + } while (/*CONSTCOND*/0) + +#define CIRCLEQ_FOREACH(var, head, field) \ + for ((var) = ((head)->cqh_first); \ + (var) != (const void *)(head); \ + (var) = ((var)->field.cqe_next)) + +#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \ + for ((var) = ((head)->cqh_last); \ + (var) != (const void *)(head); \ + (var) = ((var)->field.cqe_prev)) + +/* + * Circular queue access methods. + */ +#define CIRCLEQ_EMPTY(head) ((head)->cqh_first == (void *)(head)) +#define CIRCLEQ_FIRST(head) ((head)->cqh_first) +#define CIRCLEQ_LAST(head) ((head)->cqh_last) +#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next) +#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev) + +#define CIRCLEQ_LOOP_NEXT(head, elm, field) \ + (((elm)->field.cqe_next == (void *)(head)) \ + ? ((head)->cqh_first) \ + : (elm->field.cqe_next)) +#define CIRCLEQ_LOOP_PREV(head, elm, field) \ + (((elm)->field.cqe_prev == (void *)(head)) \ + ? ((head)->cqh_last) \ + : (elm->field.cqe_prev)) + +#endif /* sys/queue.h */