Mercurial > hg > freeDiameter
changeset 0:13530e1f02e3
Initial files imported
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.hgignore Fri Aug 28 19:14:42 2009 +0900 @@ -0,0 +1,9 @@ +syntax: glob +*.orig +*.rej +*~ +*.log +*.sum +.hg +build* +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CMakeLists.txt Fri Aug 28 19:14:42 2009 +0900 @@ -0,0 +1,43 @@ +# This file is the source for generating the Makefile for the project, using cmake tool (cmake.org) + +# Name of the project, and language +PROJECT("FreeDiameter" C) + +# Some subfolders may have tests +ENABLE_TESTING() + +# CMake version +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) + +# Location of additional CMake modules +SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/") + +# All source code should be POSIX 200112L compatible, but some other extensions might be used, so: +ADD_DEFINITIONS(-D_GNU_SOURCE) + +# Location for the include files +INCLUDE_DIRECTORIES(include) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}/include) +SUBDIRS(include/freediameter) + +# some subfolders use yacc and lex parsers +SET(BISON_GENERATE_DEFINES TRUE) +SET(BISON_PREFIX_OUTPUTS TRUE) +INCLUDE(CMakeUserUseBison) +SET(FLEX_PREFIX_OUTPUTS TRUE) +INCLUDE(CMakeUserUseFlex) +IF( NOT BISON_EXECUTABLE OR NOT FLEX_EXECUTABLE ) + MESSAGE( SEND_ERROR "Bison and Flex are required" ) +ENDIF( NOT BISON_EXECUTABLE OR NOT FLEX_EXECUTABLE ) +# Check that flex is at least 2.5.20 to support bison-bridge +# how to do the check with cmake??? + +# Location for the source code +SUBDIRS(libfreediameter) +SUBDIRS(freediameter) + +# Do we build the extensions? +OPTION(IGNORE_ALL_EXTENSIONS "Ignore the extensions completly?") +IF(NOT IGNORE_ALL_EXTENSIONS) + SUBDIRS(extensions) +ENDIF(NOT IGNORE_ALL_EXTENSIONS)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/INSTALL Fri Aug 28 19:14:42 2009 +0900 @@ -0,0 +1,39 @@ +This package uses CMake (cmake.org) as building system. You'll need the cmake tool in order +to generate the Makefiles for your platform. You can also select which extensions must be built. + +Building in a separate directory is recommended: +mkdir build +cd build +cmake ../ +make + +You can enable the unary tests by doing: +cmake -DNO_TESTS:BOOL=OFF ../ +make +make tests + +Note: instead of passing options on the command line, you can edit the CCmakeCache.txt file +or use a CMake front-end (for example cmake-gui) + +Note that there are dependencies on some external tools that may not be enforced by the configure script. +On Ubuntu Intrepid, the following packages were required (aptitude install ...): + gcc make flex bison libsctp1 libsctp-dev cmake + +On FreeBSD the following packages were required: + cmake flex bison +Then the cmake command had to look like: + cmake -DFLEX_EXECUTABLE:FILEPATH=/usr/local/bin/flex ... + +make install has not been tested yet and will probably not behave as expected! + +You can also configure which extensions to build with CMake: +NoExtensions:BOOL=OFF (or the following are ignored) +BUILD_APP_TEST:BOOL=ON +BUILD_RT_ANY:BOOL=ON +BUILD_RT_DEBUG:BOOL=ON +BUILD_RT_DEFAULT:BOOL=ON +BUILD_SAMPLE:BOOL=ON + +You can change the default configuration file name: +DEFAULT_CONF_FILE:STRING=/path/to/some/freediameter.conf +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LICENSE Fri Aug 28 19:14:42 2009 +0900 @@ -0,0 +1,34 @@ +This software package is copyrighted under the terms of the BSD license, as follow: + +Software License Agreement (BSD License) + +Copyright (c) 2009, WIDE Project and NICT +All rights reserved. + +Redistribution and use of this software in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* 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. + +* Neither the name of the WIDE Project or NICT nor the + names of its contributors may be used to endorse or + promote products derived from this software without + specific prior written permission of WIDE Project and + NICT. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/README Fri Aug 28 19:14:42 2009 +0900 @@ -0,0 +1,12 @@ +The FreeDiameter daemon implements the Diameter base protocol. +Loadable extensions add the logic of Diameter applications or advanced use of the daemon. +See http://aaa.koganei.wide.ad.jp/ for more information on the project. +FreeDiameter was previously known as the "waaad" project (WIDE AAA Daemon) + +This version is not related to the "freediameter" project from Sun, which seems abandoned. + +Author: Sebastien Decugis. + +See LICENSE file for legal information on this software. + +See INSTALL for information on building and using this software.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cmake/Modules/CMakeUserUseBison.cmake Fri Aug 28 19:14:42 2009 +0900 @@ -0,0 +1,71 @@ +# - Look for GNU Bison, the parser generator +# Based off a news post from Andy Cedilnik at Kitware +# Defines the following: +# BISON_EXECUTABLE - path to the bison executable +# BISON_FILE - parse a file with bison +# BISON_PREFIX_OUTPUTS - Set to true to make BISON_FILE produce prefixed +# symbols in the generated output based on filename. +# So for ${filename}.y, you'll get ${filename}parse(), etc. +# instead of yyparse(). +# BISON_GENERATE_DEFINES - Set to true to make BISON_FILE output the matching +# .h file for a .c file. You want this if you're using +# flex. + +IF(NOT DEFINED BISON_PREFIX_OUTPUTS) + SET(BISON_PREFIX_OUTPUTS FALSE) +ENDIF(NOT DEFINED BISON_PREFIX_OUTPUTS) + +IF(NOT DEFINED BISON_GENERATE_DEFINES) + SET(BISON_GENERATE_DEFINES FALSE) +ENDIF(NOT DEFINED BISON_GENERATE_DEFINES) + +IF(NOT BISON_EXECUTABLE) + MESSAGE(STATUS "Looking for bison") + FIND_PROGRAM(BISON_EXECUTABLE bison) + IF(BISON_EXECUTABLE) + MESSAGE(STATUS "Looking for bison -- ${BISON_EXECUTABLE}") + ENDIF(BISON_EXECUTABLE) +ENDIF(NOT BISON_EXECUTABLE) + +IF(BISON_EXECUTABLE) + MACRO(BISON_FILE FILENAME) + GET_FILENAME_COMPONENT(PATH "${FILENAME}" PATH) + IF("${PATH}" STREQUAL "") + SET(PATH_OPT "") + ELSE("${PATH}" STREQUAL "") + SET(PATH_OPT "/${PATH}") + ENDIF("${PATH}" STREQUAL "") + GET_FILENAME_COMPONENT(HEAD "${FILENAME}" NAME_WE) + IF(NOT EXISTS "${CMAKE_CURRENT_BINARY_DIR}${PATH_OPT}") + FILE(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}${PATH_OPT}") + ENDIF(NOT EXISTS "${CMAKE_CURRENT_BINARY_DIR}${PATH_OPT}") + IF(BISON_PREFIX_OUTPUTS) + SET(PREFIX "${HEAD}") + ELSE(BISON_PREFIX_OUTPUTS) + SET(PREFIX "yy") + ENDIF(BISON_PREFIX_OUTPUTS) + SET(OUTFILE "${CMAKE_CURRENT_BINARY_DIR}${PATH_OPT}/${HEAD}.tab.c") + IF(BISON_GENERATE_DEFINES) + SET(HEADER "${CMAKE_CURRENT_BINARY_DIR}${PATH_OPT}/${HEAD}.tab.h") + ADD_CUSTOM_COMMAND( + OUTPUT "${OUTFILE}" "${HEADER}" + COMMAND "${BISON_EXECUTABLE}" + ARGS "--name-prefix=${PREFIX}" + "--defines" + "--output-file=${OUTFILE}" + "${CMAKE_CURRENT_SOURCE_DIR}/${FILENAME}" + DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${FILENAME}") + SET_SOURCE_FILES_PROPERTIES("${OUTFILE}" "${HEADER}" PROPERTIES GENERATED TRUE) + SET_SOURCE_FILES_PROPERTIES("${HEADER}" PROPERTIES HEADER_FILE_ONLY TRUE) + ELSE(BISON_GENERATE_DEFINES) + ADD_CUSTOM_COMMAND( + OUTPUT "${OUTFILE}" + COMMAND "${BISON_EXECUTABLE}" + ARGS "--name-prefix=${PREFIX}" + "--output-file=${OUTFILE}" + "${CMAKE_CURRENT_SOURCE_DIR}/${FILENAME}" + DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${FILENAME}") + SET_SOURCE_FILES_PROPERTIES("${OUTFILE}" PROPERTIES GENERATED TRUE) + ENDIF(BISON_GENERATE_DEFINES) + ENDMACRO(BISON_FILE) +ENDIF(BISON_EXECUTABLE)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cmake/Modules/CMakeUserUseFlex.cmake Fri Aug 28 19:14:42 2009 +0900 @@ -0,0 +1,46 @@ +# - Look for GNU flex, the lexer generator. +# Defines the following: +# FLEX_EXECUTABLE - path to the flex executable +# FLEX_FILE - parse a file with flex +# FLEX_PREFIX_OUTPUTS - Set to true to make FLEX_FILE produce outputs of +# lex.${filename}.c, not lex.yy.c . Passes -P to flex. + +IF(NOT DEFINED FLEX_PREFIX_OUTPUTS) + SET(FLEX_PREFIX_OUTPUTS FALSE) +ENDIF(NOT DEFINED FLEX_PREFIX_OUTPUTS) + +IF(NOT FLEX_EXECUTABLE) + MESSAGE(STATUS "Looking for flex") + FIND_PROGRAM(FLEX_EXECUTABLE flex) + IF(FLEX_EXECUTABLE) + MESSAGE(STATUS "Looking for flex -- ${FLEX_EXECUTABLE}") + ENDIF(FLEX_EXECUTABLE) +ENDIF(NOT FLEX_EXECUTABLE) + +IF(FLEX_EXECUTABLE) + MACRO(FLEX_FILE FILENAME) + GET_FILENAME_COMPONENT(PATH "${FILENAME}" PATH) + IF("${PATH}" STREQUAL "") + SET(PATH_OPT "") + ELSE("${PATH}" STREQUAL "") + SET(PATH_OPT "/${PATH}") + ENDIF("${PATH}" STREQUAL "") + IF(NOT EXISTS "${CMAKE_CURRENT_BINARY_DIR}${PATH_OPT}") + FILE(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}${PATH_OPT}") + ENDIF(NOT EXISTS "${CMAKE_CURRENT_BINARY_DIR}${PATH_OPT}") + IF(FLEX_PREFIX_OUTPUTS) + GET_FILENAME_COMPONENT(PREFIX "${FILENAME}" NAME_WE) + ELSE(FLEX_PREFIX_OUTPUTS) + SET(PREFIX "yy") + ENDIF(FLEX_PREFIX_OUTPUTS) + SET(OUTFILE "${CMAKE_CURRENT_BINARY_DIR}${PATH_OPT}/lex.${PREFIX}.c") + ADD_CUSTOM_COMMAND( + OUTPUT "${OUTFILE}" + COMMAND "${FLEX_EXECUTABLE}" + ARGS "-P${PREFIX}" + "-o${OUTFILE}" + "${CMAKE_CURRENT_SOURCE_DIR}/${FILENAME}" + DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${FILENAME}") + SET_SOURCE_FILES_PROPERTIES("${OUTFILE}" PROPERTIES GENERATED TRUE) + ENDMACRO(FLEX_FILE) +ENDIF(FLEX_EXECUTABLE)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cmake/Modules/FindGNUTLS.cmake Fri Aug 28 19:14:42 2009 +0900 @@ -0,0 +1,29 @@ +# - Try to find GNU TLS library and headers +# Once done, this will define +# +# GNUTLS_FOUND - system has GNU TLS +# GNUTLS_INCLUDE_DIRS - the GNU TLS include directories +# GNUTLS_LIBRARIES - link these to use GNU TLS + +include(LibFindMacros) + +# Use pkg-config to get hints about paths +libfind_pkg_check_modules(GNUTLS_PKGCONF gnutls) + +# Include dir +find_path(GNUTLS_INCLUDE_DIR + NAMES gnutls/gnutls.h + PATHS ${GNUTLS_PKGCONF_INCLUDE_DIRS} +) + +# Finally the library itself +find_library(GNUTLS_LIBRARY + NAMES gnutls + PATHS ${GNUTLS_PKGCONF_LIBRARY_DIRS} +) + +# Set the include dir variables and the libraries and let libfind_process do the rest. +# NOTE: Singular variables for this library, plural for libraries this this lib depends on. +set(GNUTLS_PROCESS_INCLUDES GNUTLS_INCLUDE_DIR) +set(GNUTLS_PROCESS_LIBS GNUTLS_LIBRARY) +libfind_process(GNUTLS)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cmake/Modules/FindSCTP.cmake Fri Aug 28 19:14:42 2009 +0900 @@ -0,0 +1,29 @@ +# - Try to find SCTP library and headers +# Once done, this will define +# +# SCTP_FOUND - system has SCTP +# SCTP_INCLUDE_DIRS - the SCTP include directories +# SCTP_LIBRARIES - link these to use SCTP + +include(LibFindMacros) + +# Use pkg-config to get hints about paths (Note: not yet supported?) +# libfind_pkg_check_modules(SCTP_PKGCONF sctp) + +# Include dir +find_path(SCTP_INCLUDE_DIR + NAMES netinet/sctp.h + PATHS ${SCTP_PKGCONF_INCLUDE_DIRS} +) + +# Finally the library itself +find_library(SCTP_LIBRARY + NAMES sctp + PATHS ${SCTP_PKGCONF_LIBRARY_DIRS} +) + +# Set the include dir variables and the libraries and let libfind_process do the rest. +# NOTE: Singular variables for this library, plural for libraries this this lib depends on. +set(SCTP_PROCESS_INCLUDES SCTP_INCLUDE_DIR) +set(SCTP_PROCESS_LIBS SCTP_LIBRARY) +libfind_process(SCTP)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cmake/Modules/LibFindMacros.cmake Fri Aug 28 19:14:42 2009 +0900 @@ -0,0 +1,99 @@ +# Works the same as find_package, but forwards the "REQUIRED" and "QUIET" arguments +# used for the current package. For this to work, the first parameter must be the +# prefix of the current package, then the prefix of the new package etc, which are +# passed to find_package. +macro (libfind_package PREFIX) + set (LIBFIND_PACKAGE_ARGS ${ARGN}) + if (${PREFIX}_FIND_QUIETLY) + set (LIBFIND_PACKAGE_ARGS ${LIBFIND_PACKAGE_ARGS} QUIET) + endif (${PREFIX}_FIND_QUIETLY) + if (${PREFIX}_FIND_REQUIRED) + set (LIBFIND_PACKAGE_ARGS ${LIBFIND_PACKAGE_ARGS} REQUIRED) + endif (${PREFIX}_FIND_REQUIRED) + find_package(${LIBFIND_PACKAGE_ARGS}) +endmacro (libfind_package) + +# Damn CMake developers made the UsePkgConfig system deprecated in the same release (2.6) +# where they added pkg_check_modules. Consequently I need to support both in my scripts +# to avoid those deprecated warnings. Here's a helper that does just that. +# Works identically to pkg_check_modules, except that no checks are needed prior to use. +macro (libfind_pkg_check_modules PREFIX PKGNAME) + if (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4) + include(UsePkgConfig) + pkgconfig(${PKGNAME} ${PREFIX}_INCLUDE_DIRS ${PREFIX}_LIBRARY_DIRS ${PREFIX}_LDFLAGS ${PREFIX}_CFLAGS) + else (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4) + find_package(PkgConfig) + if (PKG_CONFIG_FOUND) + pkg_check_modules(${PREFIX} ${PKGNAME}) + endif (PKG_CONFIG_FOUND) + endif (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4) +endmacro (libfind_pkg_check_modules) + +# Do the final processing once the paths have been detected. +# If include dirs are needed, ${PREFIX}_PROCESS_INCLUDES should be set to contain +# all the variables, each of which contain one include directory. +# Ditto for ${PREFIX}_PROCESS_LIBS and library files. +# Will set ${PREFIX}_FOUND, ${PREFIX}_INCLUDE_DIRS and ${PREFIX}_LIBRARIES. +# Also handles errors in case library detection was required, etc. +macro (libfind_process PREFIX) + # Skip processing if already processed during this run + if (NOT ${PREFIX}_FOUND) + # Start with the assumption that the library was found + set (${PREFIX}_FOUND TRUE) + + # Process all includes and set _FOUND to false if any are missing + foreach (i ${${PREFIX}_PROCESS_INCLUDES}) + if (${i}) + set (${PREFIX}_INCLUDE_DIRS ${${PREFIX}_INCLUDE_DIRS} ${${i}}) + mark_as_advanced(${i}) + else (${i}) + set (${PREFIX}_FOUND FALSE) + endif (${i}) + endforeach (i) + + # Process all libraries and set _FOUND to false if any are missing + foreach (i ${${PREFIX}_PROCESS_LIBS}) + if (${i}) + set (${PREFIX}_LIBRARIES ${${PREFIX}_LIBRARIES} ${${i}}) + mark_as_advanced(${i}) + else (${i}) + set (${PREFIX}_FOUND FALSE) + endif (${i}) + endforeach (i) + + # Print message and/or exit on fatal error + if (${PREFIX}_FOUND) + if (NOT ${PREFIX}_FIND_QUIETLY) + message (STATUS "Found ${PREFIX} ${${PREFIX}_VERSION}") + endif (NOT ${PREFIX}_FIND_QUIETLY) + else (${PREFIX}_FOUND) + if (${PREFIX}_FIND_REQUIRED) + foreach (i ${${PREFIX}_PROCESS_INCLUDES} ${${PREFIX}_PROCESS_LIBS}) + message("${i}=${${i}}") + endforeach (i) + message (FATAL_ERROR "Required library ${PREFIX} NOT FOUND.\nInstall the library (dev version) and try again. If the library is already installed, use ccmake to set the missing variables manually.") + endif (${PREFIX}_FIND_REQUIRED) + endif (${PREFIX}_FOUND) + endif (NOT ${PREFIX}_FOUND) +endmacro (libfind_process) + +macro(libfind_library PREFIX basename) + set(TMP "") + if(MSVC80) + set(TMP -vc80) + endif(MSVC80) + if(MSVC90) + set(TMP -vc90) + endif(MSVC90) + set(${PREFIX}_LIBNAMES ${basename}${TMP}) + if(${ARGC} GREATER 2) + set(${PREFIX}_LIBNAMES ${basename}${TMP}-${ARGV2}) + string(REGEX REPLACE "\\." "_" TMP ${${PREFIX}_LIBNAMES}) + set(${PREFIX}_LIBNAMES ${${PREFIX}_LIBNAMES} ${TMP}) + endif(${ARGC} GREATER 2) + find_library(${PREFIX}_LIBRARY + NAMES ${${PREFIX}_LIBNAMES} + PATHS ${${PREFIX}_PKGCONF_LIBRARY_DIRS} + ) +endmacro(libfind_library) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/update_copyright.sh Fri Aug 28 19:14:42 2009 +0900 @@ -0,0 +1,33 @@ +#!/bin/bash -x + +# This script will search all waaad copyrights from source files, and update these if +# the waaad source was modified later. + +if [ ! -f waaad/waaad-internal.h ]; +then echo "This script must be run from waaad top directory" +exit 1; +fi; + +# Create a clean working copy +TMPDIR=`mktemp -d up_cop.XXXXXXX` || exit 1 +hg clone . $TMPDIR/waaad || exit 1 +pushd $TMPDIR/waaad + +# Now, for each file +for SRC_FILE in `find . -name .hg -prune -or -type f -exec grep -q 'Copyright (c) 20.., WIDE Project and NICT' {} \; -print`; +do +HG_YEAR=`hg log --template '{date|shortdate}' $SRC_FILE | tr - ' ' | awk '{print \$1}'` +CPY_YEAR=`grep 'Copyright (c) 20.., WIDE Project and NICT' $SRC_FILE | awk '{print substr(\$4, 1, 4) }'` +if [ $HG_YEAR -gt $CPY_YEAR ]; +then +echo "Updating copyright $CPY_YEAR -> $HG_YEAR in $SRC_FILE"; +sed -i -e "s/Copyright (c) $CPY_YEAR, WIDE Project and NICT/Copyright (c) $HG_YEAR, WIDE Project and NICT/" $SRC_FILE +fi; +done + +hg commit -m"Updated copyright information" +hg push +popd +rm -rf $TMPDIR +hg update +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/freediameter/CMakeLists.txt Fri Aug 28 19:14:42 2009 +0900 @@ -0,0 +1,24 @@ +# The subproject name +Project("freediameterd" C) + +SET(PROJECT_VERSION 0.1) +SET(PROJECT_COPYRIGHT "Copyright (c) 2008-2009, WIDE Project (www.wide.ad.jp) and NICT (www.nict.go.jp)") + +# List of source files +SET(FD_COMMON_SRC + fd.h + dict_base_proto.c + ) + +# Building the executable +ADD_EXECUTABLE(freediameterd ${FD_COMMON_SRC} main.c) + +# The link command +LINK_DIRECTORIES(${CURRENT_BINARY_DIR}/../libfreediameter) +TARGET_LINK_LIBRARIES(freediameterd libfreediameter ${FD_LIBS}) + +# The unary tests directory +OPTION(SKIP_TESTS "Skip compilation of the tests?" OFF) +IF ( NOT SKIP_TESTS ) + SUBDIRS(tests) +ENDIF ( NOT SKIP_TESTS )
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/freediameter/dict_base_proto.c Fri Aug 28 19:14:42 2009 +0900 @@ -0,0 +1,3435 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis <sdecugis@nict.go.jp> * +* * +* Copyright (c) 2009, WIDE Project and NICT * +* All rights reserved. * +* * +* Redistribution and use of this software in source and binary forms, with or without modification, are * +* permitted provided that the following conditions are met: * +* * +* * Redistributions of source code must retain the above * +* copyright notice, this list of conditions and the * +* following disclaimer. * +* * +* * 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. * +* * +* * Neither the name of the WIDE Project or NICT nor the * +* names of its contributors may be used to endorse or * +* promote products derived from this software without * +* specific prior written permission of WIDE Project and * +* NICT. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. * +*********************************************************************************************************/ + +/* Diameter Base protocol definitions. + */ + +#include "fd.h" + +#include <netinet/in.h> +#include <sys/socket.h> + +/* The pointer for the global dictionary (initialized from main) */ +struct dictionary * fd_g_dict = NULL; + +/* The functions to encode and interpret the derived types defined in the base protocol */ + +/* Address AVP <-> struct sockaddr_storage */ +static int Address_encode(void * data, union avp_value * avp_value) +{ + sSS * ss = (sSS *) data; + uint16_t AddressType = 0; + size_t size = 0; + unsigned char * buf = NULL; + + TRACE_ENTRY("%p %p", data, avp_value); + CHECK_PARAMS( data && avp_value ); + + switch (ss->ss_family) { + case AF_INET: + { + /* We are encoding an IP address */ + sSA4 * sin = (sSA4 *)ss; + + AddressType = 1;/* see http://www.iana.org/assignments/address-family-numbers/ */ + size = 6; /* 2 for AddressType + 4 for data */ + + CHECK_MALLOC( buf = malloc(size) ); + + /* may not work because of alignment: *(uint32_t *)(buf+2) = htonl(sin->sin_addr.s_addr); */ + memcpy(buf + 2, &sin->sin_addr.s_addr, 4); + } + break; + + case AF_INET6: + { + /* We are encoding an IPv6 address */ + sSA6 * sin6 = (sSA6 *)ss; + + AddressType = 2;/* see http://www.iana.org/assignments/address-family-numbers/ */ + size = 18; /* 2 for AddressType + 16 for data */ + + CHECK_MALLOC( buf = malloc(size) ); + + /* The order is already good here */ + memcpy(buf + 2, &sin6->sin6_addr.s6_addr, 16); + } + break; + + default: + CHECK_PARAMS( AddressType = 0 ); + } + + *(uint16_t *)buf = htons(AddressType); + + avp_value->os.len = size; + avp_value->os.data = buf; + + return 0; +} + +static int Address_interpret(union avp_value * avp_value, void * interpreted) +{ + uint16_t AddressType = 0; + unsigned char * buf; + + TRACE_ENTRY("%p %p", avp_value, interpreted); + + CHECK_PARAMS( avp_value && interpreted && (avp_value->os.len >= 2) ); + + AddressType = ntohs(*(uint16_t *)avp_value->os.data); + buf = &avp_value->os.data[2]; + + switch (AddressType) { + case 1 /* IP */: + { + sSA4 * sin = (sSA4 *)interpreted; + + CHECK_PARAMS( avp_value->os.len == 6 ); + + sin->sin_family = AF_INET; + /* sin->sin_addr.s_addr = ntohl( * (uint32_t *) buf); -- may not work because of bad alignment */ + memcpy(&sin->sin_addr.s_addr, buf, 4); + } + break; + + case 2 /* IP6 */: + { + sSA6 * sin6 = (sSA6 *)interpreted; + + CHECK_PARAMS( avp_value->os.len == 18 ); + + sin6->sin6_family = AF_INET6; + memcpy(&sin6->sin6_addr.s6_addr, buf, 16); + } + break; + + default: + CHECK_PARAMS( AddressType = 0 ); + } + + return 0; +} + + + +#define CHECK_dict_new( _type, _data, _parent, _ref ) \ + CHECK_FCT( fd_dict_new( dict, (_type), (_data), (_parent), (_ref)) ); + +#define CHECK_dict_search( _type, _criteria, _what, _result ) \ + CHECK_FCT( fd_dict_search( dict, (_type), (_criteria), (_what), (_result), ENOENT) ); + +struct local_rules_definition { + char *avp_name; + enum rule_position position; + int min; + int max; +}; + +#define RULE_ORDER( _position ) ((((_position) == RULE_FIXED_HEAD) || ((_position) == RULE_FIXED_TAIL)) ? 1 : 0 ) + +#define PARSE_loc_rules( _rulearray, _parent) { \ + int __ar; \ + for (__ar=0; __ar < sizeof(_rulearray) / sizeof((_rulearray)[0]); __ar++) { \ + struct dict_rule_data __data = { NULL, \ + (_rulearray)[__ar].position, \ + 0, \ + (_rulearray)[__ar].min, \ + (_rulearray)[__ar].max}; \ + __data.rule_order = RULE_ORDER(__data.rule_position); \ + CHECK_FCT( fd_dict_search( \ + dict, \ + DICT_AVP, \ + AVP_BY_NAME, \ + (_rulearray)[__ar].avp_name, \ + &__data.rule_avp, 0 ) ); \ + if ( !__data.rule_avp ) { \ + TRACE_DEBUG(INFO, "AVP Not found: '%s'", (_rulearray)[__ar].avp_name ); \ + return ENOENT; \ + } \ + CHECK_FCT_DO( fd_dict_new( dict, DICT_RULE, &__data, _parent, NULL), \ + { \ + TRACE_DEBUG(INFO, "Error on rule with AVP '%s'", \ + (_rulearray)[__ar].avp_name ); \ + return EINVAL; \ + } ); \ + } \ +} + +int fd_dict_base_protocol(struct dictionary * dict) +{ + TRACE_ENTRY("%p", dict); + CHECK_PARAMS(dict); + + /* Vendors section */ + { + /* The base RFC has no vendor information */ + ; + } + + /* Applications section */ + { + /* base accounting application */ + { + struct dict_application_data data = { 3, "Diameter Base Accounting" }; + CHECK_dict_new( DICT_APPLICATION, &data, NULL, NULL); + } + + /* relay application */ + { + struct dict_application_data data = { 0xffffffff, "Relay" }; + CHECK_dict_new( DICT_APPLICATION, &data , NULL, NULL); + } + } + + /* Derived AVP types section */ + { + /* Address */ + { + /* + The Address format is derived from the OctetString AVP Base + Format. It is a discriminated union, representing, for example a + 32-bit (IPv4) [RFC791] or 128-bit (IPv6) [RFC4291] address, most + significant octet first. The first two octets of the Address AVP + represents the AddressType, which contains an Address Family + defined in [IANAADFAM]. The AddressType is used to discriminate + the content and format of the remaining octets. + */ + struct dict_type_data data = { AVP_TYPE_OCTETSTRING, "Address" , Address_interpret , Address_encode }; + CHECK_dict_new( DICT_TYPE, &data , NULL, NULL); + } + + /* Time */ + { + /* + The Time format is derived from the OctetString AVP Base Format. + The string MUST contain four octets, in the same format as the + first four bytes are in the NTP timestamp format. The NTP + Timestamp format is defined in chapter 3 of [RFC4330]. + + This represents the number of seconds since 0h on 1 January 1900 + with respect to the Coordinated Universal Time (UTC). + + On 6h 28m 16s UTC, 7 February 2036 the time value will overflow. + SNTP [RFC4330] describes a procedure to extend the time to 2104. + This procedure MUST be supported by all DIAMETER nodes. + */ + struct dict_type_data data = { AVP_TYPE_OCTETSTRING, "Time" , NULL , NULL }; + CHECK_dict_new( DICT_TYPE, &data , NULL, NULL); + } + + /* UTF8String */ + { + /* + The UTF8String format is derived from the OctetString AVP Base + Format. This is a human readable string represented using the + ISO/IEC IS 10646-1 character set, encoded as an OctetString using + the UTF-8 [RFC3629] transformation format described in RFC 3629. + + Since additional code points are added by amendments to the 10646 + standard from time to time, implementations MUST be prepared to + encounter any code point from 0x00000001 to 0x7fffffff. Byte + sequences that do not correspond to the valid encoding of a code + point into UTF-8 charset or are outside this range are prohibited. + + The use of control codes SHOULD be avoided. When it is necessary + to represent a new line, the control code sequence CR LF SHOULD be + used. + + The use of leading or trailing white space SHOULD be avoided. + + For code points not directly supported by user interface hardware + or software, an alternative means of entry and display, such as + hexadecimal, MAY be provided. + + For information encoded in 7-bit US-ASCII, the UTF-8 charset is + identical to the US-ASCII charset. + + UTF-8 may require multiple bytes to represent a single character / + code point; thus the length of an UTF8String in octets may be + different from the number of characters encoded. + + Note that the AVP Length field of an UTF8String is measured in + octets, not characters. + */ + struct dict_type_data data = { AVP_TYPE_OCTETSTRING, "UTF8String" , NULL , NULL }; + CHECK_dict_new( DICT_TYPE, &data , NULL, NULL); + } + + /* DiameterIdentity */ + { + /* + The DiameterIdentity format is derived from the OctetString AVP + Base Format. + + DiameterIdentity = FQDN + + + DiameterIdentity value is used to uniquely identify a Diameter + node for purposes of duplicate connection and routing loop + detection. + + The contents of the string MUST be the FQDN of the Diameter node. + If multiple Diameter nodes run on the same host, each Diameter + node MUST be assigned a unique DiameterIdentity. If a Diameter + + node can be identified by several FQDNs, a single FQDN should be + picked at startup, and used as the only DiameterIdentity for that + node, whatever the connection it is sent on. Note that in this + document, DiameterIdentity is in ASCII form in order to be + compatible with existing DNS infrastructure. See Appendix D for + interactions between the Diameter protocol and Internationalized + Domain Name (IDNs). + */ + struct dict_type_data data = { AVP_TYPE_OCTETSTRING, "DiameterIdentity" , NULL , NULL }; + CHECK_dict_new( DICT_TYPE, &data , NULL, NULL); + } + + /* DiameterURI */ + { + /* + The DiameterURI MUST follow the Uniform Resource Identifiers (URI) + syntax [RFC3986] rules specified below: + + "aaa://" FQDN [ port ] [ transport ] [ protocol ] + + ; No transport security + + "aaas://" FQDN [ port ] [ transport ] [ protocol ] + + ; Transport security used + + FQDN = Fully Qualified Host Name + + port = ":" 1*DIGIT + + ; One of the ports used to listen for + ; incoming connections. + ; If absent, + ; the default Diameter port (3868) is + ; assumed. + + transport = ";transport=" transport-protocol + + ; One of the transports used to listen + ; for incoming connections. If absent, + ; the default SCTP [RFC2960] protocol is + ; assumed. UDP MUST NOT be used when + ; the aaa-protocol field is set to + ; diameter. + + transport-protocol = ( "tcp" / "sctp" / "udp" ) + + protocol = ";protocol=" aaa-protocol + + ; If absent, the default AAA protocol + ; is diameter. + + aaa-protocol = ( "diameter" / "radius" / "tacacs+" ) + + The following are examples of valid Diameter host identities: + + aaa://host.example.com;transport=tcp + aaa://host.example.com:6666;transport=tcp + aaa://host.example.com;protocol=diameter + aaa://host.example.com:6666;protocol=diameter + aaa://host.example.com:6666;transport=tcp;protocol=diameter + aaa://host.example.com:1813;transport=udp;protocol=radius + */ + struct dict_type_data data = { AVP_TYPE_OCTETSTRING, "DiameterURI" , NULL , NULL }; + CHECK_dict_new( DICT_TYPE, &data , NULL, NULL); + } + + /* Enumerated */ + { + /* + Enumerated is derived from the Integer32 AVP Base Format. The + definition contains a list of valid values and their + interpretation and is described in the Diameter application + introducing the AVP. + */ + + /* We don't use a generic "Enumerated" type in waaad. Instead, we define + * types of the form "Enumerated(<avpname>)" where <avpname> is replaced + * by the name of the AVP to which the type applies. + * Example: Enumerated(Disconnect-Cause) + */ + ; + } + + /* IPFilterRule */ + { + /* + The IPFilterRule format is derived from the OctetString AVP Base + Format and uses the ASCII charset. The rule syntax is a modified + subset of ipfw(8) from FreeBSD. Packets may be filtered based on + the following information that is associated with it: + + Direction (in or out) + Source and destination IP address (possibly masked) + Protocol + Source and destination port (lists or ranges) + TCP flags + IP fragment flag + IP options + ICMP types + + Rules for the appropriate direction are evaluated in order, with + the first matched rule terminating the evaluation. Each packet is + evaluated once. If no rule matches, the packet is dropped if the + last rule evaluated was a permit, and passed if the last rule was + a deny. + + IPFilterRule filters MUST follow the format: + + action dir proto from src to dst [options] + + (...skipped loooong explanation...) + + There is one kind of packet that the access device MUST always + discard, that is an IP fragment with a fragment offset of one. + This is a valid packet, but it only has one use, to try to + circumvent firewalls. + + An access device that is unable to interpret or apply a deny rule + MUST terminate the session. An access device that is unable to + interpret or apply a permit rule MAY apply a more restrictive + rule. An access device MAY apply deny rules of its own before the + supplied rules, for example to protect the access device owner's + infrastructure. + */ + struct dict_type_data data = { AVP_TYPE_OCTETSTRING, "IPFilterRule" , NULL , NULL }; + CHECK_dict_new( DICT_TYPE, &data , NULL, NULL); + } + } + + /* AVP section */ + { + struct dict_object * Address_type; + struct dict_object * UTF8String_type; + struct dict_object * DiameterIdentity_type; + struct dict_object * DiameterURI_type; + struct dict_object * Time_type; + + CHECK_dict_search( DICT_TYPE, TYPE_BY_NAME, "Address", &Address_type); + CHECK_dict_search( DICT_TYPE, TYPE_BY_NAME, "UTF8String", &UTF8String_type); + CHECK_dict_search( DICT_TYPE, TYPE_BY_NAME, "DiameterIdentity", &DiameterIdentity_type); + CHECK_dict_search( DICT_TYPE, TYPE_BY_NAME, "DiameterURI", &DiameterURI_type); + CHECK_dict_search( DICT_TYPE, TYPE_BY_NAME, "Time", &Time_type); + + /* Vendor-Id */ + { + /* + The Vendor-Id AVP (AVP Code 266) is of type Unsigned32 and contains + the IANA "SMI Network Management Private Enterprise Codes" [RFC3232] + value assigned to the vendor of the Diameter device. It is + envisioned that the combination of the Vendor-Id, Product-Name + (Section 5.3.7) and the Firmware-Revision (Section 5.3.4) AVPs may + provide useful debugging information. + + A Vendor-Id value of zero in the CER or CEA messages is reserved and + indicates that this field is ignored. + */ + struct dict_avp_data data = { + 266, /* Code */ + #if AC_VENDOR_ID != 266 + #error "AC_VENDOR_ID definition mismatch" + #endif + 0, /* Vendor */ + "Vendor-Id", /* Name */ + AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, /* Fixed flags */ + AVP_FLAG_MANDATORY, /* Fixed flag values */ + AVP_TYPE_UNSIGNED32 /* base type of data */ + }; + CHECK_dict_new( DICT_AVP, &data, NULL, NULL); + } + + /* Firmware-Revision */ + { + /* + The Firmware-Revision AVP (AVP Code 267) is of type Unsigned32 and is + used to inform a Diameter peer of the firmware revision of the + issuing device. + + For devices that do not have a firmware revision (general purpose + computers running Diameter software modules, for instance), the + revision of the Diameter software module may be reported instead. + */ + struct dict_avp_data data = { + 267, /* Code */ + #if AC_FIRMWARE_REVISION != 267 + #error "AC_FIRMWARE_REVISION definition mismatch" + #endif + 0, /* Vendor */ + "Firmware-Revision", /* Name */ + AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, /* Fixed flags */ + 0, /* Fixed flag values */ + AVP_TYPE_UNSIGNED32 /* base type of data */ + }; + CHECK_dict_new( DICT_AVP, &data , NULL, NULL); + } + + /* Host-IP-Address */ + { + /* + The Host-IP-Address AVP (AVP Code 257) is of type Address and is used + to inform a Diameter peer of the sender's IP address. All source + addresses that a Diameter node expects to use with SCTP [RFC2960] + MUST be advertised in the CER and CEA messages by including a + Host-IP- Address AVP for each address. This AVP MUST ONLY be used in + the CER and CEA messages. + */ + struct dict_avp_data data = { + 257, /* Code */ + #if AC_HOST_IP_ADDRESS != 257 + #error "AC_HOST_IP_ADDRESS definition mismatch" + #endif + 0, /* Vendor */ + "Host-IP-Address", /* Name */ + AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, /* Fixed flags */ + AVP_FLAG_MANDATORY, /* Fixed flag values */ + AVP_TYPE_OCTETSTRING /* base type of data */ + }; + CHECK_dict_new( DICT_AVP, &data , Address_type, NULL); + } + + /* Supported-Vendor-Id */ + { + /* + The Supported-Vendor-Id AVP (AVP Code 265) is of type Unsigned32 and + contains the IANA "SMI Network Management Private Enterprise Codes" + [RFC3232] value assigned to a vendor other than the device vendor but + including the application vendor. This is used in the CER and CEA + messages in order to inform the peer that the sender supports (a + subset of) the vendor-specific AVPs defined by the vendor identified + in this AVP. The value of this AVP SHOULD NOT be set to zero. + Multiple instances of this AVP containing the same value SHOULD NOT + be sent. + */ + struct dict_avp_data data = { + 265, /* Code */ + #if AC_SUPPORTED_VENDOR_ID != 265 + #error "AC_SUPPORTED_VENDOR_ID definition mismatch" + #endif + 0, /* Vendor */ + "Supported-Vendor-Id", /* Name */ + AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, /* Fixed flags */ + AVP_FLAG_MANDATORY, /* Fixed flag values */ + AVP_TYPE_UNSIGNED32 /* base type of data */ + }; + CHECK_dict_new( DICT_AVP, &data , NULL, NULL); + } + + /* Product-Name */ + { + /* + The Product-Name AVP (AVP Code 269) is of type UTF8String, and + contains the vendor assigned name for the product. The Product-Name + AVP SHOULD remain constant across firmware revisions for the same + product. + */ + struct dict_avp_data data = { + 269, /* Code */ + #if AC_PRODUCT_NAME != 269 + #error "AC_PRODUCT_NAME definition mismatch" + #endif + 0, /* Vendor */ + "Product-Name", /* Name */ + AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, /* Fixed flags */ + 0, /* Fixed flag values */ + AVP_TYPE_OCTETSTRING /* base type of data */ + }; + CHECK_dict_new( DICT_AVP, &data , UTF8String_type, NULL); + } + + /* Disconnect-Cause */ + { + /* + The Disconnect-Cause AVP (AVP Code 273) is of type Enumerated. A + Diameter node MUST include this AVP in the Disconnect-Peer-Request + message to inform the peer of the reason for its intention to + shutdown the transport connection. The following values are + supported: + + REBOOTING 0 + A scheduled reboot is imminent. Receiver of DPR with above result + code MAY attempt reconnection. + + BUSY 1 + The peer's internal resources are constrained, and it has + determined that the transport connection needs to be closed. + Receiver of DPR with above result code SHOULD NOT attempt + reconnection. + + DO_NOT_WANT_TO_TALK_TO_YOU 2 + The peer has determined that it does not see a need for the + transport connection to exist, since it does not expect any + messages to be exchanged in the near future. Receiver of DPR + with above result code SHOULD NOT attempt reconnection. + */ + struct dict_object * type; + struct dict_type_data tdata = { AVP_TYPE_INTEGER32, "Enumerated(Disconnect-Cause)" , NULL, NULL}; + struct dict_enumval_data t_0 = { "REBOOTING", { .i32 = 0 }}; + struct dict_enumval_data t_1 = { "BUSY", { .i32 = 1 }}; + struct dict_enumval_data t_2 = { "DO_NOT_WANT_TO_TALK_TO_YOU", { .i32 = 2 }}; + struct dict_avp_data data = { + 273, /* Code */ + #if AC_DISCONNECT_CAUSE != 273 + #error "AC_DISCONNECT_CAUSE definition mismatch" + #endif + 0, /* Vendor */ + "Disconnect-Cause", /* Name */ + AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, /* Fixed flags */ + AVP_FLAG_MANDATORY, /* Fixed flag values */ + AVP_TYPE_INTEGER32 /* base type of data */ + }; + /* Create the Enumerated type, and then the AVP */ + CHECK_dict_new( DICT_TYPE, &tdata , NULL, &type); + CHECK_dict_new( DICT_ENUMVAL, &t_0 , type, NULL); + CHECK_dict_new( DICT_ENUMVAL, &t_1 , type, NULL); + CHECK_dict_new( DICT_ENUMVAL, &t_2 , type, NULL); + CHECK_dict_new( DICT_AVP, &data , type, NULL); + } + + /* Origin-Host */ + { + /* + The Origin-Host AVP (AVP Code 264) is of type DiameterIdentity, and + MUST be present in all Diameter messages. This AVP identifies the + endpoint that originated the Diameter message. Relay agents MUST NOT + modify this AVP. + + The value of the Origin-Host AVP is guaranteed to be unique within a + single host. + + Note that the Origin-Host AVP may resolve to more than one address as + the Diameter peer may support more than one address. + + This AVP SHOULD be placed as close to the Diameter header as + possible. + */ + struct dict_avp_data data = { + 264, /* Code */ + #if AC_ORIGIN_HOST != 264 + #error "AC_ORIGIN_HOST definition mismatch" + #endif + 0, /* Vendor */ + "Origin-Host", /* Name */ + AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, /* Fixed flags */ + AVP_FLAG_MANDATORY, /* Fixed flag values */ + AVP_TYPE_OCTETSTRING /* base type of data */ + }; + CHECK_dict_new( DICT_AVP, &data , DiameterIdentity_type, NULL); + } + + /* Origin-Realm */ + { + /* + The Origin-Realm AVP (AVP Code 296) is of type DiameterIdentity. + This AVP contains the Realm of the originator of any Diameter message + and MUST be present in all messages. + + This AVP SHOULD be placed as close to the Diameter header as + possible. + */ + struct dict_avp_data data = { + 296, /* Code */ + #if AC_ORIGIN_REALM != 296 + #error "AC_ORIGIN_REALM definition mismatch" + #endif + 0, /* Vendor */ + "Origin-Realm", /* Name */ + AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, /* Fixed flags */ + AVP_FLAG_MANDATORY, /* Fixed flag values */ + AVP_TYPE_OCTETSTRING /* base type of data */ + }; + CHECK_dict_new( DICT_AVP, &data , DiameterIdentity_type, NULL); + } + + /* Destination-Host */ + { + /* + The Destination-Host AVP (AVP Code 293) is of type DiameterIdentity. + This AVP MUST be present in all unsolicited agent initiated messages, + MAY be present in request messages, and MUST NOT be present in Answer + messages. + + The absence of the Destination-Host AVP will cause a message to be + sent to any Diameter server supporting the application within the + realm specified in Destination-Realm AVP. + + This AVP SHOULD be placed as close to the Diameter header as + possible. + */ + struct dict_avp_data data = { + 293, /* Code */ + #if AC_DESTINATION_HOST != 293 + #error "AC_DESTINATION_HOST definition mismatch" + #endif + 0, /* Vendor */ + "Destination-Host", /* Name */ + AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, /* Fixed flags */ + AVP_FLAG_MANDATORY, /* Fixed flag values */ + AVP_TYPE_OCTETSTRING /* base type of data */ + }; + CHECK_dict_new( DICT_AVP, &data , DiameterIdentity_type, NULL); + } + + /* Destination-Realm */ + { + /* + The Destination-Realm AVP (AVP Code 283) is of type DiameterIdentity, + and contains the realm the message is to be routed to. The + Destination-Realm AVP MUST NOT be present in Answer messages. + Diameter Clients insert the realm portion of the User-Name AVP. + Diameter servers initiating a request message use the value of the + Origin-Realm AVP from a previous message received from the intended + target host (unless it is known a priori). When present, the + Destination-Realm AVP is used to perform message routing decisions. + + Request messages whose ABNF does not list the Destination-Realm AVP + as a mandatory AVP are inherently non-routable messages. + + This AVP SHOULD be placed as close to the Diameter header as + possible. + */ + struct dict_avp_data data = { + 283, /* Code */ + #if AC_DESTINATION_REALM != 283 + #error "AC_DESTINATION_REALM definition mismatch" + #endif + 0, /* Vendor */ + "Destination-Realm", /* Name */ + AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, /* Fixed flags */ + AVP_FLAG_MANDATORY, /* Fixed flag values */ + AVP_TYPE_OCTETSTRING /* base type of data */ + }; + CHECK_dict_new( DICT_AVP, &data , DiameterIdentity_type, NULL); + } + + /* Route-Record */ + { + /* + The Route-Record AVP (AVP Code 282) is of type DiameterIdentity. The + identity added in this AVP MUST be the same as the one received in + the Origin-Host of the Capabilities Exchange message. + */ + struct dict_avp_data data = { + 282, /* Code */ + #if AC_ROUTE_RECORD != 282 + #error "AC_ROUTE_RECORD definition mismatch" + #endif + 0, /* Vendor */ + "Route-Record", /* Name */ + AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, /* Fixed flags */ + AVP_FLAG_MANDATORY, /* Fixed flag values */ + AVP_TYPE_OCTETSTRING /* base type of data */ + }; + CHECK_dict_new( DICT_AVP, &data , DiameterIdentity_type, NULL); + } + + /* Proxy-Host */ + { + /* + The Proxy-Host AVP (AVP Code 280) is of type DiameterIdentity. This + AVP contains the identity of the host that added the Proxy-Info AVP. + */ + struct dict_avp_data adata = { + 280, /* Code */ + #if AC_PROXY_HOST != 280 + #error "AC_PROXY_HOST definition mismatch" + #endif + 0, /* Vendor */ + "Proxy-Host", /* Name */ + AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, /* Fixed flags */ + AVP_FLAG_MANDATORY, /* Fixed flag values */ + AVP_TYPE_OCTETSTRING /* base type of data */ + }; + CHECK_dict_new( DICT_AVP, &adata , DiameterIdentity_type, NULL); + } + + /* Proxy-State */ + { + /* + The Proxy-State AVP (AVP Code 33) is of type OctetString, and + contains state local information, and MUST be treated as opaque data. + */ + struct dict_avp_data adata = { + 33, /* Code */ + #if AC_PROXY_STATE != 33 + #error "AC_PROXY_STATE definition mismatch" + #endif + 0, /* Vendor */ + "Proxy-State", /* Name */ + AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, /* Fixed flags */ + AVP_FLAG_MANDATORY, /* Fixed flag values */ + AVP_TYPE_OCTETSTRING /* base type of data */ + }; + CHECK_dict_new( DICT_AVP, &adata , NULL, NULL); + } + + /* Proxy-Info */ + { + /* + The Proxy-Info AVP (AVP Code 284) is of type Grouped. The Grouped + Data field has the following ABNF grammar: + + Proxy-Info ::= < AVP Header: 284 > + { Proxy-Host } + { Proxy-State } + * [ AVP ] + */ + struct dict_object * avp; + struct dict_avp_data data = { + 284, /* Code */ + #if AC_PROXY_INFO != 284 + #error "AC_PROXY_INFO definition mismatch" + #endif + 0, /* Vendor */ + "Proxy-Info", /* Name */ + AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, /* Fixed flags */ + AVP_FLAG_MANDATORY, /* Fixed flag values */ + AVP_TYPE_GROUPED /* base type of data */ + }; + struct local_rules_definition rules[] = + { { "Proxy-Host", RULE_REQUIRED, -1, 1 } + ,{ "Proxy-State", RULE_REQUIRED, -1, 1 } + }; + + CHECK_dict_new( DICT_AVP, &data , NULL, &avp); + PARSE_loc_rules( rules, avp ); + } + + /* Auth-Application-Id */ + { + /* + The Auth-Application-Id AVP (AVP Code 258) is of type Unsigned32 and + is used in order to advertise support of the Authentication and + Authorization portion of an application (see Section 2.4). If + present in a message other than CER and CEA, the value of the Auth- + Application-Id AVP MUST match the Application Id present in the + Diameter message header. + */ + struct dict_avp_data data = { + 258, /* Code */ + #if AC_AUTH_APPLICATION_ID != 258 + #error "AC_AUTH_APPLICATION_ID definition mismatch" + #endif + 0, /* Vendor */ + "Auth-Application-Id", /* Name */ + AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, /* Fixed flags */ + AVP_FLAG_MANDATORY, /* Fixed flag values */ + AVP_TYPE_UNSIGNED32 /* base type of data */ + }; + CHECK_dict_new( DICT_AVP, &data , NULL, NULL); + } + + /* Acct-Application-Id */ + { + /* + The Acct-Application-Id AVP (AVP Code 259) is of type Unsigned32 and + is used in order to advertise support of the Accounting portion of an + application (see Section 2.4). If present in a message other than + CER and CEA, the value of the Acct-Application-Id AVP MUST match the + Application Id present in the Diameter message header. + */ + struct dict_avp_data data = { + 259, /* Code */ + #if AC_ACCT_APPLICATION_ID != 259 + #error "AC_ACCT_APPLICATION_ID definition mismatch" + #endif + 0, /* Vendor */ + "Acct-Application-Id", /* Name */ + AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, /* Fixed flags */ + AVP_FLAG_MANDATORY, /* Fixed flag values */ + AVP_TYPE_UNSIGNED32 /* base type of data */ + }; + CHECK_dict_new( DICT_AVP, &data , NULL, NULL); + } + + /* Inband-Security-Id */ + { + /* + The Inband-Security-Id AVP (AVP Code 299) is of type Unsigned32 and + is used in order to advertise support of the Security portion of the + application. + + Currently, the following values are supported, but there is ample + room to add new security Ids. + + + NO_INBAND_SECURITY 0 + + This peer does not support TLS. This is the default value, if the + AVP is omitted. + + TLS 1 + + This node supports TLS security, as defined by [RFC4346]. + */ + + /* Although the RFC does not specify an "Enumerated" type here, we go forward and create one. + * This is the reason for the "*" in the type name + */ + + struct dict_object * type; + struct dict_type_data tdata = { AVP_TYPE_UNSIGNED32, "Enumerated*(Inband-Security-Id)" , NULL, NULL}; + struct dict_enumval_data t_0 = { "NO_INBAND_SECURITY", { .u32 = 0 }}; + struct dict_enumval_data t_1 = { "TLS", { .u32 = 1 }}; + struct dict_avp_data data = { + 299, /* Code */ + #if AC_INBAND_SECURITY_ID != 299 + #error "AC_INBAND_SECURITY_ID definition mismatch" + #endif + 0, /* Vendor */ + "Inband-Security-Id", /* Name */ + AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, /* Fixed flags */ + AVP_FLAG_MANDATORY, /* Fixed flag values */ + AVP_TYPE_UNSIGNED32 /* base type of data */ + }; + /* Create the Enumerated type, and then the AVP */ + CHECK_dict_new( DICT_TYPE, &tdata , NULL, &type); + CHECK_dict_new( DICT_ENUMVAL, &t_0 , type, NULL); + CHECK_dict_new( DICT_ENUMVAL, &t_1 , type, NULL); + CHECK_dict_new( DICT_AVP, &data , type, NULL); + } + + /* Vendor-Specific-Application-Id */ + { + /* + The Vendor-Specific-Application-Id AVP (AVP Code 260) is of type + Grouped and is used to advertise support of a vendor-specific + Diameter Application. Exactly one instance of either Auth- + Application-Id or Acct-Application-Id AVP MUST be present. The + Application Id carried by either Auth-Application-Id or Acct- + Application-Id AVP MUST comply with vendor specific Application Id + assignment described in Sec 11.3. It MUST also match the Application + Id present in the diameter header except when used in a CER or CEA + messages. + + The Vendor-Id AVP is an informational AVP pertaining to the vendor + who may have authorship of the vendor-specific Diameter application. + It MUST NOT be used as a means of defining a completely separate + vendor-specific Application Id space. + + This AVP MUST also be present as the first AVP in all experimental + commands defined in the vendor-specific application. + + This AVP SHOULD be placed as close to the Diameter header as + possible. + + AVP Format + + <Vendor-Specific-Application-Id> ::= < AVP Header: 260 > + { Vendor-Id } + [ Auth-Application-Id ] + [ Acct-Application-Id ] + + A Vendor-Specific-Application-Id AVP MUST contain exactly one of + either Auth-Application-Id or Acct-Application-Id. If a Vendor- + Specific-Application-Id is received without any of these two AVPs, + then the recipient SHOULD issue an answer with a Result-Code set to + DIAMETER_MISSING_AVP. The answer SHOULD also include a Failed-AVP + which MUST contain an example of an Auth-Application-Id AVP and an + Acct-Application-Id AVP. + + If a Vendor-Specific-Application-Id is received that contains both + Auth-Application-Id and Acct-Application-Id, then the recipient + SHOULD issue an answer with Result-Code set to + DIAMETER_AVP_OCCURS_TOO_MANY_TIMES. The answer SHOULD also include a + Failed-AVP which MUST contain the received Auth-Application-Id AVP + and Acct-Application-Id AVP. + */ + struct dict_object * avp; + struct dict_avp_data data = { + 260, /* Code */ + #if AC_VENDOR_SPECIFIC_APPLICATION_ID != 260 + #error "AC_VENDOR_SPECIFIC_APPLICATION_ID definition mismatch" + #endif + 0, /* Vendor */ + "Vendor-Specific-Application-Id", /* Name */ + AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, /* Fixed flags */ + AVP_FLAG_MANDATORY, /* Fixed flag values */ + AVP_TYPE_GROUPED /* base type of data */ + }; + + struct local_rules_definition rules[] = + { { "Vendor-Id", RULE_REQUIRED, -1, 1 } + ,{ "Auth-Application-Id", RULE_OPTIONAL, -1, 1 } + ,{ "Acct-Application-Id", RULE_OPTIONAL, -1, 1 } + }; + + /* Create the grouped AVP */ + CHECK_dict_new( DICT_AVP, &data , NULL, &avp); + PARSE_loc_rules( rules, avp ); + + } + + /* Redirect-Host */ + { + /* + One or more of instances of this AVP MUST be present if the answer + message's 'E' bit is set and the Result-Code AVP is set to + DIAMETER_REDIRECT_INDICATION. + + Upon receiving the above, the receiving Diameter node SHOULD forward + the request directly to one of the hosts identified in these AVPs. + The server contained in the selected Redirect-Host AVP SHOULD be used + for all messages pertaining to this session. + */ + struct dict_avp_data data = { + 292, /* Code */ + #if AC_REDIRECT_HOST != 292 + #error "AC_REDIRECT_HOST definition mismatch" + #endif + 0, /* Vendor */ + "Redirect-Host", /* Name */ + AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, /* Fixed flags */ + AVP_FLAG_MANDATORY, /* Fixed flag values */ + AVP_TYPE_OCTETSTRING /* base type of data */ + }; + CHECK_dict_new( DICT_AVP, &data , DiameterURI_type, NULL); + } + + /* Redirect-Host-Usage */ + { + /* + The Redirect-Host-Usage AVP (AVP Code 261) is of type Enumerated. + This AVP MAY be present in answer messages whose 'E' bit is set and + the Result-Code AVP is set to DIAMETER_REDIRECT_INDICATION. + + When present, this AVP dictates how the routing entry resulting from + the Redirect-Host is to be used. The following values are supported: + + + DONT_CACHE 0 + + The host specified in the Redirect-Host AVP should not be cached. + This is the default value. + + + ALL_SESSION 1 + + All messages within the same session, as defined by the same value + of the Session-ID AVP MAY be sent to the host specified in the + Redirect-Host AVP. + + + ALL_REALM 2 + + All messages destined for the realm requested MAY be sent to the + host specified in the Redirect-Host AVP. + + + REALM_AND_APPLICATION 3 + + All messages for the application requested to the realm specified + MAY be sent to the host specified in the Redirect-Host AVP. + + ALL_APPLICATION 4 + + All messages for the application requested MAY be sent to the host + specified in the Redirect-Host AVP. + + + ALL_HOST 5 + + All messages that would be sent to the host that generated the + Redirect-Host MAY be sent to the host specified in the Redirect- + Host AVP. + + + ALL_USER 6 + + All messages for the user requested MAY be sent to the host + specified in the Redirect-Host AVP. + + + When multiple cached routes are created by redirect indications and + they differ only in redirect usage and peers to forward requests to + (see Section 6.1.8), a precedence rule MUST be applied to the + redirect usage values of the cached routes during normal routing to + resolve contentions that may occur. The precedence rule is the order + that dictate which redirect usage should be considered before any + other as they appear. The order is as follows: + + + 1. ALL_SESSION + + 2. ALL_USER + + 3. REALM_AND_APPLICATION + + 4. ALL_REALM + + 5. ALL_APPLICATION + + 6. ALL_HOST + */ + struct dict_object * type; + struct dict_type_data tdata = { AVP_TYPE_INTEGER32, "Enumerated(Redirect-Host-Usage)" , NULL, NULL}; + struct dict_enumval_data t_0 = { "DONT_CACHE", { .i32 = 0 }}; + struct dict_enumval_data t_1 = { "ALL_SESSION", { .i32 = 1 }}; + struct dict_enumval_data t_2 = { "ALL_REALM", { .i32 = 2 }}; + struct dict_enumval_data t_3 = { "REALM_AND_APPLICATION", { .i32 = 3 }}; + struct dict_enumval_data t_4 = { "ALL_APPLICATION", { .i32 = 4 }}; + struct dict_enumval_data t_5 = { "ALL_HOST", { .i32 = 5 }}; + struct dict_enumval_data t_6 = { "ALL_USER", { .i32 = 6 }}; + struct dict_avp_data data = { + 261, /* Code */ + #if AC_REDIRECT_HOST_USAGE != 261 + #error "AC_REDIRECT_HOST_USAGE definition mismatch" + #endif + 0, /* Vendor */ + "Redirect-Host-Usage", /* Name */ + AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, /* Fixed flags */ + AVP_FLAG_MANDATORY, /* Fixed flag values */ + AVP_TYPE_INTEGER32 /* base type of data */ + }; + /* Create the Enumerated type, and then the AVP */ + CHECK_dict_new( DICT_TYPE, &tdata , NULL, &type); + CHECK_dict_new( DICT_ENUMVAL, &t_0 , type, NULL); + CHECK_dict_new( DICT_ENUMVAL, &t_1 , type, NULL); + CHECK_dict_new( DICT_ENUMVAL, &t_2 , type, NULL); + CHECK_dict_new( DICT_ENUMVAL, &t_3 , type, NULL); + CHECK_dict_new( DICT_ENUMVAL, &t_4 , type, NULL); + CHECK_dict_new( DICT_ENUMVAL, &t_5 , type, NULL); + CHECK_dict_new( DICT_ENUMVAL, &t_6 , type, NULL); + CHECK_dict_new( DICT_AVP, &data , type, NULL); + } + + /* Redirect-Max-Cache-Time */ + { + /* + The Redirect-Max-Cache-Time AVP (AVP Code 262) is of type Unsigned32. + This AVP MUST be present in answer messages whose 'E' bit is set, the + Result-Code AVP is set to DIAMETER_REDIRECT_INDICATION and the + Redirect-Host-Usage AVP set to a non-zero value. + + This AVP contains the maximum number of seconds the peer and route + table entries, created as a result of the Redirect-Host, will be + cached. Note that once a host created due to a redirect indication + is no longer reachable, any associated peer and routing table entries + MUST be deleted. + */ + struct dict_avp_data data = { + 262, /* Code */ + #if AC_REDIRECT_MAX_CACHE_TIME != 262 + #error "AC_REDIRECT_MAX_CACHE_TIME definition mismatch" + #endif + 0, /* Vendor */ + "Redirect-Max-Cache-Time", /* Name */ + AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, /* Fixed flags */ + AVP_FLAG_MANDATORY, /* Fixed flag values */ + AVP_TYPE_UNSIGNED32 /* base type of data */ + }; + CHECK_dict_new( DICT_AVP, &data , NULL, NULL); + } + + /* Result-Code */ + { + /* + The Result-Code AVP (AVP Code 268) is of type Unsigned32 and + indicates whether a particular request was completed successfully or + whether an error occurred. All Diameter answer messages defined in + IETF applications MUST include one Result-Code AVP. A non-successful + Result-Code AVP (one containing a non 2xxx value other than + DIAMETER_REDIRECT_INDICATION) MUST include the Error-Reporting-Host + AVP if the host setting the Result-Code AVP is different from the + identity encoded in the Origin-Host AVP. + + The Result-Code data field contains an IANA-managed 32-bit address + space representing errors (see Section 11.4). Diameter provides the + following classes of errors, all identified by the thousands digit in + the decimal notation: + + o 1xxx (Informational) + + o 2xxx (Success) + + o 3xxx (Protocol Errors) + + o 4xxx (Transient Failures) + + o 5xxx (Permanent Failure) + + A non-recognized class (one whose first digit is not defined in this + section) MUST be handled as a permanent failure. + */ + + /* Although the RFC does not specify an "Enumerated" type here, we go forward and create one. + * This is the reason for the "*" in the type name + */ + struct dict_object * type; + struct dict_type_data tdata = { AVP_TYPE_UNSIGNED32, "Enumerated*(Result-Code)" , NULL, NULL}; + struct dict_avp_data data = { + 268, /* Code */ + #if AC_RESULT_CODE != 268 + #error "AC_RESULT_CODE definition mismatch" + #endif + 0, /* Vendor */ + "Result-Code", /* Name */ + AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, /* Fixed flags */ + AVP_FLAG_MANDATORY, /* Fixed flag values */ + AVP_TYPE_UNSIGNED32 /* base type of data */ + }; + /* Create the Enumerated type, and then the AVP */ + CHECK_dict_new( DICT_TYPE, &tdata , NULL, &type); + CHECK_dict_new( DICT_AVP, &data , type, NULL); + + /* Informational */ + { + /* 1001 */ + { + /* + This informational error is returned by a Diameter server to + inform the access device that the authentication mechanism being + used requires multiple round trips, and a subsequent request needs + to be issued in order for access to be granted. + */ + struct dict_enumval_data error_code = { "DIAMETER_MULTI_ROUND_AUTH", { .u32 = 1001 }}; + CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL); + } + } + /* Success */ + { + /* 2001 */ + { + /* + The Request was successfully completed. + */ + struct dict_enumval_data error_code = { "DIAMETER_SUCCESS", { .u32 = 2001 }}; + #if ER_DIAMETER_SUCCESS != 2001 + #error "ER_DIAMETER_SUCCESS definition mismatch" + #endif + CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL); + } + /* 2002 */ + { + /* + When returned, the request was successfully completed, but + additional processing is required by the application in order to + provide service to the user. + */ + struct dict_enumval_data error_code = { "DIAMETER_LIMITED_SUCCESS", { .u32 = 2002 }}; + CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL); + } + } + /* Protocol Errors */ + { + /* 3002 */ + { + /* + This error is given when Diameter can not deliver the message to + the destination, either because no host within the realm + supporting the required application was available to process the + request, or because Destination-Host AVP was given without the + associated Destination-Realm AVP. + */ + struct dict_enumval_data error_code = { "DIAMETER_UNABLE_TO_DELIVER", { .u32 = 3002 }}; + CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL); + } + /* 3003 */ + { + /* + The intended realm of the request is not recognized. + */ + struct dict_enumval_data error_code = { "DIAMETER_REALM_NOT_SERVED", { .u32 = 3003 }}; + #if ER_DIAMETER_REALM_NOT_SERVED != 3003 + #error "ER_DIAMETER_REALM_NOT_SERVED definition mismatch" + #endif + CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL); + } + /* 3004 */ + { + /* + When returned, a Diameter node SHOULD attempt to send the message + to an alternate peer. This error MUST only be used when a + specific server is requested, and it cannot provide the requested + service. + */ + struct dict_enumval_data error_code = { "DIAMETER_TOO_BUSY", { .u32 = 3004 }}; + #if ER_DIAMETER_TOO_BUSY != 3004 + #error "ER_DIAMETER_TOO_BUSY definition mismatch" + #endif + CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL); + } + /* 3005 */ + { + /* + An agent detected a loop while trying to get the message to the + intended recipient. The message MAY be sent to an alternate peer, + if one is available, but the peer reporting the error has + identified a configuration problem. + */ + struct dict_enumval_data error_code = { "DIAMETER_LOOP_DETECTED", { .u32 = 3005 }}; + CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL); + } + /* 3006 */ + { + /* + A redirect agent has determined that the request could not be + satisfied locally and the initiator of the request should direct + the request directly to the server, whose contact information has + been added to the response. When set, the Redirect-Host AVP MUST + be present. + */ + struct dict_enumval_data error_code = { "DIAMETER_REDIRECT_INDICATION", { .u32 = 3006 }}; + #if ER_DIAMETER_REDIRECT_INDICATION != 3006 + #error "ER_DIAMETER_REDIRECT_INDICATION definition mismatch" + #endif + CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL); + } + /* 3007 */ + { + /* + A request was sent for an application that is not supported. + */ + struct dict_enumval_data error_code = { "DIAMETER_APPLICATION_UNSUPPORTED", { .u32 = 3007 }}; + CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL); + } + /* 3011 */ + { + /* + This error is returned when a reserved bit in the Diameter header + is set to one (1) or the bits in the Diameter header defined in + Sec 3 are set incorrectly. + */ + struct dict_enumval_data error_code = { "DIAMETER_INVALID_BIT_IN_HEADER", { .u32 = 3011 }}; + CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL); + } + /* 3012 */ + { + /* + This error is returned when a request is received with an invalid + message length. + */ + struct dict_enumval_data error_code = { "DIAMETER_INVALID_MESSAGE_LENGTH", { .u32 = 3012 }}; + CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL); + } + } + /* Transient Failures */ + { + /* 4001 */ + { + /* + The authentication process for the user failed, most likely due to + an invalid password used by the user. Further attempts MUST only + be tried after prompting the user for a new password. + */ + struct dict_enumval_data error_code = { "DIAMETER_AUTHENTICATION_REJECTED", { .u32 = 4001 }}; + CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL); + } + /* 4002 */ + { + /* + A Diameter node received the accounting request but was unable to + commit it to stable storage due to a temporary lack of space. + */ + struct dict_enumval_data error_code = { "DIAMETER_OUT_OF_SPACE", { .u32 = 4002 }}; + CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL); + } + /* 4003 */ + { + /* + The peer has determined that it has lost the election process and + has therefore disconnected the transport connection. + */ + struct dict_enumval_data error_code = { "ELECTION_LOST", { .u32 = 4003 }}; + CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL); + } + } + /* Permanent Failures */ + { + /* 5001 */ + { + /* + The peer received a message that contained an AVP that is not + recognized or supported and was marked with the Mandatory bit. A + Diameter message with this error MUST contain one or more Failed- + AVP AVP containing the AVPs that caused the failure. + */ + struct dict_enumval_data error_code = { "DIAMETER_AVP_UNSUPPORTED", { .u32 = 5001 }}; + CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL); + } + /* 5002 */ + { + /* + The request contained an unknown Session-Id. + */ + struct dict_enumval_data error_code = { "DIAMETER_UNKNOWN_SESSION_ID", { .u32 = 5002 }}; + CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL); + } + /* 5003 */ + { + /* + A request was received for which the user could not be authorized. + This error could occur if the service requested is not permitted + to the user. + */ + struct dict_enumval_data error_code = { "DIAMETER_AUTHORIZATION_REJECTED",{ .u32 = 5003 }}; + CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL); + } + /* 5004 */ + { + /* + The request contained an AVP with an invalid value in its data + portion. A Diameter message indicating this error MUST include + the offending AVPs within a Failed-AVP AVP. + */ + struct dict_enumval_data error_code = { "DIAMETER_INVALID_AVP_VALUE", { .u32 = 5004 }}; + CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL); + } + /* 5005 */ + { + /* + The request did not contain an AVP that is required by the Command + Code definition. If this value is sent in the Result-Code AVP, a + Failed-AVP AVP SHOULD be included in the message. The Failed-AVP + AVP MUST contain an example of the missing AVP complete with the + Vendor-Id if applicable. The value field of the missing AVP + should be of correct minimum length and contain zeroes. + */ + struct dict_enumval_data error_code = { "DIAMETER_MISSING_AVP", { .u32 = 5005 }}; + CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL); + } + /* 5006 */ + { + /* + A request was received that cannot be authorized because the user + has already expended allowed resources. An example of this error + condition is a user that is restricted to one dial-up PPP port, + attempts to establish a second PPP connection. + */ + struct dict_enumval_data error_code = { "DIAMETER_RESOURCES_EXCEEDED", { .u32 = 5006 }}; + CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL); + } + /* 5007 */ + { + /* + The Home Diameter server has detected AVPs in the request that + contradicted each other, and is not willing to provide service to + the user. The Failed-AVP AVPs MUST be present which contains the + AVPs that contradicted each other. + */ + struct dict_enumval_data error_code = { "DIAMETER_CONTRADICTING_AVPS", { .u32 = 5007 }}; + CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL); + } + /* 5008 */ + { + /* + A message was received with an AVP that MUST NOT be present. The + Failed-AVP AVP MUST be included and contain a copy of the + offending AVP. + */ + struct dict_enumval_data error_code = { "DIAMETER_AVP_NOT_ALLOWED", { .u32 = 5008 }}; + CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL); + } + /* 5009 */ + { + /* + A message was received that included an AVP that appeared more + often than permitted in the message definition. The Failed-AVP + AVP MUST be included and contain a copy of the first instance of + the offending AVP that exceeded the maximum number of occurrences + */ + struct dict_enumval_data error_code = { "DIAMETER_AVP_OCCURS_TOO_MANY_TIMES",{ .u32 = 5009 }}; + CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL); + } + /* 5010 */ + { + /* + This error is returned by a Diameter node that is not acting as a + relay when it receives a CER which advertises a set of + applications that it does not support. + */ + struct dict_enumval_data error_code = { "DIAMETER_NO_COMMON_APPLICATION",{ .u32 = 5010 }}; + CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL); + } + /* 5011 */ + { + /* + This error is returned when a request was received, whose version + number is unsupported. + */ + struct dict_enumval_data error_code = { "DIAMETER_UNSUPPORTED_VERSION", { .u32 = 5011 }}; + CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL); + } + /* 5012 */ + { + /* + This error is returned when a request is rejected for unspecified + reasons. + */ + struct dict_enumval_data error_code = { "DIAMETER_UNABLE_TO_COMPLY", { .u32 = 5012 }}; + CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL); + } + /* 5014 */ + { + /* + The request contained an AVP with an invalid length. A Diameter + message indicating this error MUST include the offending AVPs + within a Failed-AVP AVP. In cases where the erroneous avp length + value exceeds the message length or is less than the minimum AVP + header length, it is sufficient to include the offending AVP + header and a zero filled payload of the minimum required length + for the payloads data type. If the AVP is a grouped AVP, the + grouped AVP header with an empty payload would be sufficient to + indicate the offending AVP. In the case where the offending AVP + header cannot be fully decoded when avp length is less than the + minimum AVP header length, it is sufficient to include an + offending AVP header that is formulated by padding the incomplete + AVP header with zero up to the minimum AVP header length. + */ + struct dict_enumval_data error_code = { "DIAMETER_INVALID_AVP_LENGTH", { .u32 = 5014 }}; + CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL); + } + /* 5017 */ + { + /* + This error is returned when a CER message is received, and there + are no common security mechanisms supported between the peers. A + Capabilities-Exchange-Answer (CEA) MUST be returned with the + Result-Code AVP set to DIAMETER_NO_COMMON_SECURITY. + */ + struct dict_enumval_data error_code = { "DIAMETER_NO_COMMON_SECURITY", { .u32 = 5017 }}; + CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL); + } + /* 5018 */ + { + /* + A CER was received from an unknown peer. + */ + struct dict_enumval_data error_code = { "DIAMETER_UNKNOWN_PEER", { .u32 = 5018 }}; + CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL); + } + /* 5019 */ + { + /* + The Request contained a Command-Code that the receiver did not + recognize or support. This MUST be used when a Diameter node + receives an experimental command that it does not understand. + */ + struct dict_enumval_data error_code = { "DIAMETER_COMMAND_UNSUPPORTED", { .u32 = 5019 }}; + CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL); + } + /* 5020 */ + { + /* + A request was received whose bits in the Diameter header were + either set to an invalid combination, or to a value that is + inconsistent with the command code's definition. + */ + struct dict_enumval_data error_code = { "DIAMETER_INVALID_HDR_BITS", { .u32 = 5020 }}; + CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL); + } + /* 5021 */ + { + /* + A request was received that included an AVP whose flag bits are + set to an unrecognized value, or that is inconsistent with the + AVP's definition. + */ + struct dict_enumval_data error_code = { "DIAMETER_INVALID_AVP_BITS", { .u32 = 5021 }}; + CHECK_dict_new( DICT_ENUMVAL, &error_code , type, NULL); + } + } + } + + /* Error-Message */ + { + /* + The Error-Message AVP (AVP Code 281) is of type UTF8String. It MAY + accompany a Result-Code AVP as a human readable error message. The + Error-Message AVP is not intended to be useful in real-time, and + SHOULD NOT be expected to be parsed by network entities. + */ + struct dict_avp_data data = { + 281, /* Code */ + #if AC_ERROR_MESSAGE != 281 + #error "AC_ERROR_MESSAGE definition mismatch" + #endif + 0, /* Vendor */ + "Error-Message", /* Name */ + AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, /* Fixed flags */ + 0, /* Fixed flag values */ + AVP_TYPE_OCTETSTRING /* base type of data */ + }; + CHECK_dict_new( DICT_AVP, &data , UTF8String_type, NULL); + } + + /* Error-Reporting-Host */ + { + /* + The Error-Reporting-Host AVP (AVP Code 294) is of type + DiameterIdentity. This AVP contains the identity of the Diameter + host that sent the Result-Code AVP to a value other than 2001 + (Success), only if the host setting the Result-Code is different from + the one encoded in the Origin-Host AVP. This AVP is intended to be + used for troubleshooting purposes, and MUST be set when the Result- + Code AVP indicates a failure. + */ + struct dict_avp_data data = { + 294, /* Code */ + #if AC_ERROR_REPORTING_HOST != 294 + #error "AC_ERROR_REPORTING_HOST definition mismatch" + #endif + 0, /* Vendor */ + "Error-Reporting-Host", /* Name */ + AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, /* Fixed flags */ + 0, /* Fixed flag values */ + AVP_TYPE_OCTETSTRING /* base type of data */ + }; + CHECK_dict_new( DICT_AVP, &data , DiameterIdentity_type, NULL); + } + + /* Failed-AVP */ + { + /* + The Failed-AVP AVP (AVP Code 279) is of type Grouped and provides + debugging information in cases where a request is rejected or not + fully processed due to erroneous information in a specific AVP. The + value of the Result-Code AVP will provide information on the reason + for the Failed-AVP AVP. A Diameter message SHOULD contain only one + Failed-AVP that corresponds to the error indicated by the Result-Code + AVP. For practical purposes, this Failed-AVP would typically refer + to the first AVP processing error that a Diameter node encounters. + + The possible reasons for this AVP are the presence of an improperly + constructed AVP, an unsupported or unrecognized AVP, an invalid AVP + value, the omission of a required AVP, the presence of an explicitly + excluded AVP (see tables in Section 10), or the presence of two or + more occurrences of an AVP which is restricted to 0, 1, or 0-1 + occurrences. + + A Diameter message SHOULD contain one Failed-AVP AVP, containing the + entire AVP that could not be processed successfully. If the failure + reason is omission of a required AVP, an AVP with the missing AVP + code, the missing vendor id, and a zero filled payload of the minimum + required length for the omitted AVP will be added. If the failure + reason is an invalid AVP length where the reported length is less + than the minimum AVP header length or greater than the reported + message length, a copy of the offending AVP header and a zero filled + payload of the minimum required length SHOULD be added. + + In the case where the offending AVP is embedded within a grouped AVP, + the Failed-AVP MAY contain the grouped AVP which in turn contains the + single offending AVP. The same method MAY be employed if the grouped + AVP itself is embedded in yet another grouped AVP and so on. In this + case, the Failed-AVP MAY contain the grouped AVP heirarchy up to the + single offending AVP. This enables the recipient to detect the + location of the offending AVP when embedded in a group. + + AVP Format + + <Failed-AVP> ::= < AVP Header: 279 > + 1* {AVP} + */ + struct dict_avp_data data = { + 279, /* Code */ + #if AC_FAILED_AVP != 279 + #error "AC_FAILED_AVP definition mismatch" + #endif + 0, /* Vendor */ + "Failed-AVP", /* Name */ + AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, /* Fixed flags */ + AVP_FLAG_MANDATORY, /* Fixed flag values */ + AVP_TYPE_GROUPED /* base type of data */ + }; + CHECK_dict_new( DICT_AVP, &data , NULL, NULL); + } + + /* Experimental-Result */ + { + /* + The Experimental-Result AVP (AVP Code 297) is of type Grouped, and + indicates whether a particular vendor-specific request was completed + successfully or whether an error occurred. Its Data field has the + following ABNF grammar: + + AVP Format + + Experimental-Result ::= < AVP Header: 297 > + { Vendor-Id } + { Experimental-Result-Code } + + The Vendor-Id AVP (see Section 5.3.3) in this grouped AVP identifies + the vendor responsible for the assignment of the result code which + follows. All Diameter answer messages defined in vendor-specific + applications MUST include either one Result-Code AVP or one + Experimental-Result AVP. + */ + struct dict_avp_data data = { + 297, /* Code */ + 0, /* Vendor */ + "Experimental-Result", /* Name */ + AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, /* Fixed flags */ + AVP_FLAG_MANDATORY, /* Fixed flag values */ + AVP_TYPE_GROUPED /* base type of data */ + }; + CHECK_dict_new( DICT_AVP, &data , NULL, NULL); + } + + /* Experimental-Result-Code */ + { + /* + The Experimental-Result-Code AVP (AVP Code 298) is of type Unsigned32 + and contains a vendor-assigned value representing the result of + processing the request. + + It is recommended that vendor-specific result codes follow the same + conventions given for the Result-Code AVP regarding the different + types of result codes and the handling of errors (for non 2xxx + values). + */ + /* Although the RFC does not specify an "Enumerated" type here, we go forward and create one. + * This is the reason for the "*" in the type name. Vendors will have to define their values. + */ + struct dict_object * type; + struct dict_type_data tdata = { AVP_TYPE_UNSIGNED32, "Enumerated*(Experimental-Result-Code)" , NULL, NULL}; + struct dict_avp_data data = { + 298, /* Code */ + 0, /* Vendor */ + "Experimental-Result-Code", /* Name */ + AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, /* Fixed flags */ + AVP_FLAG_MANDATORY, /* Fixed flag values */ + AVP_TYPE_UNSIGNED32 /* base type of data */ + }; + + CHECK_dict_new( DICT_TYPE, &tdata , NULL, &type); + CHECK_dict_new( DICT_AVP, &data , type, NULL); + } + + /* Auth-Request-Type */ + { + /* + The Auth-Request-Type AVP (AVP Code 274) is of type Enumerated and is + included in application-specific auth requests to inform the peers + whether a user is to be authenticated only, authorized only or both. + Note any value other than both MAY cause RADIUS interoperability + issues. The following values are defined: + + + AUTHENTICATE_ONLY 1 + + The request being sent is for authentication only, and MUST + contain the relevant application specific authentication AVPs that + are needed by the Diameter server to authenticate the user. + + + AUTHORIZE_ONLY 2 + + The request being sent is for authorization only, and MUST contain + the application specific authorization AVPs that are necessary to + identify the service being requested/offered. + + + AUTHORIZE_AUTHENTICATE 3 + + The request contains a request for both authentication and + authorization. The request MUST include both the relevant + application specific authentication information, and authorization + information necessary to identify the service being requested/ + offered. + */ + struct dict_object * type; + struct dict_type_data tdata = { AVP_TYPE_INTEGER32, "Enumerated(Auth-Request-Type)" , NULL, NULL}; + struct dict_enumval_data t_1 = { "AUTHENTICATE_ONLY", { .i32 = 1 }}; + struct dict_enumval_data t_2 = { "AUTHORIZE_ONLY", { .i32 = 2 }}; + struct dict_enumval_data t_3 = { "AUTHORIZE_AUTHENTICATE", { .i32 = 3 }}; + struct dict_avp_data data = { + 274, /* Code */ + 0, /* Vendor */ + "Auth-Request-Type", /* Name */ + AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, /* Fixed flags */ + AVP_FLAG_MANDATORY, /* Fixed flag values */ + AVP_TYPE_INTEGER32 /* base type of data */ + }; + /* Create the Enumerated type, and then the AVP */ + CHECK_dict_new( DICT_TYPE, &tdata , NULL, &type); + CHECK_dict_new( DICT_ENUMVAL, &t_1 , type, NULL); + CHECK_dict_new( DICT_ENUMVAL, &t_2 , type, NULL); + CHECK_dict_new( DICT_ENUMVAL, &t_3 , type, NULL); + CHECK_dict_new( DICT_AVP, &data , type, NULL); + } + + /* Session-Id */ + { + /* + The Session-Id AVP (AVP Code 263) is of type UTF8String and is used + to identify a specific session (see Section 8). All messages + pertaining to a specific session MUST include only one Session-Id AVP + and the same value MUST be used throughout the life of a session. + When present, the Session-Id SHOULD appear immediately following the + Diameter Header (see Section 3). + + The Session-Id MUST be globally and eternally unique, as it is meant + to uniquely identify a user session without reference to any other + information, and may be needed to correlate historical authentication + information with accounting information. The Session-Id includes a + mandatory portion and an implementation-defined portion; a + recommended format for the implementation-defined portion is outlined + below. + + (skipped, see RFC for detail) + */ + struct dict_avp_data data = { + 263, /* Code */ + #if AC_SESSION_ID != 263 + #error "AC_SESSION_ID definition mismatch" + #endif + 0, /* Vendor */ + "Session-Id", /* Name */ + AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, /* Fixed flags */ + AVP_FLAG_MANDATORY, /* Fixed flag values */ + AVP_TYPE_OCTETSTRING /* base type of data */ + }; + CHECK_dict_new( DICT_AVP, &data , UTF8String_type, NULL); + } + + /* Authorization-Lifetime */ + { + /* + The Authorization-Lifetime AVP (AVP Code 291) is of type Unsigned32 + and contains the maximum number of seconds of service to be provided + to the user before the user is to be re-authenticated and/or re- + authorized. Great care should be taken when the Authorization- + Lifetime value is determined, since a low, non-zero, value could + create significant Diameter traffic, which could congest both the + network and the agents. + + A value of zero (0) means that immediate re-auth is necessary by the + access device. This is typically used in cases where multiple + authentication methods are used, and a successful auth response with + this AVP set to zero is used to signal that the next authentication + method is to be immediately initiated. The absence of this AVP, or a + value of all ones (meaning all bits in the 32 bit field are set to + one) means no re-auth is expected. + + If both this AVP and the Session-Timeout AVP are present in a + message, the value of the latter MUST NOT be smaller than the + Authorization-Lifetime AVP. + + An Authorization-Lifetime AVP MAY be present in re-authorization + messages, and contains the number of seconds the user is authorized + to receive service from the time the re-auth answer message is + received by the access device. + + This AVP MAY be provided by the client as a hint of the maximum + lifetime that it is willing to accept. However, the server MAY + return a value that is equal to, or smaller, than the one provided by + the client. + */ + struct dict_avp_data data = { + 291, /* Code */ + 0, /* Vendor */ + "Authorization-Lifetime", /* Name */ + AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, /* Fixed flags */ + AVP_FLAG_MANDATORY, /* Fixed flag values */ + AVP_TYPE_UNSIGNED32 /* base type of data */ + }; + CHECK_dict_new( DICT_AVP, &data , NULL, NULL); + } + + /* Auth-Grace-Period */ + { + /* + The Auth-Grace-Period AVP (AVP Code 276) is of type Unsigned32 and + contains the number of seconds the Diameter server will wait + following the expiration of the Authorization-Lifetime AVP before + cleaning up resources for the session. + */ + struct dict_avp_data data = { + 276, /* Code */ + 0, /* Vendor */ + "Auth-Grace-Period", /* Name */ + AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, /* Fixed flags */ + AVP_FLAG_MANDATORY, /* Fixed flag values */ + AVP_TYPE_UNSIGNED32 /* base type of data */ + }; + CHECK_dict_new( DICT_AVP, &data , NULL, NULL); + } + + /* Auth-Session-State */ + { + /* + The Auth-Session-State AVP (AVP Code 277) is of type Enumerated and + specifies whether state is maintained for a particular session. The + client MAY include this AVP in requests as a hint to the server, but + the value in the server's answer message is binding. The following + values are supported: + + + STATE_MAINTAINED 0 + + This value is used to specify that session state is being + maintained, and the access device MUST issue a session termination + message when service to the user is terminated. This is the + default value. + + + NO_STATE_MAINTAINED 1 + + This value is used to specify that no session termination messages + will be sent by the access device upon expiration of the + Authorization-Lifetime. + */ + struct dict_object * type; + struct dict_type_data tdata = { AVP_TYPE_INTEGER32, "Enumerated(Auth-Session-State)" , NULL, NULL}; + struct dict_enumval_data t_0 = { "STATE_MAINTAINED", { .i32 = 0 }}; + struct dict_enumval_data t_1 = { "NO_STATE_MAINTAINED", { .i32 = 1 }}; + struct dict_avp_data data = { + 277, /* Code */ + 0, /* Vendor */ + "Auth-Session-State", /* Name */ + AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, /* Fixed flags */ + AVP_FLAG_MANDATORY, /* Fixed flag values */ + AVP_TYPE_INTEGER32 /* base type of data */ + }; + /* Create the Enumerated type, and then the AVP */ + CHECK_dict_new( DICT_TYPE, &tdata , NULL, &type); + CHECK_dict_new( DICT_ENUMVAL, &t_0 , type, NULL); + CHECK_dict_new( DICT_ENUMVAL, &t_1 , type, NULL); + CHECK_dict_new( DICT_AVP, &data , type, NULL); + } + + /* Re-Auth-Request-Type */ + { + /* + The Re-Auth-Request-Type AVP (AVP Code 285) is of type Enumerated and + is included in application-specific auth answers to inform the client + of the action expected upon expiration of the Authorization-Lifetime. + If the answer message contains an Authorization-Lifetime AVP with a + positive value, the Re-Auth-Request-Type AVP MUST be present in an + answer message. The following values are defined: + + + AUTHORIZE_ONLY 0 + + An authorization only re-auth is expected upon expiration of the + Authorization-Lifetime. This is the default value if the AVP is + not present in answer messages that include the Authorization- + Lifetime. + + + AUTHORIZE_AUTHENTICATE 1 + + An authentication and authorization re-auth is expected upon + expiration of the Authorization-Lifetime. + */ + struct dict_object * type; + struct dict_type_data tdata = { AVP_TYPE_INTEGER32, "Enumerated(Re-Auth-Request-Type)" , NULL, NULL}; + struct dict_enumval_data t_0 = { "AUTHORIZE_ONLY", { .i32 = 0 }}; + struct dict_enumval_data t_1 = { "AUTHORIZE_AUTHENTICATE", { .i32 = 1 }}; + struct dict_avp_data data = { + 285, /* Code */ + 0, /* Vendor */ + "Re-Auth-Request-Type", /* Name */ + AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, /* Fixed flags */ + AVP_FLAG_MANDATORY, /* Fixed flag values */ + AVP_TYPE_INTEGER32 /* base type of data */ + }; + /* Create the Enumerated type, and then the AVP */ + CHECK_dict_new( DICT_TYPE, &tdata , NULL, &type); + CHECK_dict_new( DICT_ENUMVAL, &t_0 , type, NULL); + CHECK_dict_new( DICT_ENUMVAL, &t_1 , type, NULL); + CHECK_dict_new( DICT_AVP, &data , type, NULL); + } + + /* Session-Timeout */ + { + /* + The Session-Timeout AVP (AVP Code 27) [RFC2865] is of type Unsigned32 + and contains the maximum number of seconds of service to be provided + to the user before termination of the session. When both the + Session-Timeout and the Authorization-Lifetime AVPs are present in an + answer message, the former MUST be equal to or greater than the value + of the latter. + + A session that terminates on an access device due to the expiration + of the Session-Timeout MUST cause an STR to be issued, unless both + the access device and the home server had previously agreed that no + session termination messages would be sent (see Section 8.11). + + A Session-Timeout AVP MAY be present in a re-authorization answer + message, and contains the remaining number of seconds from the + beginning of the re-auth. + + A value of zero, or the absence of this AVP, means that this session + has an unlimited number of seconds before termination. + + This AVP MAY be provided by the client as a hint of the maximum + timeout that it is willing to accept. However, the server MAY return + a value that is equal to, or smaller, than the one provided by the + client. + */ + struct dict_avp_data data = { + 27, /* Code */ + 0, /* Vendor */ + "Session-Timeout", /* Name */ + AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, /* Fixed flags */ + AVP_FLAG_MANDATORY, /* Fixed flag values */ + AVP_TYPE_UNSIGNED32 /* base type of data */ + }; + CHECK_dict_new( DICT_AVP, &data , NULL, NULL); + } + + /* User-Name */ + { + /* + The User-Name AVP (AVP Code 1) [RFC2865] is of type UTF8String, which + contains the User-Name, in a format consistent with the NAI + specification [RFC4282]. + */ + struct dict_avp_data data = { + 1, /* Code */ + 0, /* Vendor */ + "User-Name", /* Name */ + AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, /* Fixed flags */ + AVP_FLAG_MANDATORY, /* Fixed flag values */ + AVP_TYPE_OCTETSTRING /* base type of data */ + }; + CHECK_dict_new( DICT_AVP, &data , UTF8String_type, NULL); + } + + /* Termination-Cause */ + { + /* + The Termination-Cause AVP (AVP Code 295) is of type Enumerated, and + is used to indicate the reason why a session was terminated on the + access device. The following values are defined: + + + DIAMETER_LOGOUT 1 + + The user initiated a disconnect + + + DIAMETER_SERVICE_NOT_PROVIDED 2 + + This value is used when the user disconnected prior to the receipt + of the authorization answer message. + + + DIAMETER_BAD_ANSWER 3 + + This value indicates that the authorization answer received by the + access device was not processed successfully. + + + DIAMETER_ADMINISTRATIVE 4 + + The user was not granted access, or was disconnected, due to + administrative reasons, such as the receipt of a Abort-Session- + Request message. + + + DIAMETER_LINK_BROKEN 5 + + The communication to the user was abruptly disconnected. + + + DIAMETER_AUTH_EXPIRED 6 + + The user's access was terminated since its authorized session time + has expired. + + + DIAMETER_USER_MOVED 7 + + The user is receiving services from another access device. + + + DIAMETER_SESSION_TIMEOUT 8 + + The user's session has timed out, and service has been terminated. + */ + struct dict_object * type; + struct dict_type_data tdata = { AVP_TYPE_INTEGER32, "Enumerated(Termination-Cause)" , NULL, NULL}; + struct dict_enumval_data t_1 = { "DIAMETER_LOGOUT", { .i32 = 1 }}; + struct dict_enumval_data t_2 = { "DIAMETER_SERVICE_NOT_PROVIDED", { .i32 = 2 }}; + struct dict_enumval_data t_3 = { "DIAMETER_BAD_ANSWER", { .i32 = 3 }}; + struct dict_enumval_data t_4 = { "DIAMETER_ADMINISTRATIVE", { .i32 = 4 }}; + struct dict_enumval_data t_5 = { "DIAMETER_LINK_BROKEN", { .i32 = 5 }}; + struct dict_enumval_data t_6 = { "DIAMETER_AUTH_EXPIRED", { .i32 = 6 }}; + struct dict_enumval_data t_7 = { "DIAMETER_USER_MOVED", { .i32 = 7 }}; + struct dict_enumval_data t_8 = { "DIAMETER_SESSION_TIMEOUT", { .i32 = 8 }}; + struct dict_avp_data data = { + 295, /* Code */ + 0, /* Vendor */ + "Termination-Cause", /* Name */ + AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, /* Fixed flags */ + AVP_FLAG_MANDATORY, /* Fixed flag values */ + AVP_TYPE_INTEGER32 /* base type of data */ + }; + /* Create the Enumerated type, and then the AVP */ + CHECK_dict_new( DICT_TYPE, &tdata , NULL, &type); + CHECK_dict_new( DICT_ENUMVAL, &t_1 , type, NULL); + CHECK_dict_new( DICT_ENUMVAL, &t_2 , type, NULL); + CHECK_dict_new( DICT_ENUMVAL, &t_3 , type, NULL); + CHECK_dict_new( DICT_ENUMVAL, &t_4 , type, NULL); + CHECK_dict_new( DICT_ENUMVAL, &t_5 , type, NULL); + CHECK_dict_new( DICT_ENUMVAL, &t_6 , type, NULL); + CHECK_dict_new( DICT_ENUMVAL, &t_7 , type, NULL); + CHECK_dict_new( DICT_ENUMVAL, &t_8 , type, NULL); + CHECK_dict_new( DICT_AVP, &data , type, NULL); + } + + /* Origin-State-Id */ + { + /* + The Origin-State-Id AVP (AVP Code 278), of type Unsigned32, is a + monotonically increasing value that is advanced whenever a Diameter + entity restarts with loss of previous state, for example upon reboot. + Origin-State-Id MAY be included in any Diameter message, including + CER. + + A Diameter entity issuing this AVP MUST create a higher value for + this AVP each time its state is reset. A Diameter entity MAY set + Origin-State-Id to the time of startup, or it MAY use an incrementing + counter retained in non-volatile memory across restarts. + + The Origin-State-Id, if present, MUST reflect the state of the entity + indicated by Origin-Host. If a proxy modifies Origin-Host, it MUST + either remove Origin-State-Id or modify it appropriately as well. + Typically, Origin-State-Id is used by an access device that always + starts up with no active sessions; that is, any session active prior + to restart will have been lost. By including Origin-State-Id in a + message, it allows other Diameter entities to infer that sessions + associated with a lower Origin-State-Id are no longer active. If an + access device does not intend for such inferences to be made, it MUST + either not include Origin-State-Id in any message, or set its value + to 0. + */ + struct dict_avp_data data = { + 278, /* Code */ + 0, /* Vendor */ + "Origin-State-Id", /* Name */ + AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, /* Fixed flags */ + AVP_FLAG_MANDATORY, /* Fixed flag values */ + AVP_TYPE_UNSIGNED32 /* base type of data */ + }; + CHECK_dict_new( DICT_AVP, &data , NULL, NULL); + } + + /* Session-Binding */ + { + /* + The Session-Binding AVP (AVP Code 270) is of type Unsigned32, and MAY + be present in application-specific authorization answer messages. If + present, this AVP MAY inform the Diameter client that all future + application-specific re-auth messages for this session MUST be sent + to the same authorization server. This AVP MAY also specify that a + Session-Termination-Request message for this session MUST be sent to + the same authorizing server. + + This field is a bit mask, and the following bits have been defined: + + + RE_AUTH 1 + + When set, future re-auth messages for this session MUST NOT + include the Destination-Host AVP. When cleared, the default + value, the Destination-Host AVP MUST be present in all re-auth + messages for this session. + + + STR 2 + + When set, the STR message for this session MUST NOT include the + Destination-Host AVP. When cleared, the default value, the + Destination-Host AVP MUST be present in the STR message for this + session. + + + ACCOUNTING 4 + + When set, all accounting messages for this session MUST NOT + include the Destination-Host AVP. When cleared, the default + value, the Destination-Host AVP, if known, MUST be present in all + accounting messages for this session. + */ + + /* Although the RFC does not specify an "Enumerated" type here, we go forward and create one. + * This is the reason for the "*" in the type name + * The actual values of the AVP will not always be defined here, but at least it can be used in some cases. + * ... maybe the code will be changed later to support bitfields AVP ... + */ + + struct dict_object * type; + struct dict_type_data tdata = { AVP_TYPE_UNSIGNED32, "Enumerated*(Session-Binding)" , NULL, NULL}; + struct dict_enumval_data t_1 = { "RE_AUTH", { .u32 = 1 }}; + struct dict_enumval_data t_2 = { "STR", { .u32 = 2 }}; + struct dict_enumval_data t_4 = { "ACCOUNTING", { .u32 = 4 }}; + struct dict_avp_data data = { + 270, /* Code */ + 0, /* Vendor */ + "Session-Binding", /* Name */ + AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, /* Fixed flags */ + AVP_FLAG_MANDATORY, /* Fixed flag values */ + AVP_TYPE_UNSIGNED32 /* base type of data */ + }; + /* Create the Enumerated type, and then the AVP */ + CHECK_dict_new( DICT_TYPE, &tdata , NULL, &type); + CHECK_dict_new( DICT_ENUMVAL, &t_1 , type, NULL); + CHECK_dict_new( DICT_ENUMVAL, &t_2 , type, NULL); + CHECK_dict_new( DICT_ENUMVAL, &t_4 , type, NULL); + CHECK_dict_new( DICT_AVP, &data , type, NULL); + } + + /* Session-Server-Failover */ + { + /* + The Session-Server-Failover AVP (AVP Code 271) is of type Enumerated, + and MAY be present in application-specific authorization answer + messages that either do not include the Session-Binding AVP or + include the Session-Binding AVP with any of the bits set to a zero + value. If present, this AVP MAY inform the Diameter client that if a + re-auth or STR message fails due to a delivery problem, the Diameter + client SHOULD issue a subsequent message without the Destination-Host + AVP. When absent, the default value is REFUSE_SERVICE. + + The following values are supported: + + + REFUSE_SERVICE 0 + + If either the re-auth or the STR message delivery fails, terminate + service with the user, and do not attempt any subsequent attempts. + + + TRY_AGAIN 1 + + If either the re-auth or the STR message delivery fails, resend + the failed message without the Destination-Host AVP present. + + + ALLOW_SERVICE 2 + + If re-auth message delivery fails, assume that re-authorization + succeeded. If STR message delivery fails, terminate the session. + + + TRY_AGAIN_ALLOW_SERVICE 3 + + If either the re-auth or the STR message delivery fails, resend + the failed message without the Destination-Host AVP present. If + the second delivery fails for re-auth, assume re-authorization + succeeded. If the second delivery fails for STR, terminate the + session. + */ + struct dict_object * type; + struct dict_type_data tdata = { AVP_TYPE_INTEGER32, "Enumerated(Session-Server-Failover)" , NULL, NULL}; + struct dict_enumval_data t_0 = { "REFUSE_SERVICE", { .i32 = 0 }}; + struct dict_enumval_data t_1 = { "TRY_AGAIN", { .i32 = 1 }}; + struct dict_enumval_data t_2 = { "ALLOW_SERVICE", { .i32 = 2 }}; + struct dict_enumval_data t_3 = { "TRY_AGAIN_ALLOW_SERVICE", { .i32 = 3 }}; + struct dict_avp_data data = { + 271, /* Code */ + 0, /* Vendor */ + "Session-Server-Failover", /* Name */ + AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, /* Fixed flags */ + AVP_FLAG_MANDATORY, /* Fixed flag values */ + AVP_TYPE_INTEGER32 /* base type of data */ + }; + /* Create the Enumerated type, and then the AVP */ + CHECK_dict_new( DICT_TYPE, &tdata , NULL, &type); + CHECK_dict_new( DICT_ENUMVAL, &t_0 , type, NULL); + CHECK_dict_new( DICT_ENUMVAL, &t_1 , type, NULL); + CHECK_dict_new( DICT_ENUMVAL, &t_2 , type, NULL); + CHECK_dict_new( DICT_ENUMVAL, &t_3 , type, NULL); + CHECK_dict_new( DICT_AVP, &data , type, NULL); + } + + /* Multi-Round-Time-Out */ + { + /* + The Multi-Round-Time-Out AVP (AVP Code 272) is of type Unsigned32, + and SHOULD be present in application-specific authorization answer + messages whose Result-Code AVP is set to DIAMETER_MULTI_ROUND_AUTH. + This AVP contains the maximum number of seconds that the access + device MUST provide the user in responding to an authentication + request. + */ + struct dict_avp_data data = { + 272, /* Code */ + 0, /* Vendor */ + "Multi-Round-Time-Out", /* Name */ + AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, /* Fixed flags */ + AVP_FLAG_MANDATORY, /* Fixed flag values */ + AVP_TYPE_UNSIGNED32 /* base type of data */ + }; + CHECK_dict_new( DICT_AVP, &data , NULL, NULL); + } + + /* Class */ + { + /* + The Class AVP (AVP Code 25) is of type OctetString and is used to by + Diameter servers to return state information to the access device. + When one or more Class AVPs are present in application-specific + authorization answer messages, they MUST be present in subsequent re- + authorization, session termination and accounting messages. Class + AVPs found in a re-authorization answer message override the ones + found in any previous authorization answer message. Diameter server + implementations SHOULD NOT return Class AVPs that require more than + 4096 bytes of storage on the Diameter client. A Diameter client that + receives Class AVPs whose size exceeds local available storage MUST + terminate the session. + */ + struct dict_avp_data data = { + 25, /* Code */ + 0, /* Vendor */ + "Class", /* Name */ + AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, /* Fixed flags */ + AVP_FLAG_MANDATORY, /* Fixed flag values */ + AVP_TYPE_OCTETSTRING /* base type of data */ + }; + CHECK_dict_new( DICT_AVP, &data , NULL, NULL); + } + + /* Event-Timestamp */ + { + /* + The Event-Timestamp (AVP Code 55) is of type Time, and MAY be + included in an Accounting-Request and Accounting-Answer messages to + record the time that the reported event occurred, in seconds since + January 1, 1900 00:00 UTC. + */ + struct dict_avp_data data = { + 55, /* Code */ + 0, /* Vendor */ + "Event-Timestamp", /* Name */ + AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, /* Fixed flags */ + AVP_FLAG_MANDATORY, /* Fixed flag values */ + AVP_TYPE_OCTETSTRING /* base type of data */ + }; + CHECK_dict_new( DICT_AVP, &data , Time_type, NULL); + } + + + /* Accounting-Record-Type */ + { + /* + The Accounting-Record-Type AVP (AVP Code 480) is of type Enumerated + and contains the type of accounting record being sent. The following + values are currently defined for the Accounting-Record-Type AVP: + + + EVENT_RECORD 1 + + An Accounting Event Record is used to indicate that a one-time + event has occurred (meaning that the start and end of the event + are simultaneous). This record contains all information relevant + to the service, and is the only record of the service. + + + START_RECORD 2 + + An Accounting Start, Interim, and Stop Records are used to + indicate that a service of a measurable length has been given. An + Accounting Start Record is used to initiate an accounting session, + and contains accounting information that is relevant to the + initiation of the session. + + + INTERIM_RECORD 3 + + An Interim Accounting Record contains cumulative accounting + information for an existing accounting session. Interim + Accounting Records SHOULD be sent every time a re-authentication + or re-authorization occurs. Further, additional interim record + triggers MAY be defined by application-specific Diameter + applications. The selection of whether to use INTERIM_RECORD + records is done by the Acct-Interim-Interval AVP. + + + STOP_RECORD 4 + + An Accounting Stop Record is sent to terminate an accounting + session and contains cumulative accounting information relevant to + the existing session. + */ + struct dict_object * type; + struct dict_type_data tdata = { AVP_TYPE_INTEGER32, "Enumerated(Accounting-Record-Type)" , NULL, NULL}; + struct dict_enumval_data t_1 = { "EVENT_RECORD", { .i32 = 1 }}; + struct dict_enumval_data t_2 = { "START_RECORD", { .i32 = 2 }}; + struct dict_enumval_data t_3 = { "INTERIM_RECORD", { .i32 = 3 }}; + struct dict_enumval_data t_4 = { "STOP_RECORD", { .i32 = 4 }}; + struct dict_avp_data data = { + 480, /* Code */ + 0, /* Vendor */ + "Accounting-Record-Type", /* Name */ + AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, /* Fixed flags */ + AVP_FLAG_MANDATORY, /* Fixed flag values */ + AVP_TYPE_INTEGER32 /* base type of data */ + }; + /* Create the Enumerated type, and then the AVP */ + CHECK_dict_new( DICT_TYPE, &tdata , NULL, &type); + CHECK_dict_new( DICT_ENUMVAL, &t_1 , type, NULL); + CHECK_dict_new( DICT_ENUMVAL, &t_2 , type, NULL); + CHECK_dict_new( DICT_ENUMVAL, &t_3 , type, NULL); + CHECK_dict_new( DICT_ENUMVAL, &t_4 , type, NULL); + CHECK_dict_new( DICT_AVP, &data , type, NULL); + } + + /* Acct-Interim-Interval */ + { + /* + The Acct-Interim-Interval AVP (AVP Code 85) is of type Unsigned32 and + is sent from the Diameter home authorization server to the Diameter + client. The client uses information in this AVP to decide how and + when to produce accounting records. With different values in this + AVP, service sessions can result in one, two, or two+N accounting + records, based on the needs of the home-organization. The following + accounting record production behavior is directed by the inclusion of + this AVP: + + + 1. The omission of the Acct-Interim-Interval AVP or its inclusion + with Value field set to 0 means that EVENT_RECORD, START_RECORD, + and STOP_RECORD are produced, as appropriate for the service. + + + 2. The inclusion of the AVP with Value field set to a non-zero value + means that INTERIM_RECORD records MUST be produced between the + START_RECORD and STOP_RECORD records. The Value field of this + AVP is the nominal interval between these records in seconds. + + The Diameter node that originates the accounting information, + known as the client, MUST produce the first INTERIM_RECORD record + roughly at the time when this nominal interval has elapsed from + the START_RECORD, the next one again as the interval has elapsed + once more, and so on until the session ends and a STOP_RECORD + record is produced. + + The client MUST ensure that the interim record production times + are randomized so that large accounting message storms are not + created either among records or around a common service start + time. + */ + struct dict_avp_data data = { + 85, /* Code */ + 0, /* Vendor */ + "Acct-Interim-Interval", /* Name */ + AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, /* Fixed flags */ + AVP_FLAG_MANDATORY, /* Fixed flag values */ + AVP_TYPE_UNSIGNED32 /* base type of data */ + }; + CHECK_dict_new( DICT_AVP, &data , NULL, NULL); + } + + /* Accounting-Record-Number */ + { + /* + The Accounting-Record-Number AVP (AVP Code 485) is of type Unsigned32 + and identifies this record within one session. As Session-Id AVPs + are globally unique, the combination of Session-Id and Accounting- + Record-Number AVPs is also globally unique, and can be used in + matching accounting records with confirmations. An easy way to + produce unique numbers is to set the value to 0 for records of type + EVENT_RECORD and START_RECORD, and set the value to 1 for the first + INTERIM_RECORD, 2 for the second, and so on until the value for + STOP_RECORD is one more than for the last INTERIM_RECORD. + */ + struct dict_avp_data data = { + 485, /* Code */ + 0, /* Vendor */ + "Accounting-Record-Number", /* Name */ + AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, /* Fixed flags */ + AVP_FLAG_MANDATORY, /* Fixed flag values */ + AVP_TYPE_UNSIGNED32 /* base type of data */ + }; + CHECK_dict_new( DICT_AVP, &data , NULL, NULL); + } + + /* Acct-Session-Id */ + { + /* + The Acct-Session-Id AVP (AVP Code 44) is of type OctetString is only + used when RADIUS/Diameter translation occurs. This AVP contains the + contents of the RADIUS Acct-Session-Id attribute. + */ + struct dict_avp_data data = { + 44, /* Code */ + 0, /* Vendor */ + "Acct-Session-Id", /* Name */ + AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, /* Fixed flags */ + AVP_FLAG_MANDATORY, /* Fixed flag values */ + AVP_TYPE_OCTETSTRING /* base type of data */ + }; + CHECK_dict_new( DICT_AVP, &data , NULL, NULL); + } + + /* Acct-Multi-Session-Id */ + { + /* + The Acct-Multi-Session-Id AVP (AVP Code 50) is of type UTF8String, + following the format specified in Section 8.8. The Acct-Multi- + Session-Id AVP is used to link together multiple related accounting + sessions, where each session would have a unique Session-Id, but the + same Acct-Multi-Session-Id AVP. This AVP MAY be returned by the + Diameter server in an authorization answer, and MUST be used in all + accounting messages for the given session. + */ + struct dict_avp_data data = { + 50, /* Code */ + 0, /* Vendor */ + "Acct-Multi-Session-Id", /* Name */ + AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, /* Fixed flags */ + AVP_FLAG_MANDATORY, /* Fixed flag values */ + AVP_TYPE_OCTETSTRING /* base type of data */ + }; + CHECK_dict_new( DICT_AVP, &data , UTF8String_type, NULL); + } + + /* Accounting-Sub-Session-Id */ + { + /* + The Accounting-Sub-Session-Id AVP (AVP Code 287) is of type + Unsigned64 and contains the accounting sub-session identifier. The + combination of the Session-Id and this AVP MUST be unique per sub- + session, and the value of this AVP MUST be monotonically increased by + one for all new sub-sessions. The absence of this AVP implies no + sub-sessions are in use, with the exception of an Accounting-Request + whose Accounting-Record-Type is set to STOP_RECORD. A STOP_RECORD + message with no Accounting-Sub-Session-Id AVP present will signal the + termination of all sub-sessions for a given Session-Id. + */ + struct dict_avp_data data = { + 287, /* Code */ + 0, /* Vendor */ + "Accounting-Sub-Session-Id", /* Name */ + AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, /* Fixed flags */ + AVP_FLAG_MANDATORY, /* Fixed flag values */ + AVP_TYPE_UNSIGNED64 /* base type of data */ + }; + CHECK_dict_new( DICT_AVP, &data , NULL, NULL); + } + + /* Accounting-Realtime-Required */ + { + /* + The Accounting-Realtime-Required AVP (AVP Code 483) is of type + Enumerated and is sent from the Diameter home authorization server to + the Diameter client or in the Accounting-Answer from the accounting + server. The client uses information in this AVP to decide what to do + if the sending of accounting records to the accounting server has + been temporarily prevented due to, for instance, a network problem. + + + DELIVER_AND_GRANT 1 + + The AVP with Value field set to DELIVER_AND_GRANT means that the + service MUST only be granted as long as there is a connection to + an accounting server. Note that the set of alternative accounting + servers are treated as one server in this sense. Having to move + the accounting record stream to a backup server is not a reason to + discontinue the service to the user. + + + GRANT_AND_STORE 2 + + The AVP with Value field set to GRANT_AND_STORE means that service + SHOULD be granted if there is a connection, or as long as records + can still be stored as described in Section 9.4. + + This is the default behavior if the AVP isn't included in the + reply from the authorization server. + + + GRANT_AND_LOSE 3 + + The AVP with Value field set to GRANT_AND_LOSE means that service + SHOULD be granted even if the records can not be delivered or + stored. + */ + struct dict_object * type; + struct dict_type_data tdata = { AVP_TYPE_INTEGER32, "Enumerated(Accounting-Realtime-Required)" , NULL, NULL}; + struct dict_enumval_data t_1 = { "DELIVER_AND_GRANT", { .i32 = 1 }}; + struct dict_enumval_data t_2 = { "GRANT_AND_STORE", { .i32 = 2 }}; + struct dict_enumval_data t_3 = { "GRANT_AND_LOSE", { .i32 = 3 }}; + struct dict_avp_data data = { + 483, /* Code */ + 0, /* Vendor */ + "Accounting-Realtime-Required", /* Name */ + AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, /* Fixed flags */ + AVP_FLAG_MANDATORY, /* Fixed flag values */ + AVP_TYPE_INTEGER32 /* base type of data */ + }; + /* Create the Enumerated type, and then the AVP */ + CHECK_dict_new( DICT_TYPE, &tdata , NULL, &type); + CHECK_dict_new( DICT_ENUMVAL, &t_1 , type, NULL); + CHECK_dict_new( DICT_ENUMVAL, &t_2 , type, NULL); + CHECK_dict_new( DICT_ENUMVAL, &t_3 , type, NULL); + CHECK_dict_new( DICT_AVP, &data , type, NULL); + } + + } + + /* Commands section */ + { + /* To avoid defining global variables for all the AVP that we use here, we do search the dictionary in each sub-block. + * This is far from optimal, but the code is clearer like this, and the time it requires at execution is not noticeable. + */ + + /* Generic message syntax when the 'E' bit is set */ + { + /* + The 'E' (Error Bit) in the Diameter header is set when the request + caused a protocol-related error (see Section 7.1.3). A message with + the 'E' bit MUST NOT be sent as a response to an answer message. + Note that a message with the 'E' bit set is still subjected to the + processing rules defined in Section 6.2. When set, the answer + message will not conform to the ABNF specification for the command, + and will instead conform to the following ABNF: + + Message Format + + <answer-message> ::= < Diameter Header: code, ERR [PXY] > + 0*1< Session-Id > + { Origin-Host } + { Origin-Realm } + { Result-Code } + [ Origin-State-Id ] + [ Error-Message ] + [ Error-Reporting-Host ] + [ Failed-AVP ] + * [ Proxy-Info ] + * [ AVP ] + + Note that the code used in the header is the same than the one found + in the request message, but with the 'R' bit cleared and the 'E' bit + set. The 'P' bit in the header is set to the same value as the one + found in the request message. + */ + struct dict_object * cmd_error; + struct local_rules_definition rules[] = + { { "Session-Id", RULE_FIXED_HEAD,0, 1 } + ,{ "Origin-Host", RULE_REQUIRED, -1, 1 } + ,{ "Origin-Realm", RULE_REQUIRED, -1, 1 } + ,{ "Result-Code", RULE_REQUIRED, -1, 1 } + ,{ "Origin-State-Id", RULE_OPTIONAL, -1, 1 } + ,{ "Error-Message", RULE_OPTIONAL, -1, 1 } + ,{ "Error-Reporting-Host", RULE_OPTIONAL, -1, 1 } + ,{ "Failed-AVP", RULE_OPTIONAL, -1, 1 } + ,{ "Proxy-Info", RULE_OPTIONAL, -1,-1 } + }; + CHECK_FCT( fd_dict_get_error_cmd(dict, &cmd_error) ); + PARSE_loc_rules( rules, cmd_error ); + } + + /* Capabilities-Exchange-Request */ + { + /* + The Capabilities-Exchange-Request (CER), indicated by the Command- + Code set to 257 and the Command Flags' 'R' bit set, is sent to + exchange local capabilities. Upon detection of a transport failure, + this message MUST NOT be sent to an alternate peer. + + When Diameter is run over SCTP [RFC2960], which allows for + connections to span multiple interfaces and multiple IP addresses, + the Capabilities-Exchange-Request message MUST contain one Host-IP- + Address AVP for each potential IP address that MAY be locally used + when transmitting Diameter messages. + + Message Format + + <CER> ::= < Diameter Header: 257, REQ > + { Origin-Host } + { Origin-Realm } + 1* { Host-IP-Address } + { Vendor-Id } + { Product-Name } + [ Origin-State-Id ] + * [ Supported-Vendor-Id ] + * [ Auth-Application-Id ] + * [ Inband-Security-Id ] + * [ Acct-Application-Id ] + * [ Vendor-Specific-Application-Id ] + [ Firmware-Revision ] + * [ AVP ] + */ + struct dict_object * cmd; + struct dict_cmd_data data = { + 257, /* Code */ + #if CC_CAPABILITIES_EXCHANGE != 257 + #error "CC_CAPABILITIES_EXCHANGE definition mismatch" + #endif + "Capabilities-Exchange-Request", /* Name */ + CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_RETRANSMIT | CMD_FLAG_ERROR, /* Fixed flags */ + CMD_FLAG_REQUEST /* Fixed flag values */ + }; + struct local_rules_definition rules[] = + { { "Origin-Host", RULE_REQUIRED, -1, 1 } + ,{ "Origin-Realm", RULE_REQUIRED, -1, 1 } + ,{ "Host-IP-Address", RULE_REQUIRED, -1,-1 } + ,{ "Vendor-Id", RULE_REQUIRED, -1, 1 } + ,{ "Product-Name", RULE_REQUIRED, -1, 1 } + ,{ "Origin-State-Id", RULE_OPTIONAL, -1, 1 } + ,{ "Supported-Vendor-Id", RULE_OPTIONAL, -1,-1 } + ,{ "Auth-Application-Id", RULE_OPTIONAL, -1,-1 } + ,{ "Inband-Security-Id", RULE_OPTIONAL, -1,-1 } + ,{ "Acct-Application-Id", RULE_OPTIONAL, -1,-1 } + ,{ "Vendor-Specific-Application-Id", RULE_OPTIONAL, -1,-1 } + ,{ "Firmware-Revision", RULE_OPTIONAL, -1, 1 } + }; + + CHECK_dict_new( DICT_COMMAND, &data , NULL, &cmd); + PARSE_loc_rules( rules, cmd ); + } + + /* Capabilities-Exchange-Answer */ + { + /* + The Capabilities-Exchange-Answer (CEA), indicated by the Command-Code + set to 257 and the Command Flags' 'R' bit cleared, is sent in + response to a CER message. + + When Diameter is run over SCTP [RFC2960], which allows connections to + span multiple interfaces, hence, multiple IP addresses, the + Capabilities-Exchange-Answer message MUST contain one Host-IP-Address + AVP for each potential IP address that MAY be locally used when + transmitting Diameter messages. + + Message Format + + <CEA> ::= < Diameter Header: 257 > + { Result-Code } + { Origin-Host } + { Origin-Realm } + 1* { Host-IP-Address } + { Vendor-Id } + { Product-Name } + [ Origin-State-Id ] + [ Error-Message ] + [ Failed-AVP ] + * [ Supported-Vendor-Id ] + * [ Auth-Application-Id ] + * [ Inband-Security-Id ] + * [ Acct-Application-Id ] + * [ Vendor-Specific-Application-Id ] + [ Firmware-Revision ] + * [ AVP ] + */ + struct dict_object * cmd; + struct dict_cmd_data data = { + 257, /* Code */ + #if CC_CAPABILITIES_EXCHANGE != 257 + #error "CC_CAPABILITIES_EXCHANGE definition mismatch" + #endif + "Capabilities-Exchange-Answer", /* Name */ + CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_RETRANSMIT, /* Fixed flags */ + 0 /* Fixed flag values */ + }; + struct local_rules_definition rules[] = + { { "Result-Code", RULE_REQUIRED, -1, 1 } + ,{ "Origin-Host", RULE_REQUIRED, -1, 1 } + ,{ "Origin-Realm", RULE_REQUIRED, -1, 1 } + ,{ "Host-IP-Address", RULE_REQUIRED, -1,-1 } + ,{ "Vendor-Id", RULE_REQUIRED, -1, 1 } + ,{ "Product-Name", RULE_REQUIRED, -1, 1 } + ,{ "Origin-State-Id", RULE_OPTIONAL, -1, 1 } + ,{ "Error-Message", RULE_OPTIONAL, -1, 1 } + ,{ "Failed-AVP", RULE_OPTIONAL, -1, 1 } + ,{ "Supported-Vendor-Id", RULE_OPTIONAL, -1,-1 } + ,{ "Auth-Application-Id", RULE_OPTIONAL, -1,-1 } + ,{ "Inband-Security-Id", RULE_OPTIONAL, -1,-1 } + ,{ "Acct-Application-Id", RULE_OPTIONAL, -1,-1 } + ,{ "Vendor-Specific-Application-Id", RULE_OPTIONAL, -1,-1 } + ,{ "Firmware-Revision", RULE_OPTIONAL, -1, 1 } + }; + + CHECK_dict_new( DICT_COMMAND, &data , NULL, &cmd); + PARSE_loc_rules( rules, cmd ); + } + + /* Disconnect-Peer-Request */ + { + /* + The Disconnect-Peer-Request (DPR), indicated by the Command-Code set + to 282 and the Command Flags' 'R' bit set, is sent to a peer to + inform its intentions to shutdown the transport connection. Upon + detection of a transport failure, this message MUST NOT be sent to an + alternate peer. + + Message Format + + <DPR> ::= < Diameter Header: 282, REQ > + { Origin-Host } + { Origin-Realm } + { Disconnect-Cause } + */ + struct dict_object * cmd; + struct dict_cmd_data data = { + 282, /* Code */ + #if CC_DISCONNECT_PEER != 282 + #error "CC_DISCONNECT_PEER definition mismatch" + #endif + "Disconnect-Peer-Request", /* Name */ + CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_RETRANSMIT | CMD_FLAG_ERROR, /* Fixed flags */ + CMD_FLAG_REQUEST /* Fixed flag values */ + }; + struct local_rules_definition rules[] = + { { "Origin-Host", RULE_REQUIRED, -1, 1 } + ,{ "Origin-Realm", RULE_REQUIRED, -1, 1 } + ,{ "Disconnect-Cause", RULE_REQUIRED, -1, 1 } + }; + + CHECK_dict_new( DICT_COMMAND, &data , NULL, &cmd); + PARSE_loc_rules( rules, cmd ); + } + + /* Disconnect-Peer-Answer */ + { + /* + The Disconnect-Peer-Answer (DPA), indicated by the Command-Code set + to 282 and the Command Flags' 'R' bit cleared, is sent as a response + to the Disconnect-Peer-Request message. Upon receipt of this + message, the transport connection is shutdown. + + Message Format + + <DPA> ::= < Diameter Header: 282 > + { Result-Code } + { Origin-Host } + { Origin-Realm } + [ Error-Message ] + [ Failed-AVP ] + */ + struct dict_object * cmd; + struct dict_cmd_data data = { + 282, /* Code */ + #if CC_DISCONNECT_PEER != 282 + #error "CC_DISCONNECT_PEER definition mismatch" + #endif + "Disconnect-Peer-Answer", /* Name */ + CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_RETRANSMIT, /* Fixed flags */ + 0 /* Fixed flag values */ + }; + struct local_rules_definition rules[] = + { { "Result-Code", RULE_REQUIRED, -1, 1 } + ,{ "Origin-Host", RULE_REQUIRED, -1, 1 } + ,{ "Origin-Realm", RULE_REQUIRED, -1, 1 } + ,{ "Error-Message", RULE_OPTIONAL, -1, 1 } + ,{ "Failed-AVP", RULE_OPTIONAL, -1, 1 } + }; + + CHECK_dict_new( DICT_COMMAND, &data , NULL, &cmd); + PARSE_loc_rules( rules, cmd ); + } + + /* Device-Watchdog-Request */ + { + /* + The Device-Watchdog-Request (DWR), indicated by the Command-Code set + to 280 and the Command Flags' 'R' bit set, is sent to a peer when no + traffic has been exchanged between two peers (see Section 5.5.3). + Upon detection of a transport failure, this message MUST NOT be sent + to an alternate peer. + + Message Format + + <DWR> ::= < Diameter Header: 280, REQ > + { Origin-Host } + { Origin-Realm } + [ Origin-State-Id ] + */ + struct dict_object * cmd; + struct dict_cmd_data data = { + 280, /* Code */ + #if CC_DEVICE_WATCHDOG != 280 + #error "CC_DEVICE_WATCHDOG definition mismatch" + #endif + "Device-Watchdog-Request", /* Name */ + CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_RETRANSMIT | CMD_FLAG_ERROR, /* Fixed flags */ + CMD_FLAG_REQUEST /* Fixed flag values */ + }; + struct local_rules_definition rules[] = + { { "Origin-Host", RULE_REQUIRED, -1, 1 } + ,{ "Origin-Realm", RULE_REQUIRED, -1, 1 } + ,{ "Origin-State-Id", RULE_OPTIONAL, -1, 1 } + }; + + CHECK_dict_new( DICT_COMMAND, &data , NULL, &cmd); + PARSE_loc_rules( rules, cmd ); + } + + /* Device-Watchdog-Answer */ + { + /* + The Device-Watchdog-Answer (DWA), indicated by the Command-Code set + to 280 and the Command Flags' 'R' bit cleared, is sent as a response + to the Device-Watchdog-Request message. + + Message Format + + <DWA> ::= < Diameter Header: 280 > + { Result-Code } + { Origin-Host } + { Origin-Realm } + [ Error-Message ] + [ Failed-AVP ] + [ Origin-State-Id ] + */ + struct dict_object * cmd; + struct dict_cmd_data data = { + 280, /* Code */ + #if CC_DEVICE_WATCHDOG != 280 + #error "CC_DEVICE_WATCHDOG definition mismatch" + #endif + "Device-Watchdog-Answer", /* Name */ + CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_RETRANSMIT, /* Fixed flags */ + 0 /* Fixed flag values */ + }; + struct local_rules_definition rules[] = + { { "Result-Code", RULE_REQUIRED, -1, 1 } + ,{ "Origin-Host", RULE_REQUIRED, -1, 1 } + ,{ "Origin-Realm", RULE_REQUIRED, -1, 1 } + ,{ "Error-Message", RULE_OPTIONAL, -1, 1 } + ,{ "Failed-AVP", RULE_OPTIONAL, -1, 1 } + ,{ "Origin-State-Id", RULE_OPTIONAL, -1, 1 } + }; + + CHECK_dict_new( DICT_COMMAND, &data , NULL, &cmd); + PARSE_loc_rules( rules, cmd ); + } + + /* Re-Auth-Request */ + { + /* + The Re-Auth-Request (RAR), indicated by the Command-Code set to 258 + and the message flags' 'R' bit set, may be sent by any server to the + access device that is providing session service, to request that the + user be re-authenticated and/or re-authorized. + + + Message Format + + <RAR> ::= < Diameter Header: 258, REQ, PXY > + < Session-Id > + { Origin-Host } + { Origin-Realm } + { Destination-Realm } + { Destination-Host } + { Auth-Application-Id } + { Re-Auth-Request-Type } + [ User-Name ] + [ Origin-State-Id ] + * [ Proxy-Info ] + * [ Route-Record ] + * [ AVP ] + */ + struct dict_object * cmd; + struct dict_cmd_data data = { + 258, /* Code */ + #if CC_RE_AUTH != 258 + #error "CC_RE_AUTH definition mismatch" + #endif + "Re-Auth-Request", /* Name */ + CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_ERROR, /* Fixed flags */ + CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE /* Fixed flag values */ + }; + struct local_rules_definition rules[] = + { { "Session-Id", RULE_FIXED_HEAD, -1, 1 } + ,{ "Origin-Host", RULE_REQUIRED, -1, 1 } + ,{ "Origin-Realm", RULE_REQUIRED, -1, 1 } + ,{ "Destination-Realm", RULE_REQUIRED, -1, 1 } + ,{ "Destination-Host", RULE_REQUIRED, -1, 1 } + ,{ "Auth-Application-Id", RULE_REQUIRED, -1, 1 } + ,{ "Re-Auth-Request-Type", RULE_REQUIRED, -1, 1 } + ,{ "User-Name", RULE_OPTIONAL, -1, 1 } + ,{ "Origin-State-Id", RULE_OPTIONAL, -1, 1 } + ,{ "Proxy-Info", RULE_OPTIONAL, -1,-1 } + ,{ "Route-Record", RULE_OPTIONAL, -1,-1 } + }; + + CHECK_dict_new( DICT_COMMAND, &data , NULL, &cmd); + PARSE_loc_rules( rules, cmd ); + } + + /* Re-Auth-Answer */ + { + /* + The Re-Auth-Answer (RAA), indicated by the Command-Code set to 258 + and the message flags' 'R' bit clear, is sent in response to the RAR. + The Result-Code AVP MUST be present, and indicates the disposition of + the request. + + A successful RAA message MUST be followed by an application-specific + authentication and/or authorization message. + + + Message Format + + <RAA> ::= < Diameter Header: 258, PXY > + < Session-Id > + { Result-Code } + { Origin-Host } + { Origin-Realm } + [ User-Name ] + [ Origin-State-Id ] + [ Error-Message ] + [ Error-Reporting-Host ] + [ Failed-AVP ] + * [ Redirect-Host ] + [ Redirect-Host-Usage ] + [ Redirect-Max-Cache-Time ] + * [ Proxy-Info ] + * [ AVP ] + */ + struct dict_object * cmd; + struct dict_cmd_data data = { + 258, /* Code */ + #if CC_RE_AUTH != 258 + #error "CC_RE_AUTH definition mismatch" + #endif + "Re-Auth-Answer", /* Name */ + CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE, /* Fixed flags */ + CMD_FLAG_PROXIABLE /* Fixed flag values */ + }; + struct local_rules_definition rules[] = + { { "Session-Id", RULE_FIXED_HEAD, -1, 1 } + ,{ "Result-Code", RULE_REQUIRED, -1, 1 } + ,{ "Origin-Host", RULE_REQUIRED, -1, 1 } + ,{ "Origin-Realm", RULE_REQUIRED, -1, 1 } + ,{ "User-Name", RULE_OPTIONAL, -1, 1 } + ,{ "Origin-State-Id", RULE_OPTIONAL, -1, 1 } + ,{ "Error-Message", RULE_OPTIONAL, -1, 1 } + ,{ "Error-Reporting-Host", RULE_OPTIONAL, -1, 1 } + ,{ "Failed-AVP", RULE_OPTIONAL, -1, 1 } + ,{ "Redirect-Host", RULE_OPTIONAL, -1,-1 } + ,{ "Redirect-Host-Usage", RULE_OPTIONAL, -1, 1 } + ,{ "Redirect-Max-Cache-Time", RULE_OPTIONAL, -1, 1 } + ,{ "Proxy-Info", RULE_OPTIONAL, -1,-1 } + }; + + CHECK_dict_new( DICT_COMMAND, &data , NULL, &cmd); + PARSE_loc_rules( rules, cmd ); + } + + /* Session-Termination-Request */ + { + /* + The Session-Termination-Request (STR), indicated by the Command-Code + set to 275 and the Command Flags' 'R' bit set, is sent by the access + device to inform the Diameter Server that an authenticated and/or + authorized session is being terminated. + + + Message Format + + <STR> ::= < Diameter Header: 275, REQ, PXY > + < Session-Id > + { Origin-Host } + { Origin-Realm } + { Destination-Realm } + { Auth-Application-Id } + { Termination-Cause } + [ User-Name ] + [ Destination-Host ] + * [ Class ] + [ Origin-State-Id ] + * [ Proxy-Info ] + * [ Route-Record ] + * [ AVP ] + */ + struct dict_object * cmd; + struct dict_cmd_data data = { + 275, /* Code */ + #if CC_SESSION_TERMINATION != 275 + #error "CC_SESSION_TERMINATION definition mismatch" + #endif + "Session-Termination-Request", /* Name */ + CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_ERROR, /* Fixed flags */ + CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE /* Fixed flag values */ + }; + struct local_rules_definition rules[] = + { { "Session-Id", RULE_FIXED_HEAD, -1, 1 } + ,{ "Origin-Host", RULE_REQUIRED, -1, 1 } + ,{ "Origin-Realm", RULE_REQUIRED, -1, 1 } + ,{ "Destination-Realm", RULE_REQUIRED, -1, 1 } + ,{ "Auth-Application-Id", RULE_REQUIRED, -1, 1 } + ,{ "Termination-Cause", RULE_REQUIRED, -1, 1 } + ,{ "User-Name", RULE_OPTIONAL, -1, 1 } + ,{ "Destination-Host", RULE_OPTIONAL, -1, 1 } + ,{ "Class", RULE_OPTIONAL, -1,-1 } + ,{ "Origin-State-Id", RULE_OPTIONAL, -1, 1 } + ,{ "Proxy-Info", RULE_OPTIONAL, -1,-1 } + ,{ "Route-Record", RULE_OPTIONAL, -1,-1 } + }; + + CHECK_dict_new( DICT_COMMAND, &data , NULL, &cmd); + PARSE_loc_rules( rules, cmd ); + } + + /* Session-Termination-Answer */ + { + /* + The Session-Termination-Answer (STA), indicated by the Command-Code + set to 275 and the message flags' 'R' bit clear, is sent by the + Diameter Server to acknowledge the notification that the session has + been terminated. The Result-Code AVP MUST be present, and MAY + contain an indication that an error occurred while servicing the STR. + + Upon sending or receipt of the STA, the Diameter Server MUST release + all resources for the session indicated by the Session-Id AVP. Any + intermediate server in the Proxy-Chain MAY also release any + resources, if necessary. + + Message Format + + <STA> ::= < Diameter Header: 275, PXY > + < Session-Id > + { Result-Code } + { Origin-Host } + { Origin-Realm } + [ User-Name ] + * [ Class ] + [ Error-Message ] + [ Error-Reporting-Host ] + [ Failed-AVP ] + [ Origin-State-Id ] + * [ Redirect-Host ] + [ Redirect-Host-Usage ] + [ Redirect-Max-Cache-Time ] + * [ Proxy-Info ] + * [ AVP ] + */ + struct dict_object * cmd; + struct dict_cmd_data data = { + 275, /* Code */ + #if CC_SESSION_TERMINATION != 275 + #error "CC_SESSION_TERMINATION definition mismatch" + #endif + "Session-Termination-Answer", /* Name */ + CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE, /* Fixed flags */ + CMD_FLAG_PROXIABLE /* Fixed flag values */ + }; + struct local_rules_definition rules[] = + { { "Session-Id", RULE_FIXED_HEAD, -1, 1 } + ,{ "Result-Code", RULE_REQUIRED, -1, 1 } + ,{ "Origin-Host", RULE_REQUIRED, -1, 1 } + ,{ "Origin-Realm", RULE_REQUIRED, -1, 1 } + ,{ "User-Name", RULE_OPTIONAL, -1, 1 } + ,{ "Class", RULE_OPTIONAL, -1,-1 } + ,{ "Error-Message", RULE_OPTIONAL, -1, 1 } + ,{ "Error-Reporting-Host", RULE_OPTIONAL, -1, 1 } + ,{ "Failed-AVP", RULE_OPTIONAL, -1, 1 } + ,{ "Origin-State-Id", RULE_OPTIONAL, -1, 1 } + ,{ "Redirect-Host", RULE_OPTIONAL, -1,-1 } + ,{ "Redirect-Host-Usage", RULE_OPTIONAL, -1, 1 } + ,{ "Redirect-Max-Cache-Time", RULE_OPTIONAL, -1, 1 } + ,{ "Proxy-Info", RULE_OPTIONAL, -1,-1 } + }; + + CHECK_dict_new( DICT_COMMAND, &data , NULL, &cmd); + PARSE_loc_rules( rules, cmd ); + } + + /* Abort-Session-Request */ + { + /* + The Abort-Session-Request (ASR), indicated by the Command-Code set to + 274 and the message flags' 'R' bit set, may be sent by any server to + the access device that is providing session service, to request that + the session identified by the Session-Id be stopped. + + + Message Format + + <ASR> ::= < Diameter Header: 274, REQ, PXY > + < Session-Id > + { Origin-Host } + { Origin-Realm } + { Destination-Realm } + { Destination-Host } + { Auth-Application-Id } + [ User-Name ] + [ Origin-State-Id ] + * [ Proxy-Info ] + * [ Route-Record ] + * [ AVP ] + */ + struct dict_object * cmd; + struct dict_cmd_data data = { + 274, /* Code */ + #if CC_ABORT_SESSION != 274 + #error "CC_ABORT_SESSION definition mismatch" + #endif + "Abort-Session-Request", /* Name */ + CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_ERROR, /* Fixed flags */ + CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE /* Fixed flag values */ + }; + struct local_rules_definition rules[] = + { { "Session-Id", RULE_FIXED_HEAD, -1, 1 } + ,{ "Origin-Host", RULE_REQUIRED, -1, 1 } + ,{ "Origin-Realm", RULE_REQUIRED, -1, 1 } + ,{ "Destination-Realm", RULE_REQUIRED, -1, 1 } + ,{ "Destination-Host", RULE_REQUIRED, -1, 1 } + ,{ "Auth-Application-Id", RULE_REQUIRED, -1, 1 } + ,{ "User-Name", RULE_OPTIONAL, -1, 1 } + ,{ "Origin-State-Id", RULE_OPTIONAL, -1, 1 } + ,{ "Proxy-Info", RULE_OPTIONAL, -1,-1 } + ,{ "Route-Record", RULE_OPTIONAL, -1,-1 } + }; + + CHECK_dict_new( DICT_COMMAND, &data , NULL, &cmd); + PARSE_loc_rules( rules, cmd ); + } + + /* Abort-Session-Answer */ + { + /* + The Abort-Session-Answer (ASA), indicated by the Command-Code set to + 274 and the message flags' 'R' bit clear, is sent in response to the + ASR. The Result-Code AVP MUST be present, and indicates the + disposition of the request. + + If the session identified by Session-Id in the ASR was successfully + terminated, Result-Code is set to DIAMETER_SUCCESS. If the session + is not currently active, Result-Code is set to + DIAMETER_UNKNOWN_SESSION_ID. If the access device does not stop the + session for any other reason, Result-Code is set to + DIAMETER_UNABLE_TO_COMPLY. + + Message Format + + <ASA> ::= < Diameter Header: 274, PXY > + < Session-Id > + { Result-Code } + { Origin-Host } + { Origin-Realm } + [ User-Name ] + [ Origin-State-Id ] + [ Error-Message ] + [ Error-Reporting-Host ] + [ Failed-AVP ] + * [ Redirect-Host ] + [ Redirect-Host-Usage ] + [ Redirect-Max-Cache-Time ] + * [ Proxy-Info ] + * [ AVP ] + */ + struct dict_object * cmd; + struct dict_cmd_data data = { + 274, /* Code */ + #if CC_ABORT_SESSION != 274 + #error "CC_ABORT_SESSION definition mismatch" + #endif + "Abort-Session-Answer", /* Name */ + CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE, /* Fixed flags */ + CMD_FLAG_PROXIABLE /* Fixed flag values */ + }; + struct local_rules_definition rules[] = + { { "Session-Id", RULE_FIXED_HEAD, -1, 1 } + ,{ "Result-Code", RULE_REQUIRED, -1, 1 } + ,{ "Origin-Host", RULE_REQUIRED, -1, 1 } + ,{ "Origin-Realm", RULE_REQUIRED, -1, 1 } + ,{ "User-Name", RULE_OPTIONAL, -1, 1 } + ,{ "Origin-State-Id", RULE_OPTIONAL, -1, 1 } + ,{ "Error-Message", RULE_OPTIONAL, -1, 1 } + ,{ "Error-Reporting-Host", RULE_OPTIONAL, -1, 1 } + ,{ "Failed-AVP", RULE_OPTIONAL, -1, 1 } + ,{ "Redirect-Host", RULE_OPTIONAL, -1,-1 } + ,{ "Redirect-Host-Usage", RULE_OPTIONAL, -1, 1 } + ,{ "Redirect-Max-Cache-Time", RULE_OPTIONAL, -1, 1 } + ,{ "Proxy-Info", RULE_OPTIONAL, -1,-1 } + }; + + CHECK_dict_new( DICT_COMMAND, &data , NULL, &cmd); + PARSE_loc_rules( rules, cmd ); + } + + /* Accounting-Request */ + { + /* + The Accounting-Request (ACR) command, indicated by the Command-Code + field set to 271 and the Command Flags' 'R' bit set, is sent by a + Diameter node, acting as a client, in order to exchange accounting + information with a peer. + + One of Acct-Application-Id and Vendor-Specific-Application-Id AVPs + MUST be present. If the Vendor-Specific-Application-Id grouped AVP + is present, it MUST include an Acct-Application-Id AVP. + + The AVP listed below SHOULD include service specific accounting AVPs, + as described in Section 9.3. + + + Message Format + + <ACR> ::= < Diameter Header: 271, REQ, PXY > + < Session-Id > + { Origin-Host } + { Origin-Realm } + { Destination-Realm } + { Accounting-Record-Type } + { Accounting-Record-Number } + [ Acct-Application-Id ] + [ Vendor-Specific-Application-Id ] + [ User-Name ] + [ Destination-Host ] + [ Accounting-Sub-Session-Id ] + [ Acct-Session-Id ] + [ Acct-Multi-Session-Id ] + [ Acct-Interim-Interval ] + [ Accounting-Realtime-Required ] + [ Origin-State-Id ] + [ Event-Timestamp ] + * [ Proxy-Info ] + * [ Route-Record ] + * [ AVP ] + */ + struct dict_object * cmd; + struct dict_cmd_data data = { + 271, /* Code */ + #if CC_ACCOUNTING != 271 + #error "CC_ACCOUNTING definition mismatch" + #endif + "Accounting-Request", /* Name */ + CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_ERROR, /* Fixed flags */ + CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE /* Fixed flag values */ + }; + struct local_rules_definition rules[] = + { { "Session-Id", RULE_FIXED_HEAD, -1, 1 } + ,{ "Origin-Host", RULE_REQUIRED, -1, 1 } + ,{ "Origin-Realm", RULE_REQUIRED, -1, 1 } + ,{ "Destination-Realm", RULE_REQUIRED, -1, 1 } + ,{ "Accounting-Record-Type", RULE_REQUIRED, -1, 1 } + ,{ "Accounting-Record-Number", RULE_REQUIRED, -1, 1 } + ,{ "Acct-Application-Id", RULE_OPTIONAL, -1, 1 } + ,{ "Vendor-Specific-Application-Id", RULE_OPTIONAL, -1, 1 } + ,{ "User-Name", RULE_OPTIONAL, -1, 1 } + ,{ "Destination-Host", RULE_OPTIONAL, -1, 1 } + ,{ "Accounting-Sub-Session-Id", RULE_OPTIONAL, -1, 1 } + ,{ "Acct-Session-Id", RULE_OPTIONAL, -1, 1 } + ,{ "Acct-Multi-Session-Id", RULE_OPTIONAL, -1, 1 } + ,{ "Acct-Interim-Interval", RULE_OPTIONAL, -1, 1 } + ,{ "Accounting-Realtime-Required", RULE_OPTIONAL, -1, 1 } + ,{ "Origin-State-Id", RULE_OPTIONAL, -1, 1 } + ,{ "Event-Timestamp", RULE_OPTIONAL, -1, 1 } + ,{ "Proxy-Info", RULE_OPTIONAL, -1,-1 } + ,{ "Route-Record", RULE_OPTIONAL, -1,-1 } + }; + + CHECK_dict_new( DICT_COMMAND, &data , NULL, &cmd); + PARSE_loc_rules( rules, cmd ); + } + + /* Accounting-Answer */ + { + /* + The Accounting-Answer (ACA) command, indicated by the Command-Code + field set to 271 and the Command Flags' 'R' bit cleared, is used to + acknowledge an Accounting-Request command. The Accounting-Answer + command contains the same Session-Id as the corresponding request. + + Only the target Diameter Server, known as the home Diameter Server, + SHOULD respond with the Accounting-Answer command. + + One of Acct-Application-Id and Vendor-Specific-Application-Id AVPs + MUST be present. If the Vendor-Specific-Application-Id grouped AVP + is present, it MUST contain an Acct-Application-Id AVP. + + The AVP listed below SHOULD include service specific accounting AVPs, + as described in Section 9.3. + + + Message Format + + <ACA> ::= < Diameter Header: 271, PXY > + < Session-Id > + { Result-Code } + { Origin-Host } + { Origin-Realm } + { Accounting-Record-Type } + { Accounting-Record-Number } + [ Acct-Application-Id ] + [ Vendor-Specific-Application-Id ] + [ User-Name ] + [ Accounting-Sub-Session-Id ] + [ Acct-Session-Id ] + [ Acct-Multi-Session-Id ] + [ Error-Message ] + [ Error-Reporting-Host ] + [ Failed-AVP ] + [ Acct-Interim-Interval ] + [ Accounting-Realtime-Required ] + [ Origin-State-Id ] + [ Event-Timestamp ] + * [ Proxy-Info ] + * [ AVP ] + */ + struct dict_object * cmd; + struct dict_cmd_data data = { + 271, /* Code */ + #if CC_ACCOUNTING != 271 + #error "CC_ACCOUNTING definition mismatch" + #endif + "Accounting-Answer", /* Name */ + CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE, /* Fixed flags */ + CMD_FLAG_PROXIABLE /* Fixed flag values */ + }; + struct local_rules_definition rules[] = + { { "Session-Id", RULE_FIXED_HEAD, -1, 1 } + ,{ "Result-Code", RULE_REQUIRED, -1, 1 } + ,{ "Origin-Host", RULE_REQUIRED, -1, 1 } + ,{ "Origin-Realm", RULE_REQUIRED, -1, 1 } + ,{ "Accounting-Record-Type", RULE_REQUIRED, -1, 1 } + ,{ "Accounting-Record-Number", RULE_REQUIRED, -1, 1 } + ,{ "Acct-Application-Id", RULE_OPTIONAL, -1, 1 } + ,{ "Vendor-Specific-Application-Id", RULE_OPTIONAL, -1, 1 } + ,{ "User-Name", RULE_OPTIONAL, -1, 1 } + ,{ "Accounting-Sub-Session-Id", RULE_OPTIONAL, -1, 1 } + ,{ "Acct-Session-Id", RULE_OPTIONAL, -1, 1 } + ,{ "Acct-Multi-Session-Id", RULE_OPTIONAL, -1, 1 } + ,{ "Error-Message", RULE_OPTIONAL, -1, 1 } + ,{ "Error-Reporting-Host", RULE_OPTIONAL, -1, 1 } + ,{ "Failed-AVP", RULE_OPTIONAL, -1, 1 } + ,{ "Acct-Interim-Interval", RULE_OPTIONAL, -1, 1 } + ,{ "Accounting-Realtime-Required", RULE_OPTIONAL, -1, 1 } + ,{ "Origin-State-Id", RULE_OPTIONAL, -1, 1 } + ,{ "Event-Timestamp", RULE_OPTIONAL, -1, 1 } + ,{ "Proxy-Info", RULE_OPTIONAL, -1,-1 } + }; + + CHECK_dict_new( DICT_COMMAND, &data , NULL, &cmd); + PARSE_loc_rules( rules, cmd ); + } + } + + return 0; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/freediameter/fd.h Fri Aug 28 19:14:42 2009 +0900 @@ -0,0 +1,48 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis <sdecugis@nict.go.jp> * +* * +* Copyright (c) 2009, WIDE Project and NICT * +* All rights reserved. * +* * +* Redistribution and use of this software in source and binary forms, with or without modification, are * +* permitted provided that the following conditions are met: * +* * +* * Redistributions of source code must retain the above * +* copyright notice, this list of conditions and the * +* following disclaimer. * +* * +* * 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. * +* * +* * Neither the name of the WIDE Project or NICT nor the * +* names of its contributors may be used to endorse or * +* promote products derived from this software without * +* specific prior written permission of WIDE Project and * +* NICT. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. * +*********************************************************************************************************/ + +/* This file contains the definitions for internal use in the freediameter daemon */ + +#ifndef _FD_H +#define _FD_H + +#include <freediameter/freediameter-host.h> +#include <freediameter/freediameter.h> + +/* Create all the dictionary objects defined in the Diameter base RFC. */ +int fd_dict_base_protocol(struct dictionary * dict); + + +#endif /* _FD_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/freediameter/main.c Fri Aug 28 19:14:42 2009 +0900 @@ -0,0 +1,59 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis <sdecugis@nict.go.jp> * +* * +* Copyright (c) 2009, WIDE Project and NICT * +* All rights reserved. * +* * +* Redistribution and use of this software in source and binary forms, with or without modification, are * +* permitted provided that the following conditions are met: * +* * +* * Redistributions of source code must retain the above * +* copyright notice, this list of conditions and the * +* following disclaimer. * +* * +* * 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. * +* * +* * Neither the name of the WIDE Project or NICT nor the * +* names of its contributors may be used to endorse or * +* promote products derived from this software without * +* specific prior written permission of WIDE Project and * +* NICT. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. * +*********************************************************************************************************/ + +#include "fd.h" + +/* Entry point */ +int main(int argc, char * argv[]) +{ + /* Initialize the library */ + CHECK_FCT( fd_lib_init() ); + + /* Name this thread */ + fd_log_threadname("Main"); + + /* Initialize the dictionary */ + CHECK_FCT( fd_dict_init(&fd_g_dict) ); + + /* Add definitions of the base protocol */ + CHECK_FCT( fd_dict_base_protocol(fd_g_dict) ); + + /* For debug */ + fd_dict_dump(fd_g_dict); + + TRACE_DEBUG(INFO, "FreeDiameter daemon initialized."); + + return 0; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/freediameter/tests/CMakeLists.txt Fri Aug 28 19:14:42 2009 +0900 @@ -0,0 +1,47 @@ +# Test directory +PROJECT("libfreediameter tests" C) + +# give the possibility to configure the timeout duration for the tests +OPTION(TEST_TIMEOUT "Timeout for the tests, in seconds (default: 5)?") +IF(TEST_TIMEOUT) + ADD_DEFINITIONS(-DTEST_TIMEOUT=${TEST_TIMEOUT}) +ENDIF(TEST_TIMEOUT) + + +############################# +# List the test cases +SET(TEST_LIST + testdict + testmesg + testmq +) + +############################# +# Some parameters for the tests + +# Add this flag to add some debug information in the tests themselves +ADD_DEFINITIONS(-DTEST_DEBUG) + +INCLUDE_DIRECTORIES( ".." ) + +SET(TEST_COMMON_SRC "") + +FOREACH( SRC_FILE ${FD_COMMON_SRC}) + SET(TEST_COMMON_SRC ${TEST_COMMON_SRC} "../${SRC_FILE}") +ENDFOREACH(SRC_FILE) + +# FOREACH( SRC_FILE ${FD_COMMON_GEN_SRC}) +# SET(TEST_COMMON_SRC ${TEST_COMMON_SRC} "${CMAKE_CURRENT_BINARY_DIR}/../${SRC_FILE}") +# ENDFOREACH(SRC_FILE) + +# Create an archive with the daemon common files +ADD_LIBRARY(fdcore STATIC ${TEST_COMMON_SRC}) + + +############################# +# Compile each test +FOREACH( TEST ${TEST_LIST} ) + ADD_EXECUTABLE(${TEST} ${TEST}.c tests.h) + TARGET_LINK_LIBRARIES(${TEST} libfreediameter fdcore ${FD_LIBS}) + ADD_TEST(${TEST} ${EXECUTABLE_OUTPUT_PATH}/${TEST}) +ENDFOREACH( TEST )
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/freediameter/tests/testdict.c Fri Aug 28 19:14:42 2009 +0900 @@ -0,0 +1,136 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis <sdecugis@nict.go.jp> * +* * +* Copyright (c) 2009, WIDE Project and NICT * +* All rights reserved. * +* * +* Redistribution and use of this software in source and binary forms, with or without modification, are * +* permitted provided that the following conditions are met: * +* * +* * Redistributions of source code must retain the above * +* copyright notice, this list of conditions and the * +* following disclaimer. * +* * +* * 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. * +* * +* * Neither the name of the WIDE Project or NICT nor the * +* names of its contributors may be used to endorse or * +* promote products derived from this software without * +* specific prior written permission of WIDE Project and * +* NICT. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. * +*********************************************************************************************************/ + +#include "tests.h" + +/* Test for the dict_iterate_rules function */ +int iter_test(void * data, struct dict_rule_data * rule) +{ + struct dict_avp_data avpdata; + (*(int *)data)++; + + CHECK( 0, fd_dict_getval ( rule->rule_avp, &avpdata ) ); + TRACE_DEBUG(FULL, "rule #%d: avp '%s'", *(int *)data, avpdata.avp_name); + return 0; +} + +/* Main test routine */ +int main(int argc, char *argv[]) +{ + /* First, initialize the daemon modules */ + INIT_FD(); + + /* Test creating and searching all types of objects */ + { + enum dict_object_type type; + struct dict_object * obj1 = NULL; + struct dict_object * obj2 = NULL; + struct dict_object * obj3 = NULL; + + vendor_id_t vendor_id = 735671; + struct dict_vendor_data vendor1_data = { 735671, "Vendor test 1" }; + struct dict_vendor_data vendor2_data = { 735672, "Vendor test 2" }; + struct dict_application_data app1_data = { 735674, "Application test 1" }; + + + /* Create two vendors */ + CHECK( 0, fd_dict_new ( fd_g_dict, DICT_VENDOR, &vendor1_data , NULL, &obj1 ) ); + CHECK( 0, fd_dict_new ( fd_g_dict, DICT_VENDOR, &vendor2_data , NULL, NULL ) ); + + /* Check we always retrieve the correct vendor object */ + CHECK( 0, fd_dict_search ( fd_g_dict, DICT_VENDOR, VENDOR_BY_ID, &vendor_id, &obj2, ENOENT ) ); + CHECK( obj1, obj2); + CHECK( 0, fd_dict_search ( fd_g_dict, DICT_VENDOR, VENDOR_BY_NAME, "Vendor test 1", &obj2, ENOENT ) ); + CHECK( obj1, obj2); + + /* Check the error conditions */ + CHECK( 0, fd_dict_search ( fd_g_dict, DICT_VENDOR, VENDOR_BY_ID, &vendor_id, NULL, ENOENT ) ); + + vendor_id = 735673; /* Not defined */ + CHECK( ENOENT, fd_dict_search ( fd_g_dict, DICT_VENDOR, VENDOR_BY_ID, &vendor_id, NULL, ENOENT ) ); + CHECK( ENOENT, fd_dict_search ( fd_g_dict, DICT_VENDOR, VENDOR_BY_NAME, "Vendor test 3", NULL, ENOENT ) ); + CHECK( ENOENT, fd_dict_search ( fd_g_dict, DICT_VENDOR, VENDOR_BY_ID, &vendor_id, &obj2, ENOENT ) ); + CHECK( ENOENT, fd_dict_search ( fd_g_dict, DICT_VENDOR, VENDOR_BY_NAME, "Vendor test 3", &obj2, ENOENT ) ); + CHECK( ENOTSUP, fd_dict_search ( fd_g_dict, DICT_VENDOR, VENDOR_BY_NAME, "Vendor test 3", &obj2, ENOTSUP ) ); + + /* Check the get_* functions */ + CHECK( 0, fd_dict_getval ( obj1, &vendor1_data ) ); + CHECK( 735671, vendor1_data.vendor_id ); + CHECK( 0, strcmp(vendor1_data.vendor_name, "Vendor test 1") ); + /* error conditions */ + CHECK( EINVAL, fd_dict_getval ( (struct dict_object *)"not an object", &vendor1_data ) ); + + /* Create the application with vendor1 as parent */ + CHECK( EINVAL, fd_dict_new ( fd_g_dict, DICT_APPLICATION, &app1_data , (struct dict_object *)"bad object", &obj2 ) ); + CHECK( 0, fd_dict_new ( fd_g_dict, DICT_APPLICATION, &app1_data , obj1, &obj2 ) ); + + CHECK( 0, fd_dict_search ( fd_g_dict, DICT_VENDOR, VENDOR_OF_APPLICATION, obj2, &obj3, ENOENT ) ); + CHECK( obj1, obj3); + + /* Creating and searching the other objects is already done in dictionary initialization */ + } + + /* Test creation of the "Example-AVP" grouped AVP from the RFC */ + { + int nbr = 0; + struct dict_object * origin_host_avp = NULL; + struct dict_object * session_id_avp = NULL; + struct dict_object * example_avp_avp = NULL; + struct dict_rule_data rule_data = { NULL, RULE_REQUIRED, -1, -1 }; + struct dict_avp_data example_avp_data = { 999999, 0, "Example-AVP", AVP_FLAG_VENDOR , 0, AVP_TYPE_GROUPED }; + + CHECK( 0, fd_dict_search ( fd_g_dict, DICT_AVP, AVP_BY_NAME, "Origin-Host", &origin_host_avp, ENOENT ) ); + CHECK( 0, fd_dict_search ( fd_g_dict, DICT_AVP, AVP_BY_NAME, "Session-Id", &session_id_avp, ENOENT ) ); + + CHECK( 0, fd_dict_new ( fd_g_dict, DICT_AVP, &example_avp_data , NULL, &example_avp_avp ) ); + + rule_data.rule_avp = origin_host_avp; + rule_data.rule_min = 1; + rule_data.rule_max = 1; + CHECK( 0, fd_dict_new ( fd_g_dict, DICT_RULE, &rule_data, example_avp_avp, NULL ) ); + + rule_data.rule_avp = session_id_avp; + rule_data.rule_min = 1; + rule_data.rule_max = -1; + CHECK( 0, fd_dict_new ( fd_g_dict, DICT_RULE, &rule_data, example_avp_avp, NULL ) ); + + CHECK( 0, fd_dict_iterate_rules ( example_avp_avp, &nbr, iter_test) ); + CHECK( 2, nbr ); + } + + /* That's all for the tests yet */ + PASSTEST(); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/freediameter/tests/testmesg.c Fri Aug 28 19:14:42 2009 +0900 @@ -0,0 +1,1230 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis <sdecugis@nict.go.jp> * +* * +* Copyright (c) 2009, WIDE Project and NICT * +* All rights reserved. * +* * +* Redistribution and use of this software in source and binary forms, with or without modification, are * +* permitted provided that the following conditions are met: * +* * +* * Redistributions of source code must retain the above * +* copyright notice, this list of conditions and the * +* following disclaimer. * +* * +* * 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. * +* * +* * Neither the name of the WIDE Project or NICT nor the * +* names of its contributors may be used to endorse or * +* promote products derived from this software without * +* specific prior written permission of WIDE Project and * +* NICT. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. * +*********************************************************************************************************/ + +#include "tests.h" + +/* Main test routine */ +int main(int argc, char *argv[]) +{ + struct msg * acr = NULL; + struct avp * pi = NULL, *avp1, *avp2; + unsigned char * buf = NULL; + + /* First, initialize the daemon modules */ + INIT_FD(); + + /* Create the message object from model */ + { + struct dict_object * acr_model = NULL; + + /* Now find the ACR dictionary object */ + CHECK( 0, fd_dict_search ( fd_g_dict, DICT_COMMAND, CMD_BY_NAME, "Accounting-Request", &acr_model, ENOENT ) ); + + /* Create the instance, using the templates */ + CHECK( 0, fd_msg_new ( acr_model, 0, &acr ) ); + + /* Check there is no child */ + CHECK( ENOENT, fd_msg_browse ( acr, MSG_BRW_FIRST_CHILD, NULL, NULL) ); + + #if 0 + /* For debug: dump the object */ + fd_log_debug("Dumping Accounting-Request empty message\n"); + fd_msg_dump_walk( 0, acr ); + #endif + } + + /* Create the Proxy-Info AVP from model */ + { + struct dict_object * pi_model = NULL; + + /* Now find the ACR dictionary object */ + CHECK( 0, fd_dict_search ( fd_g_dict, DICT_AVP, AVP_BY_NAME, "Proxy-Info", &pi_model, ENOENT ) ); + + /* Create the instance, using the templates */ + CHECK( 0, fd_msg_avp_new ( pi_model, 0, &pi ) ); + + #if 0 + /* For debug: dump the object */ + fd_log_debug("Dumping Proxy-Info AVP\n"); + fd_msg_dump_walk(0, pi); + fd_log_debug("Dumping dictionary model\n"); + fd_dict_dump_object(pi_model); + #endif + + } + + /* Get a reference to the current last AVP in the message */ + { + int diff = 0; + + CHECK( 0, fd_msg_avp_new ( NULL, 0, &avp1 ) ); + CHECK( 0, fd_msg_avp_add ( acr, MSG_BRW_LAST_CHILD, avp1) ); + + CHECK( 0, fd_msg_browse ( acr, MSG_BRW_LAST_CHILD, &avp2, &diff) ); + CHECK( 1, diff ); + CHECK( avp1, avp2 ); + + /* Check that we cannot add this AVP to another object since it is already linked */ + CHECK( EINVAL, fd_msg_avp_add( pi, MSG_BRW_LAST_CHILD, avp1) ); + } + + /* Now add the Proxy-Info AVP at the end of the message */ + { + CHECK( 0, fd_msg_avp_add( acr, MSG_BRW_LAST_CHILD, pi) ); + #if 0 + /* For debug: dump the object */ + fd_log_debug("Dumping Accounting-Request with Proxy-Info AVP at the end\n"); + fd_msg_dump_walk(0, acr); + #endif + } + + /* Check the last child is now the proxy-Info */ + { + CHECK( 0, fd_msg_browse ( acr, MSG_BRW_LAST_CHILD, &avp2, NULL) ); + CHECK( pi, avp2 ); + } + + /* Check that the avp before the proxy-info is the previous last one */ + { + int diff = 0; + CHECK( 0, fd_msg_browse ( pi, MSG_BRW_PREV, &avp2, &diff) ); + CHECK( avp1, avp2 ); + CHECK( 0, diff); + } + + /* Check that there are no AVP after the proxy-info */ + CHECK( ENOENT, fd_msg_browse ( pi, MSG_BRW_NEXT, NULL, NULL) ); + + /* Test the fd_msg_free function unlinks the object properly */ + { + struct dict_object * rr_model = NULL; + + /* Now find the dictionary object */ + CHECK( 0, fd_dict_search ( fd_g_dict, DICT_AVP, AVP_BY_NAME, "Route-Record", &rr_model, ENOENT ) ); + + /* Create the instance, using the templates */ + CHECK( 0, fd_msg_avp_new ( rr_model, 0, &avp1 ) ); + + /* Add the AVP at the end of the message */ + CHECK( 0, fd_msg_avp_add( pi, MSG_BRW_NEXT, avp1) ); + + /* Check the last AVP of the message is now this one */ + CHECK( 0, fd_msg_browse ( acr, MSG_BRW_LAST_CHILD, &avp2, NULL) ); + CHECK( avp1, avp2 ); + + /* Now delete it */ + CHECK( 0, fd_msg_free( avp1 ) ); + + /* Check the last AVP of the message is back to pi */ + CHECK( 0, fd_msg_browse ( acr, MSG_BRW_LAST_CHILD, &avp2, NULL) ); + CHECK( pi, avp2 ); + + /* Delete the whole message */ + CHECK( 0, fd_msg_free( acr ) ); + } + + /* Recreate the message object */ + { + struct dict_object * acr_model = NULL; + + /* Now find the ACR dictionary object */ + CHECK( 0, fd_dict_search ( fd_g_dict, DICT_COMMAND, CMD_BY_NAME, "Accounting-Request", &acr_model, ENOENT ) ); + + /* Create the instance, using the templates */ + CHECK( 0, fd_msg_new ( acr_model, 0, &acr ) ); + } + + /* Now let's create some additional Dictionary objects for the test */ + { + /* The constant values used here are totally arbitrary chosen */ + struct dict_object * vendor; + { + struct dict_vendor_data vendor_data = { 73565, "Vendor test" }; + CHECK( 0, fd_dict_new ( fd_g_dict, DICT_VENDOR, &vendor_data , NULL, &vendor ) ); + } + + { + struct dict_application_data app_data = { 73566, "Application test" }; + CHECK( 0, fd_dict_new ( fd_g_dict, DICT_APPLICATION, &app_data , vendor, NULL ) ); + } + + { + struct dict_avp_data avp_data = { 73567, 0, "AVP Test - no vendor - f32", 0, 0, AVP_TYPE_FLOAT32 }; + CHECK( 0, fd_dict_new ( fd_g_dict, DICT_AVP, &avp_data , NULL, NULL ) ); + } + + { + struct dict_object * type = NULL; + struct dict_type_data type_data = { AVP_TYPE_INTEGER64, "Int64 test" }; + struct dict_avp_data avp_data = { 73568, 73565, "AVP Test - i64", AVP_FLAG_VENDOR, AVP_FLAG_VENDOR, AVP_TYPE_INTEGER64 }; + CHECK( 0, fd_dict_new ( fd_g_dict, DICT_TYPE, &type_data , NULL, &type ) ); + CHECK( 0, fd_dict_new ( fd_g_dict, DICT_AVP, &avp_data , type, NULL ) ); + } + + { + struct dict_object * type = NULL; + struct dict_type_data type_data = { AVP_TYPE_INTEGER32, "Enum32 test" }; + struct dict_enumval_data val1 = { "i32 const test (val 1)", { .i32 = 1 } }; + struct dict_enumval_data val2 = { "i32 const test (val 2)", { .i32 = 2 } }; + struct dict_enumval_data val3 = { "i32 const test (val -5)",{ .i32 = -5 } }; + struct dict_avp_data avp_data = { 73569, 73565, "AVP Test - enumi32", AVP_FLAG_VENDOR, AVP_FLAG_VENDOR, AVP_TYPE_INTEGER32 }; + + CHECK( 0, fd_dict_new ( fd_g_dict, DICT_TYPE, &type_data , NULL, &type ) ); + CHECK( 0, fd_dict_new ( fd_g_dict, DICT_AVP, &avp_data , type, NULL ) ); + CHECK( 0, fd_dict_new ( fd_g_dict, DICT_ENUMVAL, &val1 , type, NULL ) ); + CHECK( 0, fd_dict_new ( fd_g_dict, DICT_ENUMVAL, &val2 , type, NULL ) ); + CHECK( 0, fd_dict_new ( fd_g_dict, DICT_ENUMVAL, &val3 , type, NULL ) ); + } + + { + struct dict_object * type = NULL; + struct dict_type_data type_data = { AVP_TYPE_OCTETSTRING, "OS test" }; + struct dict_avp_data avp_data = { 73570, 73565, "AVP Test - os", AVP_FLAG_VENDOR, AVP_FLAG_VENDOR, AVP_TYPE_OCTETSTRING }; + CHECK( 0, fd_dict_new ( fd_g_dict, DICT_TYPE, &type_data , NULL, &type ) ); + CHECK( 0, fd_dict_new ( fd_g_dict, DICT_AVP, &avp_data , type, NULL ) ); + } + + { + struct dict_object * type = NULL; + struct dict_type_data type_data = { AVP_TYPE_OCTETSTRING, "OS enum test" }; + struct dict_enumval_data val1 = { "os const test (Test)", { .os = { (unsigned char *)"Test", 4 } } }; + struct dict_enumval_data val2 = { "os const test (waaad)", { .os = { (unsigned char *)"waaad", 5 } } }; + struct dict_enumval_data val3 = { "os const test (waa)", { .os = { (unsigned char *)"waaad", 3 } } }; + struct dict_avp_data avp_data = { 73571, 73565, "AVP Test - enumos", AVP_FLAG_VENDOR, AVP_FLAG_VENDOR, AVP_TYPE_OCTETSTRING }; + + CHECK( 0, fd_dict_new ( fd_g_dict, DICT_TYPE, &type_data , NULL, &type ) ); + CHECK( 0, fd_dict_new ( fd_g_dict, DICT_AVP, &avp_data , type, NULL ) ); + CHECK( 0, fd_dict_new ( fd_g_dict, DICT_ENUMVAL, &val1 , type, NULL ) ); + CHECK( 0, fd_dict_new ( fd_g_dict, DICT_ENUMVAL, &val2 , type, NULL ) ); + CHECK( 0, fd_dict_new ( fd_g_dict, DICT_ENUMVAL, &val3 , type, NULL ) ); + } + + { + struct dict_object * gavp = NULL; + struct dict_avp_data avp_data = { 73572, 73565, "AVP Test - grouped", AVP_FLAG_VENDOR, AVP_FLAG_VENDOR, AVP_TYPE_GROUPED }; + + CHECK( 0, fd_dict_new ( fd_g_dict, DICT_AVP, &avp_data , NULL, &gavp ) ); + + /* Macro to search AVP and create a rule */ + #define ADD_RULE( _parent, _vendor, _avpname, _pos, _min, _max, _ord ) { \ + struct dict_object * _avp = NULL; \ + struct dict_avp_request _req = { (_vendor), 0, (_avpname) }; \ + struct dict_rule_data _data; \ + CHECK( 0, fd_dict_search( fd_g_dict, DICT_AVP, AVP_BY_NAME_AND_VENDOR, &_req, &_avp, ENOENT));\ + _data.rule_avp = _avp; \ + _data.rule_position = (_pos); \ + _data.rule_order = (_ord); \ + _data.rule_min = (_min); \ + _data.rule_max = (_max); \ + CHECK( 0, fd_dict_new ( fd_g_dict, DICT_RULE, &_data , (_parent), NULL ) ); \ + } + + ADD_RULE(gavp, 73565, "AVP Test - os", RULE_OPTIONAL, -1, -1, 0); + + } + + { + struct dict_object * application = NULL; + struct dict_object * command = NULL; + struct dict_cmd_data cmd_data = { 73573, "Test-Command-Request", CMD_FLAG_REQUEST, CMD_FLAG_REQUEST }; + + CHECK( 0, fd_dict_search ( fd_g_dict, DICT_APPLICATION, APPLICATION_BY_NAME, "Application test", &application, ENOENT ) ); + CHECK( 0, fd_dict_new ( fd_g_dict, DICT_COMMAND, &cmd_data , application, &command ) ); + ADD_RULE(command, 0, "AVP Test - no vendor - f32", RULE_FIXED_HEAD, -1, 1, 1); + ADD_RULE(command, 73565, "AVP Test - i64", RULE_REQUIRED, -1, -1, 0); + ADD_RULE(command, 73565, "AVP Test - enumi32", RULE_OPTIONAL, -1, -1, 0); + ADD_RULE(command, 73565, "AVP Test - os", RULE_OPTIONAL, -1, -1, 0); + ADD_RULE(command, 73565, "AVP Test - enumos", RULE_OPTIONAL, -1, -1, 0); + ADD_RULE(command, 73565, "AVP Test - grouped", RULE_OPTIONAL, -1, -1, 0); + } + + { + struct dict_object * gavp = NULL; + struct dict_avp_data avp_data = { 73574, 73565, "AVP Test - rules", AVP_FLAG_VENDOR, AVP_FLAG_VENDOR, AVP_TYPE_GROUPED }; + + CHECK( 0, fd_dict_new ( fd_g_dict, DICT_AVP, &avp_data , NULL, &gavp ) ); + + ADD_RULE(gavp, 0, "AVP Test - no vendor - f32", RULE_FIXED_HEAD, 0, 1, 1); + ADD_RULE(gavp, 73565, "AVP Test - i64", RULE_FIXED_HEAD, -1, 1, 2); + ADD_RULE(gavp, 73565, "AVP Test - enumi32", RULE_FIXED_HEAD, -1, 1, 3); + ADD_RULE(gavp, 73565, "AVP Test - os", RULE_REQUIRED, 2, 3, 0); + ADD_RULE(gavp, 73565, "AVP Test - enumos", RULE_OPTIONAL, 0, 1, 0); + ADD_RULE(gavp, 73565, "AVP Test - grouped", RULE_FIXED_TAIL, -1, 1, 1); + + #if 0 + fd_dict_dump_object ( gavp ); + #endif + } + #if 0 + { + fd_dict_dump_object ( vendor ); + } + #endif + } + + /* Now create some values and check the length is correctly handled */ + { + struct dict_object * cmd_model = NULL; + struct msg * msg = NULL; + struct dict_object * avp_model = NULL; + struct avp * avp = NULL; + union avp_value value; + + CHECK( 0, fd_dict_search ( fd_g_dict, DICT_COMMAND, CMD_BY_NAME, "Test-Command-Request", &cmd_model, ENOENT ) ); + + /* Check an error is trigged if the AVP has no value set */ + { + CHECK( 0, fd_dict_search ( fd_g_dict, DICT_AVP, AVP_BY_NAME, "AVP Test - no vendor - f32", &avp_model, ENOENT ) ); + + CHECK( 0, fd_msg_new ( cmd_model, 0, &msg ) ); + CHECK( 0, fd_msg_avp_new ( avp_model, 0, &avp ) ); + + CHECK( 0, fd_msg_avp_add ( msg, MSG_BRW_FIRST_CHILD, avp ) ); + + CHECK( EINVAL, fd_msg_update_length ( avp ) ); + CHECK( EINVAL, fd_msg_update_length ( msg ) ); + + CHECK( 0, fd_msg_free( msg ) ); + } + + /* Check the sizes are handled properly */ + { + struct avp * avpi = NULL; + struct avp * avpch = NULL; + struct avp_hdr * avpdata = NULL; + struct msg_hdr * msgdata = NULL; + #define ADD_AVP( _parent, _position, _avpi, _avpvendor, _avpname) { \ + struct dict_object * _avp = NULL; \ + struct dict_avp_request _req = { (_avpvendor), 0, (_avpname) }; \ + CHECK( 0, fd_dict_search( fd_g_dict, DICT_AVP, AVP_BY_NAME_AND_VENDOR, &_req, &_avp, ENOENT));\ + CHECK( 0, fd_msg_avp_new ( _avp, 0, &_avpi ) ); \ + CHECK( 0, fd_msg_avp_add ( (_parent), (_position), _avpi ) ); \ + } + /* Create a message with many AVP inside */ + CHECK( 0, fd_msg_new ( cmd_model, 0, &msg ) ); + CHECK( 0, fd_msg_hdr ( msg, &msgdata ) ); + + /* Avp no vendor, float32 => size = 12 */ + ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 0, "AVP Test - no vendor - f32" ); + value.f32 = 3.1415; + CHECK( 0, fd_msg_avp_setvalue ( avpi, &value ) ); + CHECK( 0, fd_msg_update_length ( avpi ) ); + #if 0 + fd_log_debug("AVP no vendor, value 3.1415:\n"); + fd_msg_dump_one(0, avpi); + #endif + CHECK( 0, fd_msg_avp_hdr ( avpi, &avpdata ) ); + CHECK( 12, avpdata->avp_len ); + + /* Check what happens when we delete the value */ + CHECK( 0, fd_msg_avp_setvalue ( avpi, NULL ) ); + CHECK( EINVAL, fd_msg_update_length ( avpi ) ); + CHECK( 0, fd_msg_avp_setvalue ( avpi, &value ) ); + + /* Add a vendor AVP, integer64 => size = 20 */ + ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 73565, "AVP Test - i64" ); + value.i64 = 0x123456789abcdeLL; + CHECK( 0, fd_msg_avp_setvalue ( avpi, &value ) ); + CHECK( 0, fd_msg_update_length ( avpi ) ); + #if 0 + fd_log_debug("AVP vendor, value 0x123456789abcdeL:\n"); + fd_msg_dump_one(0, avpi); + #endif + CHECK( 0, fd_msg_avp_hdr ( avpi, &avpdata ) ); + CHECK( 20, avpdata->avp_len ); + + /* Check the size of the message is 20 (header) + 12 + 20 = 52 */ + CHECK( 0, fd_msg_update_length ( msg ) ); + CHECK( 52, msgdata->msg_length ); + + /* Add an AVP with an enum value */ + ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 73565, "AVP Test - enumi32" ); + { + struct dict_object * type_model = NULL; + struct dict_object * value_model = NULL; + struct dict_enumval_request request; + + CHECK( 0, fd_msg_model ( avpi, &avp_model ) ); + CHECK( 0, fd_dict_search ( fd_g_dict, DICT_TYPE, TYPE_OF_AVP, avp_model, &type_model, ENOENT ) ); + memset(&request, 0, sizeof(request)); + request.type_obj = type_model; + request.search.enum_name = "i32 const test (val 2)"; + CHECK( 0, fd_dict_search ( fd_g_dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &request, &value_model, ENOENT ) ); + CHECK( 0, fd_dict_getval ( value_model, &request.search ) ); + CHECK( 0, fd_msg_avp_setvalue ( avpi, &request.search.enum_value ) ); + #if 0 + fd_log_debug("AVP enum i32, value 2 (from const):\n"); + fd_msg_dump_one(0, avpi); + #endif + } + + /* Add an AVP with an enum value, negative */ + ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 73565, "AVP Test - enumi32" ); + { + struct dict_object * type_model = NULL; + struct dict_object * value_model = NULL; + struct dict_enumval_request request; + + CHECK( 0, fd_msg_model ( avpi, &avp_model ) ); + CHECK( 0, fd_dict_search ( fd_g_dict, DICT_TYPE, TYPE_OF_AVP, avp_model, &type_model, ENOENT ) ); + memset(&request, 0, sizeof(request)); + request.type_obj = type_model; + request.search.enum_name = "i32 const test (val -5)"; + CHECK( 0, fd_dict_search ( fd_g_dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &request, &value_model, ENOENT ) ); + CHECK( 0, fd_dict_getval ( value_model, &request.search ) ); + CHECK( 0, fd_msg_avp_setvalue ( avpi, &request.search.enum_value ) ); + #if 0 + fd_log_debug("AVP enum i32, value -5 (from const):\n"); + fd_msg_dump_one(0, avpi); + #endif + /* Check the size is correct ( 12 for header + 4 for value ) */ + CHECK( 0, fd_msg_update_length ( avpi ) ); + CHECK( 0, fd_msg_avp_hdr ( avpi, &avpdata ) ); + CHECK( 16, avpdata->avp_len ); + } + + /* Now add a value which is not a constant into an enumerated AVP */ + ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 73565, "AVP Test - enumi32" ); + value.i32 = -10; + CHECK( 0, fd_msg_avp_setvalue ( avpi, &value ) ); + CHECK( 0, fd_msg_update_length ( avpi ) ); + #if 0 + fd_log_debug("AVP vendor enum i32, value -10 (not const):\n"); + fd_msg_dump_one(0, avpi); + #endif + + /* Add an octetstring AVP */ + ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 73565, "AVP Test - os" ); + { + unsigned char buf[90]; + memcpy(&buf, "This\0 is a buffer of dat\a. It is not a string so we can have any c\0ntr\0l character here...\0\0", 89); + value.os.data = buf; + value.os.len = 89; + CHECK( 0, fd_msg_avp_setvalue ( avpi, &value ) ); + memset(&buf, 0, sizeof(buf)); /* Test that the OS value is really copied */ + CHECK( 0, fd_msg_update_length ( avpi ) ); + #if 1 + fd_log_debug("AVP octet string, 'This\\0 is a b...'\n"); + fd_msg_dump_one(0, avpi); + #endif + CHECK( 0, fd_msg_avp_hdr ( avpi, &avpdata ) ); + CHECK( 101, avpdata->avp_len ); + CHECK( 'T', avpdata->avp_value->os.data[0] ); + CHECK( 'i', avpdata->avp_value->os.data[6] ); + } + + /* Check the size of the message is 20 (header) + 12 + 20 + 16 * 3 + 101 + 3 (padding) = 204 */ + CHECK( 0, fd_msg_update_length ( msg ) ); + CHECK( 204, msgdata->msg_length ); + + /* Add an octetstring from an enumerated constant */ + ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 73565, "AVP Test - enumos" ); + { + struct dict_object * type_model = NULL; + struct dict_object * value_model = NULL; + struct dict_enumval_request request; + + CHECK( 0, fd_msg_model ( avpi, &avp_model ) ); + CHECK( 0, fd_dict_search ( fd_g_dict, DICT_TYPE, TYPE_OF_AVP, avp_model, &type_model, ENOENT ) ); + memset(&request, 0, sizeof(request)); + request.type_obj = type_model; + request.search.enum_name = "os const test (waaad)"; + CHECK( 0, fd_dict_search ( fd_g_dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &request, &value_model, ENOENT ) ); + CHECK( 0, fd_dict_getval ( value_model, &request.search ) ); + CHECK( 0, fd_msg_avp_setvalue ( avpi, &request.search.enum_value ) ); + #if 0 + fd_log_debug("AVP Enumuerated OctetString (from const):\n"); + fd_msg_dump_one(0, avpi); + #endif + /* Check the size is correct ( 12 for header + 5 for value ) */ + CHECK( 0, fd_msg_update_length ( avpi ) ); + CHECK( 0, fd_msg_avp_hdr ( avpi, &avpdata ) ); + CHECK( 17, avpdata->avp_len ); + } + + /* Add an octetstring from an enumerated constant */ + ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 73565, "AVP Test - enumos" ); + { + struct dict_object * type_model = NULL; + struct dict_object * value_model = NULL; + struct dict_enumval_request request; + + CHECK( 0, fd_msg_model ( avpi, &avp_model ) ); + CHECK( 0, fd_dict_search ( fd_g_dict, DICT_TYPE, TYPE_OF_AVP, avp_model, &type_model, ENOENT ) ); + memset(&request, 0, sizeof(request)); + request.type_obj = type_model; + request.search.enum_name = "os const test (waa)"; + CHECK( 0, fd_dict_search ( fd_g_dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &request, &value_model, ENOENT ) ); + CHECK( 0, fd_dict_getval ( value_model, &request.search ) ); + CHECK( 0, fd_msg_avp_setvalue ( avpi, &request.search.enum_value ) ); + #if 0 + fd_log_debug("AVP Enumuerated OctetString (from const):\n"); + fd_msg_dump_one(0, avpi); + #endif + /* Check the size is correct ( 12 for header + 3 for value ) */ + CHECK( 0, fd_msg_update_length ( avpi ) ); + CHECK( 0, fd_msg_avp_hdr ( avpi, &avpdata ) ); + CHECK( 15, avpdata->avp_len ); + } + + + /* Check the size of the message is 20 (header) + 12 + 20 + 16 * 3 + (101 + 3) + (17 + 3) + (15 + 1) = 240 */ + CHECK( 0, fd_msg_update_length ( msg ) ); + CHECK( 240, msgdata->msg_length ); + + /* Now test the grouped AVPs */ + ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 73565, "AVP Test - grouped" ); + ADD_AVP( avpi, MSG_BRW_LAST_CHILD, avpch, 73565, "AVP Test - os" ); + { + value.os.data = (unsigned char *)"12345678"; + value.os.len = 8; + CHECK( 0, fd_msg_avp_setvalue ( avpch, &value ) ); + #if 0 + fd_log_debug("AVP octet string, '1234678'\n"); + fd_msg_dump_one(0, avpch); + #endif + CHECK( 0, fd_msg_update_length ( avpch ) ); + CHECK( 0, fd_msg_avp_hdr ( avpch, &avpdata ) ); + CHECK( 20, avpdata->avp_len ); + } + ADD_AVP( avpi, MSG_BRW_LAST_CHILD, avpch, 73565, "AVP Test - os" ); + { + value.os.data = (unsigned char *)"123456789"; + value.os.len = 9; + CHECK( 0, fd_msg_avp_setvalue ( avpch, &value ) ); + #if 0 + fd_log_debug("AVP octet string, '12346789'\n"); + fd_msg_dump_one(0, avpch); + #endif + } + + /* Check the size is updated recursively: (gavp hdr: 12) + (avp1: 20) + (avp2: 21 + 3) = 56 */ + CHECK( 0, fd_msg_update_length ( avpi ) ); + CHECK( 0, fd_msg_avp_hdr ( avpi, &avpdata ) ); + CHECK( 56, avpdata->avp_len ); + + /* Add another similar grouped AVP, to have lot of padding */ + ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 73565, "AVP Test - grouped" ); + ADD_AVP( avpi, MSG_BRW_LAST_CHILD, avpch, 73565, "AVP Test - os" ); + { + value.os.data = (unsigned char *)"1"; + value.os.len = 1; + CHECK( 0, fd_msg_avp_setvalue ( avpch, &value ) ); + } + ADD_AVP( avpi, MSG_BRW_LAST_CHILD, avpch, 73565, "AVP Test - os" ); + { + value.os.data = (unsigned char *)"1234567"; + value.os.len = 7; + CHECK( 0, fd_msg_avp_setvalue ( avpch, &value ) ); + } + + /* Now check the global size of the message, if padding is correctly handled */ + /* size = 20 (header) + 12 + 20 + 16 * 3 + (101 + 3) + (17 + 3) + (15 + 1) + * + ( 12 + ( 20 + 21) + 3 ) # padding for the grouped AVP = 3 + * + ( 12 + ( (13 + 3) + 19 ) + 1 ) # and 1 for this one + * size = 240 + 56 + 48 = 344 + */ + CHECK( 0, fd_msg_update_length ( msg ) ); + #if 0 + fd_msg_dump_walk(0, msg); + #endif + CHECK( 344, msgdata->msg_length ); + + /* Set the application to the test application: 73566 */ + msgdata->msg_appl = 73566; + + /* Set the hop-by-hop ID to a random value: 0x4b44b41d */ + msgdata->msg_hbhid = 0x4b44b41d; + /* Set the end-to-end ID to a random value: 0xe2ee2e1d */ + msgdata->msg_eteid = 0xe2ee2e1d; + } + + /* Test the msg_bufferize function */ + { + + CHECK( 0, fd_msg_bufferize( msg, &buf, NULL ) ); + + /* Test the first bytes */ + CHECK( 0x01, buf[0] ); /* Version */ + CHECK( 0x00, buf[1] ); /* Length: 344 = 0x000158 */ + CHECK( 0x01, buf[2] ); + CHECK( 0x58, buf[3] ); + CHECK( 0x80, buf[4] ); /* flags: only "R" is set. */ + CHECK( 0x01, buf[5] ); /* Command code: 73573 = 0x011F65 */ + CHECK( 0x1F, buf[6] ); + CHECK( 0x65, buf[7] ); + CHECK( 0x00, buf[8] ); /* App ID: 73566 = 0x00011F5E */ + CHECK( 0x01, buf[9] ); + CHECK( 0x1F, buf[10] ); + CHECK( 0x5E, buf[11] ); + CHECK( 0x4b, buf[12] ); /* hop-by-hop id: 0x4b44b41d */ + CHECK( 0x44, buf[13] ); + CHECK( 0xb4, buf[14] ); + CHECK( 0x1d, buf[15] ); + CHECK( 0xe2, buf[16] ); /* end-to-end id: 0xe2ee2e1d */ + CHECK( 0xee, buf[17] ); + CHECK( 0x2e, buf[18] ); + CHECK( 0x1d, buf[19] ); + + CHECK( 0x00, buf[20] ); /* First AVP (AVP Test - no vendor - f32) begin: code 73567 = 0x00011F5F */ + CHECK( 0x01, buf[21] ); + CHECK( 0x1F, buf[22] ); + CHECK( 0x5F, buf[23] ); + CHECK( 0x00, buf[24] ); /* flags: 0 */ + CHECK( 0x00, buf[25] ); /* length: 12 = 0x00000c */ + CHECK( 0x00, buf[26] ); + CHECK( 0x0C, buf[27] ); + CHECK( 0x40, buf[28] ); /* Value: 3.1415: sign = '+' => most significant bit = 0 */ + CHECK( 0x49, buf[29] ); /* 2 <= 3.1415 < 4 => exponent = 1 => biaised (on 8 bits) = (decimal) 128 = (binary) 100 0000 0 */ + CHECK( 0x0e, buf[30] ); /* significand = (decimal) 1.57075 = (binary) 1.100 1001 0000 1110 0101 0110 */ + CHECK( 0x56, buf[31] ); /* total => 0100 0000 0100 1001 0000 1110 0101 0110 = (hexa) 40 49 0e 56*/ + + /* The other AVPs will be tested by successful parsing... */ + } + + /* Now free the message, we keep only the buffer. */ + CHECK( 0, fd_msg_free( msg ) ); + + } + + /* Test the parsing of buffers and messages */ + { + unsigned char * buf_cpy = NULL; + struct msg * msg; + + #define CPYBUF() { \ + buf_cpy = malloc(344); \ + CHECK( buf_cpy ? 1 : 0, 1); \ + memcpy(buf_cpy, buf, 344); \ + } + + /* Test the msg_parse_buffer function */ + { + CPYBUF(); + CHECK( EBADMSG, fd_msg_parse_buffer( &buf_cpy, 340, &msg) ); + + CPYBUF(); + CHECK( 0, fd_msg_parse_buffer( &buf_cpy, 344, &msg) ); + #if 0 + fd_msg_dump_walk(0, msg); + #endif + + /* reinit the msg */ + CHECK( 0, fd_msg_free ( msg ) ); + + } + + /* Test the fd_msg_search_avp function */ + { + struct dict_object * avp_model; + struct avp * found; + struct avp_hdr * avpdata = NULL; + + /* Now find the ACR dictionary object */ + CHECK( 0, fd_dict_search ( fd_g_dict, DICT_AVP, AVP_BY_NAME, "AVP Test - no vendor - f32", &avp_model, ENOENT ) ); + + CPYBUF(); + CHECK( 0, fd_msg_parse_buffer( &buf_cpy, 344, &msg) ); + + /* Search this AVP instance in the msg */ + CHECK( 0, fd_msg_search_avp( msg, avp_model, &found ) ); + + /* Check the AVP value is 3.1415 */ + CHECK( 0, fd_msg_avp_hdr ( found, &avpdata ) ); + CHECK( 3.1415F, avpdata->avp_value->f32 ); + + /* reinit the msg */ + CHECK( 0, fd_msg_free ( msg ) ); + + } + + /* Test the msg_parse_dict function */ + { + /* Test with an unknown command code */ + { + CPYBUF(); + + /* Change the command-code */ + buf_cpy[5] = 0x11; + CHECK( 0, fd_msg_parse_buffer( &buf_cpy, 344, &msg) ); + CHECK( ENOTSUP, fd_msg_parse_dict( msg, fd_g_dict ) ); + + /* reset */ + CHECK( 0, fd_msg_free ( msg ) ); + } + + /* Test with an unknown Mandatory AVP */ + { + CPYBUF(); + + buf_cpy[20] = 0x11; /* New AVP code = 0x11011F5F, undefined */ + buf_cpy[24] = 0x40; /* Add the 'M' flag */ + + /* Check that we cannot support this message now */ + CHECK( 0, fd_msg_parse_buffer( &buf_cpy, 344, &msg) ); + CHECK( ENOTSUP, fd_msg_parse_dict( msg, fd_g_dict ) ); + + /* reset */ + CHECK( 0, fd_msg_free ( msg ) ); + } + + /* Test with an unknown optional AVP */ + { + CPYBUF(); + + buf_cpy[20] = 0x11; /* New AVP code = 0x11011F5F, undefined */ + + /* Check that we can support this message now */ + CHECK( 0, fd_msg_parse_buffer( &buf_cpy, 344, &msg) ); + CHECK( 0, fd_msg_parse_dict( msg, fd_g_dict ) ); + + #if 0 + fd_msg_dump_walk(0, msg); + #endif + + /* reset */ + CHECK( 0, fd_msg_free ( msg ) ); + } + + CHECK( 0, fd_msg_parse_buffer( &buf, 344, &msg) ); + CHECK( 0, fd_msg_parse_dict( msg, fd_g_dict ) ); + #if 0 + fd_msg_dump_walk(0, msg); + #endif + } + + /* Now test the msg_parse_rule function */ + { + struct dict_object * rule; + + CHECK( 0, fd_msg_parse_rules( msg, fd_g_dict, &rule ) ); + + /* Use the "AVP Test - rules" AVP to test the rules */ + { + struct avp * tavp = NULL; + struct avp * tempavp = NULL; + struct avp * childavp = NULL; + + ADD_AVP( msg, MSG_BRW_LAST_CHILD, tavp, 73565, "AVP Test - rules" ); + + /* Create a conforming message first */ + ADD_AVP( tavp, MSG_BRW_LAST_CHILD, childavp, 0, "AVP Test - no vendor - f32" ); + ADD_AVP( tavp, MSG_BRW_LAST_CHILD, childavp, 73565, "AVP Test - i64" ); + ADD_AVP( tavp, MSG_BRW_LAST_CHILD, childavp, 73565, "AVP Test - enumi32" ); + ADD_AVP( tavp, MSG_BRW_LAST_CHILD, childavp, 73565, "AVP Test - os" ); + ADD_AVP( tavp, MSG_BRW_LAST_CHILD, childavp, 73565, "AVP Test - os" ); + ADD_AVP( tavp, MSG_BRW_LAST_CHILD, childavp, 73565, "AVP Test - grouped" ); + + /* Check the message is still conform */ + CHECK( 0, fd_msg_parse_rules( msg, fd_g_dict, &rule ) ); + + /* The first avp is optional in fixed position, so remove it and check the message is still OK */ + CHECK( 0, fd_msg_browse ( tavp, MSG_BRW_FIRST_CHILD, &childavp, NULL) ); + CHECK( 0, fd_msg_free ( childavp ) ); + CHECK( 0, fd_msg_parse_rules( msg, fd_g_dict, &rule ) ); + ADD_AVP( tavp, MSG_BRW_FIRST_CHILD, childavp, 0, "AVP Test - no vendor - f32" ); + + + /* Now break some rules and check it is detected */ + #define CHECK_CONFLICT( _msg, _ruleavp, _avpvendor ) { \ + struct dict_object * _rule; \ + CHECK( EBADMSG, fd_msg_parse_rules( _msg, fd_g_dict, &_rule ) ); \ + if ((_ruleavp) == NULL) { \ + CHECK( NULL, _rule); \ + } else { \ + struct dict_rule_data _ruledata; \ + struct dict_object * _avp; \ + struct dict_avp_request _req = { (_avpvendor), 0, (_ruleavp) }; \ + CHECK( 0, fd_dict_search( fd_g_dict, DICT_AVP, AVP_BY_NAME_AND_VENDOR, &_req, &_avp, ENOENT)); \ + CHECK( 0, fd_dict_getval( _rule, &_ruledata ) ); \ + CHECK( _avp, _ruledata.rule_avp ); \ + } \ + } + + { + /* Test the FIXED_HEAD rules positions: add another AVP before the third */ + CHECK( 0, fd_msg_browse ( tavp, MSG_BRW_FIRST_CHILD, &tempavp, NULL) ); /* tempavp is the novendor avp */ + CHECK( 0, fd_msg_browse ( tempavp, MSG_BRW_NEXT, &tempavp, NULL) ); /* tempavp is the i64 avp */ + ADD_AVP( tempavp, MSG_BRW_NEXT, childavp, 73565, "AVP Test - os" ); + + CHECK_CONFLICT( msg, "AVP Test - enumi32", 73565 ); + + /* Now remove this AVP */ + CHECK( 0, fd_msg_free ( childavp ) ); + } + { + /* Remove the third AVP, same rule must conflict */ + CHECK( 0, fd_msg_browse ( tempavp, MSG_BRW_NEXT, &childavp, NULL) ); /* childavp is the enumi32 avp */ + CHECK( 0, fd_msg_free ( childavp ) ); + + CHECK_CONFLICT( msg, "AVP Test - enumi32", 73565 ); + + /* Add the AVP back */ + ADD_AVP( tempavp, MSG_BRW_NEXT, childavp, 73565, "AVP Test - enumi32" ); + } + + { + /* Test the minimum value in the REQUIRED rule: delete one of the os AVPs */ + CHECK( 0, fd_msg_browse ( childavp, MSG_BRW_NEXT, &tempavp, NULL) ); /* tempavp is the os avp */ + CHECK( 0, fd_msg_free ( tempavp ) ); + + CHECK_CONFLICT( msg, "AVP Test - os", 73565 ); /* The rule requires at least 2 AVP, we have only 1 */ + + /* Now add this AVP */ + ADD_AVP( childavp, MSG_BRW_NEXT, tempavp, 73565, "AVP Test - os" ); + } + { + /* Test the maximum value in the REQUIRED rule: add more of the os AVPs */ + ADD_AVP( childavp, MSG_BRW_NEXT, tempavp, 73565, "AVP Test - os" ); + ADD_AVP( childavp, MSG_BRW_NEXT, tempavp, 73565, "AVP Test - os" ); + + CHECK_CONFLICT( msg, "AVP Test - os", 73565 ); /* The rule requires at most 3 AVP, we have 4 */ + + /* Now delete these AVP */ + CHECK( 0, fd_msg_free ( tempavp ) ); + CHECK( 0, fd_msg_browse ( childavp, MSG_BRW_NEXT, &tempavp, NULL) ); + CHECK( 0, fd_msg_free ( tempavp ) ); + } + + { + /* Test the maximum value in the OPTIONAL rule: add 2 enumos AVPs */ + ADD_AVP( childavp, MSG_BRW_NEXT, tempavp, 73565, "AVP Test - enumos" ); + + /* The message is still conform */ + CHECK( 0, fd_msg_parse_rules( msg, fd_g_dict, &rule ) ); + + /* Now break the rule */ + ADD_AVP( childavp, MSG_BRW_NEXT, tempavp, 73565, "AVP Test - enumos" ); + + CHECK_CONFLICT( msg, "AVP Test - enumos", 73565 ); /* The rule requires at most 3 AVP, we have 4 */ + + /* Now delete this AVP */ + CHECK( 0, fd_msg_free ( tempavp ) ); + } + + { + /* Test the RULE_FIXED_TAIL rules positions: add another AVP at the end */ + ADD_AVP( tavp, MSG_BRW_LAST_CHILD, childavp, 73565, "AVP Test - os" ); + + CHECK_CONFLICT( msg, "AVP Test - grouped", 73565 ); + + /* Now remove this AVP */ + CHECK( 0, fd_msg_free ( childavp ) ); + } + } + } + } + + /* Test the msg_avp_value_interpret and msg_avp_value_encode functions. use the Address type and Host-IP-Address AVPs */ + { + struct dict_object * cer_model = NULL; + struct msg * cer = NULL; + + struct dict_object * hia_model = NULL; + struct avp *avp4, *avp6; + #define TEST_IP4 "192.168.100.101" + char buf4[INET_ADDRSTRLEN]; + #define TEST_IP6 "1111:2222:3333:4444:1234:5678:9abc:def0" + char buf6[INET6_ADDRSTRLEN]; + + struct sockaddr_storage ss; + struct sockaddr_in sin, *psin; + struct sockaddr_in6 sin6, *psin6; + + /* Find the CER dictionary object */ + CHECK( 0, fd_dict_search ( fd_g_dict, DICT_COMMAND, CMD_BY_NAME, "Capabilities-Exchange-Request", &cer_model, ENOENT ) ); + + /* Now find the Host-IP-Address dictionary object */ + CHECK( 0, fd_dict_search ( fd_g_dict, DICT_AVP, AVP_BY_NAME, "Host-IP-Address", &hia_model, ENOENT ) ); + + /* Create the msg instance */ + CHECK( 0, fd_msg_new ( cer_model, 0, &cer ) ); + + /* Create the avp instances */ + CHECK( 0, fd_msg_avp_new ( hia_model, 0, &avp4 ) ); + CHECK( 0, fd_msg_avp_new ( hia_model, 0, &avp6 ) ); + + /* Set the value of the IP avp */ + sin.sin_family = AF_INET; + CHECK( 1, inet_pton( AF_INET, TEST_IP4, &sin.sin_addr.s_addr ) ); + CHECK( 0, fd_msg_avp_value_encode ( &sin, avp4 ) ); + + /* Set the value of the IP6 avp */ + sin6.sin6_family = AF_INET6; + CHECK( 1, inet_pton( AF_INET6, TEST_IP6, &sin6.sin6_addr.s6_addr ) ); + CHECK( 0, fd_msg_avp_value_encode ( &sin6, avp6 ) ); + + /* Add these AVPs in the message */ + CHECK( 0, fd_msg_avp_add( cer, MSG_BRW_LAST_CHILD, avp4) ); + CHECK( 0, fd_msg_avp_add( cer, MSG_BRW_LAST_CHILD, avp6) ); + + /* Create the buffer for this message */ + CHECK( 0, fd_msg_bufferize( cer, &buf, NULL ) ); + + /* Now free the message, we keep only the buffer. */ + CHECK( 0, fd_msg_free( cer ) ); + + /* Check the content of the buffer is correct (skip command header) */ + CHECK( 0x00, buf[20] ); /* First AVP (IP4) begins: code 257 = 0x00000101 */ + CHECK( 0x00, buf[21] ); + CHECK( 0x01, buf[22] ); + CHECK( 0x01, buf[23] ); + CHECK( 0x40, buf[24] ); /* flags: M */ + CHECK( 0x00, buf[25] ); /* length: 8+6 = 0x00000e */ + CHECK( 0x00, buf[26] ); + CHECK( 0x0E, buf[27] ); + CHECK( 0x00, buf[28] ); /* Value: AddressType 1 */ + CHECK( 0x01, buf[29] ); + CHECK( 192, buf[30] ); /* 192.168.100.101 */ + CHECK( 168, buf[31] ); + CHECK( 100, buf[32] ); + CHECK( 101, buf[33] ); + + CHECK( 0x00, buf[34] ); /* Padding */ + CHECK( 0x00, buf[35] ); + + CHECK( 0x00, buf[36] ); /* Second AVP (IP6) begins: code 257 = 0x00000101 */ + CHECK( 0x00, buf[37] ); + CHECK( 0x01, buf[38] ); + CHECK( 0x01, buf[39] ); + CHECK( 0x40, buf[40] ); /* flags: M */ + CHECK( 0x00, buf[41] ); /* length: 8+18 = 0x00001a */ + CHECK( 0x00, buf[42] ); + CHECK( 0x1A, buf[43] ); + CHECK( 0x00, buf[44] ); /* Value: AddressType 2 */ + CHECK( 0x02, buf[45] ); + CHECK( 0x11, buf[46] ); /* 1111:2222:3333:4444:1234:5678:9abc:def0 */ + CHECK( 0x11, buf[47] ); + CHECK( 0x22, buf[48] ); + CHECK( 0x22, buf[49] ); + CHECK( 0x33, buf[50] ); + CHECK( 0x33, buf[51] ); + CHECK( 0x44, buf[52] ); + CHECK( 0x44, buf[53] ); + CHECK( 0x12, buf[54] ); + CHECK( 0x34, buf[55] ); + CHECK( 0x56, buf[56] ); + CHECK( 0x78, buf[57] ); + CHECK( 0x9a, buf[58] ); + CHECK( 0xbc, buf[59] ); + CHECK( 0xde, buf[60] ); + CHECK( 0xf0, buf[61] ); + + /* Ok, now let's recreate the message */ + CHECK( 0, fd_msg_parse_buffer( &buf, 64, &cer) ); + CHECK( 0, fd_msg_parse_dict( cer, fd_g_dict ) ); + + /* Get the pointers to the first and last AVP */ + CHECK( 0, fd_msg_browse( cer, MSG_BRW_FIRST_CHILD, &avp4, NULL) ); + CHECK( 0, fd_msg_browse( cer, MSG_BRW_LAST_CHILD, &avp6, NULL) ); + + /* Try and interpret the data in the AVPs */ + CHECK( 0, fd_msg_avp_value_interpret ( avp4, &ss ) ); + psin = (struct sockaddr_in *)&ss; + CHECK( AF_INET, psin->sin_family ); + CHECK( 0, (inet_ntop( AF_INET, &psin->sin_addr.s_addr, buf4, sizeof(buf4) ) == NULL) ? errno : 0 ); + CHECK( 0, strcmp( buf4, TEST_IP4 ) ); + + CHECK( 0, fd_msg_avp_value_interpret ( avp6, &ss ) ); + psin6 = (struct sockaddr_in6 *)&ss; + CHECK( AF_INET6, psin6->sin6_family ); + CHECK( 0, (inet_ntop( AF_INET6, &psin6->sin6_addr.s6_addr, buf6, sizeof(buf6) ) == NULL) ? errno : 0 ); + CHECK( 0, strcasecmp( buf6, TEST_IP6 ) ); + + /* Ok, it's done */ + CHECK( 0, fd_msg_free( cer ) ); + } + + /* Check proper encoding / decoding for all basic types of AVP */ + { + { + struct dict_avp_data avp_data = { 91001, 0, "AVP Test 2 - os", 0, 0, AVP_TYPE_OCTETSTRING }; + CHECK( 0, fd_dict_new ( fd_g_dict, DICT_AVP, &avp_data , NULL, NULL ) ); + } + { + struct dict_avp_data avp_data = { 91002, 0, "AVP Test 2 - i32", 0, 0, AVP_TYPE_INTEGER32 }; + CHECK( 0, fd_dict_new ( fd_g_dict, DICT_AVP, &avp_data , NULL, NULL ) ); + } + { + struct dict_avp_data avp_data = { 91003, 0, "AVP Test 2 - i64", 0, 0, AVP_TYPE_INTEGER64 }; + CHECK( 0, fd_dict_new ( fd_g_dict, DICT_AVP, &avp_data , NULL, NULL ) ); + } + { + struct dict_avp_data avp_data = { 91004, 0, "AVP Test 2 - u32", 0, 0, AVP_TYPE_UNSIGNED32 }; + CHECK( 0, fd_dict_new ( fd_g_dict, DICT_AVP, &avp_data , NULL, NULL ) ); + } + { + struct dict_avp_data avp_data = { 91005, 0, "AVP Test 2 - u64", 0, 0, AVP_TYPE_UNSIGNED64 }; + CHECK( 0, fd_dict_new ( fd_g_dict, DICT_AVP, &avp_data , NULL, NULL ) ); + } + { + struct dict_avp_data avp_data = { 91006, 0, "AVP Test 2 - f32", 0, 0, AVP_TYPE_FLOAT32 }; + CHECK( 0, fd_dict_new ( fd_g_dict, DICT_AVP, &avp_data , NULL, NULL ) ); + } + { + struct dict_avp_data avp_data = { 91007, 0, "AVP Test 2 - f64", 0, 0, AVP_TYPE_FLOAT64 }; + CHECK( 0, fd_dict_new ( fd_g_dict, DICT_AVP, &avp_data , NULL, NULL ) ); + } + + { + struct dict_object * cmd_model = NULL; + struct msg * msg = NULL; + struct dict_object * avp_model = NULL; + struct avp * avp = NULL; + union avp_value value; + struct avp * avpi = NULL; + struct avp * avpch = NULL; + struct avp_hdr * avpdata = NULL; + struct msg_hdr * msgdata = NULL; + + CHECK( 0, fd_dict_search ( fd_g_dict, DICT_COMMAND, CMD_BY_NAME, "Test-Command-Request", &cmd_model, ENOENT ) ); + + /* Create a message */ + CHECK( 0, fd_msg_new ( cmd_model, 0, &msg ) ); + CHECK( 0, fd_msg_hdr ( msg, &msgdata ) ); + + ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 0, "AVP Test 2 - os" ); + value.os.data = (unsigned char *) "waaad"; + value.os.len = 6; + CHECK( 0, fd_msg_avp_setvalue ( avpi, &value ) ); + + ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 0, "AVP Test 2 - i32" ); + value.i32 = 0x123456; + CHECK( 0, fd_msg_avp_setvalue ( avpi, &value ) ); + + ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 0, "AVP Test 2 - i32" ); + value.i32 = -0x123456; + CHECK( 0, fd_msg_avp_setvalue ( avpi, &value ) ); + + ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 0, "AVP Test 2 - i64" ); + value.i64 = 0x11223344556677LL; + CHECK( 0, fd_msg_avp_setvalue ( avpi, &value ) ); + + ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 0, "AVP Test 2 - i64" ); + value.i64 = -0x11223344556677LL; + CHECK( 0, fd_msg_avp_setvalue ( avpi, &value ) ); + + ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 0, "AVP Test 2 - u32" ); + value.u32 = 0xFEDCBA98; + CHECK( 0, fd_msg_avp_setvalue ( avpi, &value ) ); + + ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 0, "AVP Test 2 - u64" ); + value.u64 = 0x123456789abcdef0LL; + CHECK( 0, fd_msg_avp_setvalue ( avpi, &value ) ); + + ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 0, "AVP Test 2 - f32" ); + value.f32 = 2097153.0F; + CHECK( 0, fd_msg_avp_setvalue ( avpi, &value ) ); + + ADD_AVP( msg, MSG_BRW_LAST_CHILD, avpi, 0, "AVP Test 2 - f64" ); + value.f64 = -1099511627777LL; + CHECK( 0, fd_msg_avp_setvalue ( avpi, &value ) ); + + /* Ok now bufferize */ + CHECK( 0, fd_msg_bufferize( msg, &buf, NULL ) ); + + /* Test the first bytes */ + CHECK( 0x01, buf[0] ); /* Version */ + CHECK( 0x00, buf[1] ); /* Length: 148 = 0x000094 */ + CHECK( 0x00, buf[2] ); + CHECK( 0x94, buf[3] ); + CHECK( 0x80, buf[4] ); /* flags: only "R" is set. */ + CHECK( 0x01, buf[5] ); /* Command code: 73573 = 0x011F65 */ + CHECK( 0x1F, buf[6] ); + CHECK( 0x65, buf[7] ); + CHECK( 0x00, buf[8] ); /* App ID */ + CHECK( 0x01, buf[9] ); + CHECK( 0x1F, buf[10] ); + CHECK( 0x5E, buf[11] ); + CHECK( 0x00, buf[12] ); /* hop-by-hop id */ + CHECK( 0x00, buf[13] ); + CHECK( 0x00, buf[14] ); + CHECK( 0x00, buf[15] ); + CHECK( 0x00, buf[16] ); /* end-to-end id */ + CHECK( 0x00, buf[17] ); + CHECK( 0x00, buf[18] ); + CHECK( 0x00, buf[19] ); + + CHECK( 0x00, buf[20] ); /* First AVP (AVP Test 2 - os) begin: code 91001 = 0x00016379 */ + CHECK( 0x01, buf[21] ); + CHECK( 0x63, buf[22] ); + CHECK( 0x79, buf[23] ); + CHECK( 0x00, buf[24] ); /* flags: 0 */ + CHECK( 0x00, buf[25] ); /* length: 14 = 0x00000e */ + CHECK( 0x00, buf[26] ); + CHECK( 0x0e, buf[27] ); + + CHECK( 0x77, buf[28] ); /* "waaad\0" + padding */ + CHECK( 0x61, buf[29] ); + CHECK( 0x61, buf[30] ); + CHECK( 0x61, buf[31] ); + CHECK( 0x64, buf[32] ); + CHECK( 0x00, buf[33] ); + CHECK( 0x00, buf[34] ); + CHECK( 0x00, buf[35] ); + + /* 36 ~ 43 : 2nd AVP header (size at last octet) */ + CHECK( 0x0c, buf[43] ); + CHECK( 0x00, buf[44] ); /* 0x123456 stored in integer32 in network byte order */ + CHECK( 0x12, buf[45] ); + CHECK( 0x34, buf[46] ); + CHECK( 0x56, buf[47] ); + + /* 48 ~ 55 : next AVP header */ + CHECK( 0xff, buf[56] ); /* -0x123456 stored in integer32 in network byte order. */ + CHECK( 0xed, buf[57] ); /* We assume that two's complement is the correct representation, although it's not clearly specified. */ + CHECK( 0xcb, buf[58] ); /* 00 12 34 56 inversed => FF ED CB A9 */ + CHECK( 0xaa, buf[59] ); /* then "+1" => FF ED CB AA */ + + /* 60 ~ 67 : next header */ + CHECK( 0x10, buf[67] ); /* (the size) */ + CHECK( 0x00, buf[68] ); /* 0x11223344556677 in network byte order */ + CHECK( 0x11, buf[69] ); + CHECK( 0x22, buf[70] ); + CHECK( 0x33, buf[71] ); + CHECK( 0x44, buf[72] ); + CHECK( 0x55, buf[73] ); + CHECK( 0x66, buf[74] ); + CHECK( 0x77, buf[75] ); + + /* 76 ~ 83 : next header */ + CHECK( 0xFF, buf[84] ); /* - 0x11223344556677 (in two's complement) */ + CHECK( 0xEE, buf[85] ); /* gives FF EE DD CC BB AA 99 89 */ + CHECK( 0xDD, buf[86] ); + CHECK( 0xCC, buf[87] ); + CHECK( 0xBB, buf[88] ); + CHECK( 0xAA, buf[89] ); + CHECK( 0x99, buf[90] ); + CHECK( 0x89, buf[91] ); + + /* 92 ~ 99 : next header */ + CHECK( 0x0c, buf[99] ); /* (the size) */ + CHECK( 0xFE, buf[100]); /* 0xFEDCBA98 in network byte order */ + CHECK( 0xDC, buf[101]); + CHECK( 0xBA, buf[102]); + CHECK( 0x98, buf[103]); + + /* 104 ~ 111 : next header */ + CHECK( 0x10, buf[111] ); /* (the size) */ + CHECK( 0x12, buf[112]); /* 0x123456789abcdef0LL in network byte order */ + CHECK( 0x34, buf[113]); + CHECK( 0x56, buf[114]); + CHECK( 0x78, buf[115]); + CHECK( 0x9a, buf[116]); + CHECK( 0xbc, buf[117]); + CHECK( 0xde, buf[118]); + CHECK( 0xf0, buf[119]); + + /* 120 ~ 127 : next header */ + CHECK( 0x0c, buf[127] ); /* (the size) */ + CHECK( 0x4a, buf[128]); /* http://en.wikipedia.org/wiki/IEEE_754-1985 to get descvription of the format */ + CHECK( 0x00, buf[129]); /* v = 2097153 = 2^21 + 2 ^ 0; sign : "+", 2^21 <= v < 2^22 => exponent = 21; biaised on 8 bits => 21 + 127 => 100 1010 0 */ + CHECK( 0x00, buf[130]); /* v = (+1) * (1 ^ 21) * ( 1 + 2^-21 ) => significand 000 0000 0000 0000 0000 0100 */ + CHECK( 0x04, buf[131]); /* result: 4a 00 00 04 */ + + /* 132 ~ 139 : next header */ + CHECK( 0x10, buf[139] ); /* (the size) */ + CHECK( 0xc2, buf[140]); /* -1099511627777L ( 2^40 + 1 ) in network byte order */ + CHECK( 0x70, buf[141]); /* sign: - => most significant bit = 1 */ + CHECK( 0x00, buf[142]); /* 2^40 <= v < 2^41 => biaised exponent on 11 bits: 1023 + 40: 100 0010 0111 */ + CHECK( 0x00, buf[143]); /* significand: 1 + 2^-40 => 0000 0000 0000 0000 0000 0000 0000 0000 0000 0001 0000 0000 0000 */ + CHECK( 0x00, buf[144]); /* result: c2 70 00 00 00 00 10 00 */ + CHECK( 0x00, buf[145]); + CHECK( 0x10, buf[146]); + CHECK( 0x00, buf[147]); + + + + /* Okay, now delete the message and parse the buffer, then check we obtain the same values back */ + #if 0 + fd_msg_dump_walk(0, msg); + #endif + CHECK( 0, fd_msg_free( msg ) ); + + CHECK( 0, fd_msg_parse_buffer( &buf, 148, &msg) ); + CHECK( 0, fd_msg_parse_dict( msg, fd_g_dict ) ); + #if 0 + fd_msg_dump_walk(0, msg); + #endif + + CHECK( 0, fd_msg_browse ( msg, MSG_BRW_FIRST_CHILD, &avp, NULL) ); + CHECK( 0, fd_msg_avp_hdr ( avp, &avpdata ) ); + CHECK( 6, avpdata->avp_value->os.len ); + CHECK( 'w', (char)(avpdata->avp_value->os.data[0]) ); + CHECK( 'a', (char)(avpdata->avp_value->os.data[1]) ); + CHECK( 'd', (char)(avpdata->avp_value->os.data[4]) ); + CHECK( '\0', (char)(avpdata->avp_value->os.data[5]) ); + + CHECK( 0, fd_msg_browse ( avp, MSG_BRW_NEXT, &avp, NULL) ); + CHECK( 0, fd_msg_avp_hdr ( avp, &avpdata ) ); + CHECK( 0x123456, avpdata->avp_value->i32 ); + + CHECK( 0, fd_msg_browse ( avp, MSG_BRW_NEXT, &avp, NULL) ); + CHECK( 0, fd_msg_avp_hdr ( avp, &avpdata ) ); + CHECK( -0x123456, avpdata->avp_value->i32 ); + + CHECK( 0, fd_msg_browse ( avp, MSG_BRW_NEXT, &avp, NULL) ); + CHECK( 0, fd_msg_avp_hdr ( avp, &avpdata ) ); + CHECK( 0x11223344556677LL, avpdata->avp_value->i64 ); + + CHECK( 0, fd_msg_browse ( avp, MSG_BRW_NEXT, &avp, NULL) ); + CHECK( 0, fd_msg_avp_hdr ( avp, &avpdata ) ); + CHECK( -0x11223344556677LL, avpdata->avp_value->i64 ); + + CHECK( 0, fd_msg_browse ( avp, MSG_BRW_NEXT, &avp, NULL) ); + CHECK( 0, fd_msg_avp_hdr ( avp, &avpdata ) ); + CHECK( 0xFEDCBA98, avpdata->avp_value->u32 ); + + CHECK( 0, fd_msg_browse ( avp, MSG_BRW_NEXT, &avp, NULL) ); + CHECK( 0, fd_msg_avp_hdr ( avp, &avpdata ) ); + CHECK( 0x123456789abcdef0LL, avpdata->avp_value->u64 ); + + CHECK( 0, fd_msg_browse ( avp, MSG_BRW_NEXT, &avp, NULL) ); + CHECK( 0, fd_msg_avp_hdr ( avp, &avpdata ) ); + CHECK( 2097153.0F, avpdata->avp_value->f32 ); + + CHECK( 0, fd_msg_browse ( avp, MSG_BRW_NEXT, &avp, NULL) ); + CHECK( 0, fd_msg_avp_hdr ( avp, &avpdata ) ); + CHECK( -1099511627777LL, avpdata->avp_value->f64 ); + + CHECK( 0, fd_msg_free( msg ) ); + } + } + + + /* That's all for the tests yet */ + PASSTEST(); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/freediameter/tests/testmq.c Fri Aug 28 19:14:42 2009 +0900 @@ -0,0 +1,423 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis <sdecugis@nict.go.jp> * +* * +* Copyright (c) 2009, WIDE Project and NICT * +* All rights reserved. * +* * +* Redistribution and use of this software in source and binary forms, with or without modification, are * +* permitted provided that the following conditions are met: * +* * +* * Redistributions of source code must retain the above * +* copyright notice, this list of conditions and the * +* following disclaimer. * +* * +* * 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. * +* * +* * Neither the name of the WIDE Project or NICT nor the * +* names of its contributors may be used to endorse or * +* promote products derived from this software without * +* specific prior written permission of WIDE Project and * +* NICT. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. * +*********************************************************************************************************/ + +#include "tests.h" + +/* Structure for testing threashold function */ +static struct thrh_test { + struct mqueue * queue; /* pointer to the queue */ + int h_calls; /* number of calls of h_cb */ + int l_calls; /* number of calls of l_cb */ +} thrh_td; + +/* Callbacks for threasholds test */ +void thrh_cb_h(struct mqueue *queue, void **data) +{ + if (thrh_td.h_calls == thrh_td.l_calls) { + CHECK( NULL, *data ); + *data = &thrh_td; + } else { + CHECK( *data, &thrh_td ); + } + CHECK( queue, thrh_td.queue ); + + /* Update the count */ + thrh_td.h_calls ++; +} +void thrh_cb_l(struct mqueue *queue, void **data) +{ + CHECK( 1, data ? 1 : 0 ); + CHECK( *data, &thrh_td ); + + /* Check the queue parameter is correct */ + CHECK( queue, thrh_td.queue ); + + /* Update the count */ + thrh_td.l_calls ++; + /* Cleanup the data ptr if needed */ + if (thrh_td.l_calls == thrh_td.h_calls) + *data = NULL; + /* done */ +} + + +/* Structure that is passed to the test function */ +struct test_data { + struct mqueue * queue; /* pointer to the queue */ + pthread_barrier_t * bar; /* if not NULL, barrier to synchronize before getting messages */ + struct timespec * ts; /* if not NULL, use a timedget instead of a get */ + int nbr; /* number of messages to retrieve from the queue */ +}; + +/* The test function, to be threaded */ +static void * test_fct(void * data) +{ + int ret = 0, i; + struct msg * msg = NULL; + struct test_data * td = (struct test_data *) data; + + if (td->bar != NULL) { + ret = pthread_barrier_wait(td->bar); + if (ret != PTHREAD_BARRIER_SERIAL_THREAD) { + CHECK( 0, ret); + } else { + CHECK( PTHREAD_BARRIER_SERIAL_THREAD, ret); /* just for the traces */ + } + } + + for (i=0; i< td->nbr; i++) { + if (td->ts != NULL) { + CHECK( 0, fd_mq_timedget(td->queue, &msg, td->ts) ); + } else { + CHECK( 0, fd_mq_get(td->queue, &msg) ); + } + } + + return NULL; +} + + +/* Main test routine */ +int main(int argc, char *argv[]) +{ + struct timespec ts; + + struct msg * msg1 = NULL; + struct msg * msg2 = NULL; + struct msg * msg3 = NULL; + + /* First, initialize the daemon modules */ + INIT_FD(); + + /* Prolog: create the messages */ + { + struct dict_object * acr_model = NULL; + struct dict_object * cer_model = NULL; + struct dict_object * dwr_model = NULL; + + CHECK( 0, fd_dict_search ( fd_g_dict, DICT_COMMAND, CMD_BY_NAME, "Accounting-Request", &acr_model, ENOENT ) ); + CHECK( 0, fd_dict_search ( fd_g_dict, DICT_COMMAND, CMD_BY_NAME, "Capabilities-Exchange-Request", &cer_model, ENOENT ) ); + CHECK( 0, fd_dict_search ( fd_g_dict, DICT_COMMAND, CMD_BY_NAME, "Device-Watchdog-Request", &dwr_model, ENOENT ) ); + CHECK( 0, fd_msg_new ( acr_model, 0, &msg1 ) ); + CHECK( 0, fd_msg_new ( cer_model, 0, &msg2 ) ); + CHECK( 0, fd_msg_new ( dwr_model, 0, &msg3 ) ); + } + + /* Basic operation */ + { + struct mqueue * queue = NULL; + int count; + struct msg * msg = NULL; + + /* Create the queue */ + CHECK( 0, fd_mq_new(&queue) ); + + /* Check the count is 0 */ + CHECK( 0, fd_mq_length(queue, &count) ); + CHECK( 0, count); + + /* Now enqueue */ + msg = msg1; + CHECK( 0, fd_mq_post(queue, &msg) ); + msg = msg2; + CHECK( 0, fd_mq_post(queue, &msg) ); + msg = msg3; + CHECK( 0, fd_mq_post(queue, &msg) ); + + /* Check the count is 3 */ + CHECK( 0, fd_mq_length(queue, &count) ); + CHECK( 3, count); + + /* Retrieve the first message using fd_mq_get */ + CHECK( 0, fd_mq_get(queue, &msg) ); + CHECK( msg1, msg); + CHECK( 0, fd_mq_length(queue, &count) ); + CHECK( 2, count); + + /* Retrieve the second message using fd_mq_timedget */ + CHECK(0, clock_gettime(CLOCK_REALTIME, &ts)); + ts.tv_sec += 1; /* Set the timeout to 1 second */ + CHECK( 0, fd_mq_timedget(queue, &msg, &ts) ); + CHECK( msg2, msg); + CHECK( 0, fd_mq_length(queue, &count) ); + CHECK( 1, count); + + /* Retrieve the third message using meq_tryget */ + CHECK( 0, fd_mq_tryget(queue, &msg) ); + CHECK( msg3, msg); + CHECK( 0, fd_mq_length(queue, &count) ); + CHECK( 0, count); + + /* Check that another meq_tryget does not block */ + CHECK( EWOULDBLOCK, fd_mq_tryget(queue, &msg) ); + CHECK( 0, fd_mq_length(queue, &count) ); + CHECK( 0, count); + + /* We're done for basic tests */ + CHECK( 0, fd_mq_del(&queue) ); + } + + /* Test robustness, ensure no messages are lost */ + { +#define NBR_MSG 200 +#define NBR_THREADS 60 + struct mqueue *queue = NULL; + pthread_barrier_t bar; + struct test_data td_1; + struct test_data td_2; + struct msg *msgs[NBR_MSG * NBR_THREADS * 2], *msg; + pthread_t thr [NBR_THREADS * 2]; + struct dict_object *dwr_model = NULL; + int count; + int i; + + /* Create the queue */ + CHECK( 0, fd_mq_new(&queue) ); + + /* Create the barrier */ + CHECK( 0, pthread_barrier_init(&bar, NULL, NBR_THREADS * 2 + 1) ); + + /* Initialize the ts */ + CHECK(0, clock_gettime(CLOCK_REALTIME, &ts)); + ts.tv_sec += 2; /* Set the timeout to 2 second */ + + /* Create the messages */ + CHECK( 0, fd_dict_search ( fd_g_dict, DICT_COMMAND, CMD_BY_NAME, "Device-Watchdog-Request", &dwr_model, ENOENT ) ); + for (i = 0; i < NBR_MSG * NBR_THREADS * 2; i++) { + CHECK( 0, fd_msg_new ( dwr_model, 0, &msgs[i] ) ); + } + + /* Initialize the test data structures */ + td_1.queue = queue; + td_1.bar = &bar; + td_1.ts = &ts; + td_1.nbr = NBR_MSG; + td_2.queue = queue; + td_2.bar = &bar; + td_2.ts = NULL; + td_2.nbr = NBR_MSG; + + /* Create the threads */ + for (i=0; i < NBR_THREADS * 2; i++) { + CHECK( 0, pthread_create( &thr[i], NULL, test_fct, (i & 1) ? &td_1 : &td_2 ) ); + } + + /* Synchronize everyone */ + { + int ret = pthread_barrier_wait(&bar); + if (ret != PTHREAD_BARRIER_SERIAL_THREAD) { + CHECK( 0, ret); + } else { + CHECK( PTHREAD_BARRIER_SERIAL_THREAD, ret); /* for trace only */ + } + } + + /* Now post all the messages */ + for (i=0; i < NBR_MSG * NBR_THREADS * 2; i++) { + msg = msgs[i]; + CHECK( 0, fd_mq_post(queue, &msg) ); + } + + /* Join all threads. This blocks if messages are lost... */ + for (i=0; i < NBR_THREADS * 2; i++) { + CHECK( 0, pthread_join( thr[i], NULL ) ); + } + + /* Check the count of the queue is back to 0 */ + CHECK( 0, fd_mq_length(queue, &count) ); + CHECK( 0, count); + + /* Destroy this queue and the messages */ + CHECK( 0, fd_mq_del(&queue) ); + for (i=0; i < NBR_MSG * NBR_THREADS * 2; i++) { + CHECK( 0, fd_msg_free( msgs[i] ) ); + } + } + + /* Test thread cancelation */ + { + struct mqueue *queue = NULL; + pthread_barrier_t bar; + struct test_data td; + pthread_t th; + + /* Create the queue */ + CHECK( 0, fd_mq_new(&queue) ); + + /* Create the barrier */ + CHECK( 0, pthread_barrier_init(&bar, NULL, 2) ); + + /* Initialize the ts */ + CHECK(0, clock_gettime(CLOCK_REALTIME, &ts)); + ts.tv_sec += 2; /* Set the timeout to 2 second */ + + /* Initialize the test data structures */ + td.queue = queue; + td.bar = &bar; + td.ts = &ts; + td.nbr = 1; + + /* Create the thread */ + CHECK( 0, pthread_create( &th, NULL, test_fct, &td ) ); + + /* Wait for the thread to be running */ + { + int ret = pthread_barrier_wait(&bar); + if (ret != PTHREAD_BARRIER_SERIAL_THREAD) { + CHECK( 0, ret); + } else { + CHECK( PTHREAD_BARRIER_SERIAL_THREAD, ret ); + } + } + + /* Now cancel the thread */ + CHECK( 0, pthread_cancel( th ) ); + + /* Join it */ + CHECK( 0, pthread_join( th, NULL ) ); + + /* Do the same with the other function */ + td.ts = NULL; + + /* Create the thread */ + CHECK( 0, pthread_create( &th, NULL, test_fct, &td ) ); + + /* Wait for the thread to be running */ + { + int ret = pthread_barrier_wait(&bar); + if (ret != PTHREAD_BARRIER_SERIAL_THREAD) { + CHECK( 0, ret); + } else { + CHECK( PTHREAD_BARRIER_SERIAL_THREAD, ret ); + } + } + + /* Now cancel the thread */ + CHECK( 0, pthread_cancel( th ) ); + + /* Join it */ + CHECK( 0, pthread_join( th, NULL ) ); + + /* Destroy the queue */ + CHECK( 0, fd_mq_del(&queue) ); + } + + /* Test the threashold function */ + { + struct mqueue * queue = NULL; + int i; + struct msg * msg = NULL; + + /* Create the queue */ + CHECK( 0, fd_mq_new(&queue) ); + + /* Prepare the test data */ + memset(&thrh_td, 0, sizeof(thrh_td)); + thrh_td.queue = queue; + + /* Set the thresholds for the queue */ + CHECK( 0, fd_mq_setthrhd ( queue, NULL, 6, thrh_cb_h, 4, thrh_cb_l ) ); + + /* Post 5 messages, no cb must be called. */ + for (i=0; i<5; i++) { + msg = msg1; + CHECK( 0, fd_mq_post(queue, &msg) ); + } /* 5 msg in queue */ + CHECK( 0, thrh_td.h_calls ); + CHECK( 0, thrh_td.l_calls ); + + /* Get all these messages, and check again */ + for (i=0; i<5; i++) { + CHECK( 0, fd_mq_get(queue, &msg) ); + } /* 0 msg in queue */ + CHECK( 0, thrh_td.h_calls ); + CHECK( 0, thrh_td.l_calls ); + + /* Now, post 6 messages, the high threashold */ + for (i=0; i<6; i++) { + msg = msg1; + CHECK( 0, fd_mq_post(queue, &msg) ); + } /* 6 msg in queue */ + CHECK( 1, thrh_td.h_calls ); + CHECK( 0, thrh_td.l_calls ); + + /* Remove 2 messages, to reach the low threshold */ + for (i=0; i<2; i++) { + CHECK( 0, fd_mq_get(queue, &msg) ); + } /* 4 msg in queue */ + CHECK( 1, thrh_td.h_calls ); + CHECK( 1, thrh_td.l_calls ); + + /* Come again at the high threshold */ + for (i=0; i<2; i++) { + msg = msg1; + CHECK( 0, fd_mq_post(queue, &msg) ); + } /* 6 msg in queue */ + CHECK( 2, thrh_td.h_calls ); + CHECK( 1, thrh_td.l_calls ); + + /* Suppose the queue continues to grow */ + for (i=0; i<6; i++) { + msg = msg1; + CHECK( 0, fd_mq_post(queue, &msg) ); + } /* 12 msg in queue */ + CHECK( 3, thrh_td.h_calls ); + CHECK( 1, thrh_td.l_calls ); + for (i=0; i<5; i++) { + msg = msg1; + CHECK( 0, fd_mq_post(queue, &msg) ); + } /* 17 msg in queue */ + CHECK( 3, thrh_td.h_calls ); + CHECK( 1, thrh_td.l_calls ); + + /* Now the queue goes back to 0 messages */ + for (i=0; i<17; i++) { + CHECK( 0, fd_mq_get(queue, &msg) ); + } /* 0 msg in queue */ + CHECK( 3, thrh_td.h_calls ); + CHECK( 3, thrh_td.l_calls ); + + /* We're done for this test */ + CHECK( 0, fd_mq_del(&queue) ); + } + + /* Delete the messages */ + CHECK( 0, fd_msg_free( msg1 ) ); + CHECK( 0, fd_msg_free( msg2 ) ); + CHECK( 0, fd_msg_free( msg3 ) ); + + /* That's all for the tests yet */ + PASSTEST(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/freediameter/tests/tests.h Fri Aug 28 19:14:42 2009 +0900 @@ -0,0 +1,132 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis <sdecugis@nict.go.jp> * +* * +* Copyright (c) 2009, WIDE Project and NICT * +* All rights reserved. * +* * +* Redistribution and use of this software in source and binary forms, with or without modification, are * +* permitted provided that the following conditions are met: * +* * +* * Redistributions of source code must retain the above * +* copyright notice, this list of conditions and the * +* following disclaimer. * +* * +* * 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. * +* * +* * Neither the name of the WIDE Project or NICT nor the * +* names of its contributors may be used to endorse or * +* promote products derived from this software without * +* specific prior written permission of WIDE Project and * +* NICT. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. * +*********************************************************************************************************/ + +/* This file contains the definition of our test harness. + * The harness is very simple yet. + * It may be interessant to go to dejagnu later... + * + */ +#ifndef _TESTS_H +#define _TESTS_H + +#include "fd.h" + +#include <pthread.h> +#include <errno.h> + +/* Test timeout duration, unless -n is passed on the command line */ +#ifndef TEST_TIMEOUT +#define TEST_TIMEOUT 5 /* 5 seconds */ +#endif /* TEST_TIMEOUT */ + +static int test_verbosity = 0; + +/* Standard includes */ +#include <getopt.h> +#include <time.h> +#include <libgen.h> + +/* Define the return code values */ +#define PASS 0 +#define FAIL 1 + +/* Define the macro to fail a test with a message */ +#define FAILTEST( message... ){ \ + fprintf(stderr, ## message); \ + exit(FAIL); \ +} + +/* Define the macro to pass a test */ +#define PASSTEST( ){ \ + fprintf(stderr, "Test %s passed\n", __FILE__); \ + TRACE_DEBUG(INFO, "Test passed"); \ + exit(PASS); \ +} + +/* Define the standard check routines */ +#define CHECK( _val, _assert ){ \ + if (test_verbosity > 0) { \ + fprintf(stderr, \ + "%s:%-4d: CHECK( " #_assert " == "\ + #_val " )\n", \ + __FILE__, \ + __LINE__); \ + }{ \ + __typeof__ (_val) __ret = (_assert); \ + if (__ret != (_val)) { \ + FAILTEST( "%s:%d: %s == %lx != %lx\n", \ + __FILE__, \ + __LINE__, \ + #_assert, \ + (unsigned long)__ret, \ + (unsigned long)(_val)); \ + }} \ +} + +/* Minimum inits */ +#define INIT_FD() { \ + pthread_key_create(&fd_log_thname, free); \ + fd_log_threadname(basename(__FILE__)); \ + CHECK( 0, fd_dict_init(&fd_g_dict) ); \ + CHECK( 0, fd_dict_base_protocol(fd_g_dict) ); \ + parse_cmdline(argc, argv); \ +} + +static inline void parse_cmdline(int argc, char * argv[]) { + int c; + int no_timeout = 0; + while ((c = getopt (argc, argv, "dqn")) != -1) { + switch (c) { + case 'd': /* Increase verbosity of debug messages. */ + test_verbosity++; + break; + + case 'q': /* Decrease verbosity then remove debug messages. */ + test_verbosity--; + break; + + case 'n': /* Disable the timeout of the test. */ + no_timeout = 1; + break; + + default: /* bug: option not considered. */ + return; + } + } + if (!no_timeout) + alarm(TEST_TIMEOUT); +} + +#endif /* _TESTS_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/freediameter/CMakeLists.txt Fri Aug 28 19:14:42 2009 +0900 @@ -0,0 +1,84 @@ +#CMake configuration for freediameter include directory + +Project("FreeDiameter include directory" C) + +######################## +# Configurable parameters + +# Name of the default configuration file +OPTION(DEFAULT_CONF_FILE "Default path to configuration file?" OFF) + +# Disable SCTP support completly ? +OPTION(DISABLE_SCTP "Disable SCTP support?") + + +######################## +# System checks + +INCLUDE (CheckLibraryExists) +INCLUDE (CheckFunctionExists) +INCLUDE (CheckIncludeFiles) +INCLUDE (CheckSymbolExists) +INCLUDE (TestBigEndian) + +# We use dlopen and dlclose +SET(FD_LIBS ${FD_LIBS} ${CMAKE_DL_LIBS}) + +# We need the threads library (pthread) +INCLUDE(FindThreads) +SET(FD_LIBS ${FD_LIBS} ${CMAKE_THREAD_LIBS_INIT}) + +# We need the clock_gettime function ( -lrt, -lposix4 ) +CHECK_FUNCTION_EXISTS (clock_gettime HAVE_CLOCK_GETTIME) +IF (HAVE_CLOCK_GETTIME) + SET(CLOCK_GETTIME_LIBS "") +ELSE (HAVE_CLOCK_GETTIME) + CHECK_LIBRARY_EXISTS (rt clock_gettime "" HAVE_LIBRT) + IF (HAVE_LIBRT) + SET(CLOCK_GETTIME_LIBS "-lrt") + ELSE (HAVE_LIBRT) + CHECK_LIBRARY_EXISTS (posix4 clock_gettime "" HAVE_LIBPOSIX4) + IF (HAVE_LIBPOSIX4) + SET(CLOCK_GETTIME_LIBS "-lposix4") + ENDIF (HAVE_LIBPOSIX4) + ENDIF (HAVE_LIBRT) +ENDIF (HAVE_CLOCK_GETTIME) +SET(FD_LIBS ${FD_LIBS} ${CLOCK_GETTIME_LIBS}) + +# We need the sctp_getladdrs function ( -lsctp ) +# We need the IPPROTO_SCTP symbol from sys/socket.h, netinet/in.h or netinet/sctp.h +IF(NOT DISABLE_SCTP) + CHECK_FUNCTION_EXISTS(sctp_getladdrs HAVE_NATIVE_SCTP) + IF(NOT HAVE_NATIVE_SCTP) + FIND_PACKAGE(SCTP REQUIRED) + INCLUDE_DIRECTORIES(${SCTP_INCLUDE_DIRS}) + SET(FD_LIBS ${FD_LIBS} ${SCTP_LIBRARIES}) + ENDIF(NOT HAVE_NATIVE_SCTP) +ENDIF(NOT DISABLE_SCTP) + +# Check byte ordering +TEST_BIG_ENDIAN(HOST_BIG_ENDIAN) + +# We need the getopt_long function +CHECK_FUNCTION_EXISTS (getopt_long HAVE_LONG_OPTIONS) +IF (NOT HAVE_LONG_OPTIONS) + MESSAGE(SEND_ERROR "The getopt_long function is not found, please add needed library in build system") +ENDIF (NOT HAVE_LONG_OPTIONS) + +# Check if ntohll is provided on the system +CHECK_SYMBOL_EXISTS(ntohll "" HAVE_NTOHLL) + +# malloc.h ? +CHECK_INCLUDE_FILES (malloc.h HAVE_MALLOC_H) + +# The default configuration file name +IF (NOT DEFAULT_CONF_FILE) + SET(DEFAULT_CONF_FILE "freediameter.conf") +ENDIF (NOT DEFAULT_CONF_FILE) +########################## + +# Generate the host.h file +CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/freediameter-host.h.in ${CMAKE_CURRENT_BINARY_DIR}/freediameter-host.h) + +# Save the FD_LIBS for parent scope +SET(FD_LIBS ${FD_LIBS} PARENT_SCOPE)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/freediameter/freediameter-host.h.in Fri Aug 28 19:14:42 2009 +0900 @@ -0,0 +1,54 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis <sdecugis@nict.go.jp> * +* * +* Copyright (c) 2009, WIDE Project and NICT * +* All rights reserved. * +* * +* Redistribution and use of this software in source and binary forms, with or without modification, are * +* permitted provided that the following conditions are met: * +* * +* * Redistributions of source code must retain the above * +* copyright notice, this list of conditions and the * +* following disclaimer. * +* * +* * 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. * +* * +* * Neither the name of the WIDE Project or NICT nor the * +* names of its contributors may be used to endorse or * +* promote products derived from this software without * +* specific prior written permission of WIDE Project and * +* NICT. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. * +*********************************************************************************************************/ + +/* Configuration from compile-time */ +#ifndef FD_IS_CONFIG +#define FD_IS_CONFIG + +#cmakedefine HAVE_NTOHLL +#cmakedefine HAVE_MALLOC_H + +#cmakedefine HOST_BIG_ENDIAN @HOST_BIG_ENDIAN@ + +#cmakedefine DISABLE_SCTP + +#cmakedefine PROJECT_NAME "@PROJECT_NAME@" +#cmakedefine CMAKE_PROJECT_NAME "@CMAKE_PROJECT_NAME@" +#cmakedefine PROJECT_VERSION "@PROJECT_VERSION@" +#cmakedefine PROJECT_COPYRIGHT "@PROJECT_COPYRIGHT@" + +#cmakedefine DEFAULT_CONF_FILE "@DEFAULT_CONF_FILE@" + +#endif /* FD_IS_CONFIG */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/freediameter/freediameter.h Fri Aug 28 19:14:42 2009 +0900 @@ -0,0 +1,113 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis <sdecugis@nict.go.jp> * +* * +* Copyright (c) 2009, WIDE Project and NICT * +* All rights reserved. * +* * +* Redistribution and use of this software in source and binary forms, with or without modification, are * +* permitted provided that the following conditions are met: * +* * +* * Redistributions of source code must retain the above * +* copyright notice, this list of conditions and the * +* following disclaimer. * +* * +* * 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. * +* * +* * Neither the name of the WIDE Project or NICT nor the * +* names of its contributors may be used to endorse or * +* promote products derived from this software without * +* specific prior written permission of WIDE Project and * +* NICT. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. * +*********************************************************************************************************/ + +#ifndef _FREEDIAMETER_H +#define _FREEDIAMETER_H + + +#include <freediameter/libfreediameter.h> + + +/* The global dictionary */ +extern struct dictionary * fd_g_dict; + + +/***************************************/ +/* Sending a message on the network */ +/***************************************/ + +/* + * FUNCTION: fd_msg_send + * + * PARAMETERS: + * pmsg : Location of the message to be sent on the network (set to NULL on function return to avoid double deletion). + * anscb : A callback to be called when answer is received, if msg is a request (optional) + * anscb_data : opaque data to be passed back to the anscb when it is called. + * + * DESCRIPTION: + * Sends a message on the network. (actually simply queues it in a global queue, to be picked by a daemon's thread) + * For requests, the end-to-end id must be set (see fd_msg_get_eteid / MSGFL_ALLOC_ETEID). + * For answers, the message must be created with function fd_msg_new_answ. + * + * The routing module will handle sending to the correct peer, usually based on the Destination-Realm / Destination-Host AVP. + * + * If the msg is a request, there are two ways of receiving the answer: + * - either having registered a callback in the dispatch module (see disp_register) + * - or provide a callback as parameter here. If such callback is provided, it is called before the dispatch callbacks. + * The prototype for this callback function is: + * void anscb(void * data, struct msg ** answer) + * where: + * data : opaque data that was registered along with the callback. + * answer : location of the pointer to the answer. + * note1: on function return, if *answer is not NULL, the message is passed to the dispatch module for regular callbacks. + * otherwise, the callback must take care of freeing the message (msg_free). + * note2: the opaque data is not freed by the daemon in any case, extensions should ensure clean handling in waaad_ext_fini. + * + * If no callback is registered to handle an answer, the message is discarded and an error is logged. + * + * RETURN VALUE: + * 0 : The message has been queued for sending (sending may fail asynchronously). + * EINVAL : A parameter is invalid (ex: anscb provided but message is not a request). + * ... + */ +int fd_msg_send ( struct msg ** pmsg, void (*anscb)(void *, struct msg **), void * data ); + +/* + * FUNCTION: fd_msg_rescode_set + * + * PARAMETERS: + * msg : A msg object -- it must be an answer. + * dict : dictionary to use for AVP definitions + * rescode : The name of the returned error code (ex: "DIAMETER_INVALID_AVP") + * errormsg : (optional) human-readable error message to put in Error-Message AVP + * optavp : (optional) If provided, the content will be put inside a Failed-AVP + * type_id : 0 => nothing; 1 => adds Origin-Host and Origin-Realm with local info. 2=> adds Error-Reporting-Host. + * + * DESCRIPTION: + * This function adds a Result-Code AVP to a message, and optionally + * - sets the 'E' error flag in the header, + * - adds Error-Message, Error-Reporting-Host and Failed-AVP AVPs. + * + * RETURN VALUE: + * 0 : Operation complete. + * !0 : an error occurred. + */ +int fd_msg_rescode_set( struct msg * msg, struct dictionary * dict, char * rescode, char * errormsg, struct avp * optavp, int type_id ); + +/* The following functions are used to achieve frequent operations on the messages */ +int fd_msg_add_origin ( struct msg * msg, struct dictionary * dict, int osi ); /* Add Origin-Host, Origin-Realm, (if osi) Origin-State-Id AVPS at the end of the message */ + + +#endif /* _FREEDIAMETER_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/freediameter/libfreediameter.h Fri Aug 28 19:14:42 2009 +0900 @@ -0,0 +1,2020 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis <sdecugis@nict.go.jp> * +* * +* Copyright (c) 2009, WIDE Project and NICT * +* All rights reserved. * +* * +* Redistribution and use of this software in source and binary forms, with or without modification, are * +* permitted provided that the following conditions are met: * +* * +* * Redistributions of source code must retain the above * +* copyright notice, this list of conditions and the * +* following disclaimer. * +* * +* * 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. * +* * +* * Neither the name of the WIDE Project or NICT nor the * +* names of its contributors may be used to endorse or * +* promote products derived from this software without * +* specific prior written permission of WIDE Project and * +* NICT. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. * +*********************************************************************************************************/ + +/* This file contains the definitions of functions and types used by the libfreediameter library. + * + * This library is meant to be used by both the freediameter daemon and its extensions. + * + * It provides the tools to manipulate Diameter messages and related data. + * + * This file should always be included as #include <freediameter/libfreediameter.h> + * Note that this library does not store any state. The daemon must pass the pointer to + * the dictionary and other global objects to all extensions that use the libfreediameter + * library. + */ + +#ifndef _LIBFREEDIAMETER_H +#define _LIBFREEDIAMETER_H + +#ifndef FD_IS_CONFIG +#error "You must include 'freediameter-host.h' before this file." +#endif /* FD_IS_CONFIG */ + +#include <pthread.h> +#include <string.h> +#include <assert.h> +#include <errno.h> +#include <arpa/inet.h> +#include <sys/socket.h> +#include <netdb.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +/*============================================================*/ +/* DEBUG */ +/*============================================================*/ +#ifndef ASSERT +#define ASSERT(x) assert(x) +#endif /* ASSERT */ + +/* + * FUNCTION: fd_log_debug + * + * PARAMETERS: + * format : Same format string as in the printf function + * ... : Same list as printf + * + * DESCRIPTION: + * Log internal information for use of developpers only. + * The format and arguments may contain UTF-8 encoded data. The + * output medium (file or console) is expected to support this encoding. + * + * This function assumes that a global mutex called "fd_log_lock" exists + * in the address space of the current process. + * + * RETURN VALUE: + * None. + */ +void fd_log_debug ( char * format, ... ); +extern pthread_mutex_t fd_log_lock; + +/* + * FUNCTION: fd_log_threadname + * + * PARAMETERS: + * name : \0-terminated string containing a name to identify the current thread. + * + * DESCRIPTION: + * Name the current thread, useful for debugging multi-threaded problems. + * + * This function assumes that a global thread-specific key called "fd_log_thname" exists + * in the address space of the current process. + * + * RETURN VALUE: + * None. + */ +void fd_log_threadname ( char * name ); +extern pthread_key_t fd_log_thname; + +/* + * FUNCTION: fd_log_time + * + * PARAMETERS: + * buf : An array where the time must be stored + * len : size of the buffer + * + * DESCRIPTION: + * Writes the current timestamp (in human readable format) in a buffer. + * + * RETURN VALUE: + * pointer to buf. + */ +char * fd_log_time ( char * buf, size_t len ); + +/************************** DEBUG MACROS ************************************/ + +/* levels definitions */ +#define NONE 0 /* Display no debug message */ +#define INFO 1 /* Display errors only */ +#define FULL 2 /* Display additional information to follow code execution */ +#define ANNOYING 4 /* Very verbose, for example in loops */ +#define FCTS 6 /* Display entry parameters of most functions */ +#define CALL 9 /* Display calls to most functions (with CHECK macros) */ + +/* Default level is INFO */ +#ifndef TRACE_LEVEL +#define TRACE_LEVEL INFO +#endif /* TRACE_LEVEL */ + +/* The level of the file being compiled */ +static int local_debug_level = TRACE_LEVEL; + +/* helper macros (pre-processor hacks) */ +#define __str( arg ) #arg +#define _stringize( arg ) __str( arg ) +#define __agr( arg1, arg2 ) arg1 ## arg2 +#define _aggregate( arg1, arg2 ) __agr( arg1, arg2 ) + +/* Some portability tricks to get nice function name in __PRETTY_FUNCTION__ */ +#if __STDC_VERSION__ < 199901L +# if __GNUC__ >= 2 +# define __func__ __FUNCTION__ +# else /* __GNUC__ >= 2 */ +# define __func__ "<unknown>" +# endif /* __GNUC__ >= 2 */ +#endif /* __STDC_VERSION__ < 199901L */ +#ifndef __PRETTY_FUNCTION__ +#define __PRETTY_FUNCTION__ __func__ +#endif /* __PRETTY_FUNCTION__ */ + +/* Boolean for tracing at a certain level */ +#define TRACE_BOOL(_level_) ( (_level_) <= local_debug_level ) + +/* The general debug macro, each call results in two lines of debug messages */ +#define TRACE_DEBUG(level,format,args... ) { \ + if ( TRACE_BOOL(level) ) { \ + char __buf[25]; \ + char * __thn = ((char *)pthread_getspecific(fd_log_thname) ?: "unnamed"); \ + fd_log_debug("\t | th:%-30s\t%s\tin %s@%s:%d\n" \ + "\t%s|%*s" format "\n", \ + __thn, fd_log_time(__buf, sizeof(__buf)), __PRETTY_FUNCTION__, __FILE__, __LINE__, \ + (level < FULL)?"@":" ",level, "", ## args); \ + } \ +} + +/* Helper for function entry */ +#define TRACE_ENTRY(_format,_args... ) \ + TRACE_DEBUG(FCTS, "->%s (" #_args ") = (" _format ") >", __PRETTY_FUNCTION__, ##_args ); + +/* Helper for debugging by adding traces */ +#define TRACE_HERE() \ + TRACE_DEBUG(INFO, " -- debug checkpoint -- "); + +/* Helper for tracing the CHECK_* macros bellow */ +#define TRACE_DEBUG_ALL( str ) \ + TRACE_DEBUG(CALL, str ); + + +/* Macros to check a return value and branch out in case of error. + * These macro must be used only when errors are highly improbable, not for expected errors. + */ + +/* Check the return value of a system function and execute fallback in case of error */ +#define CHECK_SYS_DO( __call__, __fallback__ ) { \ + int __ret__; \ + TRACE_DEBUG_ALL( "Check SYS: " #__call__ ); \ + __ret__ = (__call__); \ + if (__ret__ < 0) { \ + int __err__ = errno; /* We may handle EINTR here */ \ + TRACE_DEBUG(NONE, "ERROR: in '" #__call__ "' :\t%s", strerror(__err__));\ + __fallback__; \ + } \ +} +/* Check the return value of a system function, return error code on error */ +#define CHECK_SYS( __call__ ) { \ + int __ret__; \ + TRACE_DEBUG_ALL( "Check SYS: " #__call__ ); \ + __ret__ = (__call__); \ + if (__ret__ < 0) { \ + int __err__ = errno; /* We may handle EINTR here */ \ + TRACE_DEBUG(NONE, "ERROR: in '" #__call__ "' :\t%s", strerror(__err__));\ + return __err__; \ + } \ +} + +/* Check the return value of a POSIX function and execute fallback in case of error or special value */ +#define CHECK_POSIX_DO2( __call__, __speval__, __fallback1__, __fallback2__ ) { \ + int __ret__; \ + TRACE_DEBUG_ALL( "Check POSIX: " #__call__ ); \ + __ret__ = (__call__); \ + if (__ret__ != 0) { \ + if (__ret__ == (__speval__)) { \ + __fallback1__; \ + } else { \ + TRACE_DEBUG(NONE, "ERROR: in '" #__call__ "':\t%s", strerror(__ret__)); \ + __fallback2__; \ + } \ + } \ +} + +/* Check the return value of a POSIX function and execute fallback in case of error */ +#define CHECK_POSIX_DO( __call__, __fallback__ ) \ + CHECK_POSIX_DO2( (__call__), 0, , __fallback__ ); + +/* Check the return value of a POSIX function and return it if error */ +#define CHECK_POSIX( __call__ ) { \ + int __v__; \ + CHECK_POSIX_DO( __v__ = (__call__), return __v__ ); \ +} + +/* Check that a memory allocator did not return NULL, otherwise log an error and execute fallback */ +#define CHECK_MALLOC_DO( __call__, __fallback__ ) { \ + void * __ret__; \ + TRACE_DEBUG_ALL( "Check MALLOC: " #__call__ ); \ + __ret__ = (void *)( __call__ ); \ + if (__ret__ == NULL) { \ + int __err__ = errno; \ + TRACE_DEBUG(NONE, "ERROR: in '" #__call__ "':\t%s", strerror(__err__)); \ + __fallback__; \ + } \ +} + +/* Check that a memory allocator did not return NULL, otherwise return ENOMEM */ +#define CHECK_MALLOC( __call__ ) \ + CHECK_MALLOC_DO( __call__, return ENOMEM ); + + +/* The next macros can be used also for expected errors */ + +/* Check parameters at function entry, execute fallback on error */ +#define CHECK_PARAMS_DO( __bool__, __fallback__ ) \ + TRACE_DEBUG_ALL( "Check PARAMS: " #__bool__ ); \ + if ( ! (__bool__) ) { \ + TRACE_DEBUG(INFO, "Invalid parameter received in '" #__bool__ "'"); \ + __fallback__; \ + } +/* Check parameters at function entry, return EINVAL if the boolean is false (similar to assert) */ +#define CHECK_PARAMS( __bool__ ) \ + CHECK_PARAMS_DO( __bool__, return EINVAL ); + +/* Check the return value of an internal function, log and propagate */ +#define CHECK_FCT_DO( __call__, __fallback__ ) { \ + int __ret__; \ + TRACE_DEBUG_ALL( "Check FCT: " #__call__ ); \ + __ret__ = (__call__); \ + if (__ret__ != 0) { \ + TRACE_DEBUG(INFO, "Error in '" #__call__ "':\t%s", strerror(__ret__)); \ + __fallback__; \ + } \ +} +/* Check the return value of a function call, return any error code */ +#define CHECK_FCT( __call__ ) { \ + int __v__; \ + CHECK_FCT_DO( __v__ = (__call__), return __v__ ); \ +} + +/****************************** Socket helpers ************************************/ + +/* Some aliases to socket addresses structures */ +#define sSS struct sockaddr_storage +#define sSA struct sockaddr +#define sSA4 struct sockaddr_in +#define sSA6 struct sockaddr_in6 + +/* Dump one sockaddr */ +#define sSA_DUMP( level, text, sa ) { \ + sSA * __sa = (sSA *)(sa); \ + char *__str, __addrbuf[INET6_ADDRSTRLEN]; \ + if (__sa) { \ + int __rc = getnameinfo(__sa, \ + sizeof(sSS), \ + __addrbuf, \ + sizeof(__addrbuf), \ + NULL, \ + 0, \ + 0); \ + if (__rc) \ + __str = (char *)gai_strerror(__rc); \ + else \ + __str = &__addrbuf[0]; \ + } else { \ + __str = "(NULL / ANY)"; \ + } \ + TRACE_DEBUG(level, text "%s", __str); \ +} + +/* The sockaddr length of a sSS structure */ +#define sSSlen( _ss_ ) \ + ( (socklen_t) ( ((_ss_)->ss_family == AF_INET) ? (sizeof(sSA4)) : \ + (((_ss_)->ss_family == AF_INET6) ? (sizeof(sSA6)) : \ + 0 ) ) ) + +/* Define the value of IP loopback address */ +#ifndef INADDR_LOOPBACK +#define INADDR_LOOPBACK inet_addr("127.0.0.1") +#endif /* INADDR_LOOPBACK */ + +/* create a V4MAPPED address */ +#define IN6_ADDR_V4MAP( a6, a4 ) { \ + ((uint32_t *)(a6))[0] = 0; \ + ((uint32_t *)(a6))[1] = 0; \ + ((uint32_t *)(a6))[2] = htonl(0xffff); \ + ((uint32_t *)(a6))[3] = (uint32_t)(a4); \ +} + +/* Retrieve a v4 value from V4MAPPED address ( takes a s6_addr as param) */ +#define IN6_ADDR_V4UNMAP( a6 ) \ + (((in_addr_t *)(a6))[3]) + +/* + * Other macros + */ + +/* We provide macros to convert 64 bit values to and from network byte-order, on systems where it is not already provided. */ +#ifndef HAVE_NTOHLL /* Defined in config.h, if the ntohll symbol is defined on the system */ +# if HOST_BIG_ENDIAN + /* In big-endian systems, we don't have to change the values, since the order is the same as network */ +# define ntohll(x) (x) +# define htonll(x) (x) +# else /* HOST_BIG_ENDIAN */ + /* For these systems, we must reverse the bytes. Use ntohl and htonl on sub-32 blocs, and inverse these blocs. */ +# define ntohll(x) (typeof (x))( (((uint64_t)ntohl( (uint32_t)(x))) << 32 ) | ((uint64_t) ntohl( ((uint64_t)(x)) >> 32 ))) +# define htonll(x) (typeof (x))( (((uint64_t)htonl( (uint32_t)(x))) << 32 ) | ((uint64_t) htonl( ((uint64_t)(x)) >> 32 ))) +# endif /* HOST_BIG_ENDIAN */ +#endif /* HAVE_NTOHLL */ + +/* This macro will pad a size to the next multiple of 4. */ +#define PAD4(_x) ((_x) + ( (4 - (_x)) & 3 ) ) + +/* Useful to display as ASCII some bytes values */ +#define ASCII(_c) ( ((_c < 32) || (_c > 127)) ? ( _c ? '?' : ' ' ) : _c ) + +/* Compare timespec structures */ +#define TS_IS_INFERIOR( ts1, ts2 ) \ + ( ((ts1)->tv_sec < (ts2)->tv_sec ) \ + || ((ts1)->tv_nsec < (ts2)->tv_nsec) ) + +/* Some constants for dumping flags and values */ +#define DUMP_AVPFL_str "%c%c" +#define DUMP_AVPFL_val(_val) (_val & AVP_FLAG_VENDOR)?'V':'-' , (_val & AVP_FLAG_MANDATORY)?'M':'-' +#define DUMP_CMDFL_str "%c%c%c%c" +#define DUMP_CMDFL_val(_val) (_val & CMD_FLAG_REQUEST)?'R':'-' , (_val & CMD_FLAG_PROXIABLE)?'P':'-' , (_val & CMD_FLAG_ERROR)?'E':'-' , (_val & CMD_FLAG_RETRANSMIT)?'T':'-' + +/*============================================================*/ +/* THREADS */ +/*============================================================*/ + +/* Terminate a thread */ +static __inline__ int fd_thr_term(pthread_t * th) +{ + int ret = 0; + void * th_ret = NULL; + + CHECK_PARAMS(th); + + /* Test if it was already terminated */ + if (*th == (pthread_t)NULL) + return 0; + + /* Cancel the thread if it is still running - ignore error if it was already terminated */ + (void) pthread_cancel(*th); + + /* Then join the thread */ + CHECK_POSIX_DO( ret = pthread_join(*th, &th_ret), /* continue */ ); + + if (th_ret != NULL) { + TRACE_DEBUG(FULL, "The thread returned the following value: %p (ignored)", th_ret); + } + + /* Clean the location */ + *th = (pthread_t)NULL; + + return ret; +} + +/* Cleanups for cancellation (all threads should be safely cancelable!) */ +static __inline__ void fd_cleanup_mutex( void * mutex ) +{ + CHECK_POSIX_DO( pthread_mutex_unlock((pthread_mutex_t *)mutex), /* */); +} + +static __inline__ void fd_cleanup_rwlock( void * rwlock ) +{ + CHECK_POSIX_DO( pthread_rwlock_unlock((pthread_rwlock_t *)rwlock), /* */); +} + +static __inline__ void fd_cleanup_buffer( void * buffer ) +{ + free(buffer); +} + +/*============================================================*/ +/* LISTS */ +/*============================================================*/ + +/* The following structure represents a chained list element */ +struct fd_list { + struct fd_list *next; /* next element in the list */ + struct fd_list *prev; /* previous element in the list */ + struct fd_list *head; /* head of the list */ + void *o; /* additional avialbe pointer used for start of the parento object or other purpose */ +}; + +#define FD_LIST( _li ) ((struct fd_list *)( _li )) + +/* Initialize a list element */ +void fd_list_init ( struct fd_list * list, void *obj ); + +/* Return boolean, true if the list is empty */ +#define FD_IS_LIST_EMPTY( _list ) (((FD_LIST(_list))->head == (_list)) && ((FD_LIST(_list))->next == (_list))) + +/* Insert an item in a list at known position */ +void fd_list_insert_after ( struct fd_list * ref, struct fd_list * item ); +void fd_list_insert_before ( struct fd_list * ref, struct fd_list * item ); + +/* Insert an item in an ordered list -- ordering function provided. If duplicate object found, EEXIST and it is returned in ref_duplicate */ +int fd_list_insert_ordered( struct fd_list * head, struct fd_list * item, int (*cmp_fct)(void *, void *), void ** ref_duplicate); + +/* Unlink an item from a list */ +void fd_list_unlink ( struct fd_list * item ); + +/* Compute a hash value of a string (session id, diameter id, ...) */ +uint32_t fd_hash ( char * string, size_t len ); + + + +/*============================================================*/ +/* DICTIONARY */ +/*============================================================*/ +/* Structure that contains the complete dictionary definitions */ +struct dictionary; + +/* Structure that contains a dictionary object */ +struct dict_object; + +/* Types of object in the dictionary. */ +enum dict_object_type { + DICT_VENDOR = 1, /* Vendor */ + DICT_APPLICATION, /* Diameter Application */ + DICT_TYPE, /* AVP data type */ + DICT_ENUMVAL, /* Named constant (value of an enumerated AVP type) */ + DICT_AVP, /* AVP */ + DICT_COMMAND, /* Diameter Command */ + DICT_RULE /* a Rule for AVP in command or grouped AVP */ +#define DICT_TYPE_MAX DICT_RULE +}; + +/* Initialize a dictionary */ +int fd_dict_init(struct dictionary ** dict); +/* Destroy a dictionary */ +int fd_dict_fini(struct dictionary ** dict); + +/* + * FUNCTION: fd_dict_new + * + * PARAMETERS: + * dict : Pointer to the dictionnary where the object is created + * type : What kind of object must be created + * data : pointer to the data for the object. + * type parameter is used to determine the type of data (see bellow for detail). + * parent : a reference to a parent object, if needed. + * ref : upon successful creation, reference to new object is stored here if !null. + * + * DESCRIPTION: + * Create a new object in the dictionary. + * See following object sections in this header file for more information on data and parent parameters format. + * + * RETURN VALUE: + * 0 : The object is created in the dictionary. + * EINVAL : A parameter is invalid. + * EEXIST : This object is already defined in the dictionary (with conflicting data). + * If "ref" is not NULL, it points to the existing element on return. + * (other standard errors may be returned, too, with their standard meaning. Example: + * ENOMEM : Memory allocation for the new object element failed.) + */ +int fd_dict_new ( struct dictionary * dict, enum dict_object_type type, void * data, struct dict_object * parent, struct dict_object **ref ); + +/* + * FUNCTION: fd_dict_search + * + * PARAMETERS: + * dict : Pointer to the dictionnary where the object is searched + * type : type of object that is being searched + * criteria : how the object must be searched. See object-related sections bellow for more information. + * what : depending on criteria, the data that must be searched. + * result : On successful return, pointer to the object is stored here. + * retval : this value is returned if the object is not found and result is not NULL. + * + * DESCRIPTION: + * Perform a search in the dictionary. + * See the object-specific sections bellow to find how to look for each objects. + * If the "result" parameter is NULL, the function is used to check if an object is in the dictionary. + * Otherwise, a reference to the object is stored in result if found. + * If result is not NULL and the object is not found, retval is returned (should be 0 or ENOENT usually) + * + * RETURN VALUE: + * 0 : The object has been found in the dictionary, or *result is NULL. + * EINVAL : A parameter is invalid. + * ENOENT : No matching object has been found, and result was NULL. + */ +int fd_dict_search ( struct dictionary * dict, enum dict_object_type type, int criteria, void * what, struct dict_object **result, int retval ); + +/* Special case: get the generic error command object */ +int fd_dict_get_error_cmd(struct dictionary * dict, struct dict_object **obj); + +/* + * FUNCTION: fd_dict_getval + * + * PARAMETERS: + * object : Pointer to a dictionary object. + * data : pointer to a structure to hold the data for the object. + * The type is the same as "data" parameter in fd_dict_new function. + * + * DESCRIPTION: + * Retrieve content of a dictionary object. + * See following object sections in this header file for more information on data and parent parameters format. + * + * RETURN VALUE: + * 0 : The content of the object has been retrieved. + * EINVAL : A parameter is invalid. + */ +int fd_dict_getval ( struct dict_object * object, void * val); +int fd_dict_gettype ( struct dict_object * object, enum dict_object_type * type); +int fd_dict_getdict ( struct dict_object * object, struct dictionary ** dict); + +/* Debug functions */ +void fd_dict_dump_object(struct dict_object * obj); +void fd_dict_dump(struct dictionary * dict); + + +/* + *************************************************************************** + * + * Vendor object + * + * These types are used to manage vendors in the dictionary + * + *************************************************************************** + */ + +/* Type to hold a Vendor ID: "SMI Network Management Private Enterprise Codes" (RFC3232) */ +typedef uint32_t vendor_id_t; + +/* Type to hold data associated to a vendor */ +struct dict_vendor_data { + vendor_id_t vendor_id; /* ID of a vendor */ + char *vendor_name; /* The name of this vendor */ +}; + +/* The criteria for searching a vendor object in the dictionary */ +enum { + VENDOR_BY_ID = 10, /* "what" points to a vendor_id_t */ + VENDOR_BY_NAME, /* "what" points to a string */ + VENDOR_OF_APPLICATION /* "what" points to a struct dict_object containing an application (see bellow) */ +}; + +/*** + * API usage : + +Note: the value of "vendor_name" is copied when the object is created, and the string may be disposed afterwards. +On the other side, when value is retrieved with dict_getval, the string is not copied and MUST NOT be freed. It will +be freed automatically along with the object itself with call to dict_fini later. + +- dict_new: + The "parent" parameter is not used for vendors. + Sample code to create a vendor: + { + int ret; + struct dict_object * myvendor; + struct dict_vendor_data myvendordata = { 23455, "my vendor name" }; -- just an example... + ret = dict_new ( DICT_VENDOR, &myvendordata, NULL, &myvendor ); + } + +- dict_search: + Sample codes to look for a vendor object, by its id or name: + { + int ret; + struct dict_object * vendor_found; + vendor_id_t vendorid = 23455; + ret = dict_search ( DICT_VENDOR, VENDOR_BY_ID, &vendorid, &vendor_found, ENOENT); + - or - + ret = dict_search ( DICT_VENDOR, VENDOR_BY_NAME, "my vendor name", &vendor_found, ENOENT); + } + + - dict_getval: + Sample code to retrieve the data from a vendor object: + { + int ret; + struct dict_object * myvendor; + struct dict_vendor_data myvendordata; + ret = dict_search ( DICT_VENDOR, VENDOR_BY_NAME, "my vendor name", &myvendor, ENOENT); + ret = dict_getval ( myvendor, &myvendordata ); + printf("my vendor id: %d\n", myvendordata.vendor_id ); + } + + +*/ + +/* + *************************************************************************** + * + * Application object + * + * These types are used to manage Diameter applications in the dictionary + * + *************************************************************************** + */ + +/* Type to hold a Diameter application ID: IANA assigned value for this application. */ +typedef uint32_t application_id_t; + +/* Type to hold data associated to an application */ +struct dict_application_data { + application_id_t application_id; /* ID of the application */ + char *application_name; /* The name of this application */ +}; + +/* The criteria for searching an application object in the dictionary */ +enum { + APPLICATION_BY_ID = 20, /* "what" points to a application_id_t */ + APPLICATION_BY_NAME, /* "what" points to a string */ + APPLICATION_OF_TYPE, /* "what" points to a struct dict_object containing a type object (see bellow) */ + APPLICATION_OF_COMMAND /* "what" points to a struct dict_object containing a command (see bellow) */ +}; + +/*** + * API usage : + +The "parent" parameter of dict_new may point to a vendor object to inform of what vendor defines the application. +for standard-track applications, the "parent" parameter should be NULL. +The vendor associated to an application is retrieved with VENDOR_OF_APPLICATION search criteria on vendors. + +- dict_new: + Sample code for application creation: + { + int ret; + struct dict_object * vendor; + struct dict_object * appl; + struct dict_vendor_data vendor_data = { + 23455, + "my vendor name" + }; + struct dict_application_data app_data = { + 9789, + "my vendor's application" + }; + + ret = dict_new ( DICT_VENDOR, &vendor_data, NULL, &vendor ); + ret = dict_new ( DICT_APPLICATION, &app_data, vendor, &appl ); + } + +- dict_search: + Sample code to retrieve the vendor of an application + { + int ret; + struct dict_object * vendor, * appli; + + ret = dict_search ( DICT_APPLICATION, APPLICATION_BY_NAME, "my vendor's application", &appli, ENOENT); + ret = dict_search ( DICT_VENDOR, VENDOR_OF_APPLICATION, appli, &vendor, ENOENT); + } + + - dict_getval: + Sample code to retrieve the data from an application object: + { + int ret; + struct dict_object * appli; + struct dict_application_data appl_data; + ret = dict_search ( DICT_APPLICATION, APPLICATION_BY_NAME, "my vendor's application", &appli, ENOENT); + ret = dict_getval ( appli, &appl_data ); + printf("my application id: %s\n", appl_data.application_id ); + } + +*/ + +/* + *************************************************************************** + * + * Type object + * + * These types are used to manage AVP data types in the dictionary + * + *************************************************************************** + */ + +/* Type to store any AVP value */ +union avp_value { + struct { + uint8_t *data; /* bytes buffer */ + size_t len; /* length of the data buffer */ + } os; /* Storage for an octet string, data is alloc'd and must be freed */ + int32_t i32; /* integer 32 */ + int64_t i64; /* integer 64 */ + uint32_t u32; /* unsigned 32 */ + uint64_t u64; /* unsigned 64 */ + float f32; /* float 32 */ + double f64; /* float 64 */ +}; + +/* These are the basic AVP types defined in RFC3588bis */ +enum dict_avp_basetype { + AVP_TYPE_GROUPED, + AVP_TYPE_OCTETSTRING, + AVP_TYPE_INTEGER32, + AVP_TYPE_INTEGER64, + AVP_TYPE_UNSIGNED32, + AVP_TYPE_UNSIGNED64, + AVP_TYPE_FLOAT32, + AVP_TYPE_FLOAT64 +#define AVP_TYPE_MAX AVP_TYPE_FLOAT64 +}; + +/* Callbacks that can be associated with a derived type to easily interpret the AVP value. */ +/* + * CALLBACK: dict_avpdata_interpret + * + * PARAMETERS: + * val : Pointer to the AVP value that must be interpreted. + * interpreted : The result of interpretation is stored here. The format and meaning depends on each type. + * + * DESCRIPTION: + * This callback can be provided with a derived type in order to facilitate the interpretation of formated data. + * For example, when an AVP of type "Address" is received, it can be used to convert the octetstring into a struct sockaddr. + * This callback is not called directly, but through the message's API msg_avp_value_interpret function. + * + * RETURN VALUE: + * 0 : Operation complete. + * !0 : An error occurred, the error code is returned. + */ +typedef int (*dict_avpdata_interpret) (union avp_value * value, void * interpreted); +/* + * CALLBACK: dict_avpdata_encode + * + * PARAMETERS: + * data : The formated data that must be stored in the AVP value. + * val : Pointer to the AVP value storage area where the data must be stored. + * + * DESCRIPTION: + * This callback can be provided with a derived type in order to facilitate the encoding of formated data. + * For example, it can be used to convert a struct sockaddr in an AVP value of type Address. + * This callback is not called directly, but through the message's API msg_avp_value_encode function. + * If the callback is defined for an OctetString based type, the created string must be malloc'd. free will be called + * automatically later. + * + * RETURN VALUE: + * 0 : Operation complete. + * !0 : An error occurred, the error code is returned. + */ +typedef int (*dict_avpdata_encode) (void * data, union avp_value * val); + + +/* Type to hold data associated to a derived AVP data type */ +struct dict_type_data { + enum dict_avp_basetype type_base; /* How the data of such AVP must be interpreted */ + char *type_name; /* The name of this type */ + dict_avpdata_interpret type_interpret;/* cb to convert the AVP value in more comprehensive format (or NULL) */ + dict_avpdata_encode type_encode; /* cb to convert formatted data into an AVP value (or NULL) */ +}; + +/* The criteria for searching a type object in the dictionary */ +enum { + TYPE_BY_NAME = 30, /* "what" points to a string */ + TYPE_OF_ENUMVAL, /* "what" points to a struct dict_object containing an enumerated constant (DICT_ENUMVAL, see bellow). */ + TYPE_OF_AVP /* "what" points to a struct dict_object containing an AVP object. */ +}; + + +/*** + * API usage : + +- dict_new: + The "parent" parameter may point to an application object, when a type is defined by a Diameter application. + + Sample code: + { + int ret; + struct dict_object * mytype; + struct dict_type_data mytypedata = + { + AVP_TYPE_OCTETSTRING, + "Address", + NULL, + NULL + }; + ret = dict_new ( DICT_TYPE, &mytypedata, NULL, &mytype ); + } + +- dict_search: + Sample code: + { + int ret; + struct dict_object * address_type; + ret = dict_search ( DICT_TYPE, TYPE_BY_NAME, "Address", &address_type, ENOENT); + } + +*/ + +/* + *************************************************************************** + * + * Enumerated values object + * + * These types are used to manage named constants of some AVP, + * for enumerated types. Waaad allows contants for types others than Unsigned32 + * + *************************************************************************** + */ + +/* Type to hold data of named constants for AVP */ +struct dict_enumval_data { + char *enum_name; /* The name of this constant */ + union avp_value enum_value; /* Value of the constant. Union term depends on parent type's base type. */ +}; + +/* The criteria for searching a constant in the dictionary */ +enum { + ENUMVAL_BY_STRUCT = 40, /* "what" points to a struct dict_enumval_request as defined bellow */ +}; + +struct dict_enumval_request { + /* Identifier of the parent type, one of the following must not be NULL */ + struct dict_object *type_obj; + char *type_name; + + /* Search criteria for the constant */ + struct dict_enumval_data search; /* search.enum_value is used only if search.enum_name == NULL */ +}; + +/*** + * API usage : + +- dict_new: + The "parent" parameter must point to a derived type object. + Sample code to create a type "Boolean" with two constants "True" and "False": + { + int ret; + struct dict_object * type_boolean; + struct dict_type_data type_boolean_data = + { + AVP_TYPE_INTEGER32, + "Boolean", + NULL, + NULL + }; + struct dict_enumval_data boolean_false = + { + .enum_name="False", + .enum_value.i32 = 0 + }; + struct dict_enumval_data boolean_true = + { + .enum_name="True", + .enum_value.i32 = -1 + }; + ret = dict_new ( DICT_TYPE, &type_boolean_data, NULL, &type_boolean ); + ret = dict_new ( DICT_ENUMVAL, &boolean_false, type_boolean, NULL ); + ret = dict_new ( DICT_ENUMVAL, &boolean_true , type_boolean, NULL ); + + } + +- dict_search: + Sample code to look for a constant name, by its value: + { + int ret; + struct dict_object * value_found; + struct dict_enumval_request boolean_by_value = + { + .type_name = "Boolean", + .search.enum_name=NULL, + .search.enum_value.i32 = -1 + }; + + ret = dict_search ( DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &boolean_by_value, &value_found, ENOENT); + } + + - dict_getval: + Sample code to retrieve the data from a constant object: + { + int ret; + struct dict_object * value_found; + struct dict_enumval_data boolean_data = NULL; + struct dict_enumval_request boolean_by_value = + { + .type_name = "Boolean", + .search.enum_name=NULL, + .search.enum_value.i32 = 0 + }; + + ret = dict_search ( DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &boolean_by_value, &value_found, ENOENT); + ret = dict_getval ( value_found, &boolean_data ); + printf(" Boolean with value 0: %s", boolean_data.enum_name ); + } +*/ + +/* + *************************************************************************** + * + * AVP object + * + * These objects are used to manage AVP definitions in the dictionary + * + *************************************************************************** + */ + +/* Type to hold an AVP code. For vendor 0, these codes are assigned by IANA. Otherwise, it is managed by the vendor */ +typedef uint32_t avp_code_t; + +/* Values of AVP flags */ +#define AVP_FLAG_VENDOR 0x80 +#define AVP_FLAG_MANDATORY 0x40 +#define AVP_FLAG_RESERVED3 0x20 +#define AVP_FLAG_RESERVED4 0x10 +#define AVP_FLAG_RESERVED5 0x08 +#define AVP_FLAG_RESERVED6 0x04 +#define AVP_FLAG_RESERVED7 0x02 +#define AVP_FLAG_RESERVED8 0x01 + + +/* Type to hold data associated to an avp */ +struct dict_avp_data { + avp_code_t avp_code; /* Code of the avp */ + vendor_id_t avp_vendor; /* Vendor of the AVP, or 0 */ + char *avp_name; /* Name of this AVP */ + uint8_t avp_flag_mask; /* Mask of fixed AVP flags */ + uint8_t avp_flag_val; /* Values of the fixed flags */ + enum dict_avp_basetype avp_basetype; /* Basic type of data found in the AVP */ +}; + +/* The criteria for searching an avp object in the dictionary */ +enum { + AVP_BY_CODE = 50, /* "what" points to an avp_code_t, vendor is always 0 */ + AVP_BY_NAME, /* "what" points to a string, vendor is always 0 */ + AVP_BY_CODE_AND_VENDOR, /* "what" points to a struct dict_avp_request (see bellow), where avp_vendor and avp_code are set */ + AVP_BY_NAME_AND_VENDOR /* "what" points to a struct dict_avp_request (see bellow), where avp_vendor and avp_name are set */ +}; + +/* Struct used for some researchs */ +struct dict_avp_request { + vendor_id_t avp_vendor; + avp_code_t avp_code; + char *avp_name; +}; + + +/*** + * API usage : + +If "parent" parameter is not NULL during AVP creation, it must point to a DICT_TYPE object. +The extended type is then attached to the AVP. In case where it is an enumerated type, the value of +AVP is automatically interpreted in debug messages, and in message checks. +The derived type of an AVP can be retrieved with: dict_search ( DICT_TYPE, TYPE_OF_AVP, avp, ... ) + +To create the rules (ABNF) for children of Grouped AVP, see the DICT_RULE related part. + +- dict_new: + Sample code for AVP creation: + { + int ret; + struct dict_object * user_name_avp; + struct dict_object * boolean_type; + struct dict_object * sample_boolean_avp; + struct dict_avp_data user_name_data = { + 1, // code + 0, // vendor + "User-Name", // name + AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, // fixed mask: V and M values must always be defined as follow. other flags can be set or cleared + AVP_FLAG_MANDATORY, // the V flag must be cleared, the M flag must be set. + AVP_TYPE_OCTETSTRING // User-Name AVP contains OctetString data (further precision such as UTF8String can be given with a parent derived type) + }; + struct dict_avp_data sample_boolean_data = { + 31337, + 23455, + "Sample-Boolean", + AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, + AVP_FLAG_VENDOR, + AVP_TYPE_INTEGER32 // This MUST be the same as parent type's + }; + + -- Create an AVP with a base type -- + ret = dict_new ( DICT_AVP, &user_name_data, NULL, &user_name_avp ); + + -- Create an AVP with a derived type -- + ret = dict_search ( DICT_TYPE, TYPE_BY_NAME, "Boolean", &boolean_type, ENOENT); + ret = dict_new ( DICT_AVP, &sample_boolean_data , boolean_type, &sample_boolean_avp ); + + } + +- dict_search: + Sample code to look for an AVP + { + int ret; + struct dict_object * avp_username; + struct dict_object * avp_sampleboolean; + struct dict_avp_request avpvendorboolean = + { + .avp_vendor = 23455, + .avp_name = "Sample-Boolean" + }; + + ret = dict_search ( DICT_AVP, AVP_BY_NAME, "User-Name", &avp_username, ENOENT); + + ret = dict_search ( DICT_AVP, AVP_BY_NAME_AND_VENDOR, &avpvendorboolean, &avp_sampleboolean, ENOENT); + + } + + - dict_getval: + Sample code to retrieve the data from an AVP object: + { + int ret; + struct dict_object * avp_username; + struct dict_avp_data user_name_data; + ret = dict_search ( DICT_AVP, AVP_BY_NAME, "User-Name", &avp_username, ENOENT); + ret = dict_getval ( avp_username, &user_name_data ); + printf("User-Name code: %d\n", user_name_data.avp_code ); + } + +*/ + +/* + *************************************************************************** + * + * Command object + * + * These types are used to manage commands objects in the dictionary + * + *************************************************************************** + */ + +/* Type to hold a Diameter command code: IANA assigned values. 0x0-0x7fffff=standard, 0x800000-0xfffffd=vendors, 0xfffffe-0xffffff=experimental */ +typedef uint32_t command_code_t; + +/* Values of command flags */ +#define CMD_FLAG_REQUEST 0x80 +#define CMD_FLAG_PROXIABLE 0x40 +#define CMD_FLAG_ERROR 0x20 +#define CMD_FLAG_RETRANSMIT 0x10 +#define CMD_FLAG_RESERVED5 0x08 +#define CMD_FLAG_RESERVED6 0x04 +#define CMD_FLAG_RESERVED7 0x02 +#define CMD_FLAG_RESERVED8 0x01 + +/* Type to hold data associated to a command */ +struct dict_cmd_data { + command_code_t cmd_code; /* code of the command */ + char *cmd_name; /* Name of the command */ + uint8_t cmd_flag_mask; /* Mask of fixed-value flags */ + uint8_t cmd_flag_val; /* values of the fixed flags */ +}; + +/* The criteria for searching an avp object in the dictionary */ +enum { + CMD_BY_NAME = 60, /* "what" points to a string */ + CMD_BY_CODE_R, /* "what" points to a command_code_t. The "Request" command is returned. */ + CMD_BY_CODE_A, /* "what" points to a command_code_t. The "Answer" command is returned. */ + CMD_ANSWER /* "what" points to a struct dict_object of a request command. The corresponding "Answer" command is returned. */ +}; + + +/*** + * API usage : + +The "parent" parameter of dict_new may point to an application object to inform of what application defines the command. +The application associated to a command is retrieved with APPLICATION_OF_COMMAND search criteria on applications. + +To create the rules for children of commands, see the DICT_RULE related part. + +Note that the "Request" and "Answer" commands are two independant objects. This allows to have different rules for each. + +- dict_new: + Sample code for command creation: + { + int ret; + struct dict_object * cer; + struct dict_object * cea; + struct dict_cmd_data ce_data = { + 257, // code + "Capabilities-Exchange-Request", // name + CMD_FLAG_REQUEST, // mask + CMD_FLAG_REQUEST // value. Only the "R" flag is constrained here, set. + }; + + ret = dict_new ( DICT_COMMAND, &ce_data, NULL, &cer ); + + ce_data.cmd_name = "Capabilities-Exchange-Answer"; + ce_data.cmd_flag_val = 0; // Same constraint on "R" flag, but this time it must be cleared. + + ret = dict_new ( DICT_COMMAND, &ce_data, NULL, &cea ); + } + +- dict_search: + Sample code to look for a command + { + int ret; + struct dict_object * cer, * cea; + command_code_t code = 257; + ret = dict_search ( DICT_COMMAND, CMD_BY_NAME, "Capabilities-Exchange-Request", &cer, ENOENT); + ret = dict_search ( DICT_COMMAND, CMD_BY_CODE_R, &code, &cer, ENOENT); + } + + - dict_getval: + Sample code to retrieve the data from a command object: + { + int ret; + struct dict_object * cer; + struct dict_object * cea; + struct dict_cmd_data cea_data; + ret = dict_search ( DICT_COMMAND, CMD_BY_NAME, "Capabilities-Exchange-Request", &cer, ENOENT); + ret = dict_search ( DICT_COMMAND, CMD_ANSWER, cer, &cea, ENOENT); + ret = dict_getval ( cea, &cea_data ); + printf("Answer to CER: %s\n", cea_data.cmd_name ); + } + +*/ + +/* + *************************************************************************** + * + * Rule object + * + * These objects are used to manage rules in the dictionary (ABNF implementation) + * This is used for checking messages validity (more powerful than a DTD) + * + *************************************************************************** + */ + +/* This defines the kind of rule that is defined */ +enum rule_position { + RULE_FIXED_HEAD = 1, /* The AVP must be at the head of the group. The rule_order field is used to specify the position. */ + RULE_REQUIRED, /* The AVP must be present in the parent, but its position is not defined. */ + RULE_OPTIONAL, /* The AVP may be present in the message. Used to specify a max number of occurences for example */ + RULE_FIXED_TAIL /* The AVP must be at the end of the group. The rule_order field is used to specify the position. */ +}; + +/* Content of a RULE object data */ +struct dict_rule_data { + struct dict_object *rule_avp; /* Pointer to the AVP object that is concerned by this rule */ + enum rule_position rule_position; /* The position in which the rule_avp must appear in the parent */ + unsigned rule_order; /* for RULE_FIXED_* rules, the place. 1,2,3.. for HEAD rules; ...,3,2,1 for TAIL rules. */ + int rule_min; /* Minimum number of occurences. -1 means "default": 0 for optional rules, 1 for other rules */ + int rule_max; /* Maximum number of occurences. -1 means no maximum. 0 means the AVP is forbidden. */ +}; + +/* The criteria for searching a rule in the dictionary */ +enum { + RULE_BY_AVP_AND_PARENT = 70 /* "what" points to a struct dict_rule_request -- see bellow. This is used to query "what is the rule for this AVP in this group?" */ +}; + +/* Structure for querying the dictionary about a rule */ +struct dict_rule_request { + struct dict_object *rule_parent; /* The grouped avp or command to which the rule apply */ + struct dict_object *rule_avp; /* The AVP concerned by this rule */ +}; + + +/*** + * API usage : + +The "parent" parameter can not be NULL. It points to the object (grouped avp or command) to which this rule apply (i.e. for which the ABNF is defined). + +- dict_new: + Sample code for rule creation. Let's create the Proxy-Info grouped AVP for example. + { + int ret; + struct dict_object * proxy_info_avp; + struct dict_object * proxy_host_avp; + struct dict_object * proxy_state_avp; + struct dict_object * diameteridentity_type; + struct dict_rule_data rule_data; + struct dict_type_data di_type_data = { AVP_TYPE_OCTETSTRING, "DiameterIdentity", NULL, NULL }; + struct dict_avp_data proxy_info_data = { 284, 0, "Proxy-Info", AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, AVP_FLAG_MANDATORY, AVP_TYPE_GROUPED }; + struct dict_avp_data proxy_host_data = { 280, 0, "Proxy-Host", AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, AVP_FLAG_MANDATORY, AVP_TYPE_OCTETSTRING }; + struct dict_avp_data proxy_state_data = { 33, 0, "Proxy-State",AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, AVP_FLAG_MANDATORY, AVP_TYPE_OCTETSTRING }; + + -- Create the parent AVP + ret = dict_new ( DICT_AVP, &proxy_info_data, NULL, &proxy_info_avp ); + + -- Create the first child AVP. + ret = dict_new ( DICT_TYPE, &di_type_data, NULL, &diameteridentity_type ); + ret = dict_new ( DICT_AVP, &proxy_host_data, diameteridentity_type, &proxy_host_avp ); + + -- Create the other child AVP + ret = dict_new ( DICT_AVP, &proxy_state_data, NULL, &proxy_state_avp ); + + -- Now we can create the rules. Both children AVP are mandatory. + rule_data.rule_position = RULE_REQUIRED; + rule_data.rule_min = -1; + rule_data.rule_max = -1; + + rule_data.rule_avp = proxy_host_avp; + ret = dict_new ( DICT_RULE, &rule_data, proxy_info_avp, NULL ); + + rule_data.rule_avp = proxy_state_avp; + ret = dict_new ( DICT_RULE, &rule_data, proxy_info_avp, NULL ); +} + +- dict_search and dict_getval are similar to previous examples. + +*/ + +/* Define some hard-coded values */ +/* Commands Codes */ +#define CC_CAPABILITIES_EXCHANGE 257 +#define CC_RE_AUTH 258 +#define CC_ACCOUNTING 271 +#define CC_ABORT_SESSION 274 +#define CC_SESSION_TERMINATION 275 +#define CC_DEVICE_WATCHDOG 280 +#define CC_DISCONNECT_PEER 282 + +/* AVPs (Vendor 0) */ +#define AC_PROXY_STATE 33 +#define AC_HOST_IP_ADDRESS 257 +#define AC_AUTH_APPLICATION_ID 258 +#define AC_ACCT_APPLICATION_ID 259 +#define AC_VENDOR_SPECIFIC_APPLICATION_ID 260 +#define AC_REDIRECT_HOST_USAGE 261 +#define AC_REDIRECT_MAX_CACHE_TIME 262 +#define AC_SESSION_ID 263 +#define AC_ORIGIN_HOST 264 +#define AC_SUPPORTED_VENDOR_ID 265 +#define AC_VENDOR_ID 266 +#define AC_FIRMWARE_REVISION 267 +#define AC_RESULT_CODE 268 +#define AC_PRODUCT_NAME 269 +#define AC_DISCONNECT_CAUSE 273 +#define ACV_DC_REBOOTING 0 +#define ACV_DC_BUSY 1 +#define ACV_DC_NOT_FRIEND 2 +#define AC_ORIGIN_STATE_ID 278 +#define AC_FAILED_AVP 279 +#define AC_PROXY_HOST 280 +#define AC_ERROR_MESSAGE 281 +#define AC_ROUTE_RECORD 282 +#define AC_DESTINATION_REALM 283 +#define AC_PROXY_INFO 284 +#define AC_REDIRECT_HOST 292 +#define AC_DESTINATION_HOST 293 +#define AC_ERROR_REPORTING_HOST 294 +#define AC_ORIGIN_REALM 296 +#define AC_INBAND_SECURITY_ID 299 + +/* Error codes */ +#define ER_DIAMETER_SUCCESS 2001 +#define ER_DIAMETER_REALM_NOT_SERVED 3003 +#define ER_DIAMETER_TOO_BUSY 3004 +#define ER_DIAMETER_REDIRECT_INDICATION 3006 + +/* Iterator on the rules of a parent object */ +int fd_dict_iterate_rules ( struct dict_object *parent, void * data, int (*cb)(void *, struct dict_rule_data *) ); + + +/*============================================================*/ +/* MESSAGES */ +/*============================================================*/ + +/* The following types are opaque */ +struct msg; /* A message: command with children AVPs (possibly grand children) */ +struct avp; /* AVP object */ + +/* Some details about chaining: + * + * A message is made of a header ( msg ) and 0 or more AVPs ( avp ). + * The structure is a kind of tree, where some AVPs (grouped AVPs) can contain other AVPs. + * Exemple: + * msg + * |-avp + * |-gavp + * | |-avp + * | |-avp + * | \-avp + * |-avp + * \-avp + * + */ + +/* The following type is used to point to either a msg or an AVP */ +typedef void msg_or_avp; + +/* The Diameter protocol version */ +#define DIAMETER_VERSION 1 + +/* In the two following types, some fields are marked (READONLY). + * This means that the content of these fields will be overwritten by the daemon so modifying it is useless. + */ + +/* The following structure represents the header of a message. All data is in host byte order. */ +struct msg_hdr { + uint8_t msg_version; /* (READONLY) Version of Diameter: must be DIAMETER_VERSION. */ + uint32_t msg_length; /* (READONLY)(3 bytes) indicates the length of the message */ + uint8_t msg_flags; /* Message flags: CMD_FLAG_* */ + command_code_t msg_code; /* (3 bytes) the command-code. See dictionary-api.h for more detail */ + application_id_t msg_appl; /* The application issuing this message */ + uint32_t msg_hbhid; /* The Hop-by-Hop identifier of the message */ + uint32_t msg_eteid; /* The End-to-End identifier of the message */ +}; + +/* The following structure represents the visible content of an AVP. All data is in host byte order. */ +struct avp_hdr { + avp_code_t avp_code; /* the AVP Code */ + uint8_t avp_flags; /* AVP_FLAG_* flags */ + uint32_t avp_len; /* (READONLY)(Only 3 bytes are used) the length of the AVP as described in the RFC */ + vendor_id_t avp_vendor; /* Only used if AVP_FLAG_VENDOR is present */ + union avp_value *avp_value; /* pointer to the value of the AVP. NULL means that the value is not set / not understood. + One should not directly change this value. Use the msg_avp_setvalue function instead. + The content of the pointed structure can be changed directly, with this restriction: + if the AVP is an OctetString, and you change the value of the pointer avp_value->os.data, then + you must call free() on the previous value, and the new one must be free()-able. + */ +}; + +/* The following enum is used to browse inside message hierarchy (msg, gavp, avp) */ +enum msg_brw_dir { + MSG_BRW_NEXT = 1, /* Get the next element at the same level, or NULL if this is the last element. */ + MSG_BRW_PREV, /* Get the previous element at the same level, or NULL if this is the first element. */ + MSG_BRW_FIRST_CHILD, /* Get the first child AVP of this element, if any. */ + MSG_BRW_LAST_CHILD, /* Get the last child AVP of this element, if any. */ + MSG_BRW_PARENT, /* Get the parent element of this element, if any. Only the msg_t object has no parent. */ + MSG_BRW_WALK /* This is equivalent to FIRST_CHILD or NEXT or PARENT->next, first that is not NULL. Use this to walk inside all AVPs. */ +}; + +/* Some flags used in the functions bellow */ +#define MSGFL_ALLOC_ETEID 0x01 /* When creating a message, a new end-to-end ID is allocated and set in the message */ +#define MSGFL_ANSW_ERROR 0x02 /* When creating an answer message, set the 'E' bit and use the generic error ABNF instead of command-specific ABNF */ +#define MSGFL_MAX MSGFL_ANSW_ERROR /* The biggest valid flag value */ + +/**************************************************/ +/* Message creation, manipulation, disposal */ +/**************************************************/ +/* + * FUNCTION: fd_msg_avp_new + * + * PARAMETERS: + * model : Pointer to a DICT_AVP dictionary object describing the avp to create, or NULL. + * flags : Flags to use in creation (not used yet, should be 0). + * avp : Upon success, pointer to the new avp is stored here. + * + * DESCRIPTION: + * Create a new AVP instance. + * + * RETURN VALUE: + * 0 : The AVP is created. + * EINVAL : A parameter is invalid. + * (other standard errors may be returned, too, with their standard meaning. Example: + * ENOMEM : Memory allocation for the new avp failed.) + */ +int fd_msg_avp_new ( struct dict_object * model, int flags, struct avp ** avp ); + +/* + * FUNCTION: fd_msg_new + * + * PARAMETERS: + * model : Pointer to a DICT_COMMAND dictionary object describing the message to create, or NULL. + * flags : combination of MSGFL_* flags. + * msg : Upon success, pointer to the new message is stored here. + * + * DESCRIPTION: + * Create a new empty Diameter message. + * + * RETURN VALUE: + * 0 : The message is created. + * EINVAL : A parameter is invalid. + * (other standard errors may be returned, too, with their standard meaning. Example: + * ENOMEM : Memory allocation for the new message failed.) + */ +int fd_msg_new ( struct dict_object * model, int flags, struct msg ** msg ); + +/* + * FUNCTION: msg_new_answer_from_req + * + * PARAMETERS: + * dict : Pointer to the dictionary containing the model of the query. + * msg : The location of the query on function call. Updated by the location of answer message on return. + * flag : Pass MSGFL_ANSW_ERROR to indicate if the answer is an error message (will set the 'E' bit) + * + * DESCRIPTION: + * This function creates the empty answer message corresponding to a request. + * The header is set properly (R flag, ccode, appid, hbhid, eteid) + * The Session-Id AVP is copied if present. + * The calling code should usually call fd_msg_rescode_set function on the answer. + * Upon return, the original query may be retrieved by calling fd_msg_answ_getq on the message. + * + * RETURN VALUE: + * 0 : Operation complete. + * !0 : an error occurred. + */ +int fd_msg_new_answer_from_req ( struct dictionary * dict, struct msg ** msg, int flag ); + +/* + * FUNCTION: fd_msg_browse + * + * PARAMETERS: + * reference : Pointer to a struct msg or struct avp. + * dir : Direction for browsing + * found : If not NULL, updated with the element that has been found, if any, or NULL if no element was found / an error occurred. + * depth : If not NULL, points to an integer representing the "depth" of this object in the tree. This is a relative value, updated on return. + * + * DESCRIPTION: + * Explore the content of a message object (hierarchy). If "found" is null, only error checking is performed. + * If "depth" is provided, it is updated as follow on successful function return: + * - not modified for MSG_BRW_NEXT and MSG_BRW_PREV. + * - *depth = *depth + 1 for MSG_BRW_FIRST_CHILD and MSG_BRW_LAST_CHILD. + * - *depth = *depth - 1 for MSG_BRW_PARENT. + * - *depth = *depth + X for MSG_BRW_WALK, with X between 1 (returned the 1st child) and -N (returned the Nth parent's next). + * + * RETURN VALUE: + * 0 : found has been updated (if non NULL). + * EINVAL : A parameter is invalid. + * ENOENT : No element has been found where requested, and "found" was NULL (otherwise, *found is set to NULL and 0 is returned). + */ +int fd_msg_browse_internal ( msg_or_avp * reference, enum msg_brw_dir dir, msg_or_avp ** found, int * depth ); +/* Macro to avoid having to cast the third parameter everywhere */ +#define fd_msg_browse( ref, dir, found, depth ) \ + fd_msg_browse_internal( (ref), (dir), (void *)(found), (depth) ) + + +/* + * FUNCTION: fd_msg_avp_add + * + * PARAMETERS: + * reference : Pointer to a valid msg or avp. + * dir : location where the new AVP should be inserted, relative to the reference. MSG_BRW_PARENT and MSG_BRW_WALK are not valid. + * avp : pointer to the AVP object that must be inserted. + * + * DESCRIPTION: + * Adds an AVP into an object that can contain it: grouped AVP or message. + * + * RETURN VALUE: + * 0 : The AVP has been added. + * EINVAL : A parameter is invalid. + */ +int fd_msg_avp_add ( msg_or_avp * reference, enum msg_brw_dir dir, struct avp *avp); + +/* + * FUNCTION: fd_msg_search_avp + * + * PARAMETERS: + * msg : The message structure in which to search the AVP. + * what : The dictionary model of the AVP to search. + * avp : location where the AVP reference is stored if found. + * + * DESCRIPTION: + * Search the first top-level AVP of a given model inside a message. + * Note: only the first instance of the AVP is returned by this function. + * Note: only top-level AVPs are searched, not inside grouped AVPs. + * Use msg_browse if you need more advanced research features. + * + * RETURN VALUE: + * 0 : The AVP has been found. + * EINVAL : A parameter is invalid. + * ENOENT : No AVP has been found, and "avp" was NULL (otherwise, *avp is set to NULL and 0 returned). + */ +int fd_msg_search_avp ( struct msg * msg, struct dict_object * what, struct avp ** avp ); + +/* + * FUNCTION: fd_msg_free + * + * PARAMETERS: + * object : pointer to the message or AVP object that must be unlinked and freed. + * + * DESCRIPTION: + * Unlink and free a message or AVP object and its children. + * If the object is an AVP linked into a message, the AVP is removed before being freed. + * + * RETURN VALUE: + * 0 : The message has been freed. + * EINVAL : A parameter is invalid. + */ +int fd_msg_free ( msg_or_avp * object ); + +/***************************************/ +/* Dump functions */ +/***************************************/ +/* + * FUNCTION: fd_msg_dump_* + * + * PARAMETERS: + * level : the log level (INFO, FULL, ...) at which the object is dumped + * obj : A msg or avp object. + * + * DESCRIPTION: + * These functions dump the content of a message to the debug log + * either recursively or only the object itself. + * + * RETURN VALUE: + * - + */ +void fd_msg_dump_walk ( int level, msg_or_avp *obj ); +void fd_msg_dump_one ( int level, msg_or_avp *obj ); + + +/*********************************************/ +/* Message metadata management functions */ +/*********************************************/ +/* + * FUNCTION: fd_msg_model + * + * PARAMETERS: + * reference : Pointer to a valid msg or avp. + * model : on success, pointer to the dictionary model of this command or AVP. NULL if the model is unknown. + * + * DESCRIPTION: + * Retrieve the dictionary object describing this message or avp. If the object is unknown or the fd_msg_parse_dict has not been called, + * *model is set to NULL. + * + * RETURN VALUE: + * 0 : The model has been set. + * EINVAL : A parameter is invalid. + */ +int fd_msg_model ( msg_or_avp * reference, struct dict_object ** model ); + +/* + * FUNCTION: fd_msg_hdr + * + * PARAMETERS: + * msg : Pointer to a valid message object. + * pdata : Upon success, pointer to the msg_hdr structure of this message. The fields may be modified. + * + * DESCRIPTION: + * Retrieve location of modifiable section of a message. + * + * RETURN VALUE: + * 0 : The location has been written. + * EINVAL : A parameter is invalid. + */ +int fd_msg_hdr ( struct msg *msg, struct msg_hdr **pdata ); + +/* + * FUNCTION: fd_msg_avp_hdr + * + * PARAMETERS: + * avp : Pointer to a valid avp object. + * pdata : Upon success, pointer to the avp_hdr structure of this avp. The fields may be modified. + * + * DESCRIPTION: + * Retrieve location of modifiable data of an avp. + * + * RETURN VALUE: + * 0 : The location has been written. + * EINVAL : A parameter is invalid. + */ +int fd_msg_avp_hdr ( struct avp *avp, struct avp_hdr **pdata ); + +/* + * FUNCTION: fd_msg_answ_associate, fd_msg_answ_getq, fd_msg_answ_detach + * + * PARAMETERS: + * answer : the received answer message + * query : the corresponding query that had been sent + * + * DESCRIPTION: + * fd_msg_answ_associate associates a query msg with the received answer. + * Query is retrieved with fd_msg_answ_getq. + * If answer message is freed, the query is also freed. + * If the msg_answ_detach function is called, the association is removed. + * This is meant to be called from the daemon only. + * + * RETURN VALUE: + * 0 : ok + * EINVAL: a parameter is invalid + */ +int fd_msg_answ_associate( struct msg * answer, struct msg * query ); +int fd_msg_answ_getq ( struct msg * answer, struct msg ** query ); +int fd_msg_answ_detach ( struct msg * answer ); + +/* + * FUNCTION: fd_msg_anscb_associate, fd_msg_anscb_get + * + * PARAMETERS: + * msg : the answer message + * anscb : the callback to associate with the message + * data : the data to pass to the callback + * + * DESCRIPTION: + * Associate or retrieve a callback with an answer message. + * This is meant to be called from the daemon only. + * + * RETURN VALUE: + * 0 : ok + * EINVAL: a parameter is invalid + */ +int fd_msg_anscb_associate( struct msg * msg, void ( *anscb)(void *, struct msg **), void * data ); +int fd_msg_anscb_get ( struct msg * msg, void (**anscb)(void *, struct msg **), void ** data ); + +/* + * FUNCTION: fd_msg_rt_associate, fd_msg_rt_get + * + * PARAMETERS: + * msg : the query message to be sent + * list : the ordered list of possible next-peers + * + * DESCRIPTION: + * Associate a routing list with a query, and retrieve it. + * If the message is freed, the list is also freed. + * + * RETURN VALUE: + * 0 : ok + * EINVAL: a parameter is invalid + */ +int fd_msg_rt_associate( struct msg * msg, struct fd_list ** list ); +int fd_msg_rt_get ( struct msg * msg, struct fd_list ** list ); + +/* + * FUNCTION: fd_msg_is_routable + * + * PARAMETERS: + * msg : A msg object. + * + * DESCRIPTION: + * This function returns a boolean telling if a given message is routable in the Diameter network, + * or if it is a local link message only (ex: CER/CEA, DWR/DWA, ...). + * + * RETURN VALUE: + * 0 : The message is not routable / an error occurred. + * 1 : The message is routable. + */ +int fd_msg_is_routable ( struct msg * msg ); + +/* + * FUNCTION: fd_msg_source_(g/s)et + * + * PARAMETERS: + * msg : A msg object. + * diamid : The diameter id of the peer from which this message was received. + * hash : The hash for the diamid value. + * add_rr : if true, a Route-Record AVP is added to the message with content diamid. In that case, dict must be supplied. + * dict : a dictionary with definition of Route-Record AVP (if add_rr is true) + * + * DESCRIPTION: + * Store or retrieve the diameted id of the peer from which this message was received. + * Will be used for example by the routing module to add the Route-Record AVP in forwarded requests, + * or to direct answers to the appropriate peer. + * + * RETURN VALUE: + * 0 : Operation complete. + * !0 : an error occurred. + */ +int fd_msg_source_set( struct msg * msg, char * diamid, uint32_t hash, int add_rr, struct dictionary * dict ); +int fd_msg_source_get( struct msg * msg, char ** diamid, uint32_t *hash ); + +/* + * FUNCTION: fd_msg_eteid_get + * + * PARAMETERS: + * - + * + * DESCRIPTION: + * Get a new unique end-to-end id value for the local peer. + * + * RETURN VALUE: + * The new assigned value. No error code is defined. + */ +uint32_t fd_msg_eteid_get ( void ); + + +/***************************************/ +/* Manage AVP values */ +/***************************************/ + +/* + * FUNCTION: fd_msg_avp_setvalue + * + * PARAMETERS: + * avp : Pointer to a valid avp object with a NULL avp_value pointer. The model must be known. + * value : pointer to an avp_value. The content will be COPIED into the internal storage area. + * If data type is an octetstring, the data is also copied. + * If value is a NULL pointer, the previous data is erased and value is unset in the AVP. + * + * DESCRIPTION: + * Initialize the avp_value field of an AVP header. + * + * RETURN VALUE: + * 0 : The avp_value pointer has been set. + * EINVAL : A parameter is invalid. + */ +int fd_msg_avp_setvalue ( struct avp *avp, union avp_value *value ); + +/* + * FUNCTION: fd_msg_avp_value_encode + * + * PARAMETERS: + * avp : Pointer to a valid avp object with a NULL avp_value. The model must be known. + * data : Pointer to the data that must be encoded as AVP value and stored in the AVP. + * This is only valid for AVPs of derived type for which type_data_encode callback is set. (ex: Address type) + * + * DESCRIPTION: + * Initialize the avp_value field of an AVP object from formatted data, using the AVP's type "type_data_encode" callback. + * + * RETURN VALUE: + * 0 : The avp_value has been set. + * EINVAL : A parameter is invalid. + * ENOTSUP : There is no appropriate callback registered with this AVP's type. + */ +int fd_msg_avp_value_encode ( void *data, struct avp *avp ); + +/* + * FUNCTION: fd_msg_avp_value_interpret + * + * PARAMETERS: + * avp : Pointer to a valid avp object with a non-NULL avp_value value. + * data : Upon success, formatted interpretation of the AVP value is stored here. + * + * DESCRIPTION: + * Interpret the content of an AVP of Derived type and store the result in data pointer. The structure + * of the data pointer is dependent on the AVP type. This function calls the "type_data_interpret" callback + * of the type. + * + * RETURN VALUE: + * 0 : The avp_value has been set. + * EINVAL : A parameter is invalid. + * ENOTSUP : There is no appropriate callback registered with this AVP's type. + */ +int fd_msg_avp_value_interpret ( struct avp *avp, void *data ); + + +/***************************************/ +/* Message parsing functions */ +/***************************************/ + +/* + * FUNCTION: fd_msg_bufferize + * + * PARAMETERS: + * msg : A valid msg object. All AVPs must have a value set. + * buffer : Upon success, this points to a buffer (malloc'd) containing the message ready for network transmission (or security transformations). + * The buffer may be freed after use. + * len : if not NULL, the size of the buffer is written here. In any case, this size is updated in the msg header. + * + * DESCRIPTION: + * Renders a message in memory as a buffer that can be sent over the network to the next peer. + * + * RETURN VALUE: + * 0 : The location has been written. + * EINVAL : The buffer does not contain a valid Diameter message. + * ENOMEM : Unable to allocate enough memory to create the buffer object. + */ +int fd_msg_bufferize ( struct msg * msg, unsigned char ** buffer, size_t * len ); + +/* + * FUNCTION: fd_msg_parse_buffer + * + * PARAMETERS: + * buffer : Pointer to a buffer containing a message received from the network. + * buflen : the size in bytes of the buffer. + * msg : Upon success, this points to a valid msg object. No AVP value is resolved in this object, nor grouped AVP. + * + * DESCRIPTION: + * This function parses a buffer an creates a msg object to represent the structure of the message. + * Since no dictionary lookup is performed, the values of the AVPs are not interpreted. To interpret the values, + * the returned message object must be passed to fd_msg_parse_dict function. + * The buffer pointer is saved inside the message and will be freed when not needed anymore. + * + * RETURN VALUE: + * 0 : The location has been written. + * ENOMEM : Unable to allocate enough memory to create the msg object. + * EBADMSG : The buffer does not contain a valid Diameter message (or is truncated). + * EINVAL : A parameter is invalid. + */ +int fd_msg_parse_buffer ( unsigned char ** buffer, size_t buflen, struct msg ** msg ); + +/* + * FUNCTION: fd_msg_parse_dict + * + * PARAMETERS: + * object : A msg or AVP object as returned by fd_msg_parse_buffer. + * dict : the dictionary containing the objects definitions to use for resolving all AVPs. + * + * DESCRIPTION: + * This function looks up for the command and each children AVP definitions in the dictionary. + * If the dictionary definition is found, avp_model is set and the value of the AVP is interpreted accordingly and: + * - for grouped AVPs, the children AVP are created and interpreted also. + * - for numerical AVPs, the value is converted to host byte order and saved in the avp_value field. + * - for octetstring AVPs, the string is copied into a new buffer and its address is saved in avp_value. + * If the dictionary definition is not found, avp_model is set to NULL and + * the content of the AVP is saved as an octetstring in an internal structure. avp_value is NULL. + * As a result, after this function has been called, there is no more dependency of the msg object to the message buffer, that is be freed. + * + * RETURN VALUE: + * 0 : The message has been fully parsed as described. + * EINVAL : The msg parameter is invalid for this operation. + * ENOMEM : Unable to allocate enough memory to complete the operation. + * ENOTSUP : No dictionary definition for the command or one of the mandatory AVP was found. + */ +int fd_msg_parse_dict ( msg_or_avp * object, struct dictionary * dict ); + +/* + * FUNCTION: fd_msg_parse_rules + * + * PARAMETERS: + * object : A msg or grouped avp object that must be verified. + * dict : The dictionary containing the rules definitions. + * rule : If not NULL, the first conflicting rule will be saved here if a conflict is found. + * + * DESCRIPTION: + * Check that the children of the object do not conflict with the dictionary rules (ABNF compliance). + * + * RETURN VALUE: + * 0 : The message has been fully parsed and complies to the defined rules. + * EBADMSG : A conflict was detected, or a mandatory AVP is unknown in the dictionary. + * EINVAL : The msg or avp object is invalid for this operation. + * ENOMEM : Unable to allocate enough memory to complete the operation. + */ +int fd_msg_parse_rules ( msg_or_avp * object, struct dictionary * dict, struct dict_object ** rule); + + +/* + * FUNCTION: fd_msg_update_length + * + * PARAMETERS: + * object : Pointer to a valid msg or avp. + * + * DESCRIPTION: + * Update the length field of the object passed as parameter. + * As a side effect, all children objects are also updated. Therefore, all avp_value fields of + * the children AVPs must be set, or an error will occur. + * + * RETURN VALUE: + * 0 : The size has been recomputed. + * EINVAL : A parameter is invalid. + */ +int fd_msg_update_length ( msg_or_avp * object ); + + + +/*============================================================*/ +/* MESSAGE QUEUES */ +/*============================================================*/ + +/* Management of queues of messages */ + +/* A message queue is an opaque object */ +struct mqueue; + +/* + * FUNCTION: fd_mq_new + * + * PARAMETERS: + * queue : Upon success, a pointer to the new message queue is saved here. + * + * DESCRIPTION: + * Create a new empty message queue. + * + * RETURN VALUE : + * 0 : The message queue has been initialized successfully. + * EINVAL : The parameter is invalid. + * ENOMEM : Not enough memory to complete the creation. + */ +int fd_mq_new ( struct mqueue ** queue ); + +/* + * FUNCTION: fd_mq_del + * + * PARAMETERS: + * queue : Pointer to an empty message queue to delete. + * + * DESCRIPTION: + * Destroys a message queue. This is only possible if no thread is waiting for a message, + * and the queue is empty. + * + * RETURN VALUE: + * 0 : The message queue has been destroyed successfully. + * EINVAL : The parameter is invalid. + */ +int fd_mq_del ( struct mqueue ** queue ); + +/* + * FUNCTION: fd_mq_length + * + * PARAMETERS: + * queue : The queue from which to retrieve the length. + * length : Upon success, the current number of messages in the queue is stored here. + * + * DESCRIPTION: + * Retrieve the number of messages pending in a queue. + * + * RETURN VALUE: + * 0 : The length of the queue has been written. + * EINVAL : A parameter is invalid. + */ +int fd_mq_length ( struct mqueue * queue, int * length ); +int fd_mq_length_noerr ( struct mqueue * queue ); /* alternate with no error checking */ + +/* + * FUNCTION: fd_mq_setthrhd + * + * PARAMETERS: + * queue : The queue for which the thresholds are being set. + * data : An opaque pointer that is passed to h_cb and l_cb callbacks. + * high : The high-level threshold. If the number of elements in the queue increase to this value, h_cb is called. + * h_cb : if not NULL, a callback to call when the queue lengh is bigger than "high". + * low : The low-level threshold. Must be < high. + * l_cb : If the number of elements decrease to low, this callback is called. + * + * DESCRIPTION: + * This function allows to adjust the number of producer / consumer threads of a queue. + * If the consumer are slower than the producers, the number of messages in the queue increase. + * By setting a "high" value, we allow a callback to be called when this number is too high. + * The typical use would be to create an additional consumer thread in this callback. + * If the queue continues to grow, the callback will be called again when the length is 2 * high, then 3*high, ... N * high + * (the callback itself may implement a limit on the number of consumers that can be created) + * When the queue starts to decrease, and the number of elements go under ((N - 1) * high + low, the l_cb callback is called + * and would typially stop one of the consumer threads. If the queue continue to reduce, l_cb is again called at (N-2)*high + low, + * and so on. + * + * Since there is no destructor for the data pointer, if cleanup operations are required, they should be performed in + * l_cb when the length of the queue is becoming < low. + * + * Note that the callbacks are called synchronously, during fd_mq_post or fd_mq_get. Their operation should be quick. + * + * RETURN VALUE: + * 0 : The thresholds have been set + * EINVAL : A parameter is invalid. + */ +int fd_mq_setthrhd ( struct mqueue * queue, void * data, uint16_t high, void (*h_cb)(struct mqueue *, void **), uint16_t low, void (*l_cb)(struct mqueue *, void **) ); + +/* + * FUNCTION: fd_mq_post + * + * PARAMETERS: + * queue : The queue in which the message must be posted. + * msg : The message that is put in the queue. + * + * DESCRIPTION: + * A message is added in a queue. Messages are retrieved from the queue (in FIFO order) + * with the fd_mq_get, fd_mq_tryget, or fd_mq_timedget functions. + * + * RETURN VALUE: + * 0 : The message is queued. + * EINVAL : A parameter is invalid. + * ENOMEM : Not enough memory to complete the operation. + */ +int fd_mq_post ( struct mqueue * queue, struct msg ** msg ); + +/* + * FUNCTION: fd_mq_get + * + * PARAMETERS: + * queue : The queue from which the message must be retrieved. + * msg : On return, the first message of the queue is stored here. + * + * DESCRIPTION: + * This function retrieves a message from a queue. If the queue is empty, the function will block the + * thread until a new message is posted to the queue, or until the thread is canceled (in which case the + * function does not return). + * + * RETURN VALUE: + * 0 : A new message has been retrieved. + * EINVAL : A parameter is invalid. + */ +int fd_mq_get ( struct mqueue * queue, struct msg ** msg ); + +/* + * FUNCTION: fd_mq_tryget + * + * PARAMETERS: + * queue : The queue from which the message must be retrieved. + * msg : On return, the message is stored here. + * + * DESCRIPTION: + * This function is similar to fd_mq_get, except that it will not block if + * the queue is empty, but return EWOULDBLOCK instead. + * + * RETURN VALUE: + * 0 : A new message has been retrieved. + * EINVAL : A parameter is invalid. + * EWOULDBLOCK : The queue was empty. + */ +int fd_mq_tryget ( struct mqueue * queue, struct msg ** msg ); + +/* + * FUNCTION: fd_mq_timedget + * + * PARAMETERS: + * queue : The queue from which the message must be retrieved. + * msg : On return, the message is stored here. + * abstime : the absolute time until which we allow waiting for a message. + * + * DESCRIPTION: + * This function is similar to fd_mq_get, except that it will block if the queue is empty + * only until the absolute time abstime (see pthread_cond_timedwait for + info). + * If the queue is still empty when the time expires, the function returns ETIMEDOUT + * + * RETURN VALUE: + * 0 : A new message has been retrieved. + * EINVAL : A parameter is invalid. + * ETIMEDOUT : The time out has passed and no message has been received. + */ +int fd_mq_timedget ( struct mqueue * queue, struct msg ** msg, const struct timespec *abstime ); + +#endif /* _LIBFREEDIAMETER_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libfreediameter/CMakeLists.txt Fri Aug 28 19:14:42 2009 +0900 @@ -0,0 +1,25 @@ +# Name of the subproject +Project("libfreediameter" C) + +SET(PROJECT_COPYRIGHT "Copyright (c) 2008-2009, WIDE Project (www.wide.ad.jp) and NICT (www.nict.go.jp)") + +# List of source files for the library +SET(LFD_SRC + libfd.h + init.c + log.c + lists.c + dictionary.c + messages.c + mqueues.c + ) + +# Build as a shared library +ADD_LIBRARY(libfreediameter SHARED ${LFD_SRC}) + +# Avoid the liblib name +SET_TARGET_PROPERTIES(libfreediameter PROPERTIES OUTPUT_NAME "freediameter") + +# The library itself needs other libraries +TARGET_LINK_LIBRARIES(libfreediameter ${FD_LIBS}) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libfreediameter/dictionary.c Fri Aug 28 19:14:42 2009 +0900 @@ -0,0 +1,1671 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis <sdecugis@nict.go.jp> * +* * +* Copyright (c) 2009, WIDE Project and NICT * +* All rights reserved. * +* * +* Redistribution and use of this software in source and binary forms, with or without modification, are * +* permitted provided that the following conditions are met: * +* * +* * Redistributions of source code must retain the above * +* copyright notice, this list of conditions and the * +* following disclaimer. * +* * +* * 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. * +* * +* * Neither the name of the WIDE Project or NICT nor the * +* names of its contributors may be used to endorse or * +* promote products derived from this software without * +* specific prior written permission of WIDE Project and * +* NICT. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. * +*********************************************************************************************************/ + +#include "libfd.h" + +/* Names of the base types */ +const char * type_base_name[] = { /* must keep in sync with dict_avp_basetype */ + "GROUPED", /* AVP_TYPE_GROUPED */ + "OCTETSTRING", /* AVP_TYPE_OCTETSTRING */ + "INTEGER32", /* AVP_TYPE_INTEGER32 */ + "INTEGER64", /* AVP_TYPE_INTEGER64 */ + "UNSIGNED32", /* AVP_TYPE_UNSIGNED32 */ + "UNSIGNED64", /* AVP_TYPE_UNSIGNED64 */ + "FLOAT32", /* AVP_TYPE_FLOAT32 */ + "FLOAT64" /* AVP_TYPE_FLOAT64 */ + }; + +/* The number of lists in an object */ +#define NB_LISTS_PER_OBJ 3 + +/* Some eye catchers definitions */ +#define OBJECT_EYECATCHER (0x0b13c7) +#define DICT_EYECATCHER (0x00d1c7) + +/* Definition of the dictionary objects */ +struct dict_object { + enum dict_object_type type; /* What type of object is this? */ + int objeyec;/* eyecatcher for this object */ + int typeyec;/* eyecatcher for this type of object */ + struct dictionary *dico; /* The dictionary this object belongs to */ + + union { + struct dict_vendor_data vendor; + struct dict_application_data application; + struct dict_type_data type; + struct dict_enumval_data enumval; + struct dict_avp_data avp; + struct dict_cmd_data cmd; + struct dict_rule_data rule; + } data; /* The data of this object */ + + struct dict_object * parent; /* The parent of this object, if any */ + + struct fd_list list[NB_LISTS_PER_OBJ];/* used to chain objects.*/ + /* More information about the lists : + + - the use for each list depends on the type of object. See detail bellow. + + - a sentinel for a list has its 'o' field cleared. (this is the criteria to detect end of a loop) + + - The lists are always ordered. The criteria are described bellow. the functions to order them are referenced in dict_obj_info + + - The dict_lock must be held for any list operation. + + => VENDORS: + list[0]: list of the vendors, ordered by their id. The sentinel is g_dict_vendors (vendor with id 0) + list[1]: sentinel for the list of AVPs from this vendor, ordered by AVP code. + list[2]: sentinel for the list of AVPs from this vendor, ordered by AVP name. + + => APPLICATIONS: + list[0]: list of the applications, ordered by their id. The sentinel is g_dict_applications (application with id 0) + list[1]: not used + list[2]: not used. + + => TYPES: + list[0]: list of the types, ordered by their names. The sentinel is g_list_types. + list[1]: sentinel for the type_enum list of this type, ordered by their constant name. + list[2]: sentinel for the type_enum list of this type, ordered by their constant value. + + => TYPE_ENUMS: + list[0]: list of the contants for a given type, ordered by the constant name. Sentinel is a (list[1]) element of a TYPE object. + list[1]: list of the contants for a given type, ordered by the constant value. Sentinel is a (list[2]) element of a TYPE object. + list[2]: not used + + => AVPS: + list[0]: list of the AVP from a given vendor, ordered by avp code. Sentinel is a list[1] element of a VENDOR object. + list[1]: list of the AVP from a given vendor, ordered by avp name. Sentinel is a list[2] element of a VENDOR object. + list[2]: sentinel for the rule list that apply to this AVP. + + => COMMANDS: + list[0]: list of the commands, ordered by their names. The sentinel is g_list_cmd_name. + list[1]: list of the commands, ordered by their command code and 'R' flag. The sentinel is g_list_cmd_code. + list[2]: sentinel for the rule list that apply to this command. + + => RULES: + list[0]: list of the rules for a given (grouped) AVP or Command, ordered by the AVP name to which they refer. sentinel is list[2] of a command or (grouped) avp. + list[1]: not used + list[2]: not used. + + */ + + /* Sentinel for the dispatch callbacks */ + struct fd_list disp_cbs; + +}; + +/* Definition of the dictionary structure */ +struct dictionary { + int dict_eyec; /* Eye-catcher for the dictionary (DICT_EYECATCHER) */ + + pthread_rwlock_t dict_lock; /* The global rwlock for the dictionary */ + + struct dict_object dict_vendors; /* Sentinel for the list of vendors, corresponding to vendor 0 */ + struct dict_object dict_applications; /* Sentinel for the list of applications, corresponding to app 0 */ + struct fd_list dict_types; /* Sentinel for the list of types */ + struct fd_list dict_cmd_name; /* Sentinel for the list of commands, ordered by names */ + struct fd_list dict_cmd_code; /* Sentinel for the list of commands, ordered by codes */ + + struct dict_object dict_cmd_error; /* Special command object for answers with the 'E' bit set */ + + int dict_count[DICT_TYPE_MAX]; /* Number of objects of each type */ +}; + +/* Forward declarations of dump functions */ +static void dump_vendor_data ( void * data ); +static void dump_application_data ( void * data ); +static void dump_type_data ( void * data ); + /* the dump function for enum has a different prototype since it need the datatype */ +static void dump_avp_data ( void * data ); +static void dump_command_data ( void * data ); +static void dump_rule_data ( void * data ); + +/* Forward declarations of search functions */ +static int search_vendor ( struct dictionary * dict, int criteria, void * what, struct dict_object **result ); +static int search_application ( struct dictionary * dict, int criteria, void * what, struct dict_object **result ); +static int search_type ( struct dictionary * dict, int criteria, void * what, struct dict_object **result ); +static int search_enumval ( struct dictionary * dict, int criteria, void * what, struct dict_object **result ); +static int search_avp ( struct dictionary * dict, int criteria, void * what, struct dict_object **result ); +static int search_cmd ( struct dictionary * dict, int criteria, void * what, struct dict_object **result ); +static int search_rule ( struct dictionary * dict, int criteria, void * what, struct dict_object **result ); + +/* The following array contains lot of data about the different types of objects, for automated handling */ +static struct { + enum dict_object_type type; /* information for this type */ + char * name; /* string describing this object, for debug */ + size_t datasize; /* The size of the data structure */ + int parent; /* 0: never; 1: may; 2: must */ + enum dict_object_type parenttype; /* The type of the parent, when relevant */ + int eyecatcher; /* A kind of signature for this object */ + void (*dump_data)(void * data ); /* The function to dump the data section */ + int (*search_fct)(struct dictionary * dict, int criteria, void * what, struct dict_object **result );; /* The function to search an object of this type */ + int haslist[NB_LISTS_PER_OBJ]; /* Tell if this list is used */ +} dict_obj_info[] = { { 0, "(error)", 0, 0, 0, 0, NULL, NULL, {0, 0, 0} } + + /* type name datasize parent parenttype + eyecatcher dump_data search_fct, haslist[] */ + + ,{ DICT_VENDOR, "VENDOR", sizeof(struct dict_vendor_data), 0, 0, + OBJECT_EYECATCHER + 1, dump_vendor_data, search_vendor, { 1, 0, 0 } } + + ,{ DICT_APPLICATION, "APPLICATION", sizeof(struct dict_application_data), 1, DICT_VENDOR, + OBJECT_EYECATCHER + 2, dump_application_data, search_application, { 1, 0, 0 } } + + ,{ DICT_TYPE, "TYPE", sizeof(struct dict_type_data), 1, DICT_APPLICATION, + OBJECT_EYECATCHER + 3, dump_type_data, search_type, { 1, 0, 0 } } + + ,{ DICT_ENUMVAL, "ENUMVAL", sizeof(struct dict_enumval_data), 2, DICT_TYPE, + OBJECT_EYECATCHER + 4, NULL, search_enumval, { 1, 1, 0 } } + + ,{ DICT_AVP, "AVP", sizeof(struct dict_avp_data), 1, DICT_TYPE, + OBJECT_EYECATCHER + 5, dump_avp_data, search_avp, { 1, 1, 0 } } + + ,{ DICT_COMMAND, "COMMAND", sizeof(struct dict_cmd_data), 1, DICT_APPLICATION, + OBJECT_EYECATCHER + 6, dump_command_data, search_cmd, { 1, 1, 0 } } + + ,{ DICT_RULE, "RULE", sizeof(struct dict_rule_data), 2, -1 /* special case: grouped avp or command */, + OBJECT_EYECATCHER + 7, dump_rule_data, search_rule, { 1, 0, 0 } } + +}; + +/* Macro to verify a "type" value */ +#define CHECK_TYPE( type ) ( ((type) > 0) && ((type) <= DICT_TYPE_MAX) ) + +/* Cast macro */ +#define _O( object ) ((struct dict_object *) (object)) + +/* Get information line for a given object */ +#define _OBINFO(object) (dict_obj_info[CHECK_TYPE(_O(object)->type) ? _O(object)->type : 0]) + + + + +/*******************************************************************************************************/ +/*******************************************************************************************************/ +/* */ +/* Objects management */ +/* */ +/*******************************************************************************************************/ +/*******************************************************************************************************/ + +/* Functions to manage the objects creation and destruction. */ + +/* Duplicate a string inplace */ +#define DUP_string( str ) { \ + char * __str = (str); \ + CHECK_MALLOC( (str) = strdup(__str) ); \ +} + +/* Initialize an object */ +static void init_object( struct dict_object * obj, enum dict_object_type type ) +{ + int i; + + TRACE_ENTRY("%p %d", obj, type); + + /* Clean the object first */ + memset ( obj, 0, sizeof(struct dict_object)); + + CHECK_PARAMS_DO( CHECK_TYPE(type), return ); + + obj->type = type; + obj->objeyec = OBJECT_EYECATCHER; + obj->typeyec = _OBINFO(obj).eyecatcher; + + /* We don't initialize the data nor the parent here */ + + /* Now init the lists */ + for (i=0; i<NB_LISTS_PER_OBJ; i++) { + if (_OBINFO(obj).haslist[i] != 0) + fd_list_init(&obj->list[i], obj); + else + fd_list_init(&obj->list[i], NULL); + } + + fd_list_init(&obj->disp_cbs, NULL); +} + +/* Initialize the "data" part of an object */ +static int init_object_data(void * dest, void * source, enum dict_object_type type) +{ + TRACE_ENTRY("%p %p %d", dest, source, type); + CHECK_PARAMS( dest && source && CHECK_TYPE(type) ); + + /* Generic: copy the full data structure */ + memcpy( dest, source, dict_obj_info[type].datasize ); + + /* Then strings must be duplicated, not copied */ + /* This function might be simplified by always defining the "name" field as the first field of the structures, but... it's error-prone */ + switch (type) { + case DICT_VENDOR: + DUP_string( ((struct dict_vendor_data *)dest)->vendor_name ); + break; + + case DICT_APPLICATION: + DUP_string( ((struct dict_application_data *)dest)->application_name ); + break; + + case DICT_TYPE: + DUP_string( ((struct dict_type_data *)dest)->type_name ); + break; + + case DICT_ENUMVAL: + DUP_string( ((struct dict_enumval_data *)dest)->enum_name ); + break; + + case DICT_AVP: + DUP_string( ((struct dict_avp_data *)dest)->avp_name ); + break; + + case DICT_COMMAND: + DUP_string( ((struct dict_cmd_data *)dest)->cmd_name ); + break; + + default: + /* Nothing to do for RULES */ + ; + } + + return 0; +} + +/* Check that an object is valid (1: OK, 0: error) */ +static int verify_object( struct dict_object * obj ) +{ + TRACE_ENTRY("%p", obj); + + CHECK_PARAMS_DO( obj + && (obj->objeyec == OBJECT_EYECATCHER) + && CHECK_TYPE(obj->type) + && (obj->typeyec == dict_obj_info[obj->type].eyecatcher), + { + if (obj) { + TRACE_DEBUG(FULL, "Invalid object : %p\n" + " obj->objeyec : %x / %x\n" + " obj->type : %d\n" + " obj->objeyec : %x / %x\n" + " obj->typeyec : %x / %x", + obj, + obj->objeyec, OBJECT_EYECATCHER, + obj->type, + obj->objeyec, OBJECT_EYECATCHER, + obj->typeyec, _OBINFO(obj).eyecatcher); + } + return 0; + } ); + + /* The object is probably valid. */ + return 1; +} + +/* Free the data associated to an object */ +static void destroy_object_data(struct dict_object * obj) +{ + /* TRACE_ENTRY("%p", obj); */ + + switch (obj->type) { + case DICT_VENDOR: + free( obj->data.vendor.vendor_name ); + break; + + case DICT_APPLICATION: + free( obj->data.application.application_name ); + break; + + case DICT_TYPE: + free( obj->data.type.type_name ); + break; + + case DICT_ENUMVAL: + free( obj->data.enumval.enum_name ); + break; + + case DICT_AVP: + free( obj->data.avp.avp_name ); + break; + + case DICT_COMMAND: + free( obj->data.cmd.cmd_name ); + break; + + default: + /* nothing to do */ + ; + } +} + +/* Forward declaration */ +static void destroy_object(struct dict_object * obj); + +/* Destroy all objects in a list - the lock must be held */ +static void destroy_list(struct fd_list * head) +{ + /* TRACE_ENTRY("%p", head); */ + + /* loop in the list */ + while (!FD_IS_LIST_EMPTY(head)) + { + /* When destroying the object, it is unlinked from the list */ + destroy_object(_O(head->next->o)); + } +} + +/* Free an object and its sublists */ +static void destroy_object(struct dict_object * obj) +{ + int i; + + /* TRACE_ENTRY("%p", obj); */ + + /* Update global count */ + if (obj->dico) + obj->dico->dict_count[obj->type]--; + + /* Mark the object as invalid */ + obj->objeyec = 0xdead; + + /* First, destroy the data associated to the object */ + destroy_object_data(obj); + + for (i=0; i<NB_LISTS_PER_OBJ; i++) { + if (_OBINFO(obj).haslist[i]) + /* unlink the element from the list */ + fd_list_unlink( &obj->list[i] ); + else + /* This is either a sentinel or unused (=emtpy) list, let's destroy it */ + destroy_list( &obj->list[i] ); + } + + /* Unlink all elements from the dispatch list; they will be freed when callback is unregistered */ + while (!FD_IS_LIST_EMPTY(&obj->disp_cbs)) { + fd_list_unlink( obj->disp_cbs.next ); + } + + /* Last, destroy the object */ + free(obj); +} + +/*******************************************************************************************************/ +/*******************************************************************************************************/ +/* */ +/* Compare functions */ +/* */ +/*******************************************************************************************************/ +/*******************************************************************************************************/ + +/* Compare two values */ +#define ORDER_scalar( i1, i2 ) \ + ((i1 < i2 ) ? -1 : ( i1 > i2 ? 1 : 0 )) + + +/* Compare two vendor objects by their id (checks already performed) */ +static int order_vendor_by_id ( struct dict_object *o1, struct dict_object *o2 ) +{ + TRACE_ENTRY("%p %p", o1, o2); + + return ORDER_scalar( o1->data.vendor.vendor_id, o2->data.vendor.vendor_id ); +} + +/* Compare two application objects by their id (checks already performed) */ +static int order_appli_by_id ( struct dict_object *o1, struct dict_object *o2 ) +{ + TRACE_ENTRY("%p %p", o1, o2); + + return ORDER_scalar( o1->data.application.application_id, o2->data.application.application_id ); +} + +/* Compare two type objects by their name (checks already performed) */ +static int order_type_by_name ( struct dict_object *o1, struct dict_object *o2 ) +{ + TRACE_ENTRY("%p %p", o1, o2); + + return strcmp( o1->data.type.type_name, o2->data.type.type_name ); +} + +/* Compare two type_enum objects by their names (checks already performed) */ +static int order_enum_by_name ( struct dict_object *o1, struct dict_object *o2 ) +{ + TRACE_ENTRY("%p %p", o1, o2); + + return strcmp( o1->data.enumval.enum_name, o2->data.enumval.enum_name ); +} + +/* Compare two type_enum objects by their values (checks already performed) */ +static int order_enum_by_val ( struct dict_object *o1, struct dict_object *o2 ) +{ + size_t oslen; + int cmp = 0; + + TRACE_ENTRY("%p %p", o1, o2); + + /* The comparison function depends on the type of data */ + switch ( o1->parent->data.type.type_base ) { + case AVP_TYPE_OCTETSTRING: + oslen = o1->data.enumval.enum_value.os.len; + if (o2->data.enumval.enum_value.os.len < oslen) + oslen = o2->data.enumval.enum_value.os.len; + cmp = memcmp(o1->data.enumval.enum_value.os.data, o2->data.enumval.enum_value.os.data, oslen ); + return (cmp ? cmp : ORDER_scalar(o1->data.enumval.enum_value.os.len,o2->data.enumval.enum_value.os.len)); + + case AVP_TYPE_INTEGER32: + return ORDER_scalar( o1->data.enumval.enum_value.i32, o2->data.enumval.enum_value.i32 ); + + case AVP_TYPE_INTEGER64: + return ORDER_scalar( o1->data.enumval.enum_value.i64, o2->data.enumval.enum_value.i64 ); + + case AVP_TYPE_UNSIGNED32: + return ORDER_scalar( o1->data.enumval.enum_value.u32, o2->data.enumval.enum_value.u32 ); + + case AVP_TYPE_UNSIGNED64: + return ORDER_scalar( o1->data.enumval.enum_value.u64, o2->data.enumval.enum_value.u64 ); + + case AVP_TYPE_FLOAT32: + return ORDER_scalar( o1->data.enumval.enum_value.f32, o2->data.enumval.enum_value.f32 ); + + case AVP_TYPE_FLOAT64: + return ORDER_scalar( o1->data.enumval.enum_value.f64, o2->data.enumval.enum_value.f64 ); + + case AVP_TYPE_GROUPED: + default: + ASSERT(0); + } + return 0; +} + +/* Compare two avp objects by their codes (checks already performed) */ +static int order_avp_by_code ( struct dict_object *o1, struct dict_object *o2 ) +{ + TRACE_ENTRY("%p %p", o1, o2); + + return ORDER_scalar( o1->data.avp.avp_code, o2->data.avp.avp_code ); +} + +/* Compare two avp objects by their names (checks already performed) */ +static int order_avp_by_name ( struct dict_object *o1, struct dict_object *o2 ) +{ + TRACE_ENTRY("%p %p", o1, o2); + + return strcmp( o1->data.avp.avp_name, o2->data.avp.avp_name ); +} + +/* Compare two command objects by their names (checks already performed) */ +static int order_cmd_by_name ( struct dict_object *o1, struct dict_object *o2 ) +{ + TRACE_ENTRY("%p %p", o1, o2); + + return strcmp( o1->data.cmd.cmd_name, o2->data.cmd.cmd_name ); +} + +/* Compare two command objects by their codes and flags (request or answer) (checks already performed) */ +static int order_cmd_by_codefl( struct dict_object *o1, struct dict_object *o2 ) +{ + uint8_t fl1, fl2; + int cmp = 0; + + TRACE_ENTRY("%p %p", o1, o2); + + cmp = ORDER_scalar( o1->data.cmd.cmd_code, o2->data.cmd.cmd_code ); + if (cmp) + return cmp; + + /* Same command code, we must compare the value of the 'R' flag */ + fl1 = o1->data.cmd.cmd_flag_val & CMD_FLAG_REQUEST; + fl2 = o2->data.cmd.cmd_flag_val & CMD_FLAG_REQUEST; + + /* We want requests first, so we reverse the operators here */ + return ORDER_scalar(fl2, fl1); + +} + +/* Compare two rule object by the AVP name that they refer (checks already performed) */ +static int order_rule_by_avpn ( struct dict_object *o1, struct dict_object *o2 ) +{ + TRACE_ENTRY("%p %p", o1, o2); + + return strcmp( o1->data.rule.rule_avp->data.avp.avp_name, o2->data.rule.rule_avp->data.avp.avp_name ); +} + +/*******************************************************************************************************/ +/*******************************************************************************************************/ +/* */ +/* Search functions */ +/* */ +/*******************************************************************************************************/ +/*******************************************************************************************************/ + +/* Functions used to search for objects in the lists, according to some criteria */ + +/* On a general note, if result is not NULL, ENOENT is not returned but *result is NULL. */ + +/* The following macros assume that "what", "ret", "result" (variables), and "end" (label) exist +in the local context where they are called. They are meant to be called only from the functions that follow. */ + +/* For searchs of type "xxx_OF_xxx": children's parent or default parent */ +#define SEARCH_childs_parent( type_of_child, default_parent ) { \ + struct dict_object *__child = (struct dict_object *) what; \ + CHECK_PARAMS_DO( verify_object(__child) && \ + (__child->type == (type_of_child)), \ + { ret = EINVAL; goto end; } ); \ + ret = 0; \ + if (result) \ + *result = (__child->parent ? __child->parent :(default_parent));\ +} + +/* For search of strings in lists. isindex= 1 if the string is the ordering key of the list */ +#define SEARCH_string( str, sentinel, datafield, isindex ) { \ + char * __str = (char *) str; \ + int __cmp; \ + struct fd_list * __li; \ + ret = 0; \ + for (__li = (sentinel); __li->next != (sentinel); __li = __li->next) { \ + __cmp = strcmp(__str, _O(__li->next->o)->data. datafield ); \ + if (__cmp == 0) { \ + if (result) \ + *result = _O(__li->next->o); \ + goto end; \ + } \ + if ((isindex) && (__cmp < 0)) \ + break; \ + } \ + if (result) \ + *result = NULL; \ + else \ + ret = ENOENT; \ +} + +/* For search of octetstrings in lists (not \0 terminated). */ +#define SEARCH_ocstring( ostr, length, sentinel, osdatafield, isindex ) { \ + unsigned char * __ostr = (unsigned char *) ostr; \ + int __cmp; \ + size_t __len; \ + struct fd_list * __li; \ + ret = 0; \ + for (__li = (sentinel); __li->next != (sentinel); __li = __li->next) { \ + __len = _O(__li->next->o)->data. osdatafield .len; \ + if ( __len > (length) ) \ + __len = (length); \ + __cmp = memcmp(__ostr, \ + _O(__li->next->o)->data. osdatafield .data, \ + __len); \ + if (! __cmp) { \ + __cmp = ORDER_scalar( length, \ + _O(__li->next->o)->data. osdatafield .len); \ + } \ + if (__cmp == 0) { \ + if (result) \ + *result = _O(__li->next->o); \ + goto end; \ + } \ + if ((isindex) && (__cmp < 0)) \ + break; \ + } \ + if (result) \ + *result = NULL; \ + else \ + ret = ENOENT; \ +} + +/* For search of AVP name in rule lists. */ +#define SEARCH_ruleavpname( str, sentinel ) { \ + char * __str = (char *) str; \ + int __cmp; \ + struct fd_list * __li; \ + ret = 0; \ + for (__li = (sentinel); __li->next != (sentinel); __li = __li->next) { \ + __cmp = strcmp(__str, \ + _O(__li->next->o)->data.rule.rule_avp->data.avp.avp_name);\ + if (__cmp == 0) { \ + if (result) \ + *result = _O(__li->next->o); \ + goto end; \ + } \ + if (__cmp < 0) \ + break; \ + } \ + if (result) \ + *result = NULL; \ + else \ + ret = ENOENT; \ +} + +/* For search of scalars in lists. isindex= 1 if the value is the ordering key of the list */ +#define SEARCH_scalar( value, sentinel, datafield, isindex, defaultobj ) { \ + int __cmp; \ + struct fd_list * __li; \ + ret = 0; \ + if ( ((defaultobj) != NULL) \ + && (_O(defaultobj)->data. datafield == value)) { \ + if (result) \ + *result = _O(defaultobj); \ + goto end; \ + } \ + for (__li = (sentinel); __li->next != (sentinel); __li = __li->next) { \ + __cmp= ORDER_scalar(value, _O(__li->next->o)->data. datafield );\ + if (__cmp == 0) { \ + if (result) \ + *result = _O(__li->next->o); \ + goto end; \ + } \ + if ((isindex) && (__cmp < 0)) \ + break; \ + } \ + if (result) \ + *result = NULL; \ + else \ + ret = ENOENT; \ +} + +/* For search of commands in lists by code and flag. R_flag_val = 0 or CMD_FLAG_REQUEST */ +#define SEARCH_codefl( value, R_flag_val, sentinel) { \ + int __cmp; \ + struct fd_list * __li; \ + ret = 0; \ + for ( __li = (sentinel); \ + __li->next != (sentinel); \ + __li = __li->next) { \ + __cmp = ORDER_scalar(value, \ + _O(__li->next->o)->data.cmd.cmd_code ); \ + if (__cmp == 0) { \ + uint8_t __mask, __val; \ + __mask = _O(__li->next->o)->data.cmd.cmd_flag_mask; \ + __val = _O(__li->next->o)->data.cmd.cmd_flag_val; \ + if ( ! (__mask & CMD_FLAG_REQUEST) ) \ + continue; \ + if ( ( __val & CMD_FLAG_REQUEST ) != R_flag_val ) \ + continue; \ + if (result) \ + *result = _O(__li->next->o); \ + goto end; \ + } \ + if (__cmp < 0) \ + break; \ + } \ + if (result) \ + *result = NULL; \ + else \ + ret = ENOENT; \ +} + +static int search_vendor ( struct dictionary * dict, int criteria, void * what, struct dict_object **result ) +{ + int ret = 0; + vendor_id_t id; + + TRACE_ENTRY("%p %d %p %p", dict, criteria, what, result); + + switch (criteria) { + case VENDOR_BY_ID: + id = *(vendor_id_t *) what; + SEARCH_scalar( id, &dict->dict_vendors.list[0], vendor.vendor_id, 1, &dict->dict_vendors ); + break; + + case VENDOR_BY_NAME: + /* "what" is a vendor name */ + SEARCH_string( what, &dict->dict_vendors.list[0], vendor.vendor_name, 0); + break; + + case VENDOR_OF_APPLICATION: + /* "what" should be an application object */ + SEARCH_childs_parent( DICT_APPLICATION, &dict->dict_vendors ); + break; + + default: + /* Invalid criteria */ + CHECK_PARAMS( criteria = 0 ); + } +end: + return ret; +} + +static int search_application ( struct dictionary * dict, int criteria, void * what, struct dict_object **result ) +{ + int ret = 0; + application_id_t id; + + TRACE_ENTRY("%p %d %p %p", dict, criteria, what, result); + + switch (criteria) { + case APPLICATION_BY_ID: + id = *(application_id_t *) what; + + SEARCH_scalar( id, &dict->dict_applications.list[0], application.application_id, 1, &dict->dict_applications ); + break; + + case APPLICATION_BY_NAME: + /* "what" is an application name */ + SEARCH_string( what, &dict->dict_applications.list[0], application.application_name, 0); + break; + + case APPLICATION_OF_TYPE: + /* "what" should be a type object */ + SEARCH_childs_parent( DICT_TYPE, &dict->dict_applications ); + break; + + case APPLICATION_OF_COMMAND: + /* "what" should be a command object */ + SEARCH_childs_parent( DICT_COMMAND, &dict->dict_applications ); + break; + + default: + /* Invalid criteria */ + CHECK_PARAMS( criteria = 0 ); + } +end: + return ret; +} + +static int search_type ( struct dictionary * dict, int criteria, void * what, struct dict_object **result ) +{ + int ret = 0; + + TRACE_ENTRY("%p %d %p %p", dict, criteria, what, result); + + switch (criteria) { + case TYPE_BY_NAME: + /* "what" is a type name */ + SEARCH_string( what, &dict->dict_types, type.type_name, 1); + break; + + case TYPE_OF_ENUMVAL: + /* "what" should be a type_enum object */ + SEARCH_childs_parent( DICT_ENUMVAL, NULL ); + break; + + case TYPE_OF_AVP: + /* "what" should be an avp object */ + SEARCH_childs_parent( DICT_AVP, NULL ); + break; + + + default: + /* Invalid criteria */ + CHECK_PARAMS( criteria = 0 ); + } +end: + return ret; +} + +static int search_enumval ( struct dictionary * dict, int criteria, void * what, struct dict_object **result ) +{ + int ret = 0; + + TRACE_ENTRY("%p %d %p %p", dict, criteria, what, result); + + switch (criteria) { + case ENUMVAL_BY_STRUCT: + { + struct dict_object * parent = NULL; + struct dict_enumval_request * _what = (struct dict_enumval_request *) what; + + CHECK_PARAMS( _what && ( _what->type_obj || _what->type_name ) ); + + if (_what->type_obj != NULL) { + parent = _what->type_obj; + CHECK_PARAMS( verify_object(parent) && (parent->type == DICT_TYPE) ); + } else { + /* We received only the type name, we must find it first */ + CHECK_FCT_DO( search_type( dict, TYPE_BY_NAME, _what->type_name, &parent ), + CHECK_PARAMS( 0 ) ); + } + + /* From here the "parent" object is valid */ + + if ( _what->search.enum_name != NULL ) { + /* We are looking for this string */ + SEARCH_string( _what->search.enum_name, &parent->list[1], enumval.enum_name, 1 ); + } else { + /* We are looking for the value in enum_value */ + switch (parent->data.type.type_base) { + case AVP_TYPE_OCTETSTRING: + SEARCH_ocstring( _what->search.enum_value.os.data, + _what->search.enum_value.os.len, + &parent->list[2], + enumval.enum_value.os , + 1 ); + break; + + case AVP_TYPE_INTEGER32: + SEARCH_scalar( _what->search.enum_value.i32, + &parent->list[2], + enumval.enum_value.i32, + 1, + (struct dict_object *)NULL); + break; + + case AVP_TYPE_INTEGER64: + SEARCH_scalar( _what->search.enum_value.i64, + &parent->list[2], + enumval.enum_value.i64, + 1, + (struct dict_object *)NULL); + break; + + case AVP_TYPE_UNSIGNED32: + SEARCH_scalar( _what->search.enum_value.u32, + &parent->list[2], + enumval.enum_value.u32, + 1, + (struct dict_object *)NULL); + break; + + case AVP_TYPE_UNSIGNED64: + SEARCH_scalar( _what->search.enum_value.u64, + &parent->list[2], + enumval.enum_value.u64, + 1, + (struct dict_object *)NULL); + break; + + case AVP_TYPE_FLOAT32: + SEARCH_scalar( _what->search.enum_value.f32, + &parent->list[2], + enumval.enum_value.f32, + 1, + (struct dict_object *)NULL); + break; + + case AVP_TYPE_FLOAT64: + SEARCH_scalar( _what->search.enum_value.f64, + &parent->list[2], + enumval.enum_value.f64, + 1, + (struct dict_object *)NULL); + break; + + default: + /* Invalid parent type basetype */ + CHECK_PARAMS( parent = NULL ); + } + } + + } + break; + + + default: + /* Invalid criteria */ + CHECK_PARAMS( criteria = 0 ); + } +end: + return ret; +} + +static int search_avp ( struct dictionary * dict, int criteria, void * what, struct dict_object **result ) +{ + int ret = 0; + + TRACE_ENTRY("%p %d %p %p", dict, criteria, what, result); + + switch (criteria) { + case AVP_BY_CODE: + { + avp_code_t code; + code = *(avp_code_t *) what; + + SEARCH_scalar( code, &dict->dict_vendors.list[1], avp.avp_code, 1, (struct dict_object *)NULL ); + } + break; + + case AVP_BY_NAME: + /* "what" is the AVP name, vendor 0 */ + SEARCH_string( what, &dict->dict_vendors.list[2], avp.avp_name, 1); + break; + + case AVP_BY_CODE_AND_VENDOR: + case AVP_BY_NAME_AND_VENDOR: + { + struct dict_avp_request * _what = (struct dict_avp_request *) what; + struct dict_object * vendor = NULL; + + CHECK_PARAMS( (criteria != AVP_BY_NAME_AND_VENDOR) || _what->avp_name ); + + /* Now look for the vendor first */ + CHECK_FCT( search_vendor( dict, VENDOR_BY_ID, &_what->avp_vendor, &vendor ) ); + if (vendor == NULL) { + if (result) + *result = NULL; + else + ret = ENOENT; + goto end; + } + + /* We now have our vendor = head of the appropriate avp list */ + if (criteria == AVP_BY_NAME_AND_VENDOR) { + SEARCH_string( _what->avp_name, &vendor->list[2], avp.avp_name, 1); + } else { + /* AVP_BY_CODE_AND_VENDOR */ + SEARCH_scalar( _what->avp_code, &vendor->list[1], avp.avp_code, 1, (struct dict_object *)NULL ); + } + } + break; + + default: + /* Invalid criteria */ + CHECK_PARAMS( criteria = 0 ); + } +end: + return ret; +} + +static int search_cmd ( struct dictionary * dict, int criteria, void * what, struct dict_object **result ) +{ + int ret = 0; + + TRACE_ENTRY("%p %d %p %p", dict, criteria, what, result); + + switch (criteria) { + case CMD_BY_NAME: + /* "what" is a command name */ + SEARCH_string( what, &dict->dict_cmd_name, cmd.cmd_name, 1); + break; + + case CMD_BY_CODE_R: + case CMD_BY_CODE_A: + { + command_code_t code; + uint8_t searchfl = 0; + + /* The command code that we are searching */ + code = *(command_code_t *) what; + + /* The flag (request or answer) of the command we are searching */ + if (criteria == CMD_BY_CODE_R) { + searchfl = CMD_FLAG_REQUEST; + } + + /* perform the search */ + SEARCH_codefl( code, searchfl, &dict->dict_cmd_code ); + } + break; + + case CMD_ANSWER: + { + /* "what" is a command object of type "request" */ + struct dict_object * req = (struct dict_object *) what; + struct dict_object * ans = NULL; + + CHECK_PARAMS( verify_object(req) + && (req->type == DICT_COMMAND) + && (req->data.cmd.cmd_flag_mask & CMD_FLAG_REQUEST) + && (req->data.cmd.cmd_flag_val & CMD_FLAG_REQUEST) ); + + /* The answer is supposed to be the next element in the list, if it exists */ + ans = req->list[1].next->o; + if ( ans == NULL ) { + TRACE_DEBUG( FULL, "the request was the last element in the list" ); + ret = ENOENT; + goto end; + } + + /* Now check that the ans element is really the correct one */ + if ( (ans->data.cmd.cmd_code != req->data.cmd.cmd_code) + || (!(ans->data.cmd.cmd_flag_mask & CMD_FLAG_REQUEST)) + || ( ans->data.cmd.cmd_flag_val & CMD_FLAG_REQUEST ) ) { + TRACE_DEBUG( FULL, "the answer does not follow the request in the list" ); + ret = ENOENT; + goto end; + } + + if (result) + *result = ans; + ret = 0; + } + break; + + default: + /* Invalid criteria */ + CHECK_PARAMS( criteria = 0 ); + } +end: + return ret; +} + +static int search_rule ( struct dictionary * dict, int criteria, void * what, struct dict_object **result ) +{ + int ret = 0; + + TRACE_ENTRY("%p %d %p %p", dict, criteria, what, result); + + switch (criteria) { + case RULE_BY_AVP_AND_PARENT: + { + struct dict_object * parent = NULL; + struct dict_object * avp = NULL; + struct dict_rule_request * _what = (struct dict_rule_request *) what; + + CHECK_PARAMS( _what + && (parent = _what->rule_parent) + && (avp = _what->rule_avp ) ); + + CHECK_PARAMS( verify_object(parent) + && ((parent->type == DICT_COMMAND) + || ((parent->type == DICT_AVP) && (parent->data.avp.avp_basetype == AVP_TYPE_GROUPED))) ); + + CHECK_PARAMS( verify_object(avp) && (avp->type == DICT_AVP) ); + + /* Perform the search */ + SEARCH_ruleavpname( avp->data.avp.avp_name, &parent->list[2]); + + } + break; + + default: + /* Invalid criteria */ + CHECK_PARAMS( criteria = 0 ); + } +end: + return ret; +} + +/*******************************************************************************************************/ +/*******************************************************************************************************/ +/* */ +/* Dump / debug functions */ +/* */ +/*******************************************************************************************************/ +/*******************************************************************************************************/ +/* The following functions are used to debug the module, and allow to print out the content of the dictionary */ +static void dump_vendor_data ( void * data ) +{ + struct dict_vendor_data * vendor = (struct dict_vendor_data *)data; + + fd_log_debug("data: %-6u \"%s\"", vendor->vendor_id, vendor->vendor_name); +} +static void dump_application_data ( void * data ) +{ + struct dict_application_data * appli = (struct dict_application_data *) data; + fd_log_debug("data: %-6u \"%s\"", appli->application_id, appli->application_name); +} +static void dump_type_data ( void * data ) +{ + struct dict_type_data * type = ( struct dict_type_data * ) data; + + fd_log_debug("data: %-12s \"%s\"", + type_base_name[type->type_base], + type->type_name); +} +static void dump_enumval_data ( struct dict_enumval_data * enumval, enum dict_avp_basetype type ) +{ + const int LEN_MAX = 20; + fd_log_debug("data: (%-12s) \"%s\" -> ", type_base_name[type], enumval->enum_name); + switch (type) { + case AVP_TYPE_OCTETSTRING: + { + int i, n=LEN_MAX; + if (enumval->enum_value.os.len < LEN_MAX) + n = enumval->enum_value.os.len; + for (i=0; i < n; i++) + fd_log_debug("0x%02.2X/'%c' ", enumval->enum_value.os.data[i], ASCII(enumval->enum_value.os.data[i])); + if (n == LEN_MAX) + fd_log_debug("..."); + } + break; + + case AVP_TYPE_INTEGER32: + fd_log_debug("%i", enumval->enum_value.i32); + break; + + case AVP_TYPE_INTEGER64: + fd_log_debug("%lli", enumval->enum_value.i64); + break; + + case AVP_TYPE_UNSIGNED32: + fd_log_debug("%u", enumval->enum_value.u32); + break; + + case AVP_TYPE_UNSIGNED64: + fd_log_debug("%llu", enumval->enum_value.u64); + break; + + case AVP_TYPE_FLOAT32: + fd_log_debug("%f", enumval->enum_value.f32); + break; + + case AVP_TYPE_FLOAT64: + fd_log_debug("%g", enumval->enum_value.f64); + break; + + default: + fd_log_debug("??? (ERROR unknown type %d)", type); + } +} +static void dump_avp_data ( void * data ) +{ + struct dict_avp_data * avp = (struct dict_avp_data * ) data; + fd_log_debug("data: v/m:" DUMP_AVPFL_str "/" DUMP_AVPFL_str ", %12s, %-6u \"%s\"", + DUMP_AVPFL_val(avp->avp_flag_val), + DUMP_AVPFL_val(avp->avp_flag_mask), + type_base_name[avp->avp_basetype], + avp->avp_code, + avp->avp_name ); +} +static void dump_command_data ( void * data ) +{ + struct dict_cmd_data * cmd = (struct dict_cmd_data *) data; + fd_log_debug("data: v/m:" DUMP_CMDFL_str "/" DUMP_CMDFL_str ", %-6u \"%s\"", + DUMP_CMDFL_val(cmd->cmd_flag_val), DUMP_CMDFL_val(cmd->cmd_flag_mask), cmd->cmd_code, cmd->cmd_name); +} +static void dump_rule_data ( void * data ) +{ + struct dict_rule_data * rule = (struct dict_rule_data * )data; + fd_log_debug("data: pos:%d ord:%d m/M:%2d/%2d avp:\"%s\"", + rule->rule_position, + rule->rule_order, + rule->rule_min, + rule->rule_max, + rule->rule_avp->data.avp.avp_name); +} + +static void dump_object ( struct dict_object * obj, int parents, int depth, int indent ); + +static void dump_list ( struct fd_list * sentinel, int parents, int depth, int indent ) +{ + struct fd_list * li = sentinel; + /* We don't lock here, the caller must have taken the dictionary lock for reading already */ + while (li->next != sentinel) + { + li = li->next; + dump_object( _O(li->o), parents, depth, indent ); + } +} + +static void dump_object ( struct dict_object * obj, int parents, int depth, int indent ) +{ + if (obj == NULL) + return; + + if (parents) + dump_object (obj->parent, parents-1, 0, indent + 1 ); + + fd_log_debug("%*s@%p: %s%s (p:%-9p) ", + indent, + "", + obj, + verify_object(obj) ? "" : "INVALID ", + _OBINFO(obj).name, + obj->parent); + + if (obj->type == DICT_ENUMVAL) + dump_enumval_data ( &obj->data.enumval, obj->parent->data.type.type_base ); + else + _OBINFO(obj).dump_data(&obj->data); + + fd_log_debug("\n"); + + if (depth) { + int i; + for (i=0; i<NB_LISTS_PER_OBJ; i++) { + if ((obj->list[i].o == NULL) && (obj->list[i].next != &obj->list[i])) { + fd_log_debug("%*s>%p: list[%d]:\n", indent, "", obj, i); + dump_list(&obj->list[i], parents, depth - 1, indent + 2); + } + } + } +} + +void fd_dict_dump_object(struct dict_object * obj) +{ + fd_log_debug("Dictionary object %p dump:\n", obj); + dump_object( obj, 1, 2, 2 ); +} + +void fd_dict_dump(struct dictionary * dict) +{ + int i; + + CHECK_PARAMS_DO(dict && (dict->dict_eyec == DICT_EYECATCHER), return); + + CHECK_POSIX_DO( pthread_rwlock_rdlock( &dict->dict_lock ), /* ignore */ ); + + fd_log_debug("######################################################\n"); + fd_log_debug("###### Dumping vendors, AVPs and related rules #######\n"); + + dump_object( &dict->dict_vendors, 0, 3, 0 ); + + fd_log_debug("###### Dumping applications #######\n"); + + dump_object( &dict->dict_applications, 0, 1, 0 ); + + fd_log_debug("###### Dumping types #######\n"); + + dump_list( &dict->dict_types, 0, 2, 0 ); + + fd_log_debug("###### Dumping commands per name #######\n"); + + dump_list( &dict->dict_cmd_name, 0, 2, 0 ); + + fd_log_debug("###### Dumping commands per code and flags #######\n"); + + dump_list( &dict->dict_cmd_code, 0, 0, 0 ); + + fd_log_debug("###### Statistics #######\n"); + + for (i=1; i<=DICT_TYPE_MAX; i++) + fd_log_debug(" %5d objects of type %s\n", dict->dict_count[i], dict_obj_info[i].name); + + fd_log_debug("######################################################\n"); + + /* Free the rwlock */ + CHECK_POSIX_DO( pthread_rwlock_unlock( &dict->dict_lock ), /* ignore */ ); +} + +/*******************************************************************************************************/ +/*******************************************************************************************************/ +/* */ +/* Exported functions */ +/* */ +/*******************************************************************************************************/ +/*******************************************************************************************************/ + +/* These are the functions exported outside libfreediameter. */ + +/* Get the data associated to an object */ +int fd_dict_gettype ( struct dict_object * object, enum dict_object_type * type) +{ + TRACE_ENTRY("%p %p", object, type); + + CHECK_PARAMS( type && verify_object(object) ); + + /* Copy the value and return */ + *type = object->type; + return 0; +} + +int fd_dict_getdict ( struct dict_object * object, struct dictionary ** dict) +{ + TRACE_ENTRY("%p %p", object, dict); + + CHECK_PARAMS( dict && verify_object(object) ); + + /* Copy the value and return */ + *dict = object->dico; + return 0; +} + + +/* Get the data associated to an object */ +int fd_dict_getval ( struct dict_object * object, void * val) +{ + TRACE_ENTRY("%p %p", object, val); + + CHECK_PARAMS( val && verify_object(object) ); + + /* Copy the value and return */ + memcpy(val, &object->data, _OBINFO(object).datasize);; + return 0; +} + +/* Add a new object in the dictionary */ +int fd_dict_new ( struct dictionary * dict, enum dict_object_type type, void * data, struct dict_object * parent, struct dict_object **ref ) +{ + int ret = 0; + struct dict_object * new = NULL; + struct dict_object * vendor = NULL; + + TRACE_ENTRY("%p %d(%s) %p %p %p", dict, type, dict_obj_info[CHECK_TYPE(type) ? type : 0].name, data, parent, ref); + + /* Check parameters */ + CHECK_PARAMS( dict && (dict->dict_eyec == DICT_EYECATCHER) && CHECK_TYPE(type) && data ); + + /* Check the "parent" parameter */ + switch (dict_obj_info[type].parent) { + case 0: /* parent is forbidden */ + CHECK_PARAMS( parent == NULL ); + + case 1: /* parent is optional */ + if (parent == NULL) + break; + + case 2: /* parent is mandatory */ + CHECK_PARAMS( verify_object(parent) ); + + if (type == DICT_RULE ) { /* Special case : grouped AVP or Command parents are allowed */ + CHECK_PARAMS( (parent->type == DICT_COMMAND ) + || ( (parent->type == DICT_AVP) && (parent->data.avp.avp_basetype == AVP_TYPE_GROUPED ) ) ); + } else { + CHECK_PARAMS( parent->type == dict_obj_info[type].parenttype ); + } + } + + /* For AVP object, we must also check that the "vendor" referenced exists */ + if (type == DICT_AVP) { + CHECK_FCT_DO( fd_dict_search( dict, DICT_VENDOR, VENDOR_BY_ID, &(((struct dict_avp_data *)data)->avp_vendor), (void*)&vendor, ENOENT ), + CHECK_PARAMS( vendor = NULL ) ); + + /* Also check if a parent is provided, that the type are the same */ + if (parent) { + CHECK_PARAMS( parent->data.type.type_base == ((struct dict_avp_data *)data)->avp_basetype ); + } + } + + /* For RULE object, we must also check that the "avp" referenced exists */ + if (type == DICT_RULE) { + CHECK_PARAMS( verify_object(((struct dict_rule_data *)data)->rule_avp) ); + CHECK_PARAMS( ((struct dict_rule_data *)data)->rule_avp->type == DICT_AVP ); + } + + /* For COMMAND object, check that the 'R' flag is fixed */ + if (type == DICT_COMMAND) { + CHECK_PARAMS( ((struct dict_cmd_data *)data)->cmd_flag_mask & CMD_FLAG_REQUEST ); + } + + /* Parameters are valid, create the new object */ + CHECK_MALLOC( new = malloc(sizeof(struct dict_object)) ); + + /* Initialize the data of the new object */ + init_object(new, type); + init_object_data(&new->data, data, type); + new->dico = dict; + new->parent = parent; + + /* We will change the dictionary => acquire the write lock */ + CHECK_POSIX_DO( ret = pthread_rwlock_wrlock(&dict->dict_lock), goto error_free ); + + /* Now link the object -- this also checks that no object with same keys already exists */ + switch (type) { + case DICT_VENDOR: + /* A vendor object is linked in the g_dict_vendors.list[0], by their id */ + CHECK_FCT_DO( ret = fd_list_insert_ordered ( &dict->dict_vendors.list[0], &new->list[0], (int (*)(void*, void *))order_vendor_by_id, (void **)ref ), + goto error_unlock ); + break; + + case DICT_APPLICATION: + /* An application object is linked in the g_dict_applciations.list[0], by their id */ + CHECK_FCT_DO( ret = fd_list_insert_ordered ( &dict->dict_applications.list[0], &new->list[0], (int (*)(void*, void *))order_appli_by_id, (void **)ref ), + goto error_unlock ); + break; + + case DICT_TYPE: + /* A type object is linked in g_list_types by its name */ + CHECK_FCT_DO( ret = fd_list_insert_ordered ( &dict->dict_types, &new->list[0], (int (*)(void*, void *))order_type_by_name, (void **)ref ), + goto error_unlock ); + break; + + case DICT_ENUMVAL: + /* A type_enum object is linked in it's parent 'type' object lists 1 and 2 by its name and values */ + CHECK_FCT_DO( ret = fd_list_insert_ordered ( &parent->list[1], &new->list[0], (int (*)(void*, void *))order_enum_by_name, (void **)ref ), + goto error_unlock ); + CHECK_FCT_DO( ret = fd_list_insert_ordered ( &parent->list[2], &new->list[1], (int (*)(void*, void *))order_enum_by_val, (void **)ref ), + { fd_list_unlink(&new->list[0]); goto error_unlock; } ); + break; + + case DICT_AVP: + /* An avp object is linked in lists 1 and 2 of its vendor, by code and name */ + CHECK_FCT_DO( ret = fd_list_insert_ordered ( &vendor->list[1], &new->list[0], (int (*)(void*, void *))order_avp_by_code, (void **)ref ), + goto error_unlock ); + CHECK_FCT_DO( ret = fd_list_insert_ordered ( &vendor->list[2], &new->list[1], (int (*)(void*, void *))order_avp_by_name, (void **)ref ), + { fd_list_unlink(&new->list[0]); goto error_unlock; } ); + break; + + case DICT_COMMAND: + /* A command object is linked in g_list_cmd_name and g_list_cmd_code by its name and code */ + CHECK_FCT_DO( ret = fd_list_insert_ordered ( &dict->dict_cmd_code, &new->list[1], (int (*)(void*, void *))order_cmd_by_codefl, (void **)ref ), + goto error_unlock ); + CHECK_FCT_DO( ret = fd_list_insert_ordered ( &dict->dict_cmd_name, &new->list[0], (int (*)(void*, void *))order_cmd_by_name, (void **)ref ), + { fd_list_unlink(&new->list[1]); goto error_unlock; } ); + break; + + case DICT_RULE: + /* A rule object is linked in list[2] of its parent command or AVP by the name of the AVP it refers */ + CHECK_FCT_DO( ret = fd_list_insert_ordered ( &parent->list[2], &new->list[0], (int (*)(void*, void *))order_rule_by_avpn, (void **)ref ), + goto error_unlock ); + break; + + default: + ASSERT(0); + } + + /* A new object has been created, increment the global counter */ + dict->dict_count[type]++; + + /* Unlock the dictionary */ + CHECK_POSIX_DO( ret = pthread_rwlock_unlock(&dict->dict_lock), goto error_free ); + + /* Save the pointer to the new object */ + if (ref) + *ref = new; + + return 0; + +error_unlock: + CHECK_POSIX_DO( pthread_rwlock_unlock(&dict->dict_lock), /* continue */ ); +error_free: + free(new); + return ret; +} + +int fd_dict_search ( struct dictionary * dict, enum dict_object_type type, int criteria, void * what, struct dict_object **result, int retval ) +{ + int ret = 0; + + TRACE_ENTRY("%p %d(%s) %d %p %p %d", dict, type, dict_obj_info[CHECK_TYPE(type) ? type : 0].name, criteria, what, result, retval); + + /* Check param */ + CHECK_PARAMS( dict && (dict->dict_eyec == DICT_EYECATCHER) && CHECK_TYPE(type) ); + + /* Lock the dictionary for reading */ + CHECK_POSIX( pthread_rwlock_rdlock(&dict->dict_lock) ); + + /* Now call the type-specific search function */ + ret = dict_obj_info[type].search_fct (dict, criteria, what, result); + + /* Unlock */ + CHECK_POSIX( pthread_rwlock_unlock(&dict->dict_lock) ); + + /* Update the return value as needed */ + if ((result != NULL) && (*result == NULL)) + ret = retval; + + return ret; +} + +/*******************************************************************************************************/ +/*******************************************************************************************************/ +/* */ +/* The init/fini functions */ +/* */ +/*******************************************************************************************************/ +/*******************************************************************************************************/ + +/* Initialize the dictionary */ +int fd_dict_init ( struct dictionary ** dict) +{ + struct dictionary * new = NULL; + + TRACE_ENTRY(""); + + /* Sanity checks */ + ASSERT( (sizeof(type_base_name) / sizeof(type_base_name[0])) == (AVP_TYPE_MAX + 1) ); + ASSERT( (sizeof(dict_obj_info) / sizeof(dict_obj_info[0])) == (DICT_TYPE_MAX + 1) ); + CHECK_PARAMS(dict); + + /* Allocate the memory for the dictionary */ + CHECK_MALLOC( new = malloc(sizeof(struct dictionary)) ); + memset(new, 0, sizeof(struct dictionary)); + + new->dict_eyec = DICT_EYECATCHER; + + /* Initialize the lock for the dictionary */ + CHECK_POSIX( pthread_rwlock_init(&new->dict_lock, NULL) ); + + /* Initialize the sentinel for vendors and AVP lists */ + init_object( &new->dict_vendors, DICT_VENDOR ); + new->dict_vendors.data.vendor.vendor_name = "(no vendor)"; + new->dict_vendors.list[0].o = NULL; /* overwrite since element is also sentinel for this list. */ + + + /* Initialize the sentinel for applciations */ + init_object( &new->dict_applications, DICT_APPLICATION ); + new->dict_applications.data.application.application_name = "Diameter Common Messages"; + new->dict_applications.list[0].o = NULL; /* overwrite since since element is also sentinel for this list. */ + + /* Initialize the sentinel for types */ + fd_list_init ( &new->dict_types, NULL ); + + /* Initialize the sentinels for commands */ + fd_list_init ( &new->dict_cmd_name, NULL ); + fd_list_init ( &new->dict_cmd_code, NULL ); + + /* Initialize the error command object */ + init_object( &new->dict_cmd_error, DICT_COMMAND ); + new->dict_cmd_error.data.cmd.cmd_name="(generic error format)"; + new->dict_cmd_error.data.cmd.cmd_flag_mask=CMD_FLAG_ERROR | CMD_FLAG_REQUEST | CMD_FLAG_RETRANSMIT; + new->dict_cmd_error.data.cmd.cmd_flag_val =CMD_FLAG_ERROR; + + *dict = new; + + /* Done */ + return 0; +} + +/* Destroy a dictionary */ +int fd_dict_fini ( struct dictionary ** dict) +{ + int i; + + TRACE_ENTRY(""); + CHECK_PARAMS( dict && *dict && ((*dict)->dict_eyec == DICT_EYECATCHER) ); + + /* Acquire the write lock to make sure no other operation is ongoing */ + CHECK_POSIX( pthread_rwlock_wrlock(&(*dict)->dict_lock) ); + + /* Empty all the lists, free the elements */ + destroy_list ( &(*dict)->dict_cmd_error.list[2] ); + destroy_list ( &(*dict)->dict_cmd_code ); + destroy_list ( &(*dict)->dict_cmd_name ); + destroy_list ( &(*dict)->dict_types ); + for (i=0; i< NB_LISTS_PER_OBJ; i++) { + destroy_list ( &(*dict)->dict_applications.list[i] ); + destroy_list ( &(*dict)->dict_vendors.list[i] ); + } + + /* Dictionary is empty, now destroy the lock */ + CHECK_POSIX( pthread_rwlock_unlock(&(*dict)->dict_lock) ); + CHECK_POSIX( pthread_rwlock_destroy(&(*dict)->dict_lock) ); + + free(*dict); + *dict = NULL; + + return 0; +} + +/*******************************************************************************************************/ +/*******************************************************************************************************/ +/* */ +/* Other functions */ +/* */ +/*******************************************************************************************************/ +/*******************************************************************************************************/ + +/* Iterate a callback on the rules for an object */ +int fd_dict_iterate_rules ( struct dict_object *parent, void * data, int (*cb)(void *, struct dict_rule_data *) ) +{ + int ret = 0; + struct fd_list * li; + + TRACE_ENTRY("%p %p %p", parent, data, cb); + + /* Check parameters */ + CHECK_PARAMS( verify_object(parent) ); + CHECK_PARAMS( (parent->type == DICT_COMMAND) + || ((parent->type == DICT_AVP) && (parent->data.avp.avp_basetype == AVP_TYPE_GROUPED)) ); + TRACE_DEBUG (FULL, "Iterating on rules of %s: '%s'.", + _OBINFO(parent).name, + parent->type == DICT_COMMAND ? + parent->data.cmd.cmd_name + : parent->data.avp.avp_name); + + /* Acquire the read lock */ + CHECK_POSIX( pthread_rwlock_rdlock(&parent->dico->dict_lock) ); + + /* go through the list and call the cb on each rule data */ + for (li = &(parent->list[2]); li->next != &(parent->list[2]); li = li->next) { + ret = (*cb)(data, &(_O(li->next->o)->data.rule)); + if (ret != 0) + break; + } + + /* Release the lock */ + CHECK_POSIX( pthread_rwlock_unlock(&parent->dico->dict_lock) ); + + return ret; +} + +/* Create the list of vendors. Returns a 0-terminated array, that must be freed after use. Returns NULL on error. */ +uint32_t * fd_dict_get_vendorid_list(struct dictionary * dict) +{ + uint32_t * ret = NULL; + int i = 0; + struct fd_list * li; + + TRACE_ENTRY(); + + /* Acquire the read lock */ + CHECK_POSIX_DO( pthread_rwlock_rdlock(&dict->dict_lock), return NULL ); + + /* Allocate an array to contain all the elements */ + CHECK_MALLOC_DO( ret = calloc( dict->dict_count[DICT_VENDOR] + 1, sizeof(uint32_t) ), goto out ); + + /* Copy the vendors IDs */ + for (li = dict->dict_vendors.list[0].next; li != &(dict->dict_vendors.list[0]); li = li->next) { + ret[i] = _O(li->o)->data.vendor.vendor_id; + i++; + ASSERT( i <= dict->dict_count[DICT_VENDOR] ); + } +out: + /* Release the lock */ + CHECK_POSIX_DO( pthread_rwlock_unlock(&dict->dict_lock), return NULL ); + + return ret; +} + +/* Return the location of the cb list for an object, after checking its type */ +int fd_dict_disp_cb(enum dict_object_type type, struct dict_object *obj, struct fd_list ** cb_list) +{ + TRACE_ENTRY("%d %p %p", type, obj, cb_list); + CHECK_PARAMS( verify_object(obj) ); + CHECK_PARAMS( _OBINFO(obj).type == type ); + CHECK_PARAMS( cb_list ); + *cb_list = &obj->disp_cbs; + return 0; +} + +int fd_dict_get_error_cmd(struct dictionary * dict, struct dict_object **obj) +{ + TRACE_ENTRY("%p %p", dict, obj); + CHECK_PARAMS( dict && (dict->dict_eyec == DICT_EYECATCHER) && obj ); + *obj = &dict->dict_cmd_error; + return 0; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libfreediameter/init.c Fri Aug 28 19:14:42 2009 +0900 @@ -0,0 +1,53 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis <sdecugis@nict.go.jp> * +* * +* Copyright (c) 2009, WIDE Project and NICT * +* All rights reserved. * +* * +* Redistribution and use of this software in source and binary forms, with or without modification, are * +* permitted provided that the following conditions are met: * +* * +* * Redistributions of source code must retain the above * +* copyright notice, this list of conditions and the * +* following disclaimer. * +* * +* * 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. * +* * +* * Neither the name of the WIDE Project or NICT nor the * +* names of its contributors may be used to endorse or * +* promote products derived from this software without * +* specific prior written permission of WIDE Project and * +* NICT. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. * +*********************************************************************************************************/ + +#include "libfd.h" + +int fd_lib_init(void) +{ + int ret = 0; + + /* Create the thread key that contains thread name for debug messages */ + ret = pthread_key_create(&fd_log_thname, free); + if (ret != 0) { + fprintf(stderr, "Error initializing the libfreediameter library: %s\n", strerror(ret) ); + return ret; + } + + /* Initialize the end-to-end id counter with random value as described in RFC3588 */ + fd_msg_eteid_init(); + + return 0; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libfreediameter/libfd.h Fri Aug 28 19:14:42 2009 +0900 @@ -0,0 +1,50 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis <sdecugis@nict.go.jp> * +* * +* Copyright (c) 2009, WIDE Project and NICT * +* All rights reserved. * +* * +* Redistribution and use of this software in source and binary forms, with or without modification, are * +* permitted provided that the following conditions are met: * +* * +* * Redistributions of source code must retain the above * +* copyright notice, this list of conditions and the * +* following disclaimer. * +* * +* * 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. * +* * +* * Neither the name of the WIDE Project or NICT nor the * +* names of its contributors may be used to endorse or * +* promote products derived from this software without * +* specific prior written permission of WIDE Project and * +* NICT. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. * +*********************************************************************************************************/ + +/* This file contains the definitions for internal use in the libfreediameter library */ + +#ifndef _LIBFD_H +#define _LIBFD_H + +#include <freediameter/freediameter-host.h> +#include <freediameter/libfreediameter.h> + +/* Internal to the library */ +extern const char * type_base_name[]; +void fd_msg_eteid_init(void); + + + +#endif /* _LIBFD_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libfreediameter/lists.c Fri Aug 28 19:14:42 2009 +0900 @@ -0,0 +1,273 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis <sdecugis@nict.go.jp> * +* * +* Copyright (c) 2009, WIDE Project and NICT * +* All rights reserved. * +* * +* Redistribution and use of this software in source and binary forms, with or without modification, are * +* permitted provided that the following conditions are met: * +* * +* * Redistributions of source code must retain the above * +* copyright notice, this list of conditions and the * +* following disclaimer. * +* * +* * 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. * +* * +* * Neither the name of the WIDE Project or NICT nor the * +* names of its contributors may be used to endorse or * +* promote products derived from this software without * +* specific prior written permission of WIDE Project and * +* NICT. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. * +*********************************************************************************************************/ + +#include "libfd.h" + +/* Initialize a list element */ +void fd_list_init ( struct fd_list * list, void * obj ) +{ + memset(list, 0, sizeof(struct fd_list)); + list->next = list; + list->prev = list; + list->head = list; + list->o = obj; +} + +#define CHECK_SINGLE( li ) { \ + ASSERT( FD_LIST(li)->next == (li) ); \ + ASSERT( FD_LIST(li)->prev == (li) ); \ + ASSERT( FD_LIST(li)->head == (li) ); \ +} + +/* insert after a reference, checks done */ +static void list_insert_after( struct fd_list * ref, struct fd_list * item ) +{ + item->prev = ref; + item->next = ref->next; + item->head = ref->head; + ref->next->prev = item; + ref->next = item; +} + +/* insert after a reference */ +void fd_list_insert_after ( struct fd_list * ref, struct fd_list * item ) +{ + ASSERT(item != NULL); + ASSERT(ref != NULL); + CHECK_SINGLE ( item ); + ASSERT(ref->head != item); + list_insert_after(ref, item); +} + +/* insert before a reference, checks done */ +static void list_insert_before ( struct fd_list * ref, struct fd_list * item ) +{ + item->prev = ref->prev; + item->next = ref; + item->head = ref->head; + ref->prev->next = item; + ref->prev = item; +} + +/* insert before a reference */ +void fd_list_insert_before ( struct fd_list * ref, struct fd_list * item ) +{ + ASSERT(item != NULL); + ASSERT(ref != NULL); + CHECK_SINGLE ( item ); + ASSERT(ref->head != item); + list_insert_before(ref, item); +} + +/* Insert an item in an ordered list -- ordering function provided. If duplicate object found, it is returned in ref_duplicate */ +int fd_list_insert_ordered( struct fd_list * head, struct fd_list * item, int (*cmp_fct)(void *, void *), void ** ref_duplicate) +{ + struct fd_list * ptr = head; + int cmp; + + /* Some debug sanity checks */ + ASSERT(head != NULL); + ASSERT(item != NULL); + ASSERT(cmp_fct != NULL); + ASSERT(head->head == head); + CHECK_SINGLE ( item ); + + /* loop in the list */ + while (ptr->next != head) + { + /* Compare the object to insert with the next object in list */ + cmp = cmp_fct( item->o, ptr->next->o ); + if (!cmp) { + /* An element with the same key already exists */ + if (ref_duplicate != NULL) + *ref_duplicate = ptr->next->o; + return EEXIST; + } + + if (cmp < 0) + break; /* We must insert the element here */ + + ptr = ptr->next; + } + + /* Now insert the element between ptr and ptr->next */ + list_insert_after( ptr, item ); + + /* Ok */ + return 0; +} + +/* Unlink an object */ +void fd_list_unlink ( struct fd_list * item ) +{ + ASSERT(item != NULL); + if (item->head == item) + return; + /* unlink */ + item->next->prev = item->prev; + item->prev->next = item->next; + /* sanitize */ + item->next = item; + item->prev = item; + item->head = item; +} + + +/********************************************************************************************************/ +/* Hash function -- credits to Austin Appleby, thank you ^^ */ +/* See http://murmurhash.googlepages.com for more information on this function */ + +/* the strings are NOT always aligned properly (ex: received in RADIUS message), so we use the aligned MurmurHash2 function as needed */ +#define _HASH_MIX(h,k,m) { k *= m; k ^= k >> r; k *= m; h *= m; h ^= k; } +uint32_t fd_hash ( char * string, size_t len ) +{ + uint32_t hash = len; + char * data = string; + + const unsigned int m = 0x5bd1e995; + const int r = 24; + int align = (long)string & 3; + + if (!align || (len < 4)) { + + /* In case data is aligned, MurmurHash2 function */ + while(len >= 4) + { + /* Mix 4 bytes at a time into the hash */ + uint32_t k = *(uint32_t *)data; /* We don't care about the byte order */ + + _HASH_MIX(hash, k, m); + + data += 4; + len -= 4; + } + + /* Handle the last few bytes of the input */ + switch(len) { + case 3: hash ^= data[2] << 16; + case 2: hash ^= data[1] << 8; + case 1: hash ^= data[0]; + hash *= m; + } + + } else { + /* Unaligned data, use alignment-safe slower version */ + + /* Pre-load the temp registers */ + uint32_t t = 0, d = 0; + switch(align) + { + case 1: t |= data[2] << 16; + case 2: t |= data[1] << 8; + case 3: t |= data[0]; + } + t <<= (8 * align); + + data += 4-align; + len -= 4-align; + + /* From this point, "data" can be read by chunks of 4 bytes */ + + int sl = 8 * (4-align); + int sr = 8 * align; + + /* Mix */ + while(len >= 4) + { + uint32_t k; + + d = *(unsigned int *)data; + k = (t >> sr) | (d << sl); + + _HASH_MIX(hash, k, m); + + t = d; + + data += 4; + len -= 4; + } + + /* Handle leftover data in temp registers */ + d = 0; + if(len >= align) + { + uint32_t k; + + switch(align) + { + case 3: d |= data[2] << 16; + case 2: d |= data[1] << 8; + case 1: d |= data[0]; + } + + k = (t >> sr) | (d << sl); + _HASH_MIX(hash, k, m); + + data += align; + len -= align; + + /* Handle tail bytes */ + + switch(len) + { + case 3: hash ^= data[2] << 16; + case 2: hash ^= data[1] << 8; + case 1: hash ^= data[0]; + hash *= m; + }; + } + else + { + switch(len) + { + case 3: d |= data[2] << 16; + case 2: d |= data[1] << 8; + case 1: d |= data[0]; + case 0: hash ^= (t >> sr) | (d << sl); + hash *= m; + } + } + + + } + + /* Do a few final mixes of the hash to ensure the last few + bytes are well-incorporated. */ + hash ^= hash >> 13; + hash *= m; + hash ^= hash >> 15; + + return hash; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libfreediameter/log.c Fri Aug 28 19:14:42 2009 +0900 @@ -0,0 +1,107 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis <sdecugis@nict.go.jp> * +* * +* Copyright (c) 2009, WIDE Project and NICT * +* All rights reserved. * +* * +* Redistribution and use of this software in source and binary forms, with or without modification, are * +* permitted provided that the following conditions are met: * +* * +* * Redistributions of source code must retain the above * +* copyright notice, this list of conditions and the * +* following disclaimer. * +* * +* * 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. * +* * +* * Neither the name of the WIDE Project or NICT nor the * +* names of its contributors may be used to endorse or * +* promote products derived from this software without * +* specific prior written permission of WIDE Project and * +* NICT. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. * +*********************************************************************************************************/ + +#include "libfd.h" + +#include <stdarg.h> + +pthread_mutex_t fd_log_lock = PTHREAD_MUTEX_INITIALIZER; +pthread_key_t fd_log_thname; + +/* Log a debug message */ +void fd_log_debug ( char * format, ... ) +{ + va_list ap; + + (void)pthread_mutex_lock(&fd_log_lock); + + pthread_cleanup_push(fd_cleanup_mutex, &fd_log_lock); + + va_start(ap, format); + vfprintf( stdout, format, ap); + va_end(ap); + fflush(stdout); + + pthread_cleanup_pop(0); + + (void)pthread_mutex_unlock(&fd_log_lock); +} + +/* Function to set the thread's friendly name */ +void fd_log_threadname ( char * name ) +{ + void * val = NULL; + + TRACE_ENTRY("%p(%s)", name, name?:"/"); + + /* First, check if a value is already assigned to the current thread */ + val = pthread_getspecific(fd_log_thname); + if (val != NULL) { + TRACE_DEBUG(FULL, "Freeing old thread name: %s", val); + free(val); + } + + /* Now create the new string */ + if (name == NULL) { + CHECK_POSIX_DO( pthread_setspecific(fd_log_thname, NULL), /* continue */); + return; + } + + CHECK_MALLOC_DO( val = strdup(name), return ); + + CHECK_POSIX_DO( pthread_setspecific(fd_log_thname, val), /* continue */); + return; +} + +/* Write current time into a buffer */ +char * fd_log_time ( char * buf, size_t len ) +{ + int ret; + size_t offset = 0; + struct timespec tp; + struct tm tm; + + /* Get current time */ + ret = clock_gettime(CLOCK_REALTIME, &tp); + if (ret != 0) { + snprintf(buf, len, "%s", strerror(ret)); + return buf; + } + + offset += strftime(buf + offset, len - offset, "%D,%T", localtime_r( &tp.tv_sec , &tm )); + offset += snprintf(buf + offset, len - offset, ".%6.6ld", tp.tv_nsec / 1000); + + return buf; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libfreediameter/messages.c Fri Aug 28 19:14:42 2009 +0900 @@ -0,0 +1,2139 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis <sdecugis@nict.go.jp> * +* * +* Copyright (c) 2009, WIDE Project and NICT * +* All rights reserved. * +* * +* Redistribution and use of this software in source and binary forms, with or without modification, are * +* permitted provided that the following conditions are met: * +* * +* * Redistributions of source code must retain the above * +* copyright notice, this list of conditions and the * +* following disclaimer. * +* * +* * 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. * +* * +* * Neither the name of the WIDE Project or NICT nor the * +* names of its contributors may be used to endorse or * +* promote products derived from this software without * +* specific prior written permission of WIDE Project and * +* NICT. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. * +*********************************************************************************************************/ + +/* Messages module. + * + * This module allows to manipulate the msg and avp structures that represents a Diameter message in memory. + */ + +#include "libfd.h" + +#include <sys/param.h> + +/* Type of object */ +enum msg_objtype { + MSG_MSG = 1, + MSG_AVP +}; + +/* Chaining of elements as a free hierarchy */ +struct msg_avp_chain { + struct fd_list chaining; /* Chaining information at this level. */ + struct fd_list children; /* sentinel for the children of this object */ + enum msg_objtype type; /* Type of this object, _MSG_MSG or _MSG_AVP */ +}; + +/* Return the chain information from an AVP or MSG. Since it's the first field, we just cast */ +#define _C(_x) ((struct msg_avp_chain *)(_x)) + +/* Some details about chaining: + * + * A message is made of a header ( msg ) and 0 or more AVPs ( avp ). + * The structure is a kind of tree, where some AVPs (grouped AVPs) can contain other AVPs. + * Exemple: + * msg + * |-avp + * |-gavp + * | |-avp + * | |-avp + * | \-avp + * |-avp + * \-avp + * + * Each item (msg or avp) structure begins with a msg_avp_chain structure. + * The element at the top of the hierarchy (msg in our example) has all the fields of its "chaining" equal to the same value. + * + * All elements at the same level are linked by their "chaining" list. + * The "children" list is the sentinel for the lists of children of this element. + */ + +/* The following definitions are used to recognize objects in memory. */ +#define MSG_MSG_EYEC (0x11355463) +#define MSG_AVP_EYEC (0x11355467) + +/* The following structure represents an AVP instance. */ +struct avp { + struct msg_avp_chain avp_chain; /* Chaining information of this AVP */ + int avp_eyec; /* Must be equal to MSG_AVP_EYEC */ + struct dict_object *avp_model; /* If not NULL, pointer to the dictionary object of this avp */ + struct avp_hdr avp_public; /* AVP data that can be managed by other modules */ + + uint8_t *avp_source; /* If the message was parsed from a buffer, pointer to the AVP data start in the buffer. */ + uint8_t *avp_rawdata; /* when the data can not be interpreted, the raw data is copied here. The header is not part of it. */ + size_t avp_rawlen; /* The length of the raw buffer. */ + union avp_value avp_storage; /* To avoid many alloc/free, store the integer values here and set avp_public.avp_data to &storage */ + int avp_mustfreeos; /* 1 if an octetstring is malloc'd in avp_storage and must be freed. */ +}; + +/* Macro to compute the AVP header size */ +#define AVPHDRSZ_NOVEND 8 +#define AVPHDRSZ_VENDOR 12 +#define GETAVPHDRSZ( _flag ) ((_flag & AVP_FLAG_VENDOR) ? AVPHDRSZ_VENDOR : AVPHDRSZ_NOVEND) + +/* Macro to cast a msg_avp_t */ +#define _A(_x) ((struct avp *)(_x)) +/* Check the type and eyecatcher */ +#define CHECK_AVP(_x) ((_C(_x)->type == MSG_AVP) && (_A(_x)->avp_eyec == MSG_AVP_EYEC)) + +/* The following structure represents an instance of a message (command and children AVPs). */ +struct msg { + struct msg_avp_chain msg_chain; /* List of the AVPs in the message */ + int msg_eyec; /* Must be equal to MSG_MSG_EYEC */ + struct dict_object *msg_model; /* If not NULL, pointer to the dictionary object of this message */ + struct msg_hdr msg_public; /* Message data that can be managed by extensions. */ + + uint8_t *msg_rawbuffer; /* data buffer that was received, saved during fd_msg_parse_buffer and freed in fd_msg_parse_dict */ + int msg_routable; /* Is this a routable message? (0: undef, 1: routable, 2: non routable) */ + struct msg *msg_query; /* the associated query if the message is a received answer */ + struct fd_list *msg_rtlist; /* Routing list for the query */ + struct { + void (*fct)(void *, struct msg **); + void * data; + } msg_cb; /* Callback to be called when an answer is received, if not NULL */ + char * msg_src_id; /* Diameter Id of the peer this message was received from. This string is malloc'd and must be freed */ + uint32_t msg_src_hash; /* Hash of the msg_src_id value */ +}; + +/* Macro to compute the message header size */ +#define GETMSGHDRSZ() 20 + +/* Macro to cast a msg_avp_t */ +#define _M(_x) ((struct msg *)(_x)) +/* Check the type and eyecatcher */ +#define CHECK_MSG(_x) ((_C(_x)->type == MSG_MSG) && (_M(_x)->msg_eyec == MSG_MSG_EYEC)) + +#define VALIDATE_OBJ(_x) ( (CHECK_MSG(_x)) || (CHECK_AVP(_x)) ) + + +/* Macro to validate a MSGFL_ value */ +#define CHECK_MSGFL(_fl) ( ((_fl) & (- (MSGFL_MAX << 1) )) == 0 ) + + +/* initial sizes of AVP from their types, in bytes. */ +static int avp_value_sizes[] = { + 0, /* AVP_TYPE_GROUPED: size is dynamic */ + 0, /* AVP_TYPE_OCTETSTRING: size is dynamic */ + 4, /* AVP_TYPE_INTEGER32: size is 32 bits */ + 8, /* AVP_TYPE_INTEGER64: size is 64 bits */ + 4, /* AVP_TYPE_UNSIGNED32: size is 32 bits */ + 8, /* AVP_TYPE_UNSIGNED64: size is 64 bits */ + 4, /* AVP_TYPE_FLOAT32: size is 32 bits */ + 8 /* AVP_TYPE_FLOAT64: size is 64 bits */ +}; +#define CHECK_BASETYPE( _type ) ( ((_type) <= AVP_TYPE_MAX) && ((_type) >= 0) ) +#define GETINITIALSIZE( _type, _vend ) (avp_value_sizes[ CHECK_BASETYPE(_type) ? (_type) : 0] + GETAVPHDRSZ(_vend)) + +/* Forward declaration */ +static int parsedict_do_msg(struct dictionary * dict, struct msg * msg, int only_hdr); + +/***************************************************************************************************************/ +/* Creating objects */ + +/* Initialize a msg_avp_chain structure */ +static void init_chain(struct msg_avp_chain * chain, int type) +{ + fd_list_init( &chain->chaining, (void *)chain); + fd_list_init( &chain->children, (void *)chain); + chain->type = type; +} + +/* Initialize a new AVP object */ +static void init_avp ( struct avp * avp ) +{ + TRACE_ENTRY("%p", avp); + + memset(avp, 0, sizeof(struct avp)); + init_chain( &avp->avp_chain, MSG_AVP); + avp->avp_eyec = MSG_AVP_EYEC; +} + +/* Initialize a new MSG object */ +static void init_msg ( struct msg * msg ) +{ + TRACE_ENTRY("%p", msg); + + memset(msg, 0, sizeof(struct msg)); + init_chain( &msg->msg_chain, MSG_MSG); + msg->msg_eyec = MSG_MSG_EYEC; +} + + +/* Create a new AVP instance */ +int fd_msg_avp_new ( struct dict_object * model, int flags, struct avp ** avp ) +{ + struct avp *new = NULL; + + TRACE_ENTRY("%p %x %p", model, flags, avp); + + /* Check the parameters */ + CHECK_PARAMS( avp && CHECK_MSGFL(flags) ); + + if (model) { + enum dict_object_type dicttype; + CHECK_PARAMS( (fd_dict_gettype(model, &dicttype) == 0) && (dicttype == DICT_AVP) ); + } + + /* Create a new object */ + CHECK_MALLOC( new = malloc (sizeof(struct avp)) ); + + /* Initialize the fields */ + init_avp(new); + + if (model) { + struct dict_avp_data dictdata; + + CHECK_FCT( fd_dict_getval(model, &dictdata) ); + + new->avp_model = model; + new->avp_public.avp_code = dictdata.avp_code; + new->avp_public.avp_flags = dictdata.avp_flag_val; + new->avp_public.avp_len = GETINITIALSIZE(dictdata.avp_basetype, dictdata.avp_flag_val ); + new->avp_public.avp_vendor = dictdata.avp_vendor; + } + + /* The new object is ready, return */ + *avp = new; + return 0; +} + +/* Create a new message instance */ +int fd_msg_new ( struct dict_object * model, int flags, struct msg ** msg ) +{ + struct msg * new = NULL; + + TRACE_ENTRY("%p %x %p", model, flags, msg); + + /* Check the parameters */ + CHECK_PARAMS( msg && CHECK_MSGFL(flags) ); + + if (model) { + enum dict_object_type dicttype; + CHECK_PARAMS( (fd_dict_gettype(model, &dicttype) == 0) && (dicttype == DICT_COMMAND) ); + } + + /* Create a new object */ + CHECK_MALLOC( new = malloc (sizeof(struct msg)) ); + + /* Initialize the fields */ + init_msg(new); + new->msg_public.msg_version = DIAMETER_VERSION; + new->msg_public.msg_length = GETMSGHDRSZ(); /* This will be updated later */ + + if (model) { + struct dictionary *dict; + struct dict_cmd_data dictdata; + struct dict_object *dictappl; + + CHECK_FCT( fd_dict_getdict(model, &dict) ); + CHECK_FCT( fd_dict_getval(model, &dictdata) ); + + new->msg_model = model; + new->msg_public.msg_flags = dictdata.cmd_flag_val; + new->msg_public.msg_code = dictdata.cmd_code; + + /* Initialize application from the parent, if any */ + CHECK_FCT( fd_dict_search( dict, DICT_APPLICATION, APPLICATION_OF_COMMAND, model, &dictappl, 0) ); + if (dictappl != NULL) { + struct dict_application_data appdata; + CHECK_FCT( fd_dict_getval(dictappl, &appdata) ); + new->msg_public.msg_appl = appdata.application_id; + } + } + + if (flags & MSGFL_ALLOC_ETEID) { + new->msg_public.msg_eteid = fd_msg_eteid_get(); + } + + /* The new object is ready, return */ + *msg = new; + return 0; +} + +/* Create answer from a request */ +int fd_msg_new_answer_from_req ( struct dictionary * dict, struct msg ** msg, int flags ) +{ + struct dict_object * model = NULL; + struct msg *qry, *ans; + + TRACE_ENTRY("%p %x", msg, flags); + + /* Check the parameters */ + CHECK_PARAMS( msg ); + qry = *msg; + CHECK_PARAMS( CHECK_MSG(qry) && (qry->msg_public.msg_flags & CMD_FLAG_REQUEST) ); + + /* Find the model for the answer */ + if (flags & MSGFL_ANSW_ERROR) { + /* The model is the generic error format */ + CHECK_FCT( fd_dict_get_error_cmd(dict, &model) ); + } else { + /* The model is the answer corresponding to the query. It supposes that these are defined in the dictionary */ + CHECK_FCT_DO( parsedict_do_msg( dict, qry, 1), /* continue */ ); + if (qry->msg_model) { + CHECK_FCT( fd_dict_search ( dict, DICT_COMMAND, CMD_ANSWER, qry->msg_model, &model, EINVAL ) ); + } + } + + /* Create the answer */ + CHECK_FCT( fd_msg_new( model, flags, &ans ) ); + + /* Set informations in the answer as in the query */ + ans->msg_public.msg_code = qry->msg_public.msg_code; /* useful for MSGFL_ANSW_ERROR */ + ans->msg_public.msg_appl = qry->msg_public.msg_appl; + ans->msg_public.msg_eteid = qry->msg_public.msg_eteid; + ans->msg_public.msg_hbhid = qry->msg_public.msg_hbhid; + + /* associate with query */ + /* may do CHECK_FCT( msg_answ_associate( *msg, (msg_t *)qry ) ); but this is quicker */ + ans->msg_query = qry; + + /* Done */ + *msg = ans; + return 0; +} + +/***************************************************************************************************************/ + +/* Explore a message */ +int fd_msg_browse_internal ( msg_or_avp * reference, enum msg_brw_dir dir, msg_or_avp ** found, int * depth ) +{ + struct msg_avp_chain *result = NULL; + int diff = 0; + struct fd_list *li = NULL; + + TRACE_ENTRY("%p %d %p %p", reference, dir, found, depth); + + /* Initialize the "found" result if any */ + if (found) + *found = NULL; + + /* Check the parameters */ + CHECK_PARAMS( VALIDATE_OBJ(reference) ); + + TRACE_DEBUG(FCTS, "chaining(%p): nxt:%p prv:%p hea:%p top:%p", + &_C(reference)->chaining, + _C(reference)->chaining.next, + _C(reference)->chaining.prev, + _C(reference)->chaining.head, + _C(reference)->chaining.o); + TRACE_DEBUG(FCTS, "children(%p): nxt:%p prv:%p hea:%p top:%p", + &_C(reference)->children, + _C(reference)->children.next, + _C(reference)->children.prev, + _C(reference)->children.head, + _C(reference)->children.o); + + /* Now search */ + switch (dir) { + case MSG_BRW_NEXT: + /* Check the reference is an AVP */ + CHECK_PARAMS( _C(reference)->type == MSG_AVP ); + + li = &_C(reference)->chaining; + + /* Check if the next element is not the sentinel ( ==> the parent) */ + if (li->next != li->head) + result = _C(li->next->o); + break; + + case MSG_BRW_PREV: + /* Check the reference is an AVP */ + CHECK_PARAMS( _C(reference)->type == MSG_AVP ); + + li = &_C(reference)->chaining; + + /* Check if the prev element is not the sentinel ( ==> the parent) */ + if (li->prev != li->head) + result = _C(li->prev->o); + break; + + case MSG_BRW_FIRST_CHILD: + li = &_C(reference)->children; + if (! FD_IS_LIST_EMPTY(li)) { + result = _C(li->next->o); + diff = 1; + } + break; + + case MSG_BRW_LAST_CHILD: + li = &_C(reference)->children; + if (! FD_IS_LIST_EMPTY(li)) { + result = _C(li->prev->o); + diff = 1; + } + break; + + case MSG_BRW_PARENT: + /* If the object is not chained, it has no parent */ + li = &_C(reference)->chaining; + if (li != li->head) { + /* The sentinel is the parent's children list */ + result = _C(li->head->o); + diff = -1; + } + break; + + case MSG_BRW_WALK: + /* First, try to find a child */ + li = &_C(reference)->children; + if ( ! FD_IS_LIST_EMPTY(li) ) { + result = _C(li->next->o); + diff = 1; + break; + } + + /* Then try to find a "next" at this level or one of the parent's */ + li = &_C(reference)->chaining; + do { + /* If this element has a "next" element, return it */ + if (li->next != li->head) { + result = _C(li->next->o); + break; + } + /* otherwise, check if we have a parent */ + if (li == li->head) { + /* no parent */ + break; + } + /* Go to the parent's chaining information and loop */ + diff -= 1; + li = &_C(li->head->o)->chaining; + } while (1); + break; + + default: + /* Other directions are invalid */ + CHECK_PARAMS( dir = 0 ); + } + + /* Save the found object, if any */ + if (found && result) + *found = (void *)result; + + /* Modify the depth according to the walk direction */ + if (depth && diff) + (*depth) += diff; + + /* Return ENOENT if found was NULL */ + if ((!found) && (!result)) + return ENOENT; + else + return 0; +} + +/* Add an AVP into a tree */ +int fd_msg_avp_add ( msg_or_avp * reference, enum msg_brw_dir dir, struct avp *avp) +{ + TRACE_ENTRY("%p %d %p", reference, dir, avp); + + /* Check the parameters */ + CHECK_PARAMS( VALIDATE_OBJ(reference) && CHECK_AVP(avp) && FD_IS_LIST_EMPTY(&avp->avp_chain.chaining) ); + + /* Now insert */ + switch (dir) { + case MSG_BRW_NEXT: + /* Check the reference is an AVP -- we do not chain AVPs at same level as msgs. */ + CHECK_PARAMS( _C(reference)->type == MSG_AVP ); + + /* Insert the new avp after the reference */ + fd_list_insert_after( &_A(reference)->avp_chain.chaining, &avp->avp_chain.chaining ); + break; + + case MSG_BRW_PREV: + /* Check the reference is an AVP */ + CHECK_PARAMS( _C(reference)->type == MSG_AVP ); + + /* Insert the new avp before the reference */ + fd_list_insert_before( &_A(reference)->avp_chain.chaining, &avp->avp_chain.chaining ); + break; + + case MSG_BRW_FIRST_CHILD: + /* Insert the new avp after the children sentinel */ + fd_list_insert_after( &_C(reference)->children, &avp->avp_chain.chaining ); + break; + + case MSG_BRW_LAST_CHILD: + /* Insert the new avp before the children sentinel */ + fd_list_insert_before( &_C(reference)->children, &avp->avp_chain.chaining ); + break; + + default: + /* Other directions are invalid */ + CHECK_PARAMS( dir = 0 ); + } + + return 0; +} + +/* Search a given AVP model in a message */ +int fd_msg_search_avp ( struct msg * msg, struct dict_object * what, struct avp ** avp ) +{ + struct avp * nextavp; + struct dict_avp_data dictdata; + enum dict_object_type dicttype; + + TRACE_ENTRY("%p %p %p", msg, what, avp); + + CHECK_PARAMS( CHECK_MSG(msg) && what ); + + CHECK_PARAMS( (fd_dict_gettype(what, &dicttype) == 0) && (dicttype == DICT_AVP) ); + CHECK_FCT( fd_dict_getval(what, &dictdata) ); + + /* Loop on all top AVPs */ + CHECK_FCT( fd_msg_browse(msg, MSG_BRW_FIRST_CHILD, (void *)&nextavp, NULL) ); + while (nextavp) { + + if ( (nextavp->avp_public.avp_code == dictdata.avp_code) + && (nextavp->avp_public.avp_vendor == dictdata.avp_vendor) ) /* always 0 if no V flag */ + break; + + /* Otherwise move to next AVP in the message */ + CHECK_FCT( fd_msg_browse(nextavp, MSG_BRW_NEXT, (void *)&nextavp, NULL) ); + } + + if (avp) + *avp = nextavp; + + if (avp && nextavp) { + struct dictionary * dict; + CHECK_FCT( fd_dict_getdict( what, &dict) ); + CHECK_FCT_DO( fd_msg_parse_dict( nextavp, dict ), /* nothing */ ); + } + + if (avp || nextavp) + return 0; + else + return ENOENT; +} + + +/***************************************************************************************************************/ +/* Deleting objects */ + +/* Destroy and free an AVP or message */ +static int destroy_obj (struct msg_avp_chain * obj ) +{ + TRACE_ENTRY("%p", obj); + + /* Check the parameter is a valid object */ + CHECK_PARAMS( VALIDATE_OBJ(obj) && FD_IS_LIST_EMPTY( &obj->children ) ); + + /* Unlink this object if needed */ + fd_list_unlink( &obj->chaining ); + + /* Free the octetstring if needed */ + if ((obj->type == MSG_AVP) && (_A(obj)->avp_mustfreeos == 1)) { + free(_A(obj)->avp_storage.os.data); + } + /* Free the rawdata if needed */ + if ((obj->type == MSG_AVP) && (_A(obj)->avp_rawdata != NULL)) { + free(_A(obj)->avp_rawdata); + } + if ((obj->type == MSG_MSG) && (_M(obj)->msg_rawbuffer != NULL)) { + free(_M(obj)->msg_rawbuffer); + } + + if ((obj->type == MSG_MSG) && (_M(obj)->msg_src_id != NULL)) { + free(_M(obj)->msg_src_id); + } + + if ((obj->type == MSG_MSG) && (_M(obj)->msg_rtlist != NULL)) { + while (! FD_IS_LIST_EMPTY(_M(obj)->msg_rtlist) ) { + struct fd_list * li = _M(obj)->msg_rtlist->next; + fd_list_unlink(li); + free(li); + } + + free(_M(obj)->msg_rtlist); + } + + /* free the object */ + free(obj); + + return 0; +} + +/* Destroy an object and all its children */ +static void destroy_tree(struct msg_avp_chain * obj) +{ + struct fd_list *rem; + + TRACE_ENTRY("%p", obj); + + /* Destroy any subtree */ + while ( (rem = obj->children.next) != &obj->children) + destroy_tree(_C(rem->o)); + + /* Then unlink and destroy the object */ + CHECK_FCT_DO( destroy_obj(obj), /* nothing */ ); +} + +/* Free an object and its tree */ +int fd_msg_free ( msg_or_avp * object ) +{ + TRACE_ENTRY("%p", object); + + if (CHECK_MSG(object)) { + if (_M(object)->msg_query) { + CHECK_FCT( fd_msg_free( _M(object)->msg_query ) ); + _M(object)->msg_query = NULL; + } + } + + destroy_tree(_C(object)); + return 0; +} + + +/***************************************************************************************************************/ +/* Debug functions: dumping */ + +/* indent inside an object */ +#define INOBJHDR "%*s " +#define INOBJHDRVAL indent<0 ? 1 : indent, indent<0 ? "-" : "|" + +/* Dump a msg_t object */ +static void obj_dump_msg (struct msg * msg, int indent ) +{ + int ret = 0; + + fd_log_debug("%*sMSG: %p\n", INOBJHDRVAL, msg); + + if (!CHECK_MSG(msg)) { + fd_log_debug(INOBJHDR "INVALID!\n", INOBJHDRVAL); + return; + } + + if (!msg->msg_model) { + + fd_log_debug(INOBJHDR "(no model)\n", INOBJHDRVAL); + + } else { + + enum dict_object_type dicttype; + struct dict_cmd_data dictdata; + ret = fd_dict_gettype(msg->msg_model, &dicttype); + if (ret || (dicttype != DICT_COMMAND)) { + fd_log_debug(INOBJHDR "(invalid model: %d %d)\n", INOBJHDRVAL, ret, dicttype); + goto public; + } + ret = fd_dict_getval(msg->msg_model, &dictdata); + if (ret != 0) { + fd_log_debug(INOBJHDR "(error getting model data: %s)\n", INOBJHDRVAL, strerror(ret)); + goto public; + } + fd_log_debug(INOBJHDR "model : v/m:" DUMP_CMDFL_str "/" DUMP_CMDFL_str ", %u \"%s\"\n", INOBJHDRVAL, + DUMP_CMDFL_val(dictdata.cmd_flag_val), DUMP_CMDFL_val(dictdata.cmd_flag_mask), dictdata.cmd_code, dictdata.cmd_name); + } +public: + fd_log_debug(INOBJHDR "public: V:%d L:%d fl:" DUMP_CMDFL_str " CC:%u A:%d hi:%x ei:%x\n", INOBJHDRVAL, + msg->msg_public.msg_version, + msg->msg_public.msg_length, + DUMP_CMDFL_val(msg->msg_public.msg_flags), + msg->msg_public.msg_code, + msg->msg_public.msg_appl, + msg->msg_public.msg_hbhid, + msg->msg_public.msg_eteid + ); + fd_log_debug(INOBJHDR "intern: rwb:%p rt:%d cb:%p(%p) qry:%p h:%x src:%s\n", + INOBJHDRVAL, msg->msg_rawbuffer, msg->msg_routable, msg->msg_cb.fct, msg->msg_cb.data, msg->msg_query, msg->msg_src_hash, msg->msg_src_id?:"(nil)"); +} + +#define DUMP_VALUE(_format, _parms...) fd_log_debug(INOBJHDR "value : t:'%s' v:'" _format "'\n", INOBJHDRVAL, typename, ## _parms); +/* Dump an AVP value that is not a constant */ +static void dump_basic_type(union avp_value * value, enum dict_avp_basetype type, const char * typename, int indent) +{ + switch (type) { + case AVP_TYPE_GROUPED: + DUMP_VALUE("%s", "error: grouped AVP with a value!"); + break; + + case AVP_TYPE_OCTETSTRING: + { + /* Dump only up to 16 bytes of the buffer */ + unsigned char buf[8]; + memset(buf, 0, sizeof(buf)); + memcpy(buf, value->os.data, value->os.len < sizeof(buf) ? value->os.len : sizeof(buf) ); + DUMP_VALUE("l:%d, v:%02.2X %02.2X %02.2X %02.2X %02.2X %02.2X %02.2X %02.2X ... ('%-*.*s')", + value->os.len, + buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], + value->os.len, value->os.len, value->os.data + ); + } + break; + + case AVP_TYPE_INTEGER32: + DUMP_VALUE("%i",value->i32); + break; + + case AVP_TYPE_INTEGER64: + DUMP_VALUE("%lli (0x%llx)",value->i64,value->i64); + break; + + case AVP_TYPE_UNSIGNED32: + DUMP_VALUE("%u",value->u32); + break; + + case AVP_TYPE_UNSIGNED64: + DUMP_VALUE("%llu",value->u64); + break; + + case AVP_TYPE_FLOAT32: + DUMP_VALUE("%f",value->f32); + break; + + case AVP_TYPE_FLOAT64: + DUMP_VALUE("%g",value->f64); + break; + + default: + DUMP_VALUE("%s %d", "error: invalid type :", type); + } +} + +/* Dump an AVP value that is a constant */ +#define DUMP_CONST(_format, _parms...) fd_log_debug(INOBJHDR "value : t:'%s' v:'%s' ( " _format " )\n", INOBJHDRVAL, typename, value->enum_name, ## _parms); +static void dump_constant_type(struct dict_enumval_data * value, enum dict_avp_basetype type, char * typename, int indent) +{ + switch (type) { + case AVP_TYPE_GROUPED: + DUMP_CONST("%s", "error: grouped AVP with a constant value!"); + break; + case AVP_TYPE_OCTETSTRING: + DUMP_CONST("%s", "value skipped"); + break; + + case AVP_TYPE_INTEGER32: + DUMP_CONST("%i",value->enum_value.i32); + break; + + case AVP_TYPE_INTEGER64: + DUMP_CONST("%li",value->enum_value.i64); + break; + + case AVP_TYPE_UNSIGNED32: + DUMP_CONST("%u",value->enum_value.u32); + break; + + case AVP_TYPE_UNSIGNED64: + DUMP_CONST("%lu",value->enum_value.u64); + break; + + case AVP_TYPE_FLOAT32: + DUMP_CONST("%f",value->enum_value.f32); + break; + + case AVP_TYPE_FLOAT64: + DUMP_CONST("%g",value->enum_value.f64); + break; + + default: + DUMP_CONST("%s %d", "error: invalid type :", type); + } +} + +/* Dump an avp object */ +static void obj_dump_avp ( struct avp * avp, int indent ) +{ + int ret = 0; + enum dict_avp_basetype type = -1; + + if (!CHECK_AVP(avp)) { + fd_log_debug(INOBJHDR "INVALID!\n", INOBJHDRVAL); + return; + } + + if (!avp->avp_model) { + + fd_log_debug(INOBJHDR "(no model)\n", INOBJHDRVAL); + + } else { + + enum dict_object_type dicttype; + struct dict_avp_data dictdata; + ret = fd_dict_gettype(avp->avp_model, &dicttype); + if (ret || (dicttype != DICT_AVP)) { + fd_log_debug(INOBJHDR "(invalid model: %d %d)\n", INOBJHDRVAL, ret, dicttype); + goto public; + } + ret = fd_dict_getval(avp->avp_model, &dictdata); + if (ret != 0) { + fd_log_debug(INOBJHDR "(error getting model data: %s)\n", INOBJHDRVAL, strerror(ret)); + goto public; + } + fd_log_debug(INOBJHDR "model : v/m:" DUMP_AVPFL_str "/" DUMP_AVPFL_str ", %12s, %u \"%s\"\n", INOBJHDRVAL, + DUMP_AVPFL_val(dictdata.avp_flag_val), + DUMP_AVPFL_val(dictdata.avp_flag_mask), + type_base_name[dictdata.avp_basetype], + dictdata.avp_code, + dictdata.avp_name ); + type = dictdata.avp_basetype; + } +public: + fd_log_debug(INOBJHDR "public: C:%u fl:" DUMP_AVPFL_str " L:%d V:%u data:@%p\n", INOBJHDRVAL, + avp->avp_public.avp_code, + DUMP_AVPFL_val(avp->avp_public.avp_flags), + avp->avp_public.avp_len, + avp->avp_public.avp_vendor, + avp->avp_public.avp_value + ); + /* Dump the value if set */ + if (avp->avp_public.avp_value) { + if (!avp->avp_model) { + fd_log_debug(INOBJHDR "(data set but no model: ERROR)\n", INOBJHDRVAL); + } else { + /* Try and find a constant name for this value */ + struct dictionary * dict = NULL; + struct dict_object * avp_type = NULL; + struct dict_object * avp_constant = NULL; + struct dict_type_data type_data; + struct dict_enumval_request request; + ret = fd_dict_getdict(avp->avp_model, & dict); + if (ret != 0) { + dump_basic_type(avp->avp_public.avp_value, type, type_base_name[type], indent); + goto end; + } + ret = fd_dict_search(dict, DICT_TYPE, TYPE_OF_AVP, avp->avp_model, &avp_type, ENOENT); + if (ret != 0) { + dump_basic_type(avp->avp_public.avp_value, type, type_base_name[type], indent); + goto end; + } + ret = fd_dict_getval(avp_type, &type_data); + if (ret != 0) { + dump_basic_type(avp->avp_public.avp_value, type, "(error getting type data)", indent); + goto end; + } + if (type_data.type_base != type) { + dump_basic_type(avp->avp_public.avp_value, type, "(mismatching type information!)", indent); + goto end; + } + /* Create a query for a constant */ + memset(&request, 0, sizeof(request)); + request.type_obj = avp_type; + memcpy(&request.search.enum_value, avp->avp_public.avp_value, sizeof(union avp_value)); + ret = fd_dict_search(dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &request, &avp_constant, ENOENT); + if (ret != 0) { + dump_basic_type(avp->avp_public.avp_value, type, type_data.type_name, indent); + goto end; + } + /* get the constant's information; we re-use request.search field */ + ret = fd_dict_getval(avp_constant, &request.search); + if (ret != 0) { + dump_basic_type(avp->avp_public.avp_value, type, "(error getting constant data)", indent); + goto end; + } + dump_constant_type(&request.search, type, type_data.type_name, indent); + } + } +end: + fd_log_debug(INOBJHDR "intern: src:%p mf:%d raw:%p(%d)\n", INOBJHDRVAL, avp->avp_source, avp->avp_mustfreeos, avp->avp_rawdata, avp->avp_rawlen); +} + +/* Dump a single object content */ +static void msg_dump_intern ( int level, msg_or_avp * obj, int indent ) +{ + /* Log only if we are at least at level */ + if ( ! TRACE_BOOL(level) ) + return; + + /* Check the object */ + if (!VALIDATE_OBJ(obj)) { + fd_log_debug( ">>> invalid object (%p)!.\n", obj); + return; + } + + /* Dump the object */ + switch (_C(obj)->type) { + case MSG_AVP: + obj_dump_avp ( _A(obj), indent ); + break; + + case MSG_MSG: + obj_dump_msg ( _M(obj), indent ); + break; + + default: + ASSERT(0); + } +} + +/* Dump a message content -- for debug mostly */ +void fd_msg_dump_walk ( int level, msg_or_avp *obj ) +{ + msg_or_avp * ref = obj; + int indent = 1; + + TRACE_DEBUG(level, "------ Dumping object %p (w)-------", obj); + do { + msg_dump_intern ( level, ref, indent ); + + /* Now find the next object */ + CHECK_FCT_DO( fd_msg_browse ( ref, MSG_BRW_WALK, &ref, &indent ), break ); + + /* dump next object */ + } while (ref); + + TRACE_DEBUG(level, "------ /end of object %p -------", obj); +} + +/* Dump a single object content -- for debug mostly */ +void fd_msg_dump_one ( int level, msg_or_avp * obj ) +{ + TRACE_DEBUG(level, "------ Dumping object %p (s)-------", obj); + msg_dump_intern ( level, obj, 1 ); + TRACE_DEBUG(level, "------ /end of object %p -------", obj); +} + + +/***************************************************************************************************************/ +/* Simple meta-data management */ + +/* Retrieve the model of an object */ +int fd_msg_model ( msg_or_avp * reference, struct dict_object ** model ) +{ + TRACE_ENTRY("%p %p", reference, model); + + /* Check the parameters */ + CHECK_PARAMS( model && VALIDATE_OBJ(reference) ); + + /* copy the model reference */ + switch (_C(reference)->type) { + case MSG_AVP: + *model = _A(reference)->avp_model; + break; + + case MSG_MSG: + *model = _M(reference)->msg_model; + break; + + default: + CHECK_PARAMS(0); + } + + return 0; +} + +/* Retrieve the address of the msg_public field of a message */ +int fd_msg_hdr ( struct msg *msg, struct msg_hdr **pdata ) +{ + TRACE_ENTRY("%p %p", msg, pdata); + CHECK_PARAMS( CHECK_MSG(msg) && pdata ); + + *pdata = &msg->msg_public; + return 0; +} + +/* Retrieve the address of the avp_public field of an avp */ +int fd_msg_avp_hdr ( struct avp *avp, struct avp_hdr **pdata ) +{ + TRACE_ENTRY("%p %p", avp, pdata); + CHECK_PARAMS( CHECK_AVP(avp) && pdata ); + + *pdata = &avp->avp_public; + return 0; +} + +/* Associate answers and queries */ +int fd_msg_answ_associate( struct msg * answer, struct msg * query ) +{ + TRACE_ENTRY( "%p %p", answer, query ); + + CHECK_PARAMS( CHECK_MSG(answer) && CHECK_MSG(query) && (answer->msg_query == NULL ) ); + + answer->msg_query = query; + + return 0; +} + +int fd_msg_answ_getq( struct msg * answer, struct msg ** query ) +{ + TRACE_ENTRY( "%p %p", answer, query ); + + CHECK_PARAMS( CHECK_MSG(answer) && query ); + + *query = answer->msg_query; + + return 0; +} + +int fd_msg_answ_detach( struct msg * answer ) +{ + TRACE_ENTRY( "%p", answer ); + + CHECK_PARAMS( CHECK_MSG(answer) ); + + answer->msg_query = NULL; + + return 0; +} + +/* Associate / get answer callbacks */ +int fd_msg_anscb_associate( struct msg * msg, void ( *anscb)(void *, struct msg **), void * data ) +{ + TRACE_ENTRY("%p %p %p", msg, anscb, data); + + /* Check the parameters */ + CHECK_PARAMS( CHECK_MSG(msg) && anscb ); + CHECK_PARAMS( ! (msg->msg_public.msg_flags & CMD_FLAG_REQUEST) ); + CHECK_PARAMS( msg->msg_cb.fct == NULL ); /* No cb is already registered */ + + /* Associate callback and data with the message, if any */ + msg->msg_cb.fct = anscb; + msg->msg_cb.data = data; + + return 0; +} + +int fd_msg_anscb_get( struct msg * msg, void (**anscb)(void *, struct msg **), void ** data ) +{ + TRACE_ENTRY("%p %p %p", msg, anscb, data); + + /* Check the parameters */ + CHECK_PARAMS( CHECK_MSG(msg) && anscb && data ); + + /* Copy the result */ + *anscb = msg->msg_cb.fct; + *data = msg->msg_cb.data; + + return 0; +} + +/* Associate routing lists */ +int fd_msg_rt_associate( struct msg * msg, struct fd_list ** list ) +{ + TRACE_ENTRY( "%p %p", msg, list ); + + CHECK_PARAMS( CHECK_MSG(msg) && list ); + + msg->msg_rtlist = *list; + *list = NULL; + + return 0; +} + +int fd_msg_rt_get( struct msg * msg, struct fd_list ** list ) +{ + TRACE_ENTRY( "%p %p", msg, list ); + + CHECK_PARAMS( CHECK_MSG(msg) && list ); + + *list = msg->msg_rtlist; + msg->msg_rtlist = NULL; + + return 0; +} + +/* Find if a message is routable */ +int fd_msg_is_routable ( struct msg * msg ) +{ + TRACE_ENTRY("%p", msg); + + CHECK_PARAMS_DO( CHECK_MSG(msg), return 0 /* pretend the message is not routable */ ); + + if ( ! msg->msg_routable ) { + /* To define if a message is routable, we rely on the "PXY" command flag yet. */ + msg->msg_routable = (msg->msg_public.msg_flags & CMD_FLAG_PROXIABLE) ? 1 : 2; + + /* Note : the 'real' criteria according to the Diameter I-D is that the message is + routable if and only if the "Destination-Realm" AVP is required by the command ABNF. + We could make a test for this here, but it's more computational work and our test + seems accurate (until proven otherwise...) */ + } + + return (msg->msg_routable == 1) ? 1 : 0; +} + +/* Associate source peer */ +int fd_msg_source_set( struct msg * msg, char * diamid, uint32_t hash, int add_rr, struct dictionary * dict ) +{ + TRACE_ENTRY( "%p %p %x %d %p", msg, diamid, hash, add_rr, dict); + + /* Check we received a valid message */ + CHECK_PARAMS( CHECK_MSG(msg) && dict ); + + /* Cleanup any previous source */ + free(msg->msg_src_id); msg->msg_src_id = NULL; + + /* If the request is to cleanup the source, we are done */ + if (diamid == NULL) { + msg->msg_src_hash = 0; + return 0; + } + + /* Otherwise save the new informations */ + CHECK_MALLOC( msg->msg_src_id = strdup(diamid) ); + msg->msg_src_hash = hash; + + if (add_rr) { + struct dict_object *avp_rr_model; + avp_code_t code = AC_ROUTE_RECORD; + struct avp *avp; + union avp_value val; + + /* Find the model for Route-Record in the dictionary */ + CHECK_FCT( fd_dict_search ( dict, DICT_AVP, AVP_BY_CODE, &code, &avp_rr_model, ENOENT) ); + + /* Create the AVP with this model */ + CHECK_FCT( fd_msg_avp_new ( avp_rr_model, 0, &avp ) ); + + /* Set the AVP value with the diameter id */ + memset(&val, 0, sizeof(val)); + val.os.data = (unsigned char *)diamid; + val.os.len = strlen(diamid); + CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) ); + + /* Add the AVP in the message */ + CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) ); + } + + /* done */ + return 0; +} + +int fd_msg_source_get( struct msg * msg, char ** diamid, uint32_t *hash ) +{ + TRACE_ENTRY( "%p %p %p", msg, diamid, hash); + + /* Check we received valid parameters */ + CHECK_PARAMS( CHECK_MSG(msg) ); + CHECK_PARAMS( diamid ); + + /* Copy the informations */ + *diamid = msg->msg_src_id; + if (hash) + *hash = msg->msg_src_hash; + + /* done */ + return 0; +} + +/******************* End-to-end counter *********************/ +uint32_t fd_eteid; +pthread_mutex_t fd_eteid_lck = PTHREAD_MUTEX_INITIALIZER; + +void fd_msg_eteid_init(void) +{ + fd_eteid = ((uint32_t)time(NULL) << 20) | ((uint32_t)lrand48() & ( (1 << 20) - 1 )); +} + +uint32_t fd_msg_eteid_get ( void ) +{ + uint32_t ret; + + CHECK_POSIX_DO( pthread_mutex_lock(&fd_eteid_lck), /* continue */ ); + + ret = fd_eteid ++; + + CHECK_POSIX_DO( pthread_mutex_unlock(&fd_eteid_lck), /* continue */ ); + + return ret; +} + +/***************************************************************************************************************/ +/* Manage AVPs values */ + +/* Set the value of an AVP */ +int fd_msg_avp_setvalue ( struct avp *avp, union avp_value *value ) +{ + enum dict_avp_basetype type = -1; + + TRACE_ENTRY("%p %p", avp, value); + + /* Check parameter */ + CHECK_PARAMS( CHECK_AVP(avp) && avp->avp_model ); + + /* Retrieve information from the AVP model */ + { + enum dict_object_type dicttype; + struct dict_avp_data dictdata; + + CHECK_PARAMS( (fd_dict_gettype(avp->avp_model, &dicttype) == 0) && (dicttype == DICT_AVP) ); + CHECK_FCT( fd_dict_getval(avp->avp_model, &dictdata) ); + type = dictdata.avp_basetype; + CHECK_PARAMS( type != AVP_TYPE_GROUPED ); + } + + /* First, clean any previous value */ + if (avp->avp_mustfreeos != 0) { + free(avp->avp_storage.os.data); + avp->avp_mustfreeos = 0; + } + + memset(&avp->avp_storage, 0, sizeof(union avp_value)); + + /* If the request was to delete a value: */ + if (!value) { + avp->avp_public.avp_value = NULL; + return 0; + } + + /* Now we have to set the value */ + memcpy(&avp->avp_storage, value, sizeof(union avp_value)); + + /* Copy an octetstring if needed. */ + if (type == AVP_TYPE_OCTETSTRING) { + CHECK_MALLOC( avp->avp_storage.os.data = malloc(value->os.len) ); + avp->avp_mustfreeos = 1; + memcpy(avp->avp_storage.os.data, value->os.data, value->os.len); + } + + /* Set the data pointer of the public part */ + avp->avp_public.avp_value = &avp->avp_storage; + + return 0; +} + +/* Set the value of an AVP, using formatted data */ +int fd_msg_avp_value_encode ( void *data, struct avp *avp ) +{ + enum dict_avp_basetype type = -1; + struct dict_type_data type_data; + + TRACE_ENTRY("%p %p", data, avp); + + /* Check parameter */ + CHECK_PARAMS( CHECK_AVP(avp) && avp->avp_model ); + + /* Retrieve information from the AVP model and it's parent type */ + { + enum dict_object_type dicttype; + struct dict_avp_data dictdata; + struct dictionary * dict; + struct dict_object * parenttype = NULL; + + /* First check the base type of the AVP */ + CHECK_PARAMS( (fd_dict_gettype(avp->avp_model, &dicttype) == 0) && (dicttype == DICT_AVP) ); + CHECK_FCT( fd_dict_getval(avp->avp_model, &dictdata) ); + type = dictdata.avp_basetype; + CHECK_PARAMS( type != AVP_TYPE_GROUPED ); + + /* Then retrieve information about the parent's type (= derived type) */ + CHECK_FCT( fd_dict_getdict( avp->avp_model, &dict ) ); + CHECK_FCT( fd_dict_search( dict, DICT_TYPE, TYPE_OF_AVP, avp->avp_model, &parenttype, EINVAL) ); + CHECK_FCT( fd_dict_getval(parenttype, &type_data) ); + if (type_data.type_encode == NULL) { + TRACE_DEBUG(INFO, "This AVP type does not provide a callback to encode formatted data. ENOTSUP."); + return ENOTSUP; + } + } + + /* Ok, now we can encode the value */ + + /* First, clean any previous value */ + if (avp->avp_mustfreeos != 0) { + free(avp->avp_storage.os.data); + avp->avp_mustfreeos = 0; + } + avp->avp_public.avp_value = NULL; + memset(&avp->avp_storage, 0, sizeof(union avp_value)); + + /* Now call the type's callback to encode the data */ + CHECK_FCT( (*type_data.type_encode)(data, &avp->avp_storage) ); + + /* If an octetstring has been allocated, let's mark it to be freed */ + if (type == AVP_TYPE_OCTETSTRING) + avp->avp_mustfreeos = 1; + + /* Set the data pointer of the public part */ + avp->avp_public.avp_value = &avp->avp_storage; + + return 0; +} + +/* Interpret the value of an AVP into formatted data */ +int fd_msg_avp_value_interpret ( struct avp *avp, void *data ) +{ + struct dict_type_data type_data; + + TRACE_ENTRY("%p %p", avp, data); + + /* Check parameter */ + CHECK_PARAMS( CHECK_AVP(avp) && avp->avp_model && avp->avp_public.avp_value ); + + /* Retrieve information about the AVP parent type */ + { + struct dictionary * dict; + struct dict_object * parenttype = NULL; + + CHECK_FCT( fd_dict_getdict( avp->avp_model, &dict ) ); + CHECK_FCT( fd_dict_search( dict, DICT_TYPE, TYPE_OF_AVP, avp->avp_model, &parenttype, EINVAL) ); + CHECK_FCT( fd_dict_getval(parenttype, &type_data) ); + if (type_data.type_interpret == NULL) { + TRACE_DEBUG(INFO, "This AVP type does not provide a callback to interpret value in formatted data. ENOTSUP."); + return ENOTSUP; + } + } + + /* Ok, now we can interpret the value */ + + CHECK_FCT( (*type_data.type_interpret)(avp->avp_public.avp_value, data) ); + + return 0; +} + +/***************************************************************************************************************/ +/* Creating a buffer from memory objects (bufferize a struct msg) */ + +/* Following macros are used to store 32 and 64 bit fields into a buffer in network byte order */ +#define PUT_in_buf_32( _u32data, _bufptr ) { \ + *(uint32_t *)(_bufptr) = htonl((uint32_t)(_u32data)); \ +} +#define PUT_in_buf_64( _u64data, _bufptr ) { \ + *(uint64_t *)(_bufptr) = htonll((uint64_t)(_u64data)); \ +} + +/* Write a message header in the buffer */ +static int bufferize_msg(unsigned char * buffer, size_t buflen, size_t * offset, struct msg * msg) +{ + TRACE_ENTRY("%p %d %p %p", buffer, buflen, offset, msg); + + if ((buflen - *offset) < GETMSGHDRSZ()) + return ENOSPC; + + if (*offset & 0x3) + return EFAULT; /* We are supposed to start on 32 bit boundaries */ + + PUT_in_buf_32(msg->msg_public.msg_length, buffer + *offset); + buffer[*offset] = msg->msg_public.msg_version; + *offset += 4; + + PUT_in_buf_32(msg->msg_public.msg_code, buffer + *offset); + buffer[*offset] = msg->msg_public.msg_flags; + *offset += 4; + + PUT_in_buf_32(msg->msg_public.msg_appl, buffer + *offset); + *offset += 4; + + PUT_in_buf_32(msg->msg_public.msg_hbhid, buffer + *offset); + *offset += 4; + + PUT_in_buf_32(msg->msg_public.msg_eteid, buffer + *offset); + *offset += 4; + + return 0; +} + +static int bufferize_chain(unsigned char * buffer, size_t buflen, size_t * offset, struct fd_list * list); + +/* Write an AVP in the buffer */ +static int bufferize_avp(unsigned char * buffer, size_t buflen, size_t * offset, struct avp * avp) +{ + struct dict_avp_data dictdata; + + TRACE_ENTRY("%p %d %p %p", buffer, buflen, offset, avp); + + if ((buflen - *offset) < avp->avp_public.avp_len) + return ENOSPC; + + /* Write the header */ + PUT_in_buf_32(avp->avp_public.avp_code, buffer + *offset); + *offset += 4; + + PUT_in_buf_32(avp->avp_public.avp_len, buffer + *offset); + buffer[*offset] = avp->avp_public.avp_flags; + *offset += 4; + + if (avp->avp_public.avp_flags & AVP_FLAG_VENDOR) { + PUT_in_buf_32(avp->avp_public.avp_vendor, buffer + *offset); + *offset += 4; + } + + /* Then we must write the AVP value */ + + if (avp->avp_model == NULL) { + /* In the case where we don't know the type of AVP, just copy the raw data or source */ + CHECK_PARAMS( avp->avp_source || avp->avp_rawdata ); + + if ( avp->avp_source != NULL ) { + /* the message was not parsed completely */ + size_t datalen = avp->avp_public.avp_len - GETAVPHDRSZ(avp->avp_public.avp_flags); + memcpy(&buffer[*offset], avp->avp_source, datalen); + *offset += PAD4(datalen); + } else { + /* the content was stored in rawdata */ + memcpy(&buffer[*offset], avp->avp_rawdata, avp->avp_rawlen); + *offset += PAD4(avp->avp_rawlen); + } + + } else { + /* The AVP is defined in the dictionary */ + CHECK_FCT( fd_dict_getval(avp->avp_model, &dictdata) ); + + CHECK_PARAMS( ( dictdata.avp_basetype == AVP_TYPE_GROUPED ) || avp->avp_public.avp_value ); + + switch (dictdata.avp_basetype) { + case AVP_TYPE_GROUPED: + return bufferize_chain(buffer, buflen, offset, &avp->avp_chain.children); + + case AVP_TYPE_OCTETSTRING: + memcpy(&buffer[*offset], avp->avp_public.avp_value->os.data, avp->avp_public.avp_value->os.len); + *offset += PAD4(avp->avp_public.avp_value->os.len); + break; + + case AVP_TYPE_INTEGER32: + PUT_in_buf_32(avp->avp_public.avp_value->i32, buffer + *offset); + *offset += 4; + break; + + case AVP_TYPE_INTEGER64: + PUT_in_buf_64(avp->avp_public.avp_value->i64, buffer + *offset); + *offset += 8; + break; + + case AVP_TYPE_UNSIGNED32: + PUT_in_buf_32(avp->avp_public.avp_value->u32, buffer + *offset); + *offset += 4; + break; + + case AVP_TYPE_UNSIGNED64: + PUT_in_buf_64(avp->avp_public.avp_value->u64, buffer + *offset); + *offset += 8; + break; + + case AVP_TYPE_FLOAT32: + /* We read the f32 as "u32" here to avoid casting to uint make decimals go away. + The alternative would be something like "*(uint32_t *)(& f32)" but + then the compiler complains about strict-aliasing rules. */ + PUT_in_buf_32(avp->avp_public.avp_value->u32, buffer + *offset); + *offset += 4; + break; + + case AVP_TYPE_FLOAT64: + /* Same remark as previously */ + PUT_in_buf_64(avp->avp_public.avp_value->u64, buffer + *offset); + *offset += 8; + break; + + default: + ASSERT(0); + } + } + return 0; +} + +/* Write a chain of AVPs in the buffer */ +static int bufferize_chain(unsigned char * buffer, size_t buflen, size_t * offset, struct fd_list * list) +{ + struct fd_list * avpch; + + TRACE_ENTRY("%p %d %p %p", buffer, buflen, offset, list); + + for (avpch = list->next; avpch != list; avpch = avpch->next) { + /* Bufferize the AVP */ + CHECK_FCT( bufferize_avp(buffer, buflen, offset, _A(avpch->o)) ); + } + return 0; +} + +/* Create the message buffer, in network-byte order. We browse the tree twice, this could be probably improved if needed */ +int fd_msg_bufferize ( struct msg * msg, unsigned char ** buffer, size_t * len ) +{ + int ret = 0; + unsigned char * buf = NULL; + size_t offset = 0; + + TRACE_ENTRY("%p %p %p", msg, buffer, len); + + /* Check the parameters */ + CHECK_PARAMS( buffer && CHECK_MSG(msg) ); + + /* Update the length. This also checks that all AVP have their values set */ + CHECK_FCT( fd_msg_update_length(msg) ); + + /* Now allocate a buffer to store the message */ + CHECK_MALLOC( buf = malloc(msg->msg_public.msg_length) ); + + /* Clear the memory, so that the padding is always 0 (should not matter) */ + memset(buf, 0, msg->msg_public.msg_length); + + /* Write the message header in the buffer */ + CHECK_FCT_DO( ret = bufferize_msg(buf, msg->msg_public.msg_length, &offset, msg), + { + free(buf); + return ret; + } ); + + /* Write the list of AVPs */ + CHECK_FCT_DO( ret = bufferize_chain(buf, msg->msg_public.msg_length, &offset, &msg->msg_chain.children), + { + free(buf); + return ret; + } ); + + ASSERT(offset == msg->msg_public.msg_length); /* or the msg_update_length is buggy */ + + if (len) { + *len = offset; + } + + *buffer = buf; + return 0; +} + + +/***************************************************************************************************************/ +/* Parsing buffers and building AVP objects lists (not parsing the AVP values which requires dictionary knowledge) */ + +/* Parse a buffer containing a supposed list of AVPs */ +static int parsebuf_list(unsigned char * buf, size_t buflen, struct fd_list * head) +{ + size_t offset = 0; + + TRACE_ENTRY("%p %d %p", buf, buflen, head); + + while (offset < buflen) { + struct avp * avp; + + if (buflen - offset <= AVPHDRSZ_NOVEND) { + TRACE_DEBUG(INFO, "truncated buffer: remaining only %d bytes", buflen - offset); + return EBADMSG; + } + + /* Create a new AVP object */ + CHECK_MALLOC( avp = malloc (sizeof(struct avp)) ); + + init_avp(avp); + + /* Initialize the header */ + avp->avp_public.avp_code = ntohl(*(uint32_t *)(buf + offset)); + avp->avp_public.avp_flags = buf[offset + 4]; + avp->avp_public.avp_len = ((uint32_t)buf[offset+5]) << 16 | ((uint32_t)buf[offset+6]) << 8 | ((uint32_t)buf[offset+7]) ; + + offset += 8; + + if (avp->avp_public.avp_flags & AVP_FLAG_VENDOR) { + if (buflen - offset <= 4) { + TRACE_DEBUG(INFO, "truncated buffer: remaining only %d bytes for vendor and data", buflen - offset); + free(avp); + return EBADMSG; + } + avp->avp_public.avp_vendor = ntohl(*(uint32_t *)(buf + offset)); + offset += 4; + } + + /* Check there is enough remaining data in the buffer */ + if (buflen - offset < avp->avp_public.avp_len - GETAVPHDRSZ(avp->avp_public.avp_flags)) { + TRACE_DEBUG(INFO, "truncated buffer: remaining only %d bytes for data, and avp data size is %d", + buflen - offset, + avp->avp_public.avp_len - GETAVPHDRSZ(avp->avp_public.avp_flags)); + free(avp); + return EBADMSG; + } + + /* buf[offset] is now the beginning of the data */ + avp->avp_source = &buf[offset]; + + /* Now eat the data and eventual padding */ + offset += PAD4(avp->avp_public.avp_len - GETAVPHDRSZ(avp->avp_public.avp_flags)); + + /* And insert this avp in the list, at the end */ + fd_list_insert_before( head, &avp->avp_chain.chaining ); + } + + return 0; +} + +/* Create a message object from a buffer. Dictionary objects are not resolved, AVP contents are not interpreted, buffer is saved in msg */ +int fd_msg_parse_buffer ( unsigned char ** buffer, size_t buflen, struct msg ** msg ) +{ + struct msg * new = NULL; + int ret = 0; + uint32_t msglen = 0; + unsigned char * buf; + + TRACE_ENTRY("%p %d %p", buffer, buflen, msg); + + CHECK_PARAMS( buffer && *buffer && msg && (buflen >= GETMSGHDRSZ()) ); + buf = *buffer; + *buffer = NULL; + + if ( buf[0] != DIAMETER_VERSION) { + TRACE_DEBUG(INFO, "Invalid version in message: %d (supported: %d)", buf[0], DIAMETER_VERSION); + free(buf); + return EBADMSG; + } + + msglen = ntohl(*(uint32_t *)buf) & 0x00ffffff; + if ( buflen < msglen ) { + TRACE_DEBUG(INFO, "Truncated message (%d / %d)", buflen, msglen ); + free(buf); + return EBADMSG; + } + + /* Create a new object */ + CHECK_MALLOC_DO( new = malloc (sizeof(struct msg)), { free(buf); return ENOMEM; } ); + + /* Initialize the fields */ + init_msg(new); + + /* Now read from the buffer */ + new->msg_public.msg_version = buf[0]; + new->msg_public.msg_length = msglen; + + new->msg_public.msg_flags = buf[4]; + new->msg_public.msg_code = ntohl(*(uint32_t *)(buf+4)) & 0x00ffffff; + + new->msg_public.msg_appl = ntohl(*(uint32_t *)(buf+8)); + new->msg_public.msg_hbhid = ntohl(*(uint32_t *)(buf+12)); + new->msg_public.msg_eteid = ntohl(*(uint32_t *)(buf+16)); + + new->msg_rawbuffer = buf; + + /* Parse the AVP list */ + CHECK_FCT_DO( ret = parsebuf_list(buf + GETMSGHDRSZ(), buflen - GETMSGHDRSZ(), &new->msg_chain.children), { destroy_tree(_C(new)); return ret; } ); + + *msg = new; + return 0; +} + + +/***************************************************************************************************************/ +/* Parsing messages and AVP with dictionary information */ + +/* Resolve dictionary objects of the cmd and avp instances, from their headers. + * When the model is found, the data is interpreted from the avp_source buffer and copied to avp_storage. + * When the model is not found, the data is copied as rawdata and saved (in case we FW the message). + * Therefore, after this function has been called, the source buffer can be freed. + * For command, if the dictionary model is not found, an error is returned. + */ + +static int parsedict_do_chain(struct dictionary * dict, struct fd_list * head, int mandatory); + +/* Process an AVP. If we are not in recheck, the avp_source must be set. */ +static int parsedict_do_avp(struct dictionary * dict, struct avp * avp, int mandatory) +{ + struct dict_avp_data dictdata; + + TRACE_ENTRY("%p %p %d", dict, avp, mandatory); + + /* First check we received an AVP as input */ + CHECK_PARAMS( CHECK_AVP(avp) ); + + if (avp->avp_model != NULL) { + /* the model has already been resolved. we do check it is still valid */ + + CHECK_FCT( fd_dict_getval(avp->avp_model, &dictdata) ); + + if ( avp->avp_public.avp_code == dictdata.avp_code ) { + /* Ok then just process the children if any */ + return parsedict_do_chain(dict, &avp->avp_chain.children, mandatory && (avp->avp_public.avp_flags & AVP_FLAG_MANDATORY)); + } else { + /* We just erase the old model */ + avp->avp_model = NULL; + } + } + + /* Now try and resolve the model from the avp code and vendor */ + if (avp->avp_public.avp_flags & AVP_FLAG_VENDOR) { + struct dict_avp_request avpreq; + avpreq.avp_vendor = avp->avp_public.avp_vendor; + avpreq.avp_code = avp->avp_public.avp_code; + CHECK_FCT( fd_dict_search ( dict, DICT_AVP, AVP_BY_CODE_AND_VENDOR, &avpreq, &avp->avp_model, 0)); + } else { + /* no vendor */ + CHECK_FCT( fd_dict_search ( dict, DICT_AVP, AVP_BY_CODE, &avp->avp_public.avp_code, &avp->avp_model, 0)); + } + + /* First handle the case where we have not found this AVP in the dictionary */ + if (!avp->avp_model) { + + if (mandatory && (avp->avp_public.avp_flags & AVP_FLAG_MANDATORY)) { + TRACE_DEBUG(INFO, "Unsupported mandatory AVP found:"); + msg_dump_intern(INFO, avp, 2); + return ENOTSUP; + } + + if (avp->avp_source) { + /* we must copy the data from the source to the internal buffer area */ + CHECK_PARAMS( !avp->avp_rawdata ); + + avp->avp_rawlen = avp->avp_public.avp_len - GETAVPHDRSZ( avp->avp_public.avp_flags ); + + CHECK_MALLOC( avp->avp_rawdata = malloc(avp->avp_rawlen) ); + + memcpy(avp->avp_rawdata, avp->avp_source, avp->avp_rawlen); + avp->avp_source = NULL; + + TRACE_DEBUG(FULL, "Unsupported optional AVP found, raw source data saved in avp_rawdata."); + } + + return 0; + } + + /* Ok we have resolved the object. Now we need to interpret its content. */ + + CHECK_FCT( fd_dict_getval(avp->avp_model, &dictdata) ); + + if (avp->avp_rawdata) { + /* This happens if the dictionary object was defined after the first check */ + avp->avp_source = avp->avp_rawdata; + } + + /* A bit of sanity here... */ + ASSERT(CHECK_BASETYPE(dictdata.avp_basetype)); + + /* Check the size is valid */ + if ((avp_value_sizes[dictdata.avp_basetype] != 0) && + (avp->avp_public.avp_len - GETAVPHDRSZ( avp->avp_public.avp_flags ) != avp_value_sizes[dictdata.avp_basetype])) { + TRACE_DEBUG(INFO, "The AVP size is not suitable for the type. EBADMSG."); + return EBADMSG; + } + + /* Now get the value inside */ + switch (dictdata.avp_basetype) { + case AVP_TYPE_GROUPED: + /* This is a grouped AVP, so let's parse the list of AVPs inside */ + CHECK_FCT( parsebuf_list(avp->avp_source, avp->avp_public.avp_len - GETAVPHDRSZ( avp->avp_public.avp_flags ), &avp->avp_chain.children) ); + + return parsedict_do_chain(dict, &avp->avp_chain.children, mandatory && (avp->avp_public.avp_flags & AVP_FLAG_MANDATORY)); + + case AVP_TYPE_OCTETSTRING: + /* We just have to copy the string into the storage area */ + CHECK_PARAMS( avp->avp_public.avp_len > GETAVPHDRSZ( avp->avp_public.avp_flags ) ); + avp->avp_storage.os.len = avp->avp_public.avp_len - GETAVPHDRSZ( avp->avp_public.avp_flags ); + CHECK_MALLOC( avp->avp_storage.os.data = malloc(avp->avp_storage.os.len) ); + avp->avp_mustfreeos = 1; + memcpy(avp->avp_storage.os.data, avp->avp_source, avp->avp_storage.os.len); + break; + + case AVP_TYPE_INTEGER32: + avp->avp_storage.i32 = (int32_t)ntohl(*(uint32_t *)avp->avp_source); + break; + + case AVP_TYPE_INTEGER64: + avp->avp_storage.i64 = (int64_t)ntohll(*(uint64_t *)avp->avp_source); + break; + + case AVP_TYPE_UNSIGNED32: + case AVP_TYPE_FLOAT32: /* For float, we must not cast, or the value is changed. Instead we use implicit cast by changing the member of the union */ + avp->avp_storage.u32 = (uint32_t)ntohl(*(uint32_t *)avp->avp_source); + break; + + case AVP_TYPE_UNSIGNED64: + case AVP_TYPE_FLOAT64: /* same as 32 bits */ + avp->avp_storage.u64 = (uint64_t)ntohll(*(uint64_t *)avp->avp_source); + break; + + } + + /* The value is now set, so set the data pointer and return 0 */ + avp->avp_public.avp_value = &avp->avp_storage; + return 0; +} + +/* Process a list of AVPs */ +static int parsedict_do_chain(struct dictionary * dict, struct fd_list * head, int mandatory) +{ + struct fd_list * avpch; + + TRACE_ENTRY("%p %p %d", dict, head, mandatory); + + /* Sanity check */ + ASSERT ( head == head->head ); + + /* Now process the list */ + for (avpch=head->next; avpch != head; avpch = avpch->next) { + CHECK_FCT( parsedict_do_avp(dict, _A(avpch->o), mandatory) ); + } + + /* Done */ + return 0; +} + +/* Process a msg header. */ +static int parsedict_do_msg(struct dictionary * dict, struct msg * msg, int only_hdr) +{ + int ret = 0; + + TRACE_ENTRY("%p %p %d", dict, msg, only_hdr); + + CHECK_PARAMS( CHECK_MSG(msg) ); + + /* Look for the model from the header */ + CHECK_FCT( fd_dict_search ( dict, DICT_COMMAND, + (msg->msg_public.msg_flags & CMD_FLAG_REQUEST) ? CMD_BY_CODE_R : CMD_BY_CODE_A, + &msg->msg_public.msg_code, + &msg->msg_model, ENOTSUP) ); + + if (!only_hdr) { + /* Then process the children */ + ret = parsedict_do_chain(dict, &msg->msg_chain.children, 1); + + /* Free the raw buffer if any */ + if ((ret == 0) && (msg->msg_rawbuffer != NULL)) { + free(msg->msg_rawbuffer); + msg->msg_rawbuffer=NULL; + } + } + + return ret; +} + +int fd_msg_parse_dict ( msg_or_avp * object, struct dictionary * dict ) +{ + TRACE_ENTRY("%p %p", dict, object); + + CHECK_PARAMS( VALIDATE_OBJ(object) ); + + switch (_C(object)->type) { + case MSG_MSG: + return parsedict_do_msg(dict, _M(object), 0); + + case MSG_AVP: + return parsedict_do_avp(dict, _A(object), 0); + + default: + ASSERT(0); + } + return EINVAL; +} + +/***************************************************************************************************************/ +/* Parsing messages and AVP for rules (ABNF) compliance */ + +/* This function is used to get stats (first occurence position, last occurence position, number of occurences) + of AVP instances of a given model in a chain of AVP */ +static void parserules_stat_avps( struct dict_object * model_avp, struct fd_list *list, int * count, int * firstpos, int * lastpos) +{ + struct fd_list * li; + int curpos = 0; /* The current position in the list */ + + TRACE_ENTRY("%p %p %p %p %p", model_avp, list, count, firstpos, lastpos); + + *count = 0; /* number of instances found */ + *firstpos = 0; /* position of the first instance */ + *lastpos = 0; /* position of the last instance, starting from the end */ + + for (li = list->next; li != list; li = li->next) { + /* Increment the current position counter */ + curpos++; + + /* If we previously saved a "lastpos" information, increment it */ + if (*lastpos != 0) + (*lastpos)++; + + /* Check the type of the next AVP. We can compare the references directly, it is safe. */ + if (_A(li->o)->avp_model == model_avp) { + + /* This AVP is of the type we are searching */ + (*count)++; + + /* If we don't have yet a "firstpos", save it */ + if (*firstpos == 0) + *firstpos = curpos; + + /* Reset the lastpos */ + (*lastpos) = 1; + } + } +} + +/* We use this structure as parameter for the next function */ +struct parserules_data { + struct fd_list * sentinel; /* Sentinel of the list of children AVP */ + struct dict_object * ruleavp; /* If the rule conflicts, save the rule_avp here (we don't have direct access to the rule but it can be searched) */ +}; + +/* Check that a list of AVPs is compliant with a given rule -- will be iterated on the list of rules */ +static int parserules_check_one_rule(void * data, struct dict_rule_data *rule) +{ + int ret = 0, count, first, last, min; + struct parserules_data * pr_data = (struct parserules_data *) data; + + TRACE_ENTRY("%p %p", data, rule); + + /* Get statistics of the AVP concerned by this rule in the message instance */ + parserules_stat_avps( rule->rule_avp, pr_data->sentinel, &count, &first, &last); + + if (TRACE_BOOL(ANNOYING)) + { + struct dict_avp_data avpdata; + ret = fd_dict_getval(rule->rule_avp, &avpdata); + + TRACE_DEBUG(ANNOYING, "Checking rule: p:%d(%d) m/M:%2d/%2d. Counted %d (first: %d, last:%d) of AVP '%s'", + rule->rule_position, + rule->rule_order, + rule->rule_min, + rule->rule_max, + count, + first, + last, + (ret == 0) ? avpdata.avp_name : "???" + ); + } + + /* Now check the rule is not conflicting */ + ret = 0; + + /* Check the "min" value */ + if ((min = rule->rule_min) == -1) { + if (rule->rule_position == RULE_OPTIONAL) + min = 0; + else + min = 1; + } + if (count < min) { + TRACE_DEBUG(INFO, "Conflicting rule: the number of occurences (%d) is < the rule min (%d).", count, min); + ret = EBADMSG; + goto end; + } + + /* Check the "max" value */ + if ((rule->rule_max != -1) && (count > rule->rule_max)) { + TRACE_DEBUG(INFO, "Conflicting rule: the number of occurences (%d) is > the rule max (%d).", count, rule->rule_max); + ret = EBADMSG; + goto end; + } + + /* Check the position and order (if relevant) */ + switch (rule->rule_position) { + case RULE_OPTIONAL: + case RULE_REQUIRED: + /* No special position constraints */ + break; + + case RULE_FIXED_HEAD: + /* Since "0*1<fixed>" is a valid rule specifier, we only reject cases where the AVP appears *after* its fixed position */ + if (first > rule->rule_order) { + TRACE_DEBUG(INFO, "Conflicting rule: the FIXED_HEAD AVP appears first in (%d) position, the rule requires (%d).", first, rule->rule_order); + ret = EBADMSG; + goto end; + } + break; + + case RULE_FIXED_TAIL: + /* Since "0*1<fixed>" is a valid rule specifier, we only reject cases where the AVP appears *before* its fixed position */ + if (last > rule->rule_order) { /* We have a ">" here because we count in reverse order (i.e. from the end) */ + TRACE_DEBUG(INFO, "Conflicting rule: the FIXED_TAIL AVP appears last in (%d) position, the rule requires (%d).", last, rule->rule_order); + ret = EBADMSG; + goto end; + } + break; + + default: + /* What is this position ??? */ + ASSERT(0); + ret = ENOTSUP; + } + + /* We've checked all the parameters */ +end: + if (ret == EBADMSG) { + pr_data->ruleavp = rule->rule_avp; + } + + return ret; +} + +/* Check the rules recursively */ +static int parserules_do ( struct dictionary * dict, msg_or_avp * object, struct dict_object ** conflict_rule, int mandatory) +{ + int ret = 0; + struct parserules_data data; + struct dict_object * model = NULL; + + TRACE_ENTRY("%p %p %p %d", dict, object, conflict_rule, mandatory); + + /* object has already been checked and dict-parsed when we are called. */ + + /* First, handle the cases where there is no model */ + { + if (CHECK_MSG(object)) { + if ( _M(object)->msg_public.msg_flags & CMD_FLAG_ERROR ) { + /* The case of error messages: the ABNF is different */ + CHECK_FCT( fd_dict_get_error_cmd(dict, &model) ); + } else { + model = _M(object)->msg_model; + } + /* Commands MUST be supported in the dictionary */ + if (model == NULL) { + TRACE_DEBUG(INFO, "Message with no dictionary model. EBADMSG"); + return EBADMSG; + } + } + + /* AVP with the 'M' flag must also be recognized in the dictionary -- except inside an optional grouped AVP */ + if (CHECK_AVP(object) && ((model = _A(object)->avp_model) == NULL)) { + if ( mandatory && (_A(object)->avp_public.avp_flags & AVP_FLAG_MANDATORY)) { + /* Return an error in this case */ + TRACE_DEBUG(INFO, "Mandatory AVP with no dictionary model. EBADMSG"); + return EBADMSG; + } else { + /* We don't know any rule for this object, so assume OK */ + TRACE_DEBUG(FULL, "Unknown informational AVP, ignoring..."); + return 0; + } + } + } + + /* At this point we know "model" is set and points to the object's model */ + + /* If we are an AVP with no children, just return OK */ + if (CHECK_AVP(object)) { + struct dict_avp_data dictdata; + CHECK_FCT( fd_dict_getval(model, &dictdata) ); + if (dictdata.avp_basetype != AVP_TYPE_GROUPED) { + /* This object has no children and no rules */ + return 0; + } + } + + /* If this object has children, first check the rules for all its children */ + { + int is_child_mand = 0; + struct fd_list * ch = NULL; + if ( CHECK_MSG(object) + || (mandatory && (_A(object)->avp_public.avp_flags & AVP_FLAG_MANDATORY)) ) + is_child_mand = 1; + for (ch = _C(object)->children.next; ch != &_C(object)->children; ch = ch->next) { + CHECK_FCT( parserules_do ( dict, _C(ch->o), conflict_rule, is_child_mand ) ); + } + } + + /* Now check all rules of this object */ + data.sentinel = &_C(object)->children; + data.ruleavp = NULL; + ret = fd_dict_iterate_rules ( model, &data, parserules_check_one_rule ); + + /* Save the reference to the eventual conflicting rule; otherwise set to NULL */ + if (conflict_rule && data.ruleavp) { + /* data.ruleavp contains the AVP, and model is the parent */ + struct dict_object * rule = NULL; + struct dict_rule_request req = { model, data.ruleavp }; + + CHECK_FCT_DO( fd_dict_search ( dict, DICT_RULE, RULE_BY_AVP_AND_PARENT, &req, &rule, ENOENT), rule = NULL ); + + *conflict_rule = rule; + } + + return ret; +} + +int fd_msg_parse_rules ( msg_or_avp * object, struct dictionary * dict, struct dict_object ** rule) +{ + TRACE_ENTRY("%p %p", object, rule); + + /* Resolve the dictionary objects when missing. This also validates the object. */ + CHECK_FCT( fd_msg_parse_dict ( object, dict ) ); + + /* Call the recursive function */ + return parserules_do ( dict, object, rule, 1 ) ; +} + +/***************************************************************************************************************/ + +/* Compute the lengh of an object and its subtree. */ +int fd_msg_update_length ( msg_or_avp * object ) +{ + size_t sz = 0; + struct dict_object * model; + union { + struct dict_cmd_data cmddata; + struct dict_avp_data avpdata; + } dictdata; + + TRACE_ENTRY("%p", object); + + /* Get the model of the object. This also validates the object */ + CHECK_FCT( fd_msg_model ( object, &model ) ); + + /* Get the information of the model */ + if (model) { + CHECK_FCT( fd_dict_getval(model, &dictdata) ); + } else { + /* For unknown AVP, just don't change the size */ + if (_C(object)->type == MSG_AVP) + return 0; + } + + /* Deal with easy cases: AVPs without children */ + if ((_C(object)->type == MSG_AVP) && (dictdata.avpdata.avp_basetype != AVP_TYPE_GROUPED)) { + /* Sanity check */ + ASSERT(FD_IS_LIST_EMPTY(&_A(object)->avp_chain.children)); + + /* Now check that the data is set in the AVP */ + CHECK_PARAMS( _A(object)->avp_public.avp_value ); + + sz = GETAVPHDRSZ( _A(object)->avp_public.avp_flags ); + + switch (dictdata.avpdata.avp_basetype) { + case AVP_TYPE_OCTETSTRING: + sz += _A(object)->avp_public.avp_value->os.len; + break; + + case AVP_TYPE_INTEGER32: + case AVP_TYPE_INTEGER64: + case AVP_TYPE_UNSIGNED32: + case AVP_TYPE_UNSIGNED64: + case AVP_TYPE_FLOAT32: + case AVP_TYPE_FLOAT64: + sz += avp_value_sizes[dictdata.avpdata.avp_basetype]; + break; + + default: + /* Something went wrong... */ + ASSERT(0); + } + } + else /* message or grouped AVP */ + { + struct fd_list * ch = NULL; + + /* First, compute the header size */ + if (_C(object)->type == MSG_AVP) { + sz = GETAVPHDRSZ( _A(object)->avp_public.avp_flags ); + } else { + sz = GETMSGHDRSZ( ); + } + + /* Recurse in all children and update the sz information */ + for (ch = _C(object)->children.next; ch != &_C(object)->children; ch = ch->next) { + CHECK_FCT( fd_msg_update_length ( ch->o ) ); + + /* Add the padded size to the parent */ + sz += PAD4( _A(ch->o)->avp_public.avp_len ); + } + } + + /* When we arrive here, the "sz" variable contains the size to write in the object */ + if (_C(object)->type == MSG_AVP) + _A(object)->avp_public.avp_len = sz; + else + _M(object)->msg_public.msg_length = sz; + + return 0; +} + +/***************************************************************************************************************/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libfreediameter/mqueues.c Fri Aug 28 19:14:42 2009 +0900 @@ -0,0 +1,394 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis <sdecugis@nict.go.jp> * +* * +* Copyright (c) 2008, WIDE Project and NICT * +* All rights reserved. * +* * +* Redistribution and use of this software in source and binary forms, with or without modification, are * +* permitted provided that the following conditions are met: * +* * +* * Redistributions of source code must retain the above * +* copyright notice, this list of conditions and the * +* following disclaimer. * +* * +* * 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. * +* * +* * Neither the name of the WIDE Project or NICT nor the * +* names of its contributors may be used to endorse or * +* promote products derived from this software without * +* specific prior written permission of WIDE Project and * +* NICT. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. * +*********************************************************************************************************/ + +/* Messages queues module. + * + * The threads that call these functions must be in the cancellation state PTHREAD_CANCEL_ENABLE and type PTHREAD_CANCEL_DEFERRED. + * This is the default state and type on thread creation. + * + * In order to destroy properly a queue, the application must: + * -> shutdown any process that can add into the queue first. + * -> pthread_cancel any thread that could be waiting on the queue. + * -> consume any message that is in the queue, using meq_tryget. + * -> then destroy the queue using meq_del. + */ + +#include "libfd.h" + +/* Definition of a message queue object */ +struct mqueue { + int eyec; /* An eye catcher, also used to check a queue is valid. MQ_EYEC */ + + pthread_mutex_t mtx; /* Mutex protecting this queue */ + pthread_cond_t cond; /* condition variable of the list */ + + struct fd_list list; /* sentinel for the list of messages */ + int count; /* number of objects in the list */ + int thrs; /* number of threads waiting for a new message (when count is 0) */ + + uint16_t high; /* High level threshold (see libfreediameter.h for details) */ + uint16_t low; /* Low level threshhold */ + void *data; /* Opaque pointer for threshold callbacks */ + void (*h_cb)(struct mqueue *, void **); /* The callbacks */ + void (*l_cb)(struct mqueue *, void **); + int highest;/* The highest count value for which h_cb has been called */ +}; + +/* The eye catcher value */ +#define MQ_EYEC 0xe7ec1130 + +/* Macro to check a pointer */ +#define CHECK_QUEUE( _queue ) (( (_queue) != NULL) && ( (_queue)->eyec == MQ_EYEC) ) + + +/* Create a new message queue */ +int fd_mq_new ( struct mqueue ** queue ) +{ + struct mqueue * new; + + TRACE_ENTRY( "%p", queue ); + + CHECK_PARAMS( queue ); + + /* Create a new object */ + CHECK_MALLOC( new = malloc (sizeof (struct mqueue) ) ); + + /* Initialize the content */ + memset(new, 0, sizeof(struct mqueue)); + + new->eyec = MQ_EYEC; + CHECK_POSIX( pthread_mutex_init(&new->mtx, NULL) ); + CHECK_POSIX( pthread_cond_init(&new->cond, NULL) ); + + fd_list_init(&new->list, NULL); + + /* We're done */ + *queue = new; + return 0; +} + +/* Delete a message queue. It must be unused. */ +int fd_mq_del ( struct mqueue ** queue ) +{ + struct mqueue * q; + + TRACE_ENTRY( "%p", queue ); + + CHECK_PARAMS( queue && CHECK_QUEUE( *queue ) ); + + q = *queue; + + CHECK_POSIX( pthread_mutex_lock( &q->mtx ) ); + + if ((q->count != 0) || (q->thrs != 0) || (q->data != NULL)) { + TRACE_DEBUG(INFO, "The queue cannot be destroyed (%d, %d, %p)", q->count, q->thrs, q->data); + CHECK_POSIX_DO( pthread_mutex_unlock( &q->mtx ), /* no fallback */ ); + return EINVAL; + } + + /* sanity check */ + ASSERT(FD_IS_LIST_EMPTY(&q->list)); + + /* Ok, now invalidate the queue */ + q->eyec = 0xdead; + + /* And destroy it */ + CHECK_POSIX( pthread_mutex_unlock( &q->mtx ) ); + + CHECK_POSIX( pthread_cond_destroy( &q->cond ) ); + + CHECK_POSIX( pthread_mutex_destroy( &q->mtx ) ); + + free(q); + *queue = NULL; + + return 0; +} + +/* Get the length of the queue */ +int fd_mq_length ( struct mqueue * queue, int * length ) +{ + TRACE_ENTRY( "%p %p", queue, length ); + + /* Check the parameters */ + CHECK_PARAMS( CHECK_QUEUE( queue ) && length ); + + /* lock the queue */ + CHECK_POSIX( pthread_mutex_lock( &queue->mtx ) ); + + /* Retrieve the count */ + *length = queue->count; + + /* Unlock */ + CHECK_POSIX( pthread_mutex_unlock( &queue->mtx ) ); + + /* Done */ + return 0; +} + +/* alternate version with no error checking */ +int fd_mq_length_noerr ( struct mqueue * queue ) +{ + if ( !CHECK_QUEUE( queue ) ) + return 0; + + return queue->count; /* Let's hope it's read atomically, since we are not locking... */ +} + +/* Set the thresholds of the queue */ +int fd_mq_setthrhd ( struct mqueue * queue, void * data, uint16_t high, void (*h_cb)(struct mqueue *, void **), uint16_t low, void (*l_cb)(struct mqueue *, void **) ) +{ + TRACE_ENTRY( "%p %p %hu %p %hu %p", queue, data, high, h_cb, low, l_cb ); + + /* Check the parameters */ + CHECK_PARAMS( CHECK_QUEUE( queue ) && (high > low) && (queue->data == NULL) ); + + /* lock the queue */ + CHECK_POSIX( pthread_mutex_lock( &queue->mtx ) ); + + /* Save the values */ + queue->high = high; + queue->low = low; + queue->data = data; + queue->h_cb = h_cb; + queue->l_cb = l_cb; + + /* Unlock */ + CHECK_POSIX( pthread_mutex_unlock( &queue->mtx ) ); + + /* Done */ + return 0; +} + +/* Post a new message in the queue */ +int fd_mq_post ( struct mqueue * queue, struct msg ** msg ) +{ + struct fd_list * new; + int call_cb = 0; + + TRACE_ENTRY( "%p %p", queue, msg ); + + /* Check the parameters */ + CHECK_PARAMS( CHECK_QUEUE( queue ) && msg && *msg ); + + /* Create a new list item */ + CHECK_MALLOC( new = malloc (sizeof (struct fd_list)) ); + + fd_list_init(new, *msg); + *msg = NULL; + + /* lock the queue */ + CHECK_POSIX( pthread_mutex_lock( &queue->mtx ) ); + + /* Add the new message at the end */ + fd_list_insert_before( &queue->list, new); + queue->count++; + if (queue->high && ((queue->count % queue->high) == 0)) { + call_cb = 1; + queue->highest = queue->count; + } + + /* Signal if threads are asleep */ + if (queue->thrs > 0) { + CHECK_POSIX( pthread_cond_signal(&queue->cond) ); + } + + /* Unlock */ + CHECK_POSIX( pthread_mutex_unlock( &queue->mtx ) ); + + /* Call high-watermark cb as needed */ + if (call_cb && queue->h_cb) + (*queue->h_cb)(queue, &queue->data); + + /* Done */ + return 0; +} + +/* Pop the first message from the queue */ +static struct msg * mq_pop(struct mqueue * queue) +{ + struct msg * ret = NULL; + struct fd_list * li; + + ASSERT( ! FD_IS_LIST_EMPTY(&queue->list) ); + + fd_list_unlink(li = queue->list.next); + queue->count--; + ret = (struct msg *)(li->o); + free(li); + + return ret; +} + +/* Check if the low watermark callback must be called. */ +static int test_l_cb(struct mqueue * queue) +{ + if ((queue->high == 0) || (queue->low == 0) || (queue->l_cb == 0)) + return 0; + + if (((queue->count % queue->high) == queue->low) && (queue->highest > queue->count)) { + queue->highest -= queue->high; + return 1; + } + + return 0; +} + +/* Try poping a message */ +int fd_mq_tryget ( struct mqueue * queue, struct msg ** msg ) +{ + int wouldblock = 0; + int call_cb = 0; + + TRACE_ENTRY( "%p %p", queue, msg ); + + /* Check the parameters */ + CHECK_PARAMS( CHECK_QUEUE( queue ) && msg ); + + /* lock the queue */ + CHECK_POSIX( pthread_mutex_lock( &queue->mtx ) ); + + /* Check queue status */ + if (queue->count > 0) { + /* There are messages in the queue, so pick the first one */ + *msg = mq_pop(queue); + call_cb = test_l_cb(queue); + } else { + wouldblock = 1; + *msg = NULL; + } + + /* Unlock */ + CHECK_POSIX( pthread_mutex_unlock( &queue->mtx ) ); + + /* Call low watermark callback as needed */ + if (call_cb) + (*queue->l_cb)(queue, &queue->data); + + /* Done */ + return wouldblock ? EWOULDBLOCK : 0; +} + +/* This handler is called when a thread is blocked on a queue, and cancelled */ +static void mq_cleanup(void * queue) +{ + struct mqueue * q = (struct mqueue *)queue; + TRACE_ENTRY( "%p", queue ); + + /* Check the parameter */ + if ( ! CHECK_QUEUE( q )) { + TRACE_DEBUG(INFO, "Invalid queue, skipping handler"); + return; + } + + /* The thread has been cancelled, therefore it does not wait on the queue anymore */ + q->thrs--; + + /* Now unlock the queue, and we're done */ + CHECK_POSIX_DO( pthread_mutex_unlock( &q->mtx ), /* nothing */ ); + + /* End of cleanup handler */ + return; +} + +/* The internal function for meq_timedget and meq_get */ +static int mq_tget ( struct mqueue * queue, struct msg ** msg, int istimed, const struct timespec *abstime) +{ + int timedout = 0; + int call_cb = 0; + + /* Check the parameters */ + CHECK_PARAMS( CHECK_QUEUE( queue ) && msg && (abstime || !istimed) ); + + /* Initialize the msg value */ + *msg = NULL; + + /* lock the queue */ + CHECK_POSIX( pthread_mutex_lock( &queue->mtx ) ); + +awaken: + /* Check queue status */ + if (queue->count > 0) { + /* There are messages in the queue, so pick the first one */ + *msg = mq_pop(queue); + call_cb = test_l_cb(queue); + } else { + int ret = 0; + /* We have to wait for a new message */ + queue->thrs++ ; + pthread_cleanup_push( mq_cleanup, queue); + if (istimed) { + ret = pthread_cond_timedwait( &queue->cond, &queue->mtx, abstime ); + } else { + ret = pthread_cond_wait( &queue->cond, &queue->mtx ); + } + pthread_cleanup_pop(0); + queue->thrs-- ; + if (ret == 0) + goto awaken; /* test for spurious wake-ups */ + + if (istimed && (ret == ETIMEDOUT)) { + timedout = 1; + } else { + /* Unexpected error condition (means we need to debug) */ + ASSERT( ret == 0 /* never true */ ); + } + } + + /* Unlock */ + CHECK_POSIX( pthread_mutex_unlock( &queue->mtx ) ); + + /* Call low watermark callback as needed */ + if (call_cb) + (*queue->l_cb)(queue, &queue->data); + + /* Done */ + return timedout ? ETIMEDOUT : 0; +} + +/* Get the next available message, block until there is one */ +int fd_mq_get ( struct mqueue * queue, struct msg ** msg ) +{ + TRACE_ENTRY( "%p %p", queue, msg ); + return mq_tget(queue, msg, 0, NULL); +} + +/* Get the next available message, block until there is one, or the timeout expires */ +int fd_mq_timedget ( struct mqueue * queue, struct msg ** msg, const struct timespec *abstime ) +{ + TRACE_ENTRY( "%p %p %p", queue, msg, abstime ); + return mq_tget(queue, msg, 1, abstime); +} +