The finally section
finally [<request-type>] {
[ statements ]
}
The finally { … }
section allows you to configure any logging or
cleanup which is necessary before the request returns from the virtual
server. For example, if you wish to log all requests and responses
that enter a virtual server, you could place a single call to
linelog
in the finally
section.
The finally { … }
section is executed as the last thing before a
request returns from a virtual server. The finally
section is
always executed, whether the request was accepted, rejected or it
timed out.
The <request-type>
qualifier can be any valid request type for
current namespace
of the virtual server. If no there is no finally
<request-type> { … }
section, then the unqualified finally {
… }
section is run.
# Is processed first
recv Access-Request {
...
}
# Is processed second
send Access-Accept {
...
}
# Is processed last
finally {
linelog.log("Sending %{reply.Packet-Type} to %{NAS-IP-Address}")
}
# Is processed first
recv Access-Request {
...
}
# Is processed second
send Access-Accept {
...
}
# Is processed last, for all replies to Access-Request packets,
# including "Do-Not-Respond"
finally Access-Request {
linelog.log("Sending %{reply.Packet-Type} in response to Access-Request to %{NAS-IP-Address}")
}
# Is process last for all other packet types, including
# Accounting-Request.
finally {
...
}
Return Codes
Each recv …
and send …
section finishes with a
return code. These return codes are
available at the start of the finally
section.
Similarly, the finally
section also ends with a
return code, which is in some cases visible
to a parent virtual server.
At the start of "finally"
Return codes can be checked within the finally
section by using
return code expressions.
These checks allow the finally
section to perform different actions
based on the result of the protocol state machine.
# Is processed first
recv Access-Request {
...
}
# Is processed second
send Access-Accept {
ok
}
# returns `ok`
# Is processed last
finally {
if (ok || updated) {
linelog.log("Request succeeded")
} else {
linelog.log("Request failed")
}
}
At the end of "finally".
Once a finally
section is finished, it will result in a
return code. In most cases, this return
code is discarded, and has no effect on any subsequent packet
processing.
That is, the return code of a finally
section will not
modify the response packet type from the virtual server. If
finally
returns reject
, then nothing will happen, and the server
will not send an Access-Reject
response to an Access-Accept
request. This behavior is because finally
section is run after
the protocol state machine has completed.
However, the return code of a finally
is available to a parent
virtual server, when one virtual server uses
call to run another virtual server.
Timeouts
The finally
section is executed even if the request times out.
Requests terminated by a timeout will return the timeout
return code. This condition can be checked
with if (timeout)
at the start of the finally
section.
Request which are being processed in a |
We recommend doing as little as possible in a finally
section.
Logging a short message to disk will be safe. Logging complex
messages to an SQL database or to a REST API will likely be
problematic. This issue is not because FreeRADIUS has any problem
running the sql
module in a finally
section, it doesn’t. The
finally
section is just another processing section, and runs exactly
the same as a recv
or send
section.
The problem with running the sql
module in the finally
section is
that there can be cascading chains of failure when something goes
wrong. For example, if the server is configured to use log
Accounting-Request
packets to SQL for both normal accounting
processing, and in the finally
section, what happens when the SQL
databsase is slow, or has an issue?
When the SQL database is slow, the Accounting-Request
packet will
fail to log anything to SQL and time out. When all processing for the
Accounting-Request
is done, the finally
section will run, which
will also try to log information to the same slow SQL database. The
result will be doubled slow writes to SQL, or requests that never
complete.
In order to ensure that the server remains responsive, the finally
section should contain as little as possible. Any potentially long
running module calls or dynamic expansions in a finally
section
should be wrapped in a timeout block:
...
finally {
timeout 0.1s {
...
}
}
This configuration will ensure that the finally
section is limited
in how much time it spends processing a packet.
Subrequests
Where subrequest calls are used, the
finally
section in the parent will not be run until the
subrequest has finished. However, a timeout in the parent will cause
the child subrequest to be forcibly
stopped, but the childs finally
section will still run.
Timeouts in finally
sections of subrequests should therfore
be set extremely short, in order to ensure that the parent request
isn’t cancelled due to an excessively long running subrequest.