radcli adds RADIUS authentication and accounting to an application in roughly 50 lines of code using a configuration file. All server addresses, credentials, and transport choices (UDP, TCP, TLS, DTLS) live in that config file, so the application carries no transport-specific code and does not need to know or care which one is in use.
This page documents the current, recommended way to use radcli.
Protocols and features
- Authentication and accounting (RFC 2865, RFC 2866, RFC 2869): PAP-style Access-Request/Accept/Reject/Challenge, and Accounting-Request/Response, including the Message-Authenticator attribute and Acct-Input/Output-Octets and -Gigawords pairing. It includes a built-in dictionary covering RFC 2865/2866/2869's attributes as well as RFC6929 and RFC 8044 data types; extendable with additional dictionaries via radcli_ctx_read_dictionary().
- Transport: plain UDP or TCP, or TLS/DTLS ("RadSec", RFC 6614 for TLS and RFC 7360 for DTLS), selected purely through the config file's serv-type and requiring no transport-specific application code. TLS/DTLS credentials are either X.509 (CA/certificate/key files) or a Pre-Shared Key, set programmatically with radcli_ctx_set_tls_psk() or via the config file's tls-* options.
- Dynamic Authorization / DAC (RFC 5176, RFC 5176 dynamic authorization (CoA/Disconnect)): a runnable CoA-Request/Disconnect-Request server built from radcli_dae_new(), radcli_dae_set_handler(), and radcli_dae_start(), driven through the same non-blocking poll surface as the rest of the API (radcli_ctx_get_poll()/radcli_ctx_dispatch()). Incoming requests from a Dynamic Authorization Client (DAC) go through a full validation pipeline – source-address authorization, Request Authenticator, Message-Authenticator, Event-Timestamp freshness, and duplicate suppression – before the application's handler ever sees them. See src/radexample-async-dae.c for a working Disconnect server alongside an RFC 5997 watchdog. DAC traffic today is UDP/3799 per RFC 5176.
Quick start
The normal call sequence is three steps:
- Load configuration – parses the config file and initialises the transport:
radcli_ctx * radcli_ctx_read_config(const char *filename, unsigned flags)
Create a context by parsing a config file – the main, recommended way to configure radcli.
- Build an attribute list – attach the attributes you want to send. Each add call is independent; radcli_avp_list_error() gives one aggregate check for all of them instead of testing every call's return value:
}
int radcli_avp_list_error(const radcli_avp_list *list)
Check whether any radcli_avp_add_*()/_by_num() call on list has ever failed.
int radcli_avp_add_uint32_by_num(radcli_avp_list *list, const radcli_ctx *ctx, uint32_t attrid, uint32_t vendor, uint32_t value)
Look up an integer/IPv4-address/date-typed attribute by legacy numeric ID and append it.
int radcli_avp_add_bytes_by_num(radcli_avp_list *list, const radcli_ctx *ctx, uint32_t attrid, uint32_t vendor, const void *value, size_t len)
Look up an attribute by legacy numeric ID and append its bytes.
int radcli_avp_add_str_by_num(radcli_avp_list *list, const radcli_ctx *ctx, uint32_t attrid, uint32_t vendor, const char *value)
Look up a string-typed attribute by legacy numeric ID and append it.
radcli_avp_list * radcli_avp_list_new(void)
Create an empty attribute-value pair list.
- Send the request – radcli_aaa() handles retries, failover, and response validation automatically:
radcli_avp_list *recvd = NULL;
int result =
radcli_aaa(ctx, RADCLI_CODE_ACCESS_REQUEST, send, &out_code, &recvd);
void radcli_avp_list_free(radcli_avp_list *list)
Free a list and every attribute it holds.
void radcli_ctx_free(radcli_ctx *ctx)
Release a context.
int radcli_aaa(radcli_ctx *ctx, radcli_code code, const radcli_avp_list *send, radcli_code *out_code, radcli_avp_list **out_attrs)
Perform an authentication or accounting exchange with Acct-Delay-Time autofill and fail-over across e...
The transport is selected entirely in the config file (serv-type = udp, tcp, tls, or dtls); no code changes are required to switch. TLS and DTLS additionally require certificate or PSK credentials to be set in the config file (tls-ca-file, tls-cert-file, tls-key-file), or programmatically with radcli_ctx_set_tls_psk().
See src/radexample.c for a complete, runnable example, and src/radexample-async-dae.c for an asynchronous version with Dynamic Authorization one.
Transitioning from earlier versions
Already using the legacy radcli API or even freeradius-client.h/radiusclient-ng.h? See the migration guide for a function-by-function map and before/after simplifications, or go straight to the legacy API reference if you just need to look something up.