]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
doc:add exec module explaination to the dynamic translation tutorial
author98manu <manudisathmini@gmail.com>
Mon, 19 Jan 2026 06:24:24 +0000 (11:54 +0530)
committerAlan T. DeKok <aland@freeradius.org>
Wed, 28 Jan 2026 15:25:18 +0000 (10:25 -0500)
doc:Add explaination about `%exec()` function in dynamic translation

doc: fix the entry format in dynamic translation tutorial

doc:add mathematical operations sections in dynamic_translation tutorial

doc/antora/modules/tutorials/pages/dynamic-translation.adoc

index 995856fe8feb0dd3c7e411ccd388baefd51100f3..05b03ff0ad7493d6fcf447b0b709a57941cda552 100644 (file)
@@ -17,105 +17,274 @@ exercise, we will work through a number of different examples of configuring
 inter-module calls.
 
 To start, open `mods-available/exec` and read the sample configuration for
-the `exec` module. Then, edit the users file to add the following entry at the
+the `exec` module.
+
+The `exec` module is used for executing external programs or scripts
+from within FreeRADIUS.  It is intended only for very rare use cases,
+such as testing or running programs which do more than the built-in
+FreeRADIUS functionality.
+
+The `%exec()` function provides a dynamic expansion function.  The
+output of `exec` is parsed, and the result can be assigned to the
+attribute.
+
 top:
 
--------------------------------------------------------------------------------
+[source,text]
+----
 bob Password.Cleartext := "hello"
-    Callback-Id = "%exec('/bin/echo', "Hello, there")
--------------------------------------------------------------------------------
+    Callback-Id = %exec('/bin/echo', "Hello, there")
+----
 
 The `echo` program may be in `/usr/bin/echo`, depending on your local system. On
 many systems you can use the following command:
 
 [source, bash]
-------------
+----
 $ which echo
-------------
+----
 
 This will tell you the full pathname of the `echo` command. Use that pathname in
 the file entry.
 
-Start the server and send it a test packet for user `bob`. The debug output of
-the server should print messages similar to the following.
-
--------------------------------------------------------------------------------
-(0)  files : users: Matched entry bob at line 1
-Executing: /bin/echo Hello, there:
-Program returned code (0) and output 'Hello, there'
-(0)  files : EXPAND %exec('/bin/echo', "Hello, there")
-(0)  files :    --> Hello, there
-(0)   [files] = ok
--------------------------------------------------------------------------------
-
-These message indicate that the first entry in the file (at line 1) was used to
-match the incoming request.
-
-The `exec` xlat function was then used to perform the dynamic translation of the
-string, which resulted in a call to the `rlm_exec` module.
-
-That module called the `Exec-Program` function of the server to execute a
-program, and finally, the `exec` xlat function returned the string "Hello
-there".
-
-That text was then sent back to the RADIUS client in the `Callback-Id`
-attribute, which was not quoted above.
-
-// Copyright (C) 2021 Network RADIUS SAS.  Licenced under CC-by-NC 4.0.
-// This documentation was developed by Network RADIUS SAS.
-Another dynamic translation string function is the `expr` module. It performs
-some simple mathematical operations. The following sample file entry
-demonstrates how to use the `expr` module.
-
--------------------------------------------------------------------------------
+Start the server
+
+[source,bash]
+----
+$ radiusd -X
+----
+
+Send the test packet for user `bob`
+
+[source,bash]
+----
+echo 'User-Name = "bob"
+CHAP-Password = "hello"
+NAS-IP-Address = 127.0.0.1
+NAS-Port = 501
+NAS-Port-Type = Virtual' | ./scripts/bin/radclient -x 127.0.0.1 auth testing123
+----
+
+The debug output of the server should print messages similar to the following.
+
+[source,text]
+----
+files - | ||
+(0)        files - | %logical_or()
+(0)          files - | Stripped-User-Name
+(0)            files - | %{Stripped-User-Name}
+(0)            files - (null)
+(0)          files - | User-Name
+(0)          files - | %logical_or(...)
+(0)            files - | %{User-Name}
+(0)            files - | --> bob
+(0)          files - | %logical_or(...)
+(0)          files - | --> bob
+(0)      files - files - Looking for key "bob"
+(0)      files - files - Found match "bob" on line 4 of ./scripts/bin/../../raddb/mods-config/files/authorize
+(0)      files - files - Preparing attribute updates:
+(0)        files - Password.Cleartext := hello
+(0)        files - Callback-Id = %exec('/bin/echo', "Hello, there")
+(0)        files - | exec
+(0)        files - | %exec({/bin/echo}{Hello, there})
+proto_radius_udp - Received Access-Request ID 156 length 98 radius_udp server * port 1812
+Ignoring retransmit from client localhost - we are still processing the request
+(0)          files - pid 263593 (stdout) - Hello, there\n
+(0)          files - Program exited with status code 0
+(0)          files - xlat - Resuming execution
+(0)          files - | %exec(...)
+(0)          files - | --> Hello, there
+(0)      files (ok)
+----
+
+== Mathematical Operations
+
+It is possible to do mathematical operations "in place"
+Another dynamic translation string function is the `expr` module. It
+performs some simple mathematical operations. The following sample
+file entry demonstrates how to use mathematical expressions:
+
+* Just use `%{... math ... }` for mathematical operations.  There's no
+  need to use an `expr` module as was done in v3.
+* Don't use double quotes (`"..."`) around everything.  It's not necessary.
+
+[source,text]
+----
 bob    Password.Cleartext := "hello"
