reverse
The reverse block is an alternative to connect within a RADIUS backend server configuration. Instead of establishing an outbound connection to a remote RADIUS server, reverse selects an existing inbound connection from a client that has connected to a local RADIUS server running in the same Radiator process.
This enables Reverse Dynamic Authorization (Reverse CoA) scenarios where Radiator needs to send RADIUS requests such as Disconnect-Request or CoA-Request back to NAS devices over their existing connections. The NAS devices connect to a local RADIUS server (typically over RadSec/TLS), and the reverse block picks one of those connections to deliver the request.
For more information, see the Reverse Dynamic Authorization article.
Syntax
The reverse block must contain exactly one @select block which determines which connected client receives the request. The @select block is an execution pipeline.
reverse {
@select {
if all {
connection.status.nas_identifier == "NAS123";
} then {
accept;
} else {
reject;
}
}
}
The @select pipeline is evaluated for each connected client where the connection is represented in the connection.* variable namespace. If the pipeline returns accept, that connection is selected to receive the request. If it returns reject or ignore, the connection is not selected and the next connection is evaluated. If no connections are accepted, the request fails with error code errors.NO_REVERSE_CONNECTIONS. This error can be captured on the caller side. See the example below. Any other errors raised during the evaluation of the @select pipeline are also propagated to the caller.
The @select block can also reference any other variables available in the execution context, such as request attributes or user information, to make selection decisions based on the request being processed. See the Execution Context documentation for the full list of available variables.
@select {
if all {
# Select the connection where the NAS-Identifier matches the one in the RADIUS request attributes
connection.status.nas_identifier == radius.request.attr.NAS-Identifier;
} then {
accept;
} else {
reject;
}
}
Selection mode
An optional selection mode can be specified after @select:
reverse {
# Returns the first matching connection (default)
@select first {
# ...
}
}
reverse {
# Distributes requests across all matching connections
@select round-robin {
# ...
}
}
Modes
| Mode | Description |
|---|---|
first | Returns the first connected client for which the @select pipeline returns accept. This is the default when no mode is specified. |
round-robin | Collects all connected clients for which the @select pipeline returns accept, then picks one using a round-robin counter. |
Connection variables
Inside the @select pipeline, each candidate connection is exposed through the connection.* variable namespace. Use these variables to match the desired connection.
| Variable | Type | Description |
|---|---|---|
connection.id | integer | Unique connection identifier within the process |
connection.client_name | string | Name of the connected client |
connection.peer_addr | string | Remote address of the connected client (ip:port) |
connection.peer_ip | ip | Remote IP address of the connected client |
connection.server_name | string | Name of the local server the client connected to |
connection.server_addr | string | Local address the server is listening on (ip:port) |
connection.server_ip | ip | Local IP address the server is listening on |
connection.transport_protocol | string | Transport protocol: tcp or tls |
connection.connected | timestamp | Time when the connection was established |
connection.disconnected | timestamp | Time when the connection was closed (empty if connected) |
connection.status.received | timestamp | Time of the last server status message |
connection.status.nas_identifier | string | NAS-Identifier value from the connected client. Available only when the client sends Status-Server packets containing a NAS-Identifier attribute. |
When the connection uses TLS with client certificates, the following certificate variables are also available:
| Variable | Type | Description |
|---|---|---|
connection.cert.subject.cn | string | Common Name from the client certificate |
connection.cert.subject_alt.email | string | Subject Alternative Name email addresses |
connection.cert.subject_alt.email[N] | string | Specific email address by index |
connection.cert.subject_alt.dns | string | Subject Alternative Name DNS names |
connection.cert.subject_alt.dns[N] | string | Specific DNS name by index |
connection.cert.subject_alt.uri | string | Subject Alternative Name URIs |
connection.cert.subject_alt.uri[N] | string | Specific URI by index |
Constraints
- The
reverseblock and theconnectblock are mutually exclusive. A server definition must use one or the other. - The
@selectpipeline block insidereverseis required. - Only active (not disconnected) connections are considered as candidates.
- If no connected client matches the
@selectpipeline, the backend request fails with an error.
Example: basic reverse CoA
Accept the first available reverse connection:
backends {
radius "REVERSE_DYNAUTH" {
server "REVERSE" {
secret "mysecret";
reverse {
@select {
accept;
}
}
}
query "DISCONNECT_USER" {
bindings {
radius.request.code = radius.DISCONNECT_REQUEST;
radius.request.attr.User-Name = "mikem";
}
}
}
}
Example: Error handling
The backend error can be captured using the try action on the caller side:
try backend {
name "REVERSE_DYNAUTH";
request "DISCONNECT_USER";
}
if all {
aaa.caught_error.code == errors.NO_REVERSE_CONNECTIONS;
} then {
reject "operation failed: no available connections for reverse request";
} else if all {
aaa.caught_error != none;
} then {
reject "operation failed: %{aaa.caught_error}";
}
Example: Debugging connection variables
Use the debug action to inspect the available connection variables and their values for each connected client:
@select {
debug """
=== Evaluating connection %{connection.id} ===
Server name: %{connection.server_name}
NAS-Identifier: %{connection.status.nas_identifier}
Peer IP: %{connection.peer_ip}
Certificate CN: %{connection.cert.subject.cn}
Certificate SAN email: %{connection.cert.subject_alt.email[0]}
""";
# Reject all connections to see the debug output for all candidates
reject;
}