Version 5 (modified by 13 years ago) (diff) | ,
---|
The freeDiameter API
The complete API is documented in header files in [source:freeDiameter/include/freeDiameter include/freeDiameter folder]. The following is a short summary of each module's purpose.
Debug support
Namespace: fd_log_* ; Source file(s): [source:freeDiameter/libfdproto/log.c]
Almost all functions from the freeDiameter framework use the function fd_log_debug
to output information. This function is very similar to printf
, but future versions of the daemon will allow the output to be directly stored into a file.
We also use some preprocessor macros extensively through the code, defined in libfdproto.h
. The TRACE_DEBUG(level, format, ...
macro is used to issue a debug message only if the current debug level (see Usage for information on setting this level) allows it. In addition, this macro cat output a lot of meta-information if DEBUG support is compiled in. Meta-information include the current thread name, the location in source files where the debug message was issued, the timestamp, etc.
Another family of macro that is used extensively is the CHECK_*
macro. For example:
- CHECK_FCT_DO
-
This function takes two parameters.
The first parameter is a function call, the second is a "fallback path".
When this macro is called:
- Optionally if the debug level is very high, it issues a debug message with the function call
- Then the function call is executed.
- If the return value is 0, the call is successful, "fallback path" is not executed.
- Otherwise, the error code is dumped in debug, then "fallback path" is executed.
There are several families of CHECK_* macro:
- CHECK_FCT*: Use for calls to freeDiameter functions. This considers a non-0 return code as a standard error message.
- CHECK_POSIX*: Use for calls to the pthread_* functions. This also considers a non-0 return code as a standard error message.
- CHECK_SYS*: Use for other system calls. This considers <0 return codes as an error and retrieves the error code from
errno
variable. - CHECK_MALLOC*: Use for memory allocations (
malloc
,strdup
, ...). This considers a NULL return value as an error, and reads the error code inerrno
. - CHECK_PARAMS*: Use for validating input parameters of functions. A value that evaluates to false is considered an error condition, similar to the
assert
function.
In each family, the macro exists in two flavours:
- <macro>(call): In case of failure, displays an error message and return the error code.
- <macro>_DO(call, fallback): In case of failure, displays an error message then execute the fallback code.
Binary strings
Namespace: fd_os_* ; Source file(s): [source:freeDiameter/libfdproto/ostr.c]
Note: this is new in freeDiameter 1.1.x.
In order to differentiate the contents of buffers, we use the following types instead of simple char *
:
- uint8_t *
-
For bulk data, for example received from the network. We do not make any assumption on the contents. This data may not be suitable for any string operation such as
strlen
orprintf
.
- os0_t
-
This is a pointer to a binary buffer that should not contain NUL bytes ('\0') except as its terminating byte. This type always come with a
size_t
information, that does not include the final NUL. It therefore should be safe to use some string operations, but it is generally a good idea to avoid it. There is no guarantee in particular that the contents is valid UTF-8 characters, soprintf
should be avoided (except for debug purpose).
There are a few functions that allows the manipulation of os0_t
data: fd_os_is_valid_os0
(checks if a buffer contains a NUL byte), os0dup
(safe and fast equivalent to strdup), fd_os_cmp
(fast comparison function that does not mimic strcmp
) and fd_os_almostcasecmp
(a kind of case-insensitive counterpart).
- !DiamId_t
-
Buffers with this type should contain valid Diameter Identity buffer. It means that it contains only letters, digits, hyphens and dots. In particular, if a Diameter Identity is internationalized, this contains the
stringprep
transformation result ("xn--..."). Note however that if the framework is compiled with option DIAMID_IDNA_IGNORE, the buffer might contain unexpected characters. Treat with care.
There are also some functions to handle this type, such as fd_os_is_valid_DiameterIdentity
(checks if a buffer complies to the definition), and fd_os_validate_DiameterIdentity
(transforms a string into a valid !DiamId_t).
Lists
Namespace: fd_list_* ; Source file(s): [source:freeDiameter/libfdproto/lists.c]
The fd_list
structure is used extensively in the framework. It gives a unified way to handle circular doubly-linked lists in the code. You can find many examples of use throughout the code.
Although our implementation is not optimal in terms of memory occupation (the fd_list structure contains 4 pointers: next in list, previous in list, pointer to list root, and a last pointer that is available for use depending on where the list is used), it is quite simple to use and very flexible.
Macros:
- FD_LIST_INITIALIZER: To statically initialize an empty list.
- FD_IS_LIST_EMPTY: returns 0 if the list is not empty, !0 otherwise.
Main functions:
- fd_list_insert_after: insert a new element in a list after given position.
- fd_list_unlink: remove an element from a list.
Sample code
Typically, this structure will be included in a bigger structure that needs to be chained in list, like this:
struct my_data { ... struct fd_list chaining; ... };
Now, if you want to create a list of my_data elements, you declare an empty list first:
struct fd_list my_data_list = FD_LIST_INITIALIZER(my_data_list);
Add a new my_data in the list:
struct my_data * n; /* Allocate the my_data element */ CHECK_MALLOC( n = malloc(sizeof(struct my_data)) ); memset(n, 0, sizeof(struct my_data)); /* Initialize the chaining element */ fd_list_init(&n->chaining, n); /* Initialize other fields */ ... /* Now, add the element 'n' at the end of the list */ fd_list_insert_before(&my_data_list, &n->chaining);
And here is how to iterate through the list:
struct fd_list * li; /* list item */ for (li = my_data_list.next; li != &my_data_list; li = li->next) { struct my_data * md = li->o; /* Do something with this my_data element */ }
Diameter Dictionary support
Namespace: fd_dict_* ; Source file(s): [source:freeDiameter/libfdproto/dictionary.c]
This module provides support for a dictionary of Diameter objects. Because the Diameter protocol does not include any "Type" information in the AVP headers, it is impossible to parse a message and interpret the attribute contents without a dictionary definition that maps each code & vendor combination to a type.
The freeDiameter dictionary contains much more information though: the dict_object_type
enum shows all the types of objects that can be defined in the dictionary: vendors, applications, types, constants values, AVPs, commands, and rules. These types are explained bellow.
A dictionary in freeDiameter is of type struct dictionary *
. It is initialized with fd_dict_init
and destroyed with fd_dict_fini
. The libfdproto library does not depend on an instance of a dictionary. On the other hand, the libfdcore defines a global dictionary that can be used by extensions also. This instance is accessed with fd_g_config->cnf_dict
.
A dictionary contains a collection of struct dict_object *
objects, which are vendors, applications, etc. The API to access these objects is independant of the type of the object. The main functions are:
- fd_dict_new: create a new object and store in a dictionary.
- fd_dict_search: Search for an object in a dictionary.
The [source:freeDiameter/include/freeDiameter/libfdproto.h] file contains a lot of information and example on using these function for each type of objects. You may also find additional information in this archived mail.
- vendor
- This associates a name with a vendor code as defined by "SMI Network Management Private Enterprise Codes" (RFC3232). This allows for a nicer debug output, but also provides a hash table for the AVP definitions.
- application
- This associates a Diameter application code (IANA assigned) with its name. In addition, the application can be associated with a Vendor during its creation.
- type
-
This corresponds to the "Derived type" defined in RFC3588. An AVP can be either defined with a Base Type (OctetString, Unsigned32, ...) or a Derived Type. In terms of pure parsing, it does not change anything, but defining a Derived Type allows to:
- define functions to handle the data with a specific encoding for example (see this mail for more details).
- and/or define constants values for this type. This is a bit different from the Diameter RFC that defines all AVP with constant values as Enumerated type.
- constants
-
This corresponds to the values of Enumerated AVPs defined by Diameter standard -- with the following differences:
- In our dictionary model, a constant is defined for a type specific to the AVP, whereas the RFC only uses "Enumerated" derived type for all AVPs. We use the convention name "Enumerated (AVP-name)" to distinguish the different Enumerated types. In addition, if the RFC does not specifically defines the AVP as enumerated, but we want to do it, we use "Enumerated* (AVP-name)" template name.
- In our dictionary model, constants can be defined not only for Unsigned32 values (as specified in Diameter) but also for any other basic type (floats, integer64, ...)
- AVP
- This object associates AVP code and vendor to its name, flags, and basic type, which allows for parsing the Diameter message and interpreting the content of the AVP (for example, we can learn if an AVP containts OctetString data or if this is a Grouped AVP containing other AVPs in its data portion). Optionaly, the AVP can be associated with a Derived Type when it is created, which allows some additional features.
- command
- This associates a command code to the command name and flags. Note that usually two dictionary objects must be created for each code: one for the "*-Request" (R flag set) and one for the "*-Answer" (R flag cleared).
- rules
- This is a simple way to implement the ABNF rules from the RFC for commands and grouped AVPs, and have an automatic validation of the messages based on these rules.
As a "good practice", it is better to keep dictionary definitions of standard objects (i.e. that are defined in an RFC for example) as separate extensions from the applications that will use these objects. The rationale is that Diameter definitions are meant to be reused across applications, so we avoid code duplication and allow for faster development by keeping the dictionary definitions separated. Such dictionary-only extensions are conventionally named dict_*.fdx
(see Extensions for the list of existing extensions).
Sessions support
Namespace: fd_sess_* ; Source file(s): [source:freeDiameter/libfdproto/sessions.c]
Messages management
Namespace: fd_msg_* ; Source file(s): [source:freeDiameter/libfdproto/messages.c], [source:freeDiameter/libfdcore/messages.c]
libfdcore Initialization and Configuration
Namespace: fd_g_*, fd_core_* ; Source file(s): [source:freeDiameter/libfdcore/core.c]
Queues and events
Namespace: fd_fifo_*, fd_event_* ; Source file(s): [source:freeDiameter/libfdproto/fifo.c], [source:freeDiameter/libfdcore/events.c]
Peers
'Namespace: fd_peer_* ; Source file(s): [source:freeDiameter/libfdcore/peers.c], libfdcore/p_*.c
Routing and Dispatching
Namespace: fd_rt_*, fd_disp_* ; Source file(s): [source:freeDiameter/libfdproto/dispatch.c], [source:freeDiameter/libfdcore/routing_dispatch.c]
Extensions interface
Namespace: fd_ext_* ; Source file(s): [source:freeDiameter/libfdcore/extensions.c]