-       Session-Timeout = "%{60 * 60}"
--------------------------------------------------------------------------------
-
-Dynamically translated strings may also be used as "check items" to match
-requests coming in to the server. The following examples show how those strings
-(or run-time variables) may be used to both match a request and to configure
-dynamic responses.
-
-You should use the `bob-login-one.sh` script to send a request to match the
-first entry and should send another request with a different NAS-Port.
-
--------------------------------------------------------------------------------
-bob    Password.Cleartext := "hello", NAS-Port == "%exec('/usr/bin/id', '-u')"
+       Session-Timeout = %{60 * 60}
+----
+
+**What this does:**
+
+User `bob` logs in with password `hello` and the server calculates: 60
+multiplied by 60 with a result of 3600 seconds (which equals 1
+hour).This timeout value is sent back to the client and the client
+will disconnect the user after 1 hour
+
+This entry will set the Session-Timeout attribute to 3600 seconds
+(which equals 1 hour). The mathematical expression inside the `%{...}`
+braces is evaluated to produce the final value.
+
+Start the server and send it a test packet for user:
+
+[source,bash]
+----
+echo 'User-Name = "bob"
+CHAP-Password = "hello"
+NAS-IP-Address = 127.0.0.1
+NAS-Port = 501
+NAS-Port-Type = Virtual' | ./scripts/bin/radclient -x 127.0.0.1 auth testing123
+----
+
+The debug output should show the mathematical expression being evaluated and the final result.
+
+[source,text]
+----
+files - files - Looking for key "bob"
+(0)      files - files - Found match "bob" on line 1 of raddb/mods-config/files/authorize
+(0)      files - files - Preparing attribute updates:
+(0)        files - Password.Cleartext := hello
+(0)        files - Session-Timeout = %{60 * 60}
+(0)          files - | *
+(0)          files - | ({60} * {60})
+(0)          files - | --> 3600
+(0)      files (ok)
+......
+(0)  Sending Access-Accept ID 53 from 0.0.0.0/0:1812 to 127.0.0.1:39326 length 49 via socket radius_udp server * port 1812
+(0)    Session-Timeout = 3600
+(0)    Packet-Type = ::Access-Accept
+(0)    User-Name = "bob"
+(0)  Finished request
+----
+
+== Dynamic Check Items with Conditional Matching
+
+Dynamically translated strings may also be used as "check items" to
+match requests coming in to the server. The following examples show
+how those strings (or run-time variables) may be used to both match a
+request and to configure dynamic responses.
+
+You should send test packets to match the first entry and should send
+another request with a different NAS-Port.
+
+[source,text]
+----
+bob    Password.Cleartext := "hello", NAS-Port == 501
        Reply-Message = "Your port is very nice.",
-       Session-Timeout = "%{60 * 60}"
+       Session-Timeout = %{60 * 60}
 
-bob    Password.Cleartext := "hello", NAS-Port != "%exec('/usr/bin/id', '-u')"
+bob    Password.Cleartext := "hello", NAS-Port != 501
         Reply-Message = "Your port is less nice.",
-        Session-Timeout = "%{60 * 2}"
--------------------------------------------------------------------------------
+        Session-Timeout = %{60 * 2}
+----
 
-The run-time variables may be nested, too. The following file entry
-demonstrates this nesting.
-
--------------------------------------------------------------------------------
+[source,text]
+----
 bob    Password.Cleartext := "hello"
        Session-Timeout = "%{60 * %exec(/usr/bin/id -u})"
--------------------------------------------------------------------------------
+----
 
 In this case, the user "bob" is given one minute of access time,
-multiplied by the value of the "UID" of the RADIUS server.
+multiplied by the value of the "UID" of the RADIUS server.  Again,
+this example is just for testing.  Don't use `%exec()` on a production
+server!
+
+=======
+**Example:**
+Request comes in with NAS-Port = 501 → First entry matches → `Your port is very nice.`
+Request comes in with NAS-Port = 2000 → Second entry matches → `Your port is less nice.`
+=======
+
+You can find the UID by running the following command:
+
+[source,basg]
+----
+$ /bin/id -u
+----
+
+Send a test packet to verify the server’s response as Your port is very nice using:
+
+[source,bash]
+----
+echo 'User-Name = "bob"
+CHAP-Password = "hello"
+NAS-IP-Address = 127.0.0.1
+NAS-Port = 0
+NAS-Port-Type = Virtual' | radclient -x 127.0.0.1 auth testing123
+----
+
+Debug output
+
+[source,text]
+----
+files - files - Looking for key "bob"
+(0)      files - | exec
+(0)      files - | %exec({/bin/id}{-u})
+proto_radius_udp - Received Access-Request ID 17 length 98 radius_udp server * port 1812
+Ignoring retransmit from client localhost - we are still processing the request
+(0)        files - pid 270061 (stdout) - 0\n
+(0)        files - Program exited with status code 0
+(0)        files - xlat - Resuming execution
+(0)        files - | %exec(...)
+(0)        files - | --> 0
+(0)      files - files - Found match "bob" on line 5 of raddb/mods-config/files/authorize
+(0)      files - files - Preparing attribute updates:
+(0)        files - Password.Cleartext := hello
+(0)        files - Reply-Message = Your port is very nice.
+(0)        files - Session-Timeout = %{60 * 60}
+(0)          files - | *
+(0)          files - | ({60} * {60})
+(0)          files - | --> 3600
+(0)      files (ok)
+----
+
+Verify by sending a test packet to receive the response `Your port is less nice`.
+
+[source,bash]
+----
+echo 'User-Name = "bob"
+CHAP-Password = "hello"
+NAS-IP-Address = 127.0.0.1
+NAS-Port = 501
+NAS-Port-Type = Virtual' | radclient -x 127.0.0.1 auth testing123
+----
+
+Debug output
+----
+files - pid 270077 (stdout) - 0\n
+(1)        files - Program exited with status code 0
+(1)        files - xlat - Resuming execution
+(1)        files - | %exec(...)
+(1)        files - | --> 0
+(1)      files - files - Found match "bob" on line 9 of raddb/mods-config/files/authorize
+(1)      files - files - Preparing attribute updates:
+(1)        files - Password.Cleartext := hello
+(1)        files - Reply-Message = Your port is less nice.
+(1)        files - Session-Timeout = %{60 * 2}
+(1)          files - | *
+(1)          files - | ({60} * {2})
+(1)          files - | --> 120
+(1)      files (ok)
+----
+
+These check items demonstrate how dynamic expansion can be used in
+conditional logic. The server evaluates the expression and compares it
+to the incoming request attribute to determine which entry to use for
+the user.
 
-== Further considerations
 
-Run-time variables allow inter-module calling. The administrator may perform LDAP
-queries and SQL queries to use database information in other modules.
+== Further considerations
 
-Unfortunately, the format of the string is module-dependent. This limitation
-comes from the fact that each module has its own syntax for database queries.
-The syntax for querying LDAP databases is different than the syntax for querying
-SQL database. The administrator should consult the `man` pages for the relevant
-module for more information on the syntax for run-time dynamic translation of
-strings.
+Run-time variables allow inter-module calling. The administrator may
+perform LDAP queries and SQL queries to use database information in
+other modules.
 
-Another limitation is that the query string can be only approximately 250
-characters long in the current version of the server. This limitation may be
-removed in a later version.
+Unfortunately, the format of the string is module-dependent. This
+limitation comes from the fact that each module has its own syntax for
+database queries.  The syntax for querying LDAP databases is different
+than the syntax for querying SQL database. The administrator should
+consult the `man` pages for the relevant module for more information
+on the syntax for run-time dynamic translation of strings.
 
 == Questions
 
@@ -125,3 +294,5 @@ executing a program?
 modules?
 3.  What is an example of conditional syntax for a run-time variable?
 
+// Copyright (C) 2026 Network RADIUS SAS.  Licenced under CC-by-NC 4.0.
+// This documentation was developed by Network RADIUS SAS.