Radiator Server Documentation — v10.34.2

Conditions select which policies, handlers, and actions apply to a request. A condition rule compares a namespace attribute with one or more values using a comparison operator. Condition blocks combine the rules; see Condition Blocks.

For operators that assign values, see assignment operators.

Supported Operators

Equality Operators

OperatorDescription
==Equal
!=Not equal

Relational Operators

OperatorDescription
>Greater than
>=Greater than or equal
<Less than
<=Less than or equal

String Matching Operators

OperatorDescription
startsText representation starts with
!startsText representation does not start with
containsText representation contains
!containsText representation does not contain
endsText representation ends with
!endsText representation does not end with

Regular Expression Matching

Regular expressions are specified using /pattern/ literal syntax with the == or != operators:

SyntaxDescription
== /pattern/Value matches the regular expression pattern
== /pattern/iCase-insensitive regular expression match
!= /pattern/Value does not match the regular expression

Operator Details

Equal (==)

Matches values that are exactly equal. Works with all data types including strings, numbers, booleans, and IP addresses.

Examples:

if all {
    radius.request.code == radius.ACCESS_REQUEST;
    radius.request.attr.User-Name == "admin";
    radius.request.attr.NAS-Port == 1234;
} then {
    # Actions when conditions match
}

Not Equal (!=)

Matches values that are not equal. The inverse of the equal operator.

Examples:

conditions any {
    aaa.method != "eap";
    radius.request.attr.NAS-IP-Address != "10.0.0.1";
    radius.request.attr.Service-Type != 1;
}

Greater Than (>)

Matches values that are numerically or lexicographically greater than the specified value. Works with numbers and strings.

Examples:

if all {
    radius.request.attr.Session-Timeout > 3600;
    vars.priority > 5;
} then {
    # Actions when conditions match
}

Greater Than or Equal (>=)

Matches values that are greater than or equal to the specified value.

Examples:

conditions any {
    radius.request.attr.Acct-Session-Time >= 300;
    vars.user_level >= 10;
}

Less Than (<)

Matches values that are numerically or lexicographically less than the specified value.

Examples:

if all {
    radius.request.attr.Idle-Timeout < 600;
    vars.retry_count < 3;
} then {
    # Actions when conditions match
}

Less Than or Equal (<=)

Matches values that are less than or equal to the specified value.

Examples:

conditions any {
    radius.request.attr.Acct-Input-Octets <= 1000000;
    vars.connection_limit <= 100;
}

Starts With (starts)

Matches string values where the text representation starts with the specified substring. Case-sensitive.

Examples:

if all {
    radius.request.attr.User-Name starts "admin";
    radius.request.attr.Called-Station-Id starts "00:11:22";
} then {
    # Actions when conditions match
}

Contains (contains)

Matches string values where the text representation contains the specified substring. Case-sensitive.

Examples:

if all {
    radius.request.attr.User-Name contains "@example.com";
    radius.request.attr.NAS-Identifier contains "switch";
} then {
    # Actions when conditions match
}

Ends With (ends)

Matches string values where the text representation ends with the specified substring. Case-sensitive.

Examples:

if all {
    radius.request.attr.User-Name ends "@domain.com";
    radius.request.attr.Called-Station-Id ends ":SSID";
} then {
    # Actions when conditions match
}

Does Not Start With (!starts)

Matches string values where the text representation does not start with the specified substring. Case-sensitive.

Examples:

if all {
    # Reject test accounts
    radius.request.attr.User-Name !starts "test_";
    # Skip devices with specific OUI
    radius.request.attr.Called-Station-Id !starts "00:11:22";
} then {
    # Actions when conditions match
}

Does Not Contain (!contains)

Matches string values where the text representation does not contain the specified substring. Case-sensitive.

Examples:

if all {
    # Exclude guest accounts
    radius.request.attr.User-Name !contains "guest";
    # Skip certain NAS identifiers
    radius.request.attr.NAS-Identifier !contains "legacy";
} then {
    # Actions when conditions match
}

Does Not End With (!ends)

Matches string values where the text representation does not end with the specified substring. Case-sensitive.

Examples:

if all {
    # Exclude external domains
    radius.request.attr.User-Name !ends "@external.com";
    # Skip certain SSID types
    radius.request.attr.Called-Station-Id !ends ":GUEST";
} then {
    # Actions when conditions match
}

Regular Expression Match (== /pattern/)

Matches string values against a regular expression pattern using /pattern/ literal syntax. Uses Rust regex syntax.

Examples:

if all {
    # Match usernames starting with lowercase letters followed by @
    radius.request.attr.User-Name == /^[a-z]+@/;
    # Match MAC address format
    radius.request.attr.Called-Station-Id == /^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$/;
} then {
    # Actions when conditions match
}

Case-Insensitive Match (== /pattern/i)

Add the i flag after the closing slash for case-insensitive matching:

if all {
    # Case-insensitive match
    radius.request.attr.User-Name == /admin.*/i;
} then {
    # Matches "admin", "ADMIN", "Admin", etc.
}

Does Not Match (!= /pattern/)

Use != with a regex literal to reject values that match the pattern:

