summaryrefslogtreecommitdiffstats
path: root/man/man5/unlang.5
blob: 63f557033916bbcf3d4fc0beb3128e82c74968d1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
.\"     # DS - begin display
.de DS
.RS
.nf
.sp
..
.\"     # DE - end display
.de DE
.fi
.RE
.sp
..
.TH unlang 5 "16 February 2021" "" "FreeRADIUS Processing un-language"
.SH NAME
unlang \- FreeRADIUS Processing un\-language
.SH DESCRIPTION
FreeRADIUS supports a simple processing language in its configuration
files.  We call it an "un-language" because the intention is NOT to
create yet another programming language.  If you need something more
complicated than what is described here, we suggest using the Perl or
Python modules rlm_perl, or rlm_python.

The goal of the language is to allow simple policies to be written
with minimal effort.  Those policies are then applied when a request
is being processed.  Requests are processed through virtual servers
(including the default one), in the sections titled "authorize",
"authenticate", "post-auth", "preacct", "accounting", "pre-proxy",
"post-proxy", and "session".

These policies cannot be used in any other part of the configuration
files, such as module or client configuration.
.SH KEYWORDS
The keywords for the language are a combination of pre-defined
keywords, and references to loadable module names.  We document only
the pre-defined keywords here.

Subject to a few limitations described below, any keyword can appear
in any context.  The language consists of a series of entries, each
one line.  Each entry begins with a keyword.  Entries are
organized into lists.  Processing of the language is line by line,
from the start of the list to the end.  Actions are executed
per-keyword.
.IP module-name[.section-name]
A reference to the named module.  When processing reaches this point,
the pre-compiled module is called.  The module may succeed or fail,
and will return a status to "unlang" if so.  This status can be tested
in a condition.  See the "Simple Conditions" text in the CONDITIONS
section, and MODULE RETURN CODES, below.
If a section-name is provided, it will cause the module to execute 
as if it were listed in the named section.

.DS
	chap  # call the CHAP module
.br
	sql   # call the SQL module
.br
	...
.DE
.IP if
.br
Checks for a particular condition.  If true, the block after the
condition is processed.  Otherwise, the block is ignored.  See
CONDITIONS, below, for documentation on the format of the conditions.

.DS
	if (condition) {
.br
		...
.br
	}
.DE
.IP else
.br
Define a block to be executed only if the previous "if" condition
returned false.

.DS
	else {
.br
		...
.br
	}
.DE
.IP elsif
.br
Define a block to be executed only if the previous "if" condition
returned false, and if the specified condition evaluates to true.

.DS
	elsif (condition) {
.br
		...
.br
	}
.DE
.IP foreach
.br
Loops over values of an attribute, running the block for each value.
The return value of the block is the return value of the last
statement executed.  The loop can be exited early by using the "break"
keyword.  Unlike other languages, "break" here means "exit the loop at
the next iteration", not "exit the loop now".  The result is that any
statements after the "break" keyword will still be executed.  We
recommend using "break" only when it is the last statement in a
"foreach" block.

Inside of the "foreach" block, the attribute which is being looped
over can be referenced as "Foreach-Variable-#".  Where "#" is the
depth of the loop, starting at "0".  e.g. "Foreach-Variable-0".  The
loops can be nested up to eight (8) deep, though this is not
recommended.

.DS
	foreach &Attribute-Reference {
.br
		...
.br
	}
.DE
.IP switch
.br
A "switch" statement takes one argument, and contains a series of
"case" statements.  When a "switch" statement is encountered, the
argument from the "switch" is evaluated in turn against the argument
from each "case" statement.  The first "case" statement which matches
is executed.  All other "case" statements are ignored.  A default
"case" statement can be defined, by omitting its argument.

