Mercurial > hg > freeDiameter
changeset 1:bafb831ba688
Fix names to proper case for freeDiameter
author | Sebastien Decugis <sdecugis@nict.go.jp> |
---|---|
date | Mon, 31 Aug 2009 11:31:10 +0900 |
parents | 13530e1f02e3 |
children | d8ce06172629 |
files | CMakeLists.txt INSTALL README freeDiameter/CMakeLists.txt freeDiameter/dict_base_proto.c freeDiameter/fD.h freeDiameter/main.c freeDiameter/tests/CMakeLists.txt freeDiameter/tests/testdict.c freeDiameter/tests/testmesg.c freeDiameter/tests/testmq.c freeDiameter/tests/tests.h freediameter/CMakeLists.txt freediameter/dict_base_proto.c freediameter/fd.h freediameter/main.c freediameter/tests/CMakeLists.txt freediameter/tests/testdict.c freediameter/tests/testmesg.c freediameter/tests/testmq.c freediameter/tests/tests.h include/freeDiameter/CMakeLists.txt include/freeDiameter/freeDiameter-host.h.in include/freeDiameter/freeDiameter.h include/freeDiameter/libfreeDiameter.h include/freediameter/CMakeLists.txt include/freediameter/freediameter-host.h.in include/freediameter/freediameter.h include/freediameter/libfreediameter.h libfreeDiameter/CMakeLists.txt libfreeDiameter/dictionary.c libfreeDiameter/init.c libfreeDiameter/libfD.h libfreeDiameter/lists.c libfreeDiameter/log.c libfreeDiameter/messages.c libfreeDiameter/mqueues.c libfreediameter/CMakeLists.txt libfreediameter/dictionary.c libfreediameter/init.c libfreediameter/libfd.h libfreediameter/lists.c libfreediameter/log.c libfreediameter/messages.c libfreediameter/mqueues.c |
diffstat | 45 files changed, 12527 insertions(+), 12531 deletions(-) [+] |
line wrap: on
line diff
--- a/CMakeLists.txt Fri Aug 28 19:14:42 2009 +0900 +++ b/CMakeLists.txt Mon Aug 31 11:31:10 2009 +0900 @@ -1,7 +1,7 @@ # 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) +PROJECT("freeDiameter" C) # Some subfolders may have tests ENABLE_TESTING() @@ -18,7 +18,7 @@ # Location for the include files INCLUDE_DIRECTORIES(include) INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}/include) -SUBDIRS(include/freediameter) +SUBDIRS(include/freeDiameter) # some subfolders use yacc and lex parsers SET(BISON_GENERATE_DEFINES TRUE) @@ -33,8 +33,8 @@ # how to do the check with cmake??? # Location for the source code -SUBDIRS(libfreediameter) -SUBDIRS(freediameter) +SUBDIRS(libfreeDiameter) +SUBDIRS(freeDiameter) # Do we build the extensions? OPTION(IGNORE_ALL_EXTENSIONS "Ignore the extensions completly?")
--- a/INSTALL Fri Aug 28 19:14:42 2009 +0900 +++ b/INSTALL Mon Aug 31 11:31:10 2009 +0900 @@ -12,10 +12,10 @@ 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: instead of passing options on the command line, you can use a CMake front-end (for example cmake-gui) +or edit the CCmakeCache.txt file directly. -Note that there are dependencies on some external tools that may not be enforced by the configure script. +Note that there are dependencies on 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 @@ -27,13 +27,13 @@ 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) +IGNORE_ALL_EXTENSIONS: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 +You can change the default configuration file pathname: +DEFAULT_CONF_FILE:STRING=/path/to/some/freeDiameter.conf
--- a/README Fri Aug 28 19:14:42 2009 +0900 +++ b/README Mon Aug 31 11:31:10 2009 +0900 @@ -1,9 +1,9 @@ -The FreeDiameter daemon implements the Diameter base protocol. -Loadable extensions add the logic of Diameter applications or advanced use of the daemon. +The freeDiameter daemon implements the Diameter base protocol. +Loadable extensions add the logic of Diameter applications and 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) +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. +This project is not related to the "freediameter" project from Sun on sourceforge. Author: Sebastien Decugis.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/freeDiameter/CMakeLists.txt Mon Aug 31 11:31:10 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 Mon Aug 31 11:31:10 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 Mon Aug 31 11:31:10 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 Mon Aug 31 11:31:10 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 Mon Aug 31 11:31:10 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 (all but main) +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 Mon Aug 31 11:31:10 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 Mon Aug 31 11:31:10 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 Mon Aug 31 11:31:10 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 threshold 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 Mon Aug 31 11:31:10 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 */
--- a/freediameter/CMakeLists.txt Fri Aug 28 19:14:42 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,24 +0,0 @@ -# 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 )
--- a/freediameter/dict_base_proto.c Fri Aug 28 19:14:42 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3435 +0,0 @@ -/********************************************************************************************************* -* 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; -}
--- a/freediameter/fd.h Fri Aug 28 19:14:42 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,48 +0,0 @@ -/********************************************************************************************************* -* 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 */
--- a/freediameter/main.c Fri Aug 28 19:14:42 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,59 +0,0 @@ -/********************************************************************************************************* -* 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; -}
--- a/freediameter/tests/CMakeLists.txt Fri Aug 28 19:14:42 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,47 +0,0 @@ -# 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 )
--- a/freediameter/tests/testdict.c Fri Aug 28 19:14:42 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,136 +0,0 @@ -/********************************************************************************************************* -* 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(); -} -
--- a/freediameter/tests/testmesg.c Fri Aug 28 19:14:42 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1230 +0,0 @@ -/********************************************************************************************************* -* 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(); -} -
--- a/freediameter/tests/testmq.c Fri Aug 28 19:14:42 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,423 +0,0 @@ -/********************************************************************************************************* -* 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(); -}
--- a/freediameter/tests/tests.h Fri Aug 28 19:14:42 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,132 +0,0 @@ -/********************************************************************************************************* -* 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 Mon Aug 31 11:31:10 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 Mon Aug 31 11:31:10 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 Mon Aug 31 11:31:10 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 Mon Aug 31 11:31:10 2009 +0900 @@ -0,0 +1,2017 @@ +/********************************************************************************************************* +* 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> + */ + +#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 */
--- a/include/freediameter/CMakeLists.txt Fri Aug 28 19:14:42 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,84 +0,0 @@ -#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)
--- a/include/freediameter/freediameter-host.h.in Fri Aug 28 19:14:42 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,54 +0,0 @@ -/********************************************************************************************************* -* 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 */
--- a/include/freediameter/freediameter.h Fri Aug 28 19:14:42 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,113 +0,0 @@ -/********************************************************************************************************* -* 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 */
--- a/include/freediameter/libfreediameter.h Fri Aug 28 19:14:42 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2020 +0,0 @@ -/********************************************************************************************************* -* 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 Mon Aug 31 11:31:10 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 Mon Aug 31 11:31:10 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 Mon Aug 31 11:31:10 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 Mon Aug 31 11:31:10 2009 +0900 @@ -0,0 +1,49 @@ +/********************************************************************************************************* +* 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 Mon Aug 31 11:31:10 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 Mon Aug 31 11:31:10 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 Mon Aug 31 11:31:10 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 Mon Aug 31 11:31:10 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); +} +
--- a/libfreediameter/CMakeLists.txt Fri Aug 28 19:14:42 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,25 +0,0 @@ -# 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}) -
--- a/libfreediameter/dictionary.c Fri Aug 28 19:14:42 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1671 +0,0 @@ -/********************************************************************************************************* -* 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; -}
--- a/libfreediameter/init.c Fri Aug 28 19:14:42 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,53 +0,0 @@ -/********************************************************************************************************* -* 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; -}
--- a/libfreediameter/libfd.h Fri Aug 28 19:14:42 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,50 +0,0 @@ -/********************************************************************************************************* -* 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 */
--- a/libfreediameter/lists.c Fri Aug 28 19:14:42 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,273 +0,0 @@ -/********************************************************************************************************* -* 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; -}
--- a/libfreediameter/log.c Fri Aug 28 19:14:42 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,107 +0,0 @@ -/********************************************************************************************************* -* 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; -}
--- a/libfreediameter/messages.c Fri Aug 28 19:14:42 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2139 +0,0 @@ -/********************************************************************************************************* -* 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; -} - -/***************************************************************************************************************/
--- a/libfreediameter/mqueues.c Fri Aug 28 19:14:42 2009 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,394 +0,0 @@ -/********************************************************************************************************* -* 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); -} -