conditions any {
    # Reject usernames that look like test accounts
    radius.request.attr.User-Name != /^test[0-9]*@/;
}

Special Values

In addition to literal values, conditions support special keyword values:

ValueDescription
anyMatches any existing value (variable/attribute is set)
noneMatches only when the value does not exist (not set)
trueBoolean true
falseBoolean false
nowCurrent timestamp (for time-based comparisons)

Presence Checks with any and none

Use any and none to check whether a variable or attribute exists, regardless of its value:

Examples:

# Check if variable is set (has any value)
if all {
    vars.load_limit_reason == any;
} then {
    discard;
}

# Check if variable is NOT set
if all {
    vars.session_key == none;
} then {
    reject;
}

# Check if optional RADIUS attribute exists
if all {
    radius.request.attr.Framed-IP-Address == any;
} then {
    # Attribute is present
}

These are especially useful when:

  • A Lua script conditionally sets a variable
  • A backend query may or may not return a value
  • Checking for optional RADIUS attributes

Note: Comparing with empty string (vars.foo != "") only works when the variable is set. Use == any or == none for reliable presence checks.

Multiple Values

Operators can compare against multiple values using array syntax. The condition matches if the comparison is true for any of the provided values.

Example:

conditions any {
    # Match if NAS-Port-Type is any of these values
    radius.request.attr.NAS-Port-Type == [15, 19, 20];

    # Match if User-Name starts with any of these prefixes
    radius.request.attr.User-Name starts ["admin", "root", "system"];
}

Condition Blocks

A condition block combines condition rules into one result. Each rule is either a comparison or a nested condition block. Condition blocks are used in these places:

  • Policy conditions select which requests a policy handles.
  • Handler conditions select which requests a handler handles.
  • The if action and its else if clauses select which actions run.
  • The conditions action accepts or rejects the request.

Syntax

<all | any | none> {
    <namespace attribute> <comparison operator> <value>;
    <namespace attribute> <comparison operator> [<value>, <value>, <value>];
    <all | any | none> {
        <namespace attribute> <comparison operator> <value>;
        ...
    }
    ...
}

End each comparison with ;. A nested block ends with its closing brace without ;.

Matching Strategies

BlockResult is true when...
allEvery rule in the block is true
anyAt least one rule in the block is true
noneNo rule in the block is true

A block must contain at least one rule. An empty block, such as all {}, is a configuration error.

Nested Blocks

Use nested blocks to combine the matching strategies. Comparisons and nested blocks can be mixed in any order and at any depth. For example, the following conditions match mikem in the admin group and alice in the root group:

if any {
    all {
        aaa.identity == "mikem";
        user.group == "admin";
    }
    all {
        aaa.identity == "alice";
        user.group == "root";
    }
} then {
    accept;
}

A nested none block negates the rules it contains. The following conditions match requests in the corp realm from users in the admin or root group, unless the user is guest:

if all {
    aaa.identity.realm == "corp";
    any {
        user.group == "admin";
        user.group == "root";
    }
    none {
        aaa.identity == "guest";
    }
} then {
    accept;
}

Evaluation Order

Radiator evaluates the rules in configuration order and stops as soon as the result is known:

  • all stops at the first rule that is false.
  • any stops at the first rule that is true.
  • none stops at the first rule that is true.

Radiator does not evaluate the remaining rules, including nested blocks, after it stops.

Excluding Values of Multi-Valued Attributes with none

When an attribute has more than one value, such as user.group for a user who belongs to several groups, a comparison is true when it is true for any of the values. This also applies to negated operators. For a user in the groups admin and staff, user.group != "admin" is true because staff is not admin.

To match only when no value matches, put the positive comparison in a none block. The following example limits the session time of wireless users unless they belong to the admin or network-ops group. Users with no groups also get the limit:

if all {
    radius.request.attr.NAS-Port-Type == 19;  # Wireless-802.11
    none {
        user.group == ["admin", "network-ops"];
    }
} then {
    radius.reply.attr.Session-Timeout = 3600;
}

Handler Selection

policy "example" {
    conditions all {
        aaa.protocol == protocol.RADIUS;
    }

    handler "wireless" {
        conditions all {
            radius.request.code == radius.ACCESS_REQUEST;
            radius.request.attr.NAS-Port-Type == 19;  # Wireless-802.11
            radius.request.attr.User-Name !starts "guest-";
        }

        @execute {
            backend "ldap";
            pap;
        }
    }
}

Multiple Possible Realms (any)

conditions any {
    aaa.identity.realm == "engineering.example.com";
    aaa.identity.realm == "research.example.com";
}

Inline Authorization Decision

@execute {
    if all {
        user.group == "admin";
        radius.request.attr.NAS-IP-Address == 192.168.10.10;
    } then {
        radius.reply.attr.Session-Timeout = 28800;
        accept;
    }

    if any {
        user.group == "guest";
        user.role == "temporary";
    } then {
        radius.reply.attr.Session-Timeout = 3600;
    }

    accept;
}

Early Reject with any

@execute {
    if any {
        user.group == "disabled";
        radius.request.attr.User-Name ends "@expired.example.com";
    } then {
        reject;
    }
}