git-svn-id: http://svn.eurecom.fr/openair4G/trunk@4888 818b1a75-f10b-46b9-bf7c-635c3b92a50f
This commit is contained in:
@@ -0,0 +1 @@
|
||||
/usr/share/automake-1.11/COPYING
|
||||
@@ -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 `<wchar.h>' 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.
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -0,0 +1,100 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#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 <MCC><MNC> 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;
|
||||
}
|
||||
@@ -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_ */
|
||||
@@ -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
|
||||
@@ -0,0 +1,87 @@
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <gmp.h>
|
||||
|
||||
#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_ */
|
||||
@@ -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 <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#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 */
|
||||
@@ -0,0 +1,139 @@
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <gmp.h>
|
||||
#include <nettle/hmac.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <gmp.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <gmp.h>
|
||||
|
||||
#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 */
|
||||
@@ -0,0 +1,54 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
Executable
+2
@@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
autoreconf --force --install -I m4
|
||||
@@ -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";
|
||||
@@ -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";
|
||||
@@ -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; };
|
||||
+24
@@ -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
|
||||
@@ -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 <netdb.h>]])
|
||||
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}
|
||||
"
|
||||
@@ -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
|
||||
@@ -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 <pthread.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <error.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <mysql/mysql.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
@@ -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 <pthread.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <error.h>
|
||||
|
||||
#include <mysql/mysql.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
@@ -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 <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <mysql/mysql.h>
|
||||
|
||||
#include <netinet/in.h> /* 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_ */
|
||||
@@ -0,0 +1,135 @@
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <error.h>
|
||||
|
||||
#include <mysql/mysql.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
@@ -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 */;
|
||||
@@ -0,0 +1,5 @@
|
||||
openair-hss (1.0.0-1) UNRELEASED; urgency=low
|
||||
|
||||
* Initial release. (Closes: #XXXXXX)
|
||||
|
||||
-- Sebastien Roux <roux@nord> Wed, 10 Apr 2013 09:54:55 +0200
|
||||
@@ -0,0 +1 @@
|
||||
7
|
||||
@@ -0,0 +1,15 @@
|
||||
Source: openair-hss
|
||||
Maintainer: Sebastien ROUX <sebastien.roux@eurecom.fr>
|
||||
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
|
||||
@@ -0,0 +1 @@
|
||||
etc/openair-hss/
|
||||
@@ -0,0 +1 @@
|
||||
openair-hss_1.0.0-1_i386.deb misc optional
|
||||
@@ -0,0 +1,3 @@
|
||||
conf/hss.conf
|
||||
conf/hss_fd.conf
|
||||
db/oai_db.sql
|
||||
+12
@@ -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:
|
||||
@@ -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 <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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 <sebastien.roux@eurecom.fr>
|
||||
* \date 2013
|
||||
* \version 0.1
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
@@ -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 <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <freeDiameter/freeDiameter-host.h>
|
||||
#include <freeDiameter/libfdcore.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 <sebastien.roux@eurecom.fr>
|
||||
* \date 2013
|
||||
* \version 0.1
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
@@ -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 <freeDiameter/freeDiameter-host.h>
|
||||
#include <freeDiameter/libfdcore.h>
|
||||
|
||||
#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_ */
|
||||
@@ -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 <sebastien.roux@eurecom.fr>
|
||||
* \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;
|
||||
}
|
||||
@@ -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 <sebastien.roux@eurecom.fr>
|
||||
* \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;
|
||||
}
|
||||
@@ -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 <sebastien.roux@eurecom.fr>
|
||||
* \date 2013
|
||||
* \version 0.1
|
||||
*/
|
||||
|
||||
#include "hss_config.h"
|
||||
#include "db_proto.h"
|
||||
#include "s6a_proto.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_ */
|
||||
@@ -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 <sebastien.roux@eurecom.fr>
|
||||
* \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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -0,0 +1,55 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <freeDiameter/freeDiameter-host.h>
|
||||
#include <freeDiameter/libfdcore.h>
|
||||
|
||||
#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");
|
||||
}
|
||||
}
|
||||
@@ -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_ */
|
||||
@@ -0,0 +1,103 @@
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#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"));
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#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"));
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#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"));
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#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"));
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#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"));
|
||||
}
|
||||
@@ -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 <config.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
@@ -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 <string.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#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 */
|
||||
@@ -0,0 +1,19 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#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");
|
||||
}
|
||||
@@ -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
|
||||
@@ -0,0 +1,110 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
@@ -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_ */
|
||||
@@ -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 <sebastien.roux@eurecom.fr>
|
||||
* \date 2013
|
||||
* \version 0.1
|
||||
*/
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
|
||||
#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=<path>\n\t-c<path>\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;
|
||||
}
|
||||
@@ -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_ */
|
||||
@@ -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 <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#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 <string> QSTRING
|
||||
%token <integer> 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);
|
||||
}
|
||||
@@ -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 <stdio.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
%%
|
||||
@@ -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 */
|
||||
Reference in New Issue
Block a user