If the argument is a double quoted string (e.g. "%{exec:1 + 2}", it is
expanded as described in the DATA TYPES section, below.  The match is
then performed on the string returned from the expansion.  If the
argument is an attribute reference (e.g. &User-Name), then the match
is performed on the value of that attribute.  Otherwise, the argument
is taken to be a literal string, and matching is done via simple
comparison.

No statement other than "case" can appear in a "switch" block.

.DS
	switch <argument> {
.br
		...
.br
	}
.DE
.IP case
.br
Provides a place-holder which matches the argument of a parent
"switch" statement.

A "case" statement cannot appear outside of a "switch" block.

If the argument is a double quoted string (e.g. "%{exec:1 + 2}", it is
expanded as described in the DATA TYPES section, below.  The match is
then performed on the string returned from the expansion.  If the
argument is an attribute reference (e.g. &User-Name), then the match
is performed on the value of that attribute.  Otherwise, the argument
is taken to be a literal string, and matching is done via simple
comparison.

.DS
	case <argument> {
.br
		...
.br
	}
.DE
A default entry can be defined by omitting the argument, as given
below.  This entry will be used if no other "case" entry matches.
Only one default entry can exist in a "switch" section.

.DS
	case {
.br
		...
.br
	}
.DE
.IP update
.br
Update a particular attribute list, based on the attributes given in
the current block.

.DS
	update <list> {
.br
		&Attribute-Reference = value
.br
		...
.br
	}
.DE
The <list> can be one of "request", "reply", "proxy-request",
"proxy-reply", "coa", "disconnect", "session-state", or "control".  As
of Version 3, the <list> can be omitted, in which case "request" is
assumed.

The "control" list is the list of attributes maintained internally by
the server that controls how the server processes the request.  Any
attribute that does not go in a packet on the network will generally
be placed in the "control" list.

For EAP methods with tunneled authentication sessions (i.e. PEAP and
EAP-TTLS), the inner tunnel session can also reference
"outer.request", "outer.reply", and "outer.control".  Those references
allow you to address the relevant list in the outer tunnel session.

The "coa" and "disconnect" sections can only be used when the server
receives an Access-Request or Accounting-Request.  Use "request" and
"reply" instead of "coa" when the server receives a CoA-Request or
Disconnect-Request packet.

Adding one or more attributes to either of the "coa" or "disconnect"
list causes server to originate a CoA-Request or Disconnect-Request
packet.  That packet is sent when the current Access-Request or
Accounting-Request has been finished, and a reply sent to the NAS.
See raddb/sites-available/originate-coa for additional information.

The "session-state" list is primarily used for EAP.  Attributes put
into the "session-state" list are saved for the next packet in the
session.  They are automatically retrieved when the next packet is
received.

The only contents permitted in an "update" section are attributes and
values.  The contents of the "update" section are described in the
ATTRIBUTE REFERENCE and ATTRIBUTE ASSIGNMENT sections below.
.IP redundant
This section contains a simple list of modules.  The first module is
called when the section is being processed.  If the first module
succeeds in its operation, then the server stops processing the
section, and returns to the parent section.

If, however, the module fails, then the next module in the list is
tried, as described above.  The processing continues until one module
succeeds, or until the list has been exhausted.

Redundant sections can contain only a list of modules, and cannot
contain keywords that perform conditional operations (if, else, etc)
or update an attribute list.

.DS
	redundant {
.br
		sql1	# try this
.br
		sql2	# try this only if sql1 fails.
.br
		...
.br
	}
.DE
.IP load-balance
This section contains a simple list of modules.  When the section is
entered, one module is chosen at random to process the request.  All
of the modules in the list should be the same type (e.g. ldap or sql).
All of the modules in the list should behave identically, otherwise
the load-balance section will return different results for the same
request.

Load-balance sections can contain only a list of modules, and cannot
contain keywords that perform conditional operations (if, else, etc)
or update an attribute list.

.DS
	load-balance {
.br
		ldap1	# 50% of requests go here
.br
		ldap2	# 50% of requests go here
.br
	}
.DE
In general, we recommend using "redundant-load-balance" instead of
"load-balance".
.IP redundant-load-balance
This section contains a simple list of modules.  When the section is
entered, one module is chosen at random to process the request.  If
that module succeeds, then the server stops processing the section.
If, however, the module fails, then one of the remaining modules is
chosen at random to process the request.  This process repeats until
one module succeeds, or until the list has been exhausted.

All of the modules in the list should be the same type (e.g. ldap or
sql).  All of the modules in the list should behave identically,
otherwise the load-balance section will return different results for
the same request.

Load-balance sections can contain only a list of modules, and cannot
contain keywords that perform conditional operations (if, else, etc)
or update an attribute list. Please see raddb/radiusd.conf 
"instantiate" section for more configuration examples.

.DS
	redundant-load-balance {
.br
		ldap1	# 50%, unless ldap2 is down, then 100%
.br
		ldap2	# 50%, unless ldap1 is down, then 100%
.br
	}
.DE
.IP return
.br
Returns from the current top-level section, e.g. "authorize" or
"authenticate".  This keyword is mainly used to avoid layers of nested
"if" and "else" statements.

.DS
	authorize {
.br
		if (...) {
.br
			...
.br
			return
.br
		}
.br
		...  # this is never reached when the "if"
.br
		...  # statement is executed
.br
	}
.DE
.SH ATTRIBUTE REFERENCES

Attributes may be referenced via the following syntax:
.DS
	&Attribute-Name
	&Attribute-Name:TAG
	&Attribute-Name[NUM]
	&<list>:Attribute-Name
	&<list>:Attribute-Name:TAG[NUM]
.DE
Where <list> is one of "request", "reply", "control", "proxy-request",
"proxy-reply", or "outer.request", "outer.reply", "outer.control",
"outer.proxy-request", or "outer.proxy-reply". just as with the
"update" section, above.  The "<list>:" prefix is optional, and if
omitted, is assumed to refer to the "request" list.

The TAG portion is a decimal integer between 1 and 31.  Please see RFC
2868 for more information about tags.  Tags can only be used for
attributes which are marked in the dictionary as "has_tag".

The NUM portion is used when there are multiple attributes of the same
name in a list.  The "Attribute-Name" reference will return the first
attribute.  Using an array offset allows the policy to refer to the
second and subsequent attributes.

If '*' is used in the NUM portion, it evaluates to all instances of
the attribute in the request.

If 'n' is used in the NUM portion, it evaluates to the last instance
of the attribute in the request.

When an attribute name is encountered, the given list is examined for
an attribute of the given name.  Some examples are:
.DS
	User-Name
.br
	request:User-Name # same as above
.br
	reply:User-Name
.br
	Tunnel-Password:1
.br
	Cisco-AVPAir[2]
.br
	outer.request:User-Name # from inside of a TTLS/PEAP tunnel
.DE
Note that unlike C, there is no way to define new attributes at
run-time.  They MUST be declared in a dictionary file, and loaded when
the server starts.

All attributes are defined in the dictionaries that accompany the
server.  These definitions define only the name and type, and do not
define the value of the attribute.  When the server receives a packet,
it uses the packet contents to look up entries in the dictionary, and
instantiates attributes with a name taken from the dictionaries, and a
value taken from the packet contents.  This process means that if an
attribute does not exist, it is usually because it was not contained
in a packet that the server received.

Once the attribute is instantiated, it is added to a list.  It can
then be referenced, updated, replaced, etc.

.SH CONDITIONS
The conditions are similar to C conditions in syntax, though
quoted strings are supported, as with the Unix shell.
.IP Simple
conditions
.br
.DS
	(foo)
.DE
Evaluates to true if 'foo' is a non-empty string (single quotes, double
quotes, or back-quoted).  Also evaluates to true if 'foo' is a
non-zero number.  Note that the language is poorly typed, so the
string "0000" can be interpreted as a numerical zero.  This issue can
be avoided by comparing strings to an empty string, rather than by
evaluating the string by itself.

If the word 'foo' is not a quoted string, then it can be taken as a
reference to a named attribute.  See "Referencing attribute lists",
below, for examples of attribute references.  The condition evaluates
to true if the named attribute exists.

Otherwise, if the word 'foo' is not a quoted string, and is not an
attribute reference, then it is interpreted as a reference to a module
return code.  The condition evaluates to true if the most recent
module return code matches the name given here.  Valid module return
codes are given in MODULE RETURN CODES, below.
.IP Negation
.DS
	(!foo)
.DE
Evaluates to true if 'foo' evaluates to false, and vice-versa.
.PP
Short-circuit operators
.RS
.br
.DS
	(foo || bar)
.br
	(foo && bar)
.DE
"&&" and "||" are short-circuit operators.  "&&" evaluates the first
condition, and evaluates the second condition if and only if the
result of the first condition is true.  "||" is similar, but executes
the second command if and only if the result of the first condition is
false.
.RE
.IP Comparisons
.DS
	(foo == bar)
.DE
Compares 'foo' to 'bar', and evaluates to true if the comparison holds
true.  Valid comparison operators are "==", "!=", "<", "<=", ">",
">=", "=~", and "!~", all with their usual meanings.  The operators
":=", "^=" and "=" are assignment operators, and are not allowed for
comparisons.

The operators "<", "<=", ">", and ">=" are also allowed for checking
that an IP address is contained within a network.  For example:
.DS
	if (<ipaddr>192.0.2.1 < 192.0.2.0/24) {
.DE
This comparison succeeds, because the address 192.0.2.1 is contained
within the network 192.0.2.0/24.
.RE
.IP "Attribute Comparisons"
When doing attribute comparisons, the data type of the attribute is
used to determine the data type of the right-hand side argument.
.DS
	(&User-Name == "foo")
.DE
Compares the value of the User-Name attribute to the string 'foo', and
evaluates to true if the comparison holds true.

Similarly,
.DS
	(&Framed-IP-Address == 192.0.2.1)
.DE
Compares the value of the Framed-IP-Address attribute to the IP
address 192.0.2.1.  This IP address does not need to be quoted.
.RE
.IP "Inter-Attribute Comparisons"
.DS
	(&User-Name == &Filter-Id)
.DE
Compares the value of the User-Name attribute to the contents of the
Filter-Id attribute, and evaluates to true if the comparison holds
true.  Unlike the previous example, this comparison is done in a
type-safe way.  For example, comparing the IP addresses 1.2.3.4 and
127.0.0.1 as strings will return different results than comparing them
as IP addresses.

The "&" character in the condition means that the comparison "refers"
to the Filter-Id attribute.  If left off, it means that the User-Name
attribute is compared to the literal string "Filter-Id".

Where the left-hand side is an attribute, the "&" can be omitted.
However, it is allowed for backwards compatibility.  e.g. The comparison
"(&User-Name == &Filter-Id)" is equivalent to the example above.

We recommend using attribute references instead of printing
attributes to a string, e.g. (User-Name == "%{Filter-Id}").
Attribute references will be faster and more efficient.

The conditions will check only the first occurrence of an attribute.
If there is more than one instance of an attribute, the following
syntax should be used:

.DS
	(&Attribute-Name[*] == "foo")
.DE
Using the "[*]" syntax means that it checks all of the instances of
the named attribute.  If one attribute matches, the condition
succeeds.  If none match, the condition fails.

.RE
.IP Casts
.DS
	(<type>foo == bar)
.DE
The left-hand-side of a condition can be "cast" to a specific data
type.  The data type must be one which is valid for the dictionaries.
e.g. "integer", "ipaddr", etc.

The comparison is performed in a type-safe way, as with
"Inter-Attribute Comparisons", above.  Both sides of the condition are
parsed into temporary attributes, and the attributes compared via
type-specific methods.  The temporary attributes have no other effect,
and are not saved anywhere.

Casting allows conditions to perform type-specific comparisons.  In
previous versions of the server, the data would have to be manually
placed into an intermediate attribute (or attributes), and then the
attribute (or attributes) compared.  The use of a cast allows for
simpler policies.

Casts are allowed only on the left-hand side argument of a condition.
.PP
Conditions may be nested to any depth, subject only to line length
limitations (8192 bytes).
.SH DATA TYPES
There are only a few data types supported in the language.  Reference
to attributes, numbers, and strings.  Any data type can appear in
stand-alone condition, in which case they are evaluated as described
in "Simple conditions", above.  They can also appear (with some
exceptions noted below) on the left-hand or on the right-hand side of
a comparison.
.IP numbers
Numbers are composed of decimal digits.  Floating point, hex, and
octal numbers are not supported.  The maximum value for a number is
machine-dependent, but is usually 32-bits, including one bit for a
sign value.
.PP
word
.RS
Text that is not enclosed in quotes is interpreted differently
depending on where it occurs in a condition.  On the left hand side of
a condition, it is interpreted as a reference to an attribute.  On the
right hand side, it is interpreted as a simple string, in the same
manner as a single-quoted string.

Using attribute references permits limited type-specific comparisons,
as seen in the examples below.

.DS
	if (&User-Name == "bob") {
.br
		...
.br
	if (&Framed-IP-Address > 127.0.0.1) {
.br
		...
.br
	if (&Service-Type == Login-User) {
.DE
.RE
.IP """strings"""
.RS
Double-quoted strings are expanded by inserting the value of any
attributes (see ATTRIBUTE REFERENCES, below) before being evaluated.  If
the result is a number it is evaluated in a numerical context.

String length is limited by line-length, usually about 8000
characters.  A double quote character can be used in a string via
the normal back-slash escaping method.  ("like \\"this\\" !")
.RE
.IP 'strings'
Single-quoted strings are evaluated as-is.  Their values are not
expanded as with double-quoted strings above, and they are not
interpreted as attribute references.
.IP `strings`
Back-quoted strings are evaluated by expanding the contents of the
string, as described above for double-quoted strings.  The resulting
command given inside of the string in a sub-shell, and taking the
output as a string.  This behavior is much the same as that of Unix
shells.

Note that for security reasons, the input string is split into command
and arguments before string expansion is done.

For performance reasons, we suggest that the use of back-quoted
strings be kept to a minimum.  Executing external programs is
relatively expensive, and executing a large number of programs for
every request can quickly use all of the CPU time in a server.  If you
believe that you need to execute many programs, we suggest finding
alternative ways to achieve the same result.  In some cases, using a
real language may be sufficient.

.IP /regex/im
These strings are valid only on the right-hand side of a comparison,
and then only when the comparison operator is "=~" or "!~".  They are
regular expressions, as implemented by the local regular expression
library on the system.  This is usually Posix regular expressions.

The trailing 'i' is optional, and indicates that the regular
expression match should be done in a case-insensitive fashion.

The trailing 'm' is also optional, and indicates that carrot '^'
and dollar '$' anchors should match on new lines as well as at the
start and end of the subject string.

If the comparison operator is "=~", then parentheses in the regular
expression will define variables containing the matching text, as
described below in the ATTRIBUTE REFERENCES section.
.SH EXPANSIONS
Attributes are expanded using the ATTRIBUTE REFERENCE syntax
described above, and surrounding the reference with "%{...}"

.DS
	%{Attribute-Reference}
.DE

The result will be a string which contains the value of the attribute
which was referenced, as a printable string.  If the attribute does
not exist, the result will be an empty string.
.PP
Results of regular expression matches
.RS
If a regular expression match has previously been performed, then the
special variable %{0} will contain a copy of the matched portion of
the input string.  The variables %{1} through %{32} will contain the
substring matches, starting from the left-most capture group, onwards.
If there are more than 32 capture groups, the additional results will
not be accessible.
If the server is built with libpcre, the results of named capture groups
are available using the "%{regex:capture group}" expansion.  They will
also be accessible using the variables described above.
Every time a regular expression is evaluated, whether it matches or not,
the capture group values will be cleared.
.RE
.PP
Obtaining results from databases
.RS
It is useful to query a database for some information, and to use the
result in a condition.  The following syntax will call a module, pass
it the given string, and replace the string expansion with the
resulting string returned from the module.

.DS
	%{module: string ...}
.DE

The syntax of the string is module-specific.  Please read the module
documentation for additional details.
.RE
.PP
Conditional Syntax
.RS
Conditional syntax similar to that used in Unix shells may also be
used.
.IP %{%{Foo}:-bar}
If %{Foo} has a value, returns that value.
.br
Otherwise, returns literal string "bar".
.IP %{%{Foo}:-%{Bar}}
If %{Foo} has a value, returns that value.
.br
Otherwise, returns the expansion of %{Bar}.

These conditional expansions can be nested to almost any depth, such
as with %{%{One}:-%{%{Two}:-%{Three}}}
.RE
.PP
String lengths and arrays
.RS
Similar to a Unix shell, there are ways to reference string lengths,
and the second or more instance of an attribute in a list.  If you
need more than this functionality, we suggest using a real language.
.IP %{strlen:string}
The number of characters in "string".  If "string" does not exist,
then the length also does not exist, instead of being zero.

The "string" is expanded before the length is taken.

.IP %{integer:Attribute-Name}
The integer value of the Attribute-Name, instead of the enumerated
name.

e.g. If a request contains "Service-Type = Login-User", the expansion
of %{integer:Service-Type} will yield "1".

.IP %{hex:Attribute-Name}
The hex value of the Attribute-Name, as a series of hex digits.

e.g. If a request contains "Framed-IP-Address = 127.0.0.1", the expansion
of %{hex:Framed-IP-Address} will yield "0x7f000001".

.IP %{Attribute-Name[#]}
The number of instances of Attribute-Name.

e.g. If a request contains "User-Name = bob", the expansion
of %{User-Name[#]} will yield "1".

.IP %{Attribute-Name[*]}
All values of Attribute-Name, concatenated together with ',' as the
separator.

.IP %{List-Name:[#]}
The number of attributes in the named list.

.IP %{List-Name:[*]}
All values of attributes in the named-list, concatenated together with ','
as the separator. Use the %{pairs:} xlat to get a list of attributes and
values.

e.g. If a response contains "Reply-Message = 'Hello', Reply-Message = 'bob'
the expansion of "%{reply:Reply-Message[*]} will yield "Hello\\nbob"

.SH ATTRIBUTE ASSIGNMENTS
The attribute lists described above may be edited by listing one or
more attributes in an "update" section.  Once the attributes have been
defined, they may be referenced as described above in the ATTRIBUTE
REFERENCES section.

The following syntax defines attributes in an "update" section.  Each
attribute and value has to be all on one line in the configuration
file.  There is no need for commas or semi-colons after the value.

.DS
	Attribute-Reference = value
.DE
.PP
Attribute Reference
.RS
The Attribute-Reference must be a reference (see above), using a name
previously defined in a dictionary.  If an undefined name is used, the
server will return an error, and will not start.

.RE
.IP Operators
The operator used to assign the value of the attribute may be one of
the following, with the given meaning.
.RS
.IP =
Add the attribute to the list, if and only if an attribute of the same
name is not already present in that list.
.IP :=
Add the attribute to the list.  If any attribute of the same name is
already present in that list, its value is replaced with the value of
the current attribute.
.IP +=
Add the attribute to the tail of the list, even if attributes of the
same name are already present in the list. When the right hand side
of the expression resolves to multiple values, it means add all values
to the tail of the list.
.IP ^=
Add the attribute to the head of the list, even if attributes of the
same name are already present in the list.  When the right hand side
of the expression resolves to multiple values, it means prepend all
values to the head of the list.
.RE
.PP
Enforcement and Filtering Operators
.RS
The following operators may also be used in addition to the ones
listed above.  Their function is to perform enforcement or filtering
on attributes in a list.
.IP -=
Remove all matching attributes from the list.  Both the attribute name
and value have to match in order for the attribute to be removed from
the list.
.IP ==
Keep all matching attributes.  Both the attribute name and value have
to match in order for the attribute to remain in the list.

Note that this operator is very different than the '=' operator listed
above!
.IP !=
Keep all attributes with matching name, and value not equal to the
given one.
.IP <
Keep all attributes having values less than the value
given here.  Any larger value is replaced by the value given here.  If
no attribute exists, it is added with the value given here, as with
"+=".
.IP <=
Keep all attributes having values less than, or equal to, the value
given here.  Any larger value is replaced by the value given here.  If
no attribute exists, it is added with the value given here, as with
"+=".
.IP >
Keep all attributes having values greater than the value
given here.  Any smaller value is replaced by the value given here.  If
no attribute exists, it is added with the value given here, as with
"+=".
.IP >=
Keep all attributes having values greater than, or equal to, the value
given here.  Any smaller value is replaced by the value given here.  If
no attribute exists, it is added with the value given here, as with
"+=".
.IP !*
Delete all occurrences of the named attribute, no matter what the
value.
.IP =~
Keep all attributes having values which match the given regular
expression.  If no attribute matches, nothing else is done.
.IP !~
Keep all attributes having values which fail to match the given
regular expression.  If no attribute matches, nothing else is done.
.RE
.IP Values
.br
The value can be an attribute reference, or an attribute-specific
string.

When the value is an attribute reference, it must take the form of
"&Attribute-Name".  The leading "&" signifies that the value is a
reference.  The "Attribute-Name" is an attribute name, such as
"User-Name" or "request:User-Name".  When an attribute reference is
used, both attributes must have the same data type.  For example,
"User-Name := &NAS-Port" is invalid, because "User-Name" is a string,
and "NAS-Port" is an integer.

We recommend using the form "Attribute-1 = &Attribute-2" for updates,
instead of "Attribute-1 = "%{Attribute-2}".  The first version will
copy the attribute data, no matter what its form.  The second
version will print the Attribute-2 to a string, and then parse it to
create the value for Attribute-1.  This second version is slower
and more fragile than the first one.

When the value is an attribute-specific string, it can be a string,
integer, IP address, etc.  The value may be expanded as described
above in the DATA TYPES section, above.  For example, specifying
"Framed-IP-Address = 127.0.0.1" will cause the "Framed-IP-Address"
attribute to be set to the IP address "127.0.0.1".  However, using
"Framed-IP-Address := \"%{echo: 127.0.0.1}\"" will cause the "echo"
module to be run with a string "127.0.0.1".  The output of the "echo"
module will then be parsed as an IP address, and placed into the
Framed-IP-Address attribute.

This flexibility means that you can assign an IP address by specifying
it directly, or by having the address returned from a database query,
or by having the address returned as the output of a program that is
executed.

When string values are finally assigned to an attribute, they can have a
maximum length of 253 characters.  This limit is due in part to both
protocol and internal server requirements.  That is, the strings in
the language can be nearly 8k in length, say for a long SQL query.
However, the output of that SQL query should be no more than 253
characters in length.
.SH OTHER KEYWORDS
Other keywords in the language are taken from the names of modules
loaded by the server.  These keywords are dependent on both the
modules, and the local configuration.

Some use keywords that are defined in the default configuration file
are:
.IP fail
Cause the request to be treated as if a database failure had occurred.
.IP noop
Do nothing.  This also serves as an instruction to the configurable
failover tracking that nothing was done in the current section.
.IP ok
Instructs the server that the request was processed properly.  This
keyword can be used to over-ride earlier failures, if the local
administrator determines that the failures are not catastrophic.
.IP reject
Causes the request to be immediately rejected
.SH MODULE RETURN CODES
When a module is called, it returns one of the following codes to
"unlang", with the following meaning.

.DS
	notfound        information was not found
.br
	noop            the module did nothing
.br
	ok              the module succeeded
.br
	updated         the module updated the request
.br
	fail            the module failed
.br
	reject          the module rejected the request
.br
	userlock        the user was locked out
.br
	invalid         the configuration was invalid
.br
	handled         the module has handled the request itself
.DE

These return codes can be tested for in a condition, as described
above in the CONDITIONS section.

See also the file doc/configurable_failover for additional methods of
trapping and modifying module return codes.
.SH FILES
/etc/raddb/radiusd.conf
.SH "SEE ALSO"
.BR radiusd.conf (5),
.BR dictionary (5)
.SH AUTHOR
Alan DeKok <aland@deployingradius.com>