0. Contents

1. Installation
2. Configuration
   2.1. The file .../raddb/configuration (defines sources and module interfaces)
   2.2. The file .../raddb/behaviour
3. Running the server
 

1. Installation

In order to successfully put OpenRADIUS to use, the best thing is to first install it with the example configuration, to test it, and then to adapt it to suit your needs.

Here is a short summary of the steps you'll need to take.

2. Configuration

Not counting the dictionary, OpenRADIUS itself uses only two configuration files:

Both files are read only once, at server startup. No HUP handling is done yet - I'd like to do implement that by having the signal create a complete new configuration set while keeping the old one around as well; then jobs could refer to the configuration under which they were created, and we'd use job-based reference counting to discard the old configuration if there are no running jobs anymore that use them, thus guaranteeing consistent behaviour at all times. But that's for later.
 
2.1. The file .../raddb/configuration

The configuration file actually uses the same language as the behaviour file, which is described here.

Both are compiled once, when the server is started; the difference is that the configuration file is also actually executed immediately after compiling, causing the network sockets to be created and the interface modules to be started. (The behaviour file, on the other hand, is executed for each incoming request.)

Defining sources to listen on and callable interfaces for use in the behaviour file, is done using two magic callable 'interfaces' that are defined internally when the configuration file is compiled and executed: 'source', and 'interface'.

Other than their special purpose (to add sources and interfaces), they only differ from the normal callable interfaces that you create here in that they completely empty the request- and reply lists after each call. (This is done purely for convenience; otherwise you'd have to remember to do at least a 'delall sendattr, delall recvattr' between each interface that you create - see below why).

2.1.1. Sources

Each call to the pseudo-interface 'source' defines one or more sockets to listen on, based on the instances of the 'addr' and 'port' attributes that you have put on the request list before calling it. In fact, each instance of 'port' adds a new socket definition; the 'addr' specifications are optional and are put in the socket definitions only after they have been created for this source.

Some examples of using 'source' that illustrates this behaviour:

# The following few examples all define two 
# sockets, one listening on 172.16.1.1:1645 
# and the other on port 1812 for all addresses:

source(addr=172.16.1.1, port=1645,
       addr=0.0.0.0,    port=1812),

# which is the same as:

source(port=1645, addr=172.16.1.1,
       port=1812, addr=0.0.0.0),

# which is the same as:

source(port=1645, addr=172.16.1.1,
       port=1812),

# which is the same as:

source(port=1645,       port=1812,
       addr=172.16.1.1, addr=0.0.0.0),

# which is of course the same as:

source(port=1645, port=1812,
       addr=172.16.1.1),

# but not the same as:

source(port=1812, port=1645,
       addr=172.16.1.1),

# although it *is* the same as:

source(port=1812,    port=1645,
       addr=0.0.0.0, addr=172.16.1.1),

# which is also the same as

source(addr=172.16.1.1, port=1645),
source(addr=0.0.0.0, port=1812),

# which is the same as

source(port=1645, addr=172.16.1.1),
source(port=1812, addr=0.0.0.0),

# which is the same as

source(port=1645, addr=172.16.1.1),
source(port=1812),

# which is the same as

source(port=1812),
source(port=1645, addr=172.16.1.1),

# Enfin, you get the idea ;)

2.1.2. Interfaces

Each call to the pseudo-interface 'interface' defines a new external interface that can be used in the behaviour file. The following attributes are meaningful to 'interface':

Here are some examples of defining interfaces (based on the contents of ...raddb/configuration.sample included with OpenRADIUS 0.9).

The first three define logging interfaces that all use the radlogger module, but each logs a different set of attributes (the 'sendattr'-lists) to a different output file, as specified on the module's command line ('prog').

The fourth interface is a dummy one for testing the module support (uses the 'cat' command as a module that echoes all requests as responses). This works both for ASCII and binary interface types.

The fifth defines an interface that uses the 'radldap' module to connect to a replicated LDAP tree that is available on two different machines that are to be queried in round-robin mode.

interface (name="Stdlogger", 
           sendattr="RAD-Code",
           sendattr="User-Name",
           sendattr="User-Password",
           sendattr="Calling-Station-Id",
           sendattr="Called-Station-Id",
           sendattr="NAS-Port",
           sendattr="NAS-Port-Type",
           sendattr="NAS-IP-Address", 
           sendattr="NAS-Identifier",
           sendattr="str",
           prog="radlogger /tmp/radaccess.log",
           flags=Ascii + Short-Attr + Named-Const + 
	   	 Double-Backslash),

interface (name="Errorlogger", 
           sendattr="str",
           prog="radlogger /tmp/raderrs.log",
           flags=Ascii + Short-Attr + 
	   	 Double-Backslash),

interface (name="Acctlogger",
           sendattr="str",
           sendattr="User-Name",
           sendattr="Calling-Station-Id",
           sendattr="Called-Station-Id",
           sendattr="NAS-Port",
           sendattr="NAS-Port-Type",
           sendattr="NAS-IP-Address", 
           sendattr="NAS-Identifier",
           sendattr="Acct-Status-Type",
           sendattr="Acct-Session-Id",
           sendattr="Record-Unique-Key",
           sendattr="Session-Key",
           sendattr="Acct-Input-Octets",
           sendattr="Acct-Output-Octets",
           sendattr="Acct-Input-Packets",
           sendattr="Acct-Output-Packets",
           sendattr="Acct-Terminate-Cause",
           sendattr="Timestamp",
           sendattr="Acct-Delay-Time",
           sendattr="Acct-Session-Time",
           sendattr="Acct-Authentic",
           prog="radlogger /tmp/radacct.log",
           recvattr="int",
           flags=Ascii + Short-Attr + Named-Const + 
	   	 Double-Backslash,
           timeout=5),

interface (name="Loopback",
           prog="/bin/cat",
           prog="/bin/cat",
           prog="/bin/cat",
           timeout=1),

interface (name="Ldapdb",
           sendattr="User-Name",
           sendattr="User-Password",
           sendattr="str",
# Lines wrapped and concatenated with '.' only for
# brevity here. Not really needed.
           prog="radldap -b cn=radius,ou=People," .
	   		   "dc=my,dc=dom,dc=tld " .
		       " -s ldappassword 192.168.5.26",
           prog="radldap -b cn=radius,ou=People," .
	   		   "dc=my,dc=dom,dc=tld " .
		       " -s ldappassword 192.168.6.33",
           timeout=25),

2.2. The file .../raddb/behaviour

Tutorial not yet written. See the language reference for now.

Some hints: the server works by putting everything that it knows about an incoming request on a so-called REQUEST list of attribute/value pairs, and prepares an empty list, the REPLY list. Together with an expression execution context, these three things are called a 'job'.

After a new job is created (because a new request came in), the server will execute the compiled behaviour expression. You can use it to perform any operation on any instance of any pair on either list.

The expression is ran until it finishes (because of the 'abort' operator which causes the request to be dropped, the end of the expression or the 'halt' operator, which causes a response to be encoded and sent based on the contents of the REPLY list), or until it makes a call to an interface that's defined in the configuration file.

When that happens, the server builds a request message for the module using the current REQUEST list and sends it to the module. When the answer comes in, all attributes allowed in according to the recvattr ACL are added to the REPLY list, and the job again continues running the expression, again until it finishes or makes another interface call.

In the mean time, the server still tends to new requests, module communications, possibly crashed childs, and so on.

One important aspect of the expression language is the short-circuit boolean evaluation. This allows you to do conditional subexpressions, like int && str="abc", which only adds a 'str' attribute with value 'abc' to the bottom of the request list if the last instance of the int attribute on the reply list has a value that can be interpreted as 'true'.

The || operator only executes the subexpression on its right if the one on its left is 'false'. The operator returns the last evaluated subexpression, so you can write things like str = (str || "hello"), to supply a default value for an attribute, or more powerful things that employ auto-conversion, like str = (NAS-Identifier || NAS-IP-Address).

These two operators together also provide if-then-else constructs, like this:

Reply-Message = "The last 'int' on the reply list was ",

int == 3 && (
	Reply-Message := REP:Reply-Message . 
			 "indeed 3! Yes sir.",
1) || (
	Reply-Message := REP:Reply-Message . 
			 "not 3, but " . int .
			 "!"
),

For the rest, and why the '=' operator added a pair to the REQUEST instead of the REPLY list in the 'str="abc"'-example, and why the '==' operator tested the 'int' attribute on the REPLY list instead of the REQUEST list, which is what you'd expect and what the language normally does, and why I used a REP: prefix on the right hand side of the ':=' operator and not on the left even though both sides refer to the same A/V pair, see the real documentation.
 

3. Running the server

Not yet written - do 'radiusd -h' and see main.c for now.