Built-In Expansions
In addition to storing attribute references, the server has a number of built-in expansions. These expansions act largely as functions which operate on inputs, and produce an output.
Attribute Manipulation
%length( … )
The length
expansion returns the size of the input as an integer.
When the input is a string, then the output is identical to the
strlen
expansion.
When the input is an attribute reference, the output is the size of the attributes data as encoded "on the wire".
&Tmp-String-0 := "Caipirinha"
&Framed-IP-Address := 192.0.2.1
&reply += {
&Reply-Message = "The length of %{control.Tmp-String-0} is %length(&control.Tmp-String-0)"
&Reply-Message = "The length of %{control.Framed-IP-Address} is %length(&control.Framed-IP-Address)"
}
The length of Caipirinha is 10 The length of 192.168.0.2 is 4
Server Configuration
%config(<key>)
Refers to a variable in the configuration file. See the documentation on configuration file references.
"Server installed in %config('prefix')"
"Module rlm_exec.shell_escape = %config('modules.exec.shell_escape')"
Server installed in /opt/freeradius
Module rlm_exec.shell_escape = yes
Miscellaneous Expansions
%{0}..%{32}
%{0}
expands to the portion of the subject that matched the last regular
expression evaluated. %{1}
..%{32}
expand to the contents of any capture
groups in the pattern.
Every time a regular expression is evaluated, whether it matches or not, the numbered capture group values will be cleared.
%regex(<named capture group>}
Return named subcapture value from the last regular expression evaluated.
Results of named capture groups are available using the %regex(<named capture
group>}
expansion. They will also be accessible using the numbered expansions
described above.
Every time a regular expression is evaluated, whether it matches or not, the named capture group values will be cleared.
This expansion is only available if the server is built with libpcre or libpcre2.
Use the output of ... Debug : regex-pcre : no Debug : regex-pcre2 : yes Debug : regex-posix : no Debug : regex-posix-extended : no Debug : regex-binsafe : yes ... Debug : pcre2 : 10.33 (2019-04-16) - retrieved at build time |
%eval(<string>)
Evaluates the string as an expansion, and returns the result. The main difference between using this expansion and just using %{…}
is that the string being evaluated can be dynamically changed.
if (&User-Name == "bob") {
&request.Tmp-String-0 := "&User-Name"
} else {
&request.Tmp-String-0 := "not bob!"
}
&reply.Reply-Message := "%eval(&request.Tmp-String-0}"
&User-Name == bob
bob
&User-Name == not bob
not bob!
%nexttime(<time>)
Calculate number of seconds until next n hour(s
), day(s
), week(s
), year(s
).
With the current time at 16:18, %nexttime(1h)
will expand to 2520
.
&reply.Reply-Message := "You should wait for %nexttime(1h)s"
You should wait for 2520s
%sub(<subject>, /<regex>/[flags], <replace>)
Substitute text just as easily as it can match it, even using regex patterns.
&control.Tmp-String-0 := "Caipirinha is a light and refreshing drink!"
&reply.Reply-Message := "%sub(%{control.Tmp-String-0}, / /, ',')"
Caipirinha,is,a,light,and,refreshing,drink!
%time()
Return the current time.
If no argument is passed, it returns the current time. Otherwise if the argument is:
-
dst
- returns abool
indicating whether or not the system is running in daylight savings time. -
mday_offset
- returns thetime_delta
offset since the start of the month. Use%d
to get an integer representing the day of the month. -
now
- returns the current time -
offset
- returns atime_delta
of the current time zone offset. This value may be negative. -
request
- returns the time at which the request packet was received (always less thannow
!) -
wday_offset
- returns thetime_delta
offset since the start of the week. -
any other string is parsed as type
date
, using the normal date parsing routines.
&Acct-Start-Time := %time(now)
The current time can also be compared to a known date:
if (%time() < (date) 'Aug 1 2023 01:02:03 UTC') {
...
}
The format of the date strings should be the same format as the server prints out. The parse will try to accept other date formats (raw integer, etc.), but not all formats are guaranteed to work. There are hundreds of different date formats used across the world, and the server cannot support them all.
This expansion should be used in preference to the single letter expansions |
Due to limitations in the underlying time functions (both system and FreeRADIUS), previous versions of FreeRADIUS did not always handle dates correctly. When print dates, the time zone information would sometimes not be printed, or the time zone would sometimes be ignored when parsed a date from a string.
Even if the time zone was used, the nature of time zones means that
there may be duplicate time zone names! For example, the time zone
CST
has three separate (and different) definitions.
The server now tracks all times internally as UTC, and by default prints times as UTC, or prints the time zone as a decimal offset from UTC, instead of printing an ambiguous name.
This handling of time zones has some minor side effects. When calculating values like "tomorrow", the default is to return the UTC version of "tomorrow". This value may not be what you want.
In order to correctly calculate the local value of "tomorrow", it is necessary to add the local time zone offset to the UTC time.
Note that the server will automatically determine (and use) any
daylight savings time differences. So the value of %time(offset)
may change while the server is running!
The following example calculates the correct value of "tomorrow" in UTC by using the following steps:
-
taking the current time of the request
-
calculating how long it has been since the start of the day as a
time_delta
-
subtracting that
time_delta
from the current time
group {
date now
date tomorrow
time_delta time_of_day
&now := %time('request')
# We are this many seconds into one day
&time_of_day := &now % (time_delta) 1d
# calculate the start of today, and then add one day to that
&tomorrow := &now - &time_of_day + (time_delta) 1d
}
The following example calculates the correct value of "tomorrow" in local time by using the preceding example, but then adding the local time zone offset to the final value.
group {
date now
date tomorrow
time_delta time_of_day
&now := %time('request')
# We are this many seconds into one day
&time_of_day := &now % (time_delta) 1d
# calculate the start of today, and then add one day to that
&tomorrow := &now - &time_of_day + (time_delta) 1d
# add in the time zone offset
&tomorrow += %time('offset')
}
This kind of math works well for "tomorrow", but it is less useful for
"next week Monday", or "start of next month". The %nexttime(…)
expansion above should be used for those time operations.