./raddb/sites-enabled/default[230]: Parse error in condition
ERROR: if (User-Name ! "bob") {
ERROR: ^ Invalid operator
Unlang
The unlang policy language is compatible with v2 but has a number of
new features. See man unlang
for complete documentation.
Errors
Many more errors are caught when the server is starting up. Syntax
errors in unlang
are caught, and a helpful error message is
printed. The error message points to the exact place where the error
occurred:
update
sections are more generic. Instead of doing update
reply, the following can be done:
update {
reply:Class := 0x0000
control:Cleartext-Password := "hello"
}
This change means that update
sections are needed.
Comparisons
Attribute comparisons can be done via the &
operator. When two attributes required comparison, the old style was as follows:
if (User-Name = "%{control:Tmp-String-0}") {
This syntax is inefficient, as the Tmp-String-0
attribute would be
printed to an intermediate string, causing unnecessary work. The two attributes can now be compared directly:
if (&User-Name = &control:Tmp-String-0) {
See man unlang
for more details.
Casts
Casts are now permitted. This allows forced type-specific comparisons:
if ("%{sql: SELECT...}" = 127.0.0.1) {
This forces the string returned by the SELECT to be treated as an IP
address and to compare to 127.0.0.1
. Previously, the comparison
would have been done as a simple string comparison.
IP Address Networks
IP networks are now supported:
if (127.0.0.1/32 = 127.0.0.1) {
Will be true
. The various comparison operators can be used to
check IP network membership:
if (127/8 > 127.0.0.1) {
Returns true
, because 127.0.0.1
is within the 127/8
network. However, the following comparison will return false
:
if (127/8 > 192.168.0.1) {
because 192.168.0.1
is outside of the 127/8
network.
Optimization
As unlang
is now pre-compiled, many compile-time optimizations are
done. This means that the debug output may not be exactly the same as
what is in the configuration files:
if (0 && (User-Name = "bob')) {
The result will always be false
, as the if 0
prevents the
following && …
from being evaluated.
Not only that, but the entire contents of that section will be ignored entirely:
if (0) {
this_module_does_not_exist
and_this_one_does_not_exist_either
}
In v2, the above configuration would result in a parse error, as there is
no module called this_module_does_not_exist
. In v3, that text is
ignored. This ability allows for dynamic configurations where
certain parts are used (or not) depending on compile-time configuration.
Similarly, conditions that always evaluate to true
will be
optimized away:
if (1) {
files
}
That configuration will never show the if (1)
output in debugging mode.