FreeRADIUS InkBridge

The switch Statement

Syntax
switch <expansion> {
    case <match-1> {
        [ statements-1 ]
    }
    case <match-2> {
        [ statements-2 ]
    }
    default {
        [ statements-3 ]
    }
}

A switch statement causes the server to evaluate <expansion>, which can be an Attribute-Name or a dynamic expansion.

The <expansion> cannot be static data, such as a fixed string.

The data is taken from the source, and is compared against <match-1> and <match-2>, etc. in order to find a match. If no match is found, then the server looks for the default case statement.

The case statements are matches in a type-specific manner, In most cases, they are matched via an equality comparison. For IP address types, the match is done on the network which most narrowly matches the input. See below for more information.

The switch statement is largely equivalent to the following use of if statements:

Nearly equivalent syntax
if (<expansion> == <match-1>) {
    [ statements-1 ]
}
elsif (<expansion> == <match-2>) {
    [ statements-2 ]
}
else {
    [ statements-3 ]
}

However, there are some differences from a series of if statements. For a switch statement, the <expansion> is evaluated only once. For the equivalent if statement, the <expansion> is evaluated again for every if. The other difference performance. Multiple if statements are slow, and are evaluated in turn. In contrast, case statements are evaluated only once, and are put into an optimized data structure.

If a matching case is found, the statements within that case are evaluated. If no matching case is found, the default section is evaluated. If there is no default, then the switch statement behaves as if the default section was empty.

Performance and Data Types

The switch keyword will automatically choose an efficient representation for the set of case statements, depending on the data type of the <expansion>. This data type can be forced by using a cast operation, e.g.

switch (ipaddr) %sql.query("SELECT ...") {

For string and octets data type, the case statements are place into a Red-black tree. Entries are matched exactly.

For IP address data types (ipv4addr, ipv6addr, ipv4prefix, and ipv6prefix), the case statements are placed into a Patricia / Radix tree. Multiple networks can be given, including nested networks, so long as there are no duplicates. Entries are matched on the smallest network which contains the input data.

Data types which are of the various "integer" types, or ethernet, or ifid are put into a hash table. Entries are matched exactly.

Other data types such as vsa or group are not permitted in the <expansion> field of a switch statement.

These data structures mean that the <match> lookups are generally O(lg(N)) in the number of entries. In contrast, a if / elsif chain is much slower, because it is linear in the number of entries. This efficiency means that it’s possible to create a switch statement which has a thousands to hundreds of thousands of entries, with minimal performance overhead. The only cost of having 10,000 entries in a switch statement is that the server will use more memory.

Limitations

The match text for the case statement must be static data. That is, the "thing to match" cannot be an attribute reference, or a dynamic expansion.

Duplicate case statements are forbidden and will produce an error.

No statement other than case can appear in a switch statement, and the case statement cannot appear outside of a switch statement.

For compatibility with version 3, and empty case statement can also be used instead of default. This compatibility will be removed in a future release.

Examples

Switch over a User-Name
switch User-Name {
    case "bob" {
        reject
    }

    default {
        ok
    }
}
Switch over IP prefixes
switch Framed-IP-Address {
    case 192.168/16 {
        accept
    }

    case 192.168.2.0/24 {
        reject
    }

    default {
        ok
    }
}
Switch with cast
switch (ipaddr) %sql.query("SELECT ...") {
    case 192.168/16 {
        accept
    }

    case 192.168.2.0/24 {
        reject
    }

    default {
        ok
    }
}