Tools radiator-client
Command-line RADIUS and TACACS+ test utility
- Installation
- Command-Line Options
- Basic Usage
- Password Authentication Methods
- Adding Attributes
- Using Dictionary Names
- Using Raw AVP Format
- Using Vendor-Specific Attributes
- Reading Attributes from File
- Request Types
- Access-Request (auth)
- Accounting-Request (acct)
- Disconnect-Request
- CoA-Request
- Repeating Requests
- Transport Protocols
- UDP Transport (Default)
- TCP Transport
- TLS Transport (RadSec)
- TLS Options
- PROXY Protocol Support
- Raw Packet Handling
- Send Raw Hex Packet
- Save Packet to File
- Read Packet from File
- Response Code Validation
- Reply Message Validation
- Response Attribute Validation
- Subcommands
- Format Packet
- RADIUS Performance Testing
- Run a sustained performance test
- Choose the RADIUS protocol
- TACACS+ Testing
- Run a fixed number of TACACS+ sessions
- Run a sustained TACACS+ performance test
- Test TACACS+ session multiplexing
- Choose the TACACS+ transport
- Exit Codes
- Troubleshooting
- Common Issues
- Verbose Mode
- JSON Mode
- Dry Run
- Hex Dump
- Using Custom Dictionary
- Related Documentation
radiator-client sends test traffic to RADIUS and TACACS+ servers. The default
mode sends individual RADIUS requests. Use a subcommand for RADIUS performance
testing or TACACS+ sessions.
Installation
The radiator-client binary is included in the Radiator Server package:
/opt/radiator/server/bin/radiator-client
Command-Line Options
Run radiator-client --help to see all available modes. Use these commands to
see mode-specific options:
radiator-client performance --help
radiator-client tacacs --help
The examples below without a subcommand use the default RADIUS request mode.
Pass network destinations as --server HOST or --server HOST:PORT. A bare
IPv6 address does not need brackets. Enclose IPv6 in brackets when setting an
explicit port, for example --server [::1]:11812.
When the port is omitted, UDP and TCP use port 1812 for authentication, 1813
for accounting, and 3799 for CoA and Disconnect requests. TLS uses the RadSec
port 2083. If you omit --server entirely, the host defaults to 127.0.0.1.
Basic Usage
Send an Access-Request with username and password:
radiator-client \
--server 127.0.0.1 \
--secret mysecret \
--user alice \
--password alicepass
Send an Accounting-Request:
radiator-client \
--server 127.0.0.1 \
--secret mysecret \
--user alice \
--type acct
Password Authentication Methods
By default, radiator-client uses --auth-method pap and sends the password in
the RADIUS User-Password attribute.
Use --auth-method mschap or --auth-method mschapv2 to generate the required
MS-CHAP attributes automatically from --user and --password. These modes are
supported only with --type auth.
radiator-client \
--server 127.0.0.1:1812 \
--secret mysecret \
--user 'DOMAIN\alice' \
--password secret \
--auth-method mschapv2
Authentication method values:
pap: Send the password usingUser-Password.mschap: SendMS-CHAP-ChallengeandMS-CHAP-Response.mschapv2: SendMS-CHAP-ChallengeandMS-CHAP2-Response.
Adding Attributes
Using Dictionary Names
Add attributes by name with automatic type conversion:
radiator-client \
--server 127.0.0.1:1812 \
--secret mysecret \
--user alice \
--password secret \
--attr "Service-Type=Framed-User" \
--attr "NAS-IP-Address=10.0.0.1" \
--attr "Cisco-avpair=shell:priv-lvl=15"
Using Raw AVP Format
Add raw AVPs when dictionary names are unavailable:
# Syntax: <type>=<format>,<value>
# Formats: str, hex, u32, i32, u128, i128, ipv4, ipv6
radiator-client \
--server 127.0.0.1:1812 \
--secret mysecret \
--avp "1=str,alice" \
--avp "4=ipv4,10.0.0.1" \
--avp "6=u32,2"
Using Vendor-Specific Attributes
Add VSAs with vendor ID and type:
# Syntax: <vendor_id>:<vendor_type>=<format>,<value>[;<vendor_type>=<format>,<value>]
radiator-client \
--server 127.0.0.1:1812 \
--secret mysecret \
--user alice \
--vsa "9:1=str,shell:priv-lvl=15"
Reading Attributes from File
Create an attribute file:
# attributes.txt
attr User-Name=alice
attr NAS-IP-Address=10.0.0.1
avp 6=u32,2
vsa 9:1=str,shell:priv-lvl=15
Use the file:
radiator-client \
--server 127.0.0.1:1812 \
--secret mysecret \
--attr-file attributes.txt
Read from stdin:
echo "attr User-Name=testuser" | radiator-client \
--server 127.0.0.1:1812 \
--secret mysecret \
--attr-file -
Request Types
Access-Request (auth)
radiator-client \
--type auth \
--server 127.0.0.1:1812 \
--secret mysecret \
--user alice \
--password secret
Expected response: Access-Accept (code 2)
Accounting-Request (acct)
radiator-client \
--type acct \
--server 127.0.0.1:1813 \
--secret mysecret \
--user alice \
--attr "Acct-Status-Type=Start" \
--attr "Acct-Session-Id=session123"
Expected response: Accounting-Response (code 5)
Disconnect-Request
radiator-client \
--type disconnect-request \
--server 127.0.0.1:1812 \
--secret mysecret \
--user alice \
--attr "Acct-Session-Id=session123"
Expected response: Disconnect-ACK (code 40)
CoA-Request
radiator-client \
--type coa-request \
--server 127.0.0.1:1812 \
--secret mysecret \
--user alice \
--attr "Session-Timeout=3600"
Expected response: CoA-ACK (code 43)
Repeating Requests
Execute multiple requests for testing:
# Repeat 10 times, fail if any request fails
radiator-client \
--server 127.0.0.1:1812 \
--secret mysecret \
--user alice \
--password secret \
--repeat 10 \
--repeat-mode all
# Repeat 10 times, succeed if at least one request succeeds
radiator-client \
--server 127.0.0.1:1812 \
--secret mysecret \
--user alice \
--password secret \
--repeat 10 \
--repeat-mode any
Repeat modes:
all: Exit with error if any request fails (default)any: Exit with error only if all requests failwhatever: Always exit with zero regardless of results
Transport Protocols
By default, radiator-client uses UDP transport. You can specify TCP or TLS (RadSec) transport using the --transport flag.
UDP Transport (Default)
radiator-client \
--server 127.0.0.1:1812 \
--secret mysecret \
--user alice \
--password secret
TCP Transport
Use plain TCP transport for RADIUS-over-TCP:
radiator-client \
--server 127.0.0.1:2083 \
--secret mysecret \
--user alice \
--password secret \
--transport tcp
TLS Transport (RadSec)
Use TLS transport for secure RADIUS communication (RFC 6614). When using TLS, the shared secret is typically radsec.
Basic TLS with system CA certificates:
radiator-client \
--server radius.example.com:2083 \
--secret radsec \
--user alice \
--password secret \
--transport tls \
--tls-server-name radius.example.com
TLS with custom CA certificate:
radiator-client \
--server 127.0.0.1:2083 \
--secret radsec \
--user alice \
--password secret \
--transport tls \
--tls-ca-certificate /path/to/ca.pem \
--tls-server-name radius.example.com
TLS with mutual authentication (client certificate):
radiator-client \
--server 127.0.0.1:2083 \
--secret radsec \
--user alice \
--password secret \
--transport tls \
--tls-ca-certificate /path/to/ca.pem \
--tls-certificate /path/to/client-cert.pem \
--tls-key /path/to/client-key.pem \
--tls-server-name radius.example.com
TLS Options
| Option | Description |
|---|---|
--tls-certificate | Path to client certificate file (PEM format) |
--tls-key | Path to client private key file (PEM format) |
--tls-ca-certificate | Path to CA certificate file (PEM format) |
--tls-server-name | Server name for TLS SNI and certificate verification |
--radius-version | RADIUS packet profile: 1.0 or 1.1; RADIUS/1.1 requires TLS |
--tls-no-alpn | Omit ALPN for pre-ALPN RADIUS/1.0 |
--radius-token | Override the RADIUS/1.1 Token field |
--radius11-reserved-1 | Override the RADIUS/1.1 Reserved-1 field |
--radius11-reserved-2 | Override the RADIUS/1.1 Reserved-2 field |
--radius11-raw-attribute | Append a raw attribute to a RADIUS/1.1 packet |
If --tls-ca-certificate is not specified, system root certificates are used for server verification.
RADIUS/1.1 requires mutual TLS. When you set --radius-version 1.1, also set
--tls-certificate and --tls-key.
The Token, reserved-field, and raw-attribute overrides are testing options. They can deliberately generate packets that violate RADIUS/1.1 requirements so that receiver validation can be tested.
PROXY Protocol Support
PROXY protocol can be combined with TCP or TLS transport.
Send requests with PROXY protocol v2 header:
radiator-client \
--server 127.0.0.1:1812 \
--secret mysecret \
--user alice \
--password secret \
--transport tcp \
--proxy-protocol v2 \
--proxy-client-addr 192.168.1.100:12345
Raw Packet Handling
Send Raw Hex Packet
Replay captured RADIUS packets:
# From Wireshark: right-click packet, Copy -> As Hex Stream
radiator-client \
--server 127.0.0.1:1812 \
--hex "0104002a..." \
--expect-code Access-Accept
Save Packet to File
Write packet hex without sending:
radiator-client \
--server 127.0.0.1:1812 \
--secret mysecret \
--user alice \
--password secret \
--hex-output-file packet.hex
Read Packet from File
radiator-client \
--server 127.0.0.1:1812 \
--hex-input-file packet.hex
Response Code Validation
Use --expect-code to validate the response packet type. It accepts
either a numeric code or a case-insensitive packet type name:
radiator-client \
--server 127.0.0.1:1812 \
--secret mysecret \
--user alice \
--password secret \
--expect-code Access-Accept
Valid packet type names (case-insensitive): access-request, access-accept,
access-reject, accounting-request, accounting-response, access-challenge,
status-server, disconnect-request, disconnect-ack, disconnect-nak,
coa-request, coa-ack, coa-nak.
Reply Message Validation
Use --expect-message to assert that the response Reply-Message matches the
given string exactly. --expect-messsage (with triple 's') is also accepted as
an alias for backwards compatibility.
radiator-client \
--server 127.0.0.1:1812 \
--secret mysecret \
--user alice \
--password secret \
--expect-code access-reject \
--expect-message "Invalid password"
The command exits with a non-zero status if the response has no
Reply-Message or if the value does not match exactly.
Response Attribute Validation
Use --expect-attr to match a decoded response attribute value with an exact
string or a regular expression.
Supported operators:
==exact match!=exact non-match=~regex match!=~regex non-match
Use == and =~ for positive assertions:
radiator-client \
--server 127.0.0.1:1812 \
--secret mysecret \
--user alice \
--password secret \
--expect-attr "State==0xdeadbeef" \
--expect-attr "Reply-Message=~/Invalid/"
Use != and !=~ for negative assertions:
radiator-client \
--server 127.0.0.1:1812 \
--secret mysecret \
--user alice \
--password secret \
--expect-attr "Filter-Id!=ding" \
--expect-attr "Reply-Message!=~/^Internal error$/"
Positive assertions fail when the response attribute is missing or when no matching value satisfies the expectation. Negative assertions fail only when a matching response attribute value is present.
Subcommands
| Subcommand | Description |
|---|---|
format-packet | Parse and display a RADIUS packet from hex input |
performance | Generate concurrent RADIUS traffic and report statistics |
tacacs | Run configured TACACS+ sessions |
Format Packet
The format-packet subcommand parses a hex-encoded RADIUS packet and prints
its contents in a readable table format. This is useful for inspecting captured
packets from Wireshark or other tools.
# From a hex string argument
radiator-client format-packet "0104002ae15a5a35..."
It also accepts Wireshark-style hex dump input with offset prefixes:
radiator-client format-packet "
0000 28 00 00 3e e1 58 5a 35 ca a5 7b b3 cd eb 08 4e
0010 46 0b 10 b2 2c 2a 36 30 32 36 45 46 35 43 44 43
"
See radiator-client format-packet --help for details.
RADIUS Performance Testing
Use radiator-client performance to generate concurrent RADIUS traffic. The
command accepts the options previously provided by radperftest.
Create a request template named performance.config.toml:
username = "user_"
username_suffix = 1000
password = "password"
Send 10,000 requests over 20 concurrent sockets:
radiator-client performance \
--config performance.config.toml \
--requests 10000 \
--concurrency 20 \
--window 50 \
--secret mysecret \
--server 127.0.0.1:1812
Run a sustained performance test
Use --max-duration without --requests to run for a fixed time. Add
--target-tps to generate a specific global rate. The target rate is shared
across all worker sockets.
This example generates 1,000 transactions per second (TPS) for 10 minutes. It uses 20 sockets with a window of 256 outstanding requests per socket:
radiator-client performance \
--config performance.config.toml \
--max-duration 10m \
--target-tps 1000 \
--measurement-padding-time 5s \
--concurrency 20 \
--window 256 \
--timeout 5s \
--secret mysecret \
--server 127.0.0.1:1812 \
--transport udp
--measurement-padding-time excludes the initial warm-up period from the
reported measurement. It also provides time after the measurement to collect
outstanding responses.
Set the combined request window high enough for the target rate and expected
response time. The combined capacity is approximately concurrency * window
when --sockets is not set. For example, 20 sockets with a window of 256 allow
5,120 outstanding requests. This capacity can hold five seconds of requests at
1,000 TPS without rejecting scheduled requests because the window is full.
Start with a rate below the expected server capacity. Increase --target-tps
between runs until latency, timeouts, invalid replies, or window-full send
failures increase. Treat the requested target and the reported achieved TPS as
separate values. A lower achieved rate can indicate client-side window limits,
network limits, or server saturation.
Omit --target-tps to generate requests as quickly as the configured sockets
and windows allow. Use this mode to find maximum throughput. Use a target rate
for soak tests and for tests that must reproduce production traffic.
Choose the RADIUS protocol
--transport selects the connection transport. --radius-version selects the
RADIUS packet profile. Use the same combination as the server listener under
test.
| Use case | Options |
|---|---|
| Standard RADIUS over UDP | --transport udp --radius-version 1.0 |
| RADIUS over a plain TCP connection | --transport tcp --radius-version 1.0 |
| RADIUS/1.0 over TLS (RadSec) | --transport tls --radius-version 1.0 |
| RADIUS/1.1 over TLS | --transport tls --radius-version 1.1 |
UDP and RADIUS/1.0 are the defaults. Choose TCP only when the server has a
plain RADIUS/TCP listener. Choose TLS when testing RadSec. For TLS, set
--tls-server-name to the name in the server certificate. Set
--tls-ca-certificate when the certificate is not trusted by the system root
store.
RADIUS/1.1 requires TLS 1.3 and mutual TLS. Provide the client certificate and private key together:
radiator-client performance \
--config performance.config.toml \
--max-duration 10m \
--target-tps 1000 \
--concurrency 20 \
--window 256 \
--server radius.example.com:2083 \
--transport tls \
--radius-version 1.1 \
--tls-server-name radius.example.com \
--tls-ca-certificate ca.pem \
--tls-certificate client.pem \
--tls-key client-key.pem
Keep --requests-per-connection unset for a sustained test of established TCP
or TLS connections. Set it only when the test must include periodic connection
setup and TLS handshakes.
TACACS+ Testing
Use radiator-client tacacs to run TACACS+ authentication, authorization, or
accounting sessions from a TOML configuration file.
--server HOST overrides the configured server and uses the TACACS+ default
port 49. Add :PORT to use a different port.
Generate a documented example configuration:
radiator-client tacacs --print-example-config > tacacs-client.toml
Edit the connection and session values, then run the client:
radiator-client tacacs --config tacacs-client.toml
The configuration defines one complete authentication, authorization, or accounting session. In load-test output, one request means one complete configured session, not one TACACS+ packet.
Run a fixed number of TACACS+ sessions
Use --requests to set the total number of sessions. Use --concurrency to
set the number of load workers:
radiator-client tacacs \
--config tacacs-client.toml \
--requests 10000 \
--concurrency 20
Each worker opens a connection and reuses it when the server negotiates TACACS+ Single Connection Mode. Without Single Connection Mode, the worker opens a new connection for each session.
Run a sustained TACACS+ performance test
Use --max-duration without --requests to run sessions as quickly as
possible for a fixed time:
radiator-client tacacs \
--config tacacs-client.toml \
--max-duration 10m \
--concurrency 20
You can set both --requests and --max-duration. The test stops when it
reaches either limit.
The result includes the duration, average successful sessions per second, attempted sessions, successful sessions, and failed sessions. The command returns an error when any session fails.
TACACS+ load testing does not provide target-rate pacing. A duration-based test runs at the maximum rate allowed by the configured concurrency, server, and network. Use it for saturation and sustained maximum-throughput tests.
Test TACACS+ session multiplexing
--window sets the maximum number of in-flight sessions on each negotiated
single connection. Values greater than one require Single Connection Mode.
Enable it in the session configuration:
[connection]
single_connect = true
Then set the worker count and per-connection window:
radiator-client tacacs \
--config tacacs-client.toml \
--max-duration 10m \
--concurrency 20 \
--window 16
This configuration allows up to 20 worker connections and 16 in-flight sessions per negotiated connection. The client waits for the first reply to confirm Single Connection Mode before it sends concurrent sessions on that connection.
Keep --window 1 when the server does not support Single Connection Mode or
when the configured exchange expects the server to close the connection.
Choose the TACACS+ transport
TACACS+ performance tests support TCP and TLS. The session configuration
selects the transport. You can override it with --transport tcp or
--transport tls.
For TLS, use --tls-server-name for certificate name validation. Set
--tls-ca-certificate when the certificate is not trusted by the system root
store. Set --tls-certificate and --tls-key together when the server requires
a client certificate.
Exit Codes
| Code | Meaning |
|---|---|
| 0 | Success (expected response received) |
| 1 | Error (timeout, unexpected response, or other failure) |
Troubleshooting
Common Issues
Timeout errors
- Verify server IP and port are correct
- Check firewall rules allow UDP traffic
- Increase timeout with
--timeout 10s
Invalid response authenticator
- Verify shared secret matches server configuration
- Check for secret character encoding issues
Access-Reject received
- Verify username and password are correct
- Check server logs for authentication failure reason
- Use
--verbosefor detailed packet inspection
Verbose Mode
Enable verbose output to see a structured, annotated hex dump of each RADIUS
packet. The output uses #-prefixed comment lines for metadata, so it can be
directly re-fed to --hex or --hex-input-file for packet replay.
radiator-client \
--server 127.0.0.1:1812 \
--secret mysecret \
--user alice \
--password secret \
--verbose
The verbose output includes:
- Header section: Code, Length, and the fields defined by the selected packet profile. RADIUS/1.0 shows Identifier and Authenticator. RADIUS/1.1 shows Reserved-1, Token, and Reserved-2 instead. The section also includes a hex dump of the 20-byte header.
- Attribute sections: Type, Length, Format, Name, Value, and a per-attribute hex dump. For vendor-specific attributes (VSAs), Vendor-ID, Vendor-Name, and Vendor-Type are also shown
JSON Mode
Use --json to write request and response packets as JSON objects. Each packet
contains code, type, radiusVersion, length, and attributes.
Header fields depend on the packet profile:
- RADIUS/1.0 includes
identifierandauthenticator. - RADIUS/1.1 includes
token,reserved1, andreserved2. It omitsidentifierandauthenticatorbecause those fields do not exist in the RADIUS/1.1 header.
Combine --json with --verbose to add rawType, rawValueHex, and
rawValueBytes to each attribute. Resolved enumerated values also include
rawValue.
Dry Run
Use --dry-run to build and display the RADIUS request packet without sending
it to the server. This is useful for inspecting what would be sent. Works with
both normal and raw (--hex / --hex-input-file) modes:
radiator-client \
--server 127.0.0.1:1812 \
--secret mysecret \
--user alice \
--password secret \
--dry-run
In JSON mode (--json), --dry-run outputs the request as a JSON object.
Hex Dump
Use --hex-dump to print a full raw hex dump of the entire packet after the
table output. Unlike --verbose, which shows an annotated per-attribute hex
dump, --hex-dump displays a single contiguous hex dump of the whole packet:
radiator-client \
--server 127.0.0.1:1812 \
--secret mysecret \
--user alice \
--password secret \
--hex-dump
The --hex-dump flag is also available on the format-packet subcommand:
radiator-client format-packet --hex-dump "0104002ae15a5a35..."
Using Custom Dictionary
Load a custom RADIUS dictionary:
radiator-client \
--dictionary /path/to/custom.dictionary \
--server 127.0.0.1:1812 \
--secret mysecret \
--attr "Custom-Attribute=value"
Print the built-in dictionary:
radiator-client --print-dictionary
Related Documentation
- TOTP/HOTP Authentication - Detailed TOTP/HOTP configuration
- Duration Units - Timeout format reference
- Installation
- Command-Line Options
- Basic Usage
- Password Authentication Methods
- Adding Attributes
- Using Dictionary Names
- Using Raw AVP Format
- Using Vendor-Specific Attributes
- Reading Attributes from File
- Request Types
- Access-Request (auth)
- Accounting-Request (acct)
- Disconnect-Request
- CoA-Request
- Repeating Requests
- Transport Protocols
- UDP Transport (Default)
- TCP Transport
- TLS Transport (RadSec)
- TLS Options
- PROXY Protocol Support
- Raw Packet Handling
- Send Raw Hex Packet
- Save Packet to File
- Read Packet from File
- Response Code Validation
- Reply Message Validation
- Response Attribute Validation
- Subcommands
- Format Packet
- RADIUS Performance Testing
- Run a sustained performance test
- Choose the RADIUS protocol
- TACACS+ Testing
- Run a fixed number of TACACS+ sessions
- Run a sustained TACACS+ performance test
- Test TACACS+ session multiplexing
- Choose the TACACS+ transport
- Exit Codes
- Troubleshooting
- Common Issues
- Verbose Mode
- JSON Mode
- Dry Run
- Hex Dump
- Using Custom Dictionary
- Related Documentation
Application log message index
Architecture Overview
Backend Load Balancing
Basic Installation
Built-in Environment Variables
Byte Size Units
Certificate Revocation Lists
Comparison Operators
Configuration Editor
Configuration Import and Export
Containers
Cron and interval timers
Data Types
Duration Units
Environment Variables
Execution Context
Execution Pipelines
Filters
Getting a Radiator License
Health check /live and /ready
High Availability and Load Balancing
High availability identifiers
HTTP Basic Authentication
Introduction
Linux systemd support
Local AAA Backends
Logging
Management API privilege levels
Namespaces
Password Hashing
Password Rehashing During Login
Probabilistic Sampling
Prometheus and OpenMetrics scraping
PROXY Protocol Support
Radiator server health and boot up logic
Radiator sizing
Radiator software releases
Radiator software security and dependency compliance
RadiatorDB
RadiatorDB Backup
RadiatorDB CLI
RadiatorDB Installation
RadiatorDB PostgreSQL 10k TPS example
RadiatorDB REST API
RadiatorDB sizing
Rate Limiting
Rate Limiting Algorithms
Reverse Dynamic Authorization
Service Level Objective
TACACS+ Authentication, Authorization, and Accounting
Template Rendering CLI
Timestamps
Tools radiator-client
TOTP/HOTP Authentication
What is Radiator?
YubiKey Authentication
YubiKey Context Variables
Application log message index
Architecture Overview
Backend Load Balancing
Basic Installation
Built-in Environment Variables
Byte Size Units
Certificate Revocation Lists
Comparison Operators
Configuration Editor
Configuration Import and Export
Containers
Cron and interval timers
Data Types
Duration Units
Environment Variables
Execution Context
Execution Pipelines
Filters
Getting a Radiator License
Health check /live and /ready
High Availability and Load Balancing
High availability identifiers
HTTP Basic Authentication
Introduction
Linux systemd support
Local AAA Backends
Logging
Management API privilege levels
Namespaces
Password Hashing
Password Rehashing During Login
Probabilistic Sampling
Prometheus and OpenMetrics scraping
PROXY Protocol Support
Radiator server health and boot up logic
Radiator sizing
Radiator software releases
Radiator software security and dependency compliance
RadiatorDB
RadiatorDB Backup
RadiatorDB CLI
RadiatorDB Installation
RadiatorDB PostgreSQL 10k TPS example
RadiatorDB REST API
RadiatorDB sizing
Rate Limiting
Rate Limiting Algorithms
Reverse Dynamic Authorization
Service Level Objective
TACACS+ Authentication, Authorization, and Accounting
Template Rendering CLI
Timestamps
Tools radiator-client
TOTP/HOTP Authentication
What is Radiator?
YubiKey Authentication
YubiKey Context